1. 程式人生 > >OpenCV影象畫素操作及效率分析

OpenCV影象畫素操作及效率分析

        學習OpenCV也幾個月了,雖然對OpenCV有些瞭解,但是感覺基礎還是沒打實,在這在介紹一下OpenCV的畫素操作,以及OpenCV讀取影象的格式和讀取影象的效率分析。當然文章也有很多沒有介紹到的地方,希望大家多多指教,相互交流。

        在計算機視覺應用中,對於影象內容的讀取分析是第一步,所以學習高效的處理影象是很有用的。一個影象有可能包含數以萬計的畫素,從根本上說影象就是一系列畫素值,所以OpenCV使用資料結構cv::Mat來表示影象。矩陣中每一個元素都代表一個畫素,對於灰度影象,畫素用8位無符號數,0表示黑色,255表示白色。對於彩色畫素而言,每個畫素需要三位這樣的8位無符號數來表示,即三個通道(R,G,B),矩陣則依次儲存一個畫素的三個通道的值,然後再儲存下一個畫素點。

       cv::Mat中,成員變數cols代表影象的寬度(影象的列數),成員變數rows代表影象的高度(影象的行數),step代表以位元組為單位的影象的有效寬度,elemSize返回畫素的大小,畫素的大小 = 顏色大小(位元組)*通道數,比如三通道short型矩陣(CV_16SC3)的大小為2*3 = 6,畫素的channels方法返回影象的通道數,total函式返回影象的畫素數。

       閒話少說直接介紹幾種讀取方式:

       RGB影象的顏色數目是256*256*256,本文對影象進行量化,縮減顏色數目到256的1/8(即32*32*32)為目標,分別利用一下幾種方法實現,比較幾種方法的安全和效率。

       1.ptr遍歷影象

       cv::Mat中提供ptr函式訪問任意一行畫素的首地址,特別方便影象的一行一行的橫向訪問,如果需要一列一列的縱向訪問影象,就稍微麻煩一點。但是ptr訪問效率比較高,程式也比較安全,有越界判斷。

      int nl= image.rows; //行數
      int nc= image.cols * image.channels(); // 每行的元素個數,每行的畫素數*顏色通道數(RGB = 3)
              
      for (int j=0; j<nl; j++) {
          uchar* data= image.ptr<uchar>(j);
          for (int i=0; i<nc; i++) { 
            // process each pixel ---------------------              
               data[i]= data[i]/div*div + div/2;
            // end of pixel processing ----------------
          } // end of line                   
      }
    也可以使用:
      for (int j=0; j<nl; j++) {
	  uchar* data= image.ptr<uchar>(j);
          for (int i=0; i<nc; i++) {
            // process each pixel ---------------------               
		*data++= *data/div*div + div/2;
            // end of pixel processing ----------------
            } // end of line                   
      }
   

2.使用迭代器遍歷影象

     cv::Mat 同樣有標準模板庫(STL),可以使用迭代器訪問資料。

     用迭代器來遍歷影象畫素,可簡化過程降低出錯的機會,比較安全,不過效率較低;如果想避免修改輸入影象例項cv::Mat,可採用const_iterator。iterator有兩種呼叫方法,cv::MatIterator_<cv::Vec3b> it;cv::Mat_<cv::Vec3b>::iterator it;中間cv::Vec3b是因為影象是彩色影象,3通道,cv::Vec3b可以代表一個畫素。

// get iterators
	  cv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();
	  cv::Mat_<cv::Vec3b>::iterator itend= image.end<cv::Vec3b>();

	  for ( ; it!= itend; ++it) {
        
		// process each pixel ---------------------

        (*it)[0]= (*it)[0]/div*div + div/2;
        (*it)[1]= (*it)[1]/div*div + div/2;
        (*it)[2]= (*it)[2]/div*div + div/2;

        // end of pixel processing ----------------
	  }

3.at方法遍歷

      cv::Mat也是向量,可以使at方法取值,使用呼叫方法image.at<cv::Vec3b>(j,i),at方法方便,直接給i,j賦值就可以隨意訪問影象中任何一個畫素,其中j表示第j行,i表示該行第i個畫素。但是at方法效率是這3中訪問方法中最慢的一個,所以如果遍歷影象或者訪問畫素比較多時,建議不要使用這個方法,畢竟程式的效率還是比程式的可讀性要重要的。下面是完整的呼叫方法,其執行時間在下面會介紹。

	  int nl= image.rows; // number of lines
	  int nc= image.cols; // number of columns
              
      for (int j=0; j<nl; j++) {
          for (int i=0; i<nc; i++) {
 
            // process each pixel ---------------------
                 
                  image.at<cv::Vec3b>(j,i)[0]=	 image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
                  image.at<cv::Vec3b>(j,i)[1]=	 image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
                  image.at<cv::Vec3b>(j,i)[2]=	 image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
 
            // end of pixel processing ----------------
 
            } // end of line                   
      }

  4.row(i),col(i)

       cv::Mat提供image.row(i),和image.col(j)對影象整行和整列進行處理,處理比較方便,因為很少遇到,所以就沒有進行效率比較

  程式程式碼如下(三通道):

