陈教授停下笔,看着他。
“您用的是均值滤波?”
“对,均值滤波。”
吕辰摇了摇头:“均值滤波需要乘法器。像素值乘系数,再累加,再除法。乘法器在五微米工艺下面积太大,而且速度慢。”
他在自己笔记本上画了一个简图,推到桌子中间。
“我建议用中值滤波。中值滤波只需要比较器,不需要乘法器。九个像素的值,找中间那个。比较器比乘法器简单得多,面积小、速度快。”
陈教授盯着那张图看了几秒。
他没有立刻回答。
会议室里安静了两秒,只有日光灯的嗡嗡声。
然后他忽然笑了一下,很轻,像是在对自己说。
“你说得对。我想复杂了。”
他用笔在图纸上改了一笔,把“均值滤波”划掉,写上“中值滤波”。
“排序网络,九个数找中值,用九个比较器搭成排序网络。纯组合逻辑,一个时钟周期就能输出结果。”
钱兰在旁边插了一句:“九个比较器,面积大概多大?”
陈教授想了想:“按你们标准单元库里的比较器来算,八位的,大概几十个门。九个,加上连线,几百个门。可以接受。”
他在笔记本上记了一笔,然后继续往下讲。
“接下来是对比度增强。”
他在图上画了一个新的方框。
“焊盘是金属的,反光强,灰度值高。背景是陶瓷或者塑料的,反光弱,灰度值低。对比度增强就是让这种差异更明显。硬件实现用查找表。输入8位灰度,输出8位灰度。映射关系预先算好,固化在Rom里。”
钱兰立刻接话:“256乘8位的Rom,约2048个存储单元。面积很小,没问题。”
陈教授点了点头,手指移到下一个方框。
“最关键的一步,自适应二值化。”
他在方框旁边写了一行字:把灰度图像变成黑白二值图像(0=背景,1=焊盘)。
“为什么不能用固定阈值?因为光照不均匀。芯片中间和边缘的亮度可能不一样,固定阈值会导致中间焊盘能识别、边缘焊盘被漏掉。”
他抬起头,看着三个人。
“所以要用局部自适应阈值。取当前像素周围N乘N区域的灰度均值,以此为阈值。”
他在图上画了一个示意图,一个像素周围围着八个邻居,组成3乘3的区域。
“局部均值的计算不需要乘法器。九个像素的和可以用加法树实现,除以九可以用移位加法近似。”
诸葛彪开口了:“加法树的速度呢?”
“一个时钟周期。”陈教授说,“九个8位数相加,用三级加法树,每级延迟几十纳秒。一兆赫的时钟周期是一微秒,绰绰有余。”
他在纸上算了一下,然后抬起头。
“预处理部分,到此结束。输入模拟视频,输出二值化像素流。全部硬件实现,每一行像素进来,实时处理,实时输出,没有帧延迟。”
他在图纸上画了一条横线,表示第一部分结束。
然后翻开第三张图。
“第二步,特征提取。”
他的声音提高了一些,铅笔在纸上点了几下。
“这是整个算法中最核心、最巧妙的部分。”
吕辰盯着那张图,心跳微微加速。他知道,这才是真正的难点。
陈教授拿起粉笔,在黑板上画了一行二值化的像素:0 0 0 1 1 1 1 1 0 0 0 0。
“这一行像素,连续的白像素从第4列到第8列,长度5。我们可以把它压缩成一个线段:start=4, end=8, length=5。”
他在下面写了一行字:游程:[4,8]。
“硬件实现非常简单,只需要一个状态机加两个计数器。扫描一行像素,遇到从0变1就记录start,遇到从1变0就记录end,输出一个游程。”
钱兰在笔记本上飞快地记着,嘴里小声重复:“状态机加两个计数器。”
陈教授继续往下讲。
“有了游程,下一步是连通域标记。找出哪些游程属于同一个焊盘。”
他在黑板上画了三行像素的示意图,用红笔把重叠的部分圈出来。
“关键观察:当前行的游程和上一行的游程,如果列范围重叠,就属于同一个连通域。硬件实现只需要存储上一行的游程标记结果,每列一个标记。”
诸葛彪皱起了眉头:“每列一个标记?256列,就是256个标记。每个标记需要多少位?”
陈教授想了想:“最多同时有多少个焊盘在一行里?假设焊盘直径10个像素,一行最多二十几个焊盘。用5位标记就够了。256乘5等于1280位寄存器,可以接受。”
他顿了顿,铅笔