静态图像分割——水表指针提取(一)

说明

本文旨在提取水表指针实现自动读数,但由于能力有限,并未达到预期效果。仅供参考,欢迎共同探讨。

待处理图像

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总体思路

在这里插入图片描述

具体操作与结果

观察所给五张图的指针特征,可以分为两类,一类是四个红指针与四个黑指针的八指针水表,还有一类是四个红指针的四指针水表。因为所有图要采用统一的代码,所以需要找到五张图指针的共同点。

基于颜色模型ycbcr数据采集的分割

  • 采样颜色数据,归纳模型。因为基本就是提取红色与黑色,所以可以尝试通过肤色检测实验的思路来解决这个问题。但是观察可发现img1、img4光线较暗,颜色不正,需经过一定的预处理将图片调亮再提取颜色信息。同时,因为要提取红色与黑色,两者一起提取可能会导致精度下降,所以决定先提取红色指针,若效果较好则再提取黑色指针。

核心代码

光线补偿函数

function im=LightCompensate(I)
[m0,n0,l]=size(I);
%figure(1),imshow(I)
thresholdco=0.05;   %比例系数
thresholdnum=100;   %像素个数的临界常数
histogram=zeros(1,256);  %灰度级 数组
if m0*n0*thresholdco<thresholdnum
    disp('输入图像太小,请换一张!');
    return
end
gray=0;
index0=0;
for i=1:m0        %scan image
     for j=1:n0
          gray=round(I(i,j,1)*.250+I(i,j,2)*.200+I(i,j,3)*.200);
          index0=gray+1;
          histogram(1,index0)= histogram(1,index0)+1;
     end
 end
 calnum=0;
 total=m0*n0;
 num=0;
 %next获得满足系数thresholdco的临界灰度级
 index1=0;
 for i=1:256
     if calnum/total<thresholdco
         index1=256-i+1;
         calnum=calnum+histogram(1,index1);
         num=i;
     else
         break;
     end
 end
 averagegray=0;
 calnum=0;
 k=256-num+1;
 % 获得满足条件的像素总的灰度值
 for i=256:-1:k
     averagegray=averagegray+histogram(1,i)*i;
     calnum=calnum+histogram(1,i);
 end
 averagegray=averagegray/calnum;
 co=255.0/averagegray;
 %进行光线补偿
  for i=1:m0
      for j=1:n0
          I(i,j,1)=I(i,j,1)*co;
          if I(i,j,1)>255
              I(i,j,1)=255;
          end
          I(i,j,2)=I(i,j,2)*co;
          if I(i,j,2)>255
              I(i,j,2)=255;
          end
          I(i,j,3)=I(i,j,3)*co;
          if I(i,j,3)>255
              I(i,j,3)=255;
          end
      end
  end
 im=zeros(m0,n0,l);
im=I; 

提取指针

%提取红色指针
I=imread('img4.jpg');
ycbcr=rgb2ycbcr(I);
y=ycbcr(:,:,1);
cb=ycbcr(:,:,2);
cr=ycbcr(:,:,3);
id=cr>160&cr<225&cb<124&cb>100;
figure(1),subplot(1,3,1),imshow(I),
subplot(1,3,2),imshow(id)
a=imfill(id,'holes');
se=strel('disk',3);
aa=imdilate(a,se);
subplot(1,3,3);imshow(aa);
%提取黑色指针
files=dir('img4.jpg');   %读入图片
for i=1:length(files)   %循环
s=files(i).name;
I=imread(s);            %得到三维矩阵
I1=I;
R=I1(:,:,1);            %红色通道
G=I1(:,:,2);            %绿色通道
B=I1(:,:,3);            %蓝色通道
id=R>8&R<30&G>8&G<30&B>5&B<35;    %阈值
se=strel('disk',2);
a=imdilate(id,se);
figure,subplot(2,1,1),imshow(I),          %显示原始图像
subplot(2,1,2),imshow(a);end

算法分析

光线补偿的简单原理是将颜色的三个通道同比例放大或者缩小。因为要提取红色指针,同时色彩空间ycbcr模型中的cr代表红色浓度偏移量成分,依据公式将rgb数值转化为ycbcy数值。观察提取的红色的rgb、hsv、ycbcr的数据,发现ycbcr模型中cb、cr的数据较为集中,所以采用了ycbcr模型。观察数据且不断调试,得到限定条件为cr>160&cr<225&cb<124&cb>100,即将符合此条件的像素点设为1。最后经过二值形态学的适当处理得到最终效果。

