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

如何使用OpenCV为扫描文档图像添加折痕、褶皱以构建合成数据集?

我之前做过文档检测模型的合成数据集增强,用OpenCV完全能实现你要的折痕、褶皱、折叠效果,不用去逆向PS的流程。这里分享几个经过验证的实用方案,直接就能集成到你的数据 pipeline 里:

用OpenCV实现文档受损效果(数据增强)

一、折痕效果

折痕的核心是光影变化+轻微几何变形:纸张折起的部分会有高光,下方会形成阴影,同时折痕区域的像素会有微小偏移。我们分两步实现:

代码实现

import cv2
import numpy as np

def add_crease_effect(img, crease_start, crease_end, shadow_intensity=0.6, highlight_intensity=1.4):
    h, w = img.shape[:2]
    # 1. 创建折痕区域的蒙版
    mask = np.zeros((h, w), dtype=np.float32)
    crease_width = 20  # 折痕的宽度,可根据图像大小调整
    
    # 计算垂直于折痕的方向向量,用来生成折痕的多边形区域
    dx = crease_end[0] - crease_start[0]
    dy = crease_end[1] - crease_start[1]
    length = np.sqrt(dx**2 + dy**2)
    perp_dx = -dy / length * crease_width
    perp_dy = dx / length * crease_width
    
    # 定义折痕的四个顶点
    pts = np.array([
        crease_start + np.array([perp_dx, perp_dy]),
        crease_end + np.array([perp_dx, perp_dy]),
        crease_end - np.array([perp_dx, perp_dy]),
        crease_start - np.array([perp_dx, perp_dy])
    ], dtype=np.int32)
    
    cv2.fillPoly(mask, [pts], 1.0)
    
    # 2. 创建渐变光影:折痕一侧阴影,一侧高光
    gradient = np.zeros((h, w), dtype=np.float32)
    cv2.line(gradient, crease_start, crease_end, 1.0, thickness=crease_width*2)
    gradient = cv2.GaussianBlur(gradient, (51,51), 0)  # 模糊让光影更自然
    
    # 生成光影蒙版:阴影区域调暗,高光区域调亮
    shadow_mask = (gradient < 0.5).astype(np.float32) * (1 - shadow_intensity)
    highlight_mask = (gradient >= 0.5).astype(np.float32) * (highlight_intensity - 1)
    light_mask = 1 + highlight_mask - shadow_mask
    
    # 3. 应用光影到原图
    result = img.astype(np.float32) * light_mask[..., np.newaxis]
    result = np.clip(result, 0, 255).astype(np.uint8)
    
    # 4. 轻微扭曲折痕区域(可选,增强真实感)
    map_x = np.tile(np.arange(w, dtype=np.float32), (h,1))
    map_y = np.tile(np.arange(h, dtype=np.float32).reshape(-1,1), (1,w))
    
    # 折痕区域像素向垂直方向偏移
    offset = 5 * np.sin(np.arctan2(dy, dx) + np.pi/2)
    map_x[mask > 0] += offset * np.cos(np.arctan2(dy, dx))
    map_y[mask > 0] += offset * np.sin(np.arctan2(dy, dx))
    
    result = cv2.remap(result, map_x, map_y, interpolation=cv2.INTER_CUBIC)
    
    return result

# 示例调用
img = cv2.imread("your_document.jpg")
# 自定义折痕的起点和终点
result = add_crease_effect(img, (150, 200), (600, 200))
cv2.imwrite("document_with_crease.jpg", result)

二、褶皱效果

褶皱是随机的凹凸变形,核心是基于噪声的位移映射+光影模拟。我们用Perlin噪声生成高度图,模拟纸张的凹凸,再通过位移映射扭曲图像,最后叠加明暗效果:

代码实现

需要先安装noise库(pip install noise),如果不想用第三方库,也可以用OpenCV的随机噪声替代,效果稍弱但够用:

import cv2
import numpy as np
import noise

def add_crumple_effect(img, scale=100, strength=15, brightness_factor=0.2):
    h, w = img.shape[:2]
    
    # 1. 生成Perlin噪声高度图,模拟纸张的凹凸
    height_map = np.zeros((h, w), dtype=np.float32)
    for y in range(h):
        for x in range(w):
            # octaves控制噪声的细节,persistence控制衰减
            height_map[y][x] = noise.pnoise2(
                x/scale, y/scale, octaves=3,
                persistence=0.5, lacunarity=2.0,
                repeatx=w, repeaty=h, base=0
            )
    
    # 归一化高度图到[-1,1]范围
    height_map = (height_map - height_map.min()) / (height_map.max() - height_map.min()) * 2 - 1
    
    # 2. 生成位移映射,让像素根据高度图偏移
    map_x = np.tile(np.arange(w, dtype=np.float32), (h,1)) + height_map * strength
    map_y = np.tile(np.arange(h, dtype=np.float32).reshape(-1,1), (1,w)) + height_map * strength
    
    # 3. 扭曲图像,模拟褶皱的变形
    crumpled_img = cv2.remap(
        img, map_x, map_y, 
        interpolation=cv2.INTER_CUBIC, 
        borderMode=cv2.BORDER_REPLICATE  # 边缘重复填充,避免黑边
    )
    
    # 4. 添加光影:凸起区域更亮,凹陷区域更暗
    light_map = 1 + height_map * brightness_factor
    light_map = np.clip(light_map, 0.5, 1.5)  # 限制亮度范围,避免过曝或过暗
    result = crumpled_img.astype(np.float32) * light_map[..., np.newaxis]
    result = np.clip(result, 0, 255).astype(np.uint8)
    
    return result

# 示例调用
img = cv2.imread("your_document.jpg")
result = add_crumple_effect(img)
cv2.imwrite("document_with_crumple.jpg", result)

三、折叠效果(可选)

如果需要模拟整张纸对折的效果,可以用透视变换实现,再添加折叠处的阴影:

def add_fold_effect(img, fold_ratio=0.5):
    h, w = img.shape[:2]
    # 定义原始图像和折叠后图像的顶点
    src_pts = np.float32([[0,0], [w,0], [w,h], [0,h]])
    fold_width = int(w * fold_ratio)
    # 让左侧一半折叠到右侧(可根据需求调整顶点位置)
    dst_pts = np.float32([[fold_width,0], [w,0], [w,h], [0,h]])
    
    # 计算透视变换矩阵并应用
    M = cv2.getPerspectiveTransform(src_pts, dst_pts)
    folded_img = cv2.warpPerspective(img, M, (w, h))
    
    # 添加折叠处的阴影,增强真实感
    shadow = np.zeros((h, fold_width), dtype=np.uint8)
    cv2.rectangle(shadow, (0,0), (fold_width, h), 50, -1)
    folded_img[:, :fold_width] = cv2.addWeighted(folded_img[:, :fold_width], 1, shadow, 0.5, 0)
    
    return folded_img

实用建议

  • 把这些函数封装成数据增强pipeline,随机生成折痕位置、褶皱强度、折叠比例,生成多样化的合成数据,避免模型过拟合。
  • 光影参数(比如shadow_intensity、brightness_factor)可以根据你的文档图像的亮度、对比度调整,让效果更贴近真实场景。
  • 如果不想依赖noise库,用cv2.randn生成高斯噪声后再做模糊,也能生成类似的高度图,只是随机性会更强,细节稍差。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 14:27:52