You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何分割手写句子图像获取单词坐标?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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 18:20:27