彩图和灰图(调色板)

从以下几个方面谈谈灰度化,什么是灰度化?为什么要灰度化?灰度化的方法?

之前老不理解王克师兄的基于全画面是啥意思,今天看了李雪梅.唐万有《印刷品缺陷检测的方法研究》终于似乎有些明白。“密度检测和色度检测虽然是有效的印刷质量控制方式,但这种控制方式主要依赖检测局部色块(测控条),缺陷检测要求操作者积累的经验和主观判断。” 可以看得出以前的检测方式是基于局部的,机器视觉的检测是基于整个画面的。

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毫秒。
 

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