Определить углы сетки - PullRequest
0 голосов
/ 01 июля 2018

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

Чтобы определить углы сетки, я пытался использовать линии Хаф, но безрезультатно. Иногда линии Хафа не идентифицируют край сетки, и трудно определить, какие из нарисованных линий принадлежат краю сетки, а какие из них являются линиями сетки.

Затем я решил использовать контуры для определения краев сетки. Однако он обнаруживает многочисленные контуры и приводит к той же проблеме идентификации, которые находятся по углам среди всех идентифицированных контуров.

Чтобы помочь этому, я использовал двустороннюю фильтрацию, обнаружение краев Канни, морфологическое расширение и обнаружение краев Харриса, как видно из вопроса, аналогичного моей. Даже после применения всех этих мер, я все еще получаю тонны ложных углов, а иногда истинные углы не идентифицируются.

Мне было интересно, есть ли у кого-нибудь способ улучшить результаты моего обнаружения углов или кто-то имел в виду совершенно другое предположение, которое могло бы помочь решить мою проблему. Цель состоит в том, чтобы получить углы, чтобы я мог выполнить гомографию с использованием сетки 10 × 10, чтобы учесть перекос в изображениях. Это также поможет отобразить квадрат сетки в пиксельное пространство, что очень полезно.

Это мой код (немного небрежно с именами и всем, но я постараюсь исправить это позже). Кроме того, да, я сделал все возможное для двусторонней фильтрации, это, казалось, помогло избавиться от ненужных контуров и углов.

Мне также кажется, что я получаю одну ошибку при попытке применить линии Хафа к своему контурному изображению:

error: (-215) img.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3)) in function cv::HoughLinesStandard

from PIL import Image
import numpy as np
import cv2
import glob

#import images using opencv
images = [cv2.imread(file) for file in glob.glob("SpAMImages/*.jpg")]
for image in images:
    #resizes image gotten from folder and performs bilateral filtering
    img = cv2.bilateralFilter(cv2.resize(image, (715,715)), 15, 800, 800)

    #applies a canny edge detection filter on the images loaded from the folder
    gridEdges = cv2.Canny(img, 140, 170)

    #apply image dilation
    kernel = np.ones((5,5), np.uint8)
    gridEdges = cv2.dilate(gridEdges, kernel, iterations=1)
    gridEdges = np.float32(gridEdges)
    gridEdges = cv2.blur(gridEdges,(10,10))
    gridEdges = cv2.cornerHarris(gridEdges,2,3,0.04)
    gridEdges = cv2.dilate(gridEdges,None)
    img[gridEdges>0.01*gridEdges.max()]=[0,0,255]

    #draw contours on current image
    imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret, thresh = cv2.threshold(imgray, 127, 255, 0)
    contourImage, contours, hierarchy = 
    cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
    contour = cv2.drawContours(img, contours, -1, (0,255,0), 3)

    '''
    def largest_4_sided_contour(thresh, show_contours=True):
        contourImage, contours, hierarchy = 
        cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
        contour = cv2.drawContours(img, contours, -1, (0,255,0), 3)
        contours = sorted(contours, key = cv2.contourArea, reverse =  True)
        for cnt in contours[:min(5, len(contours))]:
            print(len(cnt))
            if len(cnt) == 4:
                return cnt
        return None
        print(largest_4_sided_contour(thresh))

    #applies a hough transformation to extract gridlines from the image

    -----------THIS LINE BELOW GIVES ME THE ERROR-----------------
    lines = cv2.HoughLines(img, 1, np.pi/180, 245)

    #iterates through an array of lines gottne from the hough transform
    #and draws them unto the image
    for i in range(len(lines)):
        for rho,theta in lines[i]:
            a = np.cos(theta)
            b = np.sin(theta)
            x0 = a * rho
            y0 = b * rho
            x1 = int(x0 + 1000*(-b))
            y1 = int(y0 + 1000*(a))
            x2 = int(x0 - 1000*(-b))
            y2 = int(y0 - 1000*(a))
            cv2.line(img, (x1,y1),(x2,y2),(0,0,255),2)
    cv2.imwrite('houghlines.jpg', img)
    '''
    #resize window because for some reason they are too large.
    cv2.namedWindow('image', cv2.WINDOW_NORMAL)
    cv2.resizeWindow('image', 800, 800)

    #display all the images produced from above processes
    cv2.imshow('image', img)
    cv2.imshow('dilated edges', gridEdges)
    #cv2.imshow('contour', contour)
    cv2.waitKey(0)
    '''
    retrieve image size from imported image.
    testImageWidth, testImageHeight = img.shape[:2]
    print(testImageHeight, testImageWidth)'''

Это некоторые изображения, полученные в результате моей попытки получения углов с помощью обнаружения контуров и обнаружения углов Харриса.

Определение углов с помощью контуров и определение угла Харриса:

identifying corners using contours and harris corner detection

identifying corners using contours and harris corner detection

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

Пример сетки:

prime example grid

Скошенная сетка:

somewhat skewed grid

Заранее спасибо за любую помощь !!!

Ответы [ 2 ]

0 голосов
/ 01 июля 2018

У меня есть какой-то ответ для вас, хотя и не полный, он может просто помочь вам. Я использую алгоритм Рамера-Дугласа-Пекера , чтобы определить контуры, а затем извлечь прямоугольники из контуров. Затем я использую процент области «коробки» к области изображения, чтобы удалить меньшие коробки. Это удаляет большинство ненужных ящиков.

Вот пример того, что я сделал в коде Python:

