像素是图像的基本组成单位,熟悉了如何操作像素,就能更好的理解对图像的各种处理变换的实现方式了。

1.at方法

第一种操作像素的方法是使用“at”,如一幅3通道的彩色图像image的第i行j列的B、G、R分量分别表示为:

image.at<Vec3b>(i,j)[0];
 image.at<Vec3b>(i,j)[1];
 image.at<Vec3b>(i,j)[2];

而对于单通道的灰度图像就简单很多了:

image.at<uchar>(i,j);

这里要注意at中(i,j)的顺序表示的是第i行第j列,跟Point(i,j)和Rect(i,j)中表示第j行第i列是相反的,如果把这个搞混了,很容易导致内存异常,还不容易发现错误。

 

补充说明一下:OpenCV中坐标体系中的零点坐标定义为图片的左上角,X轴为图像矩形的上面那条水平线,从左往右;Y轴为图像矩形左边的那条垂直线,从上往下。在Point(x,y)和Rect(x,y)中,第一个参数x代表的是元素所在图像的列数,第二个参数y代表的是元素所在图像的行数,而在at(x,y)中是相反的。

演示程序如下:


1. #include<iostream>  
2. #include<core/core.hpp>  
3. #include<highgui/highgui.hpp>  
4.   
5. using namespace cv;  
6. using namespace std;  
7.   
8. int main()  
9. {  
10.     Mat image(Size(500,500),CV_8UC3);  
11.     image.at<Vec3b>(100,250)[0]=0;  
12.     image.at<Vec3b>(100,250)[1]=0;  
13.     image.at<Vec3b>(100,250)[2]=255;  
14. "at(100,250) is Here!",Point(250,100),0,0.7,Scalar(255,0,0));  //点,字体,大小
15.   
16.     image.at<Vec3b>(Point(100,250))[0]=0;  
17.     image.at<Vec3b>(Point(100,250))[1]=0;  
18.     image.at<Vec3b>(Point(100,250))[2]=255;  
19. "at(Point(100,250)) is Here!",Point(100,250),0,0.7,Scalar(255,0,0));  
20.   
21. "Test Function at",image);  
22.     waitKey();  
23. return 0;  
24. }


 


像素坐标系转化为图像坐标系的python实现 像素 坐标_#include


2.行指针方法


行指针方法的思路是获取图像每一行的首地址的指针,把每一行当成一个循环对象,逐行遍历所有的像素。例如对于一个X行、Y列的图像image,每行需要循环 的数量是Y*image.channels(),channels表示的是图像的通道,对于灰度图像channels为1,对于彩色RGB图像,channels为3。


3.指针方法


对于硬件处理芯片来说,如果图像每行的长度是4或8的倍数的话,运算起来会更加快速,如果不是4或8的倍数的话,在运算前图像行长度会被补充至固定值。


“指针方法”是一种可以高效遍历图像的方式,但是只能针对没有经过填充的连续图像,所以在使用指针方法之前需要先判断图像有没有经过填充,是否连续性。判断的方法很简单,使用Mat的成员函数isContinuous来判断,若返回值为真的话,说明图像是连续的,可以应用行指针的方法遍历像素。


4.迭代方法


使用迭代器遍历图像集合中的各个元素,相比前3中方法,这个方法简直可以永简单快捷的“无脑操作”来形容,你只需获取集合中元素的首地址以及元素的终止位置,剩下的工作交给迭代器来完成就可以了,并且我们不用关心集合的数据类型,指针总是逐次访问下一地址,直到指针到达终止元素位置。不过其缺点也是显而易见的:不容易指定访问集合中某一个位置的元素。


话不多少,下边给出一个完整的四种访问图像像素方法的示例,并对每种方法的耗时作对比,包括opencv本身的Copy方法;当然五种方法实现的是同一个简单的功能——通过逐个遍历一幅图像的每一个像素,复制该图像到另一Mat对象。

