Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Попиксельное сравнение картинок


Автор: buena_suerte 21.4.2007, 16:36
у меня дана задача такая: есть картинка загруженая, на ней вырезаем определенную область(кусочек), и потом производим сканирование новой картинки и сравниваем её с этим кусочком загруженым в память
тоесть, есть два рисунка одинаковых, сравниваемых,надо допустим сделать привязку к координатам,но как? 
и попиксельно сравнить эти отрезки между собой? помогите люди добрые,а с графической частью вообще не дружу
может исходник с примером есть

Автор: Klin 21.4.2007, 17:09
Сравнение изображений сложный процесс (если делать качественно), главное составить граммотный алгоритм. Вот элементарное опредение цвета в точке:
Код

 Image1->Canvas->Pixels[X][Y];

Автор: mr.Anderson 21.4.2007, 20:08
Klin, а чем плохо, например, элементарно сравнить все пикселы первой картинки и второй (по цвету) ? Затем вычислить процент совпадений. Если алг плох, скажите, чем.

Автор: buena_suerte 21.4.2007, 21:56
Klin,спасибо и за это smile 
Цитата(mr.Anderson @ 21.4.2007,  20:08)
Klin, а чем плохо, например, элементарно сравнить все пикселы первой картинки и второй (по цвету) ? Затем вычислить процент совпадений. Если алг плох, скажите, чем.

плизз,а можно чуть поподробней ваш алгоритм,если не трудно?))

Автор: Klin 21.4.2007, 22:36
Цитата(mr.Anderson @  21.4.2007,  21:08 Найти цитируемый пост)
чем плохо, например, элементарно сравнить все пикселы первой картинки и второй (по цвету) ? Затем 

Очень мудро, попробуйте сравнить две картинки одинакового размера, состоящие из двух равных прямоугольников. В первой картинке сверху будет черный, снизу белый. А во второй сверху белый, а с низу черный. И что это даст? Обратные изображения будут совподать на 100%. Вот вам и элементарное попиксельное сравнение.


Цитата(buena_suerte @  21.4.2007,  22:56 Найти цитируемый пост)
плизз,а можно чуть поподробней ваш алгоритм,если не трудно?))

Этот способ подойдет вам, если вам нужно определить закрашен ли квадрат (стоит ли галка в пунке)

Автор: Grom 22.4.2007, 00:51
Klin, 
Цитата

А во второй сверху белый, а с низу черный. И что это даст? Обратные изображения будут совподать на 100%. Вот вам и элементарное попиксельное сравнение.


так ведь сравнение попиксельное. т.е. пиксель с координатой x=1 y=1 должен быть равен пикселю с такими же координатами в другой картинке. по этому верх - них тут значения не имеет ведь  smile 

Автор: mr.Anderson 22.4.2007, 11:42
Именно так. Мы сравниваем именно каждый конкретный пиксел с точно таким же пикселом на другой картинке. Если они по цвету совпадут, то ставим + в количество совпадений. При чем тут обратные изображения? Короче, я думаю, что вот это будет работать вполне неплохо (код не проверял, правда). Только сначала попробуйте, прежде чем делать выводы.
Код

int __fastcall CompareImages( TImage *Src1, TImage *Src2 )
{
 if( ( Src1->Width != Src2->Width ) || ( Src1->Height != Src2->Height ) )
 {
  Application->MessageBox( "Можно сравнивать только изображения одинаковых размеров", "Ошибка сравнения изображений", MB_OK );
  return;
 }

 long allPixels = Src1->Height * Src1->Width; //общее количество пикселов
 long equalPixels = 0; //количество совпадений

 for( int i=0; i<Src1->Width; i++ )
  for( int j=0; j<Src1->Height; j++ )
   if( Src1->Canvas->Pixels[ i ][ j ] == Src2->Canvas->Pixels[ i ][ j ] )
    ++equalPixels;

 return ( equalPixels * 100 / allPixels ); //возвращаем процент совпадения картинок
}

Функция вернет процент совпадения картинок. Есть только два минуса - картинки должны быть одинакового размера и только в формате *BMP.

Автор: buena_suerte 22.4.2007, 12:08
mr.Anderson, заранее огромное вам человеческое спасибо,сейчас попробую)


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

Автор: mr.Anderson 22.4.2007, 12:38
buena_suerte, я уже попробовал, функция возвращает процент совпадений картинок. Работает отлично. smile 

Насчет вашего вопроса о привязке - просто нужно начинать сравнение пикселов не с нуля (как в моем примере), а с того места, с которого вам нужно. Ну и, соответственно, заканчивать нужно тоже в определенном месте.

Автор: mr.Anderson 22.4.2007, 15:23
Ну, помогло ли?

Автор: Klin 22.4.2007, 16:02
Цитата(Grom @  22.4.2007,  01:51 Найти цитируемый пост)
так ведь сравнение попиксельное. т.е. пиксель с координатой x=1 y=1 должен быть равен пикселю с такими же координатами в другой картинке. по этому верх - них тут значения не имеет ведь  

Сорри, что-то я по ночам туго мыслить стал.  smile  Что-то с памятью мою сталось... smile

Автор: buena_suerte 22.4.2007, 17:33
Цитата(mr.Anderson @ 22.4.2007,  12:38)
buena_suerte, я уже попробовал, функция возвращает процент совпадений картинок. Работает отлично. smile 

Насчет вашего вопроса о привязке - просто нужно начинать сравнение пикселов не с нуля (как в моем примере), а с того места, с которого вам нужно. Ну и, соответственно, заканчивать нужно тоже в определенном месте.

да, очень вам благодарен)))))))
а по поводу привязки, там еще имелось ввиду как лучше определить координаты той области,чтобы ввести их, ну да ладно
спасибо

Автор: fish9370 22.4.2007, 17:54
Цитата(mr.Anderson @ 21.4.2007,  20:08)
Klin, а чем плохо, например, элементарно сравнить все пикселы первой картинки и второй (по цвету) ? Затем вычислить процент совпадений. Если алг плох, скажите, чем.

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

хотя тут, конечно, все зависит от задачи.. проигнорируйте этот пост..

Добавлено через 2 минуты и 54 секунды
Цитата(buena_suerte @ 22.4.2007,  17:33)
а по поводу привязки, там еще имелось ввиду как лучше определить координаты той области,чтобы ввести их, ну да ладно
спасибо

а по поводу привязки, думаю, никто не понял, что ты имеешь ввиду.. какая еще привязка?

Автор: buena_suerte 22.4.2007, 19:00
ну мы загрузили допустим картинку в Image, и нам нужна не вся картина для сравнения,а определенный её фрагмент,так вот,нужно вычислить координаты этого фрагмента,чтобы с другой загружаемой картинкой можно было производить сравнение точно, в этом же месте...

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

Автор: fish9370 22.4.2007, 19:22
Цитата(buena_suerte @ 22.4.2007,  19:00)
ну мы загрузили допустим картинку в Image, и нам нужна не вся картина для сравнения,а определенный её фрагмент,так вот,нужно вычислить координаты этого фрагмента,чтобы с другой загружаемой картинкой можно было производить сравнение точно, в этом же месте...

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

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

Код

int __fastcall CompareImages( TImage *Src1, TImage *Src2, int nLeft, int nTop, int nWidth, int nHeight )
{
 long allPixels = nHeight * nWidth; //общее количество пикселов
 long equalPixels = 0; //количество совпадений

 for( int i=0; i<nWidth; i++ )
  for( int j=0; j<nHeight; j++ )
   if( Src1->Canvas->Pixels[ i+nLeft ][ j+nHeight ] == Src2->Canvas->Pixels[ i+nLeft ][ j+nHeight ] )
    ++equalPixels;

 return ( equalPixels * 100 / allPixels ); //возвращаем процент совпадения картинок
}


P.S.  тут попрежднему не реализована аппроксимация.. и такие одинаковые цвета как 0xFF0000 и 0xFE0000 будут считаться разными.. результат получишь отрицательный.. учти это..

Автор: mr.Anderson 22.4.2007, 19:24
Кхм. Фальшивомонетчики развелись... smile Шутка.

В этом случае просто вычисли (вручную) координаты куска, где находятся цифры, и его сравнивай. Как - я уже написал выше.

Добавлено через 2 минуты и 41 секунду
Кстати, никто не подскажет принцип реализации аппроксимации? Что-то даже интересно стало. smile

Автор: fish9370 22.4.2007, 19:46
Цитата(mr.Anderson @ 22.4.2007,  19:24)
Добавлено @ 19:27
Кстати, никто не подскажет принцип реализации аппроксимации? Что-то даже интересно стало. smile

я знал, что вы спросите..  smile 

