文章目录
说明
资料来自《详解MATLAB图像函数及其应用》
作者:张倩,占君,陈珊
出版社:电子工业出版社
出版时间:2011-04
这是我对MTALAB图像处理整理出来的学习笔记,望与君共勉
采用部分只有代码,其他的我自己收集来的资料,萌新一枚,侵权即删
内容来自书的第十九章1457页的图像特征提取实战2
——————————————————————
本次会出现5个图像窗口,所以我会根据每一次出现的图像窗口来进行分段解释
Figure 1
首先读取图像
RGB=imread('1234.png');
imshow(RGB);
imread函数
- A=imread(filename)
从指定的图像中读取灰度图像或真彩色图像
filename是表示文件名全名的字符串(包括扩展名)
也就是***.jpg啊,***.png啊这些图像文件名字
imshow函数
- imshow(I)
显示真彩色图像RGB,
RGB为三维数组,第三维的维数为3
其他用语说明
- 三维数组
这一个三维数组在本次案例中是指imread函数操作来的三维数组,如RGB,显示如下
再打开
这个图是
Figure 2
然后对图像进行阈值分割
将图像转换为灰度图像,设置阈值,对图像进行阈值分割,生成二值图像
I=rgb2gray(RGB);
threshold=graythresh(I);
bw=im2bw(I,threshold);
figure;
imshow(bw);
rgb2gray函数
- I=rgb2gray(RGB)
将真彩色RGB图像或索引图像转换为灰度图像
参量RGB是一个维数为MxNx3的数组,表示RGB图像,上方已经展示出来
graythresh函数
- level=graythresh(I)
计算全局图像阈值
level为标准化灰度值,范围是[0 1]
其他用语解释
- 阈值
即临界值,是为转化二值图像做准备来的
而这一次把上图进行计算全局阈值后得到的level值为
而这个计算全局图像阈值的方式是大津法——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,右偏斜为正,左偏斜为负
- 一致性,度量一致性
- 熵,反映像素的随机性,越大越粗糙
im2bw函数
- BW=im2bw(I,level)
阈值法转换图像为二值图像
将灰度图像I转换成二值图像BW
level为阈值,即标准化灰度值,范围为[0 1]
I中大于level的像素设为1(白色),小于的像素为0(黑色)
结果当然会得到二值图像
继续打开
figure函数
- figure
创建新的图形对象,在屏幕上单独显示的窗口,且窗口中可以输出图形
可以看到想要提高效率(偷懒),可以只打一个figure代码,就可以按顺序新建一个图像窗口,如下图(本次代码产生的)
本次产生的图是
Figure 3
然后去除图像中的噪声,开运算去除小于30个像素的目标
bw=bwareaopen(bw,30);
figure;
imshow(bw);
bwareaopen函数
- BW2=bwareaopen(BW,P)
- 移除二值图像BW中所有少于P个像素的连通元素,生成另一个二值图像BW2
- 对二值图像进行形态学开运算,用于移除小目标
- 对于二维图像默认采用8连通邻域
三维采用26连通邻域
本次bwareaopen会对bw进行操作,故之前我展示出来的bw的逻辑矩阵已经是经过变化了的矩阵,有一些地方的1可能变成了0,但是我们只是为了看看这个图片在电脑里是怎么解释的(我就是没看过,我就是想看)
第一次的实战案例里我们了解到,开运算就是可以去除孤立的小点、毛刺这些东西,P就是结构元素,用于作为滤波的标准,BW就是滤波对象,接下来我们看看滤波的结果如何
然后我们再来看看两副图像的区别
开运算后的图片:
之前的图片:
很明显,一些很小的点被填补上了,这里被移除的是白点,故可以说是去掉了黑点周围的毛刺,使逻辑矩阵里的1(白)变成了0(黑),在图片的其他地方也是有变化的,我们就说一处
Figure 4
填充图像缝隙(洞孔)
se=strel('disk',2);
bw=imclose(bw,se);
bw=imfill(bw,'holes');
figure;
imshow(bw);
strel函数
- SE=strel(shape,parameters)
创建形态学结构元素对象
shape可是square方形,line线形,disk圆盘形,ball球形和rectangle长方形等
parameters是对shape的大小描述
结果得到的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的对象
imclose函数
- IM2=imclose(IM,SE)
对图像进行形态学闭运算
对灰度图像或二值图像IM进行形态学闭运算,返回闭运算结果图像IM2
SE为由strel函数生成的结构元素对象
imfill函数
- BW2=imfill(BW,‘holes’)
显示二值图像BW,对二值图像BW中的目标点孔进行填充点
来看看这一次的图像的变化
我们可以看到一些很明显的变化,人物耳朵的轮廓没了,用这幅图感觉看不出是怎么填的洞
那我们把imfiil函数划掉看看
可以看到耳朵的轮廓还在
那我们来看看那个imclose闭运算的结果
闭运算之后:
闭运算之前:
可以看到经过闭运算,黑色(0)的线条作为白色(1)的裂缝,被弥补了,也就是补1了
所以我们可以知道,这个过程中,开运算使1变0,闭运算使0变1,正好是两个基本算子,多少理解了一点吧
Figure 5
接下来提取图像中的各种目标的几何特征,利用几何特征及圆形检测算法判断每个目标是否是圆形目标。当目标的面积和周长满足公式ε=4piS/L^2,ε接近于1时,则认为该目标为圆形目标
[B,L]=bwboundaries(bw,'noholes');
figure;
imshow(label2rgb(L,@jet,[.5 .5 .5]));%5
bwboundaries函数
- B=bwboundaries(BW)
搜索二值图像BW的外边界和内边界
BW为矩阵,其元素为0或1(即二值图像)
该函数将0视为背景像素点,1视为待提取边界的目标
B为Px1细胞矩阵,P为目标和洞的个数
B中的每个细胞元素均为Qx2矩阵,Q为边界所含像素点的个数
故该矩阵中每一行包含边界像素点的行坐标和列坐标
我们来看看B里有什么
一个元胞数组,然后打开
套娃游戏,然后我们继续打开可以看到的是,这就是边界像素点的坐标
- 而本次代码中使用的方式是
[B,L]=bwboundaries(BW,conn,options)
返回标识矩阵L,用于标识二值图像中被边界所划分的区域,包括目标和洞
conn指定搜索算法中所使用的连通方法,可选4连通和8连通,其中8为默认值
字符串参量options指定算法的搜索方式,可选holes和noholes
默认使用holes,算法搜索目标的内边界和外边界,noholes只搜索目标的外边界
那么我们打开L
我好气啊全是0,纯属来占内存的?
然后我发现它列有点多…
这就是标识矩阵,用于下边循环绘制每个边界
label2rgb函数
- RGB=label2rgb(L,map,zerocolor)
定义输入标注矩阵L中标注为0的元素的RGB颜色
zerocolor为字符串或三元向量
本题内为三元向量,分表代表R、G、B的颜色,默认为[1 1 1],即白色 - 联合imshow函数使用
即imshow(RGB),显示真彩色图像RGB,RGB为三维数组,第三维的维数为3
这里的@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']);
先给出流程图
具体的区别我用第三幅图来观察了,有需要可以从目录跳到最后的那个第三幅图去观察
regionprops函数
- STATS=regionprops(L,properties)
测量每个标签区域L的属性
L为标识矩阵,L中的正整数元素对应不同的区域
properties可以是逗号分隔的字符串、包含字符串的细胞矩阵、字符串all或字符串basic
此处计算测量值Area(区域像素点个数)、Centroid(区域的质心)
代码说明:
获取标识区域的面积和质心属性
我们打开stats来看
再打开
area即面积,centroid即质心
threshold=0.94是在手动设置圆的阈值,可以观察下方对比metric和threshold
然后是用循环检测每一个标识目标
利用边界坐标计算目标周长
area=states(k).Area即获取目标面积属性
打开area
然后计算metric值,保存计算结果,将检测出的圆形目标用黑圈标识出其质心,判断其是否为圆
diff函数
- Y=diff(X)
计算沿大小不等于1的第一个数组维度的X相邻元素之间的差分
这个函数在这次算法里比较玄学吧,算着算着周长就出来了,我也是看了半天,我们先来看看diff函数是在干些啥
它的用途有这些
那结果呢
我们可以看到,
-
对于一维向量,diff函数会用右减左,即相邻数的增量
-
对于二维的矩阵
- 对于diff(A,1,1),就相当于diff(A),并且是用矩阵的下行作被减数,上行作减数
- 对于diff(A,2,1),就相当于diff(diff(A)),并且是用矩阵的下行作被减数,上行作减数
- 对于diff(A,1,2),就相当于diff(A),并且是用矩阵的右列-左列
- 对于diff(A,2,2),就相当于diff(diff(A),并且是用矩阵的右列-左列
所以本次算法中就是把boundary作为边缘坐标,然后下减上,得到的几乎都是(0,1)(1,0)(-1,0)和(0,-1),因为是连续的离散点,然后平方变为正,再求和,第一个sum计算得到数行一列的1,sqrt之后还是1,行数甚至不变,然后最后一个sum得到周长
sprintf函数
- str=sprintf(formatSpec,A1,…,An)
使用formatSpec指定的格式化操作符格式化数组A1,…,An中的数据,并在str中返回结果文本
text函数
- text(___,Name,Value)
最后我们来换个图试试
第一幅图
我去用ps画了图,如下
于是我们开始吧
按顺序来
第二幅图
被暗算了一波,我们换个颜色再来
第三幅图
只到最后一个imshow操作,连边都没有描
描完边之后就没了,明明figure里是有描边的,我也不知道为什么保存后就没了白边
分别把threshold修改为0.5和0.1
可以看到计算metric大于threshold的就会在质心出现黑圆圈
那我们再继续,这次把string_metric去掉
可以看到每个标记都相同了
我们来看最终结果