You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在OpenCV Python中分割连接的轮廓与粘连字符

嘿,我来帮你搞定粘连字符的分割问题!针对你要把两张图分别分割成MENTIN的需求,咱们可以从OpenCV的几个实用技巧入手,调整你的现有代码并补充关键步骤:

一、先优化你的预处理步骤

你当前用了腐蚀操作,但5x5的核+2次迭代可能会过度破坏字符形状。建议先做开运算(先腐蚀再膨胀)来去除小噪点,同时尽量保留字符完整性:

import cv2
import numpy as np

# 读取图像
im_in = cv2.imread("testResize.jpg", cv2.IMREAD_GRAYSCALE)
# 自动阈值化(比固定220更适配不同图像)
th, im_th = cv2.threshold(im_in, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

# 开运算:消除小噪点,避免干扰后续分割
kernel = np.ones((2,2), np.uint8)
im_preprocessed = cv2.morphologyEx(im_th, cv2.MORPH_OPEN, kernel, iterations=1)
二、针对不同粘连程度的分割方法

方法1:距离变换+分水岭算法(适合重度粘连,比如MENT

这个方法能精准识别每个字符的核心区域,再通过分水岭算法分割粘连的边缘:

# 距离变换:计算每个白色像素到最近黑色背景的距离,突出字符中心
dist_transform = cv2.distanceTransform(im_preprocessed, cv2.DIST_L2, 5)
# 阈值化得到字符核心区域(种子点)
ret, sure_fg = cv2.threshold(dist_transform, 0.5 * dist_transform.max(), 255, 0)
sure_fg = np.uint8(sure_fg)

# 确定背景区域
sure_bg = cv2.dilate(im_preprocessed, kernel, iterations=3)
# 找到粘连的边缘未知区域
unknown = cv2.subtract(sure_bg, sure_fg)

# 标记种子点
ret, markers = cv2.connectedComponents(sure_fg)
# 背景标记为1(避免和未知区域的0混淆)
markers = markers + 1
markers[unknown == 255] = 0

# 应用分水岭分割
markers = cv2.watershed(cv2.cvtColor(im_in, cv2.COLOR_GRAY2BGR), markers)
# 分割线会被标记为-1,这里用红色显示分割线(可选)
im_in[markers == -1] = [255, 0, 0]

# 提取每个字符的轮廓并分割
contours, _ = cv2.findContours(sure_fg.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    # 裁剪单个字符
    single_char = im_th[y:y+h, x:x+w]
    cv2.imshow('Single Character', single_char)
    cv2.waitKey(0)

cv2.destroyAllWindows()

方法2:垂直投影分割(适合轻度粘连,比如IN

通过计算每列的白色像素数量,找到背景空白列作为分割线,操作更简单:

# 计算垂直投影:每列的白色像素总和
vertical_proj = np.sum(im_preprocessed, axis=0)

# 寻找分割点:连续的背景列(像素总和为0)
split_points = []
in_blank = False
for col_idx, pixel_sum in enumerate(vertical_proj):
    if pixel_sum == 0 and not in_blank:
        split_points.append(col_idx)
        in_blank = True
    elif pixel_sum != 0:
        in_blank = False

# 补充图像左右边界,确保覆盖所有字符
split_points = [0] + split_points + [im_preprocessed.shape[1]]

# 遍历分割点,裁剪每个字符
for i in range(len(split_points)-1):
    start_col = split_points[i]
    end_col = split_points[i+1]
    # 过滤过窄的无效区域
    if end_col - start_col < 5:
        continue
    single_char = im_th[:, start_col:end_col]
    cv2.imshow('Single Character', single_char)
    cv2.waitKey(0)

cv2.destroyAllWindows()
三、实用小建议
  • 阈值参数:如果固定阈值效果不好,用cv2.THRESH_OTSU自动计算阈值(代码里已经加了),适配不同光照的图像
  • 核大小:根据字符的实际尺寸调整形态学核的大小,比如小字符用(2,2),大字符用(3,3),避免过度变形
  • 轮廓筛选:可以通过轮廓的面积、宽高比过滤掉噪点轮廓,只保留符合字符大小的区域

内容的提问来源于stack exchange,提问作者Menaga

火山引擎 最新活动