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

使用cv2.remap基于网格扭曲图像出现像素化,如何实现平滑扭曲?

如何用1/4尺寸网格实现图像平滑扭曲(解决cv2.remap像素化问题)

我有一个尺寸为图像1/4的网格,希望利用该网格信息对图像进行扭曲处理,但使用cv2.remap时得到的扭曲效果存在像素化问题。如何实现更平滑的扭曲效果?

效果对比

  • 原图:
    原图
  • 期望效果:
    期望效果
  • 当前输出:
    当前输出

我的代码

img = np.array(Image.open('astronaut.jpg')) # Shape -> (512, 512, 3)
mesh = Mesh('astronaut.msh').get_uvs() # Shape -> (128, 128, 2), 2 channels for x and y 

new_mesh = np.zeros((img.shape[1], img.shape[0], 2))
new_mesh[:,:,0] = np.repeat(np.repeat(mesh[:,:,0], 4, axis=0), 4, axis=1)
new_mesh[:,:,1] = np.repeat(np.repeat(mesh[:,:,1], 4, axis=0), 4, axis=1)

nh, nw = img.shape[:2]
xs, ys = np.meshgrid(np.arange(0, nw), np.arange(0, nh))
xs = xs + new_mesh[:,:,0] * 4 # multiply by constant to modulate distort strength
ys = ys + new_mesh[:,:,1] * 4
xs = np.float32(xs)
ys = np.float32(ys)

dst= cv2.remap(img.astype(np.uint8), xs, ys, cv2.INTER_CUBIC)

解决方法

问题出在用硬重复像素的方式放大网格,这种操作会让网格的位移信息呈块状阶梯分布,最终导致扭曲效果像素化。正确做法是对低分辨率网格做插值放大,生成连续平滑的位移场,步骤如下:

  1. 替换硬重复为插值放大
    放弃np.repeat,改用OpenCV的cv2.resize,选择INTER_CUBIC(立方插值)或INTER_LINEAR(线性插值),让网格位移值平滑过渡:

    # 替换原new_mesh生成代码
    new_mesh_x = cv2.resize(mesh[:,:,0], (nw, nh), interpolation=cv2.INTER_CUBIC)
    new_mesh_y = cv2.resize(mesh[:,:,1], (nw, nh), interpolation=cv2.INTER_CUBIC)
    new_mesh = np.stack([new_mesh_x, new_mesh_y], axis=-1)
    
  2. 保留后续位移计算与映射
    原代码中位移叠加、cv2.remap的部分无需修改,INTER_CUBIC插值方式已经能保证最终图像的平滑度。

修改后的完整代码:

import cv2
import numpy as np
from PIL import Image

img = np.array(Image.open('astronaut.jpg')) # Shape -> (512, 512, 3)
mesh = Mesh('astronaut.msh').get_uvs() # Shape -> (128, 128, 2), 2 channels for x and y 

nh, nw = img.shape[:2]
# 插值放大网格生成平滑位移场
new_mesh_x = cv2.resize(mesh[:,:,0], (nw, nh), interpolation=cv2.INTER_CUBIC)
new_mesh_y = cv2.resize(mesh[:,:,1], (nw, nh), interpolation=cv2.INTER_CUBIC)
new_mesh = np.stack([new_mesh_x, new_mesh_y], axis=-1)

xs, ys = np.meshgrid(np.arange(0, nw), np.arange(0, nh))
xs = xs + new_mesh[:,:,0] * 4 # 调整扭曲强度
ys = ys + new_mesh[:,:,1] * 4
xs = np.float32(xs)
ys = np.float32(ys)

dst = cv2.remap(img.astype(np.uint8), xs, ys, cv2.INTER_CUBIC)

原理说明

  • np.repeat是直接复制每个网格点的位移值,生成的位移场是块状不连续的,扭曲时自然出现像素化边缘;
  • cv2.resize的立方插值会根据周围网格点的位移值,计算出中间像素的过渡位移,生成连续平滑的位移场,最终扭曲效果就能和期望一致。

内容的提问来源于stack exchange,提问作者Diego Rodea

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 15:54:36