我们看到的彩色图像,一般都是BGR颜色空间的,也就是一副图像,是由蓝色通道、绿色通道、红色通道组合而成。而灰度图只有一个通道,他有256个灰度等级,255代表全白,0表示全黑。
注意啊,下面这副黑白图像,利用Emgu CV的默认方式读取的时候,可不是灰度图,而是BGR格式的彩色图片。
读取过程如下,您看是不是个三通道的BGR颜色空间图片,这个时候要是遍历srcMat的所有像素点,您会发现它每个像素点上三个通道的值是相同的。它其实可以看成是三个完全相同的灰度图组成。
记住:灰度图是单通道、彩色图片是三通道,不是黑白色的图像就是灰度图。
那要怎么把Emgu CV的读取的图片转成灰度图呢?下面以 lena.jpg 举例。
1、CvtColor()转灰度图
可以使用CvtColor(),官方介绍如下:
public static void CvtColor(
IInputArray src, // 输入图像
IOutputArray dst, // 输出图像
ColorConversion code, // 颜色空间转换代码
int dstCn = 0 // 输出图像的通道数,默认值为 0,可以不写
)
这个函数就是完成颜色空间转换,功能非常强大,当BGR转灰度图时,要选择 ColorConversion.Bgr2Gray,使用方式如下:
Mat dstMat = srcMat.Clone();
CvInvoke.CvtColor(dstMat, dstMat, ColorConversion.Bgr2Gray);
CvInvoke.Imshow("CvtColor gray Mat, " + dstMat.Size.ToString(), dstMat);
lena.jpg转成灰度图的效果如下:
2、更进一步的背景知识
其实是我从网上搜集到的,BGR转灰度图,是有很多算法的。使用不同的算法,得到的灰度图结果也不一样,比如:
- 平均值法:Gray = (Red + Green + Blue) / 3
- 最大灰度值法:Gray = Max(Red, Green, Blue)
- 最小灰度值法:Gray = Max(Red, Green, Blue)
- 加权平均 - - 浮点处理法:Gray = (Red * 0.299 + Green * 0.587 + Blue * 0.114)
- 加权平均 - - 整数处理法:Gray = (Red * 30 + Green * 59 + Blue * 11) / 100
Emgu CV里的CvtColor()函数,更准确的说是OpenCV里的CvtColor()函数采用的是什么算法,我也不知道。读者与兴趣可以去github上研究研究。
3、Decolor()脱色处理
这个介绍的不多哈,读者们赚了。彩色图片转灰度图时,除了用最常见的CvtColor()函数以外,还有一个Decolor()函数,与CvtColor()函数相比,更能保持原始图像的颜色对比度。这个算法更加复杂,该代码是源于香港中文大学计算机科学与工程系的一篇论文 ,链接地址如下:
Contrast Preserving Decolorization
不想研究源码的读者,直接看下面的使用方法:
Mat tempMat = srcMat.Clone();
Mat dstMat1 = new Mat();
Mat dstMat2 = new Mat();
Mat colorBoostMat = new Mat();
CvInvoke.Decolor(tempMat, dstMat1, colorBoostMat);
CvInvoke.CvtColor(tempMat, dstMat2, ColorConversion.Bgr2Gray);
CvInvoke.Imshow("Decolor() gray Mat, " + dstMat1.Size.ToString(), dstMat1);
CvInvoke.Imshow("Decolor() color boost Mat, " + colorBoostMat.Size.ToString(), colorBoostMat);
CvInvoke.Imshow("CvtColor gray Mat, " + dstMat2.Size.ToString(), dstMat2);
原始图片srcMat、Decolor()脱色后的灰度图、CvtColor()直接转换的灰度图如下:
可以看到,Decolor()脱色后的灰度图明暗对比要更明显一些。这个函数比较适合这种透雾处理的图片,但是运算速度要明显比CvtColor()函数慢很多,一般情况下还是选择用CvtColor()函数。
4、手搓算法转灰度图
据说是采用 加权平均更符合人眼的视觉效果,而且整数法又要快于浮点法。那接下来我们自己也可以写一个算法,试一试把BGR图像转灰度图:
int width = srcMat.Cols;
int height = srcMat.Rows;
Mat dstMat = new Mat(new System.Drawing.Size(width, height), DepthType.Cv8U, 1);
Mat tempMat = srcMat.Clone();
Image<Gray, int> dstImage = new Image<Gray, int>(width, height);
Image<Bgr, int> tempImage = tempMat.ToImage<Bgr, int>();
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; ++j)
{
dstImage.Data[i, j, 0] = ((11 * tempImage.Data[i, j, 0]) + (59 * tempImage.Data[i, j, 1]) + (30 * tempImage.Data[i, j, 2])) / 100;
}
}
dstMat = dstImage.Mat;
dstMat.ConvertTo(dstMat, DepthType.Cv8U);
CvInvoke.Imshow("Custom function gray Mat, " + dstMat.Size.ToString(), dstMat);
转换后的lena.jpg如下图:
将lena.jpg转灰度图时,用Emgu CV的CvtColor()函数用时0.012秒,手搓的算法用时0.019秒,慢差不多0.007秒,如果使用C++配合OpenCV应该更快。看到了吧,差距不小,为什么OpenCV这么流行,人家是有很高的技术含量的。至于有多高,两层楼那么高吧。
灰度图只有亮度信息而没有颜色信息,这种特性使得它在在图像处理领域中应用非常广泛,包括:
- 去除颜色影响后,灰度图像可以更容易地反应图像的纹理、边缘。比如医学领域里的X光、CT影像图片。
- 灰度图每个点就256个维度,而BGR图每个点有256 * 256 * 256 = 16777216个维度,寻找特征时计算量太大了。
- 相比彩色图像,减少了三分之二的存储空间。
一般来说,为了寻找特征,转换完成灰度图后,接下来会紧跟着下一步:二值化。那什么是二值化,二值化怎么转换,二值化后又有什么好处呢。下一篇将详细举例。
原创不易,请勿抄袭。共同进步,相互学习。