数字仪表自动读数(Matlab)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

由于之前实验室需要通过图片人工读取光强计(数字表)的读数,所以就根据网上的代码自己实现了让matlab自动读取数字仪表的读数.

一、代码主要思路

步骤一:读取文件夹下图片文件(可以选择好几张图片)
步骤二:矫正定位仪表框位置
步骤三:剪裁图表框进行预处理(灰度化,二值化,去噪,细化)
步骤四:横切和竖切,选出图像数字
步骤五:判断七段数码管a,b,c,d,e,f,g位置是否亮起
步骤六:判断读数

二、代码

%数字仪表读数%2020 07 08
close all
clear
clc

%读取文件夹下所有图片文件路径
[ReadFileName,ReadPathName,ReadFilterIndex] = uigetfile({'*.bmp','BMPFile(*.bmp)';'*.bmp','BMPFile(*.bmp)';'*.*','AllFile(*.*)'},'ReadBMP',...
    'MultiSelect','on',  'H:\pictureDir');

if isequal(ReadFileName,0) || isequal(ReadPathName,0) || isequal(ReadFilterIndex,0)
    msgbox('读取错误,请重新输入');
    
else
    if iscell(ReadFileName)
        input_bmp = cell(length(ReadFileName),1);
        full_path =input_bmp;
        for i = 1:length(ReadFileName)
            full_path{i} = fullfile(ReadPathName,ReadFileName{i});
        end
        %VideoObject = VideoReader(VideoFullPath{1});
    else
        full_path = fullfile(ReadPathName,ReadFileName);%将图片文件全部读进路径
    end
end
%对图片进行for循环,分别读取每一张图片
for n1=1:length(ReadFileName)
    file = full_path{n1};%数字
    I0 = imread(file);
    figure(1);imshow(I0);title('待读数图片');
    % [x,y,button]=ginput(2);
    % angle =90-abs((atan((y(2)-y(1))/(x(2)-x(1)))))*180/pi;
    
    angle =86.8202;
    %将仪表旋转正(这是我自己计算的角度,因为仪表的位置是固定的,所以避免每次都要自己手动矫正仪表使之处于相对于相机竖直状态)
    I0 = imrotate(I0,angle);
    %[x,y,button]=ginput(2);
    %裁剪出显示框
    I0=I0(1083:1278,864:1356);%也可以用ginput选取仪表位置,但是每一次都要选太麻烦
    figure(1);imshow(I0);title('待读数图片');
    scale = 300/length(I0);
    Irsz = imresize(I0,scale);%将裁剪的表框resize
    clear I0;%因为每一张图片都是用I0表示,所以添加了clear,将I0清出工作区
    
    if length(size(Irsz))==3
        Ig=rgb2gray(Irsz);
    else
        Ig=Irsz;
    end
    clear Irsz;
    
   Ig=imadjust(Ig,[0,0.3],[0,1],1.5);%图像灰度调整
    %figure(4),imshow(Ig);
    
    Ib = im2bw(Ig,graythresh(Ig)*0.8);
    clear If;
    
  %figure(3),imshow(Ib);
  %如果mp>0.5,则二值化之后表盘为黑,要取反,使数字为黑
   mp = mean(mean(Ib(1,:))+mean(Ib(end,:))...
        +mean(Ib(:,1))+mean(Ib(:,end)));
    if mp>0.5
        Ib = ~Ib;
    end
    %figure;imshow(Ib);%-------------------
    
    [m,n] = size(Ib);
    SE = strel('square',round(m/50)); %初步去噪,图片开运算(先腐蚀后膨胀),去除噪声
    Iop = imopen(Ib,SE);
    figure;imshow(Iop);%------------------
    
    SE = strel('square',round(m/10));
    Icl = imclose(Iop,SE);clear Iop; %闭运算,希望将组成数字的各LED段连接在一起
    figure;imshow(Icl);title('Icl')%-------------------
    Ith = bwmorph(Icl,'thin',inf);%图片细化,将数字变为由细线组成
   figure; imshow(Ith);%-----------------
    
    sRI = sum(Ith');    %进一步去噪
    upidx = find(sRI>0, 1, 'first' );
    dnidx = find(sRI>0, 1, 'last');
    pse = round((dnidx-upidx)/40);
    SE = strel('square',pse);
    Ioc = imopen(Icl,SE);
    Ith = bwmorph(Ioc,'thin',inf);clear Iop;
    figure; imshow(Ith);title('Ith')%-----------------
    
    %%%%%%%%%%%%%%%%%%%%%%%%%对字符的处理识别%%%%%%%%%%%%%%%%%%%%%%%%
    %%%cut(认为读数是单排的)
    %%横切
    sRI = sum(Ith');
    upidx = find(sRI>0, 1, 'first' );
    dnidx = find(sRI>0, 1, 'last');
    udidx = upidx:dnidx;
    ud = length(udidx);
    thrlen = ud/2; %判定LED段是否点亮的初步阈值
    Ith = Ith(udidx,:);
    %figure; imshow(Ith);%-----------------
    %%竖切
    sCI=sum(Ith);
    BsCI = (sCI>0); %将x轴投影矩阵scI变为只有01的矩阵
    dBsCI = diff(BsCI); %找BscI中为1的元素的范围的第一步,dBscI中的元素只有-101三种
    idxp = find(dBsCI==1)+1; %记录dBscI中为1的元素的位置,加1是对应BscI中1元素群开始位置
    idxn = find(dBsCI==-1);%记录dBscI中为-1的元素位置,对应BscI中1元素群结束位置,与idxp长度一样
    if length(idxp)~=length(idxn)
        idxn(find(idxn<idxp(1)))=[];%找到第一个为-1的位置
    end
    Cidxchar = [idxp;idxn]; %一个2行多列矩阵,每列对应一个字符,列的第一行为起始位置,第二行为结束位置
    dCidxchar = diff(Cidxchar);%就是第二列的数据减去第一列的数据
    tdCidx = find(dCidxchar < thrlen/2);
    %%处理切割得到的符号,将“'”型字符归到上一个字符去
    if ~isempty(tdCidx)
        ltdCidx = length(tdCidx);
        delemt0 = [];
        delemt1 = [];
        delemt2 = [];
        for i=1:ltdCidx
            lridx = Cidxchar(1,tdCidx(i)):Cidxchar(2,tdCidx(i));
            tcharI = Ith(:,lridx)';
            [m,n]=size(tcharI);
            if m>1
                sRtc = sum(tcharI);
            else
                sRtc = tcharI;
            end
            BsRtc = (sRtc>0);
            dBsRtc = diff(BsRtc);
            cupidx = find(dBsRtc==1,1,'first')+1;
            cdnidx = find(dBsRtc==-1, 1, 'last' );
            hc = cdnidx-cupidx;
            if ~isempty(hc)&&hc<thrlen/2&&cdnidx<ud*0.3
                delemt0 = [tdCidx(i),delemt0];%如果hc不为空,且长度很小
            end
            if ~isempty(hc)&&hc>thrlen/2 && hc<thrlen && cdnidx<ud*2/3
                delemt1 = [tdCidx(i),delemt1];
            end
            if ~isempty(hc)&&hc>thrlen/2 && hc<thrlen && cupidx<ud*2/3
                delemt2 = [tdCidx(i),delemt2];
            end
        end
        
        if ~isempty(delemt1)
            Cidxchar(2,delemt1-1) = Cidxchar(2,delemt1);
        end
        if ~isempty(delemt2)
            Cidxchar(1,delemt2+1) = Cidxchar(1,delemt2);
        end
        delemt = sort([delemt0,delemt1,delemt2]);
        if ~isempty(delemt)
            Cidxchar(:,delemt) = [];
        end
    end
    
    nchar = length(Cidxchar);%读数包含的字符个数(包括小数点)
    %%%%显示切割效果%---------------------------------
%     for i=1:nchar
%         figure;
%         imshow(Ith(:,Cidxchar(1,i):Cidxchar(2,i)));
%     end
%     
    %%%确定各LED段点亮与否
    ntab = [
        1 1 1 1 1 1 0  %0
        0 1 1 0 0 0 0  %1
        1 1 0 1 1 0 1  %2
        1 1 1 1 0 0 1  %3
        0 1 1 0 0 1 1  %4
        1 0 1 1 0 1 1  %5
        1 0 1 1 1 1 1  %6
        1 1 1 0 0 0 0  %7
        1 1 1 1 1 1 1  %8
        1 1 1 1 0 1 1  %9
        0 0 0 0 0 0 0  %.
        0 0 0 0 0 0 1  %-
        1 1 1 0 1 1 0 %0
        ];
    result = [];
    for i=1:nchar
        lridx = Cidxchar(1,i):Cidxchar(2,i);
        if length(lridx) < thrlen/2
            charI = [zeros(ud,int16(thrlen)-length(lridx)),Ith(:,lridx)];
        else
            charI = Ith(:,lridx);
        end
        [m,n] = size(charI);
        %               figure;imshow(charI)%-------------------
        %%%%%agd段LED,由于字符可能有倾斜,取四分之一为判断标准
        xthrlen = thrlen/4;
        ayidx = 1:int16(m/8);      %a段y轴方向长度取为总长的八分之一
        asCchar = sum( charI( ayidx , :) ); %a段LED的x轴投影
        BasCchar = asCchar(asCchar<2);
        sBa = sum(BasCchar);
        fa = (sBa > xthrlen); %a段LED是否点亮
        gyidx = int16(7*m/16):int16(9*m/16);
        gsCchar = sum(charI(gyidx,:));%g段LED的x轴投影
        BgsCchar = gsCchar(find(gsCchar<2));
        sBg = sum(BgsCchar);
        fg = (sBg > xthrlen); %g段LED是否点亮
        dyidx = int16(7*m/8):m;
        dsCchar = sum(charI(dyidx,:));    %d段LED的x轴投影
        BdsCchar = dsCchar(find(dsCchar<2));
        sBd = sum(BdsCchar);
        fd = (sBd > xthrlen); %d段LED是否点亮
        %%%%%fedc段LED,投影到y轴一般不倾斜,取二分之一为判断标准
        ythrlen = thrlen/2;
        fyidx = 1:int16(m/2); fxidx = 1:int16(n/2);
        fsRchar = sum(charI(fyidx,fxidx)');%f段LED的y轴投影
        BfsRchar = (fsRchar>0);
        sBf = sum(BfsRchar);
        ff = (sBf>ythrlen);               %f段LED是否点亮
        eyidx = int16(m/2):m; exidx = 1:int16(n/2);
        esRchar = sum(charI(eyidx,exidx)');%e段LED的y轴投影
        BesRchar = (esRchar>0);
        sBe = sum(BesRchar);
        fe = (sBe>ythrlen);               %e段LED是否点亮
        cyidx = int16(m/2):m; cxidx = int16(n/2):n;
        csRchar = sum(charI(cyidx,cxidx)');%c段LED的y轴投影
        BcsRchar = (csRchar>0);
        sBc = sum(BcsRchar);
        fc = (sBc>ythrlen);               %c段LED是否点亮
        byidx = 1:int16(m/2); bxidx = int16(n/2):n;
        bsRchar = sum(charI(byidx,bxidx)');%b段LED的y轴投影
        BbsRchar = (bsRchar>0);
        sBb = sum(BbsRchar);
        fb = (sBb>ythrlen);               %b段LED是否点亮
        
        A=[fa,fb,fc,fd,fe,ff,fg];
        for h=1:13
            if sum(ntab(h,:)==A)==7
                break;
            end
        end
        if h<=10
            str(i)=num2str(h-1);
        else
            if h==11
                str(i)='.';
            else
                if h==12
                str(i)='-';
                else
                    if h==13
                    str(i)='0';
                    end
                end
            end
        end
        
    end
    a(n1)=str2double(str);
end

总结

站上很多仪表识别的方法都是针对于指针式仪表的,很少有对数字仪表识别的方法。另外我也看到了其它博主用CNN进行数字仪表识别,效果也不错。但是此方法胜在简便,把原代码改吧改吧就可以直接使用。

声明: 本文的主要代码都是基于

https://blog.csdn.net/TIQCmatlab/article/details/115434596?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163902114916780265461447%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=163902114916780265461447&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-5-115434596.first_rank_v2_pc_rank_v29&utm_term=matlab%E6%95%B0%E5%AD%97%E4%BB%AA%E8%A1%A8%E8%AF%BB%E6%95%B0&spm=1018.2226.3001.4187

这篇文章的,并非百分百原创。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值