实验结果

在这里插入图片描述
在这里插入图片描述
分析图1.1可知,一定的光线补偿在以ycbcr颜色空间中提取红色能取得良好的改善。但对于img1而言,光线补偿的作用不大。且从图1.2中可以得到,除了img1的提取效果极差,其余四幅图的效果均不错。
由于整个水表图像中黑色区域较多,包括背景、小表盘上的字等细节,所以黑色指针的提取不采用此方法(从图1.3可得)。img2与img3已可根据此方法得到较好的分割结果。

基于阈值的分割

观察图像的直方图,发现直方图没有显著的高峰或者低谷,所以不能采用全局阈值的双峰法。同时,光照较为均匀也无需采用局部阈值法。类间方差最大法(OTSU)的准则是使进行阈值处理后分离的像素类之间的类间方差最大。当然,也可以尝试自己设定阈值来取得预期效果。

核心代码

OTSU-提取红色指针

I=imread('img1.jpg');
ycbcr=rgb2ycbcr(I);
y=ycbcr(:,:,1);
cb=ycbcr(:,:,2);
cr=ycbcr(:,:,3);
thr=graythresh(cr);  %cr提取红色指针
bw=im2bw(cr,thr);
figure(2),subplot(2,2,1),imshow(bw)
se=strel('disk',2);
bw=imclose(bw,se);   %闭操作
bw=~bw;
subplot(2,2,2),imshow(bw);
b=imfill(bw,'holes');%孔洞
subplot(2,2,3),imshow(b);
ss=imsubtract(bw,b); %相减
sss=~ss;
subplot(2,2,4),imshow(sss,[]);

自设阈值法-基于灰度提取黑色指针

I=imread('img1.jpg');
f=rgb2gray(I);         %选取灰度通道,提取黑色指针
g=zeros(size(f));
T=40;                    %自选阈值
id = f <= T;
g(id)=1;
subplot(2,2,1),imshow(g);
g=~g;                    %取反
subplot(2,2,2),imshow(g);
b=imfill(g,'holes');   %孔洞
subplot(2,2,3),imshow(b);
s=imsubtract(b,g);     %相减得到指针
s=~s;
se=strel('disk',2);
s=imopen(s,se);
subplot(2,2,4),imshow(s,[]);

算法分析

OTSU采用遍历的方法得到使类间方差最大的阈值,调用graythresh函数。基于灰度通道的算法主要通过尝试阈值以取得最佳的提取黑色指针的效果。
将两者结合,基于ycbcr的OTSU提取红色指针,基于灰度的自定阈值提取黑色指针。得到黑色、红色指针效果图后,进行形态学处理、孔洞填充得到尽量只剩下指针的图,最后加两者相加得到黑色与红色指针。

实验结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
分析可得,在这种方法下,img5的效果较佳,虽然中间的标志没有消去,但指针保留完好。该方法对于四指针水表的分割结果显然没有方法A好,但对img1的指针提取较完整,指针方向基本可以辨认。对于img4的提取指针方向明显,但是背景环境复杂且还有一个指针未被提取出来,仍然需要寻找方法。
对于img4经过尝试发现对于ycbcr颜色模型的y通道较为敏感,对y通道采用OTSU。代码详见 B.基于阈值的分割\img4-ycbcr.txt。效果如下,但仍然有一个靠近盖子的指针无法提取。

基于区域的分割-区域生长

核心代码

I=imread('img4.jpg');
if isinteger(I)
    I=im2double(I);
end
I = rgb2gray(I);
figure 
imshow(I)
[M,N]=size(I);
[y,x]=getpts;    %鼠标单击取中心像素
x1=round(x);
y1=round(y);
seed=I(x1,y1);   
J=zeros(M,N);
J(x1,y1)=1;
count=1;  
threshold=0.04;
while count>0
    count=0;
    for i=1:M                    %遍历图像
        for j=1:N
            if J(i,j)==1 
                if (i-1)>1&(i+1)<M&(j-1)>1&(j+1)<N %3*3邻域在图像范围内
                    for u=-1:1    %八邻域生长
                        for v=-1:1
                            if J(i+u,j+v)==0&abs(I(i+u,j+v)-seed)<=2.5&abs(I(i+u,j+v)-seed)<=threshold
                                J(i+u,j+v)=1;
                                count=count+1;  %记录此次新生长的点个数
                            end
                        end
                    end
                end
            end
        end
    end
end
subplot(1,2,1),imshow(I);
subplot(1,2,2),imshow(J);

算法分析

区域生长分割主要分为以下几步:对每个要分割的区域选择一个像素作为生长起点(鼠标点击);采用八邻域将周围具有相同或相似的像素合并到一个区域;将新像素作为新的种子像素,迭代,直至没有可接受的领域像素。

实验结果

在这里插入图片描述
由于前两种方法对其余图均取得了较好的结果,但对于img4始终有一个指针未被识别,所以采用区域生长的方法对其进行提取。通过修改阈值,将难以提取的指针提取出来,并与之间得到的效果做加法,以上为较好的结果。因为区域生长方法需要人工选择初始生长像素点,不够智能,且效果和鼠标所选取的生长起始点位置有关,所以仅在特殊情况下使用,这里使用其提取img4的一个指针。

基于边缘的分割

核心代码

BW=imread('img1.jpg');  
BW=rgb2gray(BW);    
thresh=[0.01,0.10];   %越小线条越多,边缘越细致
sigma=2;                %定义高斯参数   越大越粗略    
f1= edge(double(BW),'roberts');  
f2= edge(double(BW),'sobel');  
f3= edge(double(BW),'prewitt');  
f4= edge(double(BW),'log');  
f5= edge(double(BW),'canny');  
f0= edge(double(BW),'canny',thresh,sigma); 
figure(1),
subplot(2,3,6),imshow(f0,[]),title('canny-1'); 
subplot(2,3,1),imshow(f1,[]),title('roberts');    
subplot(2,3,2),imshow(f2,[]),title('sobel');
subplot(2,3,3),imshow(f3,[]),title('prewitt');  
subplot(2,3,4),imshow(f4,[]),title('log');   
subplot(2,3,5),imshow(f5,[]),title('canny'); 

算法分析

边缘检测的核心思想是通过一阶导数的幅度值来检测边缘,通过二阶导数的
过零点确定边缘位置,对x、y方向各用一个模板。Roberts采用交叉的差分利算子,对具有陡峭的低噪声的图像效果好,但提取的边缘较粗;sobel采用滤波算子,对灰度渐变和噪声较多的图像处理效果较好,但对边缘定位不准确;prewitt采用加权平均算子,对噪声有抑制作用,但边缘较宽间断点多;log采用二阶导数算子,能对任何走向的线条进行锐化,但对噪声敏感。
Canny算子具有良好的边缘检测性能,基本流程是先进行高斯平滑滤波,计算梯度幅值和方向,再进行非最大化抑制,然后进行双阈值处理,高阈值用于检测梯度幅值大的强边缘、低阈值用于检测梯度幅值小的弱边缘,最后将轮廓段连接起来。Canny方法不易受到噪声干扰,能够检测到弱边缘。

实验结果

在这里插入图片描述
观察上图可以发现log算子和canny算子的效果较好,同时,调节canny的参数可以改变边缘提取效果。总体来说,canny方法基本标出了指针的轮廓。对经过边缘检测的图像进行形态学处理、代数运算等,暂时没有想到只留下指针的方法。

K-means

核心代码

close all;
clear;
I_rgb = imread('img5.jpg');   %读取文件数据
C = makecform('srgb2lab');    %lab彩色空间:l照度,a红色,b蓝色,设置转换格式
I_lab = applycform(I_rgb, C); %颜色空间转换
ab = double(I_lab(:,:,2:3));  %取出lab空间的a分量和b分量
nrows = size(ab,1);
ncols = size(ab,2);
ab = reshape(ab,nrows*ncols,2);%变成一行矩阵
nColors = 4;                   %分割的区域个数
[cluster_idx cluster_center] = kmeans(ab,nColors,'distance','sqEuclidean'); 
%idx:聚类标号   center:聚类质心位置     ab:矩阵    ncolor:几类
%'distance':距离测度
%'sqEuclidean':欧氏距离
%'Replicates':聚类重复次数
pixel_labels = reshape(cluster_idx,nrows,ncols);
figure(1),imshow(pixel_labels,[]), title('聚类结果');

算法分析

K-means算法主要思想是遍历每一个像素点,给每一个像素点找到距离其最近的中心点,即簇内相似度高、簇间相似度低。K-means算法简单,且当簇接近正态分布时效果好,但是K-means需要预先设定聚类个数,当重复次数多时程序执行慢。