Поиск контуров:

    def findcontours(self):
        logging.info("Inside findcontours Contours...")
        # Pre-process image
        imgGray = self.imgProcess.toGrey(self.img)
        logging.info("Success on converting image to greyscale")

        imgThresh = self.imgProcess.toBinary(imgGray)
        logging.info("Success on converting image to binary")

        logging.info("Finding contours...")
        image, contours, hierarchy = cv2.findContours(imgThresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
        logging.info("Contours found: %d", len(contours))

       return contours

Использование контуров для поиска полей:

    def getRectangles(self, contours):
        arrrect = []
        imgArea = self.getArea()
        logging.info("Image Area is: %d", imgArea)

        for cnt in contours:
            epsilon = 0.01*cv2.arcLength(cnt, True)
            approx = cv2.approxPolyDP(cnt, epsilon, False)
            area = cv2.contourArea(approx)
            rect = cv2.minAreaRect(approx)
            box = cv2.boxPoints(rect)
            box = np.int0(box)
            percentage = (area * 100) / imgArea
            if percentage > 0.3:
                arrrect.append(box)

        return arrrect

Чтобы объединить эти 2 метода:

    def process(self):
        logging.info("Processing image...")
        self.shape_handler = ShapeHandler(self.img)

        contours = self.shape_handler.findcontours()

        logging.info("Finding Rectangles from contours...")
        rectangles = self.shape_handler.getRectangles(contours)

        img = self.imgDraw.draw(self.img, rectangles, "Green", 10)
        cv2.drawContours(img, array, -1, (0,255,0), thickness)
        self.display(img)

        logging.info("Amount of Rectangles Found: %d", len(rectangles))

Показать изображение:

    def display(self, img):
        cv2.namedWindow('image', cv2.WINDOW_NORMAL)
        cv2.imshow("image", img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

Последний шаг - объединить любые пересекающиеся прямоугольники, поскольку вас интересуют только края / углы, а затем получить только прямоугольник с наибольшей площадью. Посмотрите здесь , чтобы проверить, как комбинировать поля.

Мой источник кодирования: Документация OpenCV 3.1

Результат на ваших изображениях:

Нормальный:

Normal

перекос:

Skew

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

0 голосов
/ 01 июля 2018

Вы работаете в Python с OpenCV, но я собираюсь дать вам ответ, используя MATLAB с DIPimage. Я намерен ответить на этот вопрос о концепциях, а не о коде. Я уверен, что есть способы сделать все это в Python с помощью OpenCV.

Моя цель здесь - найти четыре угла доски. Саму сетку можно угадать, так как это всего лишь эквидистантное деление доски, нет необходимости пытаться обнаружить все линии. Четыре угла дают всю информацию о трансформации перспективы.

Самый простой способ обнаружить доску - это распознать, что она светлая и имеет темный фон. Начиная с изображения со значением серого, я применяю небольшое закрытие (я использовал круг диаметром 7 пикселей, это подходит для изображения с пониженной частотой дискретизации, которое я использовал в качестве примера, но вы можете соответственно увеличить размер для полноразмерное изображение). Это дает такой результат:

enter image description here

Далее я бинаризирую, используя выбор порога Оцу, и удаляю отверстия (эта часть не важна, остальная часть будет работать, если отверстия тоже есть). Подключенные компоненты, которые мы видим теперь, соответствуют плате и соседним платам (или другим белым элементам вокруг платы).

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

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

enter image description here

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

В любом случае в верхнем левом углу доски я нахожу крайний пиксель, ближайший к верхнему левому углу изображения. Аналогично для других 3 углов. Эти результаты - красные точки на изображении выше.

Третий вариант - преобразовать контур в многоугольник, упростить его с помощью алгоритма Дугласа – Пекера, отбросить края, идущие вдоль края изображения (это то место, где на изображении нет углов), и расширить два ребра по обе стороны от этого, чтобы найти вершину, которая находится за пределами изображения.

Далее следует код MATLAB (с DIPimage ).

img = readim('https://i.stack.imgur.com/GYZGa.jpg');
img = colorspace(img,'gray');
% Downsample, makes display easier
img = gaussf(img,2);
img = img(0:4:end,0:4:end);
% Simplify and binarize
sim = closing(img,7);
brd = threshold(sim); % uses Otsu threshold selection
% Fill the holes
brd = fillholes(brd);
% Keep only the largest connected component
brd = label(brd);
msr = measure(brd);
[~,I] = max(msr,'size');
brd = brd == msr(I).id;
% Extract edges
brd = brd - erosion(brd,3,'rectangular');
% Find corners
pts = findcoord(brd);
[~,top_left] = min(sum(pts.^2,2));
[~,top_right] = min(sum((pts-[imsize(brd,1),0]).^2,2));
[~,bottom_left] = min(sum((pts-[0,imsize(brd,2)]).^2,2));
[~,bottom_right] = min(sum((pts-[imsize(brd,1),imsize(brd,2)]).^2,2));
% Make an image with corner pixels set
cnr = newim(brd,'bin');
cnr(pts(top_left,1),pts(top_left,2)) = 1;
cnr(pts(top_right,1),pts(top_right,2)) = 1;
cnr(pts(bottom_left,1),pts(bottom_left,2)) = 1;
cnr(pts(bottom_right,1),pts(bottom_right,2)) = 1;
cnr = dilation(cnr,3);
% Save images
writeim(sim,'so1.png')
out = overlay(img,brd,[0,0,255]);
out = overlay(out,cnr,[255,0,0]);
writeim(out,'so2.png')
...