opencv 把3通道图像转成单通道_Opencv的Mat内容小记

本文介绍了OpenCV中的Mat对象,包括Mat的内存结构,如何创建Mat对象,以及如何进行图像通道转换。讨论了如何访问和修改Mat中的像素值,同时展示了将3通道图像转换为单通道的方法。此外,还提到了一些图像处理的实用技巧,如像素值缩放和条件操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Mat是什么

Mat是一种图像容器,是二维向量,灰度图的Mat一般存放<uchar>类型,RGB彩色图像一般存放<Vec3b>类型。

单通道灰度图数据存放样式:

v2-0efa9bf051295f79699671a2017f5a38_b.jpg

RGB三通道彩色图存放形式不同,每列并列存放通道数量的子列(注意通道数量反转为了BGR):

v2-60cc005b89290828e6bb60ae2bff83a7_b.png

通常情况下Mat的每一行是连续存放的,也就是在内存上图像的所有数据存放成一行,在用指针访问时可以提供很大方便。可以用 isContinuous()函数来判断图像数组是否为连续的,语句为

if (img.isContinuous()){}

2.如何创建Mat对象

Mat到底有哪些参数呢,OpenCV官方文档对Mat的描述如下:

class 

当我们用构造函数创建一个Mat对象的时候,可以指定图像的数据类型有CV_8UC1、CV_8UC3、CV_32FC1、CV_32FC3等多种形式,如定义

Mat img(480, 640, CV_16UC1);

就是创建一个640*480的16位深的单通道Mat对象。CV_8UC3自然就是最常见的8位3通道图(即255的RGB图像)。

当然最常用的是用opencv读入一张本地图像,此时自动就是一个Mat对象,但有时我们需要像python里的numpy那样方便地转换Mat对象的数据类型,numpy里可以这样操作

img

在C++的opencv里,就是这样转为16位的无符号整数三通道的:

using 

Mat也可以通过深拷贝创建一个新地址的Mat,避免影响原始的Mat:

Mat copy;
image.copyTo(copy);

除此之外,python的numpy里经常采用如下的方式构建全1的矩阵:

import 

那么c++里如何构建这样的Mat呢?

Mat 

这样就创建了一个全为1的矩阵了(类似的可以zeros等等是一样的创建方法)。

有时我们对图像进行一些像素值的缩放,也就是对某个图像进行全部元素的加减乘除,python里自然非常方便,如所有元素除以100,那么image / 100.0即可。c++里使用convertTo非常容易实现这点(不必for循环了)。

如果我们要对矩阵A进行元素的加减乘除操作得到B,即B=a*A+b,则

A

举个例子,刚刚提到的全部元素除以100那就是

A.convertTo(B,CV_32F,0.01,0.00);

3.Mat对象如何访问元素

从Mat的类定义看,用data可以方便访问图像元素值。用Mat存储一幅图像时,若图像在内存中是连续存储的(即Mat对象的isContinuous == true),则可将图像的数据看成是一个一维数组,而其data(uchar*)成员就是指向图像数据的第一个字节的指针,因此可以用data指针访问图像数据,OpenCV中将data定义为uchar*类型。那么我们如何通过data指针去访问和修改图像的某一个像素值呢,对于数据为uchar类型的Mat对象,可以直接用data访问和修改,对于其他类型的Mat对象,将data强制转换成指向Mat对象对应数据类型的指针即可访问。

可以把Mat对象方便地转为一维指针数组,便于指针操作(指针访问数组进行操作速度更快):

unsigned short* Array = (unsigned short*)Data.data;
delete[] Array;

也可借用opencv里Mat类的智能指针,这样即可auto data = Data.ptr();

对这个数组进行了一系列图像处理操作后,如果要保存回Mat便于后续imshow和imwrite,保存方式可以这样:

Mat newData(480, 640, CV_16UC3, Array);

当然这样是灵活的提供了自己创的指针,用了后还需要delete,为了方便可以用Mat自带的智能指针进行操作。

这里举一个实例:如果我们只对像素值小于0.1的像素才执行膨胀操作(也就是为了填充0),python里搞一个mask就可以轻易实现

empty_pixels 

在c++里则不然,用智能指针可以这样操作:(构建行智能指针数组)

Mat 

这样使用智能指针也可以(这样可以操作每个元素了)

 for (int k = 0; k < 480; k++)
    {
      for (int q = 0; q < 640; q++)
        {
           auto *ptr = depthArray.ptr<uint16_t>(k, q);
           if (ptr[0] ==0)
            {
             ptr[0] = 10;
            }
        }
    }

补充小tip:

记录一下常用的程序计时方法(和python里的time一样方便):

clock_t 

这里记录一个好用的numpy的操作:numpy.where

np.where(condition, x, y)当满足condition时用x代替y,如

src

意思就是把小的像素值全都变为nan

另外,这里的condition也可以是布尔数组,每个条件都和x,y相对应(广播机制),x遇到False时由y给x对应位置的像素值。如

mask 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值