Перейти к содержимому
Compvision.ru
aleks_neo

Поиск объекта на изображении

Recommended Posts

Здравствуйте, совсем недавно столкнулся с задачей поиска объекта на изображении, изначально пробовал писать свои функции для поиска(т.к сам объект не меняет своих размеров/яркости и прочих параметров, все что с ним происходит - смена позиции и вращение на различное число градусов в плоскости экрана), но самописные функции меня не устроили скоростью работы, поэтому я решил попробовать opencv (точнее я использую emgu - C#-обертку над opencv). Пробую использовать detector для этих дел:


            Image<Gray, Byte> objectToFind = new Image<Gray, Byte>(@"ResToFeatureDetect.bmp");  //сам обьект, который ищем на скрине      

            Image<Gray, Byte> ScreenForSearch = new Image<Gray, byte>(@"ExampleToFeatureDetect.bmp"); // скрин


            SIFTDetector det = new SIFTDetector();


            VectorOfKeyPoint keyPointOfObj = det.DetectKeyPointsRaw(objectToFind, null);

            VectorOfKeyPoint keyPointOfScreen = det.DetectKeyPointsRaw(ScreenForSearch, null);


            imageBox1.Image = Features2DToolbox.DrawKeypoints(objectToFind, keyPointOfObj, new Bgr(Color.Red), Features2DToolbox.KeypointDrawType.DEFAULT); //вывожу картину, чтобы удостовериться, что кейпоинты найдены      

И вот в чем встал вопрос: как и чем сравнить кейпоинт(изображение объекта мало, поэтому находится только один кейпоинт на нем) объекта с кейпоинтами скрина?

И еще пара вопросов: почему детектор работает только с серыми изображениями? И правильно ли я подхожу к решению моей задачи или есть более подходящие к моему случаю методы поиска?

И еще столкнулся с тем, что gpu-аналоги тех функций, что я использовал, работали заметно медленней(3-4 секунды), нежели cpu-версии(<1 сек.), с чем это связано?

В прикреплении пример скрина и сам объект для поиска.

post-6642-0-10436300-1374484852_thumb.pn

post-6642-0-63328800-1374484864_thumb.pn

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Можно попробовать выделить по цвету.

Сначала перевести в HSV, найти гистограмму искомого фрагмента и провести обратную проекцию.

http://docs.opencv.org/doc/tutorials/imgproc/histograms/back_projection/back_projection.html

После этого морфология (см. erode dilate) и поиск контуров.

Если это получится, то дальше все должно быть очевидно.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Можно попробовать выделить по цвету.

Сначала перевести в HSV, найти гистограмму искомого фрагмента и провести обратную проекцию.

http://docs.opencv.org/doc/tutorials/imgproc/histograms/back_projection/back_projection.html

После этого морфология (см. erode dilate) и поиск контуров.

Если это получится, то дальше все должно быть очевидно.

Ох, сколько всего нового :wacko:

Этот способ вы предложили из-за того, что мой не будет работать? Или все-таки можно как-то кейпоинты сравнить в моем говнокоде?

upd: кажется нашел функцию для сравнения http://www.emgu.com/wiki/files/2.4.2/document/html/ce99f5ca-1c15-5a30-3949-6c1801b71dd8.htm

но не понятно что за матрицы дескрипторов(как их получить?) ей кормятся в аргументах. первые два аргумента - это дескрипторы обьекта и скрина, а что за distance в 3ем аргументе?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
И вот в чем встал вопрос: как и чем сравнить кейпоинт(изображение объекта мало, поэтому находится только один кейпоинт на нем) объекта с кейпоинтами скрина?

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

То ключевая точка по объему информации не очень сильно будет отличаться от всего изображения, разве что у ключевой точки добавится инвариантность к поворотам и масштабу.

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

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

И еще пара вопросов: почему детектор работает только с серыми изображениями?
Есть реализации и цветных дескрипторов, например: http://koen.me/research/colordescriptors/

И еще столкнулся с тем, что gpu-аналоги тех функций, что я использовал, работали заметно медленней(3-4 секунды), нежели cpu-версии(<1 сек.), с чем это связано?

Это связано с тем, что копирование CPU <-> GPU ОЧЕНЬ медленное, поэтому GPU-шные операции обычно применяют к очень большим массивам данных, и стараются не вытаскивать из лишний раз на CPU.

ЗЫ: А можно просто сгладить и отфильтровать по пороговым значениям HSV.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

легче всего использовать выделение по цвету и размеру(если нет похожих объектов)

если не меняется масштаб то лучше использовать разность пикселей или коэффициент корреляции (проблема с поворотом решается либо заготовкой нескольких темплейтов с шагом в N градусов либо проекцией logpolar)

дескрипторы тут не самое лучшее решение, даже если проблема что дескрипторы только для серого изображения никто не мешает посчитать их для 3-х каналов отдельно.

вот еще

http://www.compvision.ru/forum/index.php?showtopic=1152

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

ЗЫ: А можно просто сгладить и отфильтровать по пороговым значениям HSV.

А вот это, кажется, мне уже под силу, попробую. Спасибо!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

А вот это, кажется, мне уже под силу, попробую. Спасибо!

Что-то не выходит. Хочу сделать так: оба изображения преобразую в hsv, сглаживаю, разделяю на каналы, ищу минимумы и максимумы каналов объекта, дальше в цикле(с шагом равным размеру объекта) иду по скрину и для каждой такой области вычисляю мин/макс, сравниваю их таковыми у объекта, если различие минимально - запоминаю координаты этой области.

Вот код:


        private void button1_Click(object sender, EventArgs e)

        {

            var ObjStr = @"C:\Users\alex\Downloads\agat\Image\image 1.bmp";

            var ScreenStr = @"C:\Users\alex\Downloads\ExampleToFeatureDetect.bmp";

            ObjSearcher(ObjStr,ScreenStr);

        }


        Image<Hsv, Byte> HsvMinMaxFinding(string ImgStr,ref double MinHVal,ref double MaxHVal,

            ref double MinSVal,ref double MaxSVal,ref double MinVVal,ref double MaxVVal)

        {

            var Img = new Image<Rgb, Byte>(ImgStr);

            var Hsv = new Image<Hsv, Byte>(Img.Width, Img.Height);

            CvInvoke.cvCvtColor(Img, Hsv, COLOR_CONVERSION.CV_RGB2HSV);

            CvInvoke.cvSmooth(Hsv, Hsv, SMOOTH_TYPE.CV_BLUR, 3, 3, 0, 0);

            IntPtr h_plane, s_plane, v_plane;

            h_plane = CvInvoke.cvCreateImage(CvInvoke.cvGetSize(Hsv), IPL_DEPTH.IPL_DEPTH_8U, 1);

            s_plane = CvInvoke.cvCreateImage(CvInvoke.cvGetSize(Hsv), IPL_DEPTH.IPL_DEPTH_8U, 1);

            v_plane = CvInvoke.cvCreateImage(CvInvoke.cvGetSize(Hsv), IPL_DEPTH.IPL_DEPTH_8U, 1);

            CvInvoke.cvCvtPixToPlane(Hsv,h_plane,s_plane,v_plane,(IntPtr) null);

            Point MinL, MaxL;

            MinL = new Point(0,0);

            MaxL = new Point(0,0);

            CvInvoke.cvMinMaxLoc(h_plane,ref MinHVal,ref MaxHVal, ref MinL, ref MaxL, (IntPtr)null);

            CvInvoke.cvMinMaxLoc(s_plane, ref MinSVal, ref MaxSVal, ref MinL, ref MaxL, (IntPtr)null);

            CvInvoke.cvMinMaxLoc(v_plane, ref MinVVal, ref MaxVVal, ref MinL, ref MaxL, (IntPtr)null);

            return Hsv;

        }

        void HsvMinMaxFinding(Image<Hsv, Byte> Hsv , ref double MinHVal, ref double MaxHVal,

    ref double MinSVal, ref double MaxSVal, ref double MinVVal, ref double MaxVVal)

        {   IntPtr h_plane, s_plane, v_plane;

            h_plane = CvInvoke.cvCreateImage(CvInvoke.cvGetSize(Hsv), IPL_DEPTH.IPL_DEPTH_8U, 1);

            s_plane = CvInvoke.cvCreateImage(CvInvoke.cvGetSize(Hsv), IPL_DEPTH.IPL_DEPTH_8U, 1);

            v_plane = CvInvoke.cvCreateImage(CvInvoke.cvGetSize(Hsv), IPL_DEPTH.IPL_DEPTH_8U, 1);

            CvInvoke.cvCvtPixToPlane(Hsv, h_plane, s_plane, v_plane, (IntPtr)null);

            Point MinL, MaxL;

            MinL = new Point(0, 0);

            MaxL = new Point(0, 0);

            CvInvoke.cvMinMaxLoc(h_plane, ref MinHVal, ref MaxHVal, ref MinL, ref MaxL, (IntPtr)null);

            CvInvoke.cvMinMaxLoc(s_plane, ref MinSVal, ref MaxSVal, ref MinL, ref MaxL, (IntPtr)null);

            CvInvoke.cvMinMaxLoc(v_plane, ref MinVVal, ref MaxVVal, ref MinL, ref MaxL, (IntPtr)null);


        }



        void ObjSearcher(string ObjStr,string ScreenStr)

        {

            double ObjMinHVal = 0, ObjMaxHVal = 0, ObjMinSVal = 0, ObjMaxSVal = 0, ObjMinVVal = 0, ObjMaxVVal = 0;

            double ScreenMinHVal = 0, ScreenMaxHVal = 0, ScreenMinSVal = 0, ScreenMaxSVal = 0, ScreenMinVVal = 0, ScreenMaxVVal = 0;


            var ObjHsv = HsvMinMaxFinding(ObjStr, ref ObjMinHVal, ref ObjMaxHVal, ref ObjMinSVal, ref ObjMaxSVal, ref ObjMinVVal,

                ref ObjMaxVVal);

            var ScreenHsv = HsvMinMaxFinding(ScreenStr, ref ScreenMinHVal, ref ScreenMaxHVal, ref ScreenMinSVal, ref ScreenMaxSVal,

               ref ScreenMinVVal, ref ScreenMaxVVal);

            int k = 5;


            for (int x = 0; x < ScreenHsv.Height; x+=ObjHsv.Height)

            {

                for (int y = 0; y < ScreenHsv.Width; y+= ObjHsv.Width)

                {

                    using (var copy = ScreenHsv.Copy(new Rectangle(x, y, ObjHsv.Width, ObjHsv.Height)))

                    {

                        HsvMinMaxFinding(copy, ref ScreenMinHVal, ref ScreenMaxHVal, ref ScreenMinSVal, ref ScreenMaxSVal,

                    ref ScreenMinVVal, ref ScreenMaxVVal);


                        if ((Math.Abs(ScreenMaxHVal - ObjMaxHVal) <= k) && (Math.Abs(ScreenMaxSVal - ObjMaxSVal) <= k) &&

                            ((Math.Abs(ScreenMaxVVal - ObjMaxVVal) <= k) && (Math.Abs(ScreenMinVVal - ObjMinVVal) <= k)))

                        {

                            richTextBox1.Text += "+1";

                            ScreenHsv.Draw(new Rectangle(x, y, 10, 10), new Hsv(0, 100, 100), 1);

                        }

                    }


                }



            }


            imageBox1.Image = ScreenHsv;

        }

Но я так и не дождался окончания его работы ( минут 15 ждал ради интереса). Что я делаю не так?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Скорее Вам больше подойдет InRange, а не MinMaxLoc.

Разделяете на каналы, затем InRange по каждому каналу, и по логическому "И", можно в принципе и плюсовать просто.

По поводу зависания, выведите что нибудь из цикла в консоль и посмотрите.

UPD: Упс, richTextBox1.Text += "+1"; уберите из внутреннего цикла :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Скорее Вам больше подойдет InRange, а не MinMaxLoc.

Разделяете на каналы, затем InRange по каждому каналу, и по логическому "И", можно в принципе и плюсовать просто.

По поводу зависания, выведите что нибудь из цикла в консоль и посмотрите.

UPD: Упс, richTextBox1.Text += "+1"; уберите из внутреннего цикла :)

Попробовал InRange, теперь получается нужно на чб-изображении искать контуры и по их форме/длине находить позицию объекта?

>UPD: Упс, richTextBox1.Text += "+1"; уберите из внутреннего цикла :)

пробовал, не помогло

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

По поводу долгой работы понятно, особо вдаваться не хочу, смотрю по циклам, получаю 2 вложенных, если не обсчитался, то есть это ширина^2*высота^2 точек перебирается причем не просто перебирается, а в каждом внутреннем цикле каждый раз выделяется память под изображения, можно же один раз это сделать. Многовато. Можно не копировать, а назначать ROI (см. SetImageROI).

У меня так было сделано в одном проекте:

//----------------------------------------------------------------------
// Детектор контуров, вывод результатов
//----------------------------------------------------------------------
void CDetector::DetectContour(Mat& img, vector<Rect>& Rects,vector<Point2d>& centers)
{
double area=0;
Rects.clear();
centers.clear();
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
Mat edges=img.clone();
Canny(img, edges, 50, 190, 3);
findContours(edges,contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point());
if(contours.size()>0)
{
for( int i = 0; i < contours.size(); i++ )
{
Rect r=cv::boundingRect(contours[i]);
Rects.push_back(r);
centers.push_back((r.br()+r.tl())*0.5);
}
}
}[/code]

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ура, получилось, спасибо! (результаты в прикреплении). Сделал как вы и посоветовали: размытие, inrange, выделение контуров, далее отбрасываем ненужные по периметру/площади и готово.

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

