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

MNIST数字OCR图像预处理:非正方形图像缩放画质损失解决方案咨询

嘿,我完全懂你现在的困扰——非正方形的数字图像缩放到MNIST要求的28x28时画质损失严重,这绝对是手写数字OCR预处理里的高频坑。我之前做类似项目时也踩过这个,给你几个亲测有效的解决方案:

解决方案1:保持比例的中心填充缩放

这个方法的核心是先按比例缩放图像,再把缩放后的图像居中放到28x28的空白画布上,避免直接拉伸导致的变形和画质损失。

具体步骤:

  • 计算裁剪后图像的宽高,找到能适配28x28的最大缩放比例(确保宽或高不超过28)
  • INTER_AREA插值缩放图像(这个插值方法对缩小图像的画质保留最好)
  • 创建28x28的空白背景(和MNIST的背景色一致,比如黑色)
  • 把缩放后的图像居中粘贴到空白画布上

代码示例:

import cv2
import numpy as np

def resize_with_padding(img):
    # 假设输入是二值化后的单通道图像(0=背景,255=数字)
    h, w = img.shape[:2]
    
    # 计算最大缩放比例,保证缩放后宽/高不超过28
    scale = min(28 / w, 28 / h)
    new_w, new_h = int(w * scale), int(h * scale)
    
    # 按比例缩放,INTER_AREA适合缩小图像
    resized_img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)
    
    # 创建28x28的空白背景(黑底)
    mnist_img = np.zeros((28, 28), dtype=np.uint8)
    
    # 计算居中偏移量
    x_offset = (28 - new_w) // 2
    y_offset = (28 - new_h) // 2
    
    # 将缩放后的图像粘贴到中心
    mnist_img[y_offset:y_offset+new_h, x_offset:x_offset+new_w] = resized_img
    
    return mnist_img
解决方案2:基于轮廓的正方形裁剪

如果你的数字本身在图像里不是居中的,先通过轮廓提取把数字区域调整为正方形再缩放,能最大程度保留数字细节。

具体步骤:

  • 提取数字的最大轮廓,获取它的最小外接矩形
  • 通过透视变换把这个矩形转换成正方形(不管原始矩形是宽还是高,都补成正方形)
  • 再把正方形图像缩放到28x28

代码示例:

def square_crop_then_resize(img):
    # 二值化(如果还没做的话)
    _, binary_img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
    
    # 提取数字轮廓
    contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    if not contours:
        return np.zeros((28, 28), dtype=np.uint8)
    
    # 取面积最大的轮廓(假设是目标数字)
    cnt = max(contours, key=cv2.contourArea)
    
    # 获取数字的最小外接矩形
    rect = cv2.minAreaRect(cnt)
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    
    # 计算矩形的宽高,确定正方形的边长
    width, height = int(rect[1][0]), int(rect[1][1])
    square_size = max(width, height)
    
    # 定义透视变换的目标点(正方形的四个角)
    src_pts = box.astype("float32")
    dst_pts = np.array([
        [0, square_size - 1],
        [0, 0],
        [square_size - 1, 0],
        [square_size - 1, square_size - 1]
    ], dtype="float32")
    
    # 计算透视变换矩阵并应用
    M = cv2.getPerspectiveTransform(src_pts, dst_pts)
    square_img = cv2.warpPerspective(binary_img, M, (square_size, square_size))
    
    # 缩放到28x28
    resized_img = cv2.resize(square_img, (28, 28), interpolation=cv2.INTER_AREA)
    return resized_img
解决方案3:预处理增强优化缩放效果

在缩放前对图像做一些增强操作,能减少缩放后的细节损失:

  • 自适应二值化:比普通二值化更适合光照不均的图像,能更好分离数字和背景:
    adaptive_binary = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
    
  • 去噪:用中值滤波或高斯滤波去掉图像中的噪点,避免缩放后噪点被放大:
    denoised_img = cv2.medianBlur(adaptive_binary, 3)
    
  • 形态学增强:用膨胀操作突出数字轮廓,减少缩放后的边缘模糊:
    kernel = np.ones((2,2), np.uint8)
    enhanced_img = cv2.dilate(denoised_img, kernel, iterations=1)
    

你可以把这些方法组合起来用,比如先做轮廓正方形裁剪,再做填充缩放,最后加预处理增强,效果会更好。另外,缩放时的插值方法要选对:缩小用INTER_AREA,如果是放大(比如原始数字很小)可以用INTER_CUBICINTER_LINEAR

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

火山引擎 最新活动