Files
2025-11-10 11:11:52 +08:00

250 lines
7.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage import gaussian_filter
from scipy.fft import fft, fftshift, fftfreq
from ROI import interface_roi
from line import interface_corr
def generate_test_edge(width=512, height=512, edge_blur=2.0):
"""生成测试用的边缘图像,用于演示MTF计算"""
# 创建理想的锐利边缘
edge_image = np.ones((height, width))
edge_position = width // 2
edge_image[:, :edge_position] = 0 # 左侧为暗区,右侧为亮区
# 添加高斯模糊模拟实际成像系统的模糊
blurred_edge = gaussian_filter(edge_image, sigma=edge_blur)
return blurred_edge
def extract_esf(edge_image, roi_size=100):
"""从边缘图像中提取边缘扩散函数(ESF)"""
height, width = edge_image.shape
# 找到边缘位置(亮度变化最大的列)
edge_profile = np.mean(edge_image, axis=0) # 沿垂直方向平均得到水平方向的亮度分布
edge_gradient = np.abs(np.gradient(edge_profile))
edge_position = np.argmax(edge_gradient)
# 提取感兴趣区域(ROI),围绕边缘位置
start = max(0, edge_position - roi_size // 2)
end = min(width, edge_position + roi_size // 2)
# 提取边缘区域并沿垂直方向平均,得到ESF
esf_region = edge_image[:, start:end]
esf = np.mean(esf_region, axis=0) # 沿垂直方向平均
# 归一化ESF到[0, 1]范围
esf = (esf - np.min(esf)) / (np.max(esf) - np.min(esf))
return esf, start, end
def calculate_mtf(esf, pixel_size=1.0):
"""
从边缘扩散函数(ESF)计算调制传递函数(MTF)
参数:
esf: 边缘扩散函数
pixel_size: 像素尺寸(mm),用于计算实际空间频率
返回:
frequencies: 空间频率 (线对/毫米)
mtf: 调制传递函数值
"""
# 对ESF求导得到线扩散函数(LSF)
lsf = np.gradient(esf)
# 对LSF进行傅里叶变换得到SFR
n = len(lsf)
lsf_fft = fft(lsf)
sfr = fftshift(lsf_fft)
# 计算幅度谱并归一化得到MTF
mtf = np.abs(sfr)
mtf = mtf / mtf[int(n / 2)] # 直流分量归一化到1
# 计算空间频率 (线对/毫米)
frequencies = fftfreq(n, d=pixel_size)
frequencies = fftshift(frequencies)
frequencies = frequencies[frequencies >= 0] # 只保留正频率
# 只保留正频率部分的MTF
mtf = mtf[int(n / 2):]
return frequencies, mtf, lsf
def trans(frequencies,pixel_size,x_unit='LP/mm',H_mm=1.0):
unit = x_unit
F = 50.0 # 焦距,单位毫米
if unit == 'LP/mm':
x = frequencies
xlabel = 'Spatial Frequency (LP/mm)'
elif unit == 'L/mm':
x = frequencies * 2.0 # 1 LP = 2 L
xlabel = 'Spatial Frequency (L/mm)'
elif unit == 'Cycles/mm':
x = frequencies # Cycles/mm 等同于 LP/mm
xlabel = 'Spatial Frequency (Cycles/mm)'
elif unit == 'Cycles/pixel':
x = frequencies * pixel_size # = (LP/mm) * (mm/pixel)
xlabel = 'Spatial Frequency (Cycles/pixel)'
elif unit == 'LP/PH':
x = frequencies * H_mm # = (LP/mm) * H(mm)
xlabel = 'Spatial Frequency (LP/PH)'
elif unit == 'LW/PH':
x = frequencies * (2.0 * H_mm) # = (LP/mm) * 2 * H(mm)
xlabel = 'Spatial Frequency (LW/PH)'
elif unit == 'C/Mrad':
x = (F *frequencies)/1000 # = (LP/mm) * H(mm) / 1000
xlabel = 'Spatial Frequency (Cycles/Mrad)'
else:
x = frequencies
xlabel = f'Spatial Frequency ({unit})'
return x,xlabel
def plot_results(edge_image, esf, frequencies, mtf, lsf,image_high=480,x_range=None, x_unit='LP/mm',
pixel_size=1.0):
"""可视化结果:边缘图像、ESF和MTF曲线"""
fig, axes = plt.subplots(2, 2, figsize=(18, 5))
axes = axes.flatten() # 展平成一维数组,方便索引
# 显示边缘图像
axes[0].imshow(edge_image, cmap='gray')
axes[0].set_title('Edge Image')
axes[0].axis('off')
# 显示边缘扩散函数(ESF)
axes[1].plot(esf)
axes[1].set_title('Edge Spread Function (ESF)')
axes[1].set_xlabel('Position (pixels)')
axes[1].set_ylabel('Normalized Intensity')
axes[1].grid(True)
# 显示MTF曲线(支持多种空间频率单位)
# ph_pixels = edge_image.shape[0]
h_mm = image_high * pixel_size # 画幅高度(mm)
unit = x_unit
x,xlabel = trans(frequencies,pixel_size,unit,h_mm)
axes[2].plot(x, mtf)
axes[2].set_title('Modulation Transfer Function (MTF)')
axes[2].set_xlabel(xlabel)
axes[2].set_ylabel('MTF Value')
axes[2].set_ylim(0, 1.1)
# 根据单位设置横坐标显示范围
if x_range is not None:
axes[2].set_xlim(0,x_range)
else:
axes[2].set_xlim(x.min(), x.max())
axes[2].grid(True)
axes[2].grid(True)
# 打印一些关键频率的MTF值
key_mtf_values = [0.5, 0.2, 0.1]
print("关键MTF对应的空间频率:")
for t in key_mtf_values:
f = find_freq_for_mtf(frequencies, mtf, t)
if f is not None:
f,xlabel = trans(f,pixel_size,unit,h_mm)
print(f"MTF={t:.3f}: {f:.3f} {xlabel}")
else:
print(f"MTF={t:.3f}: 未找到对应频率")
# 显示线扩散函数(LSF)曲线
axes[3].plot(lsf)
axes[3].set_title('Line Spread Function (LSF)')
axes[3].set_xlabel('Position (pixels)')
axes[3].set_ylabel('LSF Value')
axes[3].grid(True)
plt.tight_layout()
plt.tight_layout(pad=2.0)
plt.subplots_adjust(top=0.9, bottom=0.1, left=0.08, right=0.95, wspace=0.3, hspace=0.5)
plt.show()
def find_freq_for_mtf(freqs, mtf_vals, target):
freqs = np.asarray(freqs)
mtf_vals = np.asarray(mtf_vals)
idxs = np.where((mtf_vals[:-1] >= target) & (mtf_vals[1:] <= target))[0]
if idxs.size == 0:
return None
i = idxs[0]
m1, m2 = mtf_vals[i], mtf_vals[i + 1]
f1, f2 = freqs[i], freqs[i + 1]
if m2 == m1:
return f1
w = (target - m1) / (m2 - m1)
return f1 + w * (f2 - f1)
def main(image_path=None):
# 生成测试边缘图像
edge_image = None
# image_path = r'output/result_hough.bmp' # 将此路径改为你的图像文件路径(Windows 风格路径)
# 读取图像(支持灰度或 RGB,使用 matplotlib.imread)
img = plt.imread(image_path)
# 如果是带 alpha 的 RGBA,去掉 alpha
if img.ndim == 3 and img.shape[2] == 4:
img = img[..., :3]
# 如果是彩色图像,转换为灰度
if img.ndim == 3:
edge_image = np.mean(img, axis=2)
else:
edge_image = img.copy()
# 若像素值为 0-255,归一化到 0-1
if edge_image.max() > 1.0:
edge_image = edge_image.astype(np.float32) / 255.0
# 可选:确保图像足够大或裁剪/缩放以适应 ESF 提取(根据需要调整)
# 例如裁剪到 512x512: edge_image = edge_image[:512, :512]
# 提取边缘扩散函数
# esf, _, _ = extract_esf(edge_image, roi_size=200)
x,y = edge_image.shape
if x > y:
min_len = y
else:
min_len = x
esf, _, _ = extract_esf(edge_image, roi_size=min_len)
# # 计算MTF,假设像素尺寸为0.012毫米
# frequencies, mtf, lsf = calculate_mtf(esf, pixel_size=0.012)
#
# # 显示结果
# plot_results(edge_image, esf, frequencies, mtf, lsf)
# 计算MTF,假设像素尺寸为0.012毫米
pixel_size = 0.012
frequencies, mtf, lsf = calculate_mtf(esf, pixel_size=pixel_size)
# 显示结果
x_unit = 'C/Mrad' # 可选: 'C/Mrad','LP/mm','L/mm','Cycles/mm','Cycles/pixel','LP/PH','LW/PH'
plot_results(edge_image, esf, frequencies, mtf, lsf, x_unit=x_unit, pixel_size=pixel_size)
if __name__ == "__main__":
roi_img = interface_roi()
input_path = interface_corr(roi_img)
main(input_path)
main(r"output/result_hough.bmp")