|
@@ -0,0 +1,209 @@
|
|
|
|
|
+#!/usr/bin/env python
|
|
|
|
|
+# -*- coding: utf-8 -*-
|
|
|
|
|
+"""
|
|
|
|
|
+图像预处理工具
|
|
|
|
|
+用于提高 OCR 识别准确率
|
|
|
|
|
+"""
|
|
|
|
|
+
|
|
|
|
|
+import cv2
|
|
|
|
|
+import numpy as np
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def preprocess_id_card_image(image_path, output_path=None):
|
|
|
|
|
+ """
|
|
|
|
|
+ 预处理身份证图片以提高 OCR 识别率
|
|
|
|
|
+
|
|
|
|
|
+ 参数:
|
|
|
|
|
+ image_path: 输入图片路径
|
|
|
|
|
+ output_path: 输出图片路径(可选,如果提供则保存预处理后的图片)
|
|
|
|
|
+
|
|
|
|
|
+ 返回:
|
|
|
|
|
+ 预处理后的图片(numpy array)
|
|
|
|
|
+ """
|
|
|
|
|
+ # 读取图片
|
|
|
|
|
+ img = cv2.imread(image_path)
|
|
|
|
|
+ if img is None:
|
|
|
|
|
+ raise ValueError(f"无法读取图片: {image_path}")
|
|
|
|
|
+
|
|
|
|
|
+ # 1. 转为灰度图
|
|
|
|
|
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
+
|
|
|
|
|
+ # 2. 降噪
|
|
|
|
|
+ denoised = cv2.fastNlMeansDenoising(gray, None, 10, 7, 21)
|
|
|
|
|
+
|
|
|
|
|
+ # 3. 自适应二值化
|
|
|
|
|
+ binary = cv2.adaptiveThreshold(
|
|
|
|
|
+ denoised, 255,
|
|
|
|
|
+ cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
|
|
|
|
|
+ cv2.THRESH_BINARY,
|
|
|
|
|
+ 11, 2
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # 4. 形态学操作去除噪点
|
|
|
|
|
+ kernel = np.ones((2, 2), np.uint8)
|
|
|
|
|
+ morph = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
|
|
|
|
|
+
|
|
|
|
|
+ # 5. 锐化(可选)
|
|
|
|
|
+ kernel_sharpen = np.array([
|
|
|
|
|
+ [-1, -1, -1],
|
|
|
|
|
+ [-1, 9, -1],
|
|
|
|
|
+ [-1, -1, -1]
|
|
|
|
|
+ ])
|
|
|
|
|
+ sharpened = cv2.filter2D(morph, -1, kernel_sharpen)
|
|
|
|
|
+
|
|
|
|
|
+ # 保存预处理后的图片
|
|
|
|
|
+ if output_path:
|
|
|
|
|
+ cv2.imwrite(output_path, sharpened)
|
|
|
|
|
+
|
|
|
|
|
+ return sharpened
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def enhance_image_quality(image_path):
|
|
|
|
|
+ """
|
|
|
|
|
+ 增强图片质量(对比度、亮度等)
|
|
|
|
|
+
|
|
|
|
|
+ 参数:
|
|
|
|
|
+ image_path: 输入图片路径
|
|
|
|
|
+
|
|
|
|
|
+ 返回:
|
|
|
|
|
+ 增强后的图片(numpy array)
|
|
|
|
|
+ """
|
|
|
|
|
+ img = cv2.imread(image_path)
|
|
|
|
|
+ if img is None:
|
|
|
|
|
+ raise ValueError(f"无法读取图片: {image_path}")
|
|
|
|
|
+
|
|
|
|
|
+ # 转换到 LAB 色彩空间
|
|
|
|
|
+ lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
|
|
|
|
|
+ l, a, b = cv2.split(lab)
|
|
|
|
|
+
|
|
|
|
|
+ # 对 L 通道进行直方图均衡化
|
|
|
|
|
+ clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
|
|
|
|
|
+ cl = clahe.apply(l)
|
|
|
|
|
+
|
|
|
|
|
+ # 合并通道
|
|
|
|
|
+ enhanced_lab = cv2.merge((cl, a, b))
|
|
|
|
|
+ enhanced = cv2.cvtColor(enhanced_lab, cv2.COLOR_LAB2BGR)
|
|
|
|
|
+
|
|
|
|
|
+ return enhanced
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def auto_rotate_image(image_path):
|
|
|
|
|
+ """
|
|
|
|
|
+ 自动旋转图片使文字方向正确
|
|
|
|
|
+
|
|
|
|
|
+ 参数:
|
|
|
|
|
+ image_path: 输入图片路径
|
|
|
|
|
+
|
|
|
|
|
+ 返回:
|
|
|
|
|
+ 旋转后的图片(numpy array)
|
|
|
|
|
+ """
|
|
|
|
|
+ img = cv2.imread(image_path)
|
|
|
|
|
+ if img is None:
|
|
|
|
|
+ raise ValueError(f"无法读取图片: {image_path}")
|
|
|
|
|
+
|
|
|
|
|
+ # 转为灰度图
|
|
|
|
|
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
|
|
|
+
|
|
|
|
|
+ # 边缘检测
|
|
|
|
|
+ edges = cv2.Canny(gray, 50, 150, apertureSize=3)
|
|
|
|
|
+
|
|
|
|
|
+ # 霍夫变换检测直线
|
|
|
|
|
+ lines = cv2.HoughLines(edges, 1, np.pi / 180, 200)
|
|
|
|
|
+
|
|
|
|
|
+ if lines is not None:
|
|
|
|
|
+ # 计算平均角度
|
|
|
|
|
+ angles = []
|
|
|
|
|
+ for rho, theta in lines[:, 0]:
|
|
|
|
|
+ angle = np.rad2deg(theta)
|
|
|
|
|
+ angles.append(angle)
|
|
|
|
|
+
|
|
|
|
|
+ # 获取主要角度
|
|
|
|
|
+ median_angle = np.median(angles)
|
|
|
|
|
+
|
|
|
|
|
+ # 旋转图片
|
|
|
|
|
+ if abs(median_angle - 90) < 45:
|
|
|
|
|
+ rotation_angle = median_angle - 90
|
|
|
|
|
+ else:
|
|
|
|
|
+ rotation_angle = median_angle
|
|
|
|
|
+
|
|
|
|
|
+ # 执行旋转
|
|
|
|
|
+ (h, w) = img.shape[:2]
|
|
|
|
|
+ center = (w // 2, h // 2)
|
|
|
|
|
+ M = cv2.getRotationMatrix2D(center, rotation_angle, 1.0)
|
|
|
|
|
+ rotated = cv2.warpAffine(
|
|
|
|
|
+ img, M, (w, h),
|
|
|
|
|
+ flags=cv2.INTER_CUBIC,
|
|
|
|
|
+ borderMode=cv2.BORDER_REPLICATE
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ return rotated
|
|
|
|
|
+
|
|
|
|
|
+ return img
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def resize_for_ocr(image_path, target_width=1500):
|
|
|
|
|
+ """
|
|
|
|
|
+ 调整图片尺寸以适合 OCR
|
|
|
|
|
+
|
|
|
|
|
+ 参数:
|
|
|
|
|
+ image_path: 输入图片路径
|
|
|
|
|
+ target_width: 目标宽度(像素)
|
|
|
|
|
+
|
|
|
|
|
+ 返回:
|
|
|
|
|
+ 调整大小后的图片(numpy array)
|
|
|
|
|
+ """
|
|
|
|
|
+ img = cv2.imread(image_path)
|
|
|
|
|
+ if img is None:
|
|
|
|
|
+ raise ValueError(f"无法读取图片: {image_path}")
|
|
|
|
|
+
|
|
|
|
|
+ # 获取原始尺寸
|
|
|
|
|
+ h, w = img.shape[:2]
|
|
|
|
|
+
|
|
|
|
|
+ # 如果宽度小于目标宽度,则放大
|
|
|
|
|
+ if w < target_width:
|
|
|
|
|
+ ratio = target_width / w
|
|
|
|
|
+ new_width = target_width
|
|
|
|
|
+ new_height = int(h * ratio)
|
|
|
|
|
+ resized = cv2.resize(
|
|
|
|
|
+ img, (new_width, new_height),
|
|
|
|
|
+ interpolation=cv2.INTER_CUBIC
|
|
|
|
|
+ )
|
|
|
|
|
+ return resized
|
|
|
|
|
+
|
|
|
|
|
+ # 如果宽度大于目标宽度,则缩小
|
|
|
|
|
+ elif w > target_width * 1.5:
|
|
|
|
|
+ ratio = target_width / w
|
|
|
|
|
+ new_width = target_width
|
|
|
|
|
+ new_height = int(h * ratio)
|
|
|
|
|
+ resized = cv2.resize(
|
|
|
|
|
+ img, (new_width, new_height),
|
|
|
|
|
+ interpolation=cv2.INTER_AREA
|
|
|
|
|
+ )
|
|
|
|
|
+ return resized
|
|
|
|
|
+
|
|
|
|
|
+ return img
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# 示例用法
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ import sys
|
|
|
|
|
+
|
|
|
|
|
+ if len(sys.argv) < 2:
|
|
|
|
|
+ print("使用方法: python image_preprocess.py <图片路径>")
|
|
|
|
|
+ sys.exit(1)
|
|
|
|
|
+
|
|
|
|
|
+ image_path = sys.argv[1]
|
|
|
|
|
+
|
|
|
|
|
+ # 预处理
|
|
|
|
|
+ processed = preprocess_id_card_image(image_path, "processed.jpg")
|
|
|
|
|
+ print("预处理完成,保存为 processed.jpg")
|
|
|
|
|
+
|
|
|
|
|
+ # 增强
|
|
|
|
|
+ enhanced = enhance_image_quality(image_path)
|
|
|
|
|
+ cv2.imwrite("enhanced.jpg", enhanced)
|
|
|
|
|
+ print("质量增强完成,保存为 enhanced.jpg")
|
|
|
|
|
+
|
|
|
|
|
+ # 调整大小
|
|
|
|
|
+ resized = resize_for_ocr(image_path)
|
|
|
|
|
+ cv2.imwrite("resized.jpg", resized)
|
|
|
|
|
+ print("尺寸调整完成,保存为 resized.jpg")
|