彩圖和灰圖(調色板)

從以下幾個方面談談灰度化,什麼是灰度化?爲什麼要灰度化?灰度化的方法?

之前老不理解王克師兄的基於全畫面是啥意思,今天看了李雪梅.唐萬有《印刷品缺陷檢測的方法研究》終於似乎有些明白。“密度檢測和色度檢測雖然是有效的印刷質量控制方式,但這種控制方式主要依賴檢測局部色塊(測控條),缺陷檢測要求操作者積累的經驗和主觀判斷。” 可以看得出以前的檢測方式是基於局部的,機器視覺的檢測是基於整個畫面的。

20190104更新:昨天終於結束了開題報告,齊春老師的兩個學生分別做的是圖像去霧和2D轉3D,李傑老師的兩個學生做的是相機硬件那一塊。開完題終於可以安心弄自己的了,離放假還有三週時間,不仰望星空,只腳踏實地。0104 - 0111這一週的時間主要還是沿着開題前的灰度化做。主要研究下彩圖和灰圖的種種吧。

一,一些科普吧。

人類能辨別的色彩:人類可以辨別幾千種不同的彩色而只能辨別幾十種不同的灰度。

顏色的本質:顏色的本質是牛頓最早系統研究和發現的,牛頓真是個人才。物質之所以呈現出某種顏色,一般是由於物質有選擇地吸收了白光中的某種波長的光,從而呈現出與之互補的那種光的顏色。

灰色:灰色的一個極端白色反射了所有的光,另一個極端黑色則吸收了所有的光。中間的灰色能夠同樣吸收所有波長的光,只不過同時也會反射所有波長的光,反射的光多顯淺灰色,反射的光少顯深灰色。

彩色圖像處理技術:分爲僞彩色處理技術和真彩色處理技術,前者是將灰度圖像轉化爲彩色圖像的處理,後者是對彩色圖像不同分量的分別處理。都不是我要研究的重點,瞭解即可。

二,彩色模型:從應用的角度看,人們所提出的衆多彩色模型可分成兩類。一類面向諸如彩色顯示器或彩色打印機之類的硬設備,另一類面向視覺感知或者說以彩色處理分析爲目的的應用,如各種圖像處理算法。顏色通常用三個相對獨立的屬性來描述,三個獨立變量綜合作用,自然就構成一個空間座標,這就是顏色空間。而顏色可以由不同的角度,用三個一組的不同屬性加以描述,就產生了不同的顏色空間。但被描述的顏色對象本身是客觀的,不同顏色空間只是從不同的角度去衡量同一個對象。顏色空間按照基本結構可以分兩大類:基色顏色空間和色、亮分離顏色空間。前者的典型是 RGB,還包括 CMY、CMYK、CIE XYZ 等;後者包括 YCC/YUV、Lab、以及一批“色相類顏色空間”。

1,RGB模型

2,CMY模型,利用三基色光疊加產生的光的三補色:藍綠(cyan,藍加綠),品紅(magenta,紅加藍),黃(yellow,紅加綠)。也是顏料的三基色,故主要用於彩色打印。

3,YUV模型,Y是亮度分量,也就是灰度。UV是色度分量,疑問1。跟視覺有關,人類對綠色敏感。

4,前面三種都是面向硬設備的彩色模型,還有一大類是面向視覺感知的彩色模型。最基本的是HSI模型和LAB模型。

三,談談爲什麼要灰度化吧,相機也是有黑白相機的,CCD也是能自帶灰度圖的,這也是我的疑問,也是開題的時候李傑老師問我的。黑白CCD攝像機獲得的圖像經過彩色圖像採集卡模數轉換後,得到的是24位真彩色數字圖像。存疑。

四,編程過程中的重點操作:

    1,將邏輯調色板選入系統調色板:

HPALETTE m_hPalette;
::SelectPalette(pDC -> GetSafeHdc(), m_hPalette, TRUE);
pDC -> SetStretchBltMode(COLORONCOLOR); // 是否必要?

五,具體方法,分爲直接法和間接法,其中直接法又按照是否生成調色板分爲兩種方式。

    1,直接法不生成調色板。直接操作原像素的RGB,注意位圖數據中是以BGR,BGR的形式依次排列的。

步驟如下:
(1) 取真彩色圖像每個象素的R、G、B三個字節分量;
(2) 計算加權灰度值GrayValue=0.3B+0.59G+0.11R;
(3) 令R=G=B=GrayValue,並替換原圖中每個象素的RGB分量。

其中,GrayValue的值有多種取法,這裏採用的是最符合人眼視覺的方法。以下面圖像爲例。採用按行按列的循環方式方法1的灰度化需要123ms.

用不同的循環方式遍歷像素耗時竟然相差10倍以上!!!

	// 用如下這種for循環計算時跳不過因爲要補齊4字節整數倍的空字節,會出現不理想的效果

	/* 以下這種循環用時很少,基本在5ms以內
	for(int i = 0; i < n/3; i++)
	{
		*(m_Dib.m_lpData + i*3) = *(m_Dib.m_lpData + i*3 + 2 );
		*(m_Dib.m_lpData + i*3+1) = *(m_Dib.m_lpData + i*3 + 2 );
	}
	*/ 
	

	// 以下這種循環用時很長,基本在50ms以上
	for(int i = 0; i < m_Dib.GetHeight(); i++)
		for(int j = 0; j < m_Dib.GetWidth(); j++)
		{
			*( m_Dib.m_lpData + i*m_Dib.GetLineByte() + j*3 + 2 ) = *( m_Dib.m_lpData + 
               i*m_Dib.GetLineByte() + j*3 );
			*( m_Dib.m_lpData + i*m_Dib.GetLineByte() + j*3 + 1 ) = *( m_Dib.m_lpData + 
               i*m_Dib.GetLineByte() + j*3 );
		}

用第一種循環方式遍歷每個像素,即不考慮行和列,而是直接看作一個連續的整體,耗時雖然很短,但是如果實驗圖的每行最後需要補0以成爲4的整數倍時,容易出現失真。如下圖所示:

解決方案爲: 

     首先,如果CCD相機拍的圖每行字節數正好是4的整數倍(一般是的)就不用解決這個問題了,相機拍出的圖像是跟相機相關的,比如王蒙師姐實驗室用的CCD面陣相機採的圖都是1500*1000像素,每行有4500字節,自然是4的整數倍,因此不用考慮該問題。

    以下是具體解決方案:

if(m_Dib.GetLineByte() / 3 == 0)                 // 每行像素剛好是4字節整數倍,末尾不用補零
	{
	  for(int i = 0; i < n/3; i++)
	  {
		*(m_Dib.m_lpData + i*3) = *(m_Dib.m_lpData + i*3 + 2 );
		*(m_Dib.m_lpData + i*3+1) = *(m_Dib.m_lpData + i*3 + 2 );
	  }
	}
else                                             // 每行末尾需要補零湊齊4字節整數倍的情況
{
       int nJumpByte = m_Dib.GetLineByte() - m_Dib.GetWidth() * 3;        // 每行補零字節數
                                                                             爲nJumpByte
	   int nCount = 0;                               // 計數,追蹤處理到每行第幾個像素
	   for(int j = 0; j < m_Dib.GetWidth() * m_Dib.GetHeight();j++)
	   {
           *(m_Dib.m_lpData + j*3) = *(m_Dib.m_lpData + j*3 + 2 );
		   *(m_Dib.m_lpData + j*3+1) = *(m_Dib.m_lpData + j*3 + 2 );
		   nCount++;
		   if(nCount == m_Dib.GetWidth())
			   j += nJumpByte;
	   }
}

用了一個if-else結構判斷是否存在末尾補零的情況。具體思想就是遇到末尾零字節自動跳過。因爲RGB的分量灰度化不用計算灰度值,所以基於主色的真彩色圖灰度化是否是一種行之有效的方法?

 2,直接法生成調色板

產生256級灰度調色板。直接將24位位圖轉化爲帶調色板的256級灰度位圖。
步驟如下:
(1) 創建256色位圖的顏色表,順序排列爲(0,0,0),(1,1,1)、⋯(255,255,255);
(2) 據這個顏色表來創建邏輯調色板;計算每個象素像第一種方法一樣;
(3) 改寫圖像象素數據。此時分配內存是原圖像的三分之一,計算每個象素的加權灰度值GrayValue=0.3B+0.59G+0.11R,壓縮三個象素爲只用一個字節記錄,計算灰度後再將這個灰度值直接每字節順序填入到分配的內存中;
(4) 改變文件信息頭中相關信息,此時該圖像文件大小變爲:信息頭結構大小+顏色表結構大小+256(RGBQUAD)結構大小+圖像大小。其信息頭中的biSizelmage應爲原來的1/3;biBitCount應爲8。

  2.1 調色板的作用??

      顯示圖像的時候發現有沒有選入設備環境新的調色板結果都是一樣的。。。。

       創建調色板的一般步驟:
  1 建立一個LOGPALETTE結構和PALETTEENTRY數組
  2 對PALETTEENTRY數組進行賦值,即創建調色板顏色表
  3 建立CPalette對象並使用CreatePalette函數初始化調色板對象
  4 使用SelectPalette函數將設備描述表和調色板聯繫起來
  5 使用CDC中的RealizePalette函數使調色板生效

	// 設置調色板

	DWORD dwSize = 2*sizeof(WORD) + m_Dib.GetNumOfColor() * sizeof(PALETTEENTRY);// 調色板大小
	LPLOGPALETTE lpLogPalette = (LPLOGPALETTE)new BYTE[dwSize];                  // 爲調色板申請內存
	memset(lpLogPalette,0,dwSize);

	// 生成新的邏輯調色板
	lpLogPalette->palVersion = 0x300;
	lpLogPalette->palNumEntries = (unsigned short)m_Dib.GetNumOfColor();

	for(int i = 0; i < (int)m_Dib.GetNumOfColor(); i++)
	{
		lpLogPalette->palPalEntry[i].peBlue = i;
		lpLogPalette->palPalEntry[i].peGreen = i;
		lpLogPalette->palPalEntry[i].peRed = i;
		lpLogPalette->palPalEntry[i].peFlags = 0;
		m_Dib.m_lpRgbQuad++;
	}

	  // 創建調色板
	CPalette m_palette;
	m_palette.CreatePalette(lpLogPalette);
	dc.SelectPalette(&m_palette,TRUE);
    dc.RealizePalette();

以上代碼設置的調色板起不到作用,怎樣才能讓設置的調色板起作用呢?

  雖然設置調色板行不通了,但可以通過改造數據進行操作。這也是書夢用到的方法。以上三種方法對猩猩圖的灰度化分別爲5,137,12毫秒。
 

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