图像灰度图,直方图,像素通道问题


1.图像直方图概述
  直方图广泛运用于很多计算机视觉运用当中,通过标记帧与帧之间显著的边缘和颜色的统计变化,来检测视频中场景的变化。在每个兴趣点设置一个有相近特征的直方图所构成 “标签”,用以确定图像中的兴趣点。边缘、色彩、角度等直方图构成了可以被传递给目标识别分类器的一个通用特征类型。色彩和边缘的直方图序列还可以用来识别网络视频是否被复制。
  其实,简单来说,直方图就是对数据进行统计的一种方法,并且将统计值组织到一系列实现定义好的 bin 当中。其中, bin 为直方图中经常用到的一个概念,可以译为 “直条” 或 “组距”,其数值是从数据中计算出的特征统计量,这些数据可以是诸如梯度、方向、色彩或任何其他特征。且无论如何,直方图获得的是数据分布的统计图。通常直方图的维数要低于原始数据。
  图像直方图(Image Histogram)是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数。这种直方图中,横坐标的左侧为纯黑、较暗的区域,而右侧为较亮、纯白的区域。因此一张较暗图片的直方图中的数据多集中于左侧和中间部分,而整体明亮、只有少量阴影的图像则相反。CV 领域常借助图像直方图来实现图像的二值化。
  直方图的意义如下:
  ● 直方图是图像中像素强度分布的图形表达方式。
  ● 它统计了每一个强度值所具有的像素个数。

  直方图是对数据的统计集合,并将统计结果分布于一系列预定义的 bins 中。这里的数据不仅仅指的是灰度值,且统计数据可能是任何有效描述图像的特征。
  假设有一个矩阵包含一张图像的信息(灰度值 0 - 255),既然已知数字的范围包含 256 个值,于是可以按一定规律将这个范围分割成子区域(也就是 bins)。如:
         
  然后再统计每一个 bin(i) 的像素数目。采用这一方法来统计上面的数字矩阵,可以得到下图(其中 x 轴表示 bin,y 轴表示各个 bin 中的像素个数):
    

  直方图的一些术语和细节:
  ● dims:需要统计的特征数目。在上例中,dims = 1 ,因为仅仅统计了灰度值(灰度图像)。
  ● bins:每个特征空间子区段的数目,可译为 “直条” 或 “组距”,在上例中, bins = 16。
  ● range:每个特征空间的取值范围。在上例中,range = [0, 255]。

2.像素通道问题

用大疆Guidance获取了视觉传感模块的左右图像g_greyscale_image_left和g_greyscale_image_right,大疆官网上的解释为:they are both 320 hight, 240 width, 8 bit grayscale images.
我现在想看看它的具体像素值,所以写了以下代码:
if (!g_greyscale_image_left.empty()&&!g_greyscale_image_right.empty()) //当传来的图片不为空
{
imshow(string("left_")+char('0'+sensor_id), g_greyscale_image_left); //显示左图
imshow(string("right_")+char('0'+sensor_id), g_greyscale_image_right);  //显示右图
int nrl =g_greyscale_image_left.rows; // number of rows 
int ncl =g_greyscale_image_left.cols; // number of columns 
int cha=g_greyscale_image_left.channels();  
cout<<"灰度图的通道数为:"<<cha<<endl;  //显示通道数
for (int i = 0; i<nrl; i++)
{

for (int j = 0; j<ncl; j++) {
cout<<"像素值为:"<<g_greyscale_image_left.at<Vec3b>(i, j)<<endl;
}
}

}
结果显示如下:通道数为1,但是像素值有三个值,如果把Vec3b改为uchar,则输出的像素值为乱码,有大神能解释下为什么单通道图像显示的像素值为三个数值吗?

是因为你强制获取了 3 个值吧
 g_greyscale_image_left.at<Vec3b>(i, j)

把 Vec3b 改成 BYTE 试试

改了后就成这样了:


(int)g_greyscale_image_left.at<BYTE>(i, j)

可以显示正常的单通道数值了,谢谢。请问uchar,VEC3b,BYTE这几种各有什么区别呢?我强制显示三通道为什么也可以?



uchar, BYTE 都是 unsigned char 的 typedef 
VEC3b 应该是定义的一个结构体


数据本身无法表明自己是什么, 只能看什么方法来处理他

3.CV_8UC1,CV_8UC2,CV_8UC3等意思

一般的图像文件格式使用的是 Unsigned 8bits吧,CvMat矩阵对应的参数类型就是
CV_8UC1,CV_8UC2,CV_8UC3。
(最后的1、2、3表示通道数,譬如RGB3通道就用CV_8UC3)

而float 是32位的,对应CvMat数据结构参数就是:CV_32FC1,CV_32FC2,CV_32FC3...
double是64bits,对应CvMat数据结构参数:CV_64FC1,CV_64FC2,CV_64FC3等。

变换这种矩阵单位类型,Mat里有一个函数convertTo可以办到:
C++:void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0 )
mask.convertTo(OutputArray m, CV_8UC3, -1.0, 255.0); // inverse the mask matrix  means I_new(x,j) = -1*I(x,j) + 255.
rtype 参数就是上述单位类型。具体可以查相关的文档。
 

