OpenCV中高效的像素遍历方法,写出工程级像素遍历代码(关注效率)
本文非原创,而是转载自《OpenCV学堂》
Mat对象结构
初次接触OpenCV的开发者,必须过的第一道坎就是学会如何遍历访问Mat对象中每个像素,实现像素级别的图像操作,这个是最级别的编程技能,但是不同的像素遍历方法效率有云泥之别,相差特别大,甚至可能成为算法运行的瓶颈之一,因此找到一种速度快的遍历方法对大图像处理是很关键的。在开始寻找高效遍历方法之前,先来了解一下Mat对象的数据组织形式与像素块数据的存储方式,Mat对象由两个部分组成,元数据头部加像素数据块部分,图示如下:
在OpenCV C++中Mat对象的内存管理由OpenCV框架自动负责内存分配与回收,基于智能指针实现内存管理。
以下介绍几种遍历方法:
方法一
基于Mat对象的随机像素访问API实现,通过行列索引方式遍历每个像素值。代码实现如下
void method_1(Mat &image) {double t1 = getTickCount();int w = image.cols;int h = image.rows;for (int row = 0; row < h; row++) {for (int col = 0; col < w; col++) {Vec3b bgr = image.at(row, col);bgr[0] = 255 - bgr[0];bgr[1] = 255 - bgr[1];bgr[2] = 255 - bgr[2];image.at(row, col) = bgr;}}double t2 = getTickCount();double t = ((t2 - t1) / getTickFrequency()) * 1000;ostringstream ss;ss << "Execute time : " << std::fixed << std::setprecision(2) << t << " ms ";putText(image, ss.str(), Point(20, 20), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 255), 2, 8);imshow("result", image);
}
- 单通道遍历(灰度图像)
cv::Mat image(100 , 100 , CV_8UC1);
int rows = image.rows;
int cols = image.cols;for (int i=0; i(i,j) = 20; }
}
格式是image.at
- 多通道遍历(彩色图像)
cv::Mat image(100 , 100 , CV_8UC3);
int rows = image.rows;
int cols = image.cols;for (int i=0; i(i,j)[0]= 20; // B 通道image.at(i,j)[1]= 20; // G 通道image.at(i,j)[2]= 20; // R 通道}
}
格式是image.at
方法二
基于Mat对象的行随机访问指针方式实现对每个像素的遍历,代码实现如下:
void method_2(Mat &image) {double t1 = getTickCount();int w = image.cols;int h = image.rows;for (int row = 0; row < h; row++) {Vec3b* curr = image.ptr(row);for (int col = 0; col < w; col++) {Vec3b bgr = curr[col];bgr[0] = 255 - bgr[0];bgr[1] = 255 - bgr[1];bgr[2] = 255 - bgr[2];}}double t2 = getTickCount();double t = ((t2 - t1) / getTickFrequency()) * 1000;ostringstream ss;ss << "Execute time : " << std::fixed << std::setprecision(2) << t << " ms ";putText(image, ss.str(), Point(20, 20), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 255), 2, 8);imshow("result", image);
}
除了上述的行指针遍历方式,常见的行指针还有如下:
CV_8UC1: 灰度图像
uchar* ptr = image.ptr
CV_8UC3: 彩色图像
Vec3b* ptr = image.ptr
CV_32FC1: 单通道浮点数图像
float* ptr = image.ptr
CV_32FC3: 三通道浮点数图像
Vec3f* ptr = image.ptr
方法三(效率最高,推荐)
直接获取Mat对象的像素块的数据指针,基于指针操作,实现快速像素方法,代码实现如下:
void method_3(Mat &image) {double t1 = getTickCount();int w = image.cols;int h = image.rows;for (int row = 0; row < h; row++) {uchar* uc_pixel = image.data + row*image.step;for (int col = 0; col < w; col++) {uc_pixel[0] = 255 - uc_pixel[0];uc_pixel[1] = 255 - uc_pixel[1];uc_pixel[2] = 255 - uc_pixel[2];uc_pixel += 3;}}double t2 = getTickCount();double t = ((t2 - t1) / getTickFrequency()) * 1000;ostringstream ss;ss << "Execute time : " << std::fixed << std::setprecision(2) << t << " ms ";putText(image, ss.str(), Point(20, 20), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 255), 2, 8);imshow("result", image);
}
实验对比结果
系统信息与软件版本
输入图像大小为:1280x720, 彩色
操作系统:win10
OpenCV版本:OpenCV4.1
CPU:core i7 8th
实事证明,唯一正确的选择是直接使用data指针直接访问,但是这个在OpenCV官方的教程都没有明确说明,官方教程代码都是基于第一种方式,我想主要是让初学者容易理解与入门,这个也导致一些人在做开发的时候直接使用第一种方式做遍历,然后就是代码运行太慢,以后请用正确方式打开Mat对象遍历....
方法四 迭代器(效率最低,不推荐)
cv::Mat img = cv::imread(imagePath.toLocal8Bit().toStdString());
cv::Mat_::iterator it = img.begin();
cv::Mat_::iterator itend = img.end();
for (; it != itend; ++it)
{//Test1:仅遍历像素int blue = (*it)[0];int green = (*it)[1];int red = (*it)[2];//Test2:改变像素值(*it)[0] = 255 - (*it)[0];(*it)[1] = 255 - (*it)[1];(*it)[2] = 255 - (*it)[2];
}
---
参考文章
OpenCV中高效的像素遍历方法,写出工程级像素遍历代码
https://mp.weixin.qq.com/s/jfLdvLgAuCVclWkut9LH4g 关注微信公众号OpenCV学堂
cv::Mat像素遍历方法比较
https://www.jianshu.com/p/fc2f247fc2c4
OpenCV:OpenCV 4.5遍历像素的常见方式及效率对比_累了就要打游戏-CSDN博客_opencv 遍历cv::Mat imageTest;imageTest = cv::imread("F:/test.png");for (int row = 0; row < imageTest.rows; row++){for (int col = 0; col < imageTest.cols; col++){cv::Vec3b bgr = imageTest.athttps://blog.csdn.net/xinjiang666/article/details/105682079
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
