您的位置:首页 > 其它

指针遍历图像方法总结

2015-06-08 14:47 113 查看
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函数返回矩阵的像素个数。

#include "highgui.h"
#include "cxcore.h"
#include "cv.h"
#include "cmath"
void colorReduce(cv::Mat &, int );

int main()
{
cv::Mat tp1 = cv::imread("1.bmp");
cv::Mat tp2 = tp1.clone();//深拷贝
colorReduce(tp2, 64);
cv::imshow("1",tp1);
cv::imshow("2",tp2);
imwrite("2.jpg",tp1);//保存图片
std::cout << "all pix_count = " << tp1.total() << std::endl;//获得图像像素个数
std::cout << "channels = " << tp1.channels() << std::endl;//获得图像通道数
std::cout << "pix_size = " << tp1.elemSize() << std::endl;//获得每个像素占内存大小
cv::waitKey(0);
return 0;
}

void colorReduce(cv::Mat &tp, int div)
{
int nl = tp.rows; //行数
int nc = tp.cols * tp.channels(); //每行的元素个数
for (int j = 0; j < nl; j++)
{
uchar * data = tp.ptr<uchar>(j);//获取第j行的首地址
for(int i = 0; i < nc; i++)
{
data[i] = data[i] / div * div + div / 2;
}
}

}


效果如下:





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迭代器兼容的迭代器。(略)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: