You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何利用二维形变场实现图像变形?

如何利用二维形变场实现图像变形?

我明白你现在卡在怎么用生成的矢量场来变形图像了,之前参考了方法但没成功,咱们一步步拆解问题来解决。

首先先理清楚你的场景:你已经用二次函数生成了一个相位场,对应的形变矢量场是这个相位场的梯度(就是你图里的浅蓝箭头),现在要把这个形变效果应用到目标图像上。核心思路是利用OpenCV的remap函数——它能根据我们提供的映射表,把原始图像的像素按照指定的形变规则重新排列。

先回顾你生成相位场的代码(这个是没问题的):

import numpy as np
import cv2
import matplotlib.pyplot as plt

size = 100
x = np.linspace(-1, 1, size)
y = np.linspace(-1, 1, size)
X, Y = np.meshgrid(x, y)
phase_profile = 800.0 * ((X - 0.0)**2 + (Y - 0.0)**2)

接下来分析你尝试的代码里的几个关键问题:

  • 网格坐标搞反了:OpenCV的remap要求mapx是列方向(x轴)的坐标,mapy是行方向(y轴)的坐标,但你创建mapx_basemapy_base时,把行和列的顺序弄反了,导致形变方向完全不对。
  • 梯度空间转换混淆:你同时用了带物理步长dx和不带参数的梯度计算,这里需要统一把物理空间的梯度转换成像素空间的偏移量。
  • 映射表的类型问题remap要求映射表是浮点型数组,虽然你的代码没写错,但这点需要注意。

修正后的实现方案

下面是调整后的完整代码,我会一步步解释:

1. 正确计算形变映射表

def generate_deformation_map(phase_profile, size=100, coeff=0.01):
    # 物理空间的步长:从-1到1共size个点,步长是2/(size-1)
    dx = 2 / (size - 1)
    # 计算相位场的梯度:grad_y是y方向梯度,grad_x是x方向梯度
    grad_y, grad_x = np.gradient(phase_profile, dx, dx)
    
    # 创建像素空间的原始网格:mapx是列坐标(x轴),mapy是行坐标(y轴)
    # 注意这里的顺序:先列,再行
    mapx_base, mapy_base = np.meshgrid(np.arange(size), np.arange(size))
    
    # 把物理空间的梯度转换成像素空间的偏移,加上原始坐标得到映射表
    # coeff是形变幅度系数,根据需求调整,这里用0.01是因为你的phase_profile值很大
    mapx = mapx_base + grad_x * coeff
    mapy = mapy_base + grad_y * coeff
    
    # 转换成OpenCV要求的float32类型
    mapx = mapx.astype(np.float32)
    mapy = mapy.astype(np.float32)
    
    return mapx, mapy

2. 应用形变到图像

# 加载你的目标图像(这里假设图像尺寸是100x100,和相位场尺寸一致)
# 如果尺寸不一致,需要先把相位场缩放到图像尺寸,或者调整网格大小
img = cv2.imread("你的图像路径.png", cv2.IMREAD_GRAYSCALE)
# 确保图像尺寸和相位场一致
img = cv2.resize(img, (size, size))

# 生成映射表
mapx, mapy = generate_deformation_map(phase_profile, size=size, coeff=0.01)

# 应用形变
deformed_img = cv2.remap(img, mapx, mapy, interpolation=cv2.INTER_CUBIC)

# 显示结果
plt.figure(figsize=(10,5))
plt.subplot(121)
plt.imshow(img, cmap="gray")
plt.title("原始图像")
plt.subplot(122)
plt.imshow(deformed_img, cmap="gray")
plt.title("形变后图像")
plt.show()

关键细节说明

  • 梯度方向与形变方向:如果形变效果和你预期的相反,只需要把梯度取反(比如mapx = mapx_base - grad_x * coeff),因为梯度是相位场上升的方向,有时候我们需要沿着下降方向形变。
  • 系数coeff的调整:你的相位场值很大(800倍的二次项),所以coeff要设得小一点,不然形变会过于剧烈导致图像完全扭曲。你可以根据实际效果慢慢调整这个值。
  • 图像尺寸匹配:如果你的目标图像和相位场尺寸不一样,需要先把相位场插值到图像的尺寸,或者调整generate_deformation_map里的size参数和图像尺寸一致。

另外,你的相位场可视化函数是没问题的,继续用它可以直观看到形变矢量场的方向:

def plot_phase_heatmap_with_gradients(phase_profile, mesh_grid_x, mesh_grid_y, size=100):
    dx = 2 / (size - 1) 
    grad_y, grad_x = np.gradient(phase_profile, dx, dx)
    plt.figure(figsize=(8, 6))
    plt.imshow(phase_profile, cmap='hot', origin='lower', extent=[-1, 1, -1, 1])
    plt.colorbar(label='Phase Profile')

    print("max grad_x: ", np.max(grad_x))

    skip = 10  # 调整箭头密度
    plt.quiver(mesh_grid_x[::skip, ::skip], mesh_grid_y[::skip, ::skip], grad_x[::skip, ::skip], grad_y[::skip, ::skip], color='cyan')

    plt.xlabel('X-axis')
    plt.ylabel('Y-axis')
    plt.title('Phase Profile with Gradient Vectors')
    plt.show()

# 调用可视化
plot_phase_heatmap_with_gradients(phase_profile, X, Y, size=size)

备注:内容来源于stack exchange,提问作者alpha027

火山引擎 最新活动