Mat 为OpenCV中的核心数据结构,主要负责图像数据的保存,Mat创建方法有很多种
Mat构造函数
可以使用Mat构造函数,创建Mat,Mat构造函数有多种形式的参数,来满足要求
Mat 主要构造函数 | Description |
Mat() | 默认构造函数 |
Mat(int rows, int cols, int type) | 矩阵的行和列或者是图片的高和宽 type为存储的数据格式为单通道还是多通道,以及是char 还是unshort等,其type参数为CV_8UC1, ..., CV_64FC4 等最多为4个通道,其中UC为数据类型为unsigned char SC为 char, 16UC为unsigned short , 16SC为short, 32SC为 int, 32FC为32位浮点型, 64FC为64位浮点型 |
Mat(Size size, int type) | Size位创建一个二维矩阵,其参数为Size(cols, rows),type同上 |
Mat(int rows, int cols, int type, const Scalar& s) | 带初始化的Mat创建,其中Scalar为初始化Mat中的值,为OpenCV的比较重要的数据结构,最多具有4个元素值,常用来传递像素值,例如RGB,其表达式可以为Scalar(a, b, c). 如果是单通道可以为Scalar(a) 如果数双通道为Scalar(a, b) |
Mat(Size size, int type, const Scalar& s) | 带初始化的Mat创建 |
Mat(int ndims, const int* sizes, int type) | ndims为Mat维度 sizes为个维度大小的数组 |
Mat(const std::vector<int>& sizes, int type) | verctor形式参数初始化,其中sizes为各维度大小, |
Mat(int ndims, const int* sizes, int type, const Scalar& s) | 带初始化的Mat创建 |
Mat(const std::vector<int>& sizes, int type, const Scalar& s) | 带初始化的Mat创建 |
Mat(const Mat& m) | 直接从已有的一个Mat中进行创建一个新的Mat 注意只创建Mat的头,其数据矩阵还是指向的同一块 |
Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP) | 带数据初始化Mat创建,通常用于将一块数据转换成Mat场景中. step:Number of bytes each matrix row occupies 每个矩阵一行占用的字节数,实际值为cols*elemSize() 其中elemSize()为单个值占用的字节数*通道数,默认为AUTO_STEP |
Mat(Size size, int type, void* data, size_t step=AUTO_STEP) | 带初始化的Mat创建,同上 |
Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0) | 带初始化的Mat创建,step为本像素点与下一个像素点间隔几个数据,即该数据通道是几个通道, |
Mat(const std::vector<int>& sizes, int type, void* data, const size_t* steps=0); | 带初始化的Mat创建,同上 |
Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all()) | Mat初始化, Range(int _start, int _end),表示的是其行或列的开始以及结束位置,可以选择性的截取m中的一部分数据,其数据部分还是同一块,不会产生新的数据copy,只是相当于取指针 |
Mat(const Mat& m, const Rect& roi) | Mat初始化,和 Range类似只是取其中一部分 |
Mat(const Mat& m, const Range* ranges) | Mat初始化,只是规定行的起始位置,没有规定列的起始位置 |
Mat(const Mat& m, const std::vector<Range>& ranges) | Mat初始化,只是规定行的起始位置,没有规定列的起始位置 |
用例大概如下:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
int sz[3] = { 2,2,2 };
Mat L(3, sz, CV_8UC1, Scalar::all(12));
Mat mtx();
Mat test1(640, 480, CV_8UC1);
Mat test2(Size(640, 480), CV_8UC1);
Mat M(2, 2, CV_8UC3, Scalar(110, 0, 255));
cout << "M= " << endl << " " << M << endl << endl;
Mat test3(Size(2, 2), CV_8UC3, Scalar(120, 0, 255));
cout << "test3= " << endl << " " << test3 << endl << endl;
int sz2[2] = { 5,5};
Mat test4(2, sz2, CV_8UC3, Scalar(12));
cout << "test4= " << endl << " " << test4 << endl << endl;
Mat test5(test4);
cout << "test5= " << endl << " " << test5 << endl << endl;
Mat test6(3,3, CV_8UC3, test5.data);
cout << "test6= " << endl << " " << test6 << endl << endl;
Mat test11(4, 4, CV_8UC3, test5.data, 12);
cout << "test11= " << endl << " " << test11 << endl << endl;
Mat test7(2, sz2, CV_8UC3, test5.data);
cout << "test7= " << endl << " " << test7 << endl << endl;
const size_t steps = 3;
Mat test12(2, sz2, CV_8UC3, test5.data, &steps);
cout << "test12= " << endl << " " << test12<< endl << endl;
Mat test8(test7, Range(0,2), Range(0, 2));
cout << "test8= " << endl << " " << test8 << endl << endl;
Mat test9(test7, Rect(0,0, 3, 3));
cout << "test8= " << endl << " " << test9 << endl << endl;
Mat test10(test7, Range(0, 2));
cout << "test10= " << endl << " " << test10 << endl << endl;
}
根据已有Mat创建Mat
利用已有mat创建一个新的Mat(注意有些是深度copy,有些只是创建了新的矩阵头,没有创建数据区域,后面再介绍),涉及到的Mat类中的方法有:
Method | Description |
Mat row(int y) | 取第y行作为新矩阵 |
Mat col(int x) | 取第x列作为新矩阵 |
Mat rowRange(int startrow, int endrow) | 以starrow行起始点(0开始),到endrow行结束,产生新的Mat(不产生新的数据区域) |
Mat rowRange(const Range& r) | 同上,只是参数不同 |
Mat colRange(int startcol, int endcol) | 以startcol 列为起点(0开始),到endcol列结束,产生新的Mat(不产生新的数据区域) |
Mat colRange(const Range& r) | 同上,只是参数不同 |
Mat diag(int d=0) | 当d = 0时,取对角线 当d>0时,取对角线上部分 当d<0时,取对角线下部分 例如: Mat m = (Mat_<int>(3,3) << d0 = |
static Mat diag(const Mat& d) | d代表的就是对角线部分 即产生新的对角矩阵 |
Mat clone() | 克隆一个新的Mat |
void copyTo( OutputArray m ) | copy产生一个新的copy, 深度copy,会产生新的数据区域 |
void copyTo( OutputArray m, InputArray mask ) | opy产生一个新的copy, 深度copy,其中mask中为0的不copy, 不为零的进行copy |
上述这些操作只是产生新的mat,有些方法为深度copy,但是都没有修改Mat的type 以及shape
使用用例如下:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
int sz[3] = { 2,2,2 };
Mat L(3, sz, CV_8UC1, Scalar::all(12));
Mat mtx();
Mat test1(640, 480, CV_8UC1);
Mat test2(Size(640, 480), CV_8UC1);
Mat M(2, 2, CV_8UC3, Scalar(110, 0, 255));
cout << "M= " << endl << " " << M << endl << endl;
Mat test3(Size(2, 2), CV_8UC3, Scalar(120, 0, 255));
cout << "test3= " << endl << " " << test3 << endl << endl;
int sz2[2] = { 5,5};
Mat test4(2, sz2, CV_8UC3, Scalar(12));
cout << "test4= " << endl << " " << test4 << endl << endl;
Mat test5= test4.rowRange(1, 3);
cout << "test5= " << endl << " " << test5 << endl << endl;
Mat test6 = test4.rowRange(Range(0, 4));
cout << "test6= " << endl << " " << test6 << endl << endl;
Mat test7 = test4.colRange(0, 2);
cout << "test7= " << endl << " " << test7 << endl << endl;
Mat test8 = test4.colRange(Range(0, 3));
cout << "test8= " << endl << " " << test8 << endl << endl;
Mat test9 = test4.diag(0);
cout << "test9= " << endl << " " << test9 << endl << endl;
Mat test10 = test4.diag(1);
cout << "test10= " << endl << " " << test10 << endl << endl;
Mat test11 = test4.diag(-1);
cout << "test11= " << endl << " " << test11 << endl << endl;
Mat test12;
//Mat test13(2, sz2, CV_8UC1, Scalar(12));
test12 = test4.clone();
cout << "test12= " << endl << " " << test12 << endl << endl;
Mat test13;
test4.copyTo(test13);
cout << "test13= " << endl << " " << test13 << endl << endl;
Mat test14;
test4.copyTo(test14, test4);
cout << "test14= " << endl << " " << test14 << endl << endl;
}
产生新的Mat并修改旧Mat的type和shape
以下这些操作会根据旧的Mat生成新Mat,并会修改Mat的数据type以及shape
Method | Description |
void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) | 转换Mat的数据类型, OutputArray m:为转换后的矩阵 int rtype:为转换后的矩阵数据类型,注意通道数通最好一样,如果转换类型为负面的,转换后的类型和转换前类型一样 double alpha和double beta为防止转换后数据越界,而对数据进行缩放和偏移,其中alpha为缩放或者扩大因子,beta为偏移值,转换后结果起公式为: \f[m(x,y) = saturate \_ cast<rType>( \alpha (*this)(x,y) + \beta )\f] |
Mat reshape(int cn, int rows=0) | 重新设置Mat shape,设置前后的col * row * channel相等,否则转换失败或者和旧的一样 |
Mat reshape(int cn, int newndims, const int* newsz) | 同上,只是参数不一样 |
Mat reshape(int cn, const std::vector<int>& newshape) | 同上,只是参数不一样 |
MatExpr t() | 矩阵的转置 |
MatExpr inv(int method=DECOMP_LU) | 矩阵的逆 |
void resize(size_t sz) | 修改Row大小 |
void resize(size_t sz, const Scalar& s) | 同上 |
使用用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
int sz[3] = { 2,2,2 };
Mat L(3, sz, CV_8UC1, Scalar::all(12));
Mat mtx();
Mat test1(640, 480, CV_8UC1);
Mat test2(Size(640, 480), CV_8UC1);
Mat M(2, 2, CV_8UC3, Scalar(110, 0, 255));
cout << "M= " << endl << " " << M << endl << endl;
Mat test3(Size(2, 2), CV_8UC3, Scalar(120, 0, 255));
cout << "test3= " << endl << " " << test3 << endl << endl;
int sz2[2] = { 5,5};
Mat test4(2, sz2, CV_8UC3, Scalar(12));
cout << "test4= " << endl << " " << test4 << endl << endl;
Mat test5;
test4.convertTo(test5, CV_64FC3, 0.1, 1);
cout << "test5= " << endl << " " << test5 << endl << endl;
printf("test5 col: %d\n", test5.cols);
Mat test6;
test6 = test4.reshape(1);
cout << "test6= " << endl << " " << test6 << endl << endl;
printf("test6 col: %d\n", test6.cols);
Mat test7;
int sz3[2] = { 25,3 };
test7 = test4.reshape(1, 2, sz3);
cout << "test7= " << endl << " " << test7 << endl << endl;
Mat test8 = test7.col(0);
cout << "test8= " << endl << " " << test8 << endl << endl;
Mat test9 = test7.col(1);
cout << "test9= " << endl << " " << test9 << endl << endl;
Mat test10 = test7.col(2);
cout << "test10= " << endl << " " << test10 << endl << endl;
Mat test11 = test7.t();
cout << "test11= " << endl << " " << test11 << endl << endl;
//test7.convertTo(test7, CV_8UC1, 10);
Mat M2(5, 5, CV_32FC1, Scalar(110));
cout << "M2= " << endl << " " << M2 << endl << endl;
Mat test12 = M2.inv();
cout << "test12= " << endl << " " << test12 << endl << endl;
Mat test22;
test22.create(16, 16, CV_8UC1);
printf("Test22 col:%d, row:%d\n", test22.cols, test22.rows);
//Mat test2;
test22.resize(8);
printf("Test22 col:%d, row:%d\n", test22.cols, test22.rows);
test22.resize(4,Scalar::all(11));
printf("Test22 col:%d, row:%d\n", test22.cols, test22.rows);
cout << "test22= " << endl << " " << test22 << endl << endl;
}
create、zeros、ones、eye方法
Mat类还支持zeros,eye等类似python的创建矩阵方法
Method | Description |
void create(int rows, int cols, int type) | 创建Mat. 1:如果当前mat shape和channel和要创建的shape和channel一样,则立即返回,否则就释放旧的Mat继续下一步. 2:初始化新创建mat 头 3:申请新的data数据,大小为total()\*elemSize() 4:申请新的counter,并设为1 |
void create(Size size, int type) | 入参不一样,其他一样 |
void create(int ndims, const int* sizes, int type) | 入参不一样,其他一样 |
void create(const std::vector<int>& sizes, int type) | 入参不一样,其他一样 |
static MatExpr zeros(int rows, int cols, int type) | 将Mat 初始化为零 |
static MatExpr zeros(Size size, int type) | 同上,入参不同 |
static MatExpr zeros(int ndims, const int* sz, int type) | 同上,入参不同 |
static MatExpr ones(int rows, int cols, int type) | 初始化全部为1 |
static MatExpr ones(Size size, int type) | 初始化全部为1 |
static MatExpr ones(int ndims, const int* sz, int type) | 初始化全部为1 |
static MatExpr eye(int rows, int cols, int type) | 生成眼图矩阵 |
static MatExpr eye(Size size, int type) | 生成眼图矩阵 |
使用用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat test1;
test1.create(320, 240, CV_8UC1);
printf("Test1 col:%d, row:%d\n", test1.cols, test1.rows);
Mat test2;
test2.create(Size(320,240), CV_8UC1);
printf("Test2 col:%d, row:%d\n", test2.cols, test2.rows);
int sz[2] = {15, 15 };
Mat test3;
test3.create(2, sz, CV_8UC1);
printf("Test3 col:%d, row:%d\n", test3.cols, test3.rows);
vector<int> mat_size;
mat_size.push_back(320);
mat_size.push_back(240);
Mat test4;
test4.create(mat_size, CV_8UC1);
printf("Test4 col:%d, row:%d\n", test4.cols, test4.rows);
Mat test5(2, sz, CV_8UC1, Scalar::all(12));
cout << "test5= " << endl << " " << test5 << endl << endl;
test5= Mat::zeros(2, sz, CV_8UC1);
cout << "zero test5= " << endl << " " << test5 << endl << endl;
Mat test6;
test6 = Mat::zeros(3,4, CV_8UC1);
cout << "zero test6= " << endl << " " << test6 << endl << endl;
Mat test7 = Mat::zeros(5,6, CV_8UC1);
cout << "zero test7= " << endl << " " << test7 << endl << endl;
Mat test8 = Mat::ones(5, 6, CV_8UC1);
cout << "one test8= " << endl << " " << test8 << endl << endl;
Mat test9 = Mat::ones(Size(4, 7), CV_8UC1);
cout << "one test9= " << endl << " " << test9 << endl << endl;
Mat test10 = Mat::ones(2, sz, CV_8UC1);
cout << "one test10= " << endl << " " << test10 << endl << endl;
Mat test11 = Mat::eye(5, 6, CV_8UC1);
cout << "eye test11= " << endl << " " << test11 << endl << endl;
Mat test12 = Mat::eye(Size(7, 7), CV_8UC1);
cout << "eye test12= " << endl << " " << test12 << endl << endl;
}
Mat类中获取系列操作
Mat类中获取一系列参数操作,用于获取Mat的row,col等其他参数
Method | Description |
size_t elemSize() | 获取矩阵每个点的元素大小,起大小与数据类型和通道有关 比如 Mat 类型为CV_16SC3, 则元素大小为 3 *sizeof(short) = 6 |
size_t elemSize1() | 获取矩阵每个点的元素大小(注意忽略通道,只和数据类型有关) 比如Mat 类型为CV_16SC3,elemSize1为2 |
int type() | 获取Mat type |
int depth() | 获取Mat element depth(我的个人理解获取到的是Mat中的数据类型) 比如 18位 signed element array,返回值为CV_16S 其返回值共有: - CV_8U = 0 - 8-bit unsigned integers ( 0..255 ) - CV_USRTYPE1=7 |
int channels() | 获取mat的 通道数 |
size_t step1(int i=0) | 每一维的通道数,具体详细讲解可以查看 |
bool empty() | mat是否为空,如果 Mat::total()为0,或者 Mat::data == null 则返回true |
size_t total() | Mat的像素总数,等于row * col,不包括通道数 |
size_t total(int startDim, int endDim=INT_MAX) | 返回特定的维度的total number, startDim <= dim < endDim |
int dims | Mat中的变量,可以用于获取到Mat维度 |
int rows, cols | Mat中的变量,Mat的行和列 |
uchar* data | 指向data的指针 |
MatSize size | MatSize结构,可以获取到Mat的row,col,dim等 Mat test; test.size()返回的结构体为Size
|
MatStep step | 详解可以看: |
int flags | 包括几个标志位: - the magic signature - submatrix:第15位: |
使用用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat test2;
test2.create(Size(320,240), CV_8UC3);
printf("Test2 col:%d, row:%d,dims:%d\n", test2.cols, test2.rows, test2.dims);
printf("Test2 elemsize:%d, elemsize1:%d\n", test2.elemSize(), test2.elemSize1());
printf("Test2 type:%d\n", test2.type());
printf("Test2 depth:%d\n", test2.depth());
printf("Test2 channels: %d\n", test2.channels());
if (test2.empty())
{
printf("Test2 is empty\n");
}
else {
printf("Test2 is not empty\n");
}
printf("Test2 total:%d\n", test2.total());
printf("Test2 size:%d\n", *test2.size.p);
printf("Test2 width:%d\n", test2.size().width);
printf("Test2 height:%d\n", test2.size().height);
printf("Test2 dim:%d\n", test2.size.dims());
printf("Test2 height:%d\n", test2.size[0]);
printf("Test2 width:%d\n", test2.size[1]);
printf("Test2 step[0]:%d\n", test2.step[0]);
printf("Test2 step[1]:%d\n", test2.step[1]);
}
Step
step概念是从Mat数组占用的空间角度来讲,在一个N维的矩阵运算中如果要取其某个元素的地址,其计算公式为:
其中(i0, ii, … , iN d -1)为N维矩阵中,每个维度下的索引,如果是二维场景则:
mtx.step[0]为行相当于矩阵的col * channels* sizeof(data type),相当于在行中偏移的位置, 而mtx.step[1]为列 则为 channels* sizeof(data type) 相当于在列中的位置,Step概念就相当于在该维度下索引加1所需要移动的内存偏移量。
Step就脱离Mat中的shape概念,直接为内存角度考虑。
isContinuous函数
该函数表明Mat中的数据是否连续存储,连续存储是返回true,否则返回false。
Mat的数据部分存储是按照行进行存储的,相同行内的数据是连续存储,行与行之间的存储是否有间隙,可以通过isContinuous函数来判断。对于1*1或1*N矩阵总是连续的,一般使用Mat::create创建的矩阵总是连续的,而如果使用Mat::col,Mat::diag等提取的矩阵一部分,或者外部分配的数据构造矩阵头,则此类矩阵可能不再连续存储。
按照opencv中的解释说明,连续行标志存储是存储在Mat :: flags字段,理论上可以判断是否连续可以使用如下:
bool myCheckMatContinuity(const Mat& m)
{
//return (m.flags & Mat::CONTINUOUS_FLAG) != 0;
return m.rows == 1 || m.step == m.cols*m.elemSize();
}
在大部分对Mat数据进行遍历时都需要使用isContinuous,以及在对元素操作(例如算术和逻辑操作,数学函数,alpha混合,颜色空间变好等)不依赖于图像集合。因此,如果所有的输入和输出数组都是连续的,那么函数可以将它们处理非常长的单行向量。下面官方例子说明了如何实现一个alpha混合函数:
template<typename T>
void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst)
{
const float alpha_scale = (float)std::numeric_limits<T>::max(),
inv_scale = 1.f/alpha_scale;
CV_Assert( src1.type() == src2.type() &&
src1.type() == CV_MAKETYPE(traits::Depth<T>::value, 4) &&
src1.size() == src2.size());
Size size = src1.size();
dst.create(size, src1.type());
// here is the idiom: check the arrays for continuity and,
// if this is the case,
// treat the arrays as 1D vectors
if( src1.isContinuous() && src2.isContinuous() && dst.isContinuous() )
{
size.width *= size.height;
size.height = 1;
}
size.width *= 4;
for( int i = 0; i < size.height; i++ )
{
// when the arrays are continuous,
// the outer loop is executed only once
const T* ptr1 = src1.ptr<T>(i);
const T* ptr2 = src2.ptr<T>(i);
T* dptr = dst.ptr<T>(i);
for( int j = 0; j < size.width; j += 4 )
{
float alpha = ptr1[j+3]*inv_scale, beta = ptr2[j+3]*inv_scale;
dptr[j] = saturate_cast<T>(ptr1[j]*alpha + ptr2[j]*beta);
dptr[j+1] = saturate_cast<T>(ptr1[j+1]*alpha + ptr2[j+1]*beta);
dptr[j+2] = saturate_cast<T>(ptr1[j+2]*alpha + ptr2[j+2]*beta);
dptr[j+3] = saturate_cast<T>((1 - (1-alpha)*(1-beta))*alpha_scale);
}
}
}
使用用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg");
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
Rect rect(1, 1,100,100);
Mat crop_img = src(rect);
imshow("crop_img", crop_img);
waitKey(0);
printf("Row:%d, Col:%d\n", crop_img.rows, crop_img.cols);
printf("---crop_img.isContinuous=%d\n", crop_img.isContinuous());
Mat crop_img2 = crop_img.clone();
printf("---crop_img2.isContinuous=%d\n", crop_img2.isContinuous());
}
运行结果:
setTo方法
setTo函数为Mat类设置数据值的方法,主要是将所有或者部分的mat数据设置为所需要的值,函数原型为:
Mat& setTo(InputArray value, InputArray mask=noArray());
主要功能就是按照mask 非零的部分mat的数据值设置为所需要的值
如果不传递mask,将mask是一个与原图尺寸大小一致的且元素值全为非0的矩阵,因此不加mask的时候会将原矩阵的像素值全部赋值为value.
使用用例如下:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src(4, 4, CV_8UC1, Scalar(23));
cout << "src= " << endl << " " << src << endl << endl;
Mat mask(4, 4, CV_8UC1, Scalar(0));
src.setTo(100, mask);
cout << "src= " << endl << " " << src << endl << endl;
Mat mask2(4, 4, CV_8UC1, Scalar(1));
src.setTo(100, mask2);
cout << "src= " << endl << " " << src << endl << endl;
src.setTo(20, src<200);
cout << "src= " << endl << " " << src << endl << endl;
}
运行结果:
ptr指针
opencv 提供了丰富的获取Mat数据接口,主要是通过ptr指针获取到每行的首地址来进行对数据的访问,因为每行之间如果不是连续存储模式中间会存在空洞,故对Mat数据访问时一般都是按照行进行访问,ptr相关接口如下
Method | Description |
uchar* ptr(int i0=0) | 获取到Mat中数据矩阵的第i0行首地址,此时数据类型为uchar |
const uchar* ptr(int i0=0) | 返回的数据类型为const uchar,即只能读取不能修改值 |
uchar* ptr(int row, int col) | 针对Mat二维空间,返回第row行和第cok列的元素地址 |
const uchar* ptr(int row, int col) | 同上,返回地址不能修改其值 |
uchar* ptr(int i0, int i1, int i2) | Mat为三维空间,返回其三维空间某个元素地址 |
const uchar* ptr(int i0, int i1, int i2) | 同上,返回的地址 其值不能修改 |
uchar* ptr(const int* idx) | 各个维度中的位置用数组来表示 |
const uchar* ptr(const int* idx) | 各个维度中的位置用数组来表示 |
template<int n> uchar* ptr(const Vec<int, n>& idx) | 各个维度中的位置用vector来表示 |
template<int n> const uchar* ptr(const Vec<int, n>& idx) | 各个维度中的位置用vector来表示 |
template<typename _Tp> _Tp* ptr(int i0=0) | 返回第id0行 首地址吗,其数据类型和Tp相关 |
template<typename _Tp> const _Tp* ptr(int i0=0) | 和uchar* ptr(int i0=0)类似,差别在与Tp数据类型 |
template<typename _Tp> _Tp* ptr(int row, int col) | 和uchar* ptrint row, int col),差别在与Tp数据类型 |
template<typename _Tp> const _Tp* ptr(int row, int col) | 和const uchar*ptr(int row, int col),差别在与Tp数据类型 |
template<typename _Tp> _Tp* ptr(int i0, int i1, int i2) | 和 uchar* ptr(int i0, int i1, int i2),差别在与Tp数据类型 |
template<typename _Tp> const _Tp* ptr(int i0, int i1, int i2) | 和 uchar* ptr(int i0, int i1, int i2),差别在与Tp数据类型 |
template<typename _Tp> _Tp* ptr(const int* idx) | 和uchar* ptr(const int* idx),差别在与Tp数据类型 |
template<typename _Tp> const _Tp* ptr(const int* idx) | 和const uchar* ptr(const int* idx),差别在与Tp数据类型 |
template<typename _Tp, int n> _Tp* ptr(const Vec<int, n>& idx) | 和uchar* ptr(const Vec<int, n>& idx),差别在与Tp数据类型 |
template<typename _Tp, int n> const _Tp* ptr(const Vec<int, n>& idx) | 和const uchar* ptr(const Vec<int, n>& idx),差别在与Tp数据类型 |
ptr机制单通道机制访问
单通道 uchar类型的Mat数据访问例子如下:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg", IMREAD_GRAYSCALE);
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
{
uchar *RowPtr = src.ptr(RowIndex);
for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
{
printf("Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, *(RowPtr + ColIndex));
}
}
}
ptr多通道机制访问
ptr多通道机制访问也有两种:
第一种:采用像c指针一样,开发人员根据需要自己来算偏移,需要考虑多通道问题
第二种:使用vec类型指针,ptr模板支持vec数据类型
三通道 BGR Mat数据进行遍历用例1
三通道BGR ptr访问方法,开发人员开做多通道偏移:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg");
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
{
uchar *RowPtr = src.ptr(RowIndex);
for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
{
printf("Row:%d, Col %d, B: value:%d\n", RowIndex, ColIndex, *(RowPtr + 3* ColIndex));
printf("Row:%d, Col %d, R: value:%d\n", RowIndex, ColIndex, *(RowPtr + 3 * ColIndex + 1));
printf("Row:%d, Col %d, G: value:%d\n", RowIndex, ColIndex, *(RowPtr + 3 * ColIndex + 2));
}
}
}
三通道 BGR Mat数据进行遍历用例1
三通道BGR ptr使用模板访问方法,其模板数据类型为vec:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg");
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
{
cv::Vec3b * RowPtr = src.ptr<cv::Vec3b>(RowIndex);
for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
{
printf("Row:%d, Col %d, B: value:%d\n", RowIndex, ColIndex, RowPtr[ColIndex][0]);
printf("Row:%d, Col %d, R: value:%d\n", RowIndex, ColIndex, RowPtr[ColIndex][1]);
printf("Row:%d, Col %d, G: value:%d\n", RowIndex, ColIndex, RowPtr[ColIndex][2]);
}
}
}
Mat类中的at机制
访问Mat数据时,为了防止使用ptr机制进行指针偏移错误,Mat提供了at机制,用于对Mat数据中的某个元素进行访问,at机制消除了对指针操作的偏移出现的问题,也为不习惯使用指针的开发人员提供了极大遍历,其主要接口如下:
Method | Description |
template<typename _Tp> _Tp& at(int i0=0) | Mat数组为一维场景下,对某个元素进行访问,可修改其值 |
template<typename _Tp> const _Tp& at(int i0=0) | 同上,不可修改其值 |
template<typename _Tp> _Tp& at(int row, int col) | Mat数组为二维场景下,对某个元素进行访问,可修改其值 |
template<typename _Tp> const _Tp& at(int row, int col) | 同上,不可修改其值 |
template<typename _Tp> _Tp& at(int i0, int i1, int i2) | Mat数组为三维场景下,对某个元素进行方案,可修改其值 |
template<typename _Tp> const _Tp& at(int i0, int i1, int i2) | 同上,不可修改其值 |
template<typename _Tp> _Tp& at(const int* idx) | 各个维度下的位置用数组来表示,可修改元素值 |
template<typename _Tp> const _Tp& at(const int* idx) | 同上,不可修改元素值 |
template<typename _Tp, int n> _Tp& at(const Vec<int, n>& idx) | 各个维度的位置用vector来表示,可修改元素值 |
template<typename _Tp, int n> const _Tp& at(const Vec<int, n>& idx) | 同上,不可修改元素值 |
template<typename _Tp> _Tp& at(Point pt) | Mat二维场景下,使用Point参数传递 |
template<typename _Tp> const _Tp& at(Point pt) | 同上,不可修改元素值 |
根据Opencv源代码中的说明,Mat中的数据类型对应的其Tp类型
- - If matrix is of type `CV_8U` then use `Mat.at<uchar>(y,x)`.
- - If matrix is of type `CV_8S` then use `Mat.at<schar>(y,x)`.
- - If matrix is of type `CV_16U` then use `Mat.at<ushort>(y,x)`.
- - If matrix is of type `CV_16S` then use `Mat.at<short>(y,x)`.
- - If matrix is of type `CV_32S` then use `Mat.at<int>(y,x)`.
- - If matrix is of type `CV_32F` then use `Mat.at<float>(y,x)`.
- - If matrix is of type `CV_64F` then use `Mat.at<double>(y,x)`.
at机制单通道访问
单通道uchar 类型Mat变量访问用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg", IMREAD_GRAYSCALE);
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
{
for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
{
printf("Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<unsigned char>(RowIndex, ColIndex));
}
}
}
利用at机制修改其Mat中的数据值:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{;
Mat Src(10, 10, CV_8UC1);
for (int RowIndex = 0; RowIndex < Src.rows; RowIndex++)
{
for (int ColIndex = 0; ColIndex < Src.cols; ColIndex++)
{
Src.at<unsigned char>(RowIndex, ColIndex)= ColIndex;
}
}
cout << "Src = " << endl << Src << endl;
}
at机制多通道访问
如果Mat矩阵为多通道数据,使用数据方式有两种:
第一种是在遍历是,填写列时由开发人员自己根据一个元素占用的内存大小进行偏移,其计算方法为col*channel通道数,该方法的缺点在与需要开发人员自己算通道内偏移,比较麻烦
第二种at中的模板数据格式使用相应的vec格式,有点访问遍历使用方便
三通道 BGR Mat数据进行遍历用例1
第一个三通道 BGR Mat数据进行遍历用例,显示其通道数有开发人员自己做偏移:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg");
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
{
uchar *RowPtr = src.ptr(RowIndex);
for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
{
printf("B Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<unsigned char>(RowIndex, 3*ColIndex));
printf("G Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<unsigned char>(RowIndex, 3 * ColIndex + 1));
printf("R Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<unsigned char>(RowIndex, 3 * ColIndex + 2));
}
}
}
三通道 BGR Mat数据进行遍历用例2
at模板中使用vec数据结构用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg");
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
{
uchar *RowPtr = src.ptr(RowIndex);
for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
{
printf("B Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<cv::Vec3b>(RowIndex, ColIndex)[0]);
printf("G Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<cv::Vec3b>(RowIndex, ColIndex)[1]);
printf("R Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<cv::Vec3b>(RowIndex, ColIndex)[2]);
}
}
}
MatIterator迭代器
Mat提供了迭代器遍历接口,对迭代器熟悉的开发人员可以方便使用,主要接口如下:
Method | Description |
template<typename _Tp> MatIterator_<_Tp> begin() | 获取Mat初始迭代器 |
template<typename _Tp> MatConstIterator_<_Tp> begin() | 同上,不可修改其内容 |
template<typename _Tp> MatIterator_<_Tp> end() | 获取Mat最后迭代器 |
template<typename _Tp> MatConstIterator_<_Tp> end() | 同上,不可修改其内容 |
单通道元素迭代
单通道uchar Mat遍历使用用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg", IMREAD_GRAYSCALE);
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
Mat_<uchar>::iterator SrcStart = src.begin<uchar>();
Mat_<uchar>::iterator SrcEnd = src.end<uchar>();
int number = 0;
for (;SrcStart != SrcEnd; ++SrcStart)
{
uchar data = *SrcStart;
printf("Image index:%d, value:%d\n", number, data);
number++;
}
printf("Image count:%d\n", number);
}
多通道元素迭代
如果是其他多通道数据 ,其迭代数据结构可以使用如下opencv 定义好的数据:
typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;
typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;
typedef Vec<ushort, 2> Vec2w;
typedef Vec<ushort, 3> Vec3w;
typedef Vec<ushort, 4> Vec4w;
typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<int, 6> Vec6i;
typedef Vec<int, 8> Vec8i;
typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;
typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;
三通道BGR迭代数据访问用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg");
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
Mat_<cv::Vec3b>::iterator SrcStart = src.begin<cv::Vec3b>();
Mat_<cv::Vec3b>::iterator SrcEnd = src.end<cv::Vec3b>();
int number = 0;
for (;SrcStart != SrcEnd; ++SrcStart)
{
printf("Image index:%d, B value:%d\n", number, (*SrcStart)[0]);
printf("Image index:%d, G value:%d\n", number, (*SrcStart)[1]);
printf("Image index:%d, R value:%d\n", number, (*SrcStart)[2]);
number++;
}
printf("Image count:%d\n", number);
}
MatIterator迭代器为Mat遍历访问的比较重要方式之一。
官方给出了对RGBA的 alpha blending处理办法:
template<typename T>
void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst)
{
typedef Vec<T, 4> VT;
const float alpha_scale = (float)std::numeric_limits<T>::max(),
inv_scale = 1.f/alpha_scale;
CV_Assert( src1.type() == src2.type() &&
src1.type() == traits::Type<VT>::value &&
src1.size() == src2.size());
Size size = src1.size();
dst.create(size, src1.type());
MatConstIterator_<VT> it1 = src1.begin<VT>(), it1_end = src1.end<VT>();
MatConstIterator_<VT> it2 = src2.begin<VT>();
MatIterator_<VT> dst_it = dst.begin<VT>();
for( ; it1 != it1_end; ++it1, ++it2, ++dst_it )
{
VT pix1 = *it1, pix2 = *it2;
float alpha = pix1[3]*inv_scale, beta = pix2[3]*inv_scale;
*dst_it = VT(saturate_cast<T>(pix1[0]*alpha + pix2[0]*beta),
saturate_cast<T>(pix1[1]*alpha + pix2[1]*beta),
saturate_cast<T>(pix1[2]*alpha + pix2[2]*beta),
saturate_cast<T>((1 - (1-alpha)*(1-beta))*alpha_scale));
}
}
上述迭代器为Mat内的元素迭代,还有另外一种迭代器NAryMatIterator,该迭代器迭代的是Mat.
利用data进行Mat数据遍历
如果Mat中的数据是连续存储的,还有另外一种不是很常见的方法对Mat中 的数据进行遍历,利用Mat中的data变量,指向的数数据区域的的指针进行遍历访问。
data数据偏移
单通道用例如下:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg", IMREAD_GRAYSCALE);
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
uchar * Data = src.data;
for (int RowIndex=0; RowIndex < src.rows; ++RowIndex)
for(int ColIndex=0;ColIndex< src.cols; ++ColIndex)
{
printf("Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols + ColIndex));
}
}
三通道BGR用例如下:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg");
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
uchar * Data = src.data;
for (int RowIndex=0; RowIndex < src.rows; ++RowIndex)
for(int ColIndex=0;ColIndex< src.cols; ++ColIndex)
{
printf("B : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols*3 + ColIndex));
printf("G : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols * 3 + ColIndex + 1));
printf("R : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols * 3 + ColIndex + 2));
}
}
data偏移使用step
data变量实际上还有另外一种用法,实际上可以使用step,前面有讲到step的实际意义,使用起来更加方便
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src = imread("len_top.jpg");
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
uchar * Data = src.data;
for (int RowIndex = 0; RowIndex < src.rows; ++RowIndex)
for (int ColIndex = 0; ColIndex< src.cols; ++ColIndex)
{
printf("B : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.step[0] + ColIndex * src.step[1]));
printf("G : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.step[0] + ColIndex * src.step[1] + 1));
printf("R : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.step[0] + ColIndex * src.step[1] + 2));
}
}
Mat 数据遍历总结
对Mat中的数据遍历进行操作主要有几个方法:
1:利用ptr进行操作
2:利用at进行遍历操作
3:利用Mat迭代器
4:如果是连续存储可以直接利用data进行遍历访问
5:利用forEach方法,该方法是利用并行方法,所以是最快的,它是是通过forEach的函数Operator来实现
forEach
forEach方法充分应用了整个计算机的硬件特性,利用并行原理最大发挥了CPU和核数,是指能够发挥最大性能,其API如下:
Method | Description |
template<typename _Tp, typename Functor> void forEach(const Functor& operation) | operation函数由开发者来实现具体操作 |
template<typename _Tp, typename Functor> void forEach(const Functor& operation) const | 同上 |
forEach的实现主要是通过 operation来实现具体每个像素的计算
单通道将每个元素值修改为1的简单例子:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
typedef uint8_t Pixel;
void complicated(Pixel &pixel)
{
pixel = 1;
}
// Parallel execution with function object.
struct Operator
{
void operator ()(Pixel &pixel, const int * position) const
{
// Perform a simple threshold operation
complicated(pixel);
}
};
void main()
{
Mat src = imread("len_top.jpg", IMREAD_GRAYSCALE);
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
src.forEach<Pixel>(Operator());
uchar * Data = src.data;
for (int RowIndex=0; RowIndex < src.rows; ++RowIndex)
for(int ColIndex=0;ColIndex< src.cols; ++ColIndex)
{
printf("Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols + ColIndex));
}
}
三通道BRG将B通道全部修改为1用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
typedef Point3_<uint8_t> Pixel;
void complicated(Pixel &pixel)
{
pixel.x = 1;
}
// Parallel execution with function object.
struct Operator
{
void operator ()(Pixel &pixel, const int * position) const
{
// Perform a simple threshold operation
complicated(pixel);
}
};
void main()
{
Mat src = imread("len_top.jpg");
imshow("Src", src);
waitKey(0);
printf("---src.isContinuous=%d\n", src.isContinuous());
printf("Row:%d, Col:%d\n", src.rows, src.cols);
printf("Type:%d\n", src.type());
printf("CV_8UC3:%d\n", CV_8UC3);
printf("CV_8UC1:%d\n", CV_8UC1);
src.forEach<Pixel>(Operator());
uchar * Data = src.data;
for (int RowIndex=0; RowIndex < src.rows; ++RowIndex)
for(int ColIndex=0;ColIndex< src.cols; ++ColIndex)
{
printf("B Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols * 3 + ColIndex * 3));
printf("G Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols * 3 + ColIndex * 3 + 1));
printf("R Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols * 3 + ColIndex * 3 + 2));
}
}
operator =重载
opencv Mat类利用C+++ operator特性来完成对两个Mat之间的赋值操作,可以看到起等号赋值重载如下:
Method | Description |
Mat& operator = (const Mat& m) | m为被赋值的右侧矩阵,矩阵的赋值操作复杂度为O(1),意味着其赋值过程中没有数据段赋值,并且两个矩阵使用的是同一个计数器。在给矩阵赋新数据之前由Mat:release()释放引用 |
Mat& operator = (const MatExpr& expr) | expr为矩阵表达式.具体可以查看OpenCV Mat主要用法(2)_MatExpr详细了解 |
使用一个简单的例子验证赋值操作并没有产生新的数据区:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat src(3, 3, CV_8UC1, Scalar(0));
cout << "Src= " << endl << src << endl;
Mat dst = src;
dst.at<unsigned char>(0, 0) = 1;
dst.at<unsigned char>(0, 1) = 2;
dst.at<unsigned char>(0, 2) = 3;
cout << "dst = " << endl << dst << endl;
cout << "Src= " << endl << src << endl;
}
修改过赋值之后的Mat矩阵中的值,其源矩阵的值也同样进行了修改,因为并没有产生新的数据区域。
运行结果:
向量内积(点乘)dot
向量的点乘,也叫向量的内积、数量积,对两个向量执行点乘运算,就是对这两个向量对应位一一相乘之后求和的操作,点乘的结果是一个标量。
对于向量a和向量b:
a和b的点积公式为:
要求一维向量a和向量b的行列数相同。
对于此概念不清楚的,可以查看
对于Mat而言,如果是两个多维矩阵计算点乘,Mat会将这个矩阵扩展成一列,转换成一维之后,然后在执行向量点乘,向量点乘的结果为一个标量。例如一个M,N的二维矩阵会将其转换成1, M*N列的矩阵然后做点乘运算。
如果Mat是多通道的矩阵,先计算各个通道先独立计算dot之后,计算各个通道dot结果和
Mat的点乘API 为dot:
Method | Description |
double dot(InputArray m) | m为做点乘的另外一个矩阵 |
一个Mat 点乘的简单例子:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat A(3, 3, CV_8UC1, Scalar(0));
Mat B(3, 3, CV_8UC1, Scalar(0));
A.at<unsigned char>(0, 0) = 1;
A.at<unsigned char>(0, 1) = 2;
A.at<unsigned char>(0, 2) = 3;
A.at<unsigned char>(1, 0) = 4;
A.at<unsigned char>(1, 1) = 5;
A.at<unsigned char>(1, 2) = 6;
A.at<unsigned char>(2, 0) = 7;
A.at<unsigned char>(2, 1) = 8;
A.at<unsigned char>(2, 2) = 9;
B.at<unsigned char>(0, 0) = 3;
B.at<unsigned char>(0, 1) = 2;
B.at<unsigned char>(0, 2) = 1;
B.at<unsigned char>(1, 0) = 6;
B.at<unsigned char>(1, 1) = 5;
B.at<unsigned char>(1, 2) = 4;
B.at<unsigned char>(2, 0) = 9;
B.at<unsigned char>(2, 1) = 8;
B.at<unsigned char>(2, 2) = 7;
cout << "A = " << endl << A<< endl;
cout << "B= " << endl << B << endl;
cout << "A.dot(B)= " << endl << A.dot(B) << endl;
}
运行结果:
Mat矩阵mul
OpenCV mat同样支持两个矩阵相对应位的乘积,以简单的情况为例,对于2*2大小的Mat矩阵A和B:
对A和B执行mul运算:
需要说明的A和B两个矩阵维度,通道数等必须相同 。
Method | Description |
MatExpr mul(InputArray m, double scale=1) | m为另外一个矩阵,scale为结果比例缩放因子,防止结果越界 |
下面为一个简单的mul使用用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat A(3, 3, CV_8UC1, Scalar(0));
Mat B(3, 3, CV_8UC1, Scalar(0));
A.at<unsigned char>(0, 0) = 1;
A.at<unsigned char>(0, 1) = 2;
A.at<unsigned char>(0, 2) = 3;
A.at<unsigned char>(1, 0) = 4;
A.at<unsigned char>(1, 1) = 5;
A.at<unsigned char>(1, 2) = 6;
A.at<unsigned char>(2, 0) = 7;
A.at<unsigned char>(2, 1) = 8;
A.at<unsigned char>(2, 2) = 9;
B.at<unsigned char>(0, 0) = 3;
B.at<unsigned char>(0, 1) = 2;
B.at<unsigned char>(0, 2) = 1;
B.at<unsigned char>(1, 0) = 6;
B.at<unsigned char>(1, 1) = 5;
B.at<unsigned char>(1, 2) = 4;
B.at<unsigned char>(2, 0) = 9;
B.at<unsigned char>(2, 1) = 8;
B.at<unsigned char>(2, 2) = 7;
cout << "A = "<<endl<< A<< endl;
cout << "B= " <<endl<< B << endl;
cout << "A.mul(B)= " <<endl<< A.mul(B) << endl;
}
运行结果:
矩阵相乘
OpenCV mat同样支持两个矩阵相乘,矩阵的相乘主要是通过MatExpr中重载*来实现的,后面再详细描述
两个2*2两维度矩阵相乘如下:
如果具体对矩阵相乘的公式不懂,可以复习下大学的线性代数
下面来看一个矩阵相乘例子:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat A(3, 3, CV_32FC1);
Mat B(3, 3, CV_32FC1);
A.at<float>(0, 0) = 1;
A.at<float>(0, 1) = 2;
A.at<float>(0, 2) = 3;
A.at<float>(1, 0) = 4;
A.at<float>(1, 1) = 5;
A.at<float>(1, 2) = 6;
A.at<float>(2, 0) = 1;
A.at<float>(2, 1) = 2;
A.at<float>(2, 2) = 3;
B.at<float>(0, 0) = 3;
B.at<float>(0, 1) = 2;
B.at<float>(0, 2) = 1;
B.at<float>(1, 0) = 6;
B.at<float>(1, 1) = 5;
B.at<float>(1, 2) = 4;
B.at<float>(2, 0) = 1;
B.at<float>(2, 1) = 2;
B.at<float>(2, 2) = 3;
cout << "A = "<<endl<< A<< endl;
cout << "B= " <<endl<< B << endl;
cout << "A*B = " <<endl<< A*B << endl;
}
运行结果
矩阵的叉乘Cross
矩阵的叉乘,其实是说的两个向量的叉乘,矩阵是不能叉乘的。做叉乘的两个矩阵必须是三元素的向量。
公式:|c|=|a×b|=|a||b|sin<a,b>
几何意义:叉乘的结果是一个向量。方向垂直于原先两个向量构成的平面。
举例:a=[1,2,3],b=[4,5,6],则cross(a,b)=[-3 6 -3]。它表示的意思是三维空间中的两个点A(1,2,3)和B(4,5,6),再加上原点O,则构成的两个向量OA,OB,则cross(a,b)就是垂直平面OAB的向量。
计算方法:
给定直角坐标系的单位向量i,j,k满足下列等式:
i×j=k;
j×k=i;
k×i=j;
通过这些规则,两个向量的叉积的坐标可以方便地计算出来,不需要考虑任何角度:设
a=[a1,a2,a3]=a1i+a2j+a3k;
b=[b1,b2,b3]=b1i+b2j+b3k;
则a×b=[a2b3-a3b2,a3b1-a1b3,a1b2-a2b1]。
计算公式为:
Mat 做叉乘API为:
opencv Mat做叉乘必须是浮点型
使用用例:
#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
void main()
{
Mat A(1, 3, CV_32FC1);
Mat B(1, 3, CV_32FC1);
A.at<float>(0, 0) = 1;
A.at<float>(0, 1) = 2;
A.at<float>(0, 2) = 3;
B.at<float>(0, 0) = 3;
B.at<float>(0, 1) = 2;
B.at<float>(0, 2) = 1;
cout << "A = "<<endl<< A<< endl;
cout << "B= " <<endl<< B << endl;
cout << "A.cross(B)= " <<endl<< A.cross(B)<< endl;
}
运行结果: