Поиск прямоугольника на изображении
📁 python
Задача: Найти жёсткий диск на фото, определить его угол и контуры
nПроблема: Не всегда удаётся найти правильный контур диска.
nВ коде я делаю изображения серыми, блюрю, нахожу разницу между ними и определяю контуры, но решение не точное. Хотелось бы вашей помощи.
nfirstFrame = cv2.imread(bg_im) # Фоновое изображение без дискаnframe_img = cv2.imread(frame) # Изображение с дискомngray = cv2.cvtColor(frame_img, cv2.COLOR_BGR2GRAY)ngray = cv2.GaussianBlur(gray, (21, 21), 0)nfirstFrame = cv2.cvtColor(firstFrame, cv2.COLOR_BGR2GRAY)nfirstFrame = cv2.GaussianBlur(firstFrame, (21, 21), 0)nframeDelta = cv2.absdiff(firstFrame, gray)nthresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]nthresh = cv2.dilate(thresh, None, iterations=2)n(cnts, _) = cv2.findContours(thresh.copy(), cv2.RETR_EXTEAL, cv2.CHAIN_APPROX_SIMPLE)nc = sorted(cnts, key = cv2.contourArea, reverse = True)[0]nrect = cv2.minAreaRect(c)nbox = cv2.cv.BoxPoints(rect)nx1 = box[0][0]nx2 = box[1][0]nx4 = box[3][0]ny1 = box[0][1]ny2 = box[1][1]ny4 = box[3][1]nperimeter_1_4 = sqrt((x4 - x1)**2 + (y1 - y4)**2)nperimeter_1_2 = sqrt((x2 - x1)**2 + (y1 - y2)**2)nbox = np.int0(box)ncoer = list(rect)[2]nif perimeter_1_4 > perimeter_1_2:n coer = -(90 - coer) ncv2.drawContours(frame_img,[box],0,(0,0,255),2)n Как только речь заходит о применении размытия с большим размером ядра, то, к сожалению, получается необратимая деформация объекта интереса. Объект контурами вроде и выделен, но при этом по своим размерам, форме (а подчас и положению) может иметь значительное расхождение с оригиналом. Стоит ли говорить, что морфологические операции (эрозия, дилатация), наложенные поверх, лишь усугубляют проблему.
nВ ситуации с представленными изображениями сложность возникает ещё и от того, что лента конвейера имеет весьма различимые следы производственного пользования. Это царапины, пятна и прочие элементы подобного рода, которые на фоновом изображении и изображении с объектом интереса имеют разное положение, да и вообще в деталях различаются значительно. Этот момент становится принципиальным, когда производится попиксельное вычитание одного изображения из другого.
nВ целом задача сводится к тому, чтобы не повредив сам объект, попытаться выделить его на общем фоне. Попробуем пойти от обратного и вместо того, чтобы применять размытие повысим резкость краёв.
nvoid subtractPyr(const cv::Mat &src_mat, cv::Mat &dst_mat, int iterations) {n std::vector<cv::Size> sizes(iterations);n cv::Mat mat = src_mat.clone();n for(int i = 0; i < iterations; ++i) {n sizes[iterations-(i+1)] = mat.size();n cv::pyrDown(mat, mat);n }n for(int i = 0; i < iterations; ++i)n cv::pyrUp(mat, mat, sizes.at(i));n cv::subtract(mat, src_mat, dst_mat);n}ncv::Mat src1_mat = cv::imread(""foreground.jpg"");ncv::Mat src2_mat = cv::imread(""background.jpg"");ncv::Mat gry1_mat, gry2_mat;ncv::cvtColor(src1_mat, gry1_mat, cv::COLOR_BGR2GRAY);ncv::cvtColor(src2_mat, gry2_mat, cv::COLOR_BGR2GRAY);nsubtractPyr(gry1_mat, gry1_mat, 5);nsubtractPyr(gry2_mat, gry2_mat, 5);nnНесмотря на то, что фоновое изображение имеет свой набор шумов, тем не менее оно содержит и те детали, которые полезно вычесть из изображения с объектом интереса. После этого можно выполнить бинаризацию.
ncv::Mat gry_mat = gry1_mat - gry2_mat;ncv::Mat bin_mat;ncv::threshold(gry_mat, bin_mat, 1, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);nnПолучим следующую маску:
nЗдесь хорошо заметна одна из проблем, связанная с неверно подобранным освещением. Левый верхний угол объекта интереса частично растворён, что не позволяет произвести поиск контура, опоясывающего объект, с корректным результатом.
nТак или иначе продолжим именно через поиск контуров и отсеивание их по площади с эмпирически подобранным размером. Их иерархия больше не потребуется, а значит объединим все точки в один единственный большой контур.
nstd::vector<std::vector<cv::Point> > contours;ncv::findContours(bin_mat.clone(), contoursn , cv::RETR_EXTEAL, cv::CHAIN_APPROX_SIMPLE);nstd::vector<cv::Point> big_contour;nfor(auto itr = contours.begin(); itr != contours.end(); ++itr) {n if(cv::contourArea(*itr) > 300.) {n const std::vector<cv::Point> &contour = *itr;n for(auto itr2 = contour.begin(); itr2 != contour.end(); ++itr2) {n big_contour.push_back(*itr2);n }n }n}nnЕсли нарисовать большой контур, то можно увидеть, что лишние детали на изображении более не захватываются, а сам он в целом лежит внутри границ объекта интереса.
nОстаётся получить опоясывающий объект интереса прямоугольник.
ncv::RotatedRect rc = cv::minAreaRect(big_contour);ncv::Point2f points[4];nrc.points(points);n// Нарисуем его.nfor(int i = 0; i < 4; ++i)n cv::line(dst_mat, points[i], points[(i+1)%4], cv::Scalar(0,0,255));nnКак хорошо видно на следующем изображении полученный прямоугольник совершенно не идеально описывает объект интереса. Эта проблема связана с оптической деформацией этого самого объекта, а также с наличием довольно широкой тени, присутствующей с левой его стороны. К сожалению, эти проблемы нельзя устранить иначе, кроме как подстройкой объектива камеры и освещения.
Войдите чтобы оставить ответ