激光条纹中心线提取的方法有很多,其中,灰度重心法是比较简单易懂的一种,下文将从灰度重心法的原理、matlab代码两个方面进行介绍。
灰度重心法原理
我们遍历图像中的每一行,并认为每一行的灰度重心即为该行的光心;当然,可能存在有些行没有光心,即认为激光没有照到该行,或激光在此行不够强烈。
每一行灰度重心的计算公式如下:
上面这个公式中:i表示第几列,cols表示总的列数,表示这一行中第i列像素的灰度值。我们直接用摄像头拍到的图像是RGB三通道的,采用灰度重心法计算前最后先把这个三通道的彩色图像转为单通道的灰度图。
由于matlab中矩阵、数组等的下标都是从1开始的,故这个公式中i也是从1开始的。
显然,这个公式与物理中学过的重心计算公式是一样的。
灰度重心法的原理很简单,故不详细讨论,具体可参考这篇大佬写的博客:灰度重心法提取光条纹中心_灰度重心法提取激光条纹中心线-CSDN博客
matlab代码
代码的总体思路如下:读入原图像、高斯滤波、图像灰度化、灰度重心法提取激光条纹中心线,接下来我将逐一叙述四个步骤。
读入原图像
代码如下:
%输入原图,并显示
Laser_image = imread("D:\SRTP\Matlab\images\Laser_test.PNG");
figure(1); imshow(Laser_image);
使用matlab自带的函数读入图片:imread("......")
其中,"......"是图像的存储路径。
该函数的返回值是一个三维矩阵,大小为X×Y×Z,其中X指的是像素的总行数,Y指的是像素的总列数,Z指的是图像的通道数,一般为3,即RGB三通道。该矩阵中的每一个元素,即为图像中某一个像素的某一个通道的灰度值。
注意:这个灰度值是uint8型的,即无符号8位整数。
imshow用于显示这张图片,我们通过它来判断有没有读入成功。
我们取Laser_test.png这张图片具体操作一下,效果展示如下:
效果良好!
高斯滤波
代码如下:
%高斯滤波,并显示
Gaussian_image = imgaussfilt(Laser_image);
figure(2); imshow(Gaussian_image);
用matlab自带的函数进行高斯滤波即可:imgaussfilt("......")
其中,"......"是要滤波的图像矩阵,即第一步中读入的Laser_image
返回值是滤波后的图像矩阵,大小保持与Laser_image不变
效果如下:
我们选取用于测试的这张图片比较好,高斯滤波效果体现不大
图像灰度化
代码如下:
%图像灰度化,并显示
R = Gaussian_image(:,:,1);%取原图的R通道
G = Gaussian_image(:,:,2);%取原图的G通道
B = Gaussian_image(:,:,3);%取原图的B通道
[image_rows,image_cols] = size(Gaussian_image(:,:,1));%记录原图的行列数
gray_image = (uint8(zeros(image_rows,image_cols)));%定义一个一通道的、尺寸与原图相同的全零矩阵
for i1 = 1 : image_rows%按行遍历
for j1 = 1 : image_cols%按列遍历
gray_image(i1,j1) = 0.3 * R(i1,j1) + 0.59 * G(i1,j1) + 0.11 * B(i1,j1);
end
end
figure(3); imshow(gray_image);
其实,matlab是有自带直接得到灰度化图像的函数的,但我想自己设置灰度值的公式,就自己写了灰度化的代码,同时,这也有助于我们理解灰度化的原理。
简单来说,灰度化就是把三通道的彩色图像变成单通道,而这个单通道的灰度值可用一个定量关系由原先三通道的值计算得到。
注意:你所创建的灰度图像矩阵必须是uint8型的!!!因此要在代码相应的地方强制转换一下其类型(看上述代码第6行)!!!如果不强制转换,zeros直接生成的矩阵默认是double型的,这样用imshow函数显示图片时会出错!!!为什么出错呢?因为imshow函数显示double型矩阵时,其允许的值仅为0~1,也就是说,像素值≥1的像素,其值会被置1(即白色),像素值<1的元素,其像素值不变。
如下图所示:
因为我们测试用的这张图片,其中的像素要么是黑色,要么是比较亮的红色,所以灰度化后,如果没有转换为uint8型,由于上述imshow函数显示double型矩阵的问题,其会近似为一张二值化图像。
如果说这张图片尚不能把这个imshow函数显示double型矩阵的问题体现地很强烈的话,那我们用下面这张图片来说明,可以强烈地体现这个问题。
这是原图。
这是灰度化后,矩阵为double型而没有转为uint8型时用imshow显示出来的情况:
因此,一定要把灰度化后的图像转换为uint8型再用imshow显示,转换后就显示正常了,效果如下:
ok,图像灰度化部分就介绍到这里。
灰度重心法提取激光条纹中心线
代码如下:
%灰度重心法提取光条中心,并显示
Laser_center = zeros(image_rows,2);%定义一个空矩阵,用来装光条中心坐标
for i1 = 1 : image_rows%按行遍历
sum0 = 0;%用来存放分子部分
sum1 = 0;%用来存放分母部分
gray_max = max(gray_image(i1,:));%取每行的最大灰度值
if gray_max >= mean(mean(gray_image))%如果该行存在光条
for j1 = 1 : image_cols%按列遍历
sum0 = sum0 + double(j1 * double(gray_image(i1,j1)));%跟新分子部分
sum1 = sum1 + double(gray_image(i1,j1));%跟新分母部分
end
Laser_center(i1,2) = round(sum0/sum1);%记录这一行中心点的列坐标
Laser_center(i1,1) = i1;%记录这一行中心点的行坐标
else%如果该行不存在光条
Laser_center(i1,1) = i1;%记录这一行中心点的行坐标,列坐标仍为0
end
end
center_image = (uint8(zeros(image_rows,image_cols)));%定义一个一通道的、尺寸与原图相同的全零矩阵
for i1 = 1 : image_rows%按行遍历
if Laser_center(i1,2) ~= 0
center_image(i1,Laser_center(i1,2)) = 255;
end
end
figure(4); imshow(center_image);
具体就不解释了,代码里注释也很清楚了。
要注意的几点:
1. 代码倒数7行不是必要的部分,写它们是为了使结果可视化一下,如下图所示:
2. 真正的结果,即提取出来的中心线坐标,存放在矩阵Laser_center中
我们对行进行遍历、对每一行的所有列进行灰度重心的计算,矩阵Laser_centergon共有rows行,2列。rows即为读入的图像的像素的总行数,每行第一列的元素即为该行的行数,第二列元素为我们检测到的该列中心线点的列数。
如我们前文所言,matlab的下表都是从1开始的,但这个矩阵中第二列元素中有些为0,这就是因为我们认为该行不存在中心线点,这个存不存在的判断机制由自己定,代码里我把它写在了第7行,判断的依据是这一行最大的灰度值是否大于整个图像灰度值的平均值,即是否有足够亮的点。当然,这个标准自己定即可。
3. 算分子(代码第9行)的部分,注意要把uint8型的像素值转换为double再计算(这个与imshow的使用正好相反)!!!这是因为uint8只有8位,及允许的最大值仅为255,而列数✖灰度值显然是很容易超过255的,因为列数的范围很容易有好几百、灰度值的范围则是0—255。如果不转换而保持为uint8型,则算出来超过255的结果都会被置为255!!!则结果必然是错的!!!
4. round函数为matlab自带,作用是进行四舍五入,否则sum0/sum1很容易有小数,而显然像素坐标必是整数。
完整代码如下:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Date: 2024年9月7日
% Author: Tony
% Function: 灰度重心法提取激光条纹中心线
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
close all; clear; clc;
%输入原图,并显示
Laser_image = imread("D:\SRTP\Matlab\images\Laser_test.PNG");
figure(1); imshow(Laser_image);
%高斯滤波,并显示
Gaussian_image = imgaussfilt(Laser_image);
figure(2); imshow(Gaussian_image);
%图像灰度化,并显示
R = Gaussian_image(:,:,1);%取原图的R通道
G = Gaussian_image(:,:,2);%取原图的G通道
B = Gaussian_image(:,:,3);%取原图的B通道
[image_rows,image_cols] = size(Gaussian_image(:,:,1));%记录原图的行列数
gray_image = (uint8(zeros(image_rows,image_cols)));%定义一个一通道的、尺寸与原图相同的全零矩阵
for i1 = 1 : image_rows%按行遍历
for j1 = 1 : image_cols%按列遍历
gray_image(i1,j1) = 0.3 * R(i1,j1) + 0.59 * G(i1,j1) + 0.11 * B(i1,j1);
end
end
figure(3); imshow(gray_image);
%灰度重心法提取光条中心,并显示
Laser_center = zeros(image_rows,2);%定义一个空矩阵,用来装光条中心坐标
for i1 = 1 : image_rows%按行遍历
sum0 = 0;%用来存放分子部分
sum1 = 0;%用来存放分母部分
gray_max = max(gray_image(i1,:));%取每行的最大灰度值
if gray_max >= mean(mean(gray_image))%如果该行存在光条
for j1 = 1 : image_cols%按列遍历
sum0 = sum0 + double(j1 * double(gray_image(i1,j1)));%跟新分子部分
sum1 = sum1 + double(gray_image(i1,j1));%跟新分母部分
end
Laser_center(i1,2) = round(sum0/sum1);%记录这一行中心点的列坐标
Laser_center(i1,1) = i1;%记录这一行中心点的行坐标
else%如果该行不存在光条
Laser_center(i1,1) = i1;%记录这一行中心点的行坐标,列坐标仍为0
end
end
center_image = (uint8(zeros(image_rows,image_cols)));%定义一个一通道的、尺寸与原图相同的全零矩阵
for i1 = 1 : image_rows%按行遍历
if Laser_center(i1,2) ~= 0
center_image(i1,Laser_center(i1,2)) = 255;
end
end
figure(4); imshow(center_image);
所用的测试图片如下:
matlab代码部分、测试图片都是参考这篇大佬写的博客:
代码:灰度重心法求激光中心线(matlab)_重心法提取中心线-CSDN博客
最后,我只是个图像处理的初学者,本博客主要是为了分享出来供大家参考、同时记录一下自己的学习过程,算法仍有巨大改进空间,同时若有错误之处还请批评指正!
最后的最后,再感谢一下参考的博客: