為什么要使用顏色縮減
在對單通道圖像進行處理時,像素的可能值為256個,但處理多通道時,像素的處理就會相當麻煩,其實用這些顏色中具有代表性的一小部分就可以達到同樣的效果,所以顏色空間縮減就可以派上用場了。一個信道(channel)有256個不同的值(2^8=256),但是如果使用的是GRB方案,三個channel的話,顏色的數量就會變為256256256,大概是16個million這么多,這么多的顏色數量,對于計算機來說仍然是一個負擔,所以可以想一些方法來降低這些色彩數量。
顏色縮減簡單原理
公式:I_new = (I_old/10)*10; //利用int類型向下取整的特點
代碼:
/******************************顏色空間縮減*******************************************/
//顏色空間縮減:將現有的顏色空間值除以某個輸入值,以獲得較少的顏色數,比如:顏色值0-9->0,10-19->10,以此類推
//公式:I_new = (I_old/10)*10; //利用int類型向下取整的特點
//可以將256種計算結果存入表table中
/*
int divdeWith = 10;
uchar table[256];
for(int i=0;i<256;++i)
{table[i]= divdeWith * (i/divdeWith); //table[i]存放的是值為i的像素縮小顏色空間的結果
}
p[j]=table[p[j]];
算法步驟:
1、遍歷圖像矩陣的每一個像素
2、對像素應用上述公式
*/
/************************通過指針、迭代器、動態地址訪問元素 進行顏色縮減****************************************/
/*
訪問像素的三種方法:
1、指針訪問:C操作符[];
2、迭代器 :iterator
3、動態地址計算
*///-----------------------------------------------顏色縮減函數------------------------------------------------
void colorReduce(Mat& inputImage,Mat& outputImage,int div,int type);//-----------------------------------------------主函數------------------------------------------------
int main()
{Mat srcImage = imread("D:\\opencv_picture_test\\miku2.jpg", 2 | 4);namedWindow("原始圖", WINDOW_NORMAL);//WINDOW_NORMAL允許用戶自由伸縮窗口imshow("原始圖", srcImage);Mat dstImage;dstImage.create(srcImage.rows, srcImage.cols, srcImage.type()); //大小類型和原圖一樣double time0 = static_cast<double>(getTickCount()); //記錄起始時間//顏色縮減colorReduce(srcImage, dstImage,128,3);//一系列處理之后time0 = ((double)getTickCount() - time0) / getTickFrequency();cout << "此方法運行時間為:" << time0 << "秒" << endl; //輸出運行時間namedWindow("效果圖", WINDOW_NORMAL);//WINDOW_NORMAL允許用戶自由伸縮窗口imshow("效果圖", dstImage);imwrite("D:\\opencv_picture_test\\miku5.jpg", dstImage);waitKey(0);return 0;}
//-----------------------------------------------colorReduce()函數------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div, int type)
{//描述:使用指針訪問 ,很好理解,和C類似if (type == 1){outputImage = inputImage.clone(); //因為我們需要進行處理時不對原圖像產生影響,所以這里使用深復制int row_num = outputImage.rows; //行數int col_num = outputImage.cols * outputImage.channels(); //列數*通道數 = 每一行元素的個數 灰度圖通道數為1,彩色的為3//雙重循環,遍歷所有像素值for (int i = 0;i < row_num;i++) //行循環{uchar* data = outputImage.ptr<uchar>(i); //獲取第i行的首地址for (int j = 0;j < col_num;j++) //列循環{data[j] = data[j] / div * div; //顏色縮減,也可以是其他的一些處理}}}//描述:使用迭代器訪問/*我們僅僅需要獲得圖像矩陣的begin和end,然后增加迭代直至begin到end。將*操作符添加在迭代指針前,即可訪問當前指向的內容,相比用指針直接訪問可能出現的越界問題,迭代器絕對是非常安全的*/else if (type == 2){outputImage = inputImage.clone(); //因為我們需要進行處理時不對原圖像產生影響,所以這里使用深復制//獲取迭代器Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); //初始位置的迭代器Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>(); //初始位置的迭代器//存取彩色圖像像素for (;it != itend;++it){//-------【開始處理每個像素】---------------(*it)[0] = (*it)[0] / div * div;(*it)[1] = (*it)[1] / div * div;(*it)[2] = (*it)[2] / div * div;//-------【處理結束】---------------}}//描述:使用動態地址運算配合at訪問else{outputImage = inputImage.clone(); //因為我們需要進行處理時不對原圖像產生影響,所以這里使用深復制int row_num = outputImage.rows; //行數int col_num = outputImage.cols ; //列數//雙重循環,遍歷所有像素值for (int i = 0;i < row_num;i++) //行循環{ for (int j = 0;j < col_num;j++) //列循環{//-------【開始處理每個像素】---------------outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div * div; //B通道outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div * div; //G通道outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div * div; //R通道//-------【處理結束】---------------}}/*講解:成員函數at(int y,int x)可以用來存取圖像元素,但在編譯時需要知道圖像的數據類型,at方法本身不會對任何數據類型進行轉化,十分重要!!!存取彩色圖像的代碼可以寫成如下形式:image.at<Vec3b>(j,i)[channel] =value;opencv彩色圖像的順序存儲是按照BGR不是RGB!!!!*/ }
}
/****************************************************************/
處理效果: