Страницы

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

четверг, 13 февраля 2020 г.

Обнаружение зон при использовании порога

#c_sharp #opencv #emgucv


Имею изображение после использования порога: 

Вопрос в обнаружении белых зон:
1) Используя многоугольник (который рисует пользователь (хотелось бы еще узнать как
рисовать на Mat)) и узнавать процент закрашенности внутри этой зоны
2) Автоматически (рисуя вокруг зон квадраты)
Любой из двух вариантов подойдет.

Дополнение
Для внесения ясности попытался сфотошопить то, чтобы я хотел получить.
  

Попробовав так, получил краш при использовании FindContours (баг EmguCV или моя ошибка?)

using (Mat hirarchy = new Mat())
{
    using (VectorOfPoint countours = new VectorOfPoint())
    {
        CvInvoke.FindContours(_diffMat, countours, hirarchy, RetrType.Tree, ChainApproxMethod.ChainApproxSimple);

        for (int i = 0; i < countours.Size; i++)
        {
            CvInvoke.DrawContours(_diffMat, countours, i, new MCvScalar(0,0,255));
        }
    }
}

    


Ответы

Ответ 1



Лучше начать, наверное, сразу со второго пункта, так как могу предположить, что именно он является приоритетным. Бинаризация (пороговая обработка) - это по сути процесс, при котором количество информации, имеющейся на изображении, сводится к минимуму. Этот факт является одновременно и плюсом и минусом, поскольку вместе с непредставляющей интереса информацией стирается и та, что могла бы существенно помочь в решении задачи. Если обратиться к изображению с "фотошопом" и в частности к некоторым из многоугольников по центру, границы которых отмечены красными линиями, то не трудно заметить наличие с правой стороны от них белых "зон". Эти "зоны" имеют тот же размер и в целом никак не выделяются от точно таких же белых "зон", но уже входящих в области интереса (парковочные места). Белые "зоны" внутри области интереса также могут иметь произвольный размер и часто их нельзя рассматривать как единое целое с другими, расположенными неподалёку, поскольку они имеют самостоятельные границы и относительно близкий размер с теми, что не входят в область интереса. Ко всему прочему, "зоны" между двумя областями интереса могут находиться ближе друг другу (по расстоянию между центральными моментами контуров), нежели чем "зоны" одного и того же парковочного места. Все вышеперечисленные исключения в полной мере представлены на изображении "фотошопа", что фактически сводит на нет возможность корректной кластеризации контуров "зон" в объекты "автомобиль". При помощи бинаризации чрезвычайно редко удаётся получить приемлемый результат, если исходное изображение содержит естественную среду. Однако, если допустить вмешательство человека в работу алгоритма, а именно подсказать последнему настоящие границы объекта путём ручного выделения прямоугольников парковочных мест, то вся задача сведётся к достаточно простой операции. Пример бинаризации изображения на C++ и C# (EmguCV). Предположим, имеется исходник: Загружаем его, переводим в оттенки серого и бинаризуем: C++: cv::Mat src_mat = cv::imread("img.jpg"); if(src_mat.empty()) return; cv::Mat gry_mat; cv::cvtColor(src_mat, gry_mat, cv::COLOR_BGR2GRAY); cv::Mat bin1_mat, bin2_mat; cv::threshold(gry_mat, bin1_mat, 230, 255, cv::THRESH_BINARY); cv::threshold(gry_mat, bin2_mat, 25, 255, cv::THRESH_BINARY_INV); cv::Mat bin_mat = bin1_mat + bin2_mat; C#: Mat srcMat = new Mat("img.jpg", LoadImageType.AnyColor); if(srcMat.IsEmpty) return; Mat grayMat = new Mat(); Mat bin1Mat = new Mat(); Mat bin2Mat = new Mat(); Mat binMat = new Mat(); CvInvoke.CvtColor(srcMat, grayMat, ColorConversion.Bgr2Gray); CvInvoke.Threshold(grayMat, bin1Mat, 230, 255, ThresholdType.Binary); CvInvoke.Threshold(grayMat, bin2Mat, 25, 255, ThresholdType.BinaryInv); CvInvoke.Add(bin1Mat, bin2Mat, binMat); Здесь выполнена попытка бинаризации по верхней и нижней границам. Коэффициенты подобраны вручную и могут отличаться по оптимальности для других изображений. Таким образом получается сохранить больше полезных деталей: Далее, если мы начнём искать контуры и нарисуем их: C++: std::vector > contours; cv::findContours(bin_mat.clone(), contours , cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); cv::Mat ctr_mat = cv::Mat::zeros(bin_mat.size(), CV_8U); cv::drawContours(ctr_mat, contours, -1, cv::Scalar::all(255), 1); C#: VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint(); Mat hierarchy; CvInvoke.FindContours(binMat, contours, hierarchy, RetrType.External, ChainApproxMethod.ChainApproxSimple); Mat ctrMat = new Mat(binMat.Size, DepthType.Cv8U, 3); CvInvoke.DrawContours(ctrMat, contours, -1, new MCvScalar(255)); ... то получим такую картину: Контуров оказалось довольно много, и уже закрадывается сомнение в том, что удастся собрать контуры отдельных частей в единый контур автомобиля. Попробуем отфильтровать часть из них, например, по площади, заодно нарисовав их опоясывающие "квадратики" на исходном изображении: C++: std::vector > contours; cv::findContours(bin_mat.clone(), contours , cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); std::vector > contours2; for(auto itr = contours.begin(); itr != contours.end(); ++itr) { if(cv::contourArea(*itr) > 8) { contours2.push_back(*itr); cv::Rect rc = cv::boundingRect(*itr); cv::rectangle(src_mat, rc, cv::Scalar(0,0,255)); } } cv::Mat ctr_mat = cv::Mat::zeros(bin_mat.size(), CV_8U); cv::drawContours(ctr_mat, contours2, -1, cv::Scalar::all(255), 1); C#: VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint(); VectorOfVectorOfPoint contours2 = new VectorOfVectorOfPoint(); Mat hierarchy; CvInvoke.FindContours(binMat, contours, hierarchy, RetrType.External, ChainApproxMethod.ChainApproxSimple); for (int i = 0; i < contours.Size; ++i) { if (CvInvoke.ContourArea(contours[i]) > 8) { contours2.Push(contours[i]); Rectangle rc = CvInvoke.BoundingRectangle(contours[i]); CvInvoke.Rectangle(srcMat, rc, new MCvScalar(0, 0, 255)); } } Mat ctrMat = new Mat(binMat.Size, DepthType.Cv8U, 3); CvInvoke.DrawContours(ctrMat, contours2, -1, new MCvScalar(255)); Контуров стало значительно меньше, "шума" поубавилось, но мы тем самым продолжаем терять информацию и об объектах интереса (автомобилях), делая их в итоге всё более непохожими на исходные. Те, что расположены поближе к камере, ещё куда ни шло, но чем дальше, тем ситуация становится плачевнее: В конце концов, может быть стоит нарисовать опоясывающие контуры прямоугольники, и тогда станет видно, за что возможно было бы зацепиться с тем, чтобы объединить разрозненные части каждого автомобиля в единое целое: Но ракурс съёмки не позволяет усомниться: части различных объектов расположены на относительно тех же расстояниях, что части одного и того же объекта, приводя к невозможности корректной кластеризации. А поскольку при бинаризации у нас других факторов, за которые возможно было бы зацепиться, попросту нет, то и задача с автоматическим определением границ автомобилей в имеющихся условиях становится нерешаемой. Ну может быть получится один-два автомобиля, находящихся ближе всего к камере, но не более. В таком случае не остаётся ничего более, как вручную определить границы парковочных мест. Эту задачу на OpenCV обычно не перекладывают, т.к. у него функционал работы с мышью (с тем, чтобы пользователь указал на изображении координаты точек многоугольников парковочных мест) в целом ограничен и реализуют при помощи фреймворков общего назначения. Для получения матрицы изображения парковочного места из заранее определённых координат в кадре можно воспользоваться простой конструкцией: cv::Mat parking_lot_mat = src_mat(cv::Rect(0,0,100,100)).clone(); Теперь parking_lot_mat будет содержать часть исходного изображения по координатам x, равное 0, и y, равное 0, и будет размером 100х100 пикселей. Далее производим всю ту же работу по бинаризации, что делали над большим изображением. Под конец останется найти контуры и их площади при помощи метода: cv::contourArea(). Если сумма площадей контуров превышает некий порог, вычисленный эмпирически, то значит автомобиль на данном парковочном месте можно считать обнаруженным.

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

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