Как получить расширенный или сжатый контур в OpenCV?

Можно ли получить расширенную или сокращенную версию контура?

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

Imgur

Я хотел бы нарисовать другой контур, который имеет обычное расстояние в пикселях от исходного контура, например:

Imgur

Imgur

За исключением размытия, которое, я думаю, может быть не очень хорошей идеей, так как с помощью размытия трудно контролировать расстояние в пикселях, я понятия не имею, как решить эту проблему. Могу ли я узнать, какое направление должно быть правильным?

2 ответа

Решение

Использование cv:: erode с небольшим ядром и несколькими итерациями может быть достаточно для ваших нужд, даже если это не точно.

Код C++:

cv::Mat img = ...;
int iterations = 10;
cv::erode(img, img,
   cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3,3)),
   cv::Point(-1,-1),
   iterations);

Демо-версия:

# img is the image containing the original black contour
for form in [cv.MORPH_RECT, cv.MORPH_CROSS]:
    eroded = cv.erode(img, cv.getStructuringElement(form, (3,3)), iterations=10)
    contours, hierarchy = cv.findContours(~eroded, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    vis = cv.cvtColor(img, cv.COLOR_GRAY2BGR)
    cv.drawContours(vis, contours, 0, (0,0,255))
    cv.drawContours(vis, contours, 1, (255,0,0))
    show_image(vis)

10 итераций с использованием cv.MORPH_RECT с ядром 3x3:

10 итераций с использованием cv.MORPH_CROSS с ядром 3x3:

Вы можете изменить смещение, регулируя количество итераций.

Гораздо более точный подход - использовать cv:: distanceTransform, чтобы найти все пиксели, которые находятся примерно в 10 пикселях от контура:

dist = cv.distanceTransform(img, cv.DIST_L2, cv.DIST_MASK_PRECISE)
ring = cv.inRange(dist, 9.5, 10.5) # take all pixels at distance between 9.5px and 10.5px
show_image(ring)
contours, hierarchy = cv.findContours(ring, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)

vis = cv.cvtColor(img, cv.COLOR_GRAY2BGR)
cv.drawContours(vis, contours, 0, (0,0,255))
cv.drawContours(vis, contours, 2, (255,0,0))
show_image(vis)

Вы получите два контура на каждой стороне исходного контура. Используйте findContours с RETR_EXTERNAL, чтобы восстановить только внешний контур. Чтобы также восстановить внутренний контур, используйте RETR_LIST

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

  1. Для каждого контура ищите центр масс: cv:: моменты (контуры [i]) -> cv::Point2f mc(mu.m10 / mu.m00), mu.m01 / mu.m00));

  2. Для каждой точки контура: сделать сдвиг для центра масс -> умножить на коэффициент K -> сдвинуть назад: pt_new = (k * (pt - mc) + mc);

Но коэффициент k должен быть индивидуальным для каждой точки. Я вычислю это чуть позже...

Другие вопросы по тегам