爲什麼要使用顏色縮減
在對單通道圖像進行處理時,像素的可能值爲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!!!!
*/
}
}
/****************************************************************/
處理效果: