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

Сравнение с эталоном и указание различий на изображении

Recommended Posts

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

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

Предполагаемый путь решения первой части задачи - сравнивать гистограммы яркости с помощью cvCompareHist и в качестве процентного результата использовать полученную величину на выходе. Но как при этом пометить отличающиеся участки на изображеии не представляю. В общем я не уверен, что это лучший путь решения.

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

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


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

Попробуй просто вычесть из одного изображения другое и посмотреть результат.

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


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

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

Затем бинаризация по порогу, cvErode + cvDilate, затем компоненты связности и т.д. по накатанной.

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


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

Попробуй просто вычесть из одного изображения другое и посмотреть результат.

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

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


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

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

Затем бинаризация по порогу, cvErode + cvDilate, затем компоненты связности и т.д. по накатанной.

Мне-бы пример какой-нибуть, близко-лежащий к решению данных двух вопросов. Причем больше интересует именно второй вопрос - выделение различий на изображении.

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


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

Мне-бы пример какой-нибуть, близко-лежащий к решению данных двух вопросов. Причем больше интересует именно второй вопрос - выделение различий на изображении.

Самому сейчас лень писать )

Вот здесь вроде похожее что-то:

http://dasl.mem.drexel.edu/~noahKuntz/openCVTut8.html

http://sublimated.wordpress.com/2011/01/25/image-difference-with-opencv/

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


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

Самому сейчас лень писать )

Вот здесь вроде похожее что-то:

http://dasl.mem.drexel.edu/~noahKuntz/openCVTut8.html

http://sublimated.wordpress.com/2011/01/25/image-difference-with-opencv/

Здесь как я понял о вычитании изображений, это хорошо. А вот как выделить конкретные различия на втором изображении с помощью стандартных функций openCV сия тайна для меня велика есть. Только начал заниматься вплотную данной библиотекой!

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


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

Работа с контурами:

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

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


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

Работа с контурами:

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

Внимательно посмотрел. Все равно не то. Это и в самой библиотеке опенсв есть пример, как контуры выделить, да только это нето что нужно мне не надо искать все контуры всех объектов на одном конкретном изображении, а надо анализировать два изображения относительно друг друга и найдя отличия (ЭТО ключевой момент!) выделить именно ЭТИ отличия, а не просто найти контуры похожих объектов на одном изображении...или все контуры. Это я хоть сейчас могу сделать. А вот именно после какого-то ЭДАКОГО сравнения выделить отличные участки и только их - вот ЭТО меня стопорит по полной на данный момент.

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


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

Я имел ввиду такую последовательность:

1) CvAbsDiff по каналам примерно как описано в статьях по ссылкам выше.

2) Доработать, если нужно изображение разности, полученное в предыдущем шаге (бинаризация по порогу, cvErode + cvDilate ....).

3) Полученные белые пятна на черном фоне, пропускаем через детектор контуров.

4) Из свойств контуров вытаскиваем площадь (по ней отсеиваем мелкие участки и помехи) и координаты центров.

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


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

Я имел ввиду такую последовательность:

1) CvAbsDiff по каналам примерно как описано в статьях по ссылкам выше.

2) Доработать, если нужно изображение разности, полученное в предыдущем шаге (бинаризация по порогу, cvErode + cvDilate ....).

3) Полученные белые пятна на черном фоне, пропускаем через детектор контуров.

4) Из свойств контуров вытаскиваем площадь (по ней отсеиваем мелкие участки и помехи) и координаты центров.

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

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


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

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

Можно сразу после нахождения контуров его уничтожить.

А полученные контуры можно нарисовать на анализируемом изображении например (cvDrawContours), или на отдельном изображении, или вообще не рисовать.

Опять таки дело Ваше.

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


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

ОК! Спасибо за информацию! Завтра с утра засяду творить! Теперь ясна общая концепция! Мне на данном этапе достаточно будет даже просто все отличные контуры изобразить, даже мелкие и шум, главное, чтобы результат сравнения был ясно виден на изображении, а рядом отображалась цифра в процентах, насколько одно изображение, соответствует эталону. Кстати и функции CvAbsDiff есть на выходе какая-нибуть величина дополнительная, которую можно было бы в проценты перевести или надо какую-нибуть дополнительную функцию применять? Если да, то с помощью чего данную процентную величину можно получить?

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


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

Вот это попробуйте:

double l2_norm = cvNorm( img1, img2 );

Работать может будет медленно, хотя вроде не должно сильно тормозить.

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

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


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

ОК! Спасибо за сегодняшнюю консультацию!

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


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

Все-таки вначале решил посравнивать гистограммы яркости. Для понимания работы с гистограммами использовал пример demhist.c из библиотеки open.cv. После того как я убрал из данного файла все енужное остался вот такой код:

#ifdef _CH_

#pragma package <opencv>

#endif

#define CV_NO_BACKWARD_COMPATIBILITY

#include "cv.h"

#include "highgui.h"

#include <stdio.h>


// Определяем переменные

char file_name[] = "etalon.bmp";

int hist_size = 64;

float range_0[]={0,256};

float* ranges[] = { range_0 };

IplImage *dst_image = 0, *hist_image = 0;

CvHistogram *hist;

int i, bin_w;

float max_value = 0;


//Функция создающая гистограмму яркости и отображающая её и исходное изображение в окнах

void gistogramm( int arg )

{

// Функция отображает картинку в заранее созданном окне

cvShowImage( "image", dst_image );

// Высчитывается гистограмма hist для dst_image

    cvCalcHist( &dst_image, hist, 0, NULL );

    //cvZero( dst_image );

// Далее находим максимумы гистограммы

    cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 );

// Преобразование матриц

cvScale( hist->bins, hist->bins, ((double)hist_image->height)/max_value, 0 );

/*cvNormalizeHist( hist, 1000 );*/

// Формирование столбцов гистограммы

    cvSet( hist_image, cvScalarAll(255), 0 );

    bin_w = cvRound((double)hist_image->width/hist_size);

    for( i = 0; i < hist_size; i++ )

        cvRectangle( hist_image, cvPoint(i*bin_w, hist_image->height),

                     cvPoint((i+1)*bin_w, hist_image->height - cvRound(cvGetReal1D(hist->bins,i))),

                     cvScalarAll(0), -1, 8, 0 );

// Функция отображает гистограмму в заранее созданном окне

cvShowImage( "histogram", hist_image );


}


int main( int argc, char** argv )

{

    // Загрузка картинки из файла file_name . Функция возвращает указатель

    //  src_image на структуру данных IplImage

    dst_image = cvLoadImage( argc == 2 ? argv[1] : file_name, 0 );

    // Создается картинка в которую будет выводиться гистограмма

    hist_image = cvCreateImage(cvSize(320,200), 8, 1);

    // Создается объект гистограммы

    hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);


    cvNamedWindow("image", 0);

    cvNamedWindow("histogram", 0);

    //Присваиваем 0 функции update_brightcont

    gistogramm(0);


    cvWaitKey(0);

    // Удаляются изображения из памяти

    cvReleaseImage(&dst_image);

    cvReleaseHist(&hist);


    return 0;

}
В BCB6 этот код прекрасно компилируется, создается exe и все работает. Создается окно с изображением и окно с его гистограммой. Проблема наступает, когда переношу этот код в свой проект (тоже на ВСВ6) В моем проекте по нажатию кнопки должен создаться файл с эталонным изображением (он прекрасно создается) и окно с гистограммой данного изображения. Но вместо окна с изображением выскакивает ошибка External exeption E06D7363. Что я делаю неправильно? Все переменные и инклуды из demhist.c я в свой проект перенес. Грешу на то что может у меня функция int_main не так работает в BCB6 или я не так функции расположил и применил? Растолкуйте знатоки? Вот код моей программы со вставками из demhist.c:
void __fastcall TForm_MainDemo::Button6Click(TObject *Sender)

