Определить углы сетки
Я пытаюсь определить углы сетки на различных изображениях, которые мне нужно обработать. Изображения могут быть искажены, а некоторые могут быть относительно хорошо ориентированы, но мы не можем гарантировать, что все изображения будут такими.
Чтобы определить углы сетки, я попытался использовать линии Хафа, но безрезультатно. Иногда линии Хафа не идентифицируют край сетки, и трудно определить, какие из нарисованных линий принадлежат краю сетки, а какие из них являются линиями сетки.
Затем я решил использовать контуры, чтобы обнаружить края сетки. Однако он обнаруживает многочисленные контуры и приводит к той же проблеме идентификации, которые находятся по углам среди всех идентифицированных контуров.
Чтобы помочь этому, я использовал двустороннюю фильтрацию, обнаружение краев Канни, морфологическое расширение и обнаружение краев Харриса, как видно из вопроса с такой же проблемой, как у меня. Даже после применения всех этих мер, я все еще получаю тонны ложных углов, а иногда истинные углы не идентифицируются.
Мне было интересно, есть ли у кого-нибудь способ улучшить результаты моего обнаружения углов или кто-то имел в виду совершенно другое предложение, которое могло бы помочь решить мою проблему. Цель состоит в том, чтобы получить углы, чтобы я мог выполнить гомографию с использованием сетки 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)'''
Вот некоторые изображения, полученные от моей попытки получить углы, используя обнаружение контура и обнаружение угла Харриса.
Определение углов с использованием контуров и определение угла Харриса:
И несколько примеров изображений, с которыми мне приходится работать.
Главный пример сетки:
Несколько перекошенная сетка:
Спасибо за любую помощь заранее!!!
2 ответа
Вы работаете в Python с OpenCV, но я собираюсь дать вам ответ, используя MATLAB с DIPimage. Я предполагаю, что этот ответ будет о концепциях, а не о коде. Я уверен, что есть способы достичь всего этого в Python с помощью OpenCV.
Моя цель здесь - найти четыре угла доски. Саму сетку можно угадать, так как это всего лишь эквидистантное деление доски, нет необходимости пытаться обнаружить все линии. Четыре угла дают всю информацию о перспективной трансформации.
Самый простой способ обнаружить доску - это распознать, что она светлая и имеет темный фон. Начиная с изображения со значением серого, я применяю небольшое закрытие (я использовал круг диаметром 7 пикселей, это подходит для изображения с пониженной частотой дискретизации, которое я использовал в качестве примера, но вы можете увеличить размер соответственно для полноразмерное изображение). Это дает такой результат:
Затем я бинаризирую, используя выбор порога Otsu, и удаляю отверстия (эта часть не важна, остальная часть будет работать, если отверстия тоже есть). Подключенные компоненты, которые мы видим, теперь соответствуют плате и соседним платам (или другим белым элементам вокруг платы).
Выбор самого большого подключенного компонента является довольно распространенной процедурой. В приведенном ниже коде я помечаю изображение (идентифицирует подключенные компоненты), подсчитываю количество пикселей на подключенный компонент и выбираю тот, на котором больше всего пикселей.
Наконец, вычитая из этого результата, его размывание оставляет нас только с пикселями на краю доски (здесь синим цветом наложено на входное изображение):
Уловка, которую я использую, чтобы найти углы, довольно проста, но здесь не работает, потому что один из углов не находится на изображении. Использование Hough на этих четырех краях, вероятно, было бы более надежным способом сделать это. Используйте этот другой ответ для некоторых идей и кода о том, как это сделать.
В любом случае, в верхнем левом углу доски я нахожу крайний пиксель, ближайший к верхнему левому углу изображения. Аналогично для остальных 3 углов. Эти результаты - красные точки на изображении выше.
Третий вариант здесь - преобразовать контур в многоугольник, упростить его с помощью алгоритма Дугласа – Пекера, отбросить края, которые идут вдоль края изображения (это то место, где углы не находятся на изображении), и расширить два края на с каждой стороны, чтобы найти вершину, которая находится за пределами изображения.
Код MATLAB (с DIPimage) выглядит следующим образом.
img = readim('https://stackru.com/images/0e6c591400aa25cdb57f304b66746a1c5c6e5b14.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')
У меня есть какой-то ответ для вас, хотя и не полный, он может просто помочь вам. Я использую алгоритм Рамера-Дугласа-Пекера для определения контуров, а затем извлекаю прямоугольники из контуров. Затем я использую процент области "коробки" к области изображения, чтобы удалить меньшие коробки. Это удаляет большинство ненужных ящиков.
Вот пример того, что я сделал в коде 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
Результат на ваших изображениях:
Нормальный:
Косые:
Надеюсь это поможет!