Подтвердить что ты не робот

Обнаружение областей объекта в изображении opencv

В настоящее время мы пытаемся обнаружить области объектов в изображениях медицинских инструментов, используя методы, доступные в версии OpenCV, С++. Пример изображения показан ниже: 1

Ниже приведены шаги, которые мы выполняем:

  • Преобразование изображения в шкалу серого
  • Применение медианного фильтра
  • Поиск ребер с использованием фильтра sobel
  • Преобразование результата в двоичное изображение с использованием порогового значения 25
  • Скелетонируйте изображение, чтобы убедиться, что у нас аккуратные края
  • Поиск X самых больших подключенных компонентов

Этот подход отлично работает для изображения 1, и вот результат:

1-result

  • Желтые границы - это обнаруженные подключенные компоненты.
  • Прямоугольники просто выделяют наличие подключенного компонента.
  • Чтобы получить понятные результаты, мы просто удалили подключенные компоненты, которые полностью внутри любого другого, поэтому конечный результат выглядит примерно так:

1-endResult

До сих пор все было хорошо, но еще один образец изображения усложнил нашу работу, показанную ниже. 2

Наличие маленького светло-зеленого полотенца под объектами приводит к этому изображению:

2-result

После фильтрации областей, как мы это делали ранее, мы получили следующее:

2-endresult

Очевидно, это не то, что нам нужно. Мы исключаем что-то вроде этого:

2-ExpectedResult

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

Спасибо заранее.

4b9b3361

Ответ 1

Вот что я пробовал.

В изображениях фон в основном зеленоватый, а область фона значительно больше, чем на переднем плане. Итак, если вы возьмете цветную гистограмму изображения, зеленоватые бункеры будут иметь более высокие значения. Пороговое значение этой гистограммы, чтобы ячейки с меньшими значениями были установлены на ноль. Таким образом, мы, скорее всего, сохраним зеленоватые (более высокие значения) бункеры и отбросим другие цвета. Затем запустите эту гистограмму. Backprojection выделит эти зеленоватые области на изображении.

обратного проецирования: backprojection

  • Тогда порог этого обратного проекта. Это дает нам фон.

Фон (после некоторой морфологической фильтрации): background

  • Инвертируйте фон, чтобы получить передний план.

Foreground (после некоторой морфологической фильтрации): foreground

  • Затем найдите контуры переднего плана.

Я думаю, что это дает разумную сегментацию, и используя это как маску, вы можете использовать сегментацию GrabCut для уточнения границ (я еще не пробовал это делать).

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

Контуры: contours

Сегментация GrabCut с использованием макроса переднего плана: gc

Я использую API OpenCV C для части обработки гистограммы.

// load the color image
IplImage* im = cvLoadImage("bFly6.jpg");

// get the color histogram
IplImage* im32f = cvCreateImage(cvGetSize(im), IPL_DEPTH_32F, 3);
cvConvertScale(im, im32f);

int channels[] = {0, 1, 2};
int histSize[] = {32, 32, 32};
float rgbRange[] = {0, 256};
float* ranges[] = {rgbRange, rgbRange, rgbRange};

CvHistogram* hist = cvCreateHist(3, histSize, CV_HIST_ARRAY, ranges);
IplImage* b = cvCreateImage(cvGetSize(im32f), IPL_DEPTH_32F, 1);
IplImage* g = cvCreateImage(cvGetSize(im32f), IPL_DEPTH_32F, 1);
IplImage* r = cvCreateImage(cvGetSize(im32f), IPL_DEPTH_32F, 1);
IplImage* backproject32f = cvCreateImage(cvGetSize(im), IPL_DEPTH_32F, 1);
IplImage* backproject8u = cvCreateImage(cvGetSize(im), IPL_DEPTH_8U, 1);
IplImage* bw = cvCreateImage(cvGetSize(im), IPL_DEPTH_8U, 1);
IplConvKernel* kernel = cvCreateStructuringElementEx(3, 3, 1, 1, MORPH_ELLIPSE);

cvSplit(im32f, b, g, r, NULL);
IplImage* planes[] = {b, g, r};
cvCalcHist(planes, hist);

// find min and max values of histogram bins
float minval, maxval;
cvGetMinMaxHistValue(hist, &minval, &maxval);

// threshold the histogram. this sets the bin values that are below the threshold to zero
cvThreshHist(hist, maxval/32);

// backproject the thresholded histogram. backprojection should contain higher values for the
// background and lower values for the foreground
cvCalcBackProject(planes, backproject32f, hist);

// convert to 8u type
double min, max;
cvMinMaxLoc(backproject32f, &min, &max);
cvConvertScale(backproject32f, backproject8u, 255.0 / max);

// threshold backprojected image. this gives us the background
cvThreshold(backproject8u, bw, 10, 255, CV_THRESH_BINARY);

// some morphology on background
cvDilate(bw, bw, kernel, 1);
cvMorphologyEx(bw, bw, NULL, kernel, MORPH_CLOSE, 2);

// get the foreground
cvSubRS(bw, cvScalar(255, 255, 255), bw);
cvMorphologyEx(bw, bw, NULL, kernel, MORPH_OPEN, 2);
cvErode(bw, bw, kernel, 1);

// find contours of the foreground
//CvMemStorage* storage = cvCreateMemStorage(0);
//CvSeq* contours = 0;
//cvFindContours(bw, storage, &contours);
//cvDrawContours(im, contours, CV_RGB(255, 0, 0), CV_RGB(0, 0, 255), 1, 2);

// grabcut
Mat color(im);
Mat fg(bw);
Mat mask(bw->height, bw->width, CV_8U);

mask.setTo(GC_PR_BGD);
mask.setTo(GC_PR_FGD, fg);

Mat bgdModel, fgdModel;
grabCut(color, mask, Rect(), bgdModel, fgdModel, GC_INIT_WITH_MASK);

Mat gcfg = mask == GC_PR_FGD;

vector<vector<cv::Point>> contours;
vector<Vec4i> hierarchy;
findContours(gcfg, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
for(int idx = 0; idx < contours.size(); idx++)
{
    drawContours(color, contours, idx, Scalar(0, 0, 255), 2);
}

// cleanup ...

UPDATE: Мы можем сделать это, используя интерфейс С++, как показано ниже.

const int channels[] = {0, 1, 2};
const int histSize[] = {32, 32, 32};
const float rgbRange[] = {0, 256};
const float* ranges[] = {rgbRange, rgbRange, rgbRange};

Mat hist;
Mat im32fc3, backpr32f, backpr8u, backprBw, kernel;

Mat im = imread("bFly6.jpg");

im.convertTo(im32fc3, CV_32FC3);
calcHist(&im32fc3, 1, channels, Mat(), hist, 3, histSize, ranges, true, false);
calcBackProject(&im32fc3, 1, channels, hist, backpr32f, ranges);

double minval, maxval;
minMaxIdx(backpr32f, &minval, &maxval);
threshold(backpr32f, backpr32f, maxval/32, 255, THRESH_TOZERO);
backpr32f.convertTo(backpr8u, CV_8U, 255.0/maxval);
threshold(backpr8u, backprBw, 10, 255, THRESH_BINARY);

kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));

dilate(backprBw, backprBw, kernel);
morphologyEx(backprBw, backprBw, MORPH_CLOSE, kernel, Point(-1, -1), 2);

backprBw = 255 - backprBw;

morphologyEx(backprBw, backprBw, MORPH_OPEN, kernel, Point(-1, -1), 2);
erode(backprBw, backprBw, kernel);

Mat mask(backpr8u.rows, backpr8u.cols, CV_8U);

mask.setTo(GC_PR_BGD);
mask.setTo(GC_PR_FGD, backprBw);

Mat bgdModel, fgdModel;
grabCut(im, mask, Rect(), bgdModel, fgdModel, GC_INIT_WITH_MASK);

Mat fg = mask == GC_PR_FGD;

Ответ 2

Я бы рассмотрел несколько вариантов. Мое предположение заключается в том, что камера не двигается. Я не использовал изображения или написал какой-либо код, так что это в основном из опыта.

  • Вместо того, чтобы просто искать края, попробуйте выделить фон с помощью алгоритма сегментации. Смесь Гаусса может помочь в этом. Учитывая набор изображений по одной и той же области (например, видео), вы можете отменить регионы, которые являются постоянными. Затем появятся новые предметы, такие как инструменты. Затем подключенные компоненты могут использоваться на блоках.

    • Я бы посмотрел на алгоритмы сегментации, чтобы увидеть, можете ли вы оптимизировать условия, чтобы сделать эту работу для вас. Один из основных моментов заключается в том, чтобы убедиться, что ваша камера стабильна или вы стабилизировали изображения, предварительно предварительно обработанные.
  • Я хотел бы использовать точки интереса для определения областей изображения с большим количеством нового материала. Учитывая, что фон относительно прост, маленькие объекты, такие как иглы, создадут кучу точек интереса. Полотенце должно быть намного реже. Возможно, наложение обнаруженных точек интереса на отслеживаемый компонентный компонент даст вам метрику плотности, которую вы можете тогда порождать. Если подключенный компонент имеет большое отношение точек интереса для области элемента, то это интересный объект.

    • В этой заметке вы можете даже очистить подключенный компонентный след, используя выпуклый корпус, чтобы обрезать обнаруженные объекты. Это может помочь таким ситуациям, как медицинский инструмент, бросающий тень на полотенце, которое растягивает область компонента. Это предположение, но точки интереса могут дать вам больше информации, чем просто края.
  • Наконец, учитывая, что у вас есть стабильный фон с четкими объектами, я бы взглянул на Bag-of-Features, чтобы увидеть, можете ли вы просто обнаружить каждый отдельный объект на изображении. Это может быть полезно, поскольку, как представляется, для объектов на этих изображениях существует согласованная структура. Вы можете создать большую базу данных изображений, таких как иглы, марля, ножницы и т.д. Тогда BoF, находящийся в OpenCV, найдет этих кандидатов для вас. Вы также можете смешивать его с другими операциями, которые вы делаете для сравнения результатов.

     -

Ответ 3

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

//take the rect of the contours

Rect rect = Imgproc.boundingRect(contours.get(i));

if (rect.width < inputImageWidth / 2 && rect.height < inputImageHeight / 2)

//then continue to draw or use for next purposes.