今天我们来看一下如何访问图像的像素,以及如何改变图像的亮度与对比度。
在之前我们先来看一下图像矩阵数据的排列方式。我们以一个简单的矩阵来说明:
对单通道图像排列如下:
对于双通道图像排列如下:
那么对于三通道的RGB图像则为:
知道了排列方式之后我们来讨论一下访问图像像素常用的三种方式:
1.使用指针访问;
2.使用迭代器访问;
3.使用动态地址访问;
为了比较一下三种方式的效率,我们介绍两个函数来统计一下每种方式所需的时间。
int64 getTickCount()函数:返回CPU自某个时间(如开启电脑)以来走过的时钟周期数。
double getTickFrequency()函数:返回每秒钟CPU走过的时钟周期数。
然后我们来看第一种方式。
1.使用指针访问图像像素:我们将输入图像img_src的每一个像素值加上50后赋值给输出图像img_dst。
1 int main() 2 { 3 int c; 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<double>(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<uchar>(p_data1[k] + 50);//图像处理 22 23 //*p_data2++ = saturate_cast<uchar>((*p_data1++) + 100);//与上一行图像处理的等效方式 24 //*(p_data2 + k) = saturate_cast<uchar>(*(p_data1 + k) + 50);//同上 25 } 26 } 27 28 double time2 = static_cast<double>(getTickCount());//获取结束处理时间 29 30 time1 = (time2 - time1) / getTickFrequency();//计算处理所用时间 31 cout << "指针访问像素时间(S):" << time1 << endl; 32 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 int main() 2 { 3 int c; 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<double>(getTickCount());//获取开始处理前时间 14 15 Mat_<Vec3b>::iterator it = img_src.begin<Vec3b>();//获取原图开始地址 16 Mat_<Vec3b>::iterator itend = img_src.end<Vec3b>();//获取原图结束地址 17 Mat_<Vec3b>::iterator it2 = img_dst.begin<Vec3b>();//获取输出图开始地址 18 for (; it != itend; ++it, ++it2) 19 { 20 for (int i = 0; i < 3; i++) 21 { 22 (*it2)[i] = saturate_cast<uchar>((*it)[i] + 50);//图像处理 23 } 24 } 25 26 double time2 = static_cast<double>(getTickCount());//获取结束处理时间 27 28 time1 = (time2 - time1) / getTickFrequency();//计算处理所用时间 29 cout << "指针访问像素时间(S):" << time1 << endl; 30 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 int main() 2 { 3 int c; 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<double>(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<Vec3b>(i, k)[j] = saturate_cast<uchar>(img_src.at<Vec3b>(i, k)[j] + 50); 22 } 23 } 24 } 25 26 double time2 = static_cast<double>(getTickCount());//获取结束处理时间 27 28 time1 = (time2 - time1) / getTickFrequency();//计算处理所用时间 29 cout << "指针访问像素时间(S):" << time1 << endl; 30 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<iostream> 4 #include<opencv.hpp>//包含opencv的头文件 5 //*********************************** 6 7 8 //************命名空间*************** 9 using namespace cv;//使用opencv命名空间 10 using namespace std; 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 int main() 30 { 31 int c; 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<double>(getTickCount()); 41 img_dst1 = image_bright1(img_src);//使用指针访问像素 42 43 double time2 = static_cast<double>(getTickCount()); 44 img_dst2 = image_bright2(img_src);//使用迭代器访问像素 45 46 double time3 = static_cast<double>(getTickCount()); 47 img_dst3 = image_bright3(img_src);//使用动态地址访问像素 48 49 double time4 = static_cast<double>(getTickCount()); 50 51 time1 = (time2 - time1) / getTickFrequency(); 52 time2 = (time3 - time2) / getTickFrequency(); 53 time3 = (time4 - time3) / getTickFrequency(); 54 55 cout << "指针访问像素时间(S):"<<time1<<endl; 56 cout << "迭代器访问像素时间(S):" << time2 << endl; 57 cout << "动态地址访问像素时间(S):" << time3 << endl; 58 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<uchar>((*p_data1++) + 100); 90 //*(p_data2 + k) = saturate_cast<uchar>(*(p_data1 + k) + 50); 91 p_data2[k] = saturate_cast<uchar>(p_data1[k] + 50);//输出图像像素=原图像像素+50 92 } 93 } 94 return dst; 95 } 96 97 98 //使用迭代器访问像素 99 Mat image_bright2(Mat src) 100 { 101 Mat dst; 102 dst = src.clone(); 103 104 Mat_<Vec3b>::iterator it = src.begin<Vec3b>(); 105 Mat_<Vec3b>::iterator itend = src.end<Vec3b>(); 106 Mat_<Vec3b>::iterator it2 = dst.begin<Vec3b>(); 107 for (; it != itend; ++it, ++it2) 108 { 109 for (int i = 0; i < 3; i++) 110 { 111 (*it2)[i] = saturate_cast<uchar>(2*(*it)[i]);//输出图像像素=2*原图像像素 112 } 113 } 114 return dst; 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<Vec3b>(i, k)[j] = saturate_cast<uchar>(2*src.at<Vec3b>(i, k)[j] + 50);//输出图像像素=2*原图像像素+50 131 } 132 } 133 } 134 return dst; 135 }
结果:
从时间上我们可以看出来,使用指针的速度是最快的。
有些童鞋应该已经看出来了,在三种方法中我们将图像像素的处理方法变了一下,得出的图像也不一样了。在三种方法中我们处理像素的计算方式分别为:
- 输出图像像素=原图像像素+50;
- 输出图像像素=2*原图像像素;
- 输出图像像素=2*原图像像素+50;
其实这就是处理亮度与对比度的方法,从图像上也能看出来。
总结一下:g(x)=k*f(x)+b;其中g(x)为输出图像,f(x)为输入图像;
- 调节k的值则可以改变图像的对比度;
- 调节b的值则可以改变图像的亮度;
下载
功能很简单,代码很少,建议自己写一下或者在博文中复制一下,当然实在是懒的不要不要的土豪可以去下面的连接直接下载。
【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整