当我在OpenCV文档中找不到任何描述 - 用文字描述 - 各种类型代表什么的时候,谷歌也没有太多帮助。我知道CV_8UC1是灰度的,但CV_8UC3代表什么?它是RGB?还是YUV?

另外,来自其他的定义呢types_c.h?命名约定是什么模式?

#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))

#define CV_8SC1 CV_MAKETYPE(CV_8S,1)
#define CV_8SC2 CV_MAKETYPE(CV_8S,2)
#define CV_8SC3 CV_MAKETYPE(CV_8S,3)
#define CV_8SC4 CV_MAKETYPE(CV_8S,4)
#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))

#define CV_16UC1 CV_MAKETYPE(CV_16U,1)
#define CV_16UC2 CV_MAKETYPE(CV_16U,2)
#define CV_16UC3 CV_MAKETYPE(CV_16U,3)
#define CV_16UC4 CV_MAKETYPE(CV_16U,4)
#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))

#define CV_16SC1 CV_MAKETYPE(CV_16S,1)
#define CV_16SC2 CV_MAKETYPE(CV_16S,2)
#define CV_16SC3 CV_MAKETYPE(CV_16S,3)
#define CV_16SC4 CV_MAKETYPE(CV_16S,4)
#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))

#define CV_32SC1 CV_MAKETYPE(CV_32S,1)
#define CV_32SC2 CV_MAKETYPE(CV_32S,2)
#define CV_32SC3 CV_MAKETYPE(CV_32S,3)
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))

#define CV_32FC1 CV_MAKETYPE(CV_32F,1)
#define CV_32FC2 CV_MAKETYPE(CV_32F,2)
#define CV_32FC3 CV_MAKETYPE(CV_32F,3)
#define CV_32FC4 CV_MAKETYPE(CV_32F,4)
#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))

#define CV_64FC1 CV_MAKETYPE(CV_64F,1)
#define CV_64FC2 CV_MAKETYPE(CV_64F,2)
#define CV_64FC3 CV_MAKETYPE(CV_64F,3)
#define CV_64FC4 CV_MAKETYPE(CV_64F,4)
#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))

列表中的任何基元类型都可以通过表单中的标识符来定义 CV_<bit-depth>{U|S|F}C(<number_of_channels>)

其中U是无符号整数类型,S是有符号整数类型,F是浮点类型。

所以CV_8UC3是一个8位无符号整数矩阵/ 3通道图像。这是最常见的RGB(或实际上是BGR)图像。它只是意味着有三个渠道,如何使用它们取决于你和你的应用程序。

命名模式描述了数据如何实际布置在PC存储器中,并且与图像类型没有直接关系。实际上,数据布局和图像类型是分离的。

你可能已经找到了CV_XX模式的含义(即数据布局)。

对于成像数据类型,如果你要求OpenCV将图像作为彩色图像加载,则它会将其加载为BGR图像并将其数据作为CV_8UC3进行布局。有几件事要注意:

  • 默认频道顺序是BGR而不是RGB。
  • 可以使用模块中的类型转换功能imgproc,即cv::cvtColor()。所有数据布局和图像类型都不兼容,请检查文档 ;
  • alpha通道通常被忽略。大多数处理功能要么专用于单通道图像,要么对所有通道均匀应用相同的处理;
  • 当一个函数接受一个(alpha-)掩码时,它通常是一个可选的CV_8UC1缺省为空矩阵的类型参数。

