今天我们来看一下如何访问图像的像素,以及如何改变图像的亮度与对比度。
在之前我们先来看一下图像矩阵数据的排列方式。我们以一个简单的矩阵来说明:
对单通道图像排列如下:
对于双通道图像排列如下:
那么对于三通道的RGB图像则为:
知道了排列方式之后我们来讨论一下访问图像像素常用的三种方式:
1.使用指针访问;
2.使用迭代器访问;
3.使用动态地址访问;
为了比较一下三种方式的效率,我们介绍两个函数来统计一下每种方式所需的时间。
int64 getTickCount()函数:返回CPU自某个时间(如开启电脑)以来走过的时钟周期数。
double getTickFrequency()函数:返回每秒钟CPU走过的时钟周期数。
然后我们来看第一种方式。
1.使用指针访问图像像素:我们将输入图像img_src的每一个像素值加上50后赋值给输出图像img_dst。
1 intmain()2 {3 intc;4 Mat img_src = imread("1.jpg");5 Mat img_dst;6
7 namedWindow("原图");8 namedWindow("处理图");9
10 int channels = img_src.channels();//获取图像通道数
11 img_dst =img_src.clone();12
13 double time1 = static_cast(getTickCount());//获取开始处理前时间
14
15 for (int i = 0; i < img_src.rows; i++)//访问图像行数据
16 {17 uchar* p_data1 = img_src.ptr(i);//获取图像行首地址
18 uchar* p_data2 = img_dst.ptr(i);//获取图像行首地址
19 for (int k = 0; k < img_src.cols*channels; k++)//获取图像列(含通道)
20 {21 p_data2[k] = saturate_cast(p_data1[k] + 50);//图像处理22
23 //*p_data2++ = saturate_cast((*p_data1++) + 100);//与上一行图像处理的等效方式24 //*(p_data2 + k) = saturate_cast(*(p_data1 + k) + 50);//同上
25 }26 }27
28 double time2 = static_cast(getTickCount());//获取结束处理时间
29
30 time1 = (time2 - time1) / getTickFrequency();//计算处理所用时间
31 cout << "指针访问像素时间(S):" << time1 <
33 while (1)34 {35 imshow("原图", img_src);//显示图像
36 imshow("处理图", img_dst);//显示图像
37 c = waitKey(0);38 if (c == 27 || char(c) == 'q' || char(c) == 'Q')//按下Q键或者ESC键退出程序
39 break;40 }41 return 0;42 }
2.使用迭代器方式:
1 intmain()2 {3 intc;4 Mat img_src = imread("1.jpg");5 Mat img_dst;6
7 namedWindow("原图");8 namedWindow("处理图");9
10 int channels = img_src.channels();//获取图像通道数
11 img_dst =img_src.clone();12
13 double time1 = static_cast(getTickCount());//获取开始处理前时间
14
15 Mat_::iterator it = img_src.begin();//获取原图开始地址
16 Mat_::iterator itend = img_src.end();//获取原图结束地址
17 Mat_::iterator it2 = img_dst.begin();//获取输出图开始地址
18 for (; it != itend; ++it, ++it2)19 {20 for (int i = 0; i < 3; i++)21 {22 (*it2)[i] = saturate_cast((*it)[i] + 50);//图像处理
23 }24 }25
26 double time2 = static_cast(getTickCount());//获取结束处理时间
27
28 time1 = (time2 - time1) / getTickFrequency();//计算处理所用时间
29 cout << "指针访问像素时间(S):" << time1 <
31 while (1)32 {33 imshow("原图", img_src);//显示图像
34 imshow("处理图", img_dst);//显示图像
35 c = waitKey(0);36 if (c == 27 || char(c) == 'q' || char(c) == 'Q')//按下Q键或者ESC键退出程序
37 break;38 }39 return 0;40 }
3.动态地址方式:
1 intmain()2 {3 intc;4 Mat img_src = imread("1.jpg");5 Mat img_dst;6
7 namedWindow("原图");8 namedWindow("处理图");9
10 int channels = img_src.channels();//获取图像通道数
11 img_dst =img_src.clone();12
13 double time1 = static_cast(getTickCount());//获取开始处理前时间
14
15 for (int i = 0; i < img_src.rows; i++)16 {17 for (int k = 0; k < img_src.cols; k++)18 {19 for (int j = 0; j < channels; j++)20 {21 img_dst.at(i, k)[j] = saturate_cast(img_src.at(i, k)[j] + 50);22 }23 }24 }25
26 double time2 = static_cast(getTickCount());//获取结束处理时间
27
28 time1 = (time2 - time1) / getTickFrequency();//计算处理所用时间
29 cout << "指针访问像素时间(S):" << time1 <
31 while (1)32 {33 imshow("原图", img_src);//显示图像
34 imshow("处理图", img_dst);//显示图像
35 c = waitKey(0);36 if (c == 27 || char(c) == 'q' || char(c) == 'Q')//按下Q键或者ESC键退出程序
37 break;38 }39 return 0;40 }
我们来看一下处理的结果吧:
实例
下面我们来看一个完整调用三种方式的例子,我们定义三个函数Mat image_bright1(Mat src);Mat image_bright2(Mat src);Mat image_bright3(Mat src);分别用来用三种方式处理图片。
1 //************头文件包含*************
2 #include "stdafx.h"
3 #include
4 #include//包含opencv的头文件5 //***********************************6
7
8 //************命名空间***************
9 using namespace cv;//使用opencv命名空间
10 using namespacestd;11 //***********************************12
13
14 //************全局变量***************15
16 //***********************************17
18
19 //************全局函数***************
20 Mat image_bright1(Mat src);//使用指针访问像素
21
22 Mat image_bright2(Mat src);//使用迭代器访问像素
23
24 Mat image_bright3(Mat src);//使用动态地址访问像素25 //***********************************26
27
28 //************主函数*****************
29 intmain()30 {31 intc;32 Mat img_src = imread("1.jpg");33 Mat img_dst1, img_dst2, img_dst3;34
35 namedWindow("原图",0);36 namedWindow("指针访问像素",0);37 namedWindow("迭代器访问像素",0);38 namedWindow("动态地址访问像素",0);39
40 double time1 = static_cast(getTickCount());41 img_dst1 = image_bright1(img_src);//使用指针访问像素
42
43 double time2 = static_cast(getTickCount());44 img_dst2 = image_bright2(img_src);//使用迭代器访问像素
45
46 double time3 = static_cast(getTickCount());47 img_dst3 = image_bright3(img_src);//使用动态地址访问像素
48
49 double time4 = static_cast(getTickCount());50
51 time1 = (time2 - time1) /getTickFrequency();52 time2 = (time3 - time2) /getTickFrequency();53 time3 = (time4 - time3) /getTickFrequency();54
55 cout << "指针访问像素时间(S):"<
59 while (1)60 {61 imshow("原图", img_src);//显示图像
62 imshow("指针访问像素", img_dst1);//显示图像
63 imshow("迭代器访问像素", img_dst2);//显示图像
64 imshow("动态地址访问像素", img_dst3);//显示图像
65
66 c = waitKey(0);67 if (c == 27 || char(c) == 'q' || char(c) == 'Q')//按下Q键或者ESC键退出程序
68 break;69 }70
71 return 0;72 }73
74
75 //使用指针访问像素
76 Mat image_bright1(Mat src)77 {78 Mat dst;79 int channels =src.channels();80 dst =src.clone();81
82 for (int i = 0; i < src.rows; i++)83 {84 uchar* p_data1 =src.ptr(i);85 uchar* p_data2 =dst.ptr(i);86
87 for (int k = 0; k < src.cols*channels; k++)88 {89 //*p_data2++ = saturate_cast((*p_data1++) + 100);90 //*(p_data2 + k) = saturate_cast(*(p_data1 + k) + 50);
91 p_data2[k] = saturate_cast(p_data1[k] + 50);//输出图像像素=原图像像素+50
92 }93 }94 returndst;95 }96
97
98 //使用迭代器访问像素
99 Mat image_bright2(Mat src)100 {101 Mat dst;102 dst =src.clone();103
104 Mat_::iterator it = src.begin();105 Mat_::iterator itend = src.end();106 Mat_::iterator it2 = dst.begin();107 for (; it != itend; ++it, ++it2)108 {109 for (int i = 0; i < 3; i++)110 {111 (*it2)[i] = saturate_cast(2*(*it)[i]);//输出图像像素=2*原图像像素
112 }113 }114 returndst;115 }116
117
118 //使用动态地址访问像素
119 Mat image_bright3(Mat src)120 {121 Mat dst;122 int channels =src.channels();123 dst =src.clone();124 for (int i = 0; i < src.rows; i++)125 {126 for (int k = 0; k < src.cols; k++)127 {128 for (int j = 0; j < channels; j++)129 {130 dst.at(i, k)[j] = saturate_cast(2*src.at(i, k)[j] + 50);//输出图像像素=2*原图像像素+50
131 }132 }133 }134 returndst;135 }
结果:
从时间上我们可以看出来,使用指针的速度是最快的。
有些童鞋应该已经看出来了,在三种方法中我们将图像像素的处理方法变了一下,得出的图像也不一样了。在三种方法中我们处理像素的计算方式分别为:
输出图像像素=原图像像素+50;
输出图像像素=2*原图像像素;
输出图像像素=2*原图像像素+50;
其实这就是处理亮度与对比度的方法,从图像上也能看出来。
总结一下:g(x)=k*f(x)+b;其中g(x)为输出图像,f(x)为输入图像;
调节k的值则可以改变图像的对比度;
调节b的值则可以改变图像的亮度;
下载
功能很简单,代码很少,建议自己写一下或者在博文中复制一下,当然实在是懒的不要不要的土豪可以去下面的连接直接下载。