如何分割手写句子图像获取单词坐标?Python/OpenCV实现遇阻求助
解决横竖混合手写文本的单词提取问题
一、先区分横纵文本行并聚类
横竖文本的轮廓宽高比差异明显,先通过这个特征区分,再聚类归同行/列,最后合并相邻字符框得到单词坐标:
import cv2 import numpy as np from sklearn.cluster import KMeans # 输入是你的预处理二值图 contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 过滤噪声轮廓 filtered_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > 50] horizontal_boxes = [] vertical_boxes = [] # 按宽高比区分横纵轮廓 for cnt in filtered_contours: x, y, w, h = cv2.boundingRect(cnt) aspect_ratio = w / h if aspect_ratio > 1.5: horizontal_boxes.append((x, y, w, h)) else: vertical_boxes.append((x, y, w, h)) # 横向文本行:按y坐标聚类,合并相邻单词 if horizontal_boxes: y_coords = np.array([box[1] for box in horizontal_boxes]).reshape(-1, 1) # 按y坐标分组,用聚类自动识别行数 cluster_count = len(set([round(y[0]/20) for y in y_coords])) kmeans = KMeans(n_clusters=cluster_count, random_state=42) kmeans.fit(y_coords) labels = kmeans.labels_ horizontal_lines = {} for i, label in enumerate(labels): horizontal_lines.setdefault(label, []).append(horizontal_boxes[i]) # 处理每行,合并相邻字符框为单词 for line in horizontal_lines.values(): line.sort(key=lambda b: b[0]) merged_words = [] current_box = line[0] for box in line[1:]: # 判定是否属于同一单词:y轴重叠度高 + x轴间距小 y_overlap = abs(current_box[1] + current_box[3] - box[1] - box[3]) < 20 x_gap = box[0] - (current_box[0] + current_box[2]) < 30 if y_overlap and x_gap: current_box = ( current_box[0], min(current_box[1], box[1]), box[0] + box[2] - current_box[0], max(current_box[3], box[3]) ) else: merged_words.append(current_box) current_box = box merged_words.append(current_box) # merged_words就是该行的单词坐标列表 print("横向单词坐标:", merged_words) # 纵向文本行:按x坐标聚类,合并相邻单词 if vertical_boxes: x_coords = np.array([box[0] for box in vertical_boxes]).reshape(-1, 1) cluster_count = len(set([round(x[0]/20) for x in x_coords])) kmeans = KMeans(n_clusters=cluster_count, random_state=42) kmeans.fit(x_coords) labels = kmeans.labels_ vertical_lines = {} for i, label in enumerate(labels): vertical_lines.setdefault(label, []).append(vertical_boxes[i]) for line in vertical_lines.values(): line.sort(key=lambda b: b[1]) merged_words = [] current_box = line[0] for box in line[1:]: x_overlap = abs(current_box[0] + current_box[2] - box[0] - box[2]) < 20 y_gap = box[1] - (current_box[1] + current_box[3]) < 30 if x_overlap and y_gap: current_box = ( min(current_box[0], box[0]), current_box[1], max(current_box[2], box[2]), box[1] + box[3] - current_box[1] ) else: merged_words.append(current_box) current_box = box merged_words.append(current_box) print("纵向单词坐标:", merged_words)
二、优化形态学预处理
你的当前处理对横竖文本兼顾不足,建议分方向用针对性核:
# 原预处理步骤 img = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 11, 5) img = cv2.medianBlur(img, 5) # 分方向膨胀,强化横纵文本的连通性 # 横向文本用水平核膨胀,让同一行字符连为整体 kernel_horizontal = np.ones((1, 8)) img_horizontal = cv2.morphologyEx(img, cv2.MORPH_DILATE, kernel_horizontal) # 纵向文本用垂直核膨胀 kernel_vertical = np.ones((8, 1)) img_vertical = cv2.morphologyEx(img, cv2.MORPH_DILATE, kernel_vertical) # 后续分别从img_horizontal和img_vertical提取轮廓即可
三、YOLO方案的实际价值
- 效果:如果有足量的横竖手写单词标注数据,YOLOv8n这类轻量模型能直接输出精准的单词 bounding box;数据量少的话,可在IAM等公开手写文本数据集上预训练,再用少量自有数据微调,鲁棒性远超传统CV方法。
- 速度:YOLOv8n在CPU上单帧处理耗时约几十毫秒,GPU上更快,完全满足批量或实时处理需求。
- 决策建议:如果你的场景手写风格多样,优先选YOLO;如果数据量极少、需求简单,传统CV方法调试成本更低。
四、额外调试技巧
- 倾斜校正:先用霍夫变换检测文本倾斜角度,旋转校正后再处理,能大幅降低横纵区分的难度。
- 噪声抑制:在中值滤波后,可加
cv2.morphologyEx(img, cv2.MORPH_CLOSE, np.ones((2,2)))填补字符内部的小空洞,提升轮廓质量。
内容的提问来源于stack exchange,提问作者Rodion Shkokov
相关产品推荐
相关产品推荐

