基于matlab的快速定位限速标志牌算法

数学实验的综合实验设计,要求使用matlab建立模型和算法,能够快速从图片中取出含有限速标志的图片区域。并截取保存为图片文件。 要求算法能够自动提取红色圆形图框区域。

一,题目分析

要定位限速标志,首先需要明白标志的特征,一个典型的限速标志如下:

image-20230530000433573

该图像为一圈红色圆形区域中间包含黑色数字的形式组成,题目上要求能够自动提取红色圆形图框区域,那我们只需要想办法把这个圆弄出来。

那么基本思想如下:

  • 提取出图像中的红色像素区域
  • 找到这些像素区域中的圆
  • 根据圆的位置对原图进行截图
  • 对截取到的图片进行进一步判别

二,具体步骤

首先亮出例程图:

image-20230530003135117

图片的读取与转化

首先需要对图片进行读取和转化。

使用imread(file)读取图像,该函数会返回RGB图像数组:

1
image=imread('pic/include-80.png');

由于RGB图像不易处理,这里需先转换为HSV图像。

关于RGB,HSV,HSL:三分钟带你快速学习RGB、HSV和HSL颜色空间

使用rgb2hsv(img)进行转化,该函数会返回一个MxNx3的数组,对应图像的H,S,V分量。

1
2
3
4
hsvImg=rgb2hsv(image);%转换到HSV空间
h1=hsvImg(:,:,1);%H分量
s1=hsvImg(:,:,2);%S分量
v1=hsvImg(:,:,3);%V分量

之后,需要提取出其中的红色像素点。该步骤可以使用matlab数组运算和逻辑运算,判断HSV三个域中的每个像素点是否在红色像素点数值范围,是为1,否为0,最后将三个域的数组相与运算,就得到了只有红色像素位置信息图片,顺便还进行了图像的二值化

1
2
hsvReg1=((h1<=0.056&h1>=0)|(h1>=0.740&h1<=1.0)) & s1>=0.169& s1<=1.0  &v1>=0.180&v1<=1.0;%提取红色分量 
figure;imshow(hsvReg1);title('原图hsv检测图像');

得到的结果如下:

image-20230530003214590

图像的降噪处理

可以看到,此时图像已经可以十分清楚的看到圆环了。

之后需要对图像进行降噪处理消除图像中像素点的很少的无效区域,便于之后对图像进行处理。

使用bwareaopen(BW,P)删除二值化图像BW中像素量少于P的所有连通分量。可以将像素少于P的色块区域删除

1
2
hsvReg1=bwareaopen(hsvReg1,110);%110为去除像素点阙值
figure;imshow(hsvReg1);title('图像降噪结果');

其中P的值需要根据实际情况进行选择,太大可能导致较小的图像被直接删没了,太大图像就会有很多杂点

运行结果:

image-20230530004615534

可以看到少了很多杂点。

填充平滑图像

现在我们得到了包含各种色块的图像,由于我们使用圆度进行检测,因此需要让目标色块尽可能平滑饱满,跟圆长得更加接近,减少以下情况对识别的干扰:

image-20230530005827801

首先需要填充图像内部的小黑点。这里使用闭运算imclose(I,SE),使用结构元素 SE 对灰度或二值图像 I 执行形态学闭运算。形态学闭运算是先膨胀后腐蚀,这两种运算使用相同的结构元素。

这里补充一下关于图像开闭运算的知识:

  • 膨胀:求局部最大值的操作

  • 腐蚀:求局部最小值的操作

  • 开运算imopen():先腐蚀后膨胀称为开 (Open)

    局部的区域大小和形状由SE指定。原图经过开运算后,去除孤立的小点,毛刺和小桥(即连通两块区域的小点),而总的位置和形状不变。

  • 闭运算imclose():先膨胀后腐蚀称为闭 (Close)

    局部的区域大小和形状由SE指定。原图经过闭运算后,能够填平小湖(即小孔),弥合小缝隙,而总的位置和形状不变。

参考文章:开闭运算 imopen imclose

SE可由函数strel()生成,该函数可以生成很多类型的平面元素结构,我们只需要使用SE = strel("disk",r)盘型结构,其他结构可以参考官方文档

image-20230530011603919

对图像进行闭运算:

1
2
3
4
se=strel('disk',2);%半径为2
hsvReg1=imclose(hsvReg1,se);
hsvReg1=imfill(hsvReg1,'holes');
figure;imshow(hsvReg1);title('图像填充平滑后结果');

其中imfill(hsvReg1,'holes')填充了图像中的孔,即无法通过从图像边缘填充背景来到达的一组背景像素。结果是圆环变成圆。

image-20230530011913980

划分连通区域

该步骤将上图中的图块分别标记编号并求其边缘点。

使用函数[B,L]=bwboundaries(hsvReg1,'noholes'),该函数将图像上的连通域分别标记并记录边界点在cell类型的B中,在L中储存标记后的区域。

其中指定参数noholes将不会将hole作为新对象,而是将包括hole的整体作为一个整体。

1
2
3
4
5
6
7
[B,L]=bwboundaries(hsvReg1,'noholes');
figure;imshow(label2rgb(L,@jet,[.5 .5 .5]));%5
hold on;
for k=1:length(B)
boundary=B{k};
plot(boundary(:,2),boundary(:,1),'w','LineWidth',2);
end

处理结果:

image-20230530101400321

区分了每个连通域及其边界。

计算连通域特性

获得了不同连通域后,我们需要计算不同连通域的特性。

使用regionprops(BW,properties)进行计算,根据properties字符串计算标记好连通域的BW中不同连通域的不同特性,并返回结构体数组。

在该题中,我们依据图形的圆度判断是否为圆,因此我们需要求:

  • Centroid:区域的质心,对应圆心
  • Area:实际的像素数,对应面积
  • Circularity:圆度,由(4*Area *pi)/(Perimeter^2)计算,由于matlab2017没有,因此直接求Perimeter
  • Perimeter:围绕区域的边界,如果图像包含不连续区域,regionprops 将返回意外结果。(所以先填充平滑)
1
stats=regionprops(L,'Area','Centroid','Perimeter');

提取图形中的圆

在该步骤中,我们遍历每一个连通域,求其圆度,保持圆度大于阈值threshold的连通域的圆心,半径(由周长计算)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
threshold=0.88;%圆度的阈值

cents={};%圆心
rs=[];%周长
stats=regionprops(L,'Area','Centroid','Perimeter');
for k=1:length(B)
area=stats(k).Area;
perimeter=stats(k).Perimeter;
Circularity=4*pi*area/perimeter^2;%计算圆度
if Circularity>threshold
cents{end+1}=stats(k).Centroid;
r=perimeter/(2*pi);
rs(end+1)=r;
end
end

裁剪图像

获得圆心和半径后,我们可以计算出圆所在图像区域,使用imcrop(I,rect)进行裁剪。

rect是一个四元素数组,第一二个元素为矩形的左上角坐标,第三个元素是矩形的长,第四个是宽。

1
2
3
4
5
for i=1:length(cents)
rect=[cents{i}(1)-rs(i) cents{i}(2)-rs(i) 2*rs(i) 2*rs(i)];
result=imcrop(image,rect);
figure;imshow(result);title('图像裁剪结果');
end

结果如下:

image-20230530105244110

三,结论

本方法使用提取红色像素进行二值化,通过对二值化图像找圆的方式找到原图中的红色圆形区域,以定位限速牌。由于使用圆度进行判断,判断方法较为单一,当阈值过大,会导致变形的限速牌无法识别,当阈值过小,一些图像则会被误识别,因此还需要进行改进,以获得更加准确的方案。

目前,可以考虑对裁剪后的图像进行文字识别OCR,如果有限速的数字则可以更为准确的判断。

四,代码附录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
image=imread('pic/include-80.png');
% image=imread('pic/include-120-100.png');
% image=imread('pic/1.png');
threshold=0.95;%圆度的阈值
%======= HSV颜色分割图像 =======%
hsvImg=rgb2hsv(image);%转换到HSV空间
h1=hsvImg(:,:,1);%H分量
s1=hsvImg(:,:,2);%S分量
v1=hsvImg(:,:,3);%V分量
hsvReg1=((h1<=0.056&h1>=0)|(h1>=0.740&h1<=1.0)) & s1>=0.169& s1<=1.0 &v1>=0.180&v1<=1.0;%提取红色分量
% figure;imshow(hsvReg1);title('原图hsv检测图像');
%======= 降噪 =======%
hsvReg1=bwareaopen(hsvReg1,110);%110为去除像素点阙值
% figure;imshow(hsvReg1);title('图像降噪结果');
%======= 填充平滑图像 =======%
se=strel('disk',2);
hsvReg1=imclose(hsvReg1,se);
hsvReg1=imfill(hsvReg1,'holes');
% figure;imshow(hsvReg1);title('图像填充平滑后结果');
%======= 划分连通域 =======%
[B,L]=bwboundaries(hsvReg1,'noholes');
% figure;imshow(label2rgb(L,@jet,[.5 .5 .5]));%5
% hold on;
% for k=1:length(B)
% boundary=B{k};
% plot(boundary(:,2),boundary(:,1),'w','LineWidth',2);
% end
%======= 计算连通域特性 =======%
cents={};%圆心
rs=[];%周长
stats=regionprops(L,'Area','Centroid','Perimeter');
for k=1:length(B)
area=stats(k).Area;
perimeter=stats(k).Perimeter;
Circularity=4*pi*area/perimeter^2;%计算圆度
if Circularity>threshold
cents{end+1}=stats(k).Centroid;
r=perimeter/(2*pi);
rs(end+1)=r;
end
end

%======= 剪切保存图像 =======%
for i=1:length(cents)
rect=[cents{i}(1)-rs(i) cents{i}(2)-rs(i) 2*rs(i) 2*rs(i)];
result=imcrop(image,rect);
figure;imshow(result);title('图像裁剪结果');
end