OpenCV2計算機視覺應用編程手冊(自學版)初級三
// 時間:2014年11月30日00:22:56
// 例子:降低圖像的顏色數目 256*256*256--->64*64*64
/******************************************************************************************
本節列出來中訪問圖像各個像素的方法,有的方法執行速度快一些,有的會慢一些。
採用指針的方法訪問大的圖像數據速度較快,如果用移位運算符代替乘除法運算速度會更加
快一些。如果能夠減少運算的次數,執行速度會增加一些。(廢話好多)
也可以使用迭代器進行訪問,但是運行會慢一些,也可以使用.at的方式直接操作像素,但是
執行效率不高,因爲.at的操作方法一般應用於隨機訪問圖像中的某些點。
// 建議
1-------------- 在循環過程中,要避免本來能夠提前計算的數據反覆的計算,這可以節省很多時間
int nc=image.cols*image.channels();
...
for(int i=0;i<nc;i++)
{
...
}
如果上面的這個循環過程換成
for(int i=0;i<image.cols*image.channels();i++)
{
..
}
在下面這個循環中你需要反覆計算每一行的元素總數,其運算速度比第一個降低80%。
2----------------- 執行速度最快的算法:
/***************************************************
方法: 使用指針指針地址自加移位運算符判斷圖像有沒有padded
採用方法: *data++= *data&mask + div/2;
*data++= *data&mask + div/2;
*data++= *data&mask + div/2;
雖然採用這種方法計算步驟一樣,但是這種方法執行效率較高
這樣可以減少循環的次數。
在調試DM642的時候,程序優化一節也講到了這種用法,
不知道是不是C語言都是這樣。
void colorReduce7(cv::Mat &image, int div=64) {
int nl= image.rows; // number of lines
int nc= image.cols ; // number of columns
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;
*data++= *data&mask + div/2;
*data++= *data&mask + div/2;
// end of pixel processing ----------------
} // end of line
}
}
3--------------- 判斷一個程序運行時間長短的方法
tinit= cv::getTickCount();//開始進行計時
colorReduce10(image1);// 運行我們要測試的程序
time = cv::getTickCount()-tinit;//停止計時
time=time
/cv::getTickFrequency();// us
time=time*1000;//ms
// 最後得到的這個time就是我們的程序實際運行了多長時間,時間單位是ms
**************************************************************************************/
#include "stdafx.h"
#include<iostream>
#include <opencv2/opencv.hpp>
using namespace std;// 使用STD
using namespace cv;// 使用名字空間
// using .ptr and []
/***************************************************
方法0: 使用指針掃描圖像
使用: *
第一個地址對應的是圖像的左上角對應的像素的地址,Opencv中顏色通道的排列是BGR,
和微軟的RGB正好反過來了
****************************************************/
void colorReduce0(cv::Mat &image, int div=64) {
int nl= image.rows; // number of lines
int nc= image.cols * image.channels(); // total number of elements per 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[i]= data[i]/div*div + div/2;
// end of pixel processing ----------------
} // end of line
}
}
/***************************************************
方法1: 使用指針地址自加
指針的地址自動增加
****************************************************/
// using .ptr and * ++
void colorReduce1(cv::Mat &image, int div=64) {
int nl= image.rows; // number of lines
int nc= image.cols * image.channels(); // total number of elements per 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: 使用指針地址自加
****************************************************/
// using .ptr and * ++ and modulo
void colorReduce2(cv::Mat &image, int div=64) {
int nl= image.rows; // number of lines
int nc= image.cols * image.channels(); // total number of elements per line
for (int j=0; j<nl; j++) {
uchar* data= image.ptr<uchar>(j);
for (int i=0; i<nc; i++) {
// process each pixel ---------------------
int v= *data;
*data++= v - v%div + div/2;
// end of pixel processing ----------------
} // end of line
}
}
/***************************************************
方法3: 使用指針地址自加移位操作
使用移位操作運算
****************************************************/
// using .ptr and * ++ and bitwise
void colorReduce3(cv::Mat &image, int div=64) {
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
}
}
/***************************************************
方法4: 使用指針掃描圖像
使用立即指針運算,調用step,使指針指向下一行
的首地址
****************************************************/
// direct pointer arithmetic
void colorReduce4(cv::Mat &image, int div=64) {
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));
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<nl; j++) {
for (int i=0; i<nc; i++) {
// process each pixel ---------------------
*(data+i)= *data&mask + div/2;
// end of pixel processing ----------------
} // end of line
data+= step; // next line
}
}
/***************************************************
方法5: 使用指針地址自加移位操作
指針自加移位操作符
****************************************************/
// using .ptr and * ++ and bitwise with image.cols * image.channels()
void colorReduce5(cv::Mat &image, int div=64) {
int nl= image.rows; // number of lines
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<image.cols * image.channels(); i++) {
// process each pixel ---------------------
*data++= *data&mask + div/2;
// end of pixel processing ----------------
} // end of line
}
}
/***************************************************
方法6: 使用指針地址自加移位操作判斷圖像的每一行的像素有沒有添加Padded
指針的地址自動增加
****************************************************/
// using .ptr and * ++ and bitwise (continuous)
void colorReduce6(cv::Mat &image, int div=64) {
int nl= image.rows; // number of lines
int nc= image.cols * image.channels(); // total number of elements per line
// 判斷圖像的每一行的像素有沒有添加Padded
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
}
}
/***************************************************
方法7: 使用指針指針地址自加移位運算符判斷圖像有沒有padded
採用方法: *data++= *data&mask + div/2;
*data++= *data&mask + div/2;
*data++= *data&mask + div/2;
雖然採用這種方法計算步驟一樣,但是這種方法執行效率較高
這樣可以減少循環的次數。
在調試DM642的時候,程序優化一節也講到了這種用法,
不知道是不是C語言都是這樣。
****************************************************/
// using .ptr and * ++ and bitwise (continuous+channels)
void colorReduce7(cv::Mat &image, int div=64) {
int nl= image.rows; // number of lines
int nc= image.cols ; // number of columns
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;
*data++= *data&mask + div/2;
*data++= *data&mask + div/2;
// end of pixel processing ----------------
} // end of line
}
}
/***************************************************
方法8: 使用迭代器掃描圖像
採用方法:
在面向對象編程中,循環訪問的數據量較大的時候,都是使用
迭代器的方法。
注意:使用迭代器的主要目的是爲了簡化掃描的過程,使掃描
儘可能少的出現錯誤,而並沒有考慮到優化程序的運行過程,所
以採用迭代器的方法運行速度會慢一些。
****************************************************/
// 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) {
// 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 ----------------
}
}
/***************************************************
方法9: 使用迭代器掃描圖像+移位操作(代替除法運算)
採用方法:
在面向對象編程中,循環訪問的數據量較大的時候,都是使用
迭代器的方法。
****************************************************/
// 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>();
// scan all pixels
for ( ; it!= itend; ++it) {
// process each pixel ---------------------
(*it)[0]= (*it)[0]&mask + div/2;
(*it)[1]= (*it)[1]&mask + div/2;
(*it)[2]= (*it)[2]&mask + div/2;
// end of pixel processing ----------------
}
}
/***************************************************
方法10: 使用迭代器模板
採用方法:
在面向對象編程中,循環訪問的數據量較大的時候,都是使用
迭代器的方法。但是迭代器的執行速率好像不是很好,沒接觸過
****************************************************/
// using MatIterator_
void colorReduce10(cv::Mat &image, int div=64) {
// get iterators
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++) {
// 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 ----------------
}
}
/***************************************************
方法11: 使用at方法訪問圖像的像素
注意:
使用at的方法循環掃描大的數據塊的時候,效率是比較低的
at一般用在隨機訪問圖像中某個像素點,而不是訪問大的數據塊。
****************************************************/
void colorReduce11(cv::Mat &image, int div=64) {
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
}
}
/***************************************************
方法12:
在函數中包含了輸入和輸出。這個地方會多佔用一部分時間
多佔用的時間是因爲opencv需要給輸出result分配內存空間
****************************************************/
// with input/ouput images
void colorReduce12(const cv::Mat &image, // input image
cv::Mat &result, // output image
int div=64) {
int nl= image.rows; // number of lines
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*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= result.ptr<uchar>(j);
const uchar* idata= image.ptr<uchar>(j);
for (int i=0; i<nc; i++) {
// process each pixel ---------------------
*data++= (*idata++)&mask + div/2;
*data++= (*idata++)&mask + div/2;
*data++= (*idata++)&mask + div/2;
// end of pixel processing ----------------
} // end of line
}
}
/***************************************************
方法13:
使用運算符重載操作。這個地方Opencv對+進行了運算符重載
這使得我們可以直接對兩個矩陣相加。
大多數的C++ 操作符都被重載了
****************************************************/
// 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);
}
#define NTESTS 14// 需要知道個函數到底都運行了多長時間
#define NITERATIONS 20
int main()
{
int64 t[NTESTS],tinit;
cv::Mat image1;
cv::Mat image2;
// timer values set to 0
for (int i=0; i<NTESTS; i++)
t[i]= 0;
// repeat the tests several times //爲了保證計算的時間準確,我們對上面這14個程序循環運行了20次,然後求平均
int n=NITERATIONS;
for (int k=0; k<n; k++) {
std::cout << k << " of " << n << std::endl;
image1= cv::imread("F:\\house.jpg");
if (!image1.data)
return 0;
// using .ptr and []
tinit= cv::getTickCount();
colorReduce0(image1);
t[0]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using .ptr and * ++
tinit= cv::getTickCount();
colorReduce1(image1);
t[1]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using .ptr and * ++ and modulo
tinit= cv::getTickCount();
colorReduce2(image1);
t[2]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using .ptr and * ++ and bitwise
tinit= cv::getTickCount();
colorReduce3(image1);
t[3]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using direct pointer arithmetic
tinit= cv::getTickCount();
colorReduce4(image1);
t[4]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using .ptr and * ++ and bitwise with image.cols * image.channels()
tinit= cv::getTickCount();
colorReduce5(image1);
t[5]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using .ptr and * ++ and bitwise (continuous)
tinit= cv::getTickCount();
colorReduce6(image1);
t[6]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using .ptr and * ++ and bitwise (continuous+channels)
tinit= cv::getTickCount();
colorReduce7(image1);
t[7]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using Mat_ iterator
tinit= cv::getTickCount();
colorReduce8(image1);
t[8]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using Mat_ iterator and bitwise
tinit= cv::getTickCount();
colorReduce9(image1);
t[9]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using Mat_ iterator
tinit= cv::getTickCount();
colorReduce10(image1);
t[10]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using at
tinit= cv::getTickCount();
colorReduce11(image1);
t[11]+= cv::getTickCount()-tinit;
image1= cv::imread("F:\\house.jpg");
// using input/output images
tinit= cv::getTickCount();
cv::Mat result;
colorReduce12(image1, result);
t[12]+= cv::getTickCount()-tinit;
image2= result;
image1= cv::imread("F:\\house.jpg");
// using input/output images
tinit= cv::getTickCount();
colorReduce13(image1);
t[13]+= cv::getTickCount()-tinit;
//------------------------------
}
cv::namedWindow("Result");
cv::imshow("Result",image2);
cv::namedWindow("Image Result");
cv::imshow("Image Result",image1);
// print average execution time
std::cout << std::endl << "-------------------------------------------" << std::endl << std::endl;
std::cout << "using .ptr and [] =" << 1000.*t[0]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using .ptr and * ++ =" << 1000.*t[1]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using .ptr and * ++ and modulo =" << 1000.*t[2]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using .ptr and * ++ and bitwise =" << 1000.*t[3]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using direct pointer arithmetic =" << 1000.*t[4]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using .ptr and * ++ and bitwise with image.cols * image.channels() =" << 1000.*t[5]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using .ptr and * ++ and bitwise (continuous) =" << 1000.*t[6]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using .ptr and * ++ and bitwise (continuous+channels) =" << 1000.*t[7]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using Mat_ iterator =" << 1000.*t[8]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using Mat_ iterator and bitwise =" << 1000.*t[9]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using MatIterator_ =" << 1000.*t[10]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using at =" << 1000.*t[11]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using input/output images =" << 1000.*t[12]/cv::getTickFrequency()/n << "ms" << std::endl;
std::cout << "using overloaded operators =" << 1000.*t[13]/cv::getTickFrequency()/n << "ms" << std::endl;
cv::waitKey();
return 0;
}
運行結果: