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

如何用Python实现手写文本图像沿活页纸目标网格的变形?

手写文本图像沿活页纸弯曲网格变形实现方案

需求概述

要将神经网络生成的扁平手写文本图像,贴合到活页纸图像的弯曲网格上,分两步实现:提取活页纸的曲线网格、将文本图像按网格变形。以下是基于OpenCV+NumPy的稳定实现方案。


第一步:提取活页纸的曲线网格

实现思路

通过图像预处理提取线条,再对线条点进行曲线拟合,得到每行网格的数学表达式,最终生成网格顶点列表:

  1. 灰度化+二值化:突出纸张线条,过滤背景干扰
  2. 降噪+骨架化:提取线条的中心骨架,减少冗余点
  3. 曲线拟合:对每行线条的点集进行多项式拟合,得到平滑的曲线方程

代码实现

import cv2
import numpy as np
from skimage.morphology import skeletonize

def extract_paper_grid(paper_img_path):
    # 1. 读取并预处理图像
    img = cv2.imread(paper_img_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 自适应二值化,突出线条
    thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
    # 降噪:开运算去除小噪点
    kernel = np.ones((3,3), np.uint8)
    clean_thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)
    
    # 2. 骨架化提取线条中心
    skeleton = skeletonize(clean_thresh/255).astype(np.uint8)*255
    # 提取线条的所有非零点
    y_coords, x_coords = np.where(skeleton == 255)
    
    # 3. 按行分组并拟合曲线
    grid_curves = []
    # 按y坐标分组(步长根据线条间距调整,这里设为20)
    y_bins = np.arange(0, img.shape[0], 20)
    for i in range(len(y_bins)-1):
        mask = (y_coords >= y_bins[i]) & (y_coords < y_bins[i+1])
        if np.sum(mask) < 10:
            continue
        # 获取当前行的x,y点
        row_x = x_coords[mask]
        row_y = y_coords[mask]
        # 多项式拟合(3次多项式足够拟合弯曲线条)
        coeffs = np.polyfit(row_y, row_x, 3)
        grid_curves.append(coeffs)
    
    # 生成网格顶点列表(每个曲线取若干采样点)
    grid_points = []
    sample_ys = np.linspace(0, img.shape[0], 50)  # 每行取50个采样点
    for coeffs in grid_curves:
        row_x = np.polyval(coeffs, sample_ys)
        row_points = list(zip(row_x.astype(int), sample_ys.astype(int)))
        grid_points.append(row_points)
    
    return grid_points, img.shape

# 调用示例
grid_points, paper_shape = extract_paper_grid("活页纸图像路径")

第二步:将文本图像沿网格变形

实现思路

使用OpenCV的remap函数,基于网格曲线计算每个目标像素对应的源文本像素位置,实现平滑变形:

  1. 对齐文本与纸张的尺寸:将文本图像缩放到与活页纸图像一致的高度
  2. 生成变形映射表:根据网格曲线,计算每个目标(y,x)对应的源文本的(x,y)坐标
  3. 执行重映射:将文本图像按映射表变形,贴合网格

代码实现

def warp_text_to_grid(text_img_path, grid_points, paper_shape):
    # 读取文本图像并缩放至纸张高度
    text_img = cv2.imread(text_img_path)
    text_h, text_w = text_img.shape[:2]
    scale = paper_shape[0] / text_h
    text_img_scaled = cv2.resize(text_img, (int(text_w*scale), paper_shape[0]))
    text_scaled_h, text_scaled_w = text_img_scaled.shape[:2]
    
    # 生成映射表:map_x[y][x] = 源图像x坐标,map_y[y][x] = 源图像y坐标
    map_x = np.zeros(paper_shape, dtype=np.float32)
    map_y = np.zeros(paper_shape, dtype=np.float32)
    
    # 计算每行的变形比例
    # 先获取网格的上下行y坐标
    grid_ys = [points[0][1] for points in grid_points]
    # 遍历每个y坐标
    for y in range(paper_shape[0]):
        # 找到当前y所在的网格区间
        for i in range(len(grid_ys)-1):
            if grid_ys[i] <= y <= grid_ys[i+1]:
                # 获取上下两行的曲线
                upper_coeffs = np.polyfit([p[1] for p in grid_points[i]], [p[0] for p in grid_points[i]], 3)
                lower_coeffs = np.polyfit([p[1] for p in grid_points[i+1]], [p[0] for p in grid_points[i+1]], 3)
                # 计算当前y对应的左右偏移量
                upper_x = np.polyval(upper_coeffs, y)
                lower_x = np.polyval(lower_coeffs, y)
                # 线性插值当前行的x偏移
                t = (y - grid_ys[i]) / (grid_ys[i+1] - grid_ys[i])
                current_x_offset = upper_x*(1-t) + lower_x*t
                # 计算源文本的x坐标:按比例映射
                for x in range(paper_shape[1]):
                    # 纸张的x对应文本的x = (x - current_x_offset + text_scaled_w/2) * (text_scaled_w / paper_shape[1])
                    src_x = (x - current_x_offset + text_scaled_w/2) * (text_scaled_w / paper_shape[1])
                    src_x = np.clip(src_x, 0, text_scaled_w-1)
                    map_x[y][x] = src_x
                    map_y[y][x] = y  # y坐标直接映射(因为文本高度已对齐)
                break
    
    # 执行重映射
    warped_text = cv2.remap(text_img_scaled, map_x, map_y, cv2.INTER_CUBIC)
    
    # 可选:将变形后的文本与原纸张图像叠加
    paper_img = cv2.imread("活页纸图像路径")
    result = cv2.addWeighted(paper_img, 0.5, warped_text, 0.5, 0)
    
    return warped_text, result

# 调用示例
warped_text, final_result = warp_text_to_grid("手写文本图像路径", grid_points, paper_shape)
cv2.imwrite("变形结果图.jpg", final_result)

关键注意事项

  • 调整二值化的参数(adaptiveThreshold的块大小和常数),适配不同亮度的活页纸图像
  • 多项式拟合的阶数可根据线条弯曲程度调整(3次基本满足大多数弯曲情况)
  • 若网格线条提取仍有干扰,可增加颜色过滤:先将图像转为HSV,过滤纸张的背景色后再处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 13:45:14