基于大空白的扫描图像切片

sib*_*raj 5 python opencv image image-processing computer-vision

我打算把这个PDF 文档中的问题分开。挑战在于问题的间隔不是有序的。例如第一个问题占了整整一页,第二个问题也占了一页,第三个问题和第四个问题一起占了一页。如果我必须手动切片,那将需要很长时间。所以,我想把它分成图像并对其进行处理。是否有可能拍摄这样的图像

单幅图像

并像这样分成单独的组件?

在此输入图像描述

在此输入图像描述

nat*_*ncy 8

这是dilate的典型情况。这个想法是相邻的文本对应于同一问题,而较远的文本是另一个问题的一部分。每当您想要将多个项目连接在一起时,您可以扩大它们以将相邻轮廓连接成单个轮廓。这是一个简单的方法:

  1. 获取二值图像。 加载图像,转换为灰度高斯模糊,然后Otsu阈值得到二值图像。

  2. 消除小噪音和伪影。我们创建一个矩形内核进行变形开放,以消除图像中的小噪声和伪影。

  3. 将相邻的单词连接在一起。我们创建一个更大的矩形内核并进行扩展以将各个轮廓合并在一起。

  4. 检测问题。从这里我们找到轮廓,使用从上到下对轮廓进行排序imutils.sort_contours(),用最小轮廓面积进行过滤,获得矩形边界矩形坐标突出显示矩形轮廓。然后,我们使用 Numpy 切片裁剪每个问题并保存 ROI 图像。


获得二值图像的大津阈值

在此输入图像描述

这就是有趣的部分发生的地方。我们假设相邻的文本/字符是同一问题的一部分,因此我们将各个单词合并成一个轮廓。问题是靠近在一起的单词的一部分,因此我们扩展以将它们连接在一起。

在此输入图像描述

个别问题以绿色突出显示

在此输入图像描述

热门问题

在此输入图像描述

底部问题

在此输入图像描述

已保存的 ROI 问题(假设从上到下)

在此输入图像描述

代码

import cv2
from imutils import contours

# Load image, grayscale, Gaussian blur, Otsu's threshold
image = cv2.imread('1.png')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7,7), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Remove small artifacts and noise with morph open
open_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, open_kernel, iterations=1)

# Create rectangular structuring element and dilate
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
dilate = cv2.dilate(opening, kernel, iterations=4)

# Find contours, sort from top to bottom, and extract each question
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
(cnts, _) = contours.sort_contours(cnts, method="top-to-bottom")

# Get bounding box of each question, crop ROI, and save
question_number = 0
for c in cnts:
    # Filter by area to ensure its not noise
    area = cv2.contourArea(c)
    if area > 150:
        x,y,w,h = cv2.boundingRect(c)
        cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
        question = original[y:y+h, x:x+w]
        cv2.imwrite('question_{}.png'.format(question_number), question)
        question_number += 1

cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('image', image)
cv2.waitKey()
Run Code Online (Sandbox Code Playgroud)

  • @sibikanagaraj 是的,如果有噪音,那么您可以通过形态开口去除小伪影。默认情况下,没有关于如何选择 ROI 的顺序。您可以使用 [imutils](https://github.com/PyImageSearch/imutils) 库从上到下对轮廓进行排序。我已经更新了代码以消除噪音并根据从上到下收集问题。您可以使用“pip install imutils”安装该便利库 (2认同)

Rot*_*tem 7

我们可以使用(主要)形态学运算来解决它:

  • 将输入图像读取为灰度。
  • 通过反转应用阈值。
    自动阈值使用cv2.THRESH_OTSU效果很好。
  • 应用开放形态学操作来去除小伪影(使用内核np.ones(1, 3)
  • 使用非常长的水平内核进行水平扩展 - 从文本行中制作水平线。
  • 垂直应用闭合 - 创建两个大簇。
    垂直核的大小应根据典型间隙进行调整。
  • 通过统计数据查找连接的组件。
  • 迭代连接的组件并在垂直方向裁剪相关区域。

完整代码示例:

import cv2
import numpy as np

img = cv2.imread('scanned_image.png', cv2.IMREAD_GRAYSCALE)  # Read image as grayscale

thesh = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)[1]  # Apply automatic thresholding with inversion.

thesh = cv2.morphologyEx(thesh, cv2.MORPH_OPEN, np.ones((1, 3), np.uint8))  # Apply opening morphological operation for removing small artifacts.

thesh = cv2.dilate(thesh, np.ones((1, img.shape[1]), np.uint8))  # Dilate horizontally - make horizontally  lines out of the text.

thesh = cv2.morphologyEx(thesh, cv2.MORPH_CLOSE, np.ones((50, 1), np.uint8))  # Apply closing vertically - create two large clusters

nlabel, labels, stats, centroids = cv2.connectedComponentsWithStats(thesh, 4)  # Finding connected components with statistics

parts_list = []

# Iterate connected components:
for i in range(1, nlabel):
    top = int(stats[i, cv2.CC_STAT_TOP])  # Get most top y coordinate of the connected component
    height = int(stats[i, cv2.CC_STAT_HEIGHT])  # Get the height of the connected component

    roi = img[top-5:top+height+5, :]  # Crop the relevant part of the image (add 5 extra rows from top and bottom).
    parts_list.append(roi.copy()) # Add the cropped area to a list

    cv2.imwrite(f'part{i}.png', roi)  # Save the image part for testing
    cv2.imshow(f'part{i}', roi)  # Show part for testing

# Show image and thesh testing
cv2.imshow('img', img)
cv2.imshow('thesh', thesh)

cv2.waitKey()
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)

结果:

阶段1:
在此输入图像描述

第二阶段:
在此输入图像描述

第三阶段:
在此输入图像描述

第四阶段:
在此输入图像描述

顶部区域:
在此输入图像描述

底部面积:
在此输入图像描述