如何用Python插值对2D图像中的直线滤镜正确执行剪切变换?
二值直线滤镜剪切变换无效果的问题排查与解决
我尝试用Python的插值方法对二值图像形式的2D直线滤镜应用剪切变换,但变换后的图像和原滤镜几乎完全一致,看不到剪切效果。但同样的剪切变换用在高斯函数生成的2D图像上时,效果符合预期。(原滤镜为竖直线,剪切后的滤镜仍显示竖直线,而高斯图剪切后呈现明显的斜向拉伸效果)
问题代码
import numpy as np import matplotlib.pyplot as plt from scipy import interpolate, ndimage # 定义原始2D函数f(x, y) def f(x, y, sigma, muu): dst = np.sqrt(x ** 2 + y ** 2) normal = 1 / (2.0 * np.pi * sigma ** 2) gauss = np.exp(-((dst - muu) ** 2 / (2.0 * sigma ** 2))) * normal return gauss # 图像尺寸 x_dim, y_dim = 5, 19 x_values = np.linspace(-(x_dim // 2), (x_dim // 2), x_dim) y_values = np.linspace(-(y_dim // 2), (y_dim // 2), y_dim) xv, yv = np.meshgrid(x_values, y_values, indexing='ij') # 剪切参数 a = 2 b = 1 interp_method = "linear" # 生成高斯图像A及其剪切版本A_ab A = f(xv, yv, 2, 0) A_ab = f(xv * a + yv * b, yv, 2, 0) # 创建直线滤镜 filter = np.zeros((x_dim, y_dim)) filter[:, y_dim // 2] = 1 # 为A和滤镜创建插值器 interp_A = interpolate.RegularGridInterpolator( (x_values, y_values), A, bounds_error=False, fill_value=None, method=interp_method) interp_filter = interpolate.RegularGridInterpolator( (x_values, y_values), filter, bounds_error=False, fill_value=None, method=interp_method) # 执行剪切变换 out_xv = xv * a + yv * b out_yv = yv sheared_coords_EPI = np.transpose(np.array((out_xv, out_yv)), axes=(1, 2, 0)) sheared_A = interp_A(sheared_coords_EPI) sheared_filter = interp_filter(sheared_coords_EPI) # 坐标平移到正空间用于warping x_shift = x_dim // 2 y_shift = y_dim // 2 shifted_out_xv = out_xv + x_shift shifted_out_yv = out_yv + y_shift # 使用新坐标warp滤镜 sheared_filter2 = ndimage.map_coordinates(filter, [shifted_out_xv, shifted_out_yv], order=1, mode='constant', cval=0.0) # 扁平化后插值滤镜 points = np.vstack((xv.flatten(), yv.flatten())).T values = filter.flatten() new_points = np.vstack((out_xv.flatten(), out_yv.flatten())).T interpolated_filter = interpolate.griddata(points, values, new_points, method='linear') interpolated_filter = interpolated_filter.reshape((x_dim, y_dim)) # 绘图 plt.figure(), plt.imshow(A), plt.title("原始高斯图") plt.figure(), plt.imshow(A_ab), plt.title("直接计算的剪切高斯图") plt.figure(), plt.imshow(sheared_A), plt.title("插值得到的剪切高斯图") plt.figure(), plt.imshow(filter), plt.title("原直线滤镜") plt.figure(), plt.imshow(sheared_filter), plt.title("插值得到的剪切滤镜") plt.figure(), plt.imshow(sheared_filter2), plt.title("map_coordinates得到的剪切滤镜") plt.figure(), plt.imshow(interpolated_filter), plt.title("griddata插值得到的剪切滤镜") plt.show()
问题根源
- 滤镜结构限制:原直线滤镜是仅在
y=y_dim//2位置的竖直线(单个y坐标的所有x位置为1),这种极端稀疏的二值结构在进行线性插值时,大部分变换后的采样点落在原滤镜的0值区域,无法插值出剪切后的连续线条。 - 尺寸过小:x维度仅为5个像素,剪切变换后
out_xv = xv*2 + yv*1的取值范围会超出原x网格的范围(原x范围是[-2,2]),而fill_value=None会自动使用边缘值填充,原边缘都是0,导致超出区域无法显示有效内容。 - 插值方法适配性:线性插值对连续的高斯图效果好,但对仅单条线的二值图,无法从稀疏的1值中生成剪切后的斜线。
解决方法
1. 增大图像尺寸,给剪切变换足够空间
将x维度扩大,让剪切后的线条能在网格内有足够的采样点:
x_dim, y_dim = 20, 19 x_values = np.linspace(-(x_dim // 2), (x_dim // 2), x_dim)
2. 使用更高阶插值或调整插值参数
改用立方插值,并明确填充0,或者在ndimage.map_coordinates中使用邻近插值:
# 调整插值器参数 interp_filter = interpolate.RegularGridInterpolator( (x_values, y_values), filter, bounds_error=False, fill_value=0, method="cubic") # 或调整map_coordinates的插值阶数 sheared_filter2 = ndimage.map_coordinates(filter, [shifted_out_xv, shifted_out_yv], order=3, mode='constant', cval=0.0)
3. 调整剪切变换的强度
减小剪切参数b的值,避免坐标过度超出原网格范围:
b = 0.5 # 降低剪切强度
4. 先扩展滤镜再变换
先将原滤镜扩展到更大尺寸,执行剪切后再裁剪回原尺寸,避免坐标超出范围:
# 扩展滤镜到3倍原x尺寸 expanded_filter = np.zeros((x_dim*3, y_dim)) expanded_filter[x_dim:(x_dim*2), y_dim//2] = 1 # 对扩展后的滤镜创建插值器并执行变换 interp_expanded = interpolate.RegularGridInterpolator( (np.linspace(-x_dim, x_dim, x_dim*3), y_values), expanded_filter, bounds_error=False, fill_value=0, method="cubic") sheared_expanded = interp_expanded(sheared_coords_EPI) # 裁剪回原尺寸 sheared_filter_final = sheared_expanded[x_dim//2:(x_dim//2+x_dim), :]
内容的提问来源于stack exchange,提问作者tag:python, numpy, scipy, image-processing, interpolation
相关产品推荐
相关产品推荐

