码迷,mamicode.com
首页 > 其他好文 > 详细

指针遍历图像方法总结

时间:2015-06-08 16:32:49      阅读:437      评论:0      收藏:0      [点我收藏+]

标签:

1 指针算术

在一个彩色图像中,图像数据缓冲区中的前三个字节对应图像左上角像素的三个通道值,接下来的三个字节对应第一行的第二个像素,以此类推。一个宽为W、高为H的图像需要一个大小由W X H X3个uchar构成的内存块。但是,出于效率考虑,每行会填补一些额外的像素。这是因为,如果行的长度是4或8的倍数,一些多媒体处理芯片(如Intel的MMX架构)可以更高效的处理图像。这些额外的像素不会被显示或者保存,填补的值将被忽略。

opencv将填补后一行的长度指定为关键字。如果图像没有对行进行填补,那么图像的有效宽度就等于图像的真实宽度。成员变量cols代表图像的宽度(图像的列数),rows代表图像的高度,step代表以字节为单位的图像的有效宽度。即使你的图像的元素类型不是uchar,step仍然代表着行的字节数。像素的大小可以由elemSize函数得到:对于一个三通道的short型矩阵(CV_16SC3),elemSize返回6。图像的通道数可以channels方法得到。total函数返回矩阵的像素个数。

 1 #include "highgui.h"
 2 #include "cxcore.h"
 3 #include "cv.h"
 4 #include "cmath"
 5 void colorReduce(cv::Mat &, int );
 6 
 7 int main()
 8 {
 9     cv::Mat tp1 = cv::imread("1.bmp");
10     cv::Mat tp2 = tp1.clone();//深拷贝
11     colorReduce(tp2, 64);
12     cv::imshow("1",tp1);
13     cv::imshow("2",tp2);
14     imwrite("2.jpg",tp1);//保存图片
15 std::cout << "all pix_count = " << tp1.total() << std::endl;//获得图像像素个数
16 std::cout << "channels = " << tp1.channels() << std::endl;//获得图像通道数
17 std::cout << "pix_size = " << tp1.elemSize() << std::endl;//获得每个像素占内存大小
18     cv::waitKey(0);
19     return 0;
20 }
21 
22 
23 
24 void colorReduce(cv::Mat &tp, int div)
25 {
26     int nl = tp.rows; //行数
27     int nc = tp.cols * tp.channels(); //每行的元素个数
28     for (int j = 0; j < nl; j++)
29     {
30         uchar * data = tp.ptr<uchar>(j);//获取第j行的首地址
31         for(int i = 0; i < nc; i++)
32         {
33             data[i] = data[i] / div * div + div / 2;
34         }
35     }
36 
37 }

效果如下:

技术分享        技术分享

all pix_count = 405504
channels = 3
pix_size = 3
请按任意键继续. . .

(1)为了简化指针运算,cv::Mat提供了ptr函数可以得到图像任意行的首地址。ptr函数是一个模板函数,它返回第j行的首地址:

uchar * data = image.ptr<uchar>(j);

 (2) cv::Mat tp2 = tp1.clone();复制一幅图片。

 cv::Mat tp2;
 tp2.create(tp1.rows, tp1.cols, tp1.type());

create函数创建的图像内存都是连续的,create函数不会对图像的行进行填补。分配的内存大小为total()* elemSize()。

 

2 高效遍历连续图像

考虑到效率,图像有可能会在行尾扩大若干个像素。但是,当不对行进行填补时,图像可以被视为一个长为WxH的一维数组。我们可以通过cv::Mat 的一个成员函数isContinuous 来判断这幅图像是否对行进行了填补。如果返回值为真的话,说明这幅图像没有对行进行填补。在一些图像处理算法中,我们可以利用图像的连续性,把整个处理过程使用一个循环完成。

3 底层指针运算

在类cv::Mat中,图像数据以unsigned char 形式保存在一块内存中。这块内存的首地址可以通过data成员变量得到。data是一个unsigned char 型的指针,所以循环可以以如下方式开始:

 uchar * data = image.data;

从当前行到下一行可以通过对指针加上行宽完成:

data += image.step;

step代表图像的行宽(包括填补对象)。通常而言,可以通过如下方式获得第j行、第i列像素的地址:

data = image.data + j * image.step + i * image.elemSize();

4 使用迭代器遍历图像

在面向对象的编程中,遍历数据集合通常是通过迭代器来完成的。迭代器是一种特殊的类,它专门用来遍历集合中的各个元素,同时隐藏了在给定的集合上元素迭代的具体实现方式。这种信息隐蔽原则的使用使得遍历集合更加容易。另外,不管数据类型是什么,我们都可以使用类似的方式遍历集合。标准模板库(STL)为每个容器类型都提供了迭代器。OpenCV同样为cv::Mat提供了与STL迭代器兼容的迭代器。(略)

指针遍历图像方法总结

标签:

原文地址:http://www.cnblogs.com/tianpeng-blog/p/4560909.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!