基于OpenCV的古籍竖版文字分割与Python显示方案
基于OpenCV的古籍竖版文字分割与Python显示方案
一、竖版古籍文字处理的背景与挑战
古籍数字化是文化遗产保护的重要方向,而竖版排版古籍(如日文、中文古籍)因文字排列方向与现代横排文本不同,导致传统OCR技术难以直接应用。竖版文字的分割面临三大挑战:
- 文字方向识别:需准确判断文字是竖排还是横排;
- 字符粘连处理:古籍因年代久远可能存在墨迹晕染、纸张破损导致的字符粘连;
- 布局复杂性:竖排文本可能伴随行间注释、圈点符号等干扰元素。
以《论语》竖排古籍为例,其文字排列密度高,行间距小,传统基于投影法的分割方法易将竖排文字误判为横排连续区域。OpenCV的图像处理能力与Python的灵活性为解决此类问题提供了技术可能。
二、竖版文字分割的核心技术流程
1. 图像预处理:增强文字与背景对比度
古籍图像常存在光照不均、噪点干扰等问题,需通过以下步骤优化:
import cv2
import numpy as np
def preprocess_image(img_path):
# 读取图像并转为灰度图
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 自适应阈值二值化(处理光照不均)
binary = cv2.adaptiveThreshold(
gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV, 11, 2
)
# 去噪(中值滤波)
denoised = cv2.medianBlur(binary, 3)
return denoised
关键点:自适应阈值比全局阈值更能适应古籍图像的光照变化,cv2.ADAPTIVE_THRESH_GAUSSIAN_C
通过局部加权计算阈值,有效保留文字细节。
2. 竖排文字方向检测:基于霍夫变换的线条分析
通过检测图像中的垂直线条分布,可判断文字方向:
def detect_orientation(binary_img):
# 边缘检测(Canny)
edges = cv2.Canny(binary_img, 50, 150)
# 霍夫直线检测(参数优化:阈值80,最小线长20,最大线间隙10)
lines = cv2.HoughLinesP(
edges, 1, np.pi/180, 80,
minLineLength=20, maxLineGap=10
)
# 统计垂直线(角度接近90度)
vertical_count = 0
for line in lines:
x1, y1, x2, y2 = line[0]
angle = np.arctan2(y2-y1, x2-x1) * 180/np.pi
if 80 < abs(angle) < 100: # 接近垂直
vertical_count += 1
# 判断是否为竖排(垂直线占比超过60%)
total_lines = len(lines) if lines is not None else 0
return vertical_count / total_lines > 0.6 if total_lines > 0 else False
应用场景:若检测结果为竖排,后续处理需旋转图像或调整分割策略。例如,将图像顺时针旋转90度后,可复用横排文字分割算法。
3. 竖排文字分割:基于投影法的行切割
竖排文字的行切割需将图像旋转后按列投影:
def segment_vertical_text(binary_img):
# 旋转图像(假设已检测为竖排)
rows, cols = binary_img.shape
rotated = cv2.rotate(binary_img, cv2.ROTATE_90_CLOCKWISE)
# 列投影(统计每列的黑色像素数)
projection = np.sum(rotated == 0, axis=0)
# 寻找分割点(投影值低于阈值的列)
threshold = np.mean(projection) * 0.3
split_points = []
start = 0
for i in range(1, len(projection)):
if projection[i] < threshold and projection[i-1] >= threshold:
split_points.append(i)
# 切割字符区域
characters = []
prev = 0
for point in split_points:
char = rotated[:, prev:point]
characters.append(char)
prev = point
return characters
优化方向:针对字符粘连问题,可结合形态学操作(如闭运算)连接断裂笔画,或使用连通域分析(cv2.connectedComponents
)进一步细分。
三、Python显示竖排文字的两种方案
方案1:旋转图像后显示(简单但效率低)
def display_rotated(img_path):
img = cv2.imread(img_path)
# 旋转90度显示竖排
rotated = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
cv2.imshow("Vertical Text", rotated)
cv2.waitKey(0)
适用场景:快速预览竖排效果,但无法直接处理文字识别。
方案2:逐字符显示并控制方向(推荐)
结合Pillow库实现更灵活的显示:
from PIL import Image, ImageDraw, ImageFont
def display_vertical_chars(characters, output_path="vertical_text.png"):
# 假设characters为分割后的字符列表(需先转为PIL格式)
max_height = max(char.shape[0] for char in characters)
total_width = sum(char.shape[1] for char in characters)
# 创建空白画布(白色背景)
img = Image.new("RGB", (total_width, max_height), (255, 255, 255))
draw = ImageDraw.Draw(img)
# 加载字体(需指定竖排字体文件,如NotoSansCJKjp-Regular.otf)
try:
font = ImageFont.truetype("NotoSansCJKjp-Regular.otf", 24)
except:
font = ImageFont.load_default()
# 逐字符绘制(从右到左)
x_offset = 0
for char in characters:
# 将OpenCV格式转为PIL
char_pil = Image.fromarray(255 - char) # 反色(OpenCV二值图为黑底白字)
img.paste(char_pil, (x_offset, 0))
x_offset += char.shape[1]
img.save(output_path)
img.show()
关键细节:需处理字体文件路径问题,建议使用支持竖排的CJK字体(如Noto Sans CJK)。若字体缺失,可先用默认字体显示,再提示用户下载。
四、实际应用中的优化建议
- 数据增强:对古籍图像进行旋转、缩放、噪点添加等操作,提升模型鲁棒性。
- 深度学习辅助:结合U-Net等分割网络,处理复杂粘连字符。
- 后处理规则:添加词典校验(如日文假名频率统计)修正分割错误。
五、总结与展望
本文提出的Python+OpenCV方案通过预处理、方向检测、投影分割和显示优化,实现了竖版古籍文字的有效处理。未来可探索将传统图像处理与深度学习结合,例如用CRNN模型直接识别竖排文字,进一步提升自动化水平。对于开发者而言,掌握此类技术不仅能解决古籍数字化痛点,还可迁移至其他竖排文本场景(如海报、手写笔记)。