You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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()

问题根源

  1. 滤镜结构限制:原直线滤镜是仅在y=y_dim//2位置的竖直线(单个y坐标的所有x位置为1),这种极端稀疏的二值结构在进行线性插值时,大部分变换后的采样点落在原滤镜的0值区域,无法插值出剪切后的连续线条。
  2. 尺寸过小:x维度仅为5个像素,剪切变换后out_xv = xv*2 + yv*1的取值范围会超出原x网格的范围(原x范围是[-2,2]),而fill_value=None会自动使用边缘值填充,原边缘都是0,导致超出区域无法显示有效内容。
  3. 插值方法适配性:线性插值对连续的高斯图效果好,但对仅单条线的二值图,无法从稀疏的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 23:23:11