华南农业大学|图像处理与分析技术综合测试|题目解答:识别时钟上时间

news2024/9/29 15:26:45

设计任务

这是一幅表盘图像,试采用图像处理和分析技术,设计适当的算法和程序,计算出表盘上的指示时间(要求精确到秒)。请按统一要求写出算法原理、设计流程,并完成测试分析等报告内容。

算法设计

解题思路:使用霍夫变换检测出表针对应的直线,通过表针的长度区分出时针、分针、和秒针。通过各直线间的角度确定指示时间。

设计方案说明:

  1. 为了使霍夫变换只检测出三条直线,把原图像转化为灰度图,并使用灰度范围提取开闭运算得到只有表针和表盘中心的图像
  2. 然后使用霍夫变换。得到了直线两端的端点。为了确定哪个点是指针末端端点,计算两个点的纵坐标到表盘中心纵坐标(311)的距离,距离大的是指针末端的端点
  3. 使用霍夫变换后得到的直线不是在表针中心的,故使用三个大小和原图像相等的逻辑值矩阵掩膜(除了感兴趣区其他值为0),其中分别包含以三条直线末端点(表针末端)为中心,大小为13*13(使用imtool函数,发现指针的宽度小于6个像素,因此使用13*13的感兴趣区能包含完整的指针末端)的感兴趣区(值为1),与原图像进行乘法运算提取目标区域
  4. 对于每个目标区域,行遍历的方向是从没有白色行到有白色行,逐行向左遍历,找到碰到值从0变为1的点,以及边缘对应的斜率值突变(由正变负或由负变正)的点,记录下来,而后又逐行向右遍历,记录下点。若在向左或向右遍历中得到两个点,那么这两个点就是指针末端边的相聚最远的边缘点
  5. 对于指针的端边,由于其纵坐标随横坐标的变化是单调的,可通过其上所有点中横坐标的最大值加上横坐标的最小值除以2,再向下取整,得到指末端针中心对应最接近的横坐标点;纵坐标的算法同理。进而得到指针末端中心坐标
  6. 还需要确定表盘中心的坐标。使用imtool函数和scatter函数,得到表盘中心的坐标为(329,311)。
  7. 用指针末端中心坐标为尾端,表盘中心坐标为首端做一个向量,把它变成单位向量,求出它与和指向12点指针同方向的单位向量(1,0)的夹角。通过指针末端中心坐标和表盘中心位置的相对关系与夹角,即可算出表盘上显示的时间

关键算法的设计原理:

形态学开闭运算:消除白色小边缘。

乘法运算:0乘以任何数都是0。

从左到右遍历和从右到左遍历可以筛选出指针末端相聚最远的边缘点原因:设置条件为只有当边缘斜率值突变(由正变负或由负变正)才可以统计此点。从两边都遍历,其中有一边遍历斜率值为0,即被掩模挡住不能统计,那就会有一个点不能被统计,应该选择另外一边统计到的点(可看图5理解)。

说明7:有两种时钟,第一种时钟是时针、分针、秒针不停转的;第二种时钟是等一种针转过一圈,另一种(或两种)针才会转一个格。由图像可知,分针在两个时针刻度之间,因此此时钟是第一种时钟。因此有时针:分针:秒针的角速度关系为1:60:3600。由此出现有两种解法,第一种解法是通过时针和经过数字12的刻度和表盘中心的直线间的角度,直接算出现在的时间,但由于识别直线存在系统误差,这种方法读数不准确;因此选用第二种解法。

其他原理:观察+经验+高等数学。

算法步骤:

灰度图→灰度范围提取→开闭运算→霍夫变换→确定指针末端区域→掩模提取→求指针末端距离最远两点→指针末端中心坐标→算指针方向的单位向量与12点种方向的夹角→求时间

程序设计

算法名称:灰度特征提取、灰度范围提取、二值化图像、形态学开闭运算、掩模、乘法运算等。

工具函数:霍夫变换:hough、houghpeaks、houghlines等。

设计分析:完成任务。

测试分析

 图1

图1为原图→灰度图→提取灰度范围为[20,120)为亮部的二值图→开闭运算后图。可以看到,经过提取灰度范围一步,表面上数字和刻度剩下边缘;经过开闭运算后,边缘去除。

 图2

图2为“开闭运算后图”经过MATLAB内置霍夫变换后得到的线段和端点图。可以发现,线段上对应指针的末端的端点并不与指针末端中心重合。

 图3

图3为使用掩模提取后图。

图4

图4为感兴趣区放大图。

图5

(已手动放大)在图5中,红*为从左到右遍历找到的边缘点,绿*为从右到左遍历找到的边缘点。可以印证若一种遍历方法能找到两个点,那这两个点将是指针末端相聚最远的边缘点。

 图6

(已手动放大)图6,红*为优化找到的指针末端中点,绿*为仅通过霍夫变换得到直线末端点。对比发现,优化有效。

 图8中的蓝线为求出的指针中点和表盘中心的连线,基本与指针重合。绿*为仅通过霍夫变换得到直线末端点,有一定偏移。

结果:时钟上显示10时7分39秒 

特点与优势:算法有效,综合运用高数、经验、观察、图像处理知识

存在的问题和不足:1.代码过长,应使用函数式编程 2.秒针的角度偏大,需优化

MATLAB代码

clc,clear
dial_center=[329,311]; % 表盘中心

%% step1 -- 读入图像
I=imread('ipa01.jpg');
%% step2 -- 转化为灰度图Gray
Gray=rgb2gray(I);
sz=size(Gray);

%% step3 -- 提取Gray中灰度范围为[20,120)作为二值图的亮部,得到图像A
A=(Gray<120)-(Gray<20);

%% step4 -- 开闭运算,去掉图像A中的数字轮廓、钟表刻度轮廓
se=strel('disk',2);
B=imopen(A,se);
C=imclose(B,se);

%% step5 -- 霍夫变换,得到三条与指针中线不重合的直线
[H,theta,rho] = hough(C);
peaks = houghpeaks(H,3);
lines = houghlines(C,theta,rho,peaks);

%% step6 -- 确定指针末端端点
hms_point=zeros(3,3);% 指针末端端点
for i=1:3
    if abs(dial_center(2)-lines(i).point1(2))<abs(dial_center(2)-lines(i).point2(2))
        hms_point(i,1)=lines(i).point2(1);
        hms_point(i,2)=lines(i).point2(2);
    else
        hms_point(i,1)=lines(i).point1(1);
        hms_point(i,2)=lines(i).point1(2);
    end
    hms_point(i,3)=norm(lines(i).point1 - lines(i).point2);% 直线长度
end

%% step7 -- 通过长度排序,确定三条直线对应哪根指针
% 时针最短,分针最长,秒针长度居中
hms_point=sortrows(hms_point,3);
hms_point=[hms_point(1,:);hms_point(3,:);...
    hms_point(2,:)];

%% step7 -- 以各直线末端点为中心,造三种掩模
hms_mask=zeros(sz(1),sz(2),3); % ROI为每个指针末端的掩模
len=6; % (掩模边长-中心点(1像素))/2的值
for i=1:3
    x=hms_point(i,1);
    y=hms_point(i,2);
    hms_mask(y-len:y+len,x-len:x+len,i)=C(y-len:y+len,x-len:x+len);
end

%% step8 -- 求边缘点,当出现斜率突变时
endpoints=zeros(2,2,3);
lpoints=zeros(2,2,3);
rpoints=zeros(2,2,3);
for i=1:3
    st=hms_point(i,2)-len+1;
    ed=hms_point(i,2)+len;
    dir=1;
    temp1=find(hms_mask(st-1,:,i),1);
    if ~isempty(temp1)
        dir=-1;
        st=hms_point(i,2)+len;
        ed=hms_point(i,2)-len+1;
    end
    lflag=1000;
    rflag=1000;
    
    ltemp1=double.empty(1,0);
    rtemp1=double.empty(1,0);
    
    lis_vertical=0;
    ris_vertical=0;
    for j=st:dir:ed
        ltemp2=find(hms_mask(j,:,i),1);
        rtemp2=find(hms_mask(j,:,i),1,'last');
        
        % 记录从黑→白点
        if isempty(ltemp1) && ~isempty(ltemp2)
            lpoints(1,1,i)=ltemp2;
            lpoints(1,2,i)=j;
            rpoints(1,1,i)=rtemp2;
            rpoints(1,2,i)=j;
        end
        
        % 边缘斜率突变
        if ~isempty(ltemp1) && ~isempty(ltemp2)
            if lflag~=1000 && sign(lflag)~=sign(ltemp2-ltemp1)&&...
                    (ltemp2-ltemp1)~=0
                lpoints(2,1,i)=ltemp1;
                lpoints(2,2,i)=j-dir;
                if lis_vertical==1
                    lpoints(2,2,i)=j-2*dir;
                end
            end
            lflag=ltemp2-ltemp1;
        end
        
        if ~isempty(rtemp1) && ~isempty(rtemp2)
            if rflag~=1000 && sign(rflag)~=sign(rtemp2-rtemp1)&&...
                    (rtemp2-rtemp1)~=0
                rpoints(2,1,i)=rtemp1;
                rpoints(2,2,i)=j-dir;
                if ris_vertical==1
                    rpoints(2,2,i)=j-2*dir;
                end
            end
            rflag=rtemp2-rtemp1;
            
        end
        lis_vertical=(ltemp1-ltemp2)==0;
        ris_vertical=(rtemp1-rtemp2)==0;
        ltemp1=ltemp2;
        rtemp1=rtemp2;
    end
end
%% step9 -- 计算指针末端点坐标
old_hms_point=hms_point(:,1:2);
for i=1:3
    if sum(rpoints(:,:,i)==0,'all')==0
        hms_point(i,1)=floor((rpoints(1,1,i)+rpoints(2,1,i))/2);
        hms_point(i,2)=floor((rpoints(1,2,i)+rpoints(2,2,i))/2);
    else
        hms_point(i,1)=floor((lpoints(1,1,i)+lpoints(2,1,i))/2);
        hms_point(i,2)=floor((lpoints(1,2,i)+lpoints(2,2,i))/2);
    end
end
%% step10 --计算夹角
e=[1,0];% 取12点方向的单位向量
hms_vector=zeros(3,2);%算以时针、分针、秒针末端中点为尾端,以表盘中心为首端的向量的单位向量
theta_v=[];
for i =1:3
    hms_vector(i,1)=hms_point(i,1)-dial_center(1);
    hms_vector(i,2)=hms_point(i,2)-dial_center(2);
    len_v=norm(hms_vector(i,:));
    hms_vector(i,1)=hms_vector(i,1)/len_v;
    hms_vector(i,2)=hms_vector(i,2)/len_v;
    theta_v=[theta_v acos(abs(dot(hms_vector(i,:),e))...
        /(norm(hms_vector(i,:))*norm(e)))*180/pi];
end

%% step11 --结合指针点和夹角,计算时间并显示
refer=[30 6 6]; % 变化多少角度等于变化一个单位
add=[3 15 15]; % 每个象限
hms_times=zeros(3,1);
for i=1:3
    if hms_point(i,1)>=dial_center(1) && hms_point(i,2)<=dial_center(2)
        hms_times(i)=floor(theta_v(i)/refer(i));
    end
    if hms_point(i,1)>dial_center(1) && hms_point(i,2)>dial_center(2)
        hms_times(i)=floor(theta_v(i)/refer(i))+add(i);
    end
    if hms_point(i,1)<dial_center(1) && hms_point(i,2)>dial_center(2)
        hms_times(i)=floor(theta_v(i)/refer(i))+2*add(i);
    end
    if hms_point(i,1)<dial_center(1) && hms_point(i,2)<dial_center(2)
        hms_times(i)=floor(theta_v(i)/refer(i))+3*add(i);
    end
end

disp("时钟上显示"+num2str(hms_times(1))+"时"+num2str(hms_times(2))+"分"...
    +num2str(hms_times(3))+"秒");

%% 测试图展示
figure(1)
subplot(141),imshow(I);title("原图",'FontWeight','bold');
subplot(142),imshow(Gray);title("灰度图",'FontWeight','bold');
subplot(143),imshow(A);title("提取灰度范围为[20,120)为亮部的二值图",...
    'FontWeight','bold');
subplot(144),imshow(C);title("开闭运算后图",'FontWeight','bold');

% 注:figure(2)内容来自MATLAB官方文档
figure(2), imshow(C), hold on
max_len = 0;
for k = 1:length(lines)
    xy = [lines(k).point1; lines(k).point2];
    plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','green');

    % Plot beginnings and ends of lines
    plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow');
    plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red');

    % Determine the endpoints of the longest line segment
    len_temp = norm(lines(k).point1 - lines(k).point2);
    if ( len_temp > max_len)
        max_len = len_temp;
        xy_long = xy;
    end
    title("霍夫变换",'FontWeight','bold');
end

figure(3)
for i=1:3
    eval(['subplot(13',num2str(i),')'])
    imshow(hms_mask(:,:,i));
    title("掩模"+num2str(i)+"图",'FontWeight','bold');
end

figure(4)
for i=1:3
    x=hms_point(i,1);
    y=hms_point(i,2);
    eval(['subplot(13',num2str(i),')'])
    imshow(hms_mask(y-len:y+len,x-len:x+len,i));
    title("掩模"+num2str(i)+"局部放大图",'FontWeight','bold');
end

figure(5)
for i=1:3
    eval(['subplot(13',num2str(i),')'])
    imshow(hms_mask(:,:,i));
    hold on;
    plot(lpoints(:,1,i),lpoints(:,2,i),'*r');
    plot(rpoints(:,1,i),rpoints(:,2,i),'*g');
end
sgtitle("指针末端边的相聚最远的边缘点(红*为从左到右遍历,绿*相反)",...
        'FontWeight','bold');

figure(6)
for i=1:3
    eval(['subplot(13',num2str(i),')'])
    imshow(I);
    hold on;
    plot(hms_point(i,1),hms_point(i,2),'*r');
    plot(old_hms_point(i,1),old_hms_point(i,2),'*g');
    plot(dial_center(1),dial_center(2),'*b');
end
sgtitle("定位指针末端中点(红*是优化后的结果,绿*是霍夫变换得到直线上点的结果)",...
        'FontWeight','bold');

figure(7)
for i=1:3
    eval(['subplot(13',num2str(i),')'])
    imshow(I);
    hold on;
    plot(hms_point(i,1),hms_point(i,2),'*r');
    plot(old_hms_point(i,1),old_hms_point(i,2),'*g');
    plot([hms_point(i,1),dial_center(1)],[hms_point(i,2),dial_center(2)],'b','LineWidth',2);
end
sgtitle("定位指针末端点(蓝线为求出的指针中点与表盘中心的连线,绿*为仅使用霍夫变换得到的端点)",...
        'FontWeight','bold');

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/586181.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

张小飞的Java之路——第四十二章——字节流

写在前面&#xff1a; 视频是什么东西&#xff0c;有看文档精彩吗&#xff1f; 视频是什么东西&#xff0c;有看文档速度快吗&#xff1f; 视频是什么东西&#xff0c;有看文档效率高吗&#xff1f; 诸小亮&#xff1a;下面我们学习——字节流 张小飞&#xff1a;什么是字…

article-并联机械手爪运动学分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3aNKIR4E-1685371700448)(data:image/svgxml;utf8, )] 2.4.3 基于Robotics Toolbox的工具箱的模型检测 上文中&#xff0c;我们已经对采摘机器手爪运动学理论模型进行了创建&#xff0c;接下来要用MA…

V3.0_用exec族函数替代system()

注意点&#xff1a;exec族函数的使用 以execl为例&#xff1a; &#xff08;1&#xff09; &#xff08;2&#xff09;exec族函数中的函数调用失败时会设置error并返回-1&#xff0c;然后从源程序调用点接着往下执行。 执行成功后不会返回&#xff0c;也不会从源程序调用点接…

Redis缓存一致性问题及解决方案

对于没有并发的用户请求 先更新缓存&#xff0c;后更新数据库先更新数据库&#xff0c;后更新缓存 两者第二步没成功&#xff0c;都有问题 如果更新缓存成功&#xff0c;更新数据库没成功&#xff0c;一旦缓存失效&#xff0c;读取的仍是旧值如果更新数据库成功&#xff0c;…

MySQL官网下载Linux版本安装包

步骤一&#xff1a;https://www.mysql.com/ 步骤二&#xff1a;https://www.mysql.com/downloads/ 步骤三&#xff1a;https://dev.mysql.com/downloads/ 步骤四&#xff1a;https://dev.mysql.com/downloads/mysql/

二十分钟秒懂:实现前后端分离开发(vue+element+spring boot+mybatis+MySQL)

目录 开发者介绍 什么是前后端分离开发 vue与springboot开发的优势 Vue.js 的优势&#xff1a; Spring Boot 的优势&#xff1a; vue与springboot如何实现前后端连接 demo简介 重要部分前端部分代码 重要部分后端代码 后端解决跨域问题 Controller部分 xml部分 se…

商城APP开发需要哪些功能,如何选择开发公司

商城APP开发的功能有很多&#xff0c;从功能上看主要分为以下几个大类&#xff1a; 一、商品展示类&#xff1a;商家可以在这里展示自己产品的种类、数量、价格等信息&#xff0c;消费者在浏览和选择的时候&#xff0c;能直观的看到商家介绍。 二、优惠促销类&#xff1a;商家…

JDBC与DBCP整合

DBCP:DataBase Connection Pool,数据库连接池负责分配、管理和释放数据库连接&#xff0c;它允许应用程序重复使用一个现有的数据库连接&#xff0c;而不是再重新建立一个&#xff1b;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接…

第四章 相似矩阵与矩阵对角化

引言 题型总结中推荐例题有蓝皮书的题型较为重要&#xff0c;只有吉米多维奇的题型次之。码字不易&#xff0c;如果这篇文章对您有帮助的话&#xff0c;希望您能点赞、评论、收藏&#xff0c;投币、转发、关注。您的鼓励就是我前进的动力&#xff01; 知识点思维导图 补充&…

【Web服务器集群】Apache配置与应用

文章目录 一、构建虚拟web主机1.概述2.httpd服务支持的虚拟主机类型3.构建虚拟Web主机3.1基于域名的虚拟主机3.2基于IP地址的虚拟主机3.3基于端口的虚拟主机 4.Apache连接保持5.Apache访问控制 二、Apache日志管理rotatelogs分隔工具 三、总结1.Web虚拟主机部署步骤2.网页根目录…

DN-DETR代码学习笔记

代码地址&#xff1a;GitHub - IDEA-Research/DN-DETR: [CVPR 2022 Oral]Official implementation of DN-DETR 论文地址&#xff1a; https://arxiv.org/pdf/2203.01305.pdf DN-DETR是在DAB-DETR的基础上完成的&#xff0c;DN-DETR的作者认为导致DETR类模型收敛慢的原因在于匈牙…

【Linux升级之路】4_进程控制

文章目录 前言一、【Linux初阶】fork进程创建 & 进程终止 & 进程等待二、【Linux初阶】进程程序替换 | 初识、原理、函数、应用 & makefile工具的多文件编译三、【Linux初阶】进程替换的应用 - 简易命令行解释器的实现结语 前言 本片博客是 Linux操作系统 进程控制…

【周末闲谈】谈谈数学转码这一年来的体会与反思

——我们走了太远&#xff0c;以至于忘了为何出发 前言 笔者本科读的是数学专业&#xff0c;就是每天和数学分析、高等代数、概率论、随机过程等等这些理论打交道的专业&#xff0c;这个专业出来工作好像一般有两个方向就是金融和计算机&#xff0c;我选择了计算机方向。主要…

【自然语言处理】【大模型】ChatGLM-6B模型结构代码解析(单机版)

ChatGLM-6B模型结构代码解析(单机版) ​ 本文介绍ChatGLM-6B的模型结构&#xff0c;代码来自https://huggingface.co/THUDM/chatglm-6b/blob/main/modeling_chatglm.py。 相关博客 【自然语言处理】【大模型】ChatGLM-6B模型结构代码解析(单机版) 【自然语言处理】【大模型】BL…

日撸 Java 三百行day56-57

文章目录 day56-57 kMeans 聚类1.kMeans聚类理解2.代码理解2.1代码中变量的理解2.2代码理解 day56-57 kMeans 聚类 1.kMeans聚类理解 无监督的机器学习算法&#xff0c;其中k是划分为几个簇&#xff0c;并且选择k个数据作为不同簇的聚类中心&#xff0c;计算每个数据样本和聚…

ASRT语音识别系统的部署以及模型的使用(运用篇)

ASRT语音识别系统的部署以及模型的使用(运用篇) 前言 ASRT是一个中文语音识别系统&#xff0c;由AI柠檬博主开源在GitHub上。 GitHub地址&#xff1a;ASRT_SpeechRecognition 国内Gitee镜像地址&#xff1a;ASRT_SpeechRecognition 文档地址&#xff1a;ASRT语音识别工具文…

Python打包成EXE

一、使用Pyinstaller pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyinstaller 1.2Pyinstaller打包步骤 Pyinstaller -F -w -i apple.ico py_word.py 结果&#xff1a; 运行结果&#xff1a; 二、使用Auto-py-to-exe auto-py-to-exe 是一个用于打包 python 程序…

第二届(2023年)中国国际培育钻石产业发展与创新大会盛大召开!

5月25-26日&#xff0c;由广东省商务厅、中国国际贸易促进委员会广东省委员会&#xff08;广东国际商会&#xff09;、广州市商务局、番禺区人民政府、广东省交易控股集团有限公司/广东省公共资源交易中心指导&#xff0c;广州钻石交易中心&#xff08;简称广钻中心&#xff09…

C语言深度解析--指针

目录 指针 指针的定义&#xff1a; 指针的大小&#xff1a; 指针和指针类型 野指针 指针运算 指针-整数&#xff1a; 指针-指针&#xff1a; 指针的关系运算&#xff1a; 指针和数组 二级指针 指针数组 理解指针的第一步是在机器级上观察指针表示的内容。大多数现代…

第十六届全国大学生信息安全竞赛创新实践赛初赛部分WP AGCTF战队

持续两天的比赛&#xff0c;打的很累&#xff0c;web没有出太多的题&#xff0c;比赛被pwn师傅带飞了&#xff0c;希望下此加油&#xff0c;下边是此次比赛排名。 文章目录 MISC签到卡被加密的生产流量国粹调查问卷pyshell CRYPTO基于国密SM2算法的密钥密文分发可信度量Sign_i…