итак, для начала нужно разделить цвет на составляющие (напаминаю, каждый цвет состоит из трех составляющих RGB (Red, Green, Blue), на самом деле часто можно встретить еще одну состовляющую A (альфаканал, непрозрачность), но в данной задаче она нам не понадобится).. далее определить насколько точно нужно, чтобы были идентичны значения точек, назовем эту переменную дельтой..
а дальше все просто.. вычитаем из составляющей значение второй составляющей и сравниваем с дельтой.. вычислять нужно по модулю..

Код

if((abs(nR1-nR2))<=nDelta) bRes=true; //где nR1 составляющая красного первого цвета, nR2 составляющая красного второго цвета..
   else bRes=false;                         // nDelta константа, определяющая точность сравнения.. bRes результат сравнения..


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

Автор: mr.Anderson 22.4.2007, 20:15
Разделить - нет ничего проще. smile 
Код

TColor curPixel = Image1->Canvas->Pixels[ i ][ j ];

//затем
GetRValue( curPixel );
GetGValue( curPixel );
GetBValue( curPixel );

А за алг спасибо, попробую реализовать.

Добавлено через 3 минуты и 38 секунд
Непонятно только одно: какое значение задавать в Delta ? Чему оно может быть равно?

Автор: fish9370 22.4.2007, 21:38
Цитата(mr.Anderson @ 22.4.2007,  20:15)
Разделить - нет ничего проще. smile 
Код

TColor curPixel = Image1->Canvas->Pixels[ i ][ j ];

//затем
GetRValue( curPixel );
GetGValue( curPixel );
GetBValue( curPixel );

А за алг спасибо, попробую реализовать.

Добавлено @ 20:19
Непонятно только одно: какое значение задавать в Delta ? Чему оно может быть равно?

для выделения составляющих я бы воспользовался сдвигами и масками (привычка с АСМа), но твой способ удобный..
дельту можешь задавать от 0 до 127 (больше задавать не вижу смысла)

предположим есть два цвета 0xFF0000 и 0xF00000 логично предположить, что они одинаковы, т.к. цвета расположены близко по значению (возьми фотошоп поставь два этих цвета рядом).. в данном случае достаточно задать дельту равной 16.. и они будут считаться одинаковыми.. 

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

Автор: mr.Anderson 22.4.2007, 21:40
Все, пошла разработка нового суперкласса а-ля "Фотошоп в топку!" smile Да, можно попробовать. Но алгоритмов этих я не знаю, т.к. никогда графикой не интересовался. Однако ж, попробовать стоит. Первым делом доделаем сравнение...

Автор: fish9370 22.4.2007, 21:43
и еще.. 

Код

TColor curPixel = Image1->Canvas->Pixels[ i ][ j ];


это работает несколько медленовато, как при записи так и при считывании.. я бы воспользовался этой АПИшкой..

Код

COLORREF GetPixel(
  HDC hdc,    // handle to DC
  int nXPos,  // x-coordinate of pixel
  int nYPos   // y-coordinate of pixel
);


она гораздо быстрее.. а для записи SetPixel()..

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

Автор: mr.Anderson 23.4.2007, 16:35
Так. Слегка подредактированный код (тож не проверял):
Код

int __fastcall CompareImages( TImage *Src1, TImage *Src2 )
{
 if( ( Src1->Width != Src2->Width ) || ( Src1->Height != Src2->Height ) )
 {
  Application->MessageBox( "Можно сравнивать только изображения одинаковых размеров", "Ошибка сравнения изображений", MB_OK );
  return;
 }

 long allPixels = Src1->Height * Src1->Width; //общее количество пикселов
 long equalPixels = 0; //количество совпадений
 short Delta = 16;
 for( int i=0; i<Src1->Width; i++ )
  for( int j=0; j<Src1->Height; j++ )
  {
   TColor curPixel = GetPixel( Src1, i, j );
   TColor curPixel2 = GetPixel( Src2, i, j );

   int blue = GetBValue( curPixel );
   int green = GetGValue( curPixel );
   int red = GetRValue( curPixel );
   int blue2 = GetBValue( curPixel2 );
   int green2 = GetGValue( curPixel2 );
   int red2 = GetRValue( curPixel2 );

   if( ( abs( blue - blue2 ) <= delta ) &&
       ( abs( red - red2 ) <= delta ) &&
       ( abs( green - green2 ) <= delta ) )
    ++equalPixels;
  }
 return ( equalPixels * 100 / allPixels ); //возвращаем процент совпадения картинок
}

Возможно, наврал с использованием GetPixel - не сумел найти ее описания.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)