result.row(0).setTo(cv::Scalar(0,0,0));//將第一行資料設為零
	result.row(result.rows-1).setTo(cv::Scalar(0,0,0));//將最後一行資料設定為零
	result.col(0).setTo(cv::Scalar(0,0,0));//將第一列資料設為零
	result.col(result.cols-1).setTo(cv::Scalar(0,0,0));//將最後一列資料設為零

5.高效率影象遍歷迴圈

       對於迭代的for迴圈,外面一層的迴圈次數越少速度越快,同樣是cols*rows*channels()次迴圈,使用nc = cols,nl = rows*channels(),與使用nc = cols*rows*channels,nl = 1,以及nc = cols,nl = rows,for函式最裡層執行三次顏色的處理。這三種方法最快的是第二種,第三種其次,最慢的是第一種. 
	  int nl= image.rows; // number of lines
	  int nc= image.cols * image.channels(); // total number of elements per line

	  if (image.isContinuous())  {
		  // then no padded pixels
		  nc= nc*nl; 
		  nl= 1;  // it is now a 1D array
	   }

	  int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
	  // mask used to round the pixel value
	  uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
              
      for (int j=0; j<nl; j++) {

		  uchar* data= image.ptr<uchar>(j);

          for (int i=0; i<nc; i++) {
 
            // process each pixel ---------------------
                 
            *data++= *data&mask + div/2;
 
            // end of pixel processing ----------------
 
            } // end of line                   
      }

   6.位運算代替乘法和除法

          int nl= image.rows; // number of lines
	  int nc= image.cols * image.channels(); // total number of elements per line
	  int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
	  // mask used to round the pixel value
	  uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
              
      for (int j=0; j<nl; j++) {

		  uchar* data= image.ptr<uchar>(j);

          for (int i=0; i<nc; i++) {
 
            // process each pixel ---------------------
                 
            *data++= *data&mask + div/2;
 
            // end of pixel processing ----------------
 
            } // end of line                   
      }

執行時間比較,以測試圖片為例,(Debug模式下的時間)

   ptr函式的兩種方法的時間:
                using .ptr and [] =3.50202ms
                using .ptr and * ++ =3.26124ms
   迭代器方法:
              using Mat_ iterator =143.06ms
   at方法的執行時間:
             using at =252.779ms
   高效率迭代方法:
            using .ptr and * ++ and bitwise (continuous) =2.68335ms
   位運算方法:

            using .ptr and * ++ and bitwise =2.59823ms

還有一些比較的函式方法,現在只給出執行時間:
         using .ptr and * ++ and modulo =3.78029ms
         using .ptr and * ++ and bitwise =2.59823ms
         using direct pointer arithmetic =2.57317ms
         using .ptr and * ++ and bitwise with image.cols * image.channels() =22.864ms
         using Mat_ iterator and bitwise =139.92ms
         using MatIterator_ =185.996ms
        using .ptr and * ++ and bitwise (continuous+channels) =2.11271ms
         using input/output images =2.97717ms
         using overloaded operators =2.7237ms

源影象:


量化處理結果 image1:


量化處理後的結果 image2:


 

相關推薦

OpenCV影象操作效率分析

        學習OpenCV也幾個月了,雖然對OpenCV有些瞭解,但是感覺基礎還是沒打實,在這在介紹一下OpenCV的畫素操作,以及OpenCV讀取影象的格式和讀取影象的效率分析。當然文章也有很多沒有介紹到的地方,希望大家多多指教,相互交流。         在計

opencv影象操作方法

影象容器Mat Mat和Matlab裡的陣列格式有點像,但一般是二維向量,如果是灰度圖,一般存放<uchar>型別;如果是RGB彩色圖,存放<Vec3b>型別。 單通道灰度圖資料存放格式: 多通道的影象中,每列並列存放通道數量的子列,如RGB三通

OpenCV二值化影象操作

二值化影象畫素不是0就是255,資料型別為uchar。所以訪問方法是: // 這裡inputmat是二值化影象的mat inputmat.at<uchar>(y, x); 判斷是否為白色的方法: if (inputmat.at<uchar&g

