Страницы

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

Показаны сообщения с ярлыком emgucv. Показать все сообщения
Показаны сообщения с ярлыком emgucv. Показать все сообщения

четверг, 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(). Если сумма площадей контуров превышает некий порог, вычисленный эмпирически, то значит автомобиль на данном парковочном месте можно считать обнаруженным.

пятница, 10 января 2020 г.

Обнаружение машин на автостоянке

#c_sharp #opencv #emgucv


Добрый день. Пишу программу по обнаружению свободных мест на стоянке с использованием
EmguCV. Имеется: изображение с пустой стоянкой, также есть видео-поток с этой же стоянки
но с машинами. Вопрос в обнаружении машин, еще лучше с примерами кода (с++ также подойдет,
ибо emgucv является оберткой). Каскады Хаара отбросил сразу, так как для данной задачи
использовать это слишком "умно". Думаю в сторону "вычитания изображений" (т.к имеется
как и пустая, так и заполненная стоянка) или "перепадов" на изображении (асфальт-авто).
Так же хотелось бы услышать советы по этому поводу, т.к материалов не так много.
Из найденого: Automatic Parking Lot Classification
    


Ответы

Ответ 1



Боюсь, не помогут в решении этой задачи ни детектор движения (BackgroundSubtractorMOG2), ни тем более межкадровая разница. Стоянка - это как правило улица, а значит погода и перепады освещения. Тени в конце концов, которые запросто делают свою границу с солнечной частью более контрастной по сравнению с цветом автомобиля, близкого к серому. И всё это ещё и меняется во времени. В облачно-солнечную погоду с ветром особенно ощутимо. Не зря по приведённой ссылке автор проекта уточняет, что были подготовлены разные наборы обучающих изображений под различные погодные условия. Ну и конечно нельзя не упомянуть пешеходов, да и самих водителей, снующих вокруг машин по поводу и без, и создающих бесконечные ложные срабатывания. Алгоритмы, работающие с так называемыми feature points (точки особенностей), имеющиеся в OpenCV, для этой задачи также не работают. Даже те из них, что предназначены для оценки площадей. Вообще с улицей сложно. На мой взгляд, тут, кроме как машинное обучение на заранее подготовленных образцах и с большим их количеством, ничто не поможет. Всё по принципу того же Хаара, от которого отказались. Необязательно именно этот алгоритм (очень вероятно, что он не подойдёт), но любой другой, который сможет по неким признакам на асфальте уловить, что в конкретно таком-то месте машины нет. Дополнение Как вариант, можно попытаться использовать оператор Кэнни. Этот алгоритм имеется в инструментарии OpenCV. Возьмите два изображения одного и того же парковочного места с автомобилем и без. Попробуйте подобрать коэффициенты Кэнни для обоих изображений таким образом, чтобы на том, где есть автомобиль, было обнаружено существенно больше границ, чем на том, где автомобиля нет (как это ни парадоксально, но на "ровном, чистом, одноцветном" асфальте при определённых условиях можно обнаружить то ещё мессиво из линий/перепадов цвета). Если сразу не получится, то перед Кэнни вставляйте размытие (этих алгоритмов в OpenCV несколько). Далее, если получилось, берите другие два изображения с автомобилем и без того же самого парковочного места, но уже сдвинутые во времени. Например, если первая пара была с утренней съёмки, то берите вечернюю. Для второй пары пробуйте те же самые коэффициенты, что получили для первой. В случае успеха меняйте изображения парковочного места и автомобиля на нём. И точно также пытайтесь применить те же самые коэффициенты, но уже к новым изображениям. При наличии терпения на всё про всё уйдёт максимум пару часов, но зато будет абсолютно ясно, корректный ли выбран путь в сторону анализа цветовой разницы или следует обратиться к иным инструментам. Этот эксперимент позволит сэкономить время, если что-то пойдёт не так, либо утвердит в правоте первичных рассуждений.

вторник, 23 апреля 2019 г.

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

Имею изображение после использования порога: Вопрос в обнаружении белых зон: 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)); } } }


Ответ

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

понедельник, 25 февраля 2019 г.

Обнаружение машин на автостоянке

Добрый день. Пишу программу по обнаружению свободных мест на стоянке с использованием EmguCV. Имеется: изображение с пустой стоянкой, также есть видео-поток с этой же стоянки но с машинами. Вопрос в обнаружении машин, еще лучше с примерами кода (с++ также подойдет, ибо emgucv является оберткой). Каскады Хаара отбросил сразу, так как для данной задачи использовать это слишком "умно". Думаю в сторону "вычитания изображений" (т.к имеется как и пустая, так и заполненная стоянка) или "перепадов" на изображении (асфальт-авто). Так же хотелось бы услышать советы по этому поводу, т.к материалов не так много. Из найденого: Automatic Parking Lot Classification


Ответ

Боюсь, не помогут в решении этой задачи ни детектор движения (BackgroundSubtractorMOG2), ни тем более межкадровая разница.
Стоянка - это как правило улица, а значит погода и перепады освещения. Тени в конце концов, которые запросто делают свою границу с солнечной частью более контрастной по сравнению с цветом автомобиля, близкого к серому. И всё это ещё и меняется во времени. В облачно-солнечную погоду с ветром особенно ощутимо. Не зря по приведённой ссылке автор проекта уточняет, что были подготовлены разные наборы обучающих изображений под различные погодные условия. Ну и конечно нельзя не упомянуть пешеходов, да и самих водителей, снующих вокруг машин по поводу и без, и создающих бесконечные ложные срабатывания.
Алгоритмы, работающие с так называемыми feature points (точки особенностей), имеющиеся в OpenCV, для этой задачи также не работают. Даже те из них, что предназначены для оценки площадей. Вообще с улицей сложно.
На мой взгляд, тут, кроме как машинное обучение на заранее подготовленных образцах и с большим их количеством, ничто не поможет. Всё по принципу того же Хаара, от которого отказались. Необязательно именно этот алгоритм (очень вероятно, что он не подойдёт), но любой другой, который сможет по неким признакам на асфальте уловить, что в конкретно таком-то месте машины нет.
Дополнение
Как вариант, можно попытаться использовать оператор Кэнни. Этот алгоритм имеется в инструментарии OpenCV.
Возьмите два изображения одного и того же парковочного места с автомобилем и без. Попробуйте подобрать коэффициенты Кэнни для обоих изображений таким образом, чтобы на том, где есть автомобиль, было обнаружено существенно больше границ, чем на том, где автомобиля нет (как это ни парадоксально, но на "ровном, чистом, одноцветном" асфальте при определённых условиях можно обнаружить то ещё мессиво из линий/перепадов цвета).
Если сразу не получится, то перед Кэнни вставляйте размытие (этих алгоритмов в OpenCV несколько).
Далее, если получилось, берите другие два изображения с автомобилем и без того же самого парковочного места, но уже сдвинутые во времени. Например, если первая пара была с утренней съёмки, то берите вечернюю. Для второй пары пробуйте те же самые коэффициенты, что получили для первой.
В случае успеха меняйте изображения парковочного места и автомобиля на нём. И точно также пытайтесь применить те же самые коэффициенты, но уже к новым изображениям.
При наличии терпения на всё про всё уйдёт максимум пару часов, но зато будет абсолютно ясно, корректный ли выбран путь в сторону анализа цветовой разницы или следует обратиться к иным инструментам. Этот эксперимент позволит сэкономить время, если что-то пойдёт не так, либо утвердит в правоте первичных рассуждений.