实验结果

在这里插入图片描述
从上图可以发现,聚类个数为4时,基本能得到红色指针,对于背景光线较为复杂的图像处理效果不佳(img1)。此方法对于提取红色指针具有一定的普适性,但是由于要得到聚类中仅呈现指针的一类,则需要通过呈现所有单独的聚类后进行人工选择才可确定(如图5.2上选择第三张,图5.2下选择第二张),所以,此方法人工参与成分较多。

对特定区域提取

核心代码

A = imread('img5.jpg');
imshow(A);
Rmin = 10;
Rmax = 60;
[centersBright, radiiBright] = imfindcircles(A,[Rmin Rmax],'ObjectPolarity','bright');
viscircles(centersBright, radiiBright,'EdgeColor','b');

实验结果

在这里插入图片描述
预期打算抠出表盘的指针表盘,然后对指针表盘进行分割,这样背景的复杂性对提取的效果就不会有太大影响。但是调用MATLAB的imfingcircles函数时,发现并不能很好的提取出小圆,圆不完整、圆形变等都使得表盘框定效果不佳。所以放弃了这一思路。

总结

对于四指针水表

  • 代码
I=imread('img2.jpg');
ycbcr=rgb2ycbcr(I);
y=ycbcr(:,:,1);
cb=ycbcr(:,:,2);
cr=ycbcr(:,:,3);
id=cr>160&cr<185&cb<124&cb>113;
figure(1),subplot(2,1,1),imshow(I),
subplot(2,1,2),imshow(id)
a=imfill(id,'holes');
figure(2);imshow(a);
  • 结果
    在这里插入图片描述

对于八指针水表

  • 代码
I=imread('i4.jpg');
f=rgb2gray(I);         %选取灰度通道,提取黑色指针
g=zeros(size(f));
T=40;                  %自选阈值
id = f <= T;
g(id)=1;
subplot(2,2,1),imshow(g);
g=~g;                  %取反
subplot(2,2,2),imshow(g);
b=imfill(g,'holes');   %孔洞
subplot(2,2,3),imshow(b);
s=imsubtract(b,g);     %相减得到指针
s=~s;
se=strel('disk',2);
s=imopen(s,se);
subplot(2,2,4),imshow(s,[]);
ycbcr=rgb2ycbcr(I);
y=ycbcr(:,:,1);cb=ycbcr(:,:,2);cr=ycbcr(:,:,3);
thr=graythresh(cr);  %cr提取红色指针
bw=im2bw(cr,thr);
figure(2),subplot(2,2,1),imshow(bw)
se=strel('disk',2);
bw=imclose(bw,se);   %闭操作
bw=~bw;
subplot(2,2,2),imshow(bw);
b=imfill(bw,'holes');%孔洞
subplot(2,2,3),imshow(b);
ss=imsubtract(bw,b); %相减
sss=~ss;
subplot(2,2,4),imshow(sss,[]);
aa=imadd(sss,s);     %红黑指针相加
figure(3),
subplot(1,2,1),imshow(aa,[]);
se=strel('disk',1);
ab=imclose(aa,se);
subplot(1,2,2),imshow(ab,[]);
figure(4),imshow(s,[]);
figure(5),imshow(sss,[]);
figure(6),imshow(ab,[]);
saveas(4,'4-r.jpg');saveas(5,'4-b.jpg');saveas(6,'4.jpg');
  • 结果
    在这里插入图片描述
    对于img4效果不佳,有两个指针消失,下面为解决该问题的代码
    Ycbcr提取7个指针:
I=imread('img4.jpg');
ycbcr=rgb2ycbcr(I);
y=ycbcr(:,:,1);
cb=ycbcr(:,:,2);
cr=ycbcr(:,:,3);
thr=graythresh(y);
bw=im2bw(y,thr);
figure(3),subplot(2,2,1),imshow(bw)
se=strel('disk',2);
bw=imclose(bw,se);
subplot(2,2,2),imshow(bw);
b=imfill(bw,'holes');
subplot(2,2,3),imshow(b);
s=imsubtract(bw,b);
subplot(2,2,4),imshow(s,[]);
se1=strel('disk',2);
ss=imdilate(s,se1);
figure();
imshow(ss,[]);

用区域生长提取剩下的一个指针。最终效果如下:
在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值