Страницы

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

среда, 3 октября 2018 г.

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

Задача: Найти жёсткий диск на фото, определить его угол и контуры
Проблема: Не всегда удаётся найти правильный контур диска.
В коде я делаю изображения серыми, блюрю, нахожу разницу между ними и определяю контуры, но решение не точное. Хотелось бы вашей помощи.
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)


Ответ

Как только речь заходит о применении размытия с большим размером ядра, то, к сожалению, получается необратимая деформация объекта интереса. Объект контурами вроде и выделен, но при этом по своим размерам, форме (а подчас и положению) может иметь значительное расхождение с оригиналом. Стоит ли говорить, что морфологические операции (эрозия, дилатация), наложенные поверх, лишь усугубляют проблему.
В ситуации с представленными изображениями сложность возникает ещё и от того, что лента конвейера имеет весьма различимые следы производственного пользования. Это царапины, пятна и прочие элементы подобного рода, которые на фоновом изображении и изображении с объектом интереса имеют разное положение, да и вообще в деталях различаются значительно. Этот момент становится принципиальным, когда производится попиксельное вычитание одного изображения из другого.
В целом задача сводится к тому, чтобы не повредив сам объект, попытаться выделить его на общем фоне. Попробуем пойти от обратного и вместо того, чтобы применять размытие повысим резкость краёв.
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));
Как хорошо видно на следующем изображении полученный прямоугольник совершенно не идеально описывает объект интереса. Эта проблема связана с оптической деформацией этого самого объекта, а также с наличием довольно широкой тени, присутствующей с левой его стороны. К сожалению, эти проблемы нельзя устранить иначе, кроме как подстройкой объектива камеры и освещения.

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

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