Извлечь фиксированное количество квадратов из изображения с помощью Python / OpenCV - PullRequest
1 голос
/ 24 апреля 2019

У меня есть несколько отсканированных изображений, которые я хотел бы вычислить с помощью Python / Opencv. Каждое из этих изображений (см. Пример ниже) содержит n строк цветных квадратов. Каждый из этих квадратов имеет одинаковый размер. Цель состоит в том, чтобы обрезать каждый из этих квадратов и извлечь из него данные.

Image with squares to extract

Я нашел там код, который может извлекать квадраты из изображения.

Вот мой код, в котором я его использовал:

import numpy as np
import cv2
from matplotlib import pyplot as plt

def angle_cos(p0, p1, p2):
    import numpy as np

    d1, d2 = (p0-p1).astype('float'), (p2-p1).astype('float')
    return abs( np.dot(d1, d2) / np.sqrt( np.dot(d1, d1)*np.dot(d2, d2) ) )

def find_squares(img):
    import cv2 as cv
    import numpy as np

    img = cv.GaussianBlur(img, (5, 5), 0)
    squares = []
    for gray in cv.split(img):
        for thrs in range(0, 255, 26):
            if thrs == 0:
                bin = cv.Canny(gray, 0, 50, apertureSize=5)
                bin = cv.dilate(bin, None)
            else:
                _retval, bin = cv.threshold(gray, thrs, 255, cv.THRESH_BINARY)
            contours, _hierarchy = cv.findContours(bin, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
            for cnt in contours:
                cnt_len = cv.arcLength(cnt, True)
                cnt = cv.approxPolyDP(cnt, 0.02*cnt_len, True)
                if len(cnt) == 4 and cv.contourArea(cnt) > 1000 and cv.isContourConvex(cnt):
                    cnt = cnt.reshape(-1, 2)
                    max_cos = np.max([angle_cos( cnt[i], cnt[(i+1) % 4], cnt[(i+2) % 4] ) for i in range(4)])
                    if max_cos < 0.1:
                        squares.append(cnt)
    print(len(squares))
    return squares

img = cv2.imread("test_squares.jpg",1)

plt.axis("off")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()

squares = find_squares(img)
cv2.drawContours( img, squares, -1, (0, 255, 0), 1 )
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()

Однако он находит два много квадратов (100 вместо 15 !!). Глядя на изображение, кажется, что Opencv находит много контуров для каждого квадрата.

Я почти уверен, что его можно оптимизировать, поскольку квадраты более или менее одинакового размера и находятся далеко друг от друга. Будучи новичком в Opencv, я еще не нашел способа дать больше критериев в функции «найти квадраты», чтобы получить только 15 квадратов в конце процедуры. Может быть, площадь контура можно увеличить?

Я также нашел там более подробный код (очень близкий к предыдущему), но, похоже, он был разработан в старой версии Opencv. Мне не удалось заставить его работать (и, следовательно, изменить его).

Ответы [ 2 ]

1 голос
/ 24 апреля 2019

Это еще один более надежный метод .

Я использовал этот код, чтобы найти контуры на изображении (полный код можно найти в этой сущности ):

import cv2
import numpy as np
import matplotlib.pyplot as plt

# Define square size
min_square_size = 987
# Read Image
img = cv2.imread('/home/stephen/Desktop/3eY0k.jpg')
# Threshold and find edges
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Threshold the image - segment white background from post it notes
_, thresh = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY_INV);
# Find the contours
_, contours, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

Я перебрал контуры.Я только смотрел на контуры, которые были разумного размера.Я нашел четыре угла каждого контура.

corners

# Create a list for post-it images
images = []
# Iterate through the contours in the image
for contour in contours:
    area = cv2.contourArea(contour)
    # If the contour is not really small, or really big
    h,w = img.shape[0], img.shape[1]
    if area > min_square_size and area < h*w-(2*(h+w)):
        # Get the four corners of the contour
        epsilon = .1 * cv2.arcLength(contour, True)
        approx = cv2.approxPolyDP(contour, epsilon, True)
        # Draw the point
        for point in approx: cv2.circle(img, tuple(point[0]), 2, (255,0,0), 2)
        # Warp it to a square
        pts1 = np.float32(approx)
        pts2 = np.float32([[0,0],[300,0],[300,300],[0,300]])
        M = cv2.getPerspectiveTransform(pts1,pts2)
        dst = cv2.warpPerspective(img,M,(300,300))
        # Add the square to the list of images
        images.append(dst.copy())

Примечания к постаменту являются квадратами, но поскольку камера деформирует объекты вИзображение они не отображаются в виде квадратов.Я использовал warpPerspective, чтобы сделать заметки квадратной формы.Только несколько из них показаны на этом графике (есть и другие, которые не подходят): plot

0 голосов
/ 24 апреля 2019

Если ваша проблема заключается в том, что на изображении обнаружено слишком много контуров (ребер), я предлагаю сначала изменить деталь для поиска ребер. Это будет самая легкая модификация.

В частности, вам нужно изменить этот вызов:

bin = cv.Canny(gray, 0, 50, apertureSize=5)

Функция cv.Canny() принимает в качестве аргументов два пороговых значения, размер апертуры и логическое значение, чтобы указать, используется ли точная форма градиента. Поиграйте с этими параметрами, и я думаю, вы получите гораздо лучшие результаты.

...