{


       Image1->Picture->SaveToFile("etalon.bmp");

       Image2->Picture->LoadFromFile("etalon.bmp");

       Image2->Proportional = true;



// Высчитывается гистограмма hist для dst_image

    cvCalcHist( &dst_image, hist, 0, NULL );

    //cvZero( dst_image );

// Далее находим максимумы гистограммы

    cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 );

// Преобразование матриц

cvScale( hist->bins, hist->bins, ((double)hist_image->height)/max_value, 0 );

/*cvNormalizeHist( hist, 1000 );*/

// Формирование столбцов гистограммы

    cvSet( hist_image, cvScalarAll(255), 0 );

    bin_w = cvRound((double)hist_image->width/hist_size);

    for( i = 0; i < hist_size; i++ )

        cvRectangle( hist_image, cvPoint(i*bin_w, hist_image->height),

                     cvPoint((i+1)*bin_w, hist_image->height - cvRound(cvGetReal1D(hist->bins,i))),

                     cvScalarAll(0), -1, 8, 0 );

// Функция отображает гистограмму в заранее созданном окне

cvShowImage( "histogram", hist_image );




}

//---------------------------------------------------------------------------



int main( int argc, char** argv )

{

    // Загрузка картинки из файла file_name . Функция возвращает указатель

    //  src_image на структуру данных IplImage

    dst_image = cvLoadImage( argc == 2 ? argv[1] : file_name, 0 );

    // Создается картинка в которую будет выводиться гистограмма

    hist_image = cvCreateImage(cvSize(320,200), 8, 1);

    // Создается объект гистограммы

    hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);


    cvNamedWindow("image", 0);

    cvNamedWindow("histogram", 0);


        cvWaitKey(0);

    // Удаляются изображения из памяти

    cvReleaseImage(&dst_image);

    cvReleaseHist(&hist);


    return 0;

}

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


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

не надо там main делать, приложение уже содержит WinMain.

Вместо этого можете использовать события (FormCreate например, или конструктор формы.)

А для освобождения ресурсов FormClose или деструктор.

Или из main все в обработчик нажатия кнопки запихнутть.

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


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

не надо там main делать, приложение уже содержит WinMain.

Вместо этого можете использовать события (FormCreate например, или конструктор формы.)

А для освобождения ресурсов FormClose или деструктор.

Или из main все в обработчик нажатия кнопки запихнутть.

Точно! Все заработало! Заработал вот такой код:

void __fastcall TForm_MainDemo::Button6Click(TObject *Sender)

{


       Image1->Picture->SaveToFile("etalon.bmp");

       Image2->Picture->LoadFromFile("etalon.bmp");

       Image2->Proportional = true;


     // При создании файла определяется область изображения которая будет доступна фильтрам

    // Загрузка картинки из файла file_name . Функция возвращает указатель

    //  src_image на структуру данных IplImage

    dst_image = cvLoadImage( argc == 2 ? argv[1] : file_name, 0 );

    // Создается картинка в которую будет выводиться гистограмма

    hist_image = cvCreateImage(cvSize(320,200), 8, 1);

    // Создается объект гистограммы

    hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);

    // Создаются окна в которые будут загружаться картинка и её гистограмма

    //Первым параметром мы передаём название самого окна, а вторы

    //его размер, флаг CV_WINDOW_AUTOSIZE, указывает на то,

    //что окно будет таких же размеров, что и загружаемое изображение.

    //Можно передать вторым параметром "0" (ноль) и тогда вы сможете

    //сами изменять размер окна, когда программа запущена.

    // Высчитывается гистограмма hist для dst_image

    cvCalcHist( &dst_image, hist, 0, NULL );

    //cvZero( dst_image );

    // Далее находим максимумы гистограммы

    cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 );

    // Преобразование матриц

    cvScale( hist->bins, hist->bins, ((double)hist_image->height)/max_value, 0 );

    /*cvNormalizeHist( hist, 1000 );*/

    // Формирование столбцов гистограммы

    cvSet( hist_image, cvScalarAll(255), 0 );

    bin_w = cvRound((double)hist_image->width/hist_size);

    for( i = 0; i < hist_size; i++ )

        cvRectangle( hist_image, cvPoint(i*bin_w, hist_image->height),

                     cvPoint((i+1)*bin_w, hist_image->height - cvRound(cvGetReal1D(hist->bins,i))),

                     cvScalarAll(0), -1, 8, 0 );

    // Отображение гистограммы в заранее созданном окне

    cvNamedWindow("histogram", 0);

    cvShowImage( "histogram", hist_image );

    // Удаляются изображения из памяти

    cvReleaseImage(&dst_image);

    cvReleaseHist(&hist);

    }

