纽扣计数的MATLAB实现
本文内容较多,读者可以针对性的阅读。
一、问题背景及描述
在服装厂的仓库里,一些管理人员工作时需要清点衣服配件的数量,如纽扣,拉链,线团等等,采用人工的方式进行清点的效率较为底下,因而本人带着这个问题学习了数字图像处理课程,同时将解决这类计数问题的方法记录下来,供大家参考。描述这段背景是为了让读者不局限于本文的纽扣计数,而是能在其他类似的场景举一反三。
为了实现计算机的程序设计和对纽扣的准确识别,本人对纽扣设置了两点要求,一是纽扣不重叠,但可以独立地随机分布;二是底色与纽扣有一定的色差,以便准确提取纽扣的边界;本文采用得图片如下,
对本图像的几点实际情况稍加描述:这里纽扣内部有孔,正反不一,整副图像的光线分布不是很均匀。
二、问题分析
1. 目的
采用图像处理的方法实现纽扣的计数,本文计算对象虽然只是纽扣,但适用于其他有类似特征的物体计数。
2. 实现纽扣计数的方法分析
首先应该对图像进行预处理,一般用手机采集到的图像是彩色图像,可以将它降为灰度图像再进行处理,以减少计算量和简化计算模型。
其次要实现纽扣的准确识别,采用什么样的方法来识别?首先考虑到的是边缘检测和区域检测;在处理过程中如何解决遇到的问题?
在完成纽扣的识别之后,如何统计识别好的纽扣?采用什么方法来计算?
基本流程:
三、模型建立与编程实现
1. 图像的预处理
这里的图片预处理主要是图像的灰度化,图像灰度化有好几种方法:
(此图是转发的)
在MATLAB中直接使用rgb2gray()函数使用的是第三种方法(加权法)。
实现代码:
clc,clear
close all
filename = 'Button.jpg'; % name of image
I = imread(filename);
I = rgb2gray(I); % Grayscale processing
imshow(I);
注:Button.jpg是图片名称,文件位于MATLAB当前工作夹下,运行结果如下,
左边是原来的彩色图像,右边是灰度后的图像。
2. 图像的边缘检测
边缘检测方法有很多,比如拉普拉斯算子(二阶导)、梯度算法(与一阶导有关)、Sobel算子、canny算子等等。这里简单介绍一下各个算法,关注解决问题的读者可略过原理介绍,直接查阅后续canny算子处理图像的程序。
①拉普拉斯算子
其离散的算子形式为:4阶 ,8阶;其中8阶可以检测对角方向边缘,得到的边缘更加细致。
算子作用于图像是一个卷积的过程,可以用imfilter(I,w)来实现算子对图像的滤波,I是图像,w就是上面算子(矩阵)。
②基于梯度的边缘检测
其中幅值等于,幅角(表方向)等于。
③Sobel算子
Sobel算子相比于梯度算法没有太大的变化,只是修改了主方向上的权重,其算子形式为:
。
3. Canny算法进行边缘检测
本文使用的检测算法就是canny算法,这里对它稍作详细点的介绍。
Canny算法的特点:低误差率、边界点定位准确、单像素值响应(边界只有一个像素宽度)。
①高斯滤波及梯度检测
执行高斯滤波是为了降低噪声对边缘检测的影响,然后计算图像的梯度,做法与上面提到梯度算法的相同,利用梯度算子计算出图像的一阶差分幅值和相位角。
②去除(抑制)非最大值的点
这一步主要是将沿某一方向上的幅值最大的像素点保留下来,具体的方向由相位角确定。基于九宫格可以代表八个方向,所以将不同的相位角以“就近原则”归类到其中一个方向上:
如左图,将方向分为8个方向。右图是一个例子,某边缘点的相位角是20°,那么就将它划分到0°对应的方向,代表着最底下三个元素,其余七个方向分别代表各自所指向的三个元素,如下图,45°对应右下角,90°对应右边三个元素:
确定好方向之后,比较这三个元素,将非最大值点置零,以此保证边缘是单像素点。
如图,某个像素点在0°方向上的三个元素幅值分别为8,5,4,那么5,4对应的像素点就被置零,保留8这个像素点。
③滞后阈值(去除非边界点)
设置两个阈值,将边界点分为两类,强幅值集合表示确定的边界点,弱幅值集合表示待检测的边界点。以强幅值的边界点来选择弱幅值集合中的有效边界点。设置的两个阈值大小比例一般是2:1或3:1,用TH和TL表示,执行步骤如下。
1) TH和TL产生两个边界点的集合;
2) 用强幅值点的8邻接匹配弱幅值点,即遍历找出弱幅值点中与强幅值点相邻的像素,如果像素相邻,则认为是有效边界点,保留;
3) 当所有的弱幅值点匹配完成时,将未匹配到的点置零;
4) 最后的边界图像为“强幅值点+弱幅值点”。
MATLAB实现:边缘检测算法在MATLAB中已有封装好的函数,在使用时我们直接调用就可以了。
[g,t] = edge(I,'canny');或
[g,t] = edge(I,'canny',[0.03,0.09]);
如示例,当自己不给双阈值时,函数会自适应设置阈值,效果也挺不错的,。
基本上纽扣的边界都探测出来。
4. 形态学处理压缩图像
利用形态学的手段将纽扣压缩为区域:
形态学处理很关键的一个知识点就是结构元素(struggle element),它有点像上面提到的滤波算子,但这里执行的并不是滤波操作(卷积),而是匹配定点操作。常见的结构元素有:
注:上面的结构元素原点在中心,后面三个结构元素是基于其功能命名的,非专业名称,它们可以有8个方向。结构元素的形状也不仅限于上面给出的例子,可以多种多样,尺寸也大可小,具体功能就不一一介绍了,这里直接给出处理图像步骤。
①闭操作
闭操作可以将一些断层较小的边界连接起来,可以将图片中的小洞填充掉(可填充小于结构元素的洞或断层),选择圆盘作为自己的结构元素,对图像执行闭操作,执行命令:
se = @(x) strel('disk',(x+1)/2); %define Anonymous function to produce SE
temp = imclose(g,se(3)); %close processing
figure,imshow(temp); %show the image
第一行定义了一个匿名函数(即未命名的暂时性使用的函数),可以灵活地生成一个x大小的圆盘结构元素,第二行命令执行闭操作,结果如下,可看到纽扣边界基本连通,为后续的孔洞填充做准备。
②孔洞填充
孔洞填充可以将图片中闭合边界所围成的区域填满,执行命令:
g_fill = imfill(g,'holes');
结果:
③使用腐蚀分离边界
腐蚀可以是图像瘦身,在原本纽扣面积足够大的情况下,可以实现纽扣的完全分离(保证计数的准确性),执行命令:
region = imerode(g_fill,se(27)); %erode the image to separate the button
region = imdilate(region,se(9)); %dilate the image to make it clear(not necessary)
figure,imshow(region);
运行结果:
5. 区域计数
一种较为蛮干的解决方法就是遍历图像统计,检索到1个白点,然后以它的8邻域扩展来搜索区域,当搜索的区域不在扩大时,说明该区域搜索完毕,计数加1,继续寻找下一个新的点,重复操作,直到执行完整副图片。
区域的检测采用形态学的方法很容易实现,用方块或圆盘结构元素对这个白点进行膨胀,膨胀之后与原图像相交(取交集),当交集的结果不在变化时则说明区域探测完成。在MATLAB中有专门的计算二值图像区域个数的函数:
[~,n] = bwlabel(region);
返回的第一个数是关于区域的矩阵,不需要用到,所以用“~”代替;第二个数n就是区域个数了,结果为:
至此,纽扣的计数目标已经完成,总共有84个。读者在参考的时候可能需要设置不同的参数才能实现较好的检测,本模型也还不是很完善,请各位读者多见谅。
附完整的代码: