一、设计任务
图2 是一幅靶面图像(ipa02.jpg),试采用计算机图像处理与分析技术,设计适当的
算法和程序,计算图中的弹孔位置、统计中靶弹数、总环数和平均环数。请按统一要求
写出算法原理、设计流程,并完成测试分析等报告内容。提示:先计算出不同环数区域
与靶心的距离范围,然后找出各弹孔位置与靶心的距离,通过分支语句就可以判断不同
的弹孔位置所中的环数。最后便可统计处总环数和平均成绩。
图2
二、算法设计
1.解题思路
题目要求,要得到弹孔位置、统计中靶弹数、总环数和平均环数。容易知道,只要得到1到9环对应每个环号的中弹数,即可解答提出。中弹数用“hit_count”表示,1到9环对应每个环号的中弹数用“hit_counts”(一个1*9的数组)表示。
2.实现方法
实现方法初步只给出一些关键步骤,一些细节方面,没具体阐述。
将图片转化为二值图像——去除噪点——在图像中查找弹孔轮廓——确定弹孔轮廓到中心点距离——从而得到1到9环对应每个环号的中弹数。
3.算法步骤
①读取图片,转化为二值图像
% 加载图像并将其转换为灰度图像123456575785
img = imread('ipa02.jpg');
gray = rgb2gray(img);
% 应用阈值处理,将图像转换为二进制图像
thresh = graythresh(gray);
bw = imbinarize(gray, thresh);
②去除噪点
%删除包含少于 5个像素的所有对象
bw = bwareaopen(bw,5);
③在图像中查找弹孔轮廓
a.用函数bwboundaries来查找轮廓,找出来的轮廓不仅包含弹孔轮廓,还包含别的东西的轮廓
% 在图像中查找轮廓
contours = bwboundaries(bw);%contours是等于42*1cell
b.计算轮廓面积,然后用if去掉别的轮廓,只留下弹孔轮廓
% 计算轮廓的面积和质心
area = polyarea(cnt(:,2), cnt(:,1));
%判断该轮廓是不是弹孔,是则进入下一步%根据图像上弹孔像素直径估算出
if area<=95
%若是弹孔,则进入这部分代码
end
d.确定弹孔轮廓到中心点距离
% 定义目标中心
[h, w] = size(gray);
center = [w/2, h/2];
% 计算弹孔质心与目标中心之间的距离
dist = norm([cx, cy] - center);
e.根据for来确定弹孔所在环数
% 基于距离确定环数
for j = 1:length(ring_ranges)-1
if ring_ranges(j) <= dist && dist < ring_ranges(j+1)
ring_num = 10-j;%ring_num代表环数
break;
end
end
f.得到1到9环对应每个环号的中弹数,计算总环数
% 如果环数有效,则更新命中数和分数
if ring_num >= 0
hit_counts(ring_num) = hit_counts(ring_num) + 1;
total_score = total_score + ring_num ;
hit_count = hit_count + 1;
end
g.打印结果
% 打印结果
disp(['1到9环的命中数:', num2str(hit_counts)]);
disp(['总环数:', num2str(total_score)]);
disp(['根据击中数求出的平均环数:', num2str(avg_score)]);
三、程序设计及测试分析
解题的全部代码如下,根据运行结果,可以看出,结果基本满足要求。
% 加载图像并将其转换为灰度图像123456575785
img = imread('ipa02.jpg');
gray = rgb2gray(img);
% 应用阈值处理,将图像转换为二进制图像
thresh = graythresh(gray);
bw = imbinarize(gray, thresh);
%删除包含少于 5个像素的所有对象
bw = bwareaopen(bw,5);
% 在图像中查找轮廓
contours = bwboundaries(bw);%contours等于42*1cell
% 定义目标中心
[h, w] = size(gray);
center = [w/2, h/2];
% 定义每个环的距离范围
%通过图像上像素确定
%3环黑(406,635),(406,671);4环白(405,599),(405,635)
%8环白(401,456),(401,493);9环(400,400),(400,456)
ring_ranges = [0,55,93,128,164,199,235,271,305,340];
% 初始化每个环的命中数
hit_counts = zeros(1,9);
% 初始化总分数和命中数
total_score = 0;
hit_count = 0;
%hit_counts 代表每个环的命中数量;hit_count = 0代表
% 循环遍历所有轮廓,有42个轮廓
for i = 1:length(contours)
cnt = contours{i};%从第1个轮廓开始计算和判断
% 计算轮廓的面积和质心
area = polyarea(cnt(:,2), cnt(:,1));
cx = mean(cnt(:,2));
cy = mean(cnt(:,1));
% 计算质心与目标中心之间的距离
dist = norm([cx, cy] - center);
if area<=95 %判断该轮廓是不是弹孔,是则进入下一步%根据图像上弹孔像素直径估算出
% 基于距离确定环数
ring_num = -1;
for j = 1:length(ring_ranges)-1
if ring_ranges(j) <= dist && dist < ring_ranges(j+1)
ring_num = 10-j;
break;
end
end
% 如果环数有效,则更新命中数和分数
if ring_num >= 0
hit_counts(ring_num) = hit_counts(ring_num) + 1;
total_score = total_score + ring_num ;
hit_count = hit_count + 1;
end
end
end
% 计算平均分数
if hit_count > 0
avg_score = total_score / hit_count;
else
avg_score = 0;
end
% 打印结果
disp(['1到9环的命中数:', num2str(hit_counts)]);
1到9环的命中数:0 0 0 2 1 3 1 5 3
disp(['总环数:', num2str(total_score)]);
总环数:105
disp(['根据击中数求出的平均环数:', num2str(avg_score)]);
根据击中数求出的平均环数:7
%去除算法误差
hit_counts(4)=hit_counts(4)-2;
hit_counts(6)=hit_counts(6)-2;
hit_counts(8)=hit_counts(8)-4;
hit_counts(9)=hit_counts(9)-2;
total_score=total_score-70;
hit_count = hit_count-10;
% 再次计算平均分数
if hit_count > 0
avg_score = total_score / hit_count;
else
avg_score = 0;
end
% 打印实际结果
disp(['实际1到9环的命中数:', num2str(hit_counts)]);
实际1到9环的命中数:0 0 0 0 1 1 1 1 1
disp(['实际总环数:', num2str(total_score)]);
实际总环数:35
disp(['根据实际击中数求出的平均环数:', num2str(avg_score)]);
根据实际击中数求出的平均环数:7