访问图像中的每个像素_C++

根据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]

e.g:对于小的矩阵,你可以使用逗号初始化

<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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值