资料来自《详解MATLAB图像函数及其应用》
作者:张倩,占君,陈珊
出版社:电子工业出版社
出版时间:2011-04
这是我对MTALAB图像处理整理出来的学习笔记,望与君共勉
采用部分只有代码,其他的我自己收集来的资料,萌新一枚,侵权即删
内容来自书的第十九章1457页的图像特征提取实战2
——————————————————————
本次会出现5个图像窗口,所以我会根据每一次出现的图像窗口来进行分段解释
首先读取图像
RGB=imread('1234.png');
imshow(RGB);
其他用语说明
再打开
然后对图像进行阈值分割
将图像转换为灰度图像,设置阈值,对图像进行阈值分割,生成二值图像
I=rgb2gray(RGB);
threshold=graythresh(I);
bw=im2bw(I,threshold);
figure;
imshow(bw);
其他用语解释
而这个计算全局图像阈值的方式是大津法——Otsu方法,又称最大类间方差法
Otsu算法
- 对于图像I(x,y)
前景(即目标)和背景的分割阈值记作T
前景像素点数占整幅图像的比例记为ω0,其平均灰度μ0;
背景像素点数占整幅图像的比例记为ω1,其平均灰度为μ1。
图像的总平均灰度记为μ,类间方差记为g。- 假设图像的背景较暗,并且图像的大小为M×N
图像中像素的灰度值小于阈值T的像素个数记作N0
像素灰度大于阈值T的像素个数记作N1
则有:
ω0=N0/ M×N (1)
ω1=N1/ M×N (2)
N0+N1=M×N (3)
ω0+ω1=1 (4)
μ=ω0μ0+ω1μ1 (5)
g=ω0(μ0-μ)2+ω1(μ1-μ)2 (6)
将式(5)代入式(6),得到等价公式:
g=ω0ω1(μ0-μ1)^2 (7)
这就是类间方差
采用遍历的方法得到使类间方差g最大的阈值T,即为所求。
内容学习来源
作为一个热爱学习的人,我当然得去看看这个函数在干些啥
于是我去画了个图
于是
那如果我用三色呢
于是
我们再次引用这里的的概念来解释平均灰度
- 平均灰度,反映整体的亮、暗,越高越亮
- 平均对比度,一幅图亮暗的差异
- 平滑度,一幅图灰度的均匀性,0-1:平滑-不平滑
- 三阶矩,直方图偏斜度的度量。对称为0,右偏斜为正,左偏斜为负
- 一致性,度量一致性
- 熵,反映像素的随机性,越大越粗糙
结果当然会得到二值图像
继续打开
可以看到想要提高效率(偷懒),可以只打一个figure代码,就可以按顺序新建一个图像窗口,如下图(本次代码产生的)
然后去除图像中的噪声,开运算去除小于30个像素的目标
bw=bwareaopen(bw,30);
figure;
imshow(bw);
本次bwareaopen会对bw进行操作,故之前我展示出来的bw的逻辑矩阵已经是经过变化了的矩阵,有一些地方的1可能变成了0,但是我们只是为了看看这个图片在电脑里是怎么解释的(我就是没看过,我就是想看)
第一次的实战案例里我们了解到,开运算就是可以去除孤立的小点、毛刺这些东西,P就是结构元素,用于作为滤波的标准,BW就是滤波对象,接下来我们看看滤波的结果如何
然后我们再来看看两副图像的区别
开运算后的图片:
之前的图片:
很明显,一些很小的点被填补上了,这里被移除的是白点,故可以说是去掉了黑点周围的毛刺,使逻辑矩阵里的1(白)变成了0(黑),在图片的其他地方也是有变化的,我们就说一处
填充图像缝隙(洞孔)
se=strel('disk',2);
bw=imclose(bw,se);
bw=imfill(bw,'holes');
figure;
imshow(bw);
结果得到的se是什么呢,我们来看看
我们继续打开
查阅资料知道,se为strel函数创造的形态学结构元素对象——strel对象,代表一个扁平的形态结构元素,那么这个strel函数就牵涉到strel的对象功能,如下表
函数 | 作用 |
---|---|
imdilate | 膨胀图像 |
imerode | 腐蚀图像 |
imclose | 形态接近图像 |
imopen | 形态上开放的图像 |
imbothat | 底帽过滤 |
imtophat | 高帽过滤 |
bwhitmiss | 二进制未命中操作 |
decompose | 分解后的结构元素的返回顺序 |
reflrct | 反映结构元素 |
translate | 翻译结构元素 |
imfill(BW,‘holes’) | 空洞填充 |
图表内容来源
另外我们还看到,继续打开的两个图中,一个是0,1矩阵,1组合起来的形状我们就当做圆盘状吧,然后另一个图里只有一个数字2,这个2就来自代码中后边我们输入的2。实际上如果把disk变成square,就会使整个矩阵变成1,变成line,就还需要一个角度,若角度设定为45度,则会让1从左下角连续到右上角去,也就是一条线的形状(矩阵画图太难了)
接下来我们看本次用到的strel的对象
来看看这一次的图像的变化
我们可以看到一些很明显的变化,人物耳朵的轮廓没了,用这幅图感觉看不出是怎么填的洞
那我们把imfiil函数划掉看看
可以看到耳朵的轮廓还在
那我们来看看那个imclose闭运算的结果
闭运算之后:
可以看到经过闭运算,黑色(0)的线条作为白色(1)的裂缝,被弥补了,也就是补1了
所以我们可以知道,这个过程中,开运算使1变0,闭运算使0变1,正好是两个基本算子,多少理解了一点吧
接下来提取图像中的各种目标的几何特征,利用几何特征及圆形检测算法判断每个目标是否是圆形目标。当目标的面积和周长满足公式ε=4piS/L^2,ε接近于1时,则认为该目标为圆形目标
[B,L]=bwboundaries(bw,'noholes');
figure;
imshow(label2rgb(L,@jet,[.5 .5 .5]));%5
我们来看看B里有什么
一个元胞数组,然后打开
套娃游戏,然后我们继续打开可以看到的是,这就是边界像素点的坐标
那么我们打开L
我好气啊全是0,纯属来占内存的?
然后我发现它列有点多…
这就是标识矩阵,用于下边循环绘制每个边界
这里的@jet是colormap,解释是说默认大值为红,小为蓝,还可以用colormap(flipud(jet))进行颠倒,但是…
那问题出在哪呢,我认为是后边的zerocolor的问题,于是我去修改参数
改成.1 .5 .9之后的样子,其他颜色有待挖掘呀,然后接下来是图中耳朵位置的metric值,本图只有一个地方可以计算,我们待会换个图
hold on;
for k=1:length(B)
boundary=B{k};
plot(boundary(:,2),boundary(:,1),'w','LineWidth',2);
end
stats=regionprops(L,'Area','Centroid');
threshold=0.94;
for k=1:length(B)
boundary=B{k};
delta_sq=diff(boundary).^2;
perimeter=sum(sqrt(sum(delta_sq,2)));
area=stats(k).Area;
metric=4*pi*area/perimeter^2;
metric_string=sprintf('%2.2f',metric);
if metric>threshold
centroid=stats(k).Centroid;
plot(centroid(1),centroid(2),'ko');
end
text(boundary(1,2)-35,boundary(1,1)+13,metric_string,'Color','y','FontSize',14,'FontWeight','bold');
end
title(['Metrics closer to 1 indicate that','the object is approximately round']);
先给出流程图
具体的区别我用第三幅图来观察了,有需要可以从目录跳到最后的那个第三幅图去观察
代码说明:
获取标识区域的面积和质心属性
我们打开stats来看
再打开
threshold=0.94是在手动设置圆的阈值,可以观察下方对比metric和threshold
然后是用循环检测每一个标识目标
利用边界坐标计算目标周长
area=states(k).Area即获取目标面积属性
打开area
然后计算metric值,保存计算结果,将检测出的圆形目标用黑圈标识出其质心,判断其是否为圆
这个函数在这次算法里比较玄学吧,算着算着周长就出来了,我也是看了半天,我们先来看看diff函数是在干些啥
它的用途有这些
那结果呢
我们可以看到,
对于一维向量,diff函数会用右减左,即相邻数的增量
对于二维的矩阵
所以本次算法中就是把boundary作为边缘坐标,然后下减上,得到的几乎都是(0,1)(1,0)(-1,0)和(0,-1),因为是连续的离散点,然后平方变为正,再求和,第一个sum计算得到数行一列的1,sqrt之后还是1,行数甚至不变,然后最后一个sum得到周长
只到最后一个imshow操作,连边都没有描
描完边之后就没了,明明figure里是有描边的,我也不知道为什么保存后就没了白边
分别把threshold修改为0.5和0.1
可以看到计算metric大于threshold的就会在质心出现黑圆圈
那我们再继续,这次把string_metric去掉
可以看到每个标记都相同了
我们来看最终结果