顏色縮減 -利用指針、迭代器、動態地址實現訪問像素

爲什麼要使用顏色縮減

在對單通道圖像進行處理時,像素的可能值爲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!!!!
		*/		
	}
}
/****************************************************************/

處理效果:

原圖
效果圖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章