8

Я пытаюсь обнаружить и точно определить некоторые объекты в изображениях из контуров. Контуры, которые я получаю, часто включают некоторый шум (возможно, из фона, я не знаю). Объекты должны выглядеть так, прямоугольники или квадраты, как:Как уронить дефекты выпуклости?

enter image description here

я получаю очень хорошие результаты с согласованием формы (cv::matchShapes), чтобы обнаружить контуры с этими объектами в них, с и без шума, но у меня есть проблемы с точное расположение в случае шума.

шума выглядит следующим образом:

enter image description here или enter image description here, например.

Моя идея состояла в том, чтобы найти дефекты выпуклости, и если они станут слишком сильными, как-то уберите часть, ведущую к вогнутости. Обнаружение дефектов в порядке, обычно я получаю два дефекта на «нежелательную структуру», но я застрял в том, как решать, что и где я должен удалять точки из контуров.

Вот некоторые контуры, их маски (так что вы можете извлечь контуры легко) и выпуклая оболочка, включая пороговые дефекты выпуклости:

enter image description hereenter image description hereenter image description here

enter image description hereenter image description hereenter image description here

enter image description hereenter image description hereenter image description here

enter image description hereenter image description hereenter image description here

enter image description hereenter image description hereenter image description here

enter image description hereenter image description hereenter image description here

enter image description hereenter image description hereenter image description here

enter image description hereenter image description hereenter image description here

enter image description hereenter image description hereenter image description here

Могу ли я просто ходить по контуру и локально решить «левый поворот», выполняется ли по контуру (при ходьбе по часовой стрелке), и если да, то удалить точки контура до следующего поворота налево не принимается? Может, начинать с дефекта выпуклости?

Я ищу алгоритмы или код, язык программирования не должен быть важен, алгоритм более важен.

+0

Вы смотрели на 'convexityDefects'? http://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#convexitydefects – zeFrenchy

+0

@zeFrenchy да, красные точки в изображениях с выпуклой оболочкой - это результат порогового выпуклостиDefects. Я просто не могу придумать алгоритм о том, как продолжить. – Micka

+1

Получил вас, никогда не использовал его, но я просто бросил его там на всякий случай :) – zeFrenchy

ответ

9

Этот подход работает только по точкам. Для этого вам не нужно создавать маски.

Основная идея:

  1. Найти дефекты на контуре
  2. Если я считаю, по крайней мере два недостатка, найти два ближайших дефектов
  3. Удалить из контура точки между двумя ближайшими дефектами
  4. Перезагрузка с 1 по новому контуру

Я получаю следующие результаты. Как вы можете видеть, у него есть некоторые недостатки для гладких дефектов (например, 7-го изображения), но они очень хороши для хорошо видимых дефектов. Я не знаю, решит ли это вашу проблему, но может быть отправной точкой. На практике это должно быть довольно быстро (вы, безусловно, можете оптимизировать код ниже, особенно функцию removeFromContour). Кроме того, единственным параметром этого подхода является количество дефекта выпуклости, поэтому он хорошо работает как с малыми, так и с большими дефектными блобами.

enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here

#include <opencv2/opencv.hpp> 
using namespace cv; 
using namespace std; 

int ed2(const Point& lhs, const Point& rhs) 
{ 
    return (lhs.x - rhs.x)*(lhs.x - rhs.x) + (lhs.y - rhs.y)*(lhs.y - rhs.y); 
} 

vector<Point> removeFromContour(const vector<Point>& contour, const vector<int>& defectsIdx) 
{ 
    int minDist = INT_MAX; 
    int startIdx; 
    int endIdx; 

    // Find nearest defects 
    for (int i = 0; i < defectsIdx.size(); ++i) 
    { 
     for (int j = i + 1; j < defectsIdx.size(); ++j) 
     { 
      float dist = ed2(contour[defectsIdx[i]], contour[defectsIdx[j]]); 
      if (minDist > dist) 
      { 
       minDist = dist; 
       startIdx = defectsIdx[i]; 
       endIdx = defectsIdx[j]; 
      } 
     } 
    } 

    // Check if intervals are swapped 
    if (startIdx <= endIdx) 
    { 
     int len1 = endIdx - startIdx; 
     int len2 = contour.size() - endIdx + startIdx; 
     if (len2 < len1) 
     { 
      swap(startIdx, endIdx); 
     } 
    } 
    else 
    { 
     int len1 = startIdx - endIdx; 
     int len2 = contour.size() - startIdx + endIdx; 
     if (len1 < len2) 
     { 
      swap(startIdx, endIdx); 
     } 
    } 

    // Remove unwanted points 
    vector<Point> out; 
    if (startIdx <= endIdx) 
    { 
     out.insert(out.end(), contour.begin(), contour.begin() + startIdx); 
     out.insert(out.end(), contour.begin() + endIdx, contour.end()); 
    } 
    else 
    { 
     out.insert(out.end(), contour.begin() + endIdx, contour.begin() + startIdx); 
    } 

    return out; 
} 

