OpenCV遍历图像

前言

在图形处理中,遍历每个像素点是最基本的功能,是做算法的基础,这篇文章来总结一下OpenCV遍历图像的几种方法。
本文章参考文档OpenCV tutorials的how_to_scan_images.cpp例子。

最有效率–指针

用c语言直接访问是最有效率的,最快的,下面是简单的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int scan_image_c(Mat &I)
{
int channels = I.channels();
if (channels != 3)
{
printf("test support only three channel.\n");
return -1;
}

for (int i = 0; i < I.rows; i++)
{
Vec3b *ptr = I.ptr<Vec3b>(i);
for (int j = 0; j < I.cols; j++)
{
ptr[j][0] = 0;
ptr[j][1] = 0;
ptr[j][2] = 0;
}
}
return 0;
}

最安全–迭代器

迭代器是C++中的一个概念,因为迭代器从用户手中接管了一些工作,它会保证访问的安全,所以必然会导致一些性能上的降低,简单例子如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int scan_image_iterator(Mat &I)
{
int channels = I.channels();
if (channels != 3)
{
printf("test support only three channel.\n");
return -1;
}

MatIterator_<Vec3b> it, end;
for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
(*it)[0] = 0;
(*it)[1] = 0;
(*it)[2] = 0;
}
return 0;
}

最便捷–at方法

OpenCV的Mat类中有一个at方法,它可以直接返回某个像素点,示例如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int scan_image_random(Mat &I)
{
int channels = I.channels();
if (channels != 3)
{
printf("test support only three channel.\n");
return -1;
}

for( int i = 0; i < I.rows; ++i)
{
for( int j = 0; j < I.cols; ++j
{
I.at<Vec3b>(i, j)[0] = 0;
I.at<Vec3b>(i, j)[1] = 0;
I.at<Vec3b>(i, j)[2] = 0;
}
}

return 0;
}

完整测试代码例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int scan_image_c(Mat &I);
int scan_image_iterator(Mat &I);
int scan_image_random(Mat &I);

int main( int argc, char* argv[])
{
if (argc != 2)
{
cout << "input parameters failed!" << endl;
return -1;
}

Mat I;
I = imread(argv[1], IMREAD_COLOR);

if (I.empty())
{
cout << "The image" << argv[1] << " could not be loaded." << endl;
return -1;
}

const int times = 100;
double t = 0;

t = (double)getTickCount();

for (int i = 0; i < times; ++i)
{
cv::Mat clone_i = I.clone();
scan_image_c(clone_i);
}

t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;

cout << "Time of scan_image_c (averaged for "
<< times << " runs): " << t << " ms."<< endl;

t = (double)getTickCount();

for (int i = 0; i < times; ++i)
{
cv::Mat clone_i = I.clone();
scan_image_iterator(clone_i);
}

t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;

cout << "Time of scan_image_iterator (averaged for "
<< times << " runs): " << t << " ms."<< endl;

t = (double)getTickCount();

for (int i = 0; i < times; ++i)
{
cv::Mat clone_i = I.clone();
scan_image_random(clone_i);
}

t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;

cout << "Time of scan_image_random (averaged for "
<< times << " runs): " << t << " ms."<< endl;

return 0;
}

int scan_image_c(Mat &I)
{
int channels = I.channels();
if (channels != 3)
{
printf("test support only three channel.\n");
return -1;
}

for (int i = 0; i < I.rows; i++)
{
Vec3b *ptr = I.ptr<Vec3b>(i);
for (int j = 0; j < I.cols; j++)
{
ptr[j][0] = 0;
ptr[j][1] = 0;
ptr[j][2] = 0;
}
}
return 0;
}

int scan_image_iterator(Mat &I)
{
int channels = I.channels();
if (channels != 3)
{
printf("test support only three channel.\n");
return -1;
}

MatIterator_<Vec3b> it, end;
for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
(*it)[0] = 0;
(*it)[1] = 0;
(*it)[2] = 0;
}
return 0;
}

int scan_image_random(Mat &I)
{
int channels = I.channels();
if (channels != 3)
{
printf("test support only three channel.\n");
return -1;
}

for( int i = 0; i < I.rows; ++i)
{
for( int j = 0; j < I.cols; ++j
{
I.at<Vec3b>(i, j)[0] = 0;
I.at<Vec3b>(i, j)[1] = 0;
I.at<Vec3b>(i, j)[2] = 0;
}
}

return 0;
}

运行结果

运行结果如下:

1
2
3
Time of scan_image_c        (averaged for 100 runs): 2.04884 ms.
Time of scan_image_iterator (averaged for 100 runs): 4.77701 ms.
Time of scan_image_random (averaged for 100 runs): 3.64237 ms.

从数据上看,c语言的方法确实是最快的,和其他两种方式拉开了一定的差距。而at遍历比迭代器遍历快了不少。
在平常使用中,我们可以根据每个方法的优点去选择不同的方法。