//-

Тут еще вопрос появился, почему когда я пытаюсь выполнить в данном коде функцию

mycvShowImage(Image3,hist_image);(это из библиотеки myCV)

у меня выскакивает ошибка Scan line index out of range.

а на форме Image3 не появляется hist_image.

Хотя до этого я данной функцией на Image1 бросал scr (тип тот-же - IplImage)на форме появлялось нужное изображение. Кстати и до этого я пробовал в программе второй раз воспользоваться mycvShowImage, пытаясь бросать IplImage на разные Image и тоже ничего не получалось! Отчего такое может быть?

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


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

Во всех примерах для builder 6 на форуме есть такой кусок (он выводит изображение):

#define WIDTHBYTES(bits)	((((bits) + 31) / 32) * 4)



/*
=======================================================================================================================
Создание API шного битмапа из интеловского RGB изображения ;
=======================================================================================================================
*/



HBITMAP CreateRGBBitmap(IplImage *_Grab)
{
char *App;

IplImage *_Grab3 = 0, *_Grabf = 0;

LPBITMAPINFO lpbi = new BITMAPINFO;
lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
lpbi->bmiHeader.biWidth = _Grab->width;
lpbi->bmiHeader.biHeight = _Grab->height;
lpbi->bmiHeader.biPlanes = 1;
lpbi->bmiHeader.biBitCount = 24;
lpbi->bmiHeader.biCompression = BI_RGB;
lpbi->bmiHeader.biSizeImage = WIDTHBYTES((DWORD) _Grab->width * 8) * _Grab->height;
lpbi->bmiHeader.biXPelsPerMeter = 0;
lpbi->bmiHeader.biYPelsPerMeter = 0;
lpbi->bmiHeader.biClrUsed = 0;
lpbi->bmiHeader.biClrImportant = 0;

void *pBits;
HBITMAP hBitmap = CreateDIBSection(NULL, lpbi, DIB_RGB_COLORS, (void **) &pBits, NULL, 0);
delete lpbi;
if(hBitmap) App = (char *) pBits;

/* Если глубина изображения не IPL_DEPTH_8U (однобайтовое целое), конвертируем */
if(_Grab->depth != IPL_DEPTH_8U)
{
_Grabf = cvCloneImage(_Grab);
if(_Grab)
{
cvReleaseImage(&_Grab);
}

_Grab = cvCreateImage(cvSize(_Grabf->width, _Grabf->height), IPL_DEPTH_8U, _Grabf->nChannels);
cvConvert(_Grabf, _Grab);
if(_Grabf)
{
cvReleaseImage(&_Grabf);
}
}

/*
* Если изображение содержит один канал, создаем трехканальное изображение ;
* Серое или бинарное
*/
if(_Grab->nChannels == 1)
{
_Grab3 = cvCreateImage(cvSize(_Grab->width, _Grab->height), IPL_DEPTH_8U, 3);
cvMerge(_Grab, _Grab, _Grab, NULL, _Grab3);
}

/*
* Если входное изображение трехканальное, просто копируем указатель на него ;
* Цветное
*/
if(_Grab->nChannels == 3)
{
_Grab3 = _Grab;
}

/* Получаем указатель на данные */
unsigned char *data;
cvGetRawData(_Grab3, (uchar **) &data);

/* Копируем данные */
// cvConvertImage
if(_Grab3)
{
for(int i = 0; i < _Grab->height; i++)
{
memcpy(App + _Grab3->widthStep * (_Grab3->height - i - 1), data + _Grab3->widthStep * i, _Grab3->width * 3);
}
}

/* Очищаем память если создавали изображение, а не копировали указатель */
if(_Grab->nChannels == 1)
{
cvReleaseImage(&_Grab3);
}

return hBitmap;
}