Opencv Mat矩阵中data、size等属性的理解

  • data:  

       uchar类型的指针,指向Mat数据矩阵的首地址。可以理解为标示一个房屋的门牌号;

  • dims: 

        Mat矩阵的维度,若Mat是一个二维矩阵,则dims=2,三维则dims=3,大多数情况下处理的都是二维矩阵,是一         个平面上的矩阵。

        可以理解为房屋是一个一层的平房,三维或更多维的则是多层楼房;

  • rows:

        Mat矩阵的行数。可理解为房屋内房间行数;

  • cols: 

        Mat矩阵的列数。可理解为房屋内房间列数;

  • size():

        首先size是一个结构体,定义了Mat矩阵内数据的分布形式,数值上有关系式:

         image.size().width==image.cols;        image.size().height==image.rows                                                      

         可以理解为房屋内房间的整体布局,这其中包括了房间分别在行列上分布的数量信息;

  • channels():

        Mat矩阵元素拥有的通道数。例如常见的RGB彩色图像,channels==3;而灰度图像只有一个灰度分量信息,             channels==1。

        可以理解为每个房间内放有多少床位,3通道的放了3张床,单通道的放了1张床;

  • depth: 

        用来度量每一个像素中每一个通道的精度,但它本身与图像的通道数无关!depth数值越大,精度越高。在                 Opencv中,Mat.depth()得到的是一个0~6的数字,分别代表不同的位数,对应关系如下:                            

        enum{CV_8U=0,CV_8S=1,CV_16U=2,CV_16S=3,CV_32S=4,CV_32F=5,CV_64F=6}          

        其中U是unsigned的意思,S表示signed,也就是有符号和无符号数。

        可以理解为房间内每张床可以睡多少人,这个跟房间内有多少床并无关系;

  • elemSize:

        elem是element(元素)的缩写,表示矩阵中每一个元素的数据大小,如果Mat中的数据类型是CV_8UC1,那么             elemSize==1;如果是CV_8UC3或CV_8SC3,那么elemSize==3;如果是CV_16UC3或者CV_16SC3,那么             elemSize==6;即elemSize是以8位(一个字节)为一个单位,乘以通道数和8位的整数倍;

        可以理解为整个房间可以睡多少人,这个时候就得累计上房间内所有床位数(通道)和每张床的容纳量了;

  • elemSize1:

        elemSize加上一个“1”构成了elemSize1这个属性,1可以认为是元素内1个通道的意思,这样从命名上拆分后就很         容易解释这个属性了:表示Mat矩阵中每一个元素单个通道的数据大小,以字节为一个单位,所以有: 

        eleSize1==elemSize/channels;

  • step:

        可以理解为Mat矩阵中每一行的“步长”,以字节为基本单位,每一行中所有元素的字节总量,是累计了一行中所           有元素、所有通道、所有通道的elemSize1之后的值;

  • step1(): 

       以字节为基本单位,Mat矩阵中每一个像素的大小,累计了所有通道、所有通道的elemSize1之后的值,所以有:

        step1==step/elemSize1;

  • type:

        Mat矩阵的类型,包含有矩阵中元素的类型以及通道数信息,type的命名格式为CV_(位数)+(数据类型)+(通道               数),所有取值如下:

OpenCV中Mat的详解

我们有多种方法可以获得从现实世界的数字图像:数码相机、扫描仪、计算机体层摄影或磁共振成像就是其中的几种。在每种情况下我们(人类)看到了什么是图像。但是,转换图像到我们的数字设备时我们的记录是图像的每个点的数值。

例如在上图中你可以看到车的镜子只是一个包含所有强度值的像素点矩阵。现在,我们如何获取和存储像素值可能根据最适合我们的需要而变化,最终可能减少计算机世界内的所有图像数值矩阵和一些其他的信息的描述基质本身。OpenCV 是一个计算机视觉库,其主要的工作是处理和操作,进一步了解这些信息。因此,你需要学习和开始熟悉它的第一件事是理解OpenCV 是如何存储和处理图像。

Mat

OpenCV 自 2001 年出现以来。在那些日子里库是围绕C接口构建的。在那些日子里,他们使用名为IplImage C 的结构在内存中存储图像。这是您将在大多数较旧的教程和教材中看到的那个。使用这个结构的问题是将 C 语言的所有负面效果都摆到了桌面上。最大的问题是手动管理。它是建立在用户来负责处理内存分配和解除分配的假设之上的。当程序规模较小时,这是没有问题的,一旦代码基开始变得越来越大它将会越来越挣扎着处理所有这一切而不是着眼于实际解决自己的开发目标。

幸运的是 c + + 出现了,并引入了类的概念,使得为用户开辟另一条路成为可能:

自动内存管理 (或多或少)。好消息是,c + +,如果完全兼容 C 所以进行更改时没有兼容性问题产生。因此, OpenCV其2.0 版本引入一个新的c + + 接口,通过利用这些优点将为你的工作提供新的方法。某种程度上,在其中您不需要拨弄内存管理让你的代码简洁 (写得更少,实现的更多)。C + + 接口的唯一主要缺点在于,目前许多嵌入式的开发系统支持仅 C.因此,除非您的目标是这一平台,否则就没有理由再使用旧的方法(除非你是个受虐狂程序员和喜欢自讨苦吃)。

你需要知道的关于Mat的第一件事是你不再需要手动分配其大小并且当你不需要它的时候你不再需要手动释放它(自动内存管理)。虽然这样做仍然是可能的,大多数 OpenCV 函数将手动分配其输出数据。还有一个额外的好处是如果传递一个已存在Mat对象,它已经为矩阵分配所需的空间,这段空间将被重用。也就是说我们在任何时候只使用与我们执行任务时所必须多的内存一样多的内存。

Mat本质上是由两个数据部分组成的类: (包含信息有矩阵的大小,用于存储的方法,矩阵存储的地址等) 的矩阵头和一个指针,指向包含了像素值的矩阵(可根据选择用于存储的方法采用任何维度存储数据)。矩阵头部的大小是恒定的。然而,矩阵本身的大小因图像的不同而不同,通常是较大的数量级。因此,当你在您的程序中传递图像并在有些时候创建图像副本您需要花费很大的代价生成图像矩阵本身,而不是图像的头部。OpenCV 是图像处理库,它包含大量的图像处理函数。若要解决的计算挑战,最终大部分时间你会使用库中的多个函数。由于这一原因图像传给库中的函数是一种常见的做法。我们不应忘记我们正在谈论往往是计算量相当大的图像处理算法。我们想要做的最后一件事是通过制作不必要的可能很大的图像的拷贝进一步降低您的程序的速度。