int main() 
{ 
    Mat1b img = imread("path_to_mask", IMREAD_GRAYSCALE); 

    Mat3b out; 
    cvtColor(img, out, COLOR_GRAY2BGR); 

    vector<vector<Point>> contours; 
    findContours(img.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); 

    vector<Point> pts = contours[0]; 

    vector<int> hullIdx; 
    convexHull(pts, hullIdx, false); 

    vector<Vec4i> defects; 
    convexityDefects(pts, hullIdx, defects); 

    while (true) 
    { 
     // For debug 
     Mat3b dbg; 
     cvtColor(img, dbg, COLOR_GRAY2BGR); 

     vector<vector<Point>> tmp = {pts}; 
     drawContours(dbg, tmp, 0, Scalar(255, 127, 0)); 

     vector<int> defectsIdx; 
     for (const Vec4i& v : defects) 
     { 
      float depth = float(v[3])/256.f; 
      if (depth > 2) // filter defects by depth 
      { 
       // Defect found 
       defectsIdx.push_back(v[2]); 

       int startidx = v[0]; Point ptStart(pts[startidx]); 
       int endidx = v[1]; Point ptEnd(pts[endidx]); 
       int faridx = v[2]; Point ptFar(pts[faridx]); 

       line(dbg, ptStart, ptEnd, Scalar(255, 0, 0), 1); 
       line(dbg, ptStart, ptFar, Scalar(0, 255, 0), 1); 
       line(dbg, ptEnd, ptFar, Scalar(0, 0, 255), 1); 
       circle(dbg, ptFar, 4, Scalar(127, 127, 255), 2); 
      } 
     } 

     if (defectsIdx.size() < 2) 
     { 
      break; 
     } 

     // If I have more than two defects, remove the points between the two nearest defects 
     pts = removeFromContour(pts, defectsIdx); 
     convexHull(pts, hullIdx, false); 
     convexityDefects(pts, hullIdx, defects); 
    } 


    // Draw result contour 
    vector<vector<Point>> tmp = { pts }; 
    drawContours(out, tmp, 0, Scalar(0, 0, 255), 1); 

    imshow("Result", out); 
    waitKey(); 

    return 0; 
} 

ОБНОВЛЕНИЕ

Работа над приближенным контуром (например, используя CHAIN_APPROX_SIMPLE в findContours) может быть быстрее, но длина контуров должна быть рассчитана с использованием arcLength().

Это фрагмент кода, чтобы заменить в замены части removeFromContour:

// Check if intervals are swapped 
if (startIdx <= endIdx) 
{ 
    //int len11 = endIdx - startIdx; 
    vector<Point> inside(contour.begin() + startIdx, contour.begin() + endIdx); 
    int len1 = (inside.empty()) ? 0 : arcLength(inside, false); 

    //int len22 = contour.size() - endIdx + startIdx; 
    vector<Point> outside1(contour.begin(), contour.begin() + startIdx); 
    vector<Point> outside2(contour.begin() + endIdx, contour.end()); 
    int len2 = (outside1.empty() ? 0 : arcLength(outside1, false)) + (outside2.empty() ? 0 : arcLength(outside2, false)); 

    if (len2 < len1) 
    { 
     swap(startIdx, endIdx); 
    } 
} 
else 
{ 
    //int len1 = startIdx - endIdx; 
    vector<Point> inside(contour.begin() + endIdx, contour.begin() + startIdx); 
    int len1 = (inside.empty()) ? 0 : arcLength(inside, false); 


    //int len2 = contour.size() - startIdx + endIdx; 
    vector<Point> outside1(contour.begin(), contour.begin() + endIdx); 
    vector<Point> outside2(contour.begin() + startIdx, contour.end()); 
    int len2 = (outside1.empty() ? 0 : arcLength(outside1, false)) + (outside2.empty() ? 0 : arcLength(outside2, false)); 

    if (len1 < len2) 
    { 
     swap(startIdx, endIdx); 
    } 
} 
+0