post-6642-0-17066500-1374674368_thumb.pn

post-6642-0-40742100-1374674410_thumb.pn

post-6642-0-17855600-1374674555_thumb.pn

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Надо сформулировать, чем он отличается от правильных объектов

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Надо сформулировать, чем он отличается от правильных объектов

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

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

Не стал создавать тему ради одного мелкого вопроса, спрошу здесь: http://www.emgu.com/wiki/files/1.3.0.0/html/cd400942-e670-bb78-9491-37ac08f56863.htm - функция аппроксимации контура, первый параметр accuracy/точность немного непонятен, в каких пределах он должен лежать и что вообще понимается под "точностью аппроксимации" ? Гугл не особо помог разобраться, только лишь в том, что в качестве этого параметра часто кормят периметр контура умноженный на некоторую константу. Но почему и для чего - осталось загадкой.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

И еще один нубовопрос от меня. Нужно поместить чб изображение (res), в середину большего изображения(im), пишу следующее:


            Image<Gray, Byte> im = new Image<Gray, Byte>(100,100);

            im.ROI = new Rectangle(50, 50, res.Width, res.Height);

            im = im.Add(res);

            im.ROI = Rectangle.Empty;

            imageBox1.Image = im;

но итоговое изображение по размеру становиться равным res, фактически его копией, как такое исправить?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Нужно делать так:

1) назначить roi на источнике и приемнике (одинаковые по размеру и типу).

2) скопировать из источника в приемник (на С++ есть метод copyTo )

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

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

Нужно делать так:

1) назначить roi на источнике и приемнике (одинаковые по размеру и типу).

2) скопировать из источника в приемник (на С++ есть метод copyTo )

Спасибо, как раз copyTo мне и нужен был!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×