为了解决这一问题 OpenCV 使用引用计数系统。其思想是Mat的每个对象具有其自己的头,但可能他们通过让他们矩阵指针指向同一地址的两个实例之间共享该矩阵。此外,拷贝运算符将只能复制矩阵头部,也还将复制指向矩阵的指针,但不复制矩阵本身。

  1. Mat A, C; //仅创建了头部
  2. A = imread(argv[1], CV_LOAD_IMAGE_COLOR); //在此我们知道使用的方法(分配矩阵)
  3. Mat B(A); //使用拷贝构造函数
  4. C = A; //赋值运算符

上文中的所有对象,使用同一个数据矩阵。他们的头不同,但是其中任何一个对矩阵进行修改,都会将影响所有其他的矩阵。在实践中的不同对象只是提供相同的底层数据不同的访问方法,然而,它们的头部是不同的。真正有趣的部分是您可以创建仅指向完整数据的一小部分的头。例如,要在图像中创建兴趣区域 ( ROI) 只需创建一个新头设置新边界:

Mat D (A, Rect(10, 10, 100, 100) );

Mat E = A(Range:all(), Range(1,3));

现在,你可能会问多个Mat对象使用同一片数据,这片数据什么时候清理。简短的回答是:由最后一个使用它的对象清理。这里使用引用计数的机制,每当有人复制Mat对象的头,计数器增加。每当一个头被清除,计数器下调。当计数器变为零,矩阵也就被释放了。(类似于动态指针)因为有时会仍然也要复制矩阵的本身,存在着 clone() 或 copyTo() 函数。

Mat F = A.clone();
Mat G;
A.copyTo(G);

现在 modifyingForGwill 不会影响由 theMatheader 指出的矩阵。你要记得从所有的是:

1、输出图像分配 OpenCV 功能是自动 (除非另行指定,否则)。

2、用c + + OpenCV的接口就无需考虑内存释放。

3、赋值运算符和复制构造函数 (构造函数)只复制头。

4、使用clone () 或copyTo () 函数将复制的图像的基础矩阵。

存储方法

这是关于你是如何存储的像素值。您可以选择的颜色空间和使用的数据类型。色彩空间是指我们如何结合为了代码指定的颜色的颜色分量。最简单的是灰色的规模。在这里我们所掌握的颜色是黑色和白色。组合的这些让我们能创造很多的灰度级。

对于彩色的方法,我们有很多方法可供选择。不过,每一就是将他们拆解成三个或四个基本组成部分,这些部分就会组合给所有其他的方法。最受欢迎的这一个 RGB,主要是因为这也是我们的眼睛如何建立中我们的眼睛的颜色。其基准的颜色是红、 绿、 蓝。编写代码的一种颜色的透明度有时第四个元素: 添加 alpha (A)。但是,它们很多颜色系统每个具有自身的优势:

1、RGB 是最常见的是我们的眼睛使用类似的事情,我们显示系统还撰写使用这些颜色。

· 单纯疱疹和合肥分解颜色到他们的色相、 饱和度和亮度值/组件,这是我们来描述颜色更自然的方式。您使用,例如可驳回的最后一个组件,使你不那么明智的输入图像的光照条件的算法。

2、 YCrCb 使用流行的 JPEG 图像格式。

3、 CIE L *b*a 是均匀颜色空间,它是非常方便的如果您需要测量给定的颜色,以另一种颜色的距离。

现在,每个建筑构件都自己有效的域。这会导致使用的数据类型。我们如何存储组件的定义只是如何精细的控制,我们已于其域。最小的数据类型可能是 char 类型,这意味着一个字节或 8 位。这可能是有符号(值-127 到 + 127)或无符号(以便可以存储从 0 到 255 之间的值)。虽然这三个组件的情况下已经给 16 万可能的颜色来表示 (如 RGB 的情况下) 我们可能通过使用浮点数 (4 字节 = 32 位) 或double(8 字节 = 64 位) 数据类型的每个组件获得甚至更精细的控制。然而,请记住增加组件的大小也会增加在内存中的整张图片的大小。

显式创建Mat对象

Load, Modify and Save an Image 教程中,你已经可以看到如何使用readWriteImageVideo: 'imwrite() <imwrite>' 函数将一个矩阵写到一个图像文件中。然而,出于调试目的显示的实际值就方便得多。您可以实现此通过Mat的 <<运算符。不过,请注意这仅适用于二维矩阵。

虽然Mat是一个伟大的图像容器类,它也是一般矩阵类。因此,利用Mat创建和操作多维矩阵是可能的。您可以通过多种方式创建Mat的对象:

1、 Mat()构造函数

Mat M(2,2, CV_8UC3, Scalar(0,0,255));

cout << "M = " << endl << " " << M << endl << endl;

对于二维的和多通道的图像,我们首先定义它们的大小:按行和列计数。

然后我们需要指定的数据类型,用于存储元素和每个矩阵点通道的数量。为此,我们根据以下的约定可以作出多个定义:

CV_ [每一项的位数] [有符号或无符号] [类型前缀] C [通道数]

例如,CV_8UC3 意味着我们使用那些长的 8 位无符号的 char 类型和每个像素都有三个项目的这三个通道的形成。这是预定义的四个通道数字。Scalar 是四个元素短向量。指定此和可以初始化所有矩阵点与自定义的值。但是如果你需要更多您可以创建与上部宏和频道号码放在括号中,您可以看到下面的类型。

使用 C\C++ 数组和通过构造函数来初始化

int sz[3] = {2,2,2};

Mat L(3,sz,CV_8UC(1),Scalar::all(0));(构造函数初始化)

上例为我们展示了如何创建一个二维以上的矩阵。首先指定其维度数,然后传入一个包含了每个维度尺寸信息的指针,其他都保持不变。

2、为一个已经存在的IplImage创建一个头:

IplImage* img = cvLoadImage("greatwave.png", 1);

Mat mtx(img); // 转换 IplImage*-> Mat(创建头)

3、 Create()函数:

M.create(4,4, CV_8UC(2));

cout << "M = "<< endl << " " << M << endl << endl;

你不能通过这个构造来初始化矩阵中的数值。它只会在新的矩阵尺寸与旧的矩阵尺寸不合时重新分配矩阵的数据空间。

 MATLAB风格的初始化函数:zeros(), ones(),

:eyes().指定使用的尺寸和数据类型

Mat E = Mat::eye(4, 4, CV_64F);
cout << "E = " << endl << " " << E << endl << endl;
Mat O = Mat::ones(2, 2, CV_32F);
cout << "O = " << endl << " " << O << endl << endl;
Mat Z = Mat::zeros(3,3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;

对于小的矩阵来说你可以使用逗号隔开的初始化函数:

Mat C = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);

cout << "C = " << endl << " " << C << endl << endl;

为一个已有的Mat对象创建一个新的头然后clone()或者copyTo()这个头.

Mat RowClone = C.row(1).clone();

cout << "RowClone = " << endl << " " << RowClone << endl << endl;

打印格式

注意:你可以通过用randu()函数产生的随机值来填充矩阵。你需要给定一个上限和下限来确保随机值在你期望的范围内:

Mat R = Mat(3, 2, CV_8UC3);

cv::randu(R, Scalar::all(0), Scalar::all(255));

randu(cv::Mat &dat ,const cv::Scalar &low,const cv::Scalar &high);

在上一个例子中你可以看到默认的格式选项。尽管如此,OpenCV允许你在符合以下规则的同时格式化你的输出:

默认

cout << "R (default) = " << endl << R << endl << endl;

Python

cout << "R (python) = " << endl << format(R,"python") << endl << endl;

Comma separated values (CSV)

cout << "R (csv) = " << endl << format(R,"csv" ) << endl << endl;

Numpy

cout << "R (numpy) = " << endl << format(R,"numpy" ) << endl << endl;

C

cout << "R (c) = " << endl << format(R,"C" ) << endl << endl;

打印出其它常见数据项

OpenCV 通过<<操作符也为其他常用OpenCV数据结构提供打印输出的支持,如:

2D 点

Point2f P(5, 1);

cout << "Point (2D) = " << P << endl << endl;

3D 点

Point3f P3f(2, 6, 7);

cout << "Point (3D) = " << P3f << endl << endl;

std::vector通过 cv::Mat

vector<float> v;

v.push_back( (float)CV_PI); v.push_back(2); v.push_back(3.01f);

cout << "Vector of floats via Mat = " << Mat(v) << endl << endl;

点的std::vector

vector<Point2f> vPoints(20);

for (size_t E = 0; E < vPoints.size(); ++E)

vPoints[E] = Point2f((float)(E*5), (float)(E % 7));

cout << "A vector of 2D Points = " << vPoints << endl << endl;

这里大多数的例程都是在一个小控制台程序里运行。你可以在这里下载或是在cpp示例文件夹下找到。

Mat::~Mat

Mat的析构函数。

C++: Mat::~Mat()

析构函数调用Mat::release()。

Mat::operator =

提供矩阵赋值操作。

C++: Mat& Mat::operator=(const Mat& m)

C++: Mat& Mat::operator=(const MatExpr_Base& expr)

C++: Mat& Mat::operator=(const Scalar& s)

参数:

m – 被赋值的右侧的矩阵。 矩阵的赋值是一个复杂度为O(1) 的操作。 这就意味着没有复制数据段,并且两矩阵将使用同一引用计数器。在给矩阵赋新数据之前先由Mat::release()释放引用。

expr –被赋值的矩阵表达式对象。 作为第一种赋值方式的逆操作第二种形式可以被重新用到具有适当大小和尺寸的已分配空间的矩阵上以适应表达式的结果。矩阵表达式扩展得到的实函数将自动处理这个分配过程。例如:

C=A+B 扩展成add(A, B, C) , andadd() 要当心C重新分配数据的操作。.

s – 标量赋值给每一个矩阵元,矩阵的大小和类型将不会改变。有现成的赋值运算符。由于他们各不相同请阅读运算符参数说明。

Mat::operator MatExpr

提供一种Mat-to-MatExpr转换运算符

C++: Mat::operator MatExpr_<Mat, Mat>() const

转换运算符不能显示调用而是由矩阵表达式引擎(Matrix Expression engine)内部调用The cast operator should not be called explicitly. It is used internally by the Matrix Expressions engine.

Mat::row

创建一个指定行数的矩阵头。.

C++: Mat Mat::row(int i) const

参数:

i – 一个0基的行索引.

该方法创建一个具有指定了行数的新矩阵头的矩阵并返回它。这是一个复杂度为O(1) 的操作,无须考虑矩阵的尺寸。新矩阵和原矩阵共享一份基础数据。这是一个典型基本矩阵处理操作的例子, axpy是LU和许多其它算法都使用的一个函数

inline void matrix_axpy(Mat& A, int i, int j, double alpha)

{

A.row(i) += A.row(j)*alpha;

}

Note:在当前实现中,下面的代码不会无法按预期的效果工作:

Mat A ;

...

A.row(i) = A.row(j) ;/ /不起作用

发生这种情况是因为 A.row(i) 形成临时矩阵头进一步分配给另一个矩阵头。请记住,每个操作复杂度为O(1),即没有复制任何数据。因此,如果你预期第 j行被复制到第 i行,那么上述赋值不成立。要做到这一点,应该把这种简单的赋值转换到表达式中或使用 Mat::copyTo() 方法:

Mat A ;

...

/ / 可行,但看上去有点目的不明确。

A.row(i) = A.row(j) + 0;

/ / 这是有点儿长,但这是推荐的方法。

A.row(j).copyTo(A.row(i)) ;

Mat::col

创建一个具有指定了矩阵头中列数这个参数的矩阵

C++: Mat Mat::col(int j) const

参数:

j –一个0基(从0开始)的列索引

该方法创建一个具有指定了矩阵头中列数这个参数的新矩阵并作为函数返回值。这是一种复杂度为O(1)的操作,不用考虑矩阵的尺寸大小。新矩阵和原始矩阵共享一份基础数据。参看Mat::row()说明信息。

Mat::rowRange

为指定的行span创建一个新的矩阵头。

C++: Mat Mat::rowRange(int startrow, int endrow) const

C++: Mat Mat::rowRange(const Range& r) const

参数:

startrow – 一个包容性的0基(从0开始)的行span起始索引.。

endrow – 一个0基的独占性的行span.终止索引。

r – Range 结构包含着起始和终止的索引值。该方法给矩阵指定的行span创建了新的头。 与Mat::row() 和 Mat::col()相类似这是一个复杂度为O(1)的操作。

Mat::colRange

为指定的行span创建一个矩阵头。

C++: Mat Mat::colRange(int startcol, int endcol) const

C++: Mat Mat::colRange(const Range& r) const

参数:

startcol – 一个包容性的0基(从0开始)的span列起始索引。

endcol –一个0基的独占性的列span.终止索引。

r –Range 结构包含着起始和终止的索引值。该方法给矩阵指定的列span创建了新的头。 与Mat::row() 和 Mat::col()相类似这是一个复杂度为O(1)的操作。

Mat::diag

提取或创建矩阵对角线。

C++: Mat Mat::diag(int d) const

C++: static Mat Mat::diag(const Mat& matD)

参数:

d – 对角线的索引值,可以是以下的值:

– d=0 是主对角线

– d>0表示下半部的对角线。例如:d=1对角线是紧挨着住对角线并位于矩阵下方。

– d<0表示来自矩阵上半部的对角线。例如:d= 1表示对角线被设置在对角线的上方并紧挨着。

matD – 单列用于形成矩阵对角线的列。

该方法为指定的矩阵创建一个新的头。然后新矩阵被分割为单独的列矩阵。类似于Mat::row() 和Mat::col() ,它是复杂度为O(1)操作。

Mat::clone

创建一个数组及其基础数据的完整副本。

C++: Mat Mat::clone() const

该方法创建了一个完整的数组副本。原始的step[]不会被考虑在内的。因此数组的副本是一占用total()*elemSize()字节的连续阵列。

Mat::copyTo

把矩阵复制到另一个矩阵中。

C++: void Mat::copyTo(OutputArray m) const

C++: void Mat::copyTo(OutputArray m, InputArray mask) const

参数:

m – 目标矩阵。如果它的尺寸和类型不正确,在操作之前会重新分配。

mask – 操作掩码。它的非零元素表示矩阵中某个要被复制。

该方法把矩阵的复制到另一个新的矩阵中在复制之前该方法会调用

m.create(this->size(), this->type);

因此,目标矩阵会在必要的情况下重新分配

尽管m.copyTo(m) works ?awlessly,该函数并不处理源矩阵和目标矩阵之间有重叠的部分的情况。当操作掩码指定以及上述的Mat::create重新分配矩阵,新分配的矩阵在数据复制到里面之前全都被初始化为0。

