根据opencv官方文档user和tutorials篇,简单介绍Opencv如何存储图像和如何访问图像中的每个像素
在C语言版本中,Opencv使用IplImage*存储图像数据,这种方式的最大问题在于内存的分配和重分配。在C++版本中改用Mat结构体,无需编程者分配和释放内存空间。
Mat是一个基础类包括矩阵头和一个指向图像数据的指针,矩阵头的内存大小是一个常数。
The idea is that each Mat object has its own header,however the matrix may be shared between two instance of them by having their matrix pointer point to the same address. Moreover, the copy operators will only copy the headers, and as also copy the pointer to the large matrix too, however not the matrix itself.(复制操作只复制矩阵头和矩阵数据指针)
e.g::A、B、C指向同一矩阵,但它们的矩阵头不同
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(分配操作)
e.g:使用一个矩阵的子矩阵(just create a new header with the new boundaries)
Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries
当多个矩阵使用同一个矩阵数据时,在复制一个矩阵时,它的counter增加1,矩阵释放时counter-1,当counter为0时矩阵被释放。此时要复制矩阵只能使用clone()和copyTo()函数:
e.g:
Mat F = A.clone();
Mat G;
A.copyTo(G);
创建一个矩阵对象
e.g:带数据初始化
Mat M(2,2,CV_8UC3,Scalar(0,0,255));
cout<<"M="<<endl<<M<<endl;
输出
e.g:三维矩阵
<span style="white-space:pre"> </span>int sz[3]={2,2,2};
Mat L(3,sz,CV_8UC(1),Scalar::all(255));
cout<<(int)L.at<uchar>(1,1,1)<<endl;
e.g:重分配矩阵内存(不能用于初始化)
M.create(4,4, CV_8UC(2));
cout << "M = "<< endl << M << endl;
e.g:使用MATLAB模式进行初始化
Mat E=Mat::eye(4,4,CV_64F);
cout<<"E="<<endl<<" "<<E<<endl;
Mat O=Mat::ones(4,4,CV_32F);
cout<<"O="<<endl<<" "<<O<<endl;
Mat Z=Mat::zeros(4,4,CV_8U);
cout<<"Z="<<endl<<" "<<Z<<endl;
结果:
E= [1, 0, 0, 0; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1] | O= [1, 1, 1, 1; 1, 1, 1, 1; 1, 1, 1, 1; 1, 1, 1, 1] | Z= [0, 0, 0, 0; 0, 0, 0, 0; 0, 0, 0, 0; 0, 0, 0, 0] |
<span style="white-space:pre"> </span>Mat C=(Mat_<double>(3,3)<<0,-1,0,-1,5,-1,0,-1,0);
cout<<"C="<<endl<<" "<<C<<endl;
C=
[0,-1, 0;
-1,5, -1;
0,-1, 0]
e.g:创建一个新的矩阵头为存在的矩阵(使用clone或copyTo函数) Mat C=(Mat_<double>(3,3)<<0,-1,0,-1,5,-1,0,-1,0);
cout<<"C="<<endl<<" "<<C<<endl;
Mat RowClone=C.row(1).clone();
Mat ColCopyTo;
C.col(1).copyTo(ColCopyTo);
cout<<"Row(1)="<<endl<<" "<<RowClone<<endl;
cout<<"Col(1)="<<endl<<" "<<ColCopyTo<<endl;
结果:
控制台输出格式控制format(Mat mat,String str);
cout << "R (default) = " << endl << R << endl;
cout << "R (python) = " << endl << format(R,"python") << endl;
str可取“python”,"csv","numpy","C",个人觉得numpy信息较多,python较直观
不仅矩阵,其他数据结构也可直接使用cout输出:
Point2f P(5, 1);
cout << "Point (2D) = " << P << endl;
Point3f P3f(2, 6, 7);
cout << "Point (3D) = " << P3f << endl;
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;
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;
图像数据的储存
灰度图像
彩色图像(注意通道顺序为BGR)
下面开始正式介绍如何访问图像每个像素
一、高效但不安全的方式
灰度图像:
#include "cv.h"
#include "highgui.h"
using namespace cv;
using namespace std;
int main(int argc,char *argv[])
{
Mat img=imread("test.jpg",0);
int channels=img.channels();
int nRows=img.rows;
int nCols=img.cols;
cout<<"image:{channels:"<<channels<<"; nRows:"<<nRows<<"; nCols:"<<nCols<<"}"<<endl;
/*
if (img.isContinuous()) //如果内存上连续存放(一般为TRUE),我们只需定义一个起始指针,一直遍历图像
{
nCols*=nRows;
nRows=1;
}
*/
int i,j;
uchar *p;
for (i=0;i<nRows;i++)
{
p=img.ptr<uchar>(i); //每一行首地址
for(j=0;j<nCols;j++)
{
if(p[j]<250) cout<<".";
else cout<<" ";
}
cout<<endl;
}
namedWindow("1",0);
imshow("1",img);
waitKey(0);
return 0;
}
其中isContinuous函数将能更方便遍历(更高效),但由于输出结果不方便这里不用
灰度图像: 命令窗口输出:
彩色图像:
#include "cv.h"
#include "highgui.h"
using namespace cv;
using namespace std;
int main(int argc,char *argv[])
{
Mat img=imread("lena.jpg");
int channels=img.channels();
int nRows=img.rows;
int nCols=img.cols*3; //方便遍历
cout<<"image:{channels:"<<channels<<"; nRows:"<<nRows<<"; nCols:"<<nCols/3<<"}"<<endl;
/*
if (img.isContinuous()) //如果内存上连续存放(一般为TRUE),我们只需定义一个起始指针,一直遍历图像
{
nCols*=nRows;
nRows=1;
}
*/
int i,j;
uchar *p;
for (i=0;i<nRows;i++)
{
p=img.ptr<uchar>(i); //每一行首地址
for(j=0;j<nCols;j+=3)
{
if (p[j]+p[j+1]+p[j+2]<350) cout<<".";
else cout<<" ";
}
cout<<endl;
}
namedWindow("1",0);
imshow("1",img);
waitKey(0);
return 0;
}
控制台输出效果图:
PS:对于连续存储、灰度图像(这种方法不推荐,难读)
uchar* p = I.data;
for( unsigned int i =0; i < ncol*nrows; ++i)
*p++ = table[*p];//table为一个颜色映射表
其中I.data为整幅图像数据首地址,当读入图像时,I.data==NULL时表示图像数据为空
二、迭代但安全的方式
#include "cv.h"
#include "highgui.h"
using namespace cv;
using namespace std;
int main(int argc,char *argv[])
{
Mat img=imread("test.jpg",0);<span style="white-space:pre"> </span>//灰度图像
//Mat img=imread("lena.jpg",1); //彩色图像
const int channels=img.channels();
switch(channels)
{
case 1:
{
MatIterator_<uchar>start,end;
for (start=img.begin<uchar>(),end=img.end<uchar>();start!=end;start++)
{
if(*start<250) *start=255;
else *start=0;
}
break;
}
case 3:
{
MatIterator_<Vec3b>start,end;
for (start=img.begin<Vec3b>(),end=img.end<Vec3b>();start!=end;start++)
{
if((*start)[0]+(*start)[1]+(*start)[2]<350) (*start)[0]=(*start)[1]=(*start)[2]=0;
else (*start)[0]=(*start)[1]=(*start)[2]=255;
}
}
}
namedWindow("1",0);
imshow("1",img);
imwrite("img.jpg",img);
waitKey(0);
return 0;
}
结果输出:
||
结果输出:
三、下面的方法是最慢的(随机访问)
单通道灰度图像时
Scalar intensity = img.at<uchar>(x, y);
其中img.at<uchar>(x,y)返回uchar类型灰度值,Scalar只是构造成4通道double值
使用intensity.val[0]获取值
#include "cv.h"
#include "highgui.h"
using namespace std;
using namespace cv;
int main(int argc,char *argv[])
{
Mat image;
Scalar value;
int i,j;
image=imread("opencv.png",0);
if (!image.data)
{
cout<<"Read Error!"<<endl;
system("pause");
return -1;
}
for (int i=0;i<image.rows;i++)
{
for (j=0;j<image.cols;j++)
{
value=image.at<uchar>(i,j);
if (value[0]<255)
cout<<".";
else
cout<<" ";
}
cout<<endl;
}
namedWindow("image",0);
imshow("image",image);
cvWaitKey(0);
return 0;
}
效果:
灰度图像: 命令窗口输出:
#include "cv.h"
#include "highgui.h"
using namespace std;
using namespace cv;
int main(int argc,char *argv[])
{
Mat image;
Scalar value;
int i,j;
image=imread("opencv.png",0);
if (!image.data)
{
cout<<"Read Error!"<<endl;
system("pause");
return -1;
}
for (int i=0;i<image.rows;i++)
{
for (j=0;j<image.cols;j++)
{
value=image.at<uchar>(i,j);
image.at<uchar>(i,j)=255-value[0];
}
}
namedWindow("image",0);
imshow("image",image);
imwrite("get.jpg",image);
cvWaitKey(0);
return 0;
}
效果:
原图: 输出:
三通道BGR彩色图像时
Vec3b intensity = img.at<Vec3b>(x, y);
uchar blue = intensity.val[0];
uchar green = intensity.val[1];
uchar red = intensity.val[2];
其中Vec3b也是一个构造类型:typedef Vec<uchar, 3> Vec3b;
三通道浮点型图像时
Vec3f intensity = img.at<Vec3f>(x, y);
float blue = intensity.val[0];
float green = intensity.val[1];
float red = intensity.val[2];
矩阵点集
vector<Point2f> points;
Mat pointsMat = Mat(points);
Point2f point = pointsMat.at<Point2f>(i, 0);
修改图像像素值:img.at<uchar>(x, y) = 128;
参考:
http://blog.csdn.net/xiaowei_cqu/article/details/7771760