IplImage像素周遊和Mat之間的轉換請看下一篇博文,
學習總結:
- 當Mat為多通道時,如3通道,如果我們将其内容輸出到終端,則可以看出其列數為Mat::cols的n倍,意思是說顯示資料的時候r、g、b安順輸出,然後輸出下一個像素的r、g、b,當然n為Mat的通道數。雖是如此,但是Mat::cols的數值并沒有随之改變。
- 當複制一副圖像時,利用函數cv::Mat::clone(),則将在記憶體中重新開辟一段新的記憶體存放複制的圖像(圖像資料也将全部複制),而如果利用cv::Mat::copyTo()複制圖像,則不會在記憶體中開辟一段新的記憶體塊,同時也不會複制圖像資料,複制前後的圖像的指針指向同一個記憶體塊。使用的時候需注意兩個函數的差別。
- 因為自從openCv可以用c++方式實作之後,我們可以使用疊代器和at的方式方式周遊像素,不過at幾乎是所有方式中效率最低的一種,iterator的方式比使用at的方式好一點,這種方式比較安全和簡單易懂明了。
- 在openCv 2計算機視覺一書中降到的像素壓縮:利用位操作的算法效率最高,其次是利用整數除法中向下取整,效率最低的是取模運算。
代碼如下,可以仔細研究一下和分析一下:
/***************************************************************
*
* 内容摘要:本例采用8種方法對圖像Mat的像素進行掃描,并對像素點的像
* 素進行壓縮,壓縮間隔為div=64,并比較掃描及壓縮的效率,效
* 率最高的是采用.ptr及減少循環次數來周遊圖像,并采用位操
* 作來對圖像像素進行壓縮。
* 參考資料:《OpenCV 2 computer Vision Application Programming
* cookbook》
*
***************************************************************/
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
//利用.ptr和數組下标進行圖像像素周遊
void colorReduce0(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols * image.channels();
//周遊圖像的每個像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
data[i] = data[i]/div*div+div/2; //減少圖像中顔色總數的關鍵算法:if div = 64, then the total number of colors is 4x4x4;整數除法時,是向下取整。
}
}
}
//利用.ptr和 *++ 進行圖像像素周遊
void colorReduce1(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols * image.channels();
//周遊圖像的每個像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
*data++ = *data/div*div + div/2;
}
}
}
//利用.ptr和數組下标進行圖像像素周遊,取模運算用于減少圖像顔色總數
void colorReduce2(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols * image.channels();
//周遊圖像的每個像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
data[i] = data[i]-data[i]%div +div/2; //利用取模運算,速度變慢,因為要讀每個像素兩次
}
}
}
//利用.ptr和數組下标進行圖像像素周遊,位操作運算用于減少圖像顔色總數
void colorReduce3(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols * image.channels();
int n = static_cast<int>(log(static_cast<double>(div))/log(2.0)); //div=64, n=6
uchar mask = 0xFF<<n; //e.g. div=64, mask=0xC0
//周遊圖像的每個像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
*data++ = *data&mask + div/2;
}
}
}
//形參傳入const conference,故輸入圖像不會被修改;利用.ptr和數組下标進行圖像像素周遊
void colorReduce4(const cv::Mat &image, cv::Mat &result,int div = 64)
{
int nl = image.rows;
int nc = image.cols * image.channels();
result.create(image.rows,image.cols,image.type());
//周遊圖像的每個像素
for(int j=0; j<nl ;++j)
{
const uchar *data_in = image.ptr<uchar>(j);
uchar *data_out = result.ptr<uchar>(j);
for(int i=0; i<nc; ++i)
{
data_out[i] = data_in[i]/div*div+div/2; //減少圖像中顔色總數的關鍵算法:if div = 64, then the total number of colors is 4x4x4;整數除法時,是向下取整。
}
}
}
//利用.ptr和數組下标進行圖像像素周遊,并将nc放入for循環中(比較糟糕的做法)
void colorReduce5(cv::Mat &image, int div = 64)
{
int nl = image.rows;
//周遊圖像的每個像素
for(int j=0; j<nl ;++j)
{
uchar *data = image.ptr<uchar>(j);
for(int i=0; i<image.cols * image.channels(); ++i)
{
data[i] = data[i]/div*div+div/2; //減少圖像中顔色總數的關鍵算法:if div = 64, then the total number of colors is 4x4x4;整數除法時,是向下取整。
}
}
}
//利用疊代器 cv::Mat iterator 進行圖像像素周遊
void colorReduce6(cv::Mat &image, int div = 64)
{
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; //利用operator[]處理每個通道的像素
(*it)[1] = (*it)[1]/div*div+div/2;
(*it)[2] = (*it)[2]/div*div+div/2;
}
}
//利用.at<cv::Vec3b>(j,i)進行圖像像素周遊
void colorReduce7(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols;
//周遊圖像的每個像素
for(int j=0; j<nl ;++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;
}
}
}
//減少循環次數,進行圖像像素周遊,調用函數較少,效率最高。
void colorReduce8(cv::Mat &image, int div = 64)
{
int nl = image.rows;
int nc = image.cols;
//判斷是否是連續圖像,即是否有像素填充
if(image.isContinuous())
{
nc = nc*nl;
nl = 1;
}
int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
uchar mask = 0xFF<<n;
//周遊圖像的每個像素
for(int j=0; j<nl ;++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;
}
}
}
const int NumTests = 9; //測試算法的數量
const int NumIteration = 20; //疊代次數
int main(int argc, char* argv[])
{
int64 t[NumTests],tinit;
cv::Mat image1;
cv::Mat image2;
//數組初始化
int i=0;
while(i<NumTests)
{
t[i++] = 0;
}
int n = NumIteration;
//疊代n次,取平均數
for(int i=0; i<n; ++i)
{
image1 = cv::imread("../boldt.jpg");
if(!image1.data)
{
std::cout<<"read image failue!"<<std::endl;
return -1;
}
// using .ptr and []
tinit = cv::getTickCount();
colorReduce0(image1);
t[0] += cv::getTickCount() - tinit;
// using .ptr and *++
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce1(image1);
t[1] += cv::getTickCount() - tinit;
// using .ptr and [] and modulo
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce2(image1);
t[2] += cv::getTickCount() - tinit;
// using .ptr and *++ and bitwise
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce3(image1);
t[3] += cv::getTickCount() - tinit;
//using input and output image
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce4(image1,image2);
t[4] += cv::getTickCount() - tinit;
// using .ptr and [] with image.cols * image.channels()
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce5(image1);
t[5] += cv::getTickCount() - tinit;
// using .ptr and *++ and iterator
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce6(image1);
t[6] += cv::getTickCount() - tinit;
//using at
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce7(image1);
t[7] += cv::getTickCount() - tinit;
//using .ptr and * ++ and bitwise (continuous+channels)
image1 = cv::imread("../boldt.jpg");
tinit = cv::getTickCount();
colorReduce8(image1);
t[8] += cv::getTickCount() - tinit;
}
cv::namedWindow("Result");
cv::imshow("Result",image1);
cv::namedWindow("Result Image");
cv::imshow("Result Image",image2);
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 input and output image = "<<1000*t[4]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using .ptr and [] with image.cols * image.channels() = "<<1000*t[5]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using .ptr and *++ and iterator = "<<1000*t[6]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using at = "<<1000*t[7]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<"using .ptr and * ++ and bitwise (continuous+channels) = "<<1000*t[8]/cv::getTickFrequency()/n<<"ms"<<std::endl;
std::cout<<std::endl<<"-------------------------------------------------------------------------"<<std::endl<<std::endl;
cv::waitKey();
return 0;
}