Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Поиск одного изображения в другом [D7, WinXP]


Страницы:   1 2

czuryk ©   (2009-01-23 15:48:00) 

Добрый день коллеги! Очень прошу помочь!

Давно уже бьюсь, но никак не могу расколоть сабжевую проблему, гугл выдает некую информацию, но мне никак не удается ее адаптировать для своих нужд. Учтите, речь идет не о сравнении одного изображения с другим.
Мне нужно организовать поиск одного изображения (маленького, порядка 100х50 пикселей) в другом - большом, порядка 1280х1024 пикселей, причем поиск должен осуществлятся максимально быстро < 1 сек. Результатом работы функкции должны быть координаты X,Y маленьгоко изображения в большом (исходном).
Причем поиск долже выполнятся по полному схождению части большого изображения и маленького. Так как если искать по нескольки ключевых точек, то такие функции в моем случае дают сбой.

procedure TForm1.Button4Click(Sender: TObject);
Type
 TRGBTripleArray =  ARRAY[WORD] OF TRGBTriple;
 pRGBTripleArray =  ^TRGBTripleArray;
 
var
 b1, b2: TBitmap;
//  c1, c2: PByteArray;
 c1, c2: pRGBTripleArray;
 x, y, i,: Integer;
 eq: boolean;
 resX, resY: integer;

begin

b1 := Image1.Picture.Bitmap;
b2 := Image2.Picture.Bitmap;
Assert(b1.PixelFormat = b2.PixelFormat); // they have to be equal

for y := 0 to b1.Height - 1 do // Внешний цикл по строкам оригинала
  begin
  c1 := b1.Scanline[y];
  c2 := b2.Scanline[0]; // Ищу на соответствие только по 1-й строке

  for x := 0 to b1.Width - 1 do
     begin
     eq := true;
     for i := 0 to b2.Width - 1 do // Цикл по строке искомой строки
        begin
        if (c1[x+i].RGBtRed <> c2[i].RGBtRed) or (c1[x+i].RGBtGreen <> c2[i].RGBtGreen) or (c1[x+i].RGBtBlue <> c2[i].RGBtBlue) then
           begin eq := false; break; end
        end;
     if ( eq ) then begin resX:=x; resY:=y; break; end;
     end;
  if ( eq ) then break;
  end;

if ( eq ) then
  begin
  Memo1.Lines.Add('FOUND');
  b1.canvas.Brush.Color := clRed;
  b1.canvas.Ellipse(resX-3, resY-3, resX+3, resY+3);
  end
  else Memo1.Lines.Add('NOT FOUND');
end;


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

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


...
...
PInt = ^integer;
var
 Form1: TForm1;

implementation

{$R *.dfm}

function SearchBitmap(const bmMain,bmSub:TBitmap; var Res:TPoint):boolean;
var iMainHeight, iMainWidth,
   iSubHeight,  iSubWidth,
   iMainPXWidth, iSubPXWidth, iDiffPXWidth,
   iDiffHeight: integer;
   i,j:integer;
   eq: boolean;
   pRowMain, pRowSub : PByteArray;
   ltPt,rtPt,lbPt,rbPt : PInt;
   cPoints:array[0..3] of integer;
begin
 Res.X := -1;
 Res.Y := -1;
 SearchBitmap := false;
 bmMain.PixelFormat:=pf24bit;
 bmSub.PixelFormat:=pf24bit;
 iMainHeight := bmMain.Height;
 iMainWidth := bmMain.Width;
 iMainPXWidth := iMainWidth * 3;
 iSubHeight := bmSub.Height;
 iSubWidth := bmSub.Width ;
 iSubPXWidth := iSubWidth *3 ;
 iDiffPXWidth := iMainPXWidth - iSubPXWidth;

 iDiffHeight:= iMainHeight - iSubHeight;
 pRowSub := bmSub.ScanLine[0];
 cPoints[0]:= PInt(@(pRowSub^[0]))^ and $FFFFFF;
 cPoints[1]:= PInt(@(pRowSub^[iSubPXWidth-3]))^ and $FFFFFF;
 pRowSub := bmSub.ScanLine[iSubHeight-1];
 cPoints[2]:= PInt(@(pRowSub^[0]))^ and $FFFFFF;
 cPoints[3]:= PInt(@(pRowSub^[iSubPXWidth-3]))^ and $FFFFFF;
 eq:=false;
 for i:=0 to iDiffHeight - 1 do
 begin
   pRowMain := bmMain.ScanLine[i];
   pRowSub := bmMain.ScanLine[i+iSubHeight-1];
   j:=0;
   ltPt := PInt(@pRowMain^[j]);
   lbPt := PInt(@pRowSub^[j]);
   //rtPt := PInt(pRowMain + iSubPXWidth - 3);
   //rbPt := PInt(pRowSub + iSubPXWidth - 3);
   asm
     mov   eax,iSubPXWidth
     sub   eax,3
     mov   ecx,eax
     add   ecx,ltPt
     mov   rtPt,ecx
     mov   ecx,eax
     add   ecx,lbPt
     mov   rbPt,ecx
   end;

   while j<iDiffPXWidth do
   begin
     {
     eq := ((PInt(@(pRowMain^[j]))^ and $FFFFFF) = cPoints[0])
       and ((PInt(@(pRowMain^[j+iSubPXWidth-3]))^ and $FFFFFF) = cPoints[1])
       and ((PInt(@(pRowSub^[j]))^ and $FFFFFF ) = cPoints[2])
       and ((PInt(@(pRowSub^[j+iSubPXWidth-3]))^ and $FFFFFF) = cPoints[3]);
     }
     eq := ((ltPt^ and $FFFFFF) = cPoints[0])
       and ((rtPt^ and $FFFFFF) = cPoints[1])
       and ((lbPt^ and $FFFFFF) = cPoints[2])
       and ((rbPt^ and $FFFFFF) = cPoints[3]);
     if ( eq ) then
     begin
       Res.X := j div 3;
       Res.Y := i;
       SearchBitmap := true;
       break;
     end;
     asm
       add ltPt,3
       add rtPt,3
       add lbPt,3
       add rbPt,3
     end;
     inc(j,3);
   end;
   if eq then break;
 end;

end;

function CaptureScreenRect(ARect : TRect) : TBitmap;
var
 ScreenDC: HDC;
begin
Result:=TBitmap.Create;
with result, ARect do
  begin
  Width:=Right-Left;
  Height:=Bottom-Top;
  ScreenDC:=GetDC(0);
  try
     BitBlt(Canvas.Handle, 0,0,Width,Height,ScreenDC, Left, Top, SRCCOPY );
  finally
  ReleaseDC(0, ScreenDC);
  end;
  end;
end;

procedure Search(pattern: string; p_color: TColor);
var
 bmMain, bmSub: TBitmap;
 startPoint: TPoint;
 c: TCanvas;

begin
c := TCanvas.Create;
c.Handle := GetDC(0);

 bmMain := TBitmap.Create();
 bmSub  := TBitmap.Create();
 try
//  image1.Picture.Bitmap := CaptureScreenRect(Rect(0,0,Screen.Width,Screen.Height));
 bmMain := CaptureScreenRect(Rect(0,0,Screen.Width,Screen.Height));
//       bmMain.LoadFromFile('screen_main.bmp');
   bmSub.LoadFromFile(pattern);
   if (SearchBitmap(bmMain, bmSub, startPoint)) then
       begin
       c.Brush.Color := p_color;
       c.Ellipse(startPoint.x-3, startPoint.y-3, startPoint.x+3, startPoint.y+3);
       end;
 finally
   bmMain.Free;
   bmSub.Free;
   c.Free;
 end;
end;</CODE


Германн   (2009-01-25 03:03:00) [1]


> czuryk ©   (25.01.09 02:07) [35]
>
> Вот тут представленны два неплохих алгоритма

Ну так и используй их!


czuryk   (2009-01-25 02:07:00) [2]

Вот тут представленны два неплохих алгоритма, которые быстро справляются с моей задачей:
http://forum.sources.ru/index.php?act=ST&f=11&t=263125

Всем спасибо.


czuryk   (2009-01-24 22:53:00) [3]

Pavia, а можно примерчик?


Pavia   (2009-01-24 22:17:00) [4]


> я тут тоже ноль без палочки, как работать с битмапом напрямую
> как с массивом .... :(

Да тут достаточно scanline будет прямая работа с битмепом в памяти.
Только совет делать так через scanline  получаем указатель на первый пиксель. Через разность получаем длину линии. Из-за выравнивания она будет отличаться от width*bytePerPixel. И еще вспомнить что у битмепа обратный порядок строк.  И работать с указателями.


czuryk   (2009-01-24 21:50:00) [5]

я тут тоже ноль без палочки, как работать с битмапом напрямую как с массивом .... :(


KilkennyCat   (2009-01-24 21:07:00) [6]


> czuryk ©   (24.01.09 19:20) [28]

Не нужно сканлайн и проч, это все теже танцы с графикой. Читаем память напрямую. Но это выше моего уровня, кодом не помогу, ибо мне придется пару дней сидеть за учебниками. Но я бы шел в этом направлении. Или в направлении
> Pavia ©   (23.01.09 23:22) [20]


czuryk   (2009-01-24 20:30:00) [7]

Johnmen, и? проблема до сих пор актуальна.


Johnmen   (2009-01-24 20:15:00) [8]

http://sql.ru/forum/actualthread.aspx?tid=632890


czuryk   (2009-01-24 19:20:00) [9]

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

KilkennyCat, я пытался, но получаемая информация из ScanLine в виде массива не сравнивается как элементы массива, а нужно расшифровывать:
    for i := 0 to b2.Width - 1 do // Цикл по строке искомой строки
       begin
       if (c1[x+i].RGBtRed <> c2[i].RGBtRed) or (c1[x+i].RGBtGreen <> c2[i].RGBtGreen) or (c1[x+i].RGBtBlue <> c2[i].RGBtBlue) then
          begin eq := false; break; end
       end;

Как это преодолеть я не знаю.


KilkennyCat   (2009-01-24 15:47:00) [10]


> т.к. работа идет с 24-битным цветом.

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


Pavia   (2009-01-24 15:40:00) [11]


>  распозновать границы окна, и текст в некоторых окнах, а
> это не так много шаблонов.

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


czuryk   (2009-01-24 12:33:00) [12]

Привет Ramon, да, согласен. Я с него и начал... но он оооочень медленный. Прядка 1-2 секунд на двухядернике. Быстрее работает через ScanLine, но тоже недостаточно быстро, можешь посмотреть код, который я выложил.


Ramon   (2009-01-24 12:26:00) [13]

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


czuryk   (2009-01-24 12:25:00) [14]

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

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

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

KilkennyCat, идея с матрицей неплоха, но ее внедрить не получится, т.к. работа идет с 24-битным цветом.


Pavia   (2009-01-23 23:36:00) [15]


> и припаять DSP :)

Можно и на видео карте.


KilkennyCat   (2009-01-23 23:32:00) [16]


> Свертку делать через FFT.

и припаять DSP :)


Pavia   (2009-01-23 23:22:00) [17]


> Быстрого говоришь, а ведь тут примерно 1280х1024х100х50/2
> сравнений.

Несогласен вы про свертку забыли Будет примерно 1280х1024х11х10х2
И время сравнения порядка 1 секунды.
Свертку делать через FFT.


tesseract   (2009-01-23 22:35:00) [18]


>  Диагональ, спираль, ход конем... Но это реально ускорит.


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


KilkennyCat   (2009-01-23 22:11:00) [19]

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


KilkennyCat   (2009-01-23 22:02:00) [20]


> tesseract ©   (23.01.09 21:43) [16]

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


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо