OpenCV學習C++接口:圖像遍歷+像素壓縮


編譯環境:VS2010+OpenCV2.3.1

學習體會:

  1. 當Mat為多通道時,如3通道,如果我們將其內容輸出到終端,則可以看出其列數為Mat::colsn倍,當然nMat的通道數。雖是如此,但是Mat::cols的數值並沒有隨之改變。
  2. 當復制一副圖像時,利用函數cv::Mat::clone(),則將在內存中重新開辟一段新的內存存放復制的圖像(圖像數據也將全部復制),而如果利用cv::Mat::copyTo()復制圖像,則不會在內存中開辟一段新的內存塊,同時也不會復制圖像數據,復制前后的圖像的指針指向同一個內存塊。使用的時候需注意兩個函數的區別。
  3. 為了避免函數參數傳遞時出現復制情況,函數的形參多采用傳遞reference,如cv::Mat &image,傳遞輸入圖像的引用,不過這樣函數可能會對輸入圖像進行修改,並反映到輸出結果上;如果想避免修改輸入圖像,則函數形參可傳遞const reference,這樣輸入圖像不會被修改,同時可以創建一個輸出圖像Mat,將函數處理的結果保存到輸出圖像Mat中,例如:void colorReduce4(const cv::Mat &image, cv::Mat &result,int div = 64)。
  4. 采用迭代器iterator來遍歷圖像像素,可簡化過程,比較安全,不過效率較低;如果想避免修改輸入圖像實例cv::Mat,可采用const_iterator
  5. 遍歷圖像時,不要采用.at()方式,這種效率最低。
  6. 進行圖像像素壓縮時,利用位操作的算法效率最高,其次是利用整數除法中向下取整,效率最低的是取模運算。
  7. 設計函數時,需要檢查計算效率來提高程序的性能,不過以犧牲程序的可讀性來提高代碼執行的效率並不是一個明智的選擇。
  8. 執行效率情況見程序運行結果。

