5 回复
宽度?
import cv2
import numpy as np
from skimage import morphology, measure
import matplotlib.pyplot as plt
def split_connected_chars(image_path):
"""
分割粘连字符的主要函数
参数:
image_path: 输入图像路径
返回:
separated_chars: 分割后的字符列表
"""
# 1. 读取图像并预处理
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
if img is None:
raise ValueError("无法读取图像,请检查路径")
# 二值化
_, binary = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 2. 形态学操作去除噪声
kernel = np.ones((3,3), np.uint8)
cleaned = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
cleaned = cv2.morphologyEx(cleaned, cv2.MORPH_OPEN, kernel)
# 3. 距离变换找分割点
dist_transform = cv2.distanceTransform(cleaned, cv2.DIST_L2, 5)
_, sure_fg = cv2.threshold(dist_transform, 0.5*dist_transform.max(), 255, 0)
sure_fg = np.uint8(sure_fg)
# 4. 分水岭算法分割
unknown = cv2.subtract(cleaned, sure_fg)
_, markers = cv2.connectedComponents(sure_fg)
markers = markers + 1
markers[unknown == 255] = 0
# 应用分水岭
markers = cv2.watershed(cv2.cvtColor(img, cv2.COLOR_GRAY2BGR), markers)
# 5. 提取分割后的字符
separated_chars = []
for label in np.unique(markers):
if label < 2: # 跳过背景
continue
# 创建掩码
mask = np.zeros_like(img, dtype=np.uint8)
mask[markers == label] = 255
# 找到边界框
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
x,y,w,h = cv2.boundingRect(contours[0])
char_img = img[y:y+h, x:x+w]
separated_chars.append(char_img)
return separated_chars
def alternative_method(image_path):
"""
备选方法:使用轮廓分析
适用于简单粘连情况
"""
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
_, binary = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 查找轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
chars = []
for contour in contours:
# 获取边界框
x,y,w,h = cv2.boundingRect(contour)
# 根据宽高比判断是否需要分割
aspect_ratio = w / h
if aspect_ratio > 1.5: # 可能是粘连字符
# 尝试垂直投影分割
vertical_projection = np.sum(binary[y:y+h, x:x+w], axis=0)
gaps = np.where(vertical_projection < np.max(vertical_projection)*0.1)[0]
if len(gaps) > 0:
# 在间隙处分割
split_points = [0] + list(gaps) + [w]
for i in range(len(split_points)-1):
start = split_points[i]
end = split_points[i+1]
if end - start > 5: # 最小宽度阈值
char = img[y:y+h, x+start:x+end]
chars.append(char)
else:
chars.append(img[y:y+h, x:x+w])
else:
chars.append(img[y:y+h, x:x+w])
return chars
# 使用示例
if __name__ == "__main__":
# 方法1:分水岭算法(适合复杂粘连)
chars1 = split_connected_chars("your_image.png")
# 方法2:轮廓分析(适合简单粘连)
chars2 = alternative_method("your_image.png")
# 显示结果
fig, axes = plt.subplots(1, len(chars1), figsize=(12,4))
for i, char in enumerate(chars1):
axes[i].imshow(char, cmap='gray')
axes[i].axis('off')
plt.show()
核心思路:
- 预处理:二值化+形态学去噪
- 距离变换:找到字符中心区域
- 分水岭算法:基于地形模拟分割粘连区域
- 轮廓提取:获取分割后的独立字符
简单建议: 先试分水岭,简单粘连用轮廓分析就够了。
个人感觉,如果图片没有旋转的话,可以把字符宽度和 Y 方向黑色的像素点的个数同时作为判断标准,一般粘连处像素点的个数不会太多
根据 y 方向的像素判断,来分割字符,PIL 可以解决
可以试试水滴算法及各种改良版:
http://www.intjit.org/cms/journal/volume/12/4/124_5.pdf




