前言
提示:总结了遍历图像像素的三种方法,并对其优劣进行了比较。
一、图像像素的遍历
提示:以下遍历图像像素示例,均实现以下主功能。
#include <iostream>
using namespace std;
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
int main(int argc, char** argv)
{
cv::Mat image = imread("tower.jpg", -1);
imshow("input", image);
.....
imshow("output", image);
waitKey(0);
}
1. at方法遍历
template<typename _Tp >
_Tp& cv::Mat::at (
int row,
int col
)
for (int j = 0; j < image.rows; j++)
{
for (int i = 0; i < image.cols; i++)
{
if (image.type() == CV_8UC1)
{
image.at<uchar>(j, i) = image.at<uchar>(j, i) / 2;
}
else if(image.type() == CV_8UC3)
{
image.at<Vec3b>(j, i)[0] = image.at<Vec3b>(j, i)[0] / 2;
image.at<Vec3b>(j, i)[1] = image.at<Vec3b>(j, i)[1] / 3;
image.at<Vec3b>(j, i)[2] = image.at<Vec3b>(j, i)[2] / 4;
}
}
}
【Tips】 (1)在调用at方法时,必须指定图像元素的类型; (2)因为cv::Mat 可以接受任何类型的元素,你需要指定返回值的预期类型,故at方法被实现成为了一个模板方法。
2. 指针遍历
template<typename _Tp >
using cv::Ptr = typedef std::shared_ptr<_Tp>
for (int j = 0; j < image.rows; j++)
{
uchar* data = image.ptr<uchar>(j);
for (int i = 0; i < image.cols * image.channels(); i++)
{
data[i] = data[i] / 2;
}
}
【Tips】:ptr方法是一个模板方法,返回第j行的地址。
3. 迭代器遍历
cv::Mat_<cv::Vec3b>::Iterator it;
cv::MatIterator_<cv::Vec3b> it;
cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
while (it != itend)
{
*it = *it / 2;
it++;
}
【Tips】:创建迭代器有两种方法:cv::Mat_<cv::Vec3b>::iterator 或 cv::MatIterator_<cv::Vec3b>;
二、遍历效果对比
(一)、需要对程序的处理耗时进行计数
double t = getTickCount();
.....
double timeConsume = (getTickCount() - t) /getTickFrequency();
【Tips】 (1)cv::getTickCount() 函数会返回从最近一次计算机开机到当前的时钟周期数; (2)cv::getTickFrequency() 函数返回计算机每秒的周期数。
(二)、耗时对比 对于一张 499 x 335 的照片,分别遍历其所有的像素,将像素值减半处理,耗时统计结果如下:
遍历方法 | 耗时时间 |
---|
at方法遍历 | 0.231494 | 指针遍历 | 0.0241424 | 迭代器遍历 | 0.262805 |
【结论】 (1)指针遍历图像像素最高效; (2)at方法和迭代器方法遍历图像的速度差不多; (3)at方法适用在需要随机访问像素的时候,绝不要在扫描图像时使用; (4)对于一张连续图像来说,生成一个连续的一维循环,会比对行和列运行常规的二重循环,运行速度可以平均提高10%。
总结
以上就是今天要讲的全部内容。
参考资料
- OpenCV计算机视觉编程攻略(第3版);
- opencv官方文档https://docs.opencv.org/4.5.3/
|