參考資料:《OpenCV 2 Computer Vision Application Programming Cookbook》

 

  1 /***************************************************************
  2 *
  3 *    內容摘要:本例采用8種方法對圖像Mat的像素進行掃描,並對像素點的像
  4 *            素進行壓縮,壓縮間隔為div=64,並比較掃描及壓縮的效率,效
  5 *            率最高的是采用.ptr及減少循環次數來遍歷圖像,並采用位操
  6 *            作來對圖像像素進行壓縮。
  7 *   作    者:Jacky Liu
  8 *   完成日期:2012.8.10
  9 *   參考資料:《OpenCV 2 computer Vision Application Programming
 10 *              cookbook》
 11 *
 12 ***************************************************************/
 13 #include <opencv2/core/core.hpp>
 14 #include <opencv2/imgproc/imgproc.hpp>
 15 #include <opencv2/highgui/highgui.hpp>
 16 #include <iostream>
 17 
 18 
 19 //利用.ptr和數組下標進行圖像像素遍歷
 20 void colorReduce0(cv::Mat &image, int div = 64)
 21 {
 22     int nl = image.rows;
 23     int nc = image.cols * image.channels();
 24     
 25     //遍歷圖像的每個像素
 26     for(int j=0; j<nl ;++j)
 27     {
 28         uchar *data = image.ptr<uchar>(j);
 29         for(int i=0; i<nc; ++i)
 30         {
 31             data[i] = data[i]/div*div+div/2;     //減少圖像中顏色總數的關鍵算法:if div = 64, then the total number of colors is 4x4x4;整數除法時,是向下取整。
 32         }
 33     }
 34 }
 35 
 36 
 37 //利用.ptr和 *++ 進行圖像像素遍歷
 38 void colorReduce1(cv::Mat &image, int div = 64)
 39 {
 40     int nl = image.rows;
 41     int nc = image.cols * image.channels();
 42     
 43     //遍歷圖像的每個像素
 44     for(int j=0; j<nl ;++j)
 45     {
 46         uchar *data = image.ptr<uchar>(j);
 47         for(int i=0; i<nc; ++i)
 48         {
 49             *data++ = *data/div*div + div/2;
 50         }
 51     }
 52 }
 53 
 54 
 55 //利用.ptr和數組下標進行圖像像素遍歷,取模運算用於減少圖像顏色總數
 56 void colorReduce2(cv::Mat &image, int div = 64)
 57 {
 58     int nl = image.rows;
 59     int nc = image.cols * image.channels();
 60     
 61     //遍歷圖像的每個像素
 62     for(int j=0; j<nl ;++j)
 63     {
 64         uchar *data = image.ptr<uchar>(j);
 65         for(int i=0; i<nc; ++i)
 66         {
 67             data[i] = data[i]-data[i]%div +div/2;  //利用取模運算,速度變慢,因為要讀每個像素兩次
 68         }
 69     }
 70 }
 71 
 72 //利用.ptr和數組下標進行圖像像素遍歷,位操作運算用於減少圖像顏色總數
 73 void colorReduce3(cv::Mat &image, int div = 64)
 74 {
 75     int nl = image.rows;
 76     int nc = image.cols * image.channels();
 77 
 78     int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));   //div=64, n=6
 79     uchar mask = 0xFF<<n;                                            //e.g. div=64, mask=0xC0
 80     
 81     //遍歷圖像的每個像素
 82     for(int j=0; j<nl ;++j)
 83     {
 84         uchar *data = image.ptr<uchar>(j);
 85         for(int i=0; i<nc; ++i)
 86         {
 87             *data++ = *data&mask + div/2;
 88         }
 89     }
 90 }
 91 
 92 //形參傳入const conference,故輸入圖像不會被修改;利用.ptr和數組下標進行圖像像素遍歷
 93 void colorReduce4(const cv::Mat &image, cv::Mat &result,int div = 64)
 94 {
 95     int nl = image.rows;
 96     int nc = image.cols * image.channels();
 97 
 98     result.create(image.rows,image.cols,image.type());
 99     
100     //遍歷圖像的每個像素
101     for(int j=0; j<nl ;++j)
102     {
103         const uchar *data_in = image.ptr<uchar>(j);
104         uchar *data_out = result.ptr<uchar>(j);
105         for(int i=0; i<nc; ++i)
106         {
107             data_out[i] = data_in[i]/div*div+div/2;     //減少圖像中顏色總數的關鍵算法:if div = 64, then the total number of colors is 4x4x4;整數除法時,是向下取整。
108         }
109     }
110 }
111 
112 //利用.ptr和數組下標進行圖像像素遍歷,並將nc放入for循環中(比較糟糕的做法)
113 void colorReduce5(cv::Mat &image, int div = 64)
114 {
115     int nl = image.rows;
116     
117     //遍歷圖像的每個像素
118     for(int j=0; j<nl ;++j)
119     {
120         uchar *data = image.ptr<uchar>(j);
121         for(int i=0; i<image.cols * image.channels(); ++i)
122         {
123             data[i] = data[i]/div*div+div/2;     //減少圖像中顏色總數的關鍵算法:if div = 64, then the total number of colors is 4x4x4;整數除法時,是向下取整。
124         }
125     }
126 }
127 
128 //利用迭代器 cv::Mat iterator 進行圖像像素遍歷
129 void colorReduce6(cv::Mat &image, int div = 64)
130 {
131     cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();    //由於利用圖像迭代器處理圖像像素,因此返回類型必須在編譯時知道
132     cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
133 
134     for(;it != itend; ++it)
135     {
136         (*it)[0] = (*it)[0]/div*div+div/2;        //利用operator[]處理每個通道的像素
137         (*it)[1] = (*it)[1]/div*div+div/2;
138         (*it)[2] = (*it)[2]/div*div+div/2;
139     }
140 }
141 
142 //利用.at<cv::Vec3b>(j,i)進行圖像像素遍歷
143 void colorReduce7(cv::Mat &image, int div = 64)
144 {
145     int nl = image.rows;
146     int nc = image.cols;
147     
148     //遍歷圖像的每個像素
149     for(int j=0; j<nl ;++j)
150     {
151         for(int i=0; i<nc; ++i)
152         {
153             image.at<cv::Vec3b>(j,i)[0] = image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
154             image.at<cv::Vec3b>(j,i)[1] = image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
155             image.at<cv::Vec3b>(j,i)[2] = image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
156         }
157     }
158 }
159 
160 //減少循環次數,進行圖像像素遍歷,調用函數較少,效率最高。
161 void colorReduce8(cv::Mat &image, int div = 64)
162 {
163     int nl = image.rows;
164     int nc = image.cols;
165 
166     //判斷是否是連續圖像,即是否有像素填充
167     if(image.isContinuous())
168     {
169         nc = nc*nl;
170         nl = 1;
171     }
172 
173     int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
174     uchar mask = 0xFF<<n;
175     
176     //遍歷圖像的每個像素
177     for(int j=0; j<nl ;++j)
178     {
179         uchar *data = image.ptr<uchar>(j);
180         for(int i=0; i<nc; ++i)
181         {
182             *data++ = *data & mask +div/2;
183             *data++ = *data & mask +div/2;
184             *data++ = *data & mask +div/2;
185         }
186     }
187 }
188 
189 const int NumTests = 9;        //測試算法的數量
190 const int NumIteration = 20;   //迭代次數
191 
192 int main(int argc, char* argv[])
193 {
194     int64 t[NumTests],tinit;
195     cv::Mat image1;
196     cv::Mat image2;
197     
198     //數組初始化
199     int i=0;
200     while(i<NumTests)
201     {
202         t[i++] = 0;
203     }
204 
205     int n = NumIteration;
206     
207     //迭代n次,取平均數
208     for(int i=0; i<n; ++i)
209     {
210         image1 = cv::imread("../boldt.jpg");
211 
212         if(!image1.data)
213         {
214             std::cout<<"read image failue!"<<std::endl;
215             return -1;
216         }
217 
218         // using .ptr and []
219         tinit = cv::getTickCount();
220         colorReduce0(image1);
221         t[0] += cv::getTickCount() - tinit;
222         
223         // using .ptr and *++
224         image1 = cv::imread("../boldt.jpg");
225         tinit = cv::getTickCount();
226         colorReduce1(image1);
227         t[1] += cv::getTickCount()  - tinit;
228         
229         // using .ptr and [] and modulo
230         image1 = cv::imread("../boldt.jpg");
231         tinit = cv::getTickCount();
232         colorReduce2(image1);
233         t[2] += cv::getTickCount()  - tinit;
234         
235         // using .ptr and *++ and bitwise
236         image1 = cv::imread("../boldt.jpg");
237         tinit = cv::getTickCount();
238         colorReduce3(image1);
239         t[3] += cv::getTickCount()  - tinit;
240 
241         //using input and output image
242         image1 = cv::imread("../boldt.jpg");
243         tinit = cv::getTickCount();
244         colorReduce4(image1,image2);
245         t[4] += cv::getTickCount()  - tinit;
246         
247         // using .ptr and [] with image.cols * image.channels()
248         image1 = cv::imread("../boldt.jpg");
249         tinit = cv::getTickCount();
250         colorReduce5(image1);
251         t[5] += cv::getTickCount()  - tinit;
252         
253         // using .ptr and *++ and iterator
254         image1 = cv::imread("../boldt.jpg");
255         tinit = cv::getTickCount();
256         colorReduce6(image1);
257         t[6] += cv::getTickCount()  - tinit;
258         
259         //using at
260         image1 = cv::imread("../boldt.jpg");
261         tinit = cv::getTickCount();
262         colorReduce7(image1);
263         t[7] += cv::getTickCount()  - tinit;
264 
265         //using .ptr and * ++ and bitwise (continuous+channels)
266         image1 = cv::imread("../boldt.jpg");
267         tinit = cv::getTickCount();
268         colorReduce8(image1);
269         t[8] += cv::getTickCount()  - tinit;
270     }
271 
272     cv::namedWindow("Result");
273     cv::imshow("Result",image1);
274     cv::namedWindow("Result Image");
275     cv::imshow("Result Image",image2);
276 
277     std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;
278     std::cout<<"using .ptr and [] = "<<1000*t[0]/cv::getTickFrequency()/n<<"ms"<<std::endl;
279     std::cout<<"using .ptr and *++ = "<<1000*t[1]/cv::getTickFrequency()/n<<"ms"<<std::endl;
280     std::cout<<"using .ptr and [] and modulo = "<<1000*t[2]/cv::getTickFrequency()/n<<"ms"<<std::endl;
281     std::cout<<"using .ptr and *++ and bitwise = "<<1000*t[3]/cv::getTickFrequency()/n<<"ms"<<std::endl;
282     std::cout<<"using input and output image = "<<1000*t[4]/cv::getTickFrequency()/n<<"ms"<<std::endl;
283     std::cout<<"using .ptr and [] with image.cols * image.channels() = "<<1000*t[5]/cv::getTickFrequency()/n<<"ms"<<std::endl;
284     std::cout<<"using .ptr and *++ and iterator = "<<1000*t[6]/cv::getTickFrequency()/n<<"ms"<<std::endl;
285     std::cout<<"using at = "<<1000*t[7]/cv::getTickFrequency()/n<<"ms"<<std::endl;
286     std::cout<<"using .ptr and * ++ and bitwise (continuous+channels) = "<<1000*t[8]/cv::getTickFrequency()/n<<"ms"<<std::endl;
287     std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;
288     cv::waitKey();
289     return 0;
290 }

 

運行結果圖:


免責聲明!

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



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