Mat::convertTo

在缩放或不缩放的情况下转换为另一种数据类型。

C++:

void Mat::convertTo(OutputArray m,int rtype,double alpha=1,double beta=0)const

参数:

m – 目标矩阵。如果它的尺寸和类型不正确,在操作之前会重新分配。

rtype – 要求是目标矩阵的类型,或者在当前通道数与源矩阵通道数相同的情况下的depth。如果rtype 为负,目标矩阵与源矩阵类型相同。

beta – 可选的delta加到缩放值中去。

该方法将源像素值转化为目标类型saturate_cast<> 要放在最后以避免溢出

m( x;y) = saturate_cast < rType > ( α*( *this)( x;y) +β)

Mat::assignTo

提供了一个convertTo的功能形式。

C++: void Mat::assignTo(Mat& m, int type=-1 ) const

Parameters

m – 目标阵列。

type – 要求是目标阵列depth或-1(如果阵列的类型和源矩阵类型相同)

这是一个 internally 使用的由 Matrix Expressions引擎调用的方法。

Mat::setTo

将阵列中所有的或部分的元素设置为指定的值。

C++: Mat& Mat::setTo(const Scalar& s, InputArray mask=noArray())

参数:

s – 把标量赋给阵列并转化到阵列的实际类型。

mask – 与 *this尺寸相同的操作掩码。这是Mat::operator=(const Scalar& s)运算符的一个高级变量。

Mat::reshape

在无需复制数据的前提下改变2D矩阵的形状和通道数或其中之一。

C++: Mat Mat::reshape(int cn, int rows=0) const

参数:

cn – 新的通道数。若cn=0,那么通道数就保持不变。

rows –新的行数。 若rows = 0, 那么行数保持不变。

该方法为*this元素创建新的矩阵头。这新的矩阵头尺寸和通道数或其中之一发生改变,在以下的情况任意组合都是有可能的:

ü 新的矩阵没有新增或减少元素。通常,rows*cols*channels()在转换过程中保持一致。.

ü 无数据的复制。也就是说,这是一个复杂度为 O(1)的操作。通常,如果该操作改变行数或透过其他方式改变元素行索引,那么矩阵必定是连续的。参见Mat::isContinuous()。

例如,有一存储了STL向量的三维点集,你想用3xN的矩阵来完成下面的操作:

std::vector<Point3f> vec;

...

Mat pointMat = Mat(vec). //把向量转化成Mat, 复杂度为O(1)的运算

reshape(1). // 从Nx1的3通道矩阵得出Nx3 的单通道矩阵

//同样是复杂度为O(1)的运算

t(); // 最后转置Nx3 的矩阵

//这个过程要复制所有的元素

Mat::t

转置矩阵。.

C++: MatExpr Mat::t() const

该方法通过矩阵表达式(matrix expression)实现矩阵的转置The method performs matrix transposition by means of matrix expressions. 它并未真正完成了转置但却返回一个临时的可以进一步用在更复杂的矩阵表达式中或赋给一个矩阵的转置矩阵对象:

Mat A1 = A + Mat::eye(A.size(), A.type)*lambda;

Mat C = A1.t()*A1; //计算(A + lambda*I)^t * (A + lamda*I).

Mat::inv

反转矩阵

C++: MatExpr Mat::inv(int method=DECOMP_LU) const

参数:

method – 反转矩阵的方法。有以下几种可能的值:

– DECOMP_LU是 LU 分解一定不能是单数的。

– DECOMP_CHOLESKY 是 Cholesky LLT只适用于对称正矩阵的分解。该类型在处理大的矩阵时的速度是LU的两倍左右。

– DECOMP_SVD是 SVD 分解。如果矩阵是单数或甚至不是2维,函数就会计算伪反转矩阵。

该方法执行矩阵的反转矩阵表达。这意味着该方法返回一个临时矩阵反转对象并可进一步用于更复杂的矩阵表达式的中或分配给一个矩阵。

Mat::mul

执行两个矩阵按元素相乘或这两个矩阵的除法。

C++: MatExpr Mat::mul(InputArray m, double scale=1) const

参数:

m – 与*this具有相同类型和大小的矩阵,或矩阵表达式。

scale – 可选缩放系数。

该方法返回一个用可选的缩放比率编码了每个元素的数组乘法的临时的对象。 注意:这不是一个对应“*”运算符的简单的矩阵乘法。.

例::

Mat C = A.mul(5/B); // 等价于divide(A, B, C, 5)

Mat::cross

计算3元素向量的一个叉乘积。

C++: Mat Mat::cross(InputArray m) const

参数:

–另一个叉乘操作对象。

该方法计算了两个3元素向量的叉乘的积被操作向量必须是3元素浮点型的具有相同形状和尺寸的向量。结果也是一语被操作对象的具有相同形状和大小的浮点型3元素向量。

Mat::dot

计算两向量的点乘。

C++: double Mat::dot(InputArray m) const

参数:

–另一个点积操作对象。