Благодарю вас, я попробую. – Micka

+1

@ Микка Вероятно, с более разумной реализацией вышеуказанного кода, работая над приближенным контуром (аналогично CHAIN_APPROX_SIMPLE), это может быть действительно очень быстро. Пожалуйста, напишите ответ, если вы найдете что-то, что подходит для ваших требований, это может быть очень полезно : D – Miki

+0

В настоящее время решение об обмене производится с помощью индекса-расстояния внутри контура? Вероятно, это причина, почему для 'CV_CHAIN_APPROX_SIMPLE' иногда неправильные части обрезаются (неправильное направление)? Может ли arcLength быть подходящей эвристикой? – Micka

1

В качестве отправной точки и допущения, что дефекты никогда не слишком велики относительно объекта, который вы пытаетесь распознать, перед использованием cv::matchShapes вы можете попробовать простую стратегию размывания + расширять, как показано ниже.

int max = 40; // depending on expected object and defect size 
cv::Mat img = cv::imread("example.png"); 
cv::Mat eroded, dilated; 
cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(max*2,max*2), cv::Point(max,max)); 
cv::erode(img, eroded, element); 
cv::dilate(eroded, dilated, element); 
cv::imshow("original", img); 
cv::imshow("eroded", eroded); 
cv::imshow("dilated", dilated); 

enter image description here

+0

проблема в том, что размер объекта может меняться, поэтому я не могу исправить 'max'. Есть ли у вас предположения о том, как выбрать 'max' в зависимости от свойств экстрагируемого контура, таких как прямоугольник границы, область контура или подобное? – Micka

+0

Можете ли вы не использовать процент от максимального размера блоба, который вы сейчас тестируете? Просто мысль. – zeFrenchy

+0

Вы также можете попробовать увеличить количество эрозии/растяжения, пока не найдете то, что ищете, или ничего не осталось от эрозии. – zeFrenchy

2

я придумал следующий подход для определения границ прямоугольника/квадрат. Он работает на основе нескольких предположений: форма прямоугольная или квадратная, она центрирована по изображению, она не наклонена.

  • делят на замаскирована (заполненный) изображение пополам вдоль оси х, так что вы получите две области (верхнюю половину и нижнюю половину)
  • взять проекцию каждой области на х -axis
  • возьмите все ненулевые записи этих выступов и возьмите их медианы. Эти медианы дают вам границы y
  • аналогичным образом делят изображение по оси y по оси y, вынимают проекции на ось y, затем вычисляют медианы для получения границ x
  • используйте рамки для обрезки региона

Средняя линия и проекция верхней половины образца изображения показаны ниже. proj-n-med-line

Итоговые оценки и обрезано областей для двух образцов: s1 s2

Код в Octave/Matlab, и я испытал это на октаву (вам нужен пакет образа для запуска этого).

clear all 
close all 

im = double(imread('kTouF.png')); 
[r, c] = size(im); 
% top half 
p = sum(im(1:int32(end/2), :), 1); 
y1 = -median(p(find(p > 0))) + int32(r/2); 
% bottom half 
p = sum(im(int32(end/2):end, :), 1); 
y2 = median(p(find(p > 0))) + int32(r/2); 
% left half 
p = sum(im(:, 1:int32(end/2)), 2); 
x1 = -median(p(find(p > 0))) + int32(c/2); 
% right half 
p = sum(im(:, int32(end/2):end), 2); 
x2 = median(p(find(p > 0))) + int32(c/2); 

% crop the image using the bounds 
rect = [x1 y1 x2-x1 y2-y1]; 
cr = imcrop(im, rect); 
im2 = zeros(size(im)); 
im2(y1:y2, x1:x2) = cr; 

figure, 
axis equal 
subplot(1, 2, 1) 
imagesc(im) 
hold on 
plot([x1 x2 x2 x1 x1], [y1 y1 y2 y2 y1], 'g-') 
hold off 
subplot(1, 2, 2) 
imagesc(im2) 
Смежные вопросы