129 lines
3.9 KiB
Python
129 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
验证码识别脚本
|
||
输入:验证码图片路径
|
||
输出:识别结果(打印到 stdout)
|
||
|
||
预处理流程:
|
||
1. 灰度化
|
||
2. 去除水平干扰线(形态学开运算)
|
||
3. 二值化
|
||
4. 去噪
|
||
5. ddddocr 识别
|
||
"""
|
||
import sys
|
||
import cv2
|
||
import numpy as np
|
||
import ddddocr
|
||
|
||
def preprocess(img_path):
|
||
"""多种预处理方案"""
|
||
img = cv2.imread(img_path)
|
||
if img is None:
|
||
return []
|
||
|
||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||
results = []
|
||
|
||
# 方案1:去水平线 + 二值化
|
||
# 用水平核做形态学开运算,提取水平线
|
||
h_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 1))
|
||
h_lines = cv2.morphologyEx(gray, cv2.MORPH_OPEN, h_kernel)
|
||
# 从原图减去水平线
|
||
no_lines = cv2.subtract(gray, h_lines)
|
||
# 二值化
|
||
_, binary1 = cv2.threshold(no_lines, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
||
results.append(binary1)
|
||
|
||
# 方案2:自适应阈值
|
||
adaptive = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
|
||
cv2.THRESH_BINARY, 11, 2)
|
||
results.append(adaptive)
|
||
|
||
# 方案3:去水平线 + 自适应阈值
|
||
adaptive2 = cv2.adaptiveThreshold(no_lines, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
|
||
cv2.THRESH_BINARY, 11, 2)
|
||
results.append(adaptive2)
|
||
|
||
# 方案4:反色 + 去线 + 二值化
|
||
inverted = cv2.bitwise_not(gray)
|
||
h_lines_inv = cv2.morphologyEx(inverted, cv2.MORPH_OPEN, h_kernel)
|
||
no_lines_inv = cv2.subtract(inverted, h_lines_inv)
|
||
_, binary4 = cv2.threshold(no_lines_inv, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
||
results.append(binary4)
|
||
|
||
# 方案5:中值滤波去噪 + OTSU
|
||
median = cv2.medianBlur(gray, 3)
|
||
_, binary5 = cv2.threshold(median, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
||
results.append(binary5)
|
||
|
||
# 方案6:放大 + 去线 + 锐化
|
||
big = cv2.resize(gray, None, fx=3, fy=3, interpolation=cv2.INTER_CUBIC)
|
||
h_kernel_big = cv2.getStructuringElement(cv2.MORPH_RECT, (75, 1))
|
||
h_lines_big = cv2.morphologyEx(big, cv2.MORPH_OPEN, h_kernel_big)
|
||
no_lines_big = cv2.subtract(big, h_lines_big)
|
||
_, binary6 = cv2.threshold(no_lines_big, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
||
# 去小噪点
|
||
kernel_small = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
|
||
binary6 = cv2.morphologyEx(binary6, cv2.MORPH_OPEN, kernel_small)
|
||
results.append(binary6)
|
||
|
||
# 方案7:原图直接用
|
||
results.append(gray)
|
||
|
||
# 方案8: 原始彩色图(ddddocr 有时对彩色图效果更好)
|
||
results.append(img)
|
||
|
||
return results
|
||
|
||
|
||
def main():
|
||
if len(sys.argv) < 2:
|
||
print("用法: python3 ocr_captcha.py <图片路径>", file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
img_path = sys.argv[1]
|
||
|
||
ocr = ddddocr.DdddOcr(show_ad=False)
|
||
|
||
# 先直接用原图试
|
||
with open(img_path, 'rb') as f:
|
||
raw_result = ocr.classification(f.read())
|
||
|
||
if raw_result and len(raw_result) == 4 and raw_result.isalnum():
|
||
print(raw_result)
|
||
return
|
||
|
||
# 多种预处理方案
|
||
preprocessed = preprocess(img_path)
|
||
|
||
best = raw_result
|
||
for i, processed in enumerate(preprocessed):
|
||
try:
|
||
if len(processed.shape) == 3:
|
||
_, buf = cv2.imencode('.png', processed)
|
||
else:
|
||
_, buf = cv2.imencode('.png', processed)
|
||
result = ocr.classification(buf.tobytes())
|
||
|
||
if result and len(result) == 4 and result.isalnum():
|
||
print(result)
|
||
return
|
||
|
||
# 记录最佳结果
|
||
if result and len(result) >= 3 and (not best or len(result) == 4):
|
||
best = result
|
||
except Exception:
|
||
continue
|
||
|
||
# 返回最佳结果
|
||
if best:
|
||
print(best[:4] if len(best) > 4 else best)
|
||
else:
|
||
print("FAIL", file=sys.stderr)
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|