opencv影象值讀取

說到影象畫素,肯定要先認識一下影象中的座標系長什麼樣。 1. 座標體系中的零點座標為圖片的左上角,X軸為影象矩形的上面那條水平線;Y軸為影象矩形左邊的那條垂直線。該座標體系在諸如結構體Mat,Rect,

Android遊戲Graphics繪圖之影象操作

  我們在玩遊戲時經常會看到一些影象的特效,比如半透明等效果。要實現這些效果並不難,只需要對影象本身的畫素執行操作。Android中的 Bitmap同樣提供了操作畫素的方法,可以通過getPixels方法來獲得該影象的畫素並放到一個數組中,我們處理這個畫素陣列就可以了,最後

opencv 影象讀取顯示和操作

從今天起,開始從頭系統學習下opencv,下面記錄下影象的基本操作: 1. 影象的讀取和顯示 Mat image = imread(imagePath, 1); 第二個引數表示圖片讀入的方式(

Python-OpenCV 處理影象(三):影象操作

https://segmentfault.com/a/1190000003742442 0x01. 畫素 有兩種直接操作圖片畫素點的方法: 第一種辦法就是將一張圖片看成一個多維的list,例如對於一張圖片im,想要操作第四行第四列的畫素點就直接 im[3,3] 就可以獲取到這個點的RGB值。 第二種就是

opencv影象進行操作

用Opencv對影象畫素進行操作大概有以下三種方式: 1. One using the Inbuilt macro 2. One using the pointer to the image data 3. Getting the raw data from the image. =============

運用opencv 讀取BMP影象資訊 程式碼實現

1. 環境:Win7(64位),opencv2.3,vs2010 2.程式碼: /////////////////////////////////////////////////////////////////////////////////////////////////

OpenCV學習筆記(三)之影象的提取

     提取影象的畫素及畫素索引 Mat src, dst; src = imread("mountainandwater.jpg"); //讀取影象 if (src.empty()) { qDebug()<<"can

opencv讀取影象值讀取並儲存到txt檔案(二)灰度圖

#include "stdafx.h" #include"cv.h" #include <stdlib.h> #include <stdio.h> #include <math.h> #include <fstream> #include &l

opencv讀取影象值讀取並儲存到txt檔案(一)RGB

#include “stdafx.h” #include"cv.h" #include <stdlib.h> #include <stdio.h> #include <math.h> #include #include #include “iost

openCV--訪問影象的三個方法

方法一       指標訪問:C操作符[ ] 方法二       迭代器iterater 方法三        動態地址計算 訪問速度上,debug模式下 ,方法一 > 方法二 > 方法

常用的操作演算法:影象加法、混合、提取影象中的ROI

影象可以是看成是一個多維的陣列。讀取一張圖片,可以看成是讀入了一系列的畫素內容。這些畫素內容,按

OpenCV——修改影象(隨心所欲)

這一節將講述OpenCV——修改影象畫素,根據自己需要新增特定的畫素部分 原圖如下,我們就是先在這個視訊流上新增一條直線段(有一定寬度的) 現在我們想新增一條,135行-455行,列350--360的直線段 #include<opencv2/opencv.hp

opencv中對影象點訪問的三種方法利用程式進行解讀

程式碼放到自己的工程中,執行就可以的 #include <opencv2\opencv.hpp> #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #includ

opencv之訪問影象

訪問畫素的三種方法 ①指標訪問:最快 ②迭代器iterator:較慢,非常安全,指標訪問可能出現越界問題 ③動態地址計算:更慢,通過at()實現。適用於訪問具體某個第i行,j列的畫素,而不適用遍歷畫素 Mat在記憶體中儲存形式   灰度圖的儲存形式        RGB的儲存形式    一般情況下,Mat

opencv之訪問影象的 三種方法

訪問畫素的三種方法 ①指標訪問:最快 ②迭代器iterator:較慢,非常安全,指標訪問可能出現越界問題 ③動態地址計算:更慢,通過at()實現。適用於訪問具體某個第i行,j列的畫素,而不適用遍歷畫素 Mat在記憶體中儲存形式   灰度圖的儲存形式        RGB的儲存形式    一般情況下,M

opencv訪問影象

(1) 假設你要訪問第k通道、第i行、第j列的畫素。 (2) 間接訪問: (通用,但效率低,可訪問任意格式的影象) 對於單通道位元組型影象: IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); CvSc

opencv 通過指標訪問影象值,輸出為空的問題

for (int i = 0; i < img_roi_gray_at.rows; ++i) { uchar* datatemp = img_roi_gray_at.ptr<uchar>(i);