如何用OpenCV程序化识别含异色字符行的验证码?
基于OpenCV的异色字符验证码识别方案
核心思路
目标是定位存在异色字符的行并返回对应行号,核心逻辑是逐行分析字符串区域的颜色一致性——若某行存在与其他字符颜色差异显著的字符,则标记该行。由于前景清晰,无需AI模型,仅用OpenCV基础图像处理功能即可实现。
关键步骤及对应OpenCV功能
1. 图像预处理与背景过滤
- 用
cv2.cvtColor()将图像从BGR转为HSV色彩空间(HSV对颜色差异的区分度更高,优于RGB)。 - 若背景干扰极小,可直接跳过复杂滤波;若需强化前景,用
cv2.threshold()对灰度图进行二值化,或用cv2.inRange()基于颜色范围提取前景字符的掩码,过滤背景像素。
2. 行区域分割
- 利用垂直投影分割字符行:将图像转为灰度图后,用
cv2.reduce()计算每行的像素总和(垂直投影),根据投影值的峰值定位每行的上下边界,得到所有行的区域范围。 - 定位
#列:对第一行的灰度图计算水平投影,找到#字符所在的列(投影值峰值对应的列),以此为界分割每行的行号区域和字符串区域。
3. 逐行颜色差异检测
- 对每行的字符串区域,提取H通道(HSV的色调通道)数据,用
cv2.calcHist()统计色调分布,找到该行字符的主色调。 - 统计区域内与主色调差异超过阈值的像素数量,若占比超过设定比例(如5%),则判定该行存在异色字符。
- 更精准的方式:用
cv2.findContours()提取每个字符的轮廓,对每个字符的包围盒计算色调均值,对比该行内所有字符的均值,找到差异显著的字符。
4. 行号识别
- 对标记的目标行,提取
#列左侧的行号区域,用cv2.threshold()二值化后,通过模板匹配(cv2.matchTemplate())识别数字(提前制作0-9的数字模板),或用简单的像素特征统计(如数字的像素占比)快速识别。
简化实现示例代码
import cv2 import numpy as np def get_target_line(img_path): # 读取图像 img = cv2.imread(img_path) hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 垂直投影分割行 row_projection = cv2.reduce(gray_img, 1, cv2.REDUCE_SUM, dtype=cv2.CV_32S) row_threshold = np.max(row_projection) * 0.5 row_ranges = [] start_row = None for idx, val in enumerate(row_projection): if val > row_threshold and start_row is None: start_row = idx elif val <= row_threshold and start_row is not None: row_ranges.append((start_row, idx)) start_row = None if start_row is not None: row_ranges.append((start_row, len(row_projection)-1)) # 定位#列(取第一行的水平投影峰值) first_row_gray = gray_img[row_ranges[0][0]:row_ranges[0][1], :] col_projection = cv2.reduce(first_row_gray, 0, cv2.REDUCE_SUM, dtype=cv2.CV_32S) hash_col = np.argmax(col_projection) target_line_num = -1 # 逐行分析颜色差异 for line_idx, (top, bottom) in enumerate(row_ranges): # 提取字符串区域的H通道 str_h_channel = hsv_img[top:bottom, hash_col+1:, 0] # 统计主色调 hue_hist = cv2.calcHist([str_h_channel], [0], None, [180], [0, 180]) main_hue = np.argmax(hue_hist) # 计算异色像素占比 diff_pixel_count = np.sum(np.abs(str_h_channel - main_hue) > 20) total_pixels = str_h_channel.size if diff_pixel_count / total_pixels > 0.05: # 识别行号(示例简化处理,实际可替换为模板匹配) num_region = gray_img[top:bottom, :hash_col] _, num_bin = cv2.threshold(num_region, 127, 255, cv2.THRESH_BINARY_INV) # 此处仅为示例,实际需根据数字样式调整识别逻辑 target_line_num = line_idx + 1 break return target_line_num
内容的提问来源于stack exchange,提问作者Newbie
相关产品推荐
相关产品推荐

