数字图像可看作一个数值矩阵, 其中的每个元素代表一个像素点,如下图所示:
OpenCV 中,用 Mat 来表示该数值矩阵,它是很关键的一种数据结构,因为 OpenCV 中的大部分函数都和 Mat 有关:
有的是 Mat 的成员函数;有的把 Mat 作为参数;还有的将 Mat 作为返回值
1 Mat 简介
Mat,在 OpenCV 中表示的是 N 维稠密矩阵,与稠密矩阵相对的是稀疏矩阵(只存储非零的像素值),后者常用于直方图处理中,OpenCV 中对应为 cv::SparseMat
如下所示:第一个为稠密矩阵的存储方式,存储所有的像素数值;第二个为稀疏矩阵的存储方式,只存储非零的像素值
[020101020]” role=”presentation” style=”position: relative;”>⎡⎣⎢010202010⎤⎦⎥[020101020]
当 N=1 时,所有像素存储为一行;当 N=2 时,所有像素按照一行行的顺序排列;当 N=3 时,所有像素按照一面面的顺序排列,其中一行行的像素构成一个平面。
下图左,为灰度图的存储方式;图右,为 RGB 图像的存储方式,注意其存储顺序为 BGR (Blue->Green->Red)
2 Mat 特点
2.1 组成
Mat 类包含两部分,一是 矩阵头 (matrix header),二是 矩阵指针 (pointer to matrix),部分矩阵头如下:
int flags; // signaling the contents of the matrix int dims; // dimensions int rows, cols; // rows and columns MatSize size; // MatStep step; //
矩阵指针如下,指向包含所有像素值的矩阵
uchar* data; // pointer to the data
2.2 赋值算子
Mat 类中的赋值算子 “=” 和 拷贝构造函数,涉及的是浅拷贝,因此,当执行这两个操作时,仅仅是复制了矩阵头。
如果想要深拷贝,达到复制图像矩阵的目的,应使用 clone() 或 copyTo() 函数,如下图所示 (摘自参考资料 – 4):
2.3 代码示例
下面是简单的验证,将矩阵 m3 通过 copyTo() 函数复制给 m1,而 m2 是通过 m1 直接赋值的,二者指向的是同样的数据。因此,如果改变了 m1,则 m2 对应的矩阵数值,也会进行相应的改变。
Mat m1(3, 3, CV_32FC1, Scalar(1.1f) ); cout << "m1 = " << endl << " " << m1 << endl << endl;
// using assign operator Mat m2 = m1; cout << "m2 = " << endl << " " << m2 << endl << endl; Mat m3(3, 3, CV_32FC1, Scalar(3.3f) ); m3.copyTo(m1); cout << "m1 = " << endl << " " << m1 << endl << endl; cout << "m2 = " << endl << " " << m2 << endl << endl;
3 Mat 创建
3.1 数据类型
在创建 Mat 之前,首先了解 Mat 中元素的数据类型,其格式为 CV_{8U, 16S, 16U, 32S, 32F, 64F}C{1, 2, 3} 或 CV_{8U, 16S, 16U, 32S, 32F, 64F}C(n)
第一个 {} 内数据表示的意义如下:
CV_8U - 8-bit 无符号整数 ( 0..255 ) CV_8S - 8-bit 有符号整数 ( -128..127 ) CV_16U - 16-bit 无符号整数 ( 0..65535 ) CV_16S - 16-bit 有符号整数 ( -32768..32767 ) CV_32S - 32-bit 有符号整数 ( -2147483648..2147483647 ) CV_32F - 32-bit 浮点数 ( -FLT_MAX..FLT_MAX, INF, NAN ) CV_64F - 64-bit 浮点数 ( -DBL_MAX..DBL_MAX, INF, NAN )
第二个 {} 内的数据 或 (n),表示的是图像矩阵的通道数,CV_8UC3 则等价于 CV_8UC(3),表示的数据类型为:3通道8位无符号整数
3.2 创建方式
3.2.1 构造函数
创建一个 3 行 5 列,3 通道 32 位,浮点型的矩阵,通道 1, 2, 3 的值分别为 1.1f,2.2f,3.3f
Mat m(3, 5, CV_32FC3, Scalar(1.1f, 2.2f, 3.3f) ); cout << "m = " << endl << " " << m << endl << endl;
输出的矩阵如下:
3.2.2 create 函数
使用 Mat() + create() + setTo(),也可以构建如上的数值矩阵
Mat m;
// Create data area for 3 rows and 10 columns of 3-channel 32-bit floats m.create(3,5,CV_32FC3);
// Set the values in the 1st channel to 1.0, the 2nd to 0.0, and the 3rd to 1.0 m.setTo(Scalar(1.1f, 2.2f,3.3f)); cout << "m = " << endl << " " << m << endl << endl;
3.2.3 特殊矩阵
单位矩阵 (ones),对角矩阵 (eye),零矩阵 (zeros),如下所示:
// 单位矩阵 Mat O = Mat::ones(3, 3, CV_32F); cout << "O = " << endl << " " << O << endl << endl; // 零矩阵 Mat Z = Mat::zeros(3, 3, CV_8UC1); cout << "Z = " << endl << " " << Z << endl << endl; // 对角矩阵 Mat E = Mat::eye(3, 3, CV_64F); cout << "E = " << endl << " " << E << endl << endl;
4 Mat 遍历
4.1 at<>() 函数
常用来遍历 Mat 元素的基本函数为 at<>(),其中 <> 内的数据类型,取决于 Mat 中元素的数据类型,二者的对应关系如下:
CV_8U -- Mat.at<uchar>(y,x) CV_8S -- Mat.at<schar>(y,x) CV_16U -- Mat.at<ushort>(y,x) CV_16S -- Mat.at<short>(y,x) CV_32S -- Mat.at<int>(y,x) CV_32F -- Mat.at<float>(y,x) CV_64F -- Mat.at<double>(y,x)
简单的遍历如下,使用了 Qt 的 qDebug() 来显示输出
Mat m1 = Mat::eye(10, 10, CV_32FC1);
// use qDebug() qDebug() << "Element (3,3) is : " << m1.at<float>(3,3); Mat m2 = Mat::eye(10, 10, CV_32FC2);
// use qDebug()
qDebug() << "Element (3,3) is " << m2.at<cv::Vec2f>(3,3)[0] << "," << m2.at<cv::Vec2f>(3,3)[1];
注意:at<>() 函数中 () 内,行索引号在前,列索引号在后,也即 (y, x)
4.2 遍历方式
4.2.1 高效遍历
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table) { // accept only char type matrices CV_Assert(I.depth() == CV_8U); int channels = I.channels(); int nRows = I.rows; int nCols = I.cols * channels; if (I.isContinuous()) { nCols *= nRows; nRows = 1; } int i,j; uchar* p; for(i=0; i<nRows; ++i) { p = I.ptr<uchar>(i); for (j = 0; j<nCols; ++j) { p[j] = table[p[j]]; } } return I; }
4.2.2 迭代器遍历
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table) { // accept only char type matrices CV_Assert(I.depth() == CV_8U); const int channels = I.channels(); switch(channels) { case 1: { MatIterator_<uchar> it, end; for(it=I.begin<uchar>(), end=I.end<uchar>(); it!=end; ++it) *it = table[*it]; break; } case 3: { MatIterator_<Vec3b> it, end; for(it=I.begin<Vec3b>(), end=I.end<Vec3b>(); it!=end; ++it) { (*it)[0] = table[(*it)[0]]; (*it)[1] = table[(*it)[1]]; (*it)[2] = table[(*it)[2]]; } } } return I; }
4.2.3 耗时计算
比较上面两种方法的耗时,可使用如下代码来进行计算:
double t = (double)getTickCount(); // do something ... t = ((double)getTickCount() - t)/getTickFrequency(); qDebug() << "Times passed in seconds: " << t << endl; // using qDebug()
参考资料:
1. <Learning OpenCV3> chapter 4
2. OpenCV Tutorials / The Core Functionality (core module) / Mat - The Basic Image Container
3. OpenCV Tutorials / The Core Functionality (core module) / How to scan images, lookup tables and time measurement with OpenCV
今天在看Opencv的SIFT源码,至于有关于SIFT算法的博客还没有写完,等着我把源码看完再一起写完吧。
之前用Opencv编过不少的程序了,没想道OpenCV 2.0版本里最基础的Mat类用法还是有些不清楚,这里就总结一下
一、Mat类的综述
1、Mat类存储图像
2、Mat按像素读取图像内容
-
#include <opencv2\core\core.hpp>
-
#include <opencv2\imgproc\imgproc.hpp>
-
#include <opencv2\highgui\highgui.hpp>
-
#include <iostream>
-
-
using
namespace
std;
-
using
namespace cv;
-
-
int main()
-
{
-
Mat img = imread(
“1.jpg”);
-
resize(img, img, Size(
375,
500));
//resize为500*375的图像
-
cvtColor(img, img, CV_RGB2GRAY);
//转为灰度图
-
imshow(
“gray_ori”, img);
-
for (
int i =
0; i < img.rows; i++)
-
{
-
for (
int j =
0; j < img.cols; j++)
-
{
-
//at<类型>(i,j)进行操作,对于灰度图
-
img.at<uchar>(i, j) = i+j;
-
}
-
}
-
imshow(
“gray_result”, img);
-
waitKey(
0);
-
return
0;
-
}
结果图如下:
-
#include <opencv2\core\core.hpp>
-
#include <opencv2\imgproc\imgproc.hpp>
-
#include <opencv2\highgui\highgui.hpp>
-
#include <iostream>
-
-
using
namespace
std;
-
using
namespace cv;
-
-
int main()
-
{
-
Mat img = imread(
“1.jpg”);
-
resize(img, img, Size(
375,
500));
//resize为500*375的图像
-
imshow(
“ori”, img);
-
for (
int i =
0; i < img.rows; i++)
-
{
-
for (
int j =
0; j < img.cols; j++)
-
{
-
//at<类型>(i,j)进行操作,对于灰度图
-
img.at<Vec3b>(i, j)[
0] =
255;
//对于蓝色通道进行操作
-
//img.at<Vec3b>(i, j)[1] = 255;//对于绿色通道进行操作
-
//img.at<Vec3b>(i, j)[2] = 255;//对于红色通道进行操作
-
}
-
}
-
imshow(
“result”, img);
-
waitKey(
0);
-
return
0;
-
}
效果图如下:
-
#include <opencv2\highgui\highgui.hpp>
-
#include <opencv2\imgproc\imgproc.hpp>
-
#include <opencv2\core\core.hpp>
-
#include <iostream>
-
-
using
namespace cv;
-
using
namespace
std;
-
int main()
-
{
-
Mat img = imread(
“1.jpg”);
-
int rows = img.rows;
-
int cols = img.cols * img.channels();
-
if(img.isContinuous())
//判断是否在内存中连续
-
{
-
cols = cols * rows;
-
rows =
1;
-
}
-
imshow(
“ori”,img);
-
for(
int i =
0;i<rows;i++)
-
{
-
//调取存储图像内存的第i行的指针
-
uchar *pointer = img.ptr<uchar>(i);
-
-
for(
int j =
0;j<cols;j +=
3)
-
{
-
//pointer[j] = 255;//对蓝色通道进行操作
-
//pointer[j+1] = 255;//对绿色通道进行操作
-
pointer[j+
2] =
255;
//对红色通道进行操作
-
}
-
}
-
imshow(
“result”,img);
-
waitKey();
-
return
0;
-
}
从上面个的代码中可以很明显的看出我们是如何操作图像的数据以及图像在Mat中的存放格式的,就是我们上面那个彩色图像的存放示意图中的格式,这里把彩色图像中的一个像素点分成三份,每一份都是uchar类型,因此我们这里不需要使用Vec3b数据类型。把彩色图像看成一个rows * (cols * channels)的二维数组进行操作,其中的每个元素的类型都是uchar类型。
-
#include <opencv2\highgui\highgui.hpp>
-
#include <opencv2\imgproc\imgproc.hpp>
-
#include <opencv2\core\core.hpp>
-
#include <iostream>
-
#include <time.h>
-
-
using
namespace cv;
-
using
namespace
std;
-
int main()
-
{
-
Mat img = imread(
“1.jpg”);
-
Mat img2;
-
img.copyTo(img2);
-
-
cout<<
“图像的行数: “<<img.rows<<
endl;
-
cout<<
“图像的列数: “<<img.cols<<
endl;
-
cout<<
“图像通道数: “<<img.channels()<<
endl;
-
-
double time1;
-
time1 = (
double)getTickCount();
-
-
int rows = img.rows;
-
int cols = img.cols * img.channels();
-
if(img.isContinuous())
//判断是否在内存中连续
-
{
-
cols = cols * rows;
-
rows =
1;
-
}
-
-
for(
int i =
0;i<rows;i++)
-
{
-
//调取存储图像内存的第i行的指针
-
uchar *pointer = img.ptr<uchar>(i);
-
-
for(
int j =
0;j<cols;j +=
3)
-
{
-
//pointer[j] = 255;//对蓝色通道进行操作
-
//pointer[j+1] = 255;//对绿色通道进行操作
-
pointer[j+
2] =
255;
//对红色通道进行操作
-
}
-
}
-
time1 =
1000 * ((
double)getTickCount() - time1) / getTickFrequency();
-
//imshow(“result”,img);
-
cout<<
“第一种方法用时: “<<time1<<
endl;
-
-
double time2 = (
double)getTickCount();
-
for (
int i =
0; i < img2.rows; i++)
-
{
-
for (
int j =
0; j < img2.cols; j++)
-
{
-
//at<类型>(i,j)进行操作,对于灰度图
-
img2.at<Vec3b>(i, j)[
0] =
255;
//对于蓝色通道进行操作
-
//img.at<Vec3b>(i, j)[1] = 255;//对于绿色通道进行操作
-
//img.at<Vec3b>(i, j)[2] = 255;//对于红色通道进行操作
-
}
-
}
-
time2 =
1000 * ((
double)getTickCount() - time2)/getTickFrequency();
-
cout<<
“第二种方法用时: “<<time2<<
endl;
-
imshow(
“img”,img);
-
imshow(
“img2”,img2);
-
waitKey(
0);
-
return
0;
-
}
2、Mat类中的变量参数
-
#include <opencv2\highgui\highgui.hpp>
-
#include <opencv2\imgproc\imgproc.hpp>
-
#include <opencv2\core\core.hpp>
-
#include <iostream>
-
#include <time.h>
-
-
using
namespace cv;
-
using
namespace
std;
-
int main()
-
{
-
Mat img = imread(
“1.jpg”);
-
cout<<
“img.rows: “<<img.rows<<
” img.cols: “<<img.cols<<
” img.channels(): “<<img.channels()<<
endl;
-
cout<<
“###############################################”<<
endl;
-
cout<<
“img.step1(0): “<<img.step1(
0)<<
endl;
-
cout<<
“img.step1(1): “<<img.step1(
1)<<
endl;
-
cout<<
“img.step[0]: “<<img.step[
0]<<
endl;
-
cout<<
“img.step[1]: “<<img.step[
1]<<
endl;
-
cout<<
“img.size[0]: “<<img.size[
0]<<
endl;
-
cout<<
“img.size[1]: “<<img.size[
1]<<
endl;
-
cout<<
“img.elemSize(): “<<img.elemSize()<<
endl;
-
cout<<
“img.elemSize1(): “<<img.elemSize1()<<
endl;
-
return
0;
-
}
看一下结果:
我记得开始接触OpenCV就是因为一个算法里面需要2维动态数组,那时候看core这部分也算是走马观花吧,随着使用的增多,对Mat这个结构越来越喜爱,也觉得有必要温故而知新,于是这次再看看Mat。
Mat最大的优势跟STL很相似,都是对内存进行动态的管理,不需要之前用户手动的管理内存,对于一些大型的开发,有时候投入的lpImage内存管理的时间甚至比关注算法实现的时间还要多,这显然是不合适的。除了有些嵌入式场合必须使用c语言,我任何时候都强烈像大家推荐Mat。
Mat这个类有两部分数据。一个是matrix header,这部分的大小是固定的,包含矩阵的大小,存储的方式,矩阵存储的地址等等。另一个部分是一个指向矩阵包含像素值的指针。
-
Mat A, C;
// creates just the header parts
-
A = imread(argv[
1], CV_LOAD_IMAGE_COLOR);
// here we’ll know the method used (allocate matrix)
-
Mat B(A);
// Use the copy constructor
-
C = A;
// Assignment operator
需要注意的是, copy这样的操作只是copy了矩阵的matrix header和那个指针,而不是矩阵的本身,也就意味着两个矩阵的数据指针指向的是同一个地址,需要开发者格外注意。比如上面这段程序,A、B、C指向的是同一块数据,他们的header不同,但对于A的操作同样也影响着B、C的结果。刚刚提高了内存自动释放的问题,那么当我不再使用A的时候就把内存释放了,那时候再操作B和C岂不是很危险。不用担心,OpenCV的大神为我们已经考虑了这个问题,是在最后一个Mat不再使用的时候才会释放内存,咱们就放心用就行了。
如果想建立互不影响的Mat,是真正的复制操作,需要使用函数clone()或者copyTo()。
说到数据的存储,这一直就是一个值得关注的问题,Mat_<uchar>对应的是CV_8U,Mat_<uchar>对应的是CV_8U,Mat_<char>对应的是CV_8S,Mat_<int>对应的是CV_32S,Mat_<float>对应的是CV_32F,Mat_<double>对应的是CV_64F,对应的数据深度如下:
• CV_8U - 8-bit unsigned integers ( 0..255 )
• CV_8S - 8-bit signed integers ( -128..127 )
• CV_16U - 16-bit unsigned integers ( 0..65535 )
• CV_16S - 16-bit signed integers ( -32768..32767 )
• CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
• CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
• CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )
这里还需要注意一个问题,很多OpenCV的函数支持的数据深度只有8位和32位的,所以要少使用CV_64F,但是vs的编译器又会把float数据自动变成double型,有些不太爽。
还有个需要注意的问题,就是流操作符<<对于Mat的操作,仅限于Mat是2维的情况。
还有必要说一下Mat的存储是逐行的存储的。
再说说Mat的创建,方式有两种,罗列一下:1.调用create(行,列,类型)2.Mat(行,列,类型(值))。例如:
-
// make a 7x7 complex matrix filled with 1+3j.
-
Mat M(7,7,CV_32FC2,Scalar(1,3));
-
// and now turn M to a 100x60 15-channel 8-bit matrix.
-
// The old content will be deallocated
-
M.create(
100,
60,CV_8UC(
15));
要是想创建更高维的矩阵,要写成下面的方式
-
// create a 100x100x100 8-bit array
-
int sz[] = {
100,
100,
100};
-
Mat bigCube(3, sz, CV_8U, Scalar::all(0));
对于矩阵的行操作或者列操作,方式如下:( 注意对列操作时要新建一个Mat,我想应该跟列地址不连续有关)
-
// add the 5-th row, multiplied by 3 to the 3rd row
-
M.row(
3) = M.row(
3) + M.row(
5)*
3;
-
// now copy the 7-th column to the 1-st column
-
// M.col(1) = M.col(7); // this will not work
-
Mat M1 = M.col(
1);
-
M.col(
7).copyTo(M1);
下面的东西就比较狂暴了, 对于外来的数据,比如你从别的地方接受了一幅图片,但可以不是Mat结构的,而只有一个数据的指针,看看接下来的代码是如何应付的,重点哦,亲
-
void process_video_frame(const unsigned char* pixels,
-
int width,
int height,
int step)
-
{
-
Mat img(height, width, CV_8UC3, pixels, step);
-
GaussianBlur(img, img, Size(
7,
7),
1.5,
1.5);
-
}
亲,有木有很简单!!!
还有一种快速初始化数据的办法,如下:
-
double m[
3][
3] = {{a, b, c}, {d, e, f}, {g, h, i}};
-
Mat M = Mat(
3,
3, CV_64F, m).inv();
也可以把原来的IplImage格式的图片直接用Mat(IplImage)的方式转成Mat结构,也可以像Matlab一样调用zeros()、ones()、eye()这样的函数进行初始化。
如果你需要提前释放数据的指针和内存,可以调用release()。
对于数据的获取,当然还是调用at<float>(3, 3)这样的格式为最佳。其他的方法我甚少尝试,就不敢介绍了。
最后要提的一点是关于Mat的表达式,这个也非常多,加减乘除,转置求逆,我怎么记得我以前介绍过呢。那就不多说啦~
我记得开始接触OpenCV就是因为一个算法里面需要2维动态数组,那时候看core这部分也算是走马观花吧,随着使用的增多,对Mat这个结构越来越喜爱,也觉得有必要温故而知新,于是这次再看看Mat。
Mat最大的优势跟STL很相似,都是对内存进行动态的管理,不需要之前用户手动的管理内存,对于一些大型的开发,有时候投入的lpImage内存管理的时间甚至比关注算法实现的时间还要多,这显然是不合适的。除了有些嵌入式场合必须使用c语言,我任何时候都强烈像大家推荐Mat。
Mat这个类有两部分数据。一个是matrix header,这部分的大小是固定的,包含矩阵的大小,存储的方式,矩阵存储的地址等等。另一个部分是一个指向矩阵包含像素值的指针。
-
Mat A, C;
// creates just the header parts
-
A = imread(argv[
1], CV_LOAD_IMAGE_COLOR);
// here we’ll know the method used (allocate matrix)
-
Mat B(A);
// Use the copy constructor
-
C = A;
// Assignment operator
需要注意的是, copy这样的操作只是copy了矩阵的matrix header和那个指针,而不是矩阵的本身,也就意味着两个矩阵的数据指针指向的是同一个地址,需要开发者格外注意。比如上面这段程序,A、B、C指向的是同一块数据,他们的header不同,但对于A的操作同样也影响着B、C的结果。刚刚提高了内存自动释放的问题,那么当我不再使用A的时候就把内存释放了,那时候再操作B和C岂不是很危险。不用担心,OpenCV的大神为我们已经考虑了这个问题,是在最后一个Mat不再使用的时候才会释放内存,咱们就放心用就行了。
如果想建立互不影响的Mat,是真正的复制操作,需要使用函数clone()或者copyTo()。
说到数据的存储,这一直就是一个值得关注的问题,Mat_<uchar>对应的是CV_8U,Mat_<uchar>对应的是CV_8U,Mat_<char>对应的是CV_8S,Mat_<int>对应的是CV_32S,Mat_<float>对应的是CV_32F,Mat_<double>对应的是CV_64F,对应的数据深度如下:
• CV_8U - 8-bit unsigned integers ( 0..255 )
• CV_8S - 8-bit signed integers ( -128..127 )
• CV_16U - 16-bit unsigned integers ( 0..65535 )
• CV_16S - 16-bit signed integers ( -32768..32767 )
• CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
• CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
• CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )
这里还需要注意一个问题,很多OpenCV的函数支持的数据深度只有8位和32位的,所以要少使用CV_64F,但是vs的编译器又会把float数据自动变成double型,有些不太爽。
还有个需要注意的问题,就是流操作符<<对于Mat的操作,仅限于Mat是2维的情况。
还有必要说一下Mat的存储是逐行的存储的。
再说说Mat的创建,方式有两种,罗列一下:1.调用create(行,列,类型)2.Mat(行,列,类型(值))。例如:
-
// make a 7x7 complex matrix filled with 1+3j.
-
Mat M(7,7,CV_32FC2,Scalar(1,3));
-
// and now turn M to a 100x60 15-channel 8-bit matrix.
-
// The old content will be deallocated
-
M.create(
100,
60,CV_8UC(
15));
要是想创建更高维的矩阵,要写成下面的方式
-
// create a 100x100x100 8-bit array
-
int sz[] = {
100,
100,
100};
-
Mat bigCube(3, sz, CV_8U, Scalar::all(0));
对于矩阵的行操作或者列操作,方式如下:( 注意对列操作时要新建一个Mat,我想应该跟列地址不连续有关)
-
// add the 5-th row, multiplied by 3 to the 3rd row
-
M.row(
3) = M.row(
3) + M.row(
5)*
3;
-
// now copy the 7-th column to the 1-st column
-
// M.col(1) = M.col(7); // this will not work
-
Mat M1 = M.col(
1);
-
M.col(
7).copyTo(M1);
下面的东西就比较狂暴了, 对于外来的数据,比如你从别的地方接受了一幅图片,但可以不是Mat结构的,而只有一个数据的指针,看看接下来的代码是如何应付的,重点哦,亲
-
void process_video_frame(const unsigned char* pixels,
-
int width,
int height,
int step)
-
{
-
Mat img(height, width, CV_8UC3, pixels, step);
-
GaussianBlur(img, img, Size(
7,
7),
1.5,
1.5);
-
}
亲,有木有很简单!!!
还有一种快速初始化数据的办法,如下:
-
double m[
3][
3] = {{a, b, c}, {d, e, f}, {g, h, i}};
-
Mat M = Mat(
3,
3, CV_64F, m).inv();
也可以把原来的IplImage格式的图片直接用Mat(IplImage)的方式转成Mat结构,也可以像Matlab一样调用zeros()、ones()、eye()这样的函数进行初始化。
如果你需要提前释放数据的指针和内存,可以调用release()。
对于数据的获取,当然还是调用at<float>(3, 3)这样的格式为最佳。其他的方法我甚少尝试,就不敢介绍了。
最后要提的一点是关于Mat的表达式,这个也非常多,加减乘除,转置求逆,我怎么记得我以前介绍过呢。那就不多说啦~