Страницы

Поиск по вопросам

среда, 27 ноября 2019 г.

Поиск прямоугольника на изображении

#python #opencv


Задача: 
Найти жёсткий диск на фото, определить его угол и контуры

Проблема:
Не всегда удаётся найти правильный контур диска.

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

firstFrame = cv2.imread(bg_im)  # Фоновое изображение без диска
frame_img = cv2.imread(frame)   # Изображение с диском

gray = cv2.cvtColor(frame_img, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
firstFrame = cv2.cvtColor(firstFrame, cv2.COLOR_BGR2GRAY)
firstFrame = cv2.GaussianBlur(firstFrame, (21, 21), 0)

frameDelta = cv2.absdiff(firstFrame, gray)
thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.dilate(thresh, None, iterations=2)
(cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

c = sorted(cnts, key = cv2.contourArea, reverse = True)[0]

rect = cv2.minAreaRect(c)
box = cv2.cv.BoxPoints(rect)

x1 = box[0][0]
x2 = box[1][0]
x4 = box[3][0]

y1 = box[0][1]
y2 = box[1][1]
y4 = box[3][1]

perimeter_1_4 = sqrt((x4 - x1)**2 + (y1 - y4)**2)
perimeter_1_2 = sqrt((x2 - x1)**2 + (y1 - y2)**2)

box = np.int0(box)
corner = list(rect)[2]

if perimeter_1_4 > perimeter_1_2:
    corner = -(90 - corner)    

cv2.drawContours(frame_img,[box],0,(0,0,255),2)




    


Ответы

Ответ 1



Как только речь заходит о применении размытия с большим размером ядра, то, к сожалению, получается необратимая деформация объекта интереса. Объект контурами вроде и выделен, но при этом по своим размерам, форме (а подчас и положению) может иметь значительное расхождение с оригиналом. Стоит ли говорить, что морфологические операции (эрозия, дилатация), наложенные поверх, лишь усугубляют проблему. В ситуации с представленными изображениями сложность возникает ещё и от того, что лента конвейера имеет весьма различимые следы производственного пользования. Это царапины, пятна и прочие элементы подобного рода, которые на фоновом изображении и изображении с объектом интереса имеют разное положение, да и вообще в деталях различаются значительно. Этот момент становится принципиальным, когда производится попиксельное вычитание одного изображения из другого. В целом задача сводится к тому, чтобы не повредив сам объект, попытаться выделить его на общем фоне. Попробуем пойти от обратного и вместо того, чтобы применять размытие повысим резкость краёв. void subtractPyr(const cv::Mat &src_mat, cv::Mat &dst_mat, int iterations) { std::vector sizes(iterations); cv::Mat mat = src_mat.clone(); for(int i = 0; i < iterations; ++i) { sizes[iterations-(i+1)] = mat.size(); cv::pyrDown(mat, mat); } for(int i = 0; i < iterations; ++i) cv::pyrUp(mat, mat, sizes.at(i)); cv::subtract(mat, src_mat, dst_mat); } cv::Mat src1_mat = cv::imread("foreground.jpg"); cv::Mat src2_mat = cv::imread("background.jpg"); cv::Mat gry1_mat, gry2_mat; cv::cvtColor(src1_mat, gry1_mat, cv::COLOR_BGR2GRAY); cv::cvtColor(src2_mat, gry2_mat, cv::COLOR_BGR2GRAY); subtractPyr(gry1_mat, gry1_mat, 5); subtractPyr(gry2_mat, gry2_mat, 5); Изображение с объектом интереса: Фоновое изображение: Несмотря на то, что фоновое изображение имеет свой набор шумов, тем не менее оно содержит и те детали, которые полезно вычесть из изображения с объектом интереса. После этого можно выполнить бинаризацию. cv::Mat gry_mat = gry1_mat - gry2_mat; cv::Mat bin_mat; cv::threshold(gry_mat, bin_mat, 1, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); Получим следующую маску: Здесь хорошо заметна одна из проблем, связанная с неверно подобранным освещением. Левый верхний угол объекта интереса частично растворён, что не позволяет произвести поиск контура, опоясывающего объект, с корректным результатом. Так или иначе продолжим именно через поиск контуров и отсеивание их по площади с эмпирически подобранным размером. Их иерархия больше не потребуется, а значит объединим все точки в один единственный большой контур. std::vector > contours; cv::findContours(bin_mat.clone(), contours , cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); std::vector big_contour; for(auto itr = contours.begin(); itr != contours.end(); ++itr) { if(cv::contourArea(*itr) > 300.) { const std::vector &contour = *itr; for(auto itr2 = contour.begin(); itr2 != contour.end(); ++itr2) { big_contour.push_back(*itr2); } } } Если нарисовать большой контур, то можно увидеть, что лишние детали на изображении более не захватываются, а сам он в целом лежит внутри границ объекта интереса. Остаётся получить опоясывающий объект интереса прямоугольник. cv::RotatedRect rc = cv::minAreaRect(big_contour); cv::Point2f points[4]; rc.points(points); // Нарисуем его. for(int i = 0; i < 4; ++i) cv::line(dst_mat, points[i], points[(i+1)%4], cv::Scalar(0,0,255)); Как хорошо видно на следующем изображении полученный прямоугольник совершенно не идеально описывает объект интереса. Эта проблема связана с оптической деформацией этого самого объекта, а также с наличием довольно широкой тени, присутствующей с левой его стороны. К сожалению, эти проблемы нельзя устранить иначе, кроме как подстройкой объектива камеры и освещения.

Комментариев нет:

Отправить комментарий