方法计算两个矩阵的点积。如果矩阵不单列或单行的向量,用顶部到底部从左到右扫描次序将它们视为 1 D向量。这些向量必须具有相同的大小和类型。如果矩阵有多个通道,从所有通道得到的点积会被加在一起。

Mat::zeros

返回指定的大小和类型的零数组。

C++: static MatExpr Mat::zeros(int rows, int cols, int type)

C++: static MatExpr Mat::zeros(Size size, int type)

C++: static MatExpr Mat::zeros(int ndims, const int* sizes, int type)

参数

ndims – 数组的维数。

rows–行数。

cols –列数。

size–替代矩阵大小规格Size(cols, rows)的方法。

sizes– 指定数组的形状的整数数组。

type– 创建的矩阵的类型。

该方法返回一个 Matlab 式的零数组初始值设定项。它可以用于快速形成一个常数数组作为函数参数,作为矩阵的表达式或矩阵初始值设定项的一部分。

Mat A;

A = Mat::zeros (3,3,CV_32F);

在上面的示例中,只要A不是 3 x 3浮点矩阵它就会被分配新的矩阵。否则为现有的

矩阵 A填充零。

Mat::ones

返回一个指定的大小和类型的全为1的数组。

C++: static MatExpr Mat::ones(int rows, int cols, int type)

C++: static MatExpr Mat::ones(Size size, int type)

C++: static MatExpr Mat::ones(int ndims, const int* sizes, int type)

参数:

ndims –数组的维数。

rows –行数。.

cols –列数。

size –替代矩阵大小规格Size(cols, rows)的方法。

sizes –指定数组的形状的整数数组。

type –创建的矩阵的类型。

该方法返回一个 Matlab 样式 1 的数组初始值设定项,类似Mat::zeros()。请注意,这种方法中你可以使用任意一个值和Matlab 语法初始化数组如下:

Mat A = Mat::ones (100,100,CV_8U) * 3 ;/ / 使100 x 100 矩阵里充满 3。

上述操作不会形成一个 100 x 100 1 的矩阵,然后乘以 3。相反,它只是记住

缩放因子(在本例中 3)在实际调用矩阵初始值设定项时使用它。

关于图像三通道和单通道的解释 

(一):单通道图,
俗称灰度图,每个像素点只能有有一个值表示颜色,它的像素值在0到255之间,0是黑色,255是白色,中间值是一些不同等级的灰色。(也有3通道的灰度图,3通道灰度图只有一个通道有值,其他两个通道的值都是零)。
(二):三通道图,每个像素点都有3个值表示 ,所以就是3通道。也有4通道的图。例如RGB图片即为三通道图片,RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一。总之,每一个点由三个值表示。
下面用一个简单的例子说明三通道图片和单通道图片的区别
[cpp] view plain copy

  1.  
  2. #include
  3. #includ

e

  • #include
  • #include
  • #include
  •  
  • using namespace std;
  • using namespace cv;
  •  
  • int main()
  • {
  • //载入一张彩色图片并显示
  • Mat srcImage=imread('G:\\Image\\lenaRGB.png',1);
  • namedWindow('Image',WINDOW

_AUTOSIZE);

  • imshow('Image',srcImage);
  •  
  • int nHeight=srcImage.rows;
  • int nWidth=srcImage.cols;
  •  
  • //载入一张灰度图并显示,这里使用同一张图片 只是imread函数的最后一个参数不一样 效果是相同的
  • Mat grayImage=imread('G:\\Image\\lenaRGB.png',0);
  • namedWindow('grayImage',WINDOW_AUTOSIZE);
  • imshow('grayImage',grayImage);
  •  
  • //基本信息
  • cout<<'图像的高度'<<nHeight<<endl;
  • cout<<'图像的宽度'<<nWidth<<endl;
  • cout<<'Image的通道数'<<srcImage.channels()<<endl; //彩色图片的通道数
  • cout<<'grayImage的通道数'<<grayImage.channels()<<endl; //灰度图片的通道数
  •  
  • for(int i=0;i

LI>

  • {
  • for(int j=0;j
  • {
  • srcImage.at(i,j)=0;
  • grayImage.at(i,j)=0;
  • }
  • }
  • namedWindow('彩色图片处理后对应黑色图片',WINDOW_AUTOSIZE);
  • imshow('彩色图片处理后对应黑色图片',srcImage);
  •  
  •  

namedWindow('灰度图片处理后对应黑色图片',WINDOW_AUTOSIZE);

  • imshow('灰度图片处理后对应黑色图片',grayImage);
  •  
  • cvWaitKey(0);
  • cvDestroyWindow('Image');
  • cvDestroyWindow('grayImage');
  • cvDestroyWindow('彩色图片处理后对应黑色图片');
  • cvDestroyWindow('灰度图片处理后对应黑色图片');
  •  
  • return 0;
  •  
  • }


程序结果: 





 
好好体会 运行结果 就知道了。 

发布了171 篇原创文章 · 获赞 271 · 访问量 109万+

猜你喜欢

转载自blog.csdn.net/u011555996/article/details/104262158