检测文本页上的初始/草图

doinxwow  于 2021-07-13  发布在  Java
关注(0)|答案(3)|浏览(341)

我想在下一页上获得首字母(“h”)周围框的坐标(以及与其他首字母类似的坐标,因此opencv模板匹配不是一个选项):

在本教程之后,我尝试使用opencv等高线解决问题:

import cv2
import matplotlib.pyplot as plt

page = "image.jpg"

# read the image

image = cv2.imread(page)

# convert to RGB

image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# convert to grayscale

gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

# create a binary thresholded image

_, binary = cv2.threshold(gray, 0,150,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# find the contours from the thresholded image

contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# draw all contours

image = cv2.drawContours(image, contours, 3, (0, 255, 0), 2)
plt.savefig("result.png")

结果当然不是我想要的:

有没有人知道一个可行的算法(可能还有一个实现),可以为我的任务提供一个简单的解决方案?

xmjla07d

xmjla07d1#

你可以通过过滤你的轮廓来找到目标区域。现在,您至少可以使用两个筛选条件。一个是过滤 area -也就是说,丢弃太小和太大的轮廓,直到你得到你要找的轮廓。另一种是通过计算 extent 每一个轮廓。这个 extent 等高线面积与其边框面积之比。你在寻找一个正方形的轮廓,所以 extent 应该接近 1.0 .
让我们看看代码:


# imports:

import cv2
import numpy as np

# Reading an image in default mode:

inputImage = cv2.imread(path + fileName)

# Deep copy for results:

inputImageCopy = inputImage.copy()

# Convert RGB to grayscale:

grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

# Get binary image via Otsu:

_, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

代码的第一部分为您获取一个二进制图像,您可以将其用作计算轮廓的遮罩:

现在,让我们过滤轮廓。让我们使用 area 先接近。你需要定义一系列 minimum area 以及 maximum area 过滤不在此范围内的所有内容。我已经试探性地从 30000 px至 150000 二甲苯:


# Find the contours on the binary image:

contours, hierarchy = cv2.findContours(binaryImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# Look for the outer bounding boxes (no children):

for _, c in enumerate(contours):

    # Get blob area:
    currentArea = cv2.contourArea(c)
    print("Contour Area: "+str(currentArea))

    # Set an area range:
    minArea = 30000
    maxArea = 150000

    if minArea < currentArea < maxArea:

        # Get the contour's bounding rectangle:
        boundRect = cv2.boundingRect(c)

        # Get the dimensions of the bounding rect:
        rectX = boundRect[0]
        rectY = boundRect[1]
        rectWidth = boundRect[2]
        rectHeight = boundRect[3]

        # Set bounding rect:
        color = (0, 0, 255)
        cv2.rectangle( inputImageCopy, (int(rectX), int(rectY)),
                       (int(rectX + rectWidth), int(rectY + rectHeight)), color, 2 )

        cv2.imshow("Rectangles", inputImageCopy)
        cv2.waitKey(0)

一旦你成功地过滤了这个区域,你就可以计算出 bounding rectangle 轮廓的 cv2.boundingRect . 可以检索边界矩形的 x , y (左上)坐标及其 width 以及 height . 之后,只需在原始输入的深拷贝上绘制矩形。
现在,让我们看看第二个选项,使用轮廓的 extent . 这个 for 循环修改如下:


# Look for the outer bounding boxes (no children):

for _, c in enumerate(contours):

    # Get blob area:
    currentArea = cv2.contourArea(c)

    # Get the contour's bounding rectangle:
    boundRect = cv2.boundingRect(c)

    # Get the dimensions of the bounding rect:
    rectX = boundRect[0]
    rectY = boundRect[1]
    rectWidth = boundRect[2]
    rectHeight = boundRect[3]

    # Calculate extent:
    extent = float(currentArea)/(rectWidth *rectHeight)
    print("Extent: " + str(extent))

    # Set the extent filter, look for an extent close to 1.0:
    delta = abs(1.0 - extent)
    epsilon = 0.1

    if delta < epsilon:

        # Set bounding rect:
        color = (0, 0, 255)
        cv2.rectangle( inputImageCopy, (int(rectX), int(rectY)),
                       (int(rectX + rectWidth), int(rectY + rectHeight)), color, 2 )

        cv2.imshow("Rectangles", inputImageCopy)
        cv2.waitKey(0)

两种方法都会产生这样的结果:

5t7ly7z5

5t7ly7z52#

你差点就拿到了。你只需要过滤面积和纵横比轮廓。下面是我在python/opencv中的方法。
输入:

import cv2
import numpy as np

# read image as grayscale

img = cv2.imread('syriados.jpg')

# convert to grayscale

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold to binary

# thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)[1]

thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

# invert threshold

thresh = 255 - thresh

# apply morphology to remove small white regions and to close the rectangle boundary

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# find contours

result = img.copy()
cntrs = cv2.findContours(morph, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]

# filter on area and aspect ratio

for c in cntrs:
    area = cv2.contourArea(c)
    x,y,w,h = cv2.boundingRect(c)
    if area > 10000 and abs(w-h) < 100:
        cv2.drawContours(result, [c], 0, (0,0,255), 2)

# write results

cv2.imwrite("syriados_thresh.jpg", thresh)
cv2.imwrite("syriados_morph.jpg", morph)
cv2.imwrite("syriados_box.jpg", result)

# show results

cv2.imshow("thresh", thresh)
cv2.imshow("morph", morph)
cv2.imshow("result", result)
cv2.waitKey(0)

阈值图像:

形态学图像:

生成的轮廓图像:

06odsfpq

06odsfpq3#

要得到这样的结果:

您需要检测图像中面积从第二到最大的轮廓,因为面积最大的轮廓就是图像的边界。
所以有了等高线列表,我们可以通过内置的 sorted 方法,使用 cv2.contourArea 方法作为自定义键:

import cv2
import numpy as np

def process(img):
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    img_blur = cv2.GaussianBlur(img_gray, (7, 7), 2)
    img_canny = cv2.Canny(img_blur, 50, 50)
    kernel = np.ones((6, 6))
    img_dilate = cv2.dilate(img_canny, kernel, iterations=1)
    img_erode = cv2.erode(img_dilate, kernel, iterations=2)
    return img_erode

def get_contours(img):
    contours, _ = cv2.findContours(process(img), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    cnt = sorted(contours, key=cv2.contourArea)[-2]
    peri = cv2.arcLength(cnt, True)
    approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
    cv2.drawContours(img, [approx], -1, (0, 255, 0), 2)

page = "image.jpg"
image = cv2.imread(page)
get_contours(image)
cv2.imshow("Image", image)
cv2.waitKey(0)

以上只考虑了等高线的面积;如果你想要更可靠的结果,你可以这样做,它将只检测轮廓是4边。

相关问题