一、前言

  之前在《VcImgProc小插曲》中,笔者对系列博文的安排做了一些调整,把对 DEMO 的介绍(也就是本篇博文啦)放在了前面,提前进入情景。

  为什么要提前?第一,我考虑到 “自顶向下” 的学习方法效果往往比 “自底向上” 更好,能够更早接触实际问题,学习起来也更加有劲儿;第二,数字图像处理的算法实现是个技术活儿,在笔者眼中,技术的意义并不在技术本身,而在于它所能够带来的改变。

  本篇博文首先从某一特定情景出发,引入了自己遇到的一个小问题,然后介绍了解决此问题所需的算法——灰度阈值变换的原理,最终初步讨论了如何实现此算法,为后文做铺垫。

二、情景

  笔者是一个业余的钢笔书法爱好者,喜欢没事儿在纸上涂涂写写,字也就慢慢练好了。

  首先展示一下我的绝世神兵 —— 英雄616,超便宜(10元)却超级好用!

  钢笔

  喜欢边听歌边练字,非常有感觉,下面是摘抄的 JJ《醉赤壁》的歌词:

  

  写完了当然用相机拍一下保存起来,可惜我在 camera 方面是个小白,傻瓜相机也觉得我傻!结果自然照出来的效果不行了,底色太暗了,整体上呈现一种灰蒙蒙的感觉,

  该怎么办?怎样把讨厌的灰色背景去掉的同时,又不会破坏钢笔字本身的线条?

三、灰度阈值变换

  现在,我们从数字图像处理的角度来分析如何解决这个问题,可以分为两步来进行:

  (1)转换成灰度图像

  首先,上面的图像是 24 位真彩色图像,每一个像素都包含 R、G、B 分量,每个分量占用 8 bit。但是对于钢笔字来说,就是白底黑字嘛,其他的颜色基本上毫无意义,所以我们首先就要把 24 位的真彩色凸显转换成 8 位灰度图像。所谓灰度图像,就是每一位的像素由 8 bit 构成,可以表示从 0(纯白色) ~ 255(纯黑色) 一共 256 个灰度级。这样转换之后,处理起来会更加方便,也可以减小图片的大小。

  从RGB转换成灰度图像,我们可以利用现有的公式:

  (2)灰度阈值变换  

  在上一步的基础上,我们来考虑怎样把去掉背色的同时,保留黑色的钢笔的线条。

  要想把两者分开,当然要找到它们之间的不同点了,这个一眼就看出来了:一个更黑,另一个则比较灰。用专业一点的说法就是两者的灰度级范围不同,钢笔字线条的灰度级比背景的更大。进一步,我们肯定可以找到一个灰度值 T(0 < T < 255),钢笔线条的灰度级大于 T,而背景的灰度级则小于 T。这个 T 也就是所谓的阈值了,可以很好地区分钢笔线条和背景。然后,我们考虑把背景去掉,去掉也就是意味着变白了(Gray = 0);对于钢笔字,让它变得更黑(Gray = 255),自然也就有了下面的公式: 

  其中 x 表示某一像素的灰度级,f(x) 表示对这种灰度级的变换。上面的公式的意思是:灰度级小于 T 的,全部置为纯白;灰度级大于 T 的,都变成黑色!呵呵,这就是我们接触到的第一个数字图像处理算法了,是不是非常简单?简单得就像学某种语言时开始的 "Hello World" 一样?

四、Photoshop 中的预演

  算法介绍了,但是还不能立马在 Vc 中实现,不过考虑到 Photoshop 可是图像处理的杀手级应用,而且它也提供了可视化的灰度变换设置,所以我们可以利用它来预演一下灰度阈值变换算法,看看效果咋样!

  首先载入图像,我使用的 PS CS3 版本:  

  

  接着我们按下 Ctrl + M,出现一个对话框:

  

  这就是灰度变换的坐标图了,怎么看呢?横轴代表源图像的灰度级(0~255),纵轴轴代表变换后的图像的灰度级(0~255),而坐标轴中的曲线就表示两者之间的对应关系了!大家可以看到曲线初始化的时候,它的表达式可以写作:

  这就是完全不做变换的意思,然后我们可以手动调节这个曲线,最终达到想要的对应关系:

  

  这里我们设置的阈值是77,在 77 以下的灰度级全部变成白色,以上则变为黑色。

  再看看变换后的图像吧:

  

  对比一下之前的图像:

  

  可以发现,变换后,钢笔线条显得非常醒目,清晰挺拔,可以说基本上达到了所要的效果。(有个小缺憾就是把第一行反光的文字基本上腐蚀得几乎看不见了......)

五、结语

  上面,我们借助强大的 PS 的力量,预演了灰度阈值变换算法,  

  PS 强大归强大,但是强大的终究是软件(而不是你),如果不懂得背后运行的算法,那么你只是个工具的使用者罢了。再说了,每个程序员可都是天生的 DIYER ,呵呵,“自己动手,丰衣足食” 可是毛爷爷留给我们 coder 的箴言! 恩,接下来我们就考虑如何来动手实现灰度阈值变换算法。

  那么在搭建好 Vc 平台后,我们想要在平台上实现此算法,接下来该做些什么呢?

  (1)图像处理类

  要处理图像,首先得有个封装图像数据和一些基本函数的类才好吧,这样的话可以充分利用类的好处,更加方便。

  c# 中有 Bitmap,而 MFC 中也有 CImage。那是不是直接用 CImage 就万事大吉了?不是,原因会在下一篇博文中讨论。

  (2)必要的 MFC 知识(只是必要的...) 

  借助 MFC 实现算法演示,总得对 MFC 有最基本的了解吧,至少要懂 Document/View 结构,还有 CMenu(菜单类)+ CDialog(对话框类)这一些必须用到的控件有一定了解。

  以上两点,也是接下来的两篇博文所要讨论的类容,敬请期待系列博文之:

  swicth ( VcImageProc ) case 3:CImg 和 CImgProc —— 图像操作和处理类

作者: 鹏程 发表于 2011-07-31 16:26 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"