1. #include<iostream>  
2. #include<core/core.hpp>  
3. #include<highgui/highgui.hpp>  
4.   
5. using namespace cv;  
6. using namespace std;  
7.   
8. //At方法  
9. double CopyImageByAt(Mat originalImage, Mat &targetImage);  
10. //行指针方法  
11. double CopyImageByRowPtr(Mat originalImage, Mat &targetImage);  
12. //指针方法  
13. double CopyImageByPtr(Mat originalImage, Mat &targetImage);  
14. //迭代方法  
15. double CopyImageByIterator(Mat originalImage, Mat &targetImage);  
16. //Opencv方法  
17. double CopyFun(Mat originalImage, Mat &targetImage);  
18.   
19.   
20. int main()  
21. {  
22. //读入图片,注意图片路径 
23. //第二个参数表示图片读入的方式(flags可以缺省,缺省时flags=1,表示以彩色图片方式读入图片) 
//flags>0时表示以彩色方式读入图片 
//flags=0时表示以灰度图方式读入图片 
//flags<0时表示以图片的本来的格式读入图片24. "D:\\Picture\\lena.jpg",1);  
25.   
26. //图片读入成功与否判定  
27. if(!image.data)  
28.     {  
29. "you idiot!where did you hide lena!"<<endl;  
30. //等待按键  
31. "pause");  
32. return -1;  
33.     }  
34. "原始图像",image);  
35. //输出图像  
36.     Mat targetImage(image.size(),image.type());  
37. "At方法耗时:"<<CopyImageByAt(image,targetImage)<<endl;  
38. "At方法",targetImage);  
39. "行指针方法耗时:"<<CopyImageByRowPtr(image,targetImage)<<endl;  
40. "行指针方法",targetImage);  
41. "指针方法耗时:"<<CopyImageByPtr(image,targetImage)<<endl;  
42. "指针方法",targetImage);  
43. "迭代方法耗时:"<<CopyImageByIterator(image,targetImage)<<endl;  
44. "迭代方法",targetImage);  
45. "OpenCV Copy方法耗时:"<<CopyFun(image,targetImage)<<endl;  
46. "Copy方法",targetImage);     
47.     waitKey();  
48. return 0;  
49. }  
50.   
51. //使用at方法实现逐个像素复制  
52. double CopyImageByAt(Mat originalImage, Mat &targetImage)  
53. {  
54. double now=getTickCount();  
55. //行  
56. int rows=originalImage.rows;  
57. //列  
58. int cols=originalImage.cols;  
59. for(int i=0;i<rows;i++)  
60.     {  
61. for(int j=0;j<cols;j++)  
62.         {  
63. //若是灰度图像应使用如下表示:   
64. //targetImage.at<uchar>(i,j)=originalImage.at<Vec3b>(i,j);            
65.             targetImage.at<Vec3b>(i,j)[0]=originalImage.at<Vec3b>(i,j)[0];  
66.             targetImage.at<Vec3b>(i,j)[1]=originalImage.at<Vec3b>(i,j)[1];  
67.             targetImage.at<Vec3b>(i,j)[2]=originalImage.at<Vec3b>(i,j)[2];  
68.         }  
69.     }  
70. double end=getTickCount();  
71. //返回方法耗时  
72. return (end-now)/getTickFrequency();  
73. }  
74.   
75. //使用访问每行首指针方法实现像素复制  
76. double CopyImageByRowPtr(Mat originalImage, Mat &targetImage)  
77. {  
78. double now=getTickCount();  
79. //行  
80. int rows=targetImage.rows;  
81. //每行总元素数量,此处图像为3通道  
82. int totalNum=targetImage.cols*targetImage.channels();  
83. for(int i=0;i<rows;i++)  
84.     {  
85. //data1指向目标图像第i行的首元素  
86.         uchar *data1=targetImage.ptr<uchar>(i);  
87. data2指向原始图像第i行的首元素  
88.         uchar *data2=originalImage.ptr<uchar>(i);  
89. for(int j=0;j<totalNum;j++)  
90.         {  
91. //遍历每行所有元素  
92.             data1[j]=data2[j];  
93.         }  
94.     }  
95. double end=getTickCount();  
96. //返回方法耗时  
97. return (end-now)/getTickFrequency();  
98. }  
99.   
100. //无扩充的图像,采用指针方法逐个像素复制  
101. double CopyImageByPtr(Mat originalImage, Mat &targetImage)  
102. {  
103. double now=getTickCount();  
104. //行  
105. int rows=targetImage.rows;  
106. //每行总元素数量,此处图像为3通道  
107. int totalNum=targetImage.cols*targetImage.channels();  
108. //判断图像数据是否连续  
109. if(originalImage.isContinuous())  
110.     {  
111.         totalNum*=rows;  
112.         rows=1;  
113.     }  
114. //外层循环只执行一次  
115. for(int i=0;i<rows;i++)  
116.     {  
117.         uchar *data1=targetImage.ptr<uchar>(i);  
118.         uchar *data2=originalImage.ptr<uchar>(i);  
119. for(int j=0;j<totalNum;j++)  
120.         {  
121.           data1[j]=data2[j];  
122.         }  
123.     }  
124. double end=getTickCount();  
125. //返回方法耗时  
126. return (end-now)/getTickFrequency();  
127. }  
128.   
129. //使用迭代器遍历逐个像素复制  
130. double CopyImageByIterator(Mat originalImage, Mat &targetImage)  
131. {  
132. double now=getTickCount();  
133. //获取起始位置迭代器  
134.     Mat_<Vec3b>::iterator itBegin1=targetImage.begin<Vec3b>();  
135.     Mat_<Vec3b>::iterator itBegin2=originalImage.begin<Vec3b>();  
136. //获取终止位置迭代器  
137.     Mat_<Vec3b>::iterator itEnd1=targetImage.end<Vec3b>();  
138.     Mat_<Vec3b>::iterator itEnd2=originalImage.end<Vec3b>();  
139. for(;itBegin1!=itEnd1;++itBegin1)  
140.     {  
141.         (*itBegin1)[0]=(*itBegin2)[0];  
142.         (*itBegin1)[1]=(*itBegin2)[1];  
143.         (*itBegin1)[2]=(*itBegin2)[2];  
144.         ++itBegin2;  
145.     }  
146. double end=getTickCount();  
147. //返回方法耗时  
148. return (end-now)/getTickFrequency();  
149. }  
150.   
151. //OpenCV Copy方法实现图像复制  
152. double CopyFun(Mat originalImage, Mat &targetImage)  
153. {  
154. double now=getTickCount();  
155.     originalImage.copyTo(targetImage);  
156. double end=getTickCount();    
157. //返回方法耗时  
158. return (end-now)/getTickFrequency();  
159. }



像素坐标系转化为图像坐标系的python实现 像素 坐标_迭代_02

像素坐标系转化为图像坐标系的python实现 像素 坐标_迭代器_03

像素坐标系转化为图像坐标系的python实现 像素 坐标_迭代器_04

像素坐标系转化为图像坐标系的python实现 像素 坐标_#include_05

像素坐标系转化为图像坐标系的python实现 像素 坐标_迭代器_06

像素坐标系转化为图像坐标系的python实现 像素 坐标_迭代_07

像素坐标系转化为图像坐标系的python实现 像素 坐标_迭代_08

可见,指针方法是高效快捷访问像素的首选方法,然而跟opencv的Copy方法相比,还是弱爆了……