如何使用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
相关产品推荐
相关产品推荐

