本文是基于给定的两张多线激光图片,如下图所示。需将图片中的激光线的中心线坐标提取出来并绘制激光中心线图形。因为是Matlab课程训练研究大作业,所以全文代码为Matlab,希望可以为相似作业的非专业同学提供一些帮助。
1.问题分析
由于是给定图片,数量不是很多,因此可以对每个图像单独分析,这也是本篇文章的局限性。在拿到图像时,如果懂ps的,可以先调大整体图像的亮度,可以看到一些原本图像显示并不清楚的地方,例如在图二中,原图中右下部分是没有激光线的,但是在调亮后确有,这是在交大作业的前一天晚上发现的,因此大家都没有再改的了。
第一张图中,其线条相对简单,激光线基本垂直于水平线,没有较大的噪点,因此可以先二值化处理图形,再利用重心法提取中心线,可以实现像素级提取激光中心线。
第二张图中,可以发现激光线有较大的倾斜角,且亮度不一,并有较大的光斑对图像造成干扰,因此提取较第一张图更加复杂一些。可以先对图像进行区域二值化处理,再去除光斑的影响,最后提取激光条纹中心线。
2.研究思路及关键代码
2.1图片一部分
2.1.1二值化处理
遍历图一每个像素点,经过多次调试后设定二值化的阈值为60,当图片的灰度值小于60是置为0,否则置为255,得到处理后的图。
for i=1:a
for j=1:b
if I(i,j)<60
I(i,j)=0;
else
I(i,j)=255;
end
end
end
2.1.2 重心法提取中心线
从图中任取出一条水平线,读取其处理后的灰度值如下图。
可以发现,每个峰值区即为激光线,因此提取每段峰值区的中点即可提取其中心线,再遍历每条水平线即可。
w=1; %字母含义为某排识别出的中心点横坐标的序号
for i=1:a
for j=1:b
if I(i,j)==255 %识别出某条激光线的第一个位置坐标
k=j;
while I(i,k)==255 %判断该条激光线的最后一个位置坐标
k=k+1;
if k>=b;
break;
end
end
s(w)=ceil((k-1+j)/2); %通过首尾位置计算中心点坐标存入数组中
w=w+1;
end
I(i,j)=0; %抹除图像所有亮点
end
I(i,s)=255; %赋给中心点255
w=1;
s=0;
end
2.1.3 图像叠加
再与原图叠加即可验证其提取效果。由于需要用彩色图来区分,因此需把灰度图转化成RGB图像,灰度图是一个0-255的数来显示的,而RGB图是(R,G,B)三个0-255的数来描述,因此涉及到图像的转化部分。代码和最终图像如下。
for i=1:a
for j=1:b
q=I0(i,j); %读取原图
II(i,j,1)=q;
II(i,j,2)=q;
II(i,j,3)=q;
if I(i,j)==255 %读取提取中心线的图
II(i,j-2,1)=255; %中心线显示为红色
II(i,j-2,2)=0;
II(i,j-2,3)=0;
end
end
end
2.2 图像二部分
2.2.1 分区二值化处理
可以明显观察到左上角的条纹的亮度较小,且有斜向右下的亮宽条纹对激光条纹有较大的影响。因此,对图像进行分区二值化以得到更好的预处理效果。
利用四条直线将上图分成5个部分。
对于1号和3号部分,其激光条纹亮度差异不大,因此可以设置较大的阈值,设定像素的灰度值小于160时置为0,否则置为255。
对于2号和5号部分,其中激光条纹亮度差异较大,应设置较小的阈值。设定像素的灰度值小于45时置为0,否则置为255。
对于4号部分,由于其干扰线亮度大,激光条纹线亮度也大,因此设定阈值应更大以滤去干扰线部分。设定像素的灰度值小于230时置为0,否则置为255。
处理后的图如下。
2.2.2 光斑处理
对图像进行膨胀腐蚀处理,可以提取图中的圆。
se=strel('disk',8); %设置圆半径
fc=imerode(I,se); %图像腐蚀
fc=imdilate(fc,se); %图像膨胀
将上图反值后,与原图像叠加,得到去除圆的图像,再经过降噪处理,得到待提取中心线的图像。
I=bwareaopen(I,10); %降噪处理
将之前提取到的圆的图像与上图进行相交判断处理,识别圆是否与直线相交。首先使用canny算子提取圆的边缘,再判断每个边缘像素点周围5个点的位置是否于待提取直线相交。在找到相交边缘后,再把对应圆找到。以每个圆内点为圆心,50为半径,判断其是否与边缘点相交,得到相交边缘所在圆。最后再将其与待提取中心线的图像部分叠加,得到最后的待提取中心线部分。
fc1=edge(fc,'canny'); %用Canny算子进行边缘检测
for i=1:a %检测边缘是否与直线相交
for j=1:b-5
if fc1(i,j)==1
s=0;
for m=1:5
if I(i-m,j-m)==1||I(i+m,j-m)==1||I(i-m,j+m)==1||I(i+m,j+m)==1
s=1;
end
end
if s==0
fc1(i,j)=0;
end
end
end
end
for i=1:a %找到相交边缘圆
for j=1:b
if fc(i,j)==255
s=0;
radius=50;
for m=max(1,i-radius):min(a,i+radius)
for n=max(1,j-radius):min(b,j+radius)
if ((m-i)^2+(n-j)^2<=radius^2)&&(fc1(m,n)==1)
s=1;
break;
end
end
end
if s==0
fc(i,j)=0;
end
end
end
end
for i=1:a %合并相交圆与待提取中心线
for j=1:b
if fc(i,j)==255
I(i,j)=255;
end
end
end
2.2.3 提取中心线
采用bwmorph函数,提取激光条纹的骨架线作为其中心线。
I = bwmorph(I,'thin',100);
再将其与原图叠加,得到最终图。
3.总结及整体代码
3.1 总结
在对图片一进行处理时,二分阈值的选取可能不适用于部分区域,导致提取出来的中心线有部分偏移。当然也可以不预先进行二值化,从而采用基于灰度的重心法,对于噪声小的图像可能适用,但是噪声过大就会提取出噪声了。
在对第二张图片处理时,和第一张图相比,最大的不同便是整个图像倾斜、激光线明暗变化较大,且有明显的噪点,给二值化处理带来困难,分区二值化相当于是一个妥协,也是不能通用的阻碍。在查找资料时,其实还有更专业的处理方法,opencv已提供有现成可用的api,但是本文要求为Matlab代码,因此仅仅为同为需求的同学提供一些帮助,也不失为应付作业的一种方法罢。
3.2 整体代码
3.2.1 图片一
clc ; clear all;
I = imread('图片一.bmp');
[a,b]=size(I);
figure(1);
imshow(I);
title('原图');
I0=I;%保存一份原图,用于后面叠加显示
%******二值化图像**********
for i=1:a
for j=1:b
if I(i,j)<60
I(i,j)=0;
else
I(i,j)=255;
end
end
end
figure(2);
imshow(I);
title('二值化的图');
imwrite(I,'二值化的图.tif');
%******重心法提取中心线**********
q=100;
while q>0
w=1;
for i=1:a
for j=1:b
if I(i,j)==255
k=j;
while I(i,k)==255
k=k+1;
if k>=b;
break;
end
end
s(w)=ceil((k-1+j)/2);
w=w+1;
end
I(i,j)=0;
end
I(i,s)=255;
w=1;
s=0;
end
q=q-1;
end
for i=1:a %中心线与原图叠加
for j=1:b
q=I0(i,j);
II(i,j,1)=q;
II(i,j,2)=q;
II(i,j,3)=q;
if I(i,j)==255
II(i,j-2,1)=255;
II(i,j-2,2)=0;
II(i,j-2,3)=0;
end
end
end
figure(3);
imshow(II);
title('最终图1');
imwrite(II,'最终图1.tif');
3.2.2 图片二
clc ; clear all;
I = imread('图片二.bmp');
[a,b]=size(I);
figure(1);
imshow(I);
title('原图');
pif=I;
%******分区二值化图像**********
for i=1:a
for j=1:b
if(((i/320+j/128)>1)&&((i/800+j/400<1))||((i/1900+j/1100>1)))%四条直线分区
if I(i,j)<45
I(i,j)=0;
else
I(i,j)=255;
end
else if ((i/800+j/400>1))&&((i/1900+j/1100<1)&&(3*i-5*j>200))
if I(i,j)<230
I(i,j)=0;
else
I(i,j)=255;
end
else
if I(i,j)<160 %设置不同的阈值过滤图像
I(i,j)=0;
else
I(i,j)=255;
end
end
end
end
end
figure(2);
imshow(I);
title('二值化的图');
imwrite(I,'二值化的图.bmp');
%******处理图中的圆形噪点**********
se=strel('disk',8);
fc=imerode(I,se); %图像腐蚀
fc=imdilate(fc,se); %图像膨胀
figure(3);
imshow(fc);
title('原图处理后的圆点图');
imwrite(fc,'原图处理后的圆点图.bmp');
for i=1:a
for j=1:b
if fc(i,j)==255
I(i,j)=0; %将白色圆形置为黑色
end
end
end
figure(4);
imshow(I);
title('去除圆形的图');
imwrite(I,'去除圆形的图.bmp');
%******去除图像中的剩余噪点**********
I=bwareaopen(I,10);
figure(5);
imshow(I);
title('去噪图');
imwrite(I,'去噪图.bmp');
%******检测与直线相交的圆边缘**********
fc1=edge(fc,'canny'); %用Canny算子进行边缘检测
figure(6);
imshow(fc1);
title('圆边缘图');
imwrite(fc1,'圆边缘图.bmp');
%检测边缘是否与直线相交
for i=1:a
for j=1:b-5
if fc1(i,j)==1
s=0;
for m=1:5
if I(i-m,j-m)==1||I(i+m,j-m)==1||I(i-m,j+m)==1||I(i+m,j+m)==1
s=1;
end
end
if s==0
fc1(i,j)=0;
end
end
end
end
figure(7);
imshow(fc1);
title('相交圆边缘');
imwrite(fc1,'相交圆边缘图.bmp');
%******找到相交边缘圆**********
for i=1:a
for j=1:b
if fc(i,j)==255
s=0;
radius=50;
for m=max(1,i-radius):min(a,i+radius)
for n=max(1,j-radius):min(b,j+radius)
if ((m-i)^2+(n-j)^2<=radius^2)&&(fc1(m,n)==1)
s=1;
break;
end
end
end
if s==0
fc(i,j)=0;
end
end
end
end
figure(8);
imshow(fc);
title('相交圆');
imwrite(fc,'相交圆图.bmp');
%******合并相交圆与线**********
for i=1:a
for j=1:b
if fc(i,j)==255
I(i,j)=255;
end
end
end
figure(9);
imshow(I);
title('需要找中心线的部分');
imwrite(I,'需要找中心线的部分.bmp');
%******提取中心线**********
I = bwmorph(I,'thin',100);%生成的I为逻辑数
figure(10);
imshow(I);
title('提取的中心线');
imwrite(I,'提取的中心线.bmp');
%******合并中心线与原图,中心线置红**********
for i=1:a
for j=1:b
q=pif(i,j);
pifs(i,j,1)=q;
pifs(i,j,2)=q;
pifs(i,j,3)=q;
if I(i,j)==1
pifs(i,j,1)=255;
pifs(i,j,2)=0;
pifs(i,j,3)=0;
end
end
end
figure(11);
imshow(pifs);
title('最终图');
imwrite(pifs,'最终图.bmp');