【opencv學習筆記七】訪問圖像中的像素與圖像亮度對比度調整


 今天我們來看一下如何訪問圖像的像素,以及如何改變圖像的亮度與對比度。


 在之前我們先來看一下圖像矩陣數據的排列方式。我們以一個簡單的矩陣來說明:

對單通道圖像排列如下:

對於雙通道圖像排列如下:

那么對於三通道的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學習筆記七】訪問圖像中的像素與圖像亮度對比度調整

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM