matlab——识别图像中的圆形目标

说明

资料来自《详解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

其他用语说明

  1. 三维数组
    这一个三维数组在本次案例中是指imread函数操作来的三维数组,如RGB,显示如下

在matlab中的读取样式

再打开

再打开这个图是

动漫图

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]

其他用语解释

  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,即为所求。
    内容学习来源
    作为一个热爱学习的人,我当然得去看看这个函数在干些啥
    于是我去画了个图
    黑白五五卡一号
    于是
    没想到
    那如果我用三色呢
    三色
    于是
    三色
    我们再次引用这里的的概念来解释平均灰度
  1. 平均灰度,反映整体的亮、暗,越高越亮
  2. 平均对比度,一幅图亮暗的差异
  3. 平滑度,一幅图灰度的均匀性,0-1:平滑-不平滑
  4. 三阶矩,直方图偏斜度的度量。对称为0,右偏斜为正,左偏斜为负
  5. 一致性,度量一致性
  6. 熵,反映像素的随机性,越大越粗糙

im2bw函数

  • BW=im2bw(I,level)
    阈值法转换图像为二值图像
    将灰度图像I转换成二值图像BW
    level为阈值,即标准化灰度值,范围为[0 1]
    I中大于level的像素设为1(白色),小于的像素为0(黑色)

结果当然会得到二值图像
逻辑矩阵
继续打开

好不容易找到的有1的地方

figure函数

  • figure
    创建新的图形对象,在屏幕上单独显示的窗口,且窗口中可以输出图形

可以看到想要提高效率(偷懒),可以只打一个figure代码,就可以按顺序新建一个图像窗口,如下图(本次代码产生的)

无描述
本次产生的图是
第二张图

Figure 3

然后去除图像中的噪声,开运算去除小于30个像素的目标

bw=bwareaopen(bw,30);
figure;
imshow(bw);

bwareaopen函数

  • BW2=bwareaopen(BW,P)
    1. 移除二值图像BW中所有少于P个像素的连通元素,生成另一个二值图像BW2
    2. 对二值图像进行形态学开运算,用于移除小目标
  • 对于二维图像默认采用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
我们继续打开

打开se
可以看到还有两个按钮可以继续点开
再开!

Neighborhood
另外一个也不会放过的

Dimensionality
查阅资料知道,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
一个元胞数组,然后打开

套娃
套娃游戏,然后我们继续打开可以看到的是,这就是边界像素点的坐标

继续打开

  • 而本次代码中使用的方式是
    [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函数是在干些啥
它的用途有这些
在这里插入图片描述那结果呢
在这里插入图片描述我们可以看到,

  1. 对于一维向量,diff函数会用右减左,即相邻数的增量

  2. 对于二维的矩阵

    • 对于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去掉
在这里插入图片描述可以看到每个标记都相同了
我们来看最终结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43600632/article/details/105535107