一、单缝和细丝的类比
昨天关于夫琅禾费衍射的文章——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;
诶,这样才对劲。
关键步骤解析
- 直线采样:通过参数 t 均匀生成直线上的点,采样点数等于直线长度(像素),保证每个像素至少被采样一次。
- 双线性插值:对于任意非整数坐标(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} 为小数部分。
- 坐标范围控制:确保采样点不超出图像边界,避免索引错误。
效果与应用
- 该方法可准确提取任意倾斜角度直线上的灰度值,适用于非水平 / 垂直的衍射条纹;
- 双线性插值能有效减少因像素离散化导致的误差,保证灰度分布的连续性;
- 后续可基于提取的灰度值,用之前提到的 “暗纹定位法” 计算倾斜条纹的间距(需注意将像素间距转换为物理距离时,考虑直线的倾斜角度)。
三、自动化识别提取中心:
在能够识别非水平的条纹之后,我希望能够通过某种方式自动识别而不是手动框选。
手动绘制直线法存在一个严重的问题——若绘制不当,一边比较贴近中心线,而另一边略有偏离,就会产生类似这种结果:
于是就有了这个代码:
% 读取图像并转换为灰度图
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+1
、y_floor=floor(y)
、y_ceil=y_floor+1
),再通过max(1, min(..., size(img,2)))
和max(1, min(..., size(img,1)))
限制坐标在图像范围内,避免索引越界错误; - 灰度加权计算:
- 计算采样点与周围 4 个像素的距离权重(
dx = x - x_floor
,dy = y - y_floor
,取值 0~1,距离越近权重越大); - 横向插值:分别计算
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
)的灰度值; - 纵向插值:通过
gray_values(i) = f1*(1-dy) + f2*dy
得到采样点最终灰度,解决非整数坐标的灰度离散问题,保证灰度分布连续。
- 计算采样点与周围 4 个像素的距离权重(
五)结果可视化
- 子图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 核心功能
- 自动化条纹识别:无需人工选点,自动完成从 “图像预处理” 到 “灰度提取” 的全流程,适配多幅图像批量处理;
- 精准中心线定位:通过霍夫变换筛选主条纹,结合双线性插值实现亚像素级灰度计算,避免倾斜条纹的灰度失真;
- 抗干扰能力:高斯滤波、小噪声去除、边缘增强等步骤,可有效抑制噪声和背景干扰,即使弱条纹也能被识别提取。
6.2 实际效果
- 灰度分布曲线能清晰反映条纹的周期性:明纹区域对应灰度峰值,暗纹区域对应灰度谷值,无明显毛刺(噪声已被抑制);
- 中心线贴合度高:红色中心线可准确覆盖主条纹的中间区域,提取的灰度数据能真实反映条纹的灰度变化规律,为后续 “暗纹定位计算条纹间距”“衍射参数反演” 等精细化分析提供可靠数据基础。
至于高斯模糊滤波是否有必要、加不加模糊会对测量精度有什么影响,下篇再进行详细探索。