Как разбить изображение стола по вертикальным линиям на три изображения? - PullRequest
5 голосов
/ 11 февраля 2020

Я хочу разделить изображение таблицы по вертикальным линиям на три изображения, как показано ниже. Является ли это возможным? Ширина каждого столбца является переменной. И печально то, что левая вертикальная линия нарисована вниз от заголовка, как вы можете видеть.

  • Входное изображение (input.png)

enter image description here

  • Выходное изображение (output1.png)

enter image description here

  • Выходное изображение (output2.png)

enter image description here

  • Выходное изображение (output3.png)

enter image description here


Обновление 1

И, к сожалению, вертикальная линия слева направлена ​​вниз от заголовка, как вы можете видеть.

Это значит, что следующее изображение B легче разделить. Но мой случай - A.

enter image description here


Обновление 2

Я пытаюсь сделать то, что дал мне @HansHirse. Я ожидаю, что sub_image_1.png, sub_image_2.png и sub_image_3.png хранятся в папке out. Но пока не повезло. Я смотрю на это.

https://github.com/zono/ocr/blob/16fd0ec9a2c7d2e26279ec53947fe7fbab9f526d/src/opencv.py

$ git clone https://github.com/zono/ocr.git
$ cd ocr
$ git checkout 16fd0ec9a2c7d2e26279ec53947fe7fbab9f526d
$ docker-compose up -d
$ docker exec -it ocr /bin/bash
$ python3 opencv.py

Ответы [ 2 ]

6 голосов
/ 11 февраля 2020

Поскольку ваша таблица идеально выровнена, вы можете инвертировать двоичное пороговое значение для вашего изображения и подсчитать (белые) пиксели вдоль оси y, чтобы обнаружить вертикальные линии:

Count white pixels along y-axis

Вам нужно будет очистить пики, поскольку вы можете получить плато для более толстых линий.

Это было бы моей идеей в Python OpenCV:

import cv2
import numpy as np
from skimage import io              # Only needed for web reading images

# Web read image via scikit-image; convert to OpenCV's BGR color ordering
img = cv2.cvtColor(io.imread('https://i.stack.imgur.com/BTqBs.png'), cv2.COLOR_RGB2BGR)

# Inverse binary threshold grayscale version of image
img_thr = cv2.threshold(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), 128, 255, cv2.THRESH_BINARY_INV)[1]

# Count pixels along the y-axis, find peaks
thr_y = 200
y_sum = np.count_nonzero(img_thr, axis=0)
peaks = np.where(y_sum > thr_y)[0]

# Clean peaks
thr_x = 50
temp = np.diff(peaks).squeeze()
idx = np.where(temp > thr_x)[0]
peaks = np.concatenate(([0], peaks[idx+1]), axis=0) + 1

# Save sub-images
for i in np.arange(peaks.shape[0] - 1):
    cv2.imwrite('sub_image_' + str(i) + '.png', img[:, peaks[i]:peaks[i+1]])

Я получаю следующие три изображения:

Sub image 1

Sub image 2

Sub image 3

Как видите, вы можете изменить выделение на +/- 1 пиксель, если фактическая линия имеет ширину всего 1 пиксель.

Надеюсь, это поможет!

----------------------------------------
System information
----------------------------------------
Platform:    Windows-10-10.0.16299-SP0
Python:      3.8.1
NumPy:       1.18.1
OpenCV:      4.2.0
----------------------------------------
2 голосов
/ 11 февраля 2020

OpenCV имеет функцию определения строки:

Вы можете отфильтровать строки, которые возвращаются, передавая min_theta и max_theta. Для вертикальных линий вы можете указать: 88 и 92 соответственно для поля.

Это отредактированный образец, взятый из документации openCV:

import sys
import math
import cv2 as cv
import numpy as np
def main(argv):

    default_file = 'img.png'
    filename = argv[0] if len(argv) > 0 else default_file
    # Loads an image
    src = cv.imread(cv.samples.findFile(filename), cv.IMREAD_GRAYSCALE)

    #some preparation of the photo
    dst = cv.Canny(src, 50, 200, None, 3)

    # Copy edges to the images that will display the results in BGR
    cdst = cv.cvtColor(dst, cv.COLOR_GRAY2BGR)
    cdstP = np.copy(cdst)

    lines = cv.HoughLines(dst, 1, np.pi / 180, 150, None, 88, 92) #min and max theta

Вы можете получить x, y координаты линии и нарисуйте их, используя следующий код.

    if lines is not None:
        for i in range(0, len(lines)):
            rho = lines[i][0][0]
            theta = lines[i][0][2]
            a = math.cos(theta)
            b = math.sin(theta)
            x0 = a * rho
            y0 = b * rho
            pt1 = (int(x0 + 1000*(-b)), int(y0 + 1000*(a)))
            pt2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a)))
            cv.line(cdst, pt1, pt2, (0,0,255), 3, cv.LINE_AA)

В качестве альтернативы вы также можете использовать HoughLinesP, так как это позволяет вам указать минимальную длину, которая поможет вашей фильтрации. Также строки возвращаются в виде пар x, y для каждого конца, что облегчает работу с ними.

    linesP = cv.HoughLinesP(dst, 1, np.pi / 180, 50, None, 50, 10)

    if linesP is not None:
        for i in range(0, len(linesP)):
            l = linesP[i][0]
            cv.line(cdstP, (l[0], l[2]), (l[2], l[3]), (0,0,255), 3, cv.LINE_AA)

    cv.imshow("Source", src)
    cv.imshow("Detected Lines (in red) - Standard Hough Line Transform", cdst)
    cv.imshow("Detected Lines (in red) - Probabilistic Line Transform", cdstP)

    cv.waitKey()
    return 0

Документация

Чтобы обрезать изображение, вы можете взять x координаты обнаруженных вами линий и использование numpy срезов.

for i in range(0, len(linesP) - 1):
            l = linesP[i][0]
            xcoords = l[0], linesP[i+1][0][0]
            slice = img[:xcoords[0],xcoords[1]]
            cv.imshow('slice', slice)
            cv.waitKey(0)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...