如何编程分割手写时间戳单数字以适配MNIST分类器?
解决手写时间戳数字分割的可靠方案
嗨,看你描述的情况——手动提取数字给MNIST分类器效果极佳,但自动分割卡壳,其实核心可以利用你时间戳**格式固定(HH:MM)**这个关键特点,完全绕开不稳定的轮廓检测,用固定区域分割的方法来解决,这比依赖轮廓靠谱多了。
下面是一步步可落地的实现思路,结合OpenCV来做:
1. 先做图像预处理,统一输入质量
不管是从整页报告截取的列,还是单条时间戳,先把图像标准化:
- 转灰度图:
cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - 用自适应二值化对付手写的明暗不均:
cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2),这样能把手写数字和背景分离开 - 用形态学操作去小噪声:
cv2.morphologyEx(thresh, cv2.MORPH_OPEN, np.ones((2,2), np.uint8)),避免小斑点干扰后续处理
2. 定位单个时间戳的整体区域
从你提供的截图来看,所有时间戳都在同一列,位置对齐得很好。如果是整页报告,你可以先按行截取每个时间戳的矩形区域——要么用轮廓检测定位每行的文本块,要么直接利用列的坐标范围(如果报告格式规整的话),把每个时间戳单独抠出来。
3. 按固定比例分割单个数字
因为时间格式是XX:XX,四个数字加中间的冒号,位置相对固定。你可以先手动测量几个样本里数字的宽度比例,比如:
- 假设单个时间戳的整体宽度是
w,把它分成5个等份(前两个数字各占1份,冒号占1份,后两个数字各占1份),如果冒号更窄,就微调比例(比如冒号占0.8份,每个数字占1.05份) - 用代码裁剪的话,示例如下:
# time_roi是已经截取好的单个时间戳二值图 roi_width = time_roi.shape[1] digit_segment_width = roi_width // 5 # 可根据实际样本微调这个数值 # 分割四个数字,跳过中间的冒号区域 digit_1 = time_roi[:, 0:digit_segment_width] digit_2 = time_roi[:, digit_segment_width:2*digit_segment_width] digit_3 = time_roi[:, 3*digit_segment_width:4*digit_segment_width] digit_4 = time_roi[:, 4*digit_segment_width:5*digit_segment_width]
4. 适配MNIST分类器的输入规格
MNIST的输入是28x28的灰度图,所以要把每个裁剪后的数字调整成对应规格:
- 先把数字区域resize到20x20(保留手写的细节,避免直接缩到28x28丢失特征)
- 创建一个28x28的空白画布,把20x20的数字放在画布中心,周围填充空白,这样数字的位置和MNIST训练样本的对齐方式一致,能提升识别准确率
5. 小偏移的优化方案
如果少数行的数字有轻微左右偏移,可以用投影直方图来校准:
- 对单个时间戳的二值图,计算水平方向的像素投影(统计每列的白色像素数),找到数字聚集的左右边界,然后调整分割的起始位置,确保每个数字都被完整裁剪
这个方法完全避开了轮廓检测的痛点——比如手写数字连笔、和冒号粘连的问题,毕竟你的时间格式是固定的,固定区域分割是最适配你场景的方案,和你手动PS提取的逻辑一致,效果应该会很好。
内容的提问来源于stack exchange,提问作者Henrik Lied
相关产品推荐
相关产品推荐

