基于OpenCV及Python的数独问题识别与求解(三)数字提取

时隔10个月的时间,我终于决定继续更新这个系列博客……
所有代码已经放在GitHub,包括用mnist数据集做手写识别的尝试,文档和注释等正在完善,程序的结构也在优化。

在上一篇文章中得到了清晰、标准的数独问题图像,为下一步提取并识别数字做好了准备。

处理过程

数字提取的主要步骤是:将含有数字的图像,分为 9*9 即 81 个大小相同的方格,遍历这个81个位置,判断每个方格中是否有数字,记录数字所在位置(0~80),保存在 indexes_numbers 中,数字储存在数组sudoku中,主要的代码如下:

# 识别并记录序号 (SUDOKU_SIZE = 9)
indexes_numbers = []
for i in range(SUDOKU_SIZE):
    for j in range(SUDOKU_SIZE):
        img_number = img_puzzle[i * GRID_HEIGHT:(i + 1) * GRID_HEIGHT][:, j * GRID_WIDTH:(j + 1) * GRID_WIDTH]
        hasNumber, sudoku[i * 9 + j, :] = extractNumber.recognize_number(img_number, i, j)
        if hasNumber:
            indexes_numbers.append(i * 9 + j)

Recognize_number() 函数

数字提取部分的核心函数就是 Recognize_number(),用来判断方格中是否有数字,并存储数字,代码如下:

def recognize_number(im_number, x, y):
    """
    判断当前方格是否存在数字并存储该数字
    :param im_number: 方格图像
    :param x: 方格横坐标 (0~9)
    :param y: 方格纵坐标 (0~9)
    :return: 是否有数字,数字的一维数组
    """
    # 提取并处理方格图像
    [im_number_thresh, n_active_pixels] = extract_grid(im_number)

    # 条件1:非零像素大于设定的最小值
    if n_active_pixels > N_MIN_ACTIVE_PIXELS:

        # 找出外接矩形
        [x_b, y_b, w, h] = find_biggest_bounding_box(im_number_thresh)

        # 计算矩形中心与方格中心距离
        cX = x_b + w // 2
        cY = y_b + h // 2
        d = np.sqrt(np.square(cX - GRID_WIDTH // 2) + np.square(cY - GRID_HEIGHT // 2))

        # 条件2: 外接矩形中心与方格中心距离足够小
        if d < GRID_WIDTH // 4:

            # 取出方格中数字
            number_roi = im_number[y_b:y_b + h, x_b:x_b + w]

            # 扩充数字图像为正方形,边长取长宽较大者
            h1, w1 = np.shape(number_roi)
            if h1 > w1:
                number = np.zeros(shape=(h1, h1))
                number[:, (h1 - w1) // 2:(h1 - w1) // 2 + w1] = number_roi
            else:
                number = np.zeros(shape=(w1, w1))
                number[(w1 - h1) // 2:(w1 - h1) // 2 + h1, :] = number_roi

            # 将数字缩放为标准大小
            number = cv2.resize(number, (NUM_WIDTH, NUM_HEIGHT), interpolation=cv2.INTER_LINEAR)
			# 二值化
            retVal, number = cv2.threshold(number, 50, 255, cv2.THRESH_BINARY)

            # 转换为1维数组并返回
            return True, number.reshape(1, NUM_WIDTH * NUM_HEIGHT)

    # 没有数字,则返回全零1维数组
    return False, np.zeros(shape=(1, NUM_WIDTH * NUM_HEIGHT))
  1. 提取当前小方格的图像,并对方格图像进行预处理操作,这里用一个函数 extract_grid() 实现,具体代码见下文。函数返回方格图像、方格二值图像、方格二值图像非零像素数

  2. 条件1,判断方格二值图像的非零像素数是否大于阈值,若大于阈值,认为可能存在数字。

  3. 使用 find_biggest_bounding_box() 函数找出数字的外接矩形,具体代码见下文。函数返回外接矩形的左上坐标、宽度、高度

  4. 计算矩形中心,计算矩形中心与方格中心距离。

  5. 条件2,判断矩形中心是否离方格中心足够近,若小于阈值(方格宽度的四分之一),认为存在数字。

  6. 使用 Python 的切片取出方格中数字,之后扩充数字图像为正方形,正方形边长取长宽较大者。

  7. 将数字缩放为统一的标准大小。注意! cv2.resize() 函数在缩放二值化的图像时,默认插值方法输出不再是二值图像,若令参数 interpolation=cv2.INTER_NEAREST 输出还是二值图像,但因为像素较低效果并不好。因此涉及缩放的地方均使用原图而不是使用二值化后的图像。

  8. 最后,转换为一维数组返回。

extract_grid() 函数

该函数用来提取小方格的图像,然后对其进行预处理。

def extract_grid(im_number):
    """
    将校正后图像划分为9x9的棋盘,取出小方格;二值化;去除离中心较远的像素(排除边框干扰);统计非零像素数(判断方格中是否有数字)
    :param im_number: 方格图像
    :param x: 方格横坐标 (0~9)
    :param y: 方格纵坐标 (0~9)
    :return: im_number_thresh: 二值化及处理后图像
             n_active_pixels: 非零像素数
    """

    # 二值化
    retVal, im_number_thresh = cv2.threshold(im_number, 150, 255, cv2.THRESH_BINARY)

    # 去除离中心较远的像素点(排除边框干扰)
    for i in range(im_number.shape[0]):
        for j in range(im_number.shape[1]):
            dist_center = np.sqrt(np.square(GRID_WIDTH // 2 - i) + np.square(GRID_HEIGHT // 2 - j))
            if dist_center > GRID_WIDTH // 2 - 2:
                im_number_thresh[i, j] = 0

    # 统计非零像素数,以判断方格中是否有数字
    n_active_pixels = cv2.countNonZero(im_number_thresh)

    return [im_number_thresh, n_active_pixels]
  1. 取出坐标为 (x,y) 的方格图像 im_number。使用Python中的二维数组切片功能,类似OpenCV中感兴趣区域(ROI)功能,可以方便地提取图像的某一部分,对图像进行分割。
  2. 方格图像自适应二值化。
  3. 由于校正后图像中有白色边框,在分割后会在每一个方格四周存在白色像素,需要去除离中心较远的像素点(排除边框干扰)。这里遍历小方格每一个像素,判断该像素与中心的距离,远于阈值的将其值设为0,简单来说就是以方格中心为圆心画一个圆,圆内的保留,圆外的去除。
  4. 使用 cv2.countNonZero() 统计现在方格中的非零像素数。
  5. 将当前坐标方格图像和非零像素数返回。

find_biggest_bounding_box() 函数

该函数用来找出数字的外接矩形,输入参数为二值图像,输出为外接矩形的左上坐标和宽度、高度。

def find_biggest_bounding_box(im_number_thresh):
    """
    找出小方格中外接矩形面积最大的轮廓,返回其外接矩形参数
    :param im_number_thresh: 当前方格的二值化及处理后图像
    :return: 外接矩形参数(左上坐标及长和宽)
    """
    # 轮廓检测
    b, contour, hierarchy1 = cv2.findContours(im_number_thresh.copy(),
                                              cv2.RETR_EXTERNAL,
                                              cv2.CHAIN_APPROX_SIMPLE)
    # 找出外接矩形面积最大的轮廓
    biggest_bound_rect = []
    bound_rect_max_size = 0
    for i in range(len(contour)):
        bound_rect = cv2.boundingRect(contour[i])
        size_bound_rect = bound_rect[2] * bound_rect[3]
        if size_bound_rect > bound_rect_max_size:
            bound_rect_max_size = size_bound_rect
            biggest_bound_rect = bound_rect

    # 将外接矩形扩大一个像素
    x_b, y_b, w, h = biggest_bound_rect
    x_b = x_b - 1
    y_b = y_b - 1
    w = w + 2
    h = h + 2
    return [x_b, y_b, w, h]
  1. 对方格二值图像使用轮廓检测。
  2. 遍历找到的轮廓,使用 cv2.boundingRect() 去轮廓的外接矩形, 求外接矩形面积,找出最大的面积。
  3. 将外接矩形扩大一个像素,有利于识别,返回外接矩形坐标、长、宽。

效果展示

通过以上步骤,可找出哪些方格有数字,并将数字存储,如下图所示。

提取数字过程

1->2 去除了四周的边框干扰;

2->3 找出外接矩形面积最大的轮廓并提取;

3->4 扩充为正方形,再进行缩放二值化等;

最终数字提取结果如图所示。
数字提取效果

提取了数字,并统一成标准大小,下一步就可以进行数字识别了。事实上数字提取的过程还有一些需要改进的地方:比如从原图上提取出的数字,未缩放前为29x29,而标准大小定义为20x20,这样一缩放就会损失信息,有可能影响识别精度,等等。

上文提到的方法在很多细节上都可以改进,欢迎交流。

  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 基于opencv-python的车牌识别是一种利用计算机视觉技术实现的车牌自动识别系统,通过将车牌图像输入到系统中,利用opencv-python库中的图像处理算法和人工智能算法进行处理,最终得到车牌号码。 云盘下载是指将该系统提供的代码和相关文件上传到云盘,方便用户下载和使用。由于该系统所需的文件较大,因此使用云盘下载可以更加方便和快捷。 基于opencv-python的车牌识别的优点是准确度高,识别速度快,且能够适应不同光照和角度的情况,因此具有广泛的应用价值。例如,在道路交通安全管理中,可以利用该系统自动识别车牌号码,提高交通事故的处理效率;在车辆卡口管理中,可以利用该系统对过往车辆进行车牌识别,实现自动化的流量统计和监管。 总之,基于opencv-python的车牌识别是一种实用的计算机视觉技术,通过云盘下载可以更加方便快捷地实现应用。 ### 回答2: 基于opencv-python的车牌识别是一种使用Python编程语言和OpenCV计算机视觉库进行车牌识别的技术。这种技术可以通过静态图像或视频流识别车牌,以实现智能交通、车辆管理等目的。这种技术在实际应用中可以提高车辆追踪的效率,减少违章交通行为和交通事故的发生。 在进行车牌识别时,需要通过使用Python编程语言和OpenCV图像处理工具对图片进行处理,从而检测出车牌的位置和角度。接下来,需要使用深度学习算法对检测到的车牌信息进行识别,并将车牌中的字符进行提取和解析。 为方便下载和使用,可以将车牌识别的代码和所需的依赖文件打包成一个ZIP或RAR文件,并上传到云盘中。这样,用户可以通过下载并解压缩文件来快速使用车牌识别技术,同时可以在实际应用中根据需要进行二次开发和优化。近年来,车牌识别技术在智能交通和社会管理领域得到广泛应用,帮助提高城市管理和公共安全管理水平。 ### 回答3: 基于opencv-python的车牌识别是一种将图像处理技术应用于车牌识别的方法,利用opencv-python这一开源计算机视觉库,结合现代物联网技术,可实现快速、精准、高效的车牌识别。 在使用基于opencv-python的车牌识别前,我们需要在云盘上下载相关程序,安装好opencv-python和其他相关依赖库。在准备工作完成后,我们可以将待识别的车牌图像导入到识别系统中。车牌图像可以是通过摄像头获取的实时图像,也可以是已保存在本地的图片。 基于opencv-python的车牌识别系统会自动对车牌图像进行预处理,包括二值化、去噪、定位等步骤。在车牌图像预处理完成后,系统会提取车牌上的字符信息,并基于机器学习算法对字符进行识别。最终,系统将结果输出到显示屏上,同时也可以保存到计算机本地或者上传到云端进行存储。 基于opencv-python的车牌识别技术已经日益成熟,应用也越来越广泛。在交通管理、车辆识别、停车管理等领域都可以发挥重要作用。与传统的车牌识别技术相比,基于opencv-python的车牌识别技术更加精准、高效、便捷。随着互联网技术的不断发展和普及,相信这一技术的应用前景将会更加广阔。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值