【OpenCV】訪問Mat中每個像素的值(新)(III)
在《OpenCV 2 Computer Vision Application Programming Cookbook》看到的例子,非常不錯,算是對之前的文章<訪問Mat圖像中每個像素的值>的回顧和補充。
Color Reduce
還是使用經典的Reduce Color的例子,即對圖像中的像素表達進行量化。如常見的RGB24圖像有256×256×256中顏色,通過Reduce Color將每個通道的像素減少8倍至256/8=32種,則圖像隻有32×32×32種顏色。假設量化減少的倍數是N,則代碼實現時就是簡單的value/N*N,通常我們會再加上N/2以得到相鄰的N的倍數的中間值,最後圖像被量化為(256/N)×(256/N)×(256/N)種顏色。
方法零:.ptr和[]操作符
Mat最直接的訪問方法是通過.ptr<>函數得到一行的指針,並用[]操作符訪問某一列的像素值。
- // using .ptr and []
- void colorReduce0(cv::Mat &image, int div=64) {
- int nr= image.rows; // number of rows
- int nc= image.cols * image.channels(); // total number of elements per line
- for (int j=0; j<nr; j++) {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- data[i]= data[i]/div*div + div/2;
- }
- }
- }
方法一:.ptr和指針操作
除了[]操作符,我們可以移動指針*++的組合方法訪問某一行中所有像素的值。
- // using .ptr and * ++
- void colorReduce1(cv::Mat &image, int div=64) {
- int nr= image.rows; // number of rows
- int nc= image.cols * image.channels(); // total number of elements per line
- for (int j=0; j<nr; j++) {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- *data++= *data/div*div + div/2;
- } // end of row
- }
- }
方法二:.ptr、指針操作和取模運算
方法二和方法一的訪問方式相同,不同的是color reduce用模運算代替整數除法
- // using .ptr and * ++ and modulo
- void colorReduce2(cv::Mat &image, int div=64) {
- int nr= image.rows; // number of rows
- int nc= image.cols * image.channels(); // total number of elements per line
- for (int j=0; j<nr; j++) {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- int v= *data;
- *data++= v - v%div + div/2;
- } // end of row
- }
- }
方法三:.ptr、指針運算和位運算
由於進行量化的單元div通常是2的整次方,因此所有的乘法和除法都可以用位運算表示。
- // using .ptr and * ++ and bitwise
- void colorReduce3(cv::Mat &image, int div=64) {
- int nr= image.rows; // number of rows
- 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<nr; j++) {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- *data++= *data&mask + div/2;
- } // end of row
- }
- }
方法四:指針運算
方法四和方法三量化處理的方法相同,不同的是用指針運算代替*++操作。
- // direct pointer arithmetic
- void colorReduce4(cv::Mat &image, int div=64) {
- int nr= image.rows; // number of rows
- 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));
- int step= image.step; // effective width
- // mask used to round the pixel value
- uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
- // get the pointer to the image buffer
- uchar *data= image.data;
- for (int j=0; j<nr; j++) {
- for (int i=0; i<nc; i++) {
- *(data+i)= *data&mask + div/2;
- } // end of row
- data+= step; // next line
- }
- }
方法五:.ptr、*++、位運算以及image.cols * image.channels()
這種方法就是沒有計算nc,基本是個充數的方法。
- // using .ptr and * ++ and bitwise with image.cols * image.channels()
- void colorReduce5(cv::Mat &image, int div=64) {
- int nr= image.rows; // number of rows
- 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<nr; j++) {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<image.cols * image.channels(); i++) {
- *data++= *data&mask + div/2;
- } // end of row
- }
- }
方法六:連續圖像
Mat提供了isContinuous()函數用來查看Mat在內存中是不是連續存儲,如果是則圖片被存儲在一行中。
- // using .ptr and * ++ and bitwise (continuous)
- void colorReduce6(cv::Mat &image, int div=64) {
- int nr= image.rows; // number of rows
- int nc= image.cols * image.channels(); // total number of elements per line
- if (image.isContinuous()) {
- // then no padded pixels
- nc= nc*nr;
- nr= 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<nr; j++) {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- *data++= *data&mask + div/2;
- } // end of row
- }
- }
方法七:continuous+channels
與方法六基本相同,也是充數的。
- // using .ptr and * ++ and bitwise (continuous+channels)
- void colorReduce7(cv::Mat &image, int div=64) {
- int nr= image.rows; // number of rows
- int nc= image.cols ; // number of columns
- if (image.isContinuous()) {
- // then no padded pixels
- nc= nc*nr;
- nr= 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<nr; j++) {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- *data++= *data&mask + div/2;
- *data++= *data&mask + div/2;
- *data++= *data&mask + div/2;
- } // end of row
- }
- }
方法八:Mat _iterator
真正有區別的方法來啦,用Mat提供的迭代器代替前麵的[]操作符或指針,血統純正的官方方法~
- // using Mat_ iterator
- void colorReduce8(cv::Mat &image, int div=64) {
- // 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) {
- (*it)[0]= (*it)[0]/div*div + div/2;
- (*it)[1]= (*it)[1]/div*div + div/2;
- (*it)[2]= (*it)[2]/div*div + div/2;
- }
- }
方法九:Mat_ iterator 和位運算
把方法八中的乘除法換成位運算。
- // using Mat_ iterator and bitwise
- void colorReduce9(cv::Mat &image, int div=64) {
- // div must be a power of 2
- 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
- // 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) {
- (*it)[0]= (*it)[0]&mask + div/2;
- (*it)[1]= (*it)[1]&mask + div/2;
- (*it)[2]= (*it)[2]&mask + div/2;
- }
- }
方法十:MatIterator_
和方法八基本相同。
- // using MatIterator_
- void colorReduce10(cv::Mat &image, int div=64) {
- cv::Mat_<cv::Vec3b> cimage= image;
- cv::Mat_<cv::Vec3b>::iterator it=cimage.begin();
- cv::Mat_<cv::Vec3b>::iterator itend=cimage.end();
- for ( ; it!= itend; it++) {
- (*it)[0]= (*it)[0]/div*div + div/2;
- (*it)[1]= (*it)[1]/div*div + div/2;
- (*it)[2]= (*it)[2]/div*div + div/2;
- }
- }
方法十一:圖像坐標
- // using (j,i)
- void colorReduce11(cv::Mat &image, int div=64) {
- int nr= image.rows; // number of rows
- int nc= image.cols; // number of columns
- for (int j=0; j<nr; j++) {
- for (int i=0; i<nc; i++) {
- 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 row
- }
- }
方法十二:創建輸出圖像
之前的方法都是直接修改原圖,方法十二新建了輸出圖像,主要用於後麵的時間對比。
- // with input/ouput images
- void colorReduce12(const cv::Mat &image, // input image
- cv::Mat &result, // output image
- int div=64) {
- int nr= image.rows; // number of rows
- int nc= image.cols ; // number of columns
- // allocate output image if necessary
- result.create(image.rows,image.cols,image.type());
- // created images have no padded pixels
- nc= nc*nr;
- nr= 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<nr; j++) {
- uchar* data= result.ptr<uchar>(j);
- const uchar* idata= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- *data++= (*idata++)&mask + div/2;
- *data++= (*idata++)&mask + div/2;
- *data++= (*idata++)&mask + div/2;
- } // end of row
- }
- }
方法十三:重載操作符
Mat重載了+&等操作符,可以直接將兩個Scalar(B,G,R)數據進行位運算和數學運算。
- // using overloaded operators
- void colorReduce13(cv::Mat &image, int div=64) {
- 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
- // perform color reduction
- image=(image&cv::Scalar(mask,mask,mask))+cv::Scalar(div/2,div/2,div/2);
- }
時間對比
通過迭代二十次取平均時間,得到每種方法是運算時間如下。
可以看到,指針*++訪問和位運算是最快的方法;而不斷的計算image.cols*image.channles()花費了大量重複的時間;另外迭代器訪問雖然安全,但性能遠低於指針運算;通過圖像坐標(j,i)訪問時最慢的,使用重載操作符直接運算效率最高。
(轉載請注明作者和出處:https://blog.csdn.net/xiaowei_cqu 未經允許請勿用於商業用途)
最後更新:2017-04-03 05:40:09