/*
=======================================================================================================================
Функция вывода изображения на HANDLE оконного компонента ;
=======================================================================================================================
*/
void APIDrawIpl(int x, int y, IplImage *_Grab, void *HANDLE)
{
HDC hMemDC, hDC;
hDC = GetDC(HANDLE);
hMemDC = CreateCompatibleDC(hDC);

HBITMAP Bitmap = CreateRGBBitmap(_Grab);
SelectObject(hMemDC, Bitmap);
BitBlt(hDC, x, y, _Grab->width, _Grab->height, hMemDC, 0, 0, SRCCOPY);
DeleteObject(Bitmap);
DeleteDC(hMemDC);
DeleteDC(hDC);
}

/*
=======================================================================================================================
=======================================================================================================================
*/
// Пример применения APIDrawIpl(10,10,gray,Form1->Handle); // Рисуем результат
// Можно выводить и на Panel1->Handle
// На Image труднее но тоже можно.
[/code]

Поправил немного в прошлый раз взял из старого исходника.

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


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

Не совсем ясно как пользоваться и где размещать блоки HBITMAP CreateRGBBitmap(IplImage* _Grab) и APIDrawIpl, а также как при помощи всего этого хозяйства поместить IplImage на КОНКРЕТНЫЙ компонент Image на форме.

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


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

Не совсем ясно как пользоваться и где размещать блоки HBITMAP CreateRGBBitmap(IplImage* _Grab) и APIDrawIpl, а также как при помощи всего этого хозяйства поместить IplImage на КОНКРЕТНЫЙ компонент Image на форме.

Размещать вначале файла Unit1.cpp

Если на компонент Image то ковыряйте тему: http://www.compvision.ru/forum/index.php?showtopic=280&hl=timage&st=20

если это не очень важно, тогда вывод делается так (Вывод на компонент TPanel):

APIDrawIpl(смещение по горизонтали,смещение по вертикали,ВашIPLImage,Panel1->Handle);

смещение по горизонтали,смещение по вертикали - Это смещения картинки относительно компонента на который она выводится.

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


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

Спасибо. Буду пробовать.

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


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

Размещать вначале файла Unit1.cpp

Если на компонент Image то ковыряйте тему: http://www.compvision.ru/forum/index.php?showtopic=280&hl=timage&st=20

если это не очень важно, тогда вывод делается так (Вывод на компонент TPanel):

APIDrawIpl(смещение по горизонтали,смещение по вертикали,ВашIPLImage,Panel1->Handle);

смещение по горизонтали,смещение по вертикали - Это смещения картинки относительно компонента на который она выводится.

Получилось! Интересный способ - не на Image а на Panel. Удобно, когда с изображением ничего не надо делать потом.

А вот как быть если изображение при этом больше Panel? Как уменьшить масштаб, чтобы картинка вписалась? У Image для этого есть свойство Image2->Proportional = true; а как быть при использовании Panel?

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


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

Я так понимаю cvResize мне в помощь?

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


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

Я так понимаю cvResize мне в помощь?

У меня так делалось:

if(!frame_resized)
frame_resized = cvCreateImage(cvSize(Panel1->ClientWidth, Panel1->ClientHeight), IPL_DEPTH_8U, 1);
cvResize(frame, frame_resized, CV_INTER_LINEAR);
[/code] Это для работы с серым изображением. Для цветного естественно:
[code]frame_resized = cvCreateImage(cvSize(Panel1->ClientWidth, Panel1->ClientHeight), IPL_DEPTH_8U, 3)

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


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

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

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

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

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

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

Войти

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

Войти сейчас


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

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

×