是否存在可实现仅缩放修改区域的4点非均匀扭曲Python函数?
实现基于4点的非均匀图像扭曲(Python)
当然可以实现这种非均匀扭曲效果,核心思路是通过自定义坐标映射+插值来实现,避开透视变换的全局特性,只针对宽度/高度做局部缩放。下面是具体的实现方案:
核心原理
仿射变换只能处理平行性不变的线性变换,透视变换是全局的投影变换,都不符合你的需求。我们可以手动构建输出图像像素到原始图像像素的映射关系,基于4个控制点定义局部缩放规则,再通过插值填充像素值。
代码实现(OpenCV + NumPy)
以下是针对「宽度随高度线性变化」的非均匀扭曲实现,你可以根据需求调整映射逻辑:
import cv2 import numpy as np from scipy.ndimage import map_coordinates def non_uniform_width_warp(img, src_pts, dst_pts, output_shape): """ 实现基于4点的非均匀宽度扭曲(高度方向上宽度线性变化) :param img: 原始输入图像 :param src_pts: 原始图像4个控制点,格式[[左上x,左上y], [右上x,右上y], [左下x,左下y], [右下x,右下y]] :param dst_pts: 目标图像对应4个控制点,格式同src_pts :param output_shape: 输出图像尺寸(高, 宽, 通道数) """ h_out, w_out = output_shape[:2] # 创建输出图像的坐标网格 y_out, x_out = np.mgrid[0:h_out, 0:w_out] # 转换为浮点型数组方便计算 src = np.float32(src_pts) dst = np.float32(dst_pts) # 拆分四个角点 src_tl, src_tr, src_bl, src_br = src dst_tl, dst_tr, dst_bl, dst_br = dst # 计算当前y在目标垂直方向的比例(0到1) y_ratio = (y_out - dst_tl[1]) / (dst_bl[1] - dst_tl[1]) y_ratio = np.clip(y_ratio, 0.0, 1.0) # 避免超出边界 # 计算目标图像当前y对应的左右边界x坐标 dst_x_left = dst_tl[0] + y_ratio * (dst_bl[0] - dst_tl[0]) dst_x_right = dst_tr[0] + y_ratio * (dst_br[0] - dst_tr[0]) # 计算当前x在目标左右边界间的比例 x_ratio = (x_out - dst_x_left) / (dst_x_right - dst_x_left) x_ratio = np.clip(x_ratio, 0.0, 1.0) # 映射到原始图像的x坐标 src_x_left = src_tl[0] + y_ratio * (src_bl[0] - src_tl[0]) src_x_right = src_tr[0] + y_ratio * (src_br[0] - src_tr[0]) src_x = src_x_left + x_ratio * (src_x_right - src_x_left) # 映射到原始图像的y坐标 src_y = src_tl[1] + y_ratio * (src_bl[1] - src_tl[1]) # 使用scipy的map_coordinates做线性插值(替代手动双线性插值) coords = np.vstack([src_y.flatten(), src_x.flatten()]) output_img = np.zeros(output_shape, dtype=img.dtype) for channel in range(img.shape[2]): channel_data = map_coordinates(img[..., channel], coords, order=1) output_img[..., channel] = channel_data.reshape(h_out, w_out) return output_img # 示例使用 if __name__ == "__main__": # 读取图像 original_img = cv2.imread("original_image.png") h, w = original_img.shape[:2] # 定义原始图像四个角点 src_points = [[0, 0], [w-1, 0], [0, h-1], [w-1, h-1]] # 定义目标点:模拟底部宽度收缩的非均匀效果 dst_points = [[0, 0], [w-1, 0], [w//4, h-1], [3*w//4, h-1]] # 执行扭曲 warped_img = non_uniform_width_warp(original_img, src_points, dst_points, original_img.shape) # 保存或展示结果 cv2.imwrite("non_uniform_warped.png", warped_img) cv2.imshow("Original", original_img) cv2.imshow("Non-uniform Warped", warped_img) cv2.waitKey(0) cv2.destroyAllWindows()
调整与扩展
- 如果需要高度随宽度变化的扭曲,只需要交换x和y的映射逻辑,计算x方向的比例来控制高度变化。
- 若要实现非线性扭曲(比如弧形收缩),可以将
y_ratio替换为非线性函数(如二次函数y_ratio ** 2),让缩放变化更自然。 - 如果你不需要依赖scipy,也可以用手动实现的双线性插值替换
map_coordinates,代码逻辑可参考插值公式自行编写。
内容的提问来源于stack exchange,提问作者Aziz mohammed
相关产品推荐
相关产品推荐

