【微实验】激光测径系列(一)

一、单缝和细丝的类比

昨天关于夫琅禾费衍射的文章——https://blog.csdn.net/Harrytutu_ZuiYue/article/details/151366978

课程要求是通过激光测径,根据实验的器械要求,基本上是使用衍射。氦氖激光器发出平行光,然后通过细丝衍射。

细丝和狭缝的夫琅禾费衍射,本质是互补屏衍射,电场分布满足巴比涅原理。

要对比单缝和细丝夫琅禾费衍射的电场强度分布,需从惠更斯 - 菲涅耳原理出发:单缝衍射是 “开孔” 的子波相干叠加,细丝衍射是 “障碍物” 的子波相干叠加。

让我的坏朋友AI写了个代码来对比,然后就出现了以下令人困惑的结果。

???也就这个看起来正确一些。

这个稀奇古怪的问题,数学物理和编程都非常差的扒谱机决定不再深究()

二、MATLAB图像处理

通过手动把不同直径的铁丝固定到氦氖激光器的激光束内,然后使用白屏和毛玻璃进行成像,我们拍摄了不同直径的铁丝和光纤的衍射图样,如下图所示——

衍射图样的延展方向与细丝的方向垂直

为避免过曝,曝光时长调得较低,光圈也开得较小。令AI生成代码,手动绘制一条直线,与衍射图样重合,然后读取这条线上的光强(图像灰度)信息。

代码一:

% 读取灰度图像
img = imread('test5.bmp');

% 确保图像为灰度图
if size(img, 3) == 3
    img = rgb2gray(img);
end

% 显示原始图像
figure('Name', '原始灰度图像');
imshow(img);
title('请在图像上点击并拖动绘制一条横线,完成后双击确认');

% 允许用户绘制一条横线
h = imline;
wait(h); % 等待用户完成绘制

% 获取线条的端点坐标
endpoints = getPosition(h);
x1 = endpoints(1, 1);
y1 = endpoints(1, 2);
x2 = endpoints(2, 1);
y2 = endpoints(2, 2);

% 确保是水平线(取平均y坐标)
y = mean([y1, y2]);
y = round(y); % 转换为整数像素坐标

% 确定x坐标范围
x_start = round(min(x1, x2));
x_end = round(max(x1, x2));
x = x_start:x_end;

% 提取横线上的灰度值
gray_values = img(y, x);

% 显示灰度值二维图像(线图形式)
figure('Name', '横线上的灰度值分布');
plot(x, gray_values, 'b-', 'LineWidth', 1.5);
xlabel('x坐标 (像素)');
ylabel('灰度值');
title(['y = ', num2str(y), ' 处的灰度值分布']);
grid on;

% 显示灰度值二维图像(图像形式)
figure('Name', '横线上的灰度值图像');
imagesc(gray_values);
colormap(gray);
xlabel('x坐标 (像素)');
ylabel('单一水平线');
title(['y = ', num2str(y), ' 处的灰度值图像']);
axis tight;
axis xy; % 使y轴向上为正方向

效果如下——

但是,还是不出所料的,水灵灵地过曝了……

似乎这种比较细小的更容易观察到?


怎么说呢,感觉确实要好一些,但是数据准确与否就另说了。

在画到非常倾斜的直线时,我发现了一个问题……那就是,这个取值根本就是不倾斜的!AI写的代码是默认横线的(果然我还是过于信任AI写的代码了)

于是——

代码二:

% 读取图像并确保为灰度图
img = imread('test.png');
if size(img, 3) == 3
    img = rgb2gray(img);
end
img = im2double(img);  % 转换为双精度,便于插值计算

% 显示图像并让用户选择倾斜直线的两个端点
figure; imshow(imcomplement(img));%这里反色显示,为了能够更加清楚地看清所选择点的标识
title('请点击直线的两个端点(倾斜条纹方向)');
[x, y] = ginput(2);  % 获取两个端点坐标(x为列,y为行)
x1 = x(1); y1 = y(1);
x2 = x(2); y2 = y(2);

% 计算直线上的采样点(确保覆盖所有像素)
line_length = sqrt((x2 - x1)^2 + (y2 - y1)^2);  % 直线长度(像素)
num_points = round(line_length);  % 采样点数(约等于直线长度,保证密度)
t = linspace(0, 1, num_points);  % 参数t从0到1
x_line = x1 + t * (x2 - x1);  % 直线上的x坐标(列)
y_line = y1 + t * (y2 - y1);  % 直线上的y坐标(行)

% 双线性插值计算灰度值(核心步骤)
% 注意:MATLAB中图像坐标是(行,列),对应(y,x)
gray_values = zeros(1, num_points);
for i = 1:num_points
    x = x_line(i);  % 当前点x坐标(列)
    y = y_line(i);  % 当前点y坐标(行)
    
    % 找到周围四个整数像素
    x_floor = floor(x);
    x_ceil = x_floor + 1;
    y_floor = floor(y);
    y_ceil = y_floor + 1;
    
    % 确保坐标在图像范围内
    x_floor = max(1, min(size(img, 2), x_floor));
    x_ceil = max(1, min(size(img, 2), x_ceil));
    y_floor = max(1, min(size(img, 1), y_floor));
    y_ceil = max(1, min(size(img, 1), y_ceil));
    
    % 双线性插值公式
    dx = x - x_floor;
    dy = y - y_floor;
    f1 = img(y_floor, x_floor) * (1 - dx) + img(y_floor, x_ceil) * dx;
    f2 = img(y_ceil, x_floor) * (1 - dx) + img(y_ceil, x_ceil) * dx;
    gray_values(i) = f1 * (1 - dy) + f2 * dy;
end

% 可视化结果
figure;
subplot(2,1,1);
imshow(img); hold on;
plot([x1, x2], [y1, y2], 'r-', 'LineWidth', 2);  % 绘制选中的倾斜直线
title('原始图像与选中的倾斜直线');

subplot(2,1,2);
plot(1:num_points, gray_values, 'b-', 'LineWidth', 1.5);
xlabel('沿直线的采样点序号');
ylabel('灰度值');
title('倾斜直线上的灰度分布');
grid on;

诶,这样才对劲。

关键步骤解析
  1. 直线采样:通过参数 t 均匀生成直线上的点,采样点数等于直线长度(像素),保证每个像素至少被采样一次。
  2. 双线性插值:对于任意非整数坐标(x,y),通过其周围四个整数像素 (x_{floor},y_{floor})、(x_{ceil},y_{floor})、(x_{floor},y_{ceil})、(x_{ceil},y_{ceil}) 的灰度值,加权计算目标点的灰度值,公式为:f(x,y) = (1 - dx)(1 - dy)f(x_{floor},y_{floor}) + dx(1 - dy)f(x_{ceil},y_{floor}) + (1 - dx)dyf(x_{floor},y_{ceil}) + dxdyf(x_{ceil},y_{ceil}),其中 dx = x - x_{floor},dy = y - y_{floor} 为小数部分。
  3. 坐标范围控制:确保采样点不超出图像边界,避免索引错误。
效果与应用
  • 该方法可准确提取任意倾斜角度直线上的灰度值,适用于非水平 / 垂直的衍射条纹;
  • 双线性插值能有效减少因像素离散化导致的误差,保证灰度分布的连续性;
  • 后续可基于提取的灰度值,用之前提到的 “暗纹定位法” 计算倾斜条纹的间距(需注意将像素间距转换为物理距离时,考虑直线的倾斜角度)。

三、自动化识别提取中心:

在能够识别非水平的条纹之后,我希望能够通过某种方式自动识别而不是手动框选。

手动绘制直线法存在一个严重的问题——若绘制不当,一边比较贴近中心线,而另一边略有偏离,就会产生类似这种结果:

于是就有了这个代码:

% 读取图像并转换为灰度图
img = imread('test.png');
if size(img, 3) == 3
    img = rgb2gray(img);
end
img = im2double(img);
% 1. 预处理:高斯模糊去噪
 sigma = 250;
 h = fspecial('gaussian', [50 50], sigma);
 img_blur = imfilter(img, h, 'replicate');
%img_blur = imgaussfilt(img, 1.2);  % 替代fspecial+imfilter,速度更快
img=img_blur;
% 2. 条纹增强:突出周期性结构
% 用拉普拉斯算子增强边缘(条纹处灰度变化剧烈)
laplacian = fspecial('laplacian', 0.2);
img_edge = imfilter(img_blur, laplacian);
img_enhance = img_blur - 0.5*abs(img_edge);  % 增强条纹对比度
% 3. 阈值分割:提取条纹候选区域
level = graythresh(img_enhance);  % 自动计算阈值
bw = imbinarize(img_enhance, level);  % 二值化
bw = bwareaopen(bw, 50);  % 去除小噪声区域
% 4. 霍夫变换检测条纹方向(获取主方向角度)
[H, theta, rho] = hough(bw, 'RhoResolution', 1, 'ThetaResolution', 0.5);
P = houghpeaks(H, 5);  % 取前5个最强峰值
lines = houghlines(bw, theta, rho, P, 'FillGap', 20, 'MinLength', 50);
% 筛选最长的条纹直线(主条纹)
max_len = 0;
for k = 1:length(lines)
    len = norm(lines(k).point1 - lines(k).point2);
    if len > max_len
        max_len = len;
        best_line = lines(k);  % 最佳条纹直线
    end
end
% 5. 提取条纹中心线上的点(直线端点)
p1 = best_line.point1;  % 起点 [x1, y1]
p2 = best_line.point2;  % 终点 [x2, y2]
% 6. 计算直线上的灰度值(双线性插值)
line_length = round(norm(p1 - p2));  % 直线长度(像素)
t = linspace(0, 1, line_length);
x_line = p1(1) + t*(p2(1) - p1(1));  % x坐标(列)
y_line = p1(2) + t*(p2(2) - p1(2));  % y坐标(行)

% 双线性插值获取灰度值
gray_values = zeros(1, line_length);
for i = 1:line_length
    x = x_line(i); y = y_line(i);
    x_floor = max(1, min(floor(x), size(img, 2)));
    x_ceil = min(x_floor + 1, size(img, 2));
    y_floor = max(1, min(floor(y), size(img, 1)));
    y_ceil = min(y_floor + 1, size(img, 1));
    
    dx = x - x_floor; dy = y - y_floor;
    f1 = img_blur(y_floor, x_floor)*(1-dx) + img_blur(y_floor, x_ceil)*dx;
    f2 = img_blur(y_ceil, x_floor)*(1-dx) + img_blur(y_ceil, x_ceil)*dx;
    gray_values(i) = f1*(1-dy) + f2*dy;
end

% 可视化结果
figure;
subplot(2,1,1);
imshow(img); hold on;
plot([p1(1), p2(1)], [p1(2), p2(2)], 'r-', 'LineWidth', 2);
title('原始图像与自动识别的条纹中心线');

subplot(2,1,2);
plot(1:line_length, gray_values, 'b-', 'LineWidth', 1.5);
xlabel('沿条纹中心线的采样点');
ylabel('灰度值');
title('条纹中心线上的灰度分布');
grid on;

这段代码为无交互的自动条纹灰度提取模块,通过图像预处理、条纹识别、中心线检测及灰度计算,实现衍射条纹中心线灰度分布的自动化提取,无需人工干预,适用于批量处理或无明确条纹方向的场景。

一)图像预处理:去噪与条纹增强

1.1 图像读取与格式转换

  • 读取目标图像(test.png),若为彩色图像,通过rgb2gray转换为二维灰度图;
  • im2double将灰度图转换为双精度格式,避免后续插值、滤波等计算过程中的精度损失。

1.2 高斯滤波去噪

  • 定义尺寸为50×50、标准差sigma=250的高斯卷积核(fspecial('gaussian', [50 50], sigma)),通过imfilter以 “边缘复制”('replicate')方式对图像滤波;
  • 滤波后图像赋值给img_blur(并覆盖原变量img),核心作用是抑制随机噪声,同时保留条纹的整体周期性结构,避免噪声干扰后续条纹识别。

1.3 条纹对比度增强

  • 生成拉普拉斯算子(fspecial('laplacian', 0.2)),对模糊后的图像进行边缘检测 —— 条纹区域因灰度突变明显,边缘信号更强,可通过边缘突出条纹轮廓;
  • 采用 “模糊图像 - 0.5× 边缘绝对值” 的运算(img_enhance = img_blur - 0.5*abs(img_edge)),压缩背景灰度范围、强化条纹与背景的差异,让弱条纹更易被后续阈值分割识别。

二)条纹候选区域提取:阈值分割与噪声过滤

2.1 自动阈值分割

  • graythresh函数基于图像灰度直方图的 “最大类间方差法”,自动计算二值化阈值(level),该阈值能最大化条纹与背景的灰度区分度;
  • 通过imbinarize(img_enhance, level)将增强后的图像转换为黑白二值图(bw),其中条纹区域为白色(灰度 1),背景区域为黑色(灰度 0)。

2.2 小噪声区域去除

  • 调用bwareaopen(bw, 50)删除二值图中面积小于 50 像素的连通区域,这些区域多为滤波后残留的噪声点,而非连续的条纹结构,去除后可减少后续霍夫变换的干扰。

三)霍夫变换:自动检测条纹中心线

3.1 霍夫变换找直线

  • 对处理后的二值图(bw)执行霍夫变换(hough),设置参数:距离分辨率RhoResolution=1(像素)、角度分辨率ThetaResolution=0.5(度),生成霍夫空间矩阵H(矩阵值越大,对应直线的可能性越高);
  • houghpeaks(H, 5)提取霍夫空间中前 5 个最强峰值,这些峰值对应图像中最明显的 5 条直线(候选条纹);
  • 通过houghlines(bw, theta, rho, P, 'FillGap', 20, 'MinLength', 50)将峰值转换为图像中的直线参数:'FillGap=20'表示允许将间距≤20 像素的断裂线段连接为一条直线,'MinLength=50'表示仅保留长度≥50 像素的直线(排除短噪声线段)。

3.2 筛选主条纹直线

  • 遍历所有检测到的直线,通过norm(lines(k).point1 - lines(k).point2)计算每条直线的长度(像素);
  • 筛选出长度最长的直线作为 “主条纹中心线”(best_line),并记录其两个端点坐标(p1为起点[x1,y1]p2为终点[x2,y2])—— 最长直线通常对应最清晰、最完整的主条纹,确保后续灰度提取的有效性。

四)灰度提取:双线性插值计算

4.1 直线均匀采样

  • 计算主条纹中心线的长度(line_length = round(norm(p1 - p2))),以 “采样点数≈直线长度” 为原则,通过t = linspace(0, 1, line_length)生成均匀分布的参数t(0≤t≤1);
  • 基于参数t生成直线上所有采样点的坐标:x_line = p1(1) + t*(p2(1)-p1(1))(列坐标,对应图像 x 轴)、y_line = p1(2) + t*(p2(2)-p1(2))(行坐标,对应图像 y 轴),确保采样密度一致,覆盖整条中心线。

4.2 双线性插值计算灰度

  • 坐标边界控制:对每个采样点(x,y),先计算其周围 4 个整数像素的坐标(x_floor=floor(x)x_ceil=x_floor+1y_floor=floor(y)y_ceil=y_floor+1),再通过max(1, min(..., size(img,2)))max(1, min(..., size(img,1)))限制坐标在图像范围内,避免索引越界错误;
  • 灰度加权计算
    1. 计算采样点与周围 4 个像素的距离权重(dx = x - x_floordy = y - y_floor,取值 0~1,距离越近权重越大);
    2. 横向插值:分别计算y_floor行(f1 = img_blur(y_floor,x_floor)*(1-dx) + img_blur(y_floor,x_ceil)*dx)和y_ceil行(f2 = img_blur(y_ceil,x_floor)*(1-dx) + img_blur(y_ceil,x_ceil)*dx)的灰度值;
    3. 纵向插值:通过gray_values(i) = f1*(1-dy) + f2*dy得到采样点最终灰度,解决非整数坐标的灰度离散问题,保证灰度分布连续。

五)结果可视化

  • 子图1:显示高斯滤波后的图像(img),叠加绘制红色主条纹中心线(plot([p1(1),p2(1)], [p1(2),p2(2)], 'r-', 'LineWidth',2)),直观展示中心线在原始图像中的位置,便于验证是否贴合条纹;
  • 子图2:以 “采样点序号” 为横轴、“灰度值” 为纵轴,绘制蓝色灰度分布曲线(plot(1:line_length, gray_values, 'b-', 'LineWidth',1.5)),清晰呈现条纹沿中心线的明暗变化(如暗纹谷值、明纹峰值),为后续条纹间距计算等分析提供数据支撑。

六)算法功能与效果

6.1 核心功能

  1. 自动化条纹识别:无需人工选点,自动完成从 “图像预处理” 到 “灰度提取” 的全流程,适配多幅图像批量处理;
  2. 精准中心线定位:通过霍夫变换筛选主条纹,结合双线性插值实现亚像素级灰度计算,避免倾斜条纹的灰度失真;
  3. 抗干扰能力:高斯滤波、小噪声去除、边缘增强等步骤,可有效抑制噪声和背景干扰,即使弱条纹也能被识别提取。

6.2 实际效果

  • 灰度分布曲线能清晰反映条纹的周期性:明纹区域对应灰度峰值,暗纹区域对应灰度谷值,无明显毛刺(噪声已被抑制);
  • 中心线贴合度高:红色中心线可准确覆盖主条纹的中间区域,提取的灰度数据能真实反映条纹的灰度变化规律,为后续 “暗纹定位计算条纹间距”“衍射参数反演” 等精细化分析提供可靠数据基础。

至于高斯模糊滤波是否有必要、加不加模糊会对测量精度有什么影响,下篇再进行详细探索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值