在更大的图像中快速找到小图像

时间:2010-06-19 07:39:54

标签: delphi image search computer-vision

我需要获取驻留在大图像中的小图像位置的坐标(假设我需要搜索森林照片中的特定树。如果找到子图像,则结果将类似于: x=120 y=354例如。)

我可以使用快速算法吗?

我正在使用Delphi(如果需要也可以使用Java)

6 个答案:

答案 0 :(得分:4)

编辑:关于理论的一些事情:

简而言之,在图像上应用滤镜有两个“空格”:颜色备用或频率空间。如果你确定了空间(freq,这里有两种过滤器:应用为卷积和相关(这里)。为了保持简单,我们假设应用相关简单意味着“我们将两个东西相乘”。使用频率中的相关性图像的空间你可以测量相似的图像是什么。两个图像是相似的,如果灰度梯度是。这是由协方差测量。(也许有人可以帮助我在这里插入配方。)交叉相关系数是标准化的协方差(插入这里的公式:() 如果你把它放入一个算法来搜索“模型”和“参考图像”之间的亲和力(模型是你在参考文献中搜索的一小部分),你会得到matlab代码,我也评论过。代码使用的公式是这样的: FT([f°g](m,n))= F(u,v)* G°(u,v)。其中F是fft,G°是G的共轭复合物(G是模型的fft)

请快速定义。 :) 我想到的解决方案将需要fft,这很快,但可能没有你想要的那么快。它在I图像中搜索小的“I_ausschnitt”图像,并给出具有较高“可能性”的位置。

在matlab中,这个可行。我希望你能把它放到delphi中。 :)

I = im2double(imread('Textiltextur.tif')); // This is our reference image
I_model = imcrop( I, [49 36 42 28] ); // Our model - what we want so search in I

[X Y] = size( I ); // Get the size of the reference image. 
f = fft2(I); // Perform the fast fourier transform->put the image into frequency space. 
f_model = fft2(I_model ,X,Y); // Perform the fft of the model but do this in the size of the original image. matlab will center I_model and set other pixel to zero
w = conj(model ); // Complex conjugated
g = real( ifft2(w.*f)); // .* will perform a komponent wise multiplicaion e.g. [0][0]*[0][0], [0][1]*[0][1] and not a matrix mul. 
gs =im2uint8(mat2gray(g)); // Convert the resulting correlation into an grayscale image with larger values->higher correlation

// Rest of the code is for displaying only
figure(1);
imshow(gs);
colormap hsv

figure;
[ XX YY] = meshgrid(1:Y,1:X );
colormap hsv 
surfc(XX,YY,double(gs)), title('3D Korrelation') 
min_corr = min(min(gs))
max_corr = max(max(gs))
%axis([1 X 1 Y min_corr max_corr])
colorbar

编辑:这仅适用于灰度图像。

答案 1 :(得分:2)

一种可能性是Boyer-Moore字符串搜索:http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm

当然,你必须使它适应你的图像搜索问题。 假设大图像有N个像素而小的M个像素,你会看到N / M的平均情况,这是相当不错的。

答案 2 :(得分:1)

有许多不同的技术可以在图像中找到子图像。

最直接的方法是在较大的图像上使用小图像的2D相关性。这将非常缓慢但易于实施。只有当子图像与原始图像(无旋转)和相同比例对齐时,它才能正常工作。

如果不是这种情况(您有旋转和/或比例变化),那么您需要更高级的东西。我的选择是使用特征检测算法,如SIFT或SURF。

并且重申我在大多数先前的答案中所提出的内容:使用专为一维字符串(Boyer-Moore)设计的算法进行图像处理是错误的。如果你这样做,你最有可能最终花费数小时来实现和调整在当前环境中不起作用的东西,而你可以使用其他更好的算法。

答案 3 :(得分:1)

它非常快(无法在160毫秒内找到,在1600x900上找到90毫秒)并且是唯一一个你会发现的。 任何加速都是受欢迎的。检查在Win7 / Win10 x64,XE2,XE5下使用24位位图。

uses
  System.Generics.Collections;

type
  TSubImageInfo = record
    X: integer;
    Y: integer;
    Color: integer;
  end;

function ImageSearch(const ASubimageFile: string): TRect;
var
  X, Y, K, _Color: integer;
  _SubImageInfo: TSubImageInfo;
  _SubImageInfoList: TList<TSubImageInfo>;
  _SmallWidth, _SmallHeight, _BigWidth, _BigHeight: integer;
  _MatchingPixels: integer;
  _LTColor, _RTColor, _LBColor, _RBColor: integer;
  _FirstPixels: TList<TSubImageInfo>;
  _Offset: TPoint;
  _Desktop: HDC;
  _ScreenBitmap: TBitmap;
  _SubimageBitmap: TPNGImage;
  _Pos: TPoint;
begin
  Result.Left := -1;
  Result.Top := Result.Left;
  Result.Height := Result.Left;
  Result.Width := Result.Left;

  if not FileExists(ASubimageFile) then
    Exit;

  _SubImageInfoList := TList<TSubImageInfo>.Create;
  _ScreenBitmap := TBitmap.Create;
  _SubimageBitmap := TPNGImage.Create;
  _FirstPixels := TList<TSubImageInfo>.Create;
  try
    _SubimageBitmap.LoadFromFile(ASubimageFile);

    if (_SubimageBitmap.Height < 3) or (_SubimageBitmap.Width < 3) then
      Exit; // Image is too small

    X := 0;
    Y := _SubimageBitmap.Height div 2;
    while X < _SubimageBitmap.Width - 1 do
    begin
      _SubImageInfo.X := X;
      _SubImageInfo.Y := Y;
      _Color := _SubimageBitmap.Canvas.Pixels[X, Y];
      _SubImageInfo.Color := _Color;
      _SubImageInfoList.Add(_SubImageInfo);
      X := X + 3;
    end;

    Y := 0;
    X := _SubimageBitmap.Width div 2;
    while Y < _SubimageBitmap.Height - 1 do
    begin
      _SubImageInfo.X := X;
      _SubImageInfo.Y := Y;
      _Color := _SubimageBitmap.Canvas.Pixels[X, Y];
      _SubImageInfo.Color := _Color;
      _SubImageInfoList.Add(_SubImageInfo);
      Y := Y + 3;
    end;

    X := 0;
    Y := _SubimageBitmap.Height div 4;
    while X < _SubimageBitmap.Width - 1 do
    begin
      _SubImageInfo.X := X;
      _SubImageInfo.Y := Y;
      _Color := _SubimageBitmap.Canvas.Pixels[X, Y];
      _SubImageInfo.Color := _Color;
      _SubImageInfoList.Add(_SubImageInfo);
      X := X + 3;
    end;

    Y := 0;
    X := _SubimageBitmap.Width div 4;
    while Y < _SubimageBitmap.Height - 1 do
    begin
      _SubImageInfo.X := X;
      _SubImageInfo.Y := Y;
      _Color := _SubimageBitmap.Canvas.Pixels[X, Y];
      _SubImageInfo.Color := _Color;
      _SubImageInfoList.Add(_SubImageInfo);
      Y := Y + 3;
    end;

    X := 0;
    Y := (_SubimageBitmap.Height div 4) + (_SubimageBitmap.Height div 2);
    while X < _SubimageBitmap.Width - 1 do
    begin
      _SubImageInfo.X := X;
      _SubImageInfo.Y := Y;
      _Color := _SubimageBitmap.Canvas.Pixels[X, Y];
      _SubImageInfo.Color := _Color;
      _SubImageInfoList.Add(_SubImageInfo);
      X := X + 3;
    end;

    Y := 0;
    X := (_SubimageBitmap.Width div 4) + (_SubimageBitmap.Width div 2);
    while Y < _SubimageBitmap.Height - 1 do
    begin
      _SubImageInfo.X := X;
      _SubImageInfo.Y := Y;
      _Color := _SubimageBitmap.Canvas.Pixels[X, Y];
      _SubImageInfo.Color := _Color;
      _SubImageInfoList.Add(_SubImageInfo);
      Y := Y + 3;
    end;

    _Desktop := GetDC(0);
    _ScreenBitmap.PixelFormat := pf32bit;
    _ScreenBitmap.Width := Screen.Width;
    _ScreenBitmap.Height := Screen.Height;
    BitBlt(_ScreenBitmap.Canvas.Handle, 0, 0, _ScreenBitmap.Width,
      _ScreenBitmap.Height, _Desktop, 0, 0, SRCCOPY);
    _MatchingPixels := 0;
    _SmallWidth := _SubimageBitmap.Width - 1;
    _SmallHeight := _SubimageBitmap.Height - 1;
    _BigWidth := _ScreenBitmap.Width;
    _BigHeight := _ScreenBitmap.Height;

    _LTColor := _SubimageBitmap.Canvas.Pixels[0, 0];
    _RTColor := _SubimageBitmap.Canvas.Pixels[_SmallWidth, 0];
    _LBColor := _SubimageBitmap.Canvas.Pixels[0, _SmallHeight];
    _RBColor := _SubimageBitmap.Canvas.Pixels[_SmallWidth, _SmallHeight];

    for X := 1 to 3 do
    begin
      for Y := 1 to 3 do
      begin
        _SubImageInfo.X := X;
        _SubImageInfo.Y := Y;
        _SubImageInfo.Color := _SubimageBitmap.Canvas.Pixels[X, Y];
        _FirstPixels.Add(_SubImageInfo);
      end;
    end;

    X := 0;
    while X < _BigWidth - _SmallWidth do
    begin
      Y := 0;
      while Y < _BigHeight - _SmallHeight do
      begin
        _Color := _ScreenBitmap.Canvas.Pixels[X, Y];
        _Offset.X := 0;
        _Offset.Y := 0;
        for K := 0 to _FirstPixels.Count - 1 do
        begin
          if (_Color = _FirstPixels[K].Color) then
          begin
            _Offset.X := _FirstPixels[K].X;
            _Offset.Y := _FirstPixels[K].Y;
            Break;
          end;
        end;

        // Check if all corners matches of smaller image
        if ((_Offset.X <> 0) or (_Color = _LTColor)) and
          (_ScreenBitmap.Canvas.Pixels[X + _SmallWidth, Y] = _RTColor) and
          (_ScreenBitmap.Canvas.Pixels[X, Y + _SmallHeight] = _LBColor) and
          (_ScreenBitmap.Canvas.Pixels[X + _SmallWidth, Y + _SmallHeight]
          = _RBColor) then
        begin
          // Checking if content matches
          for K := 0 to _SubImageInfoList.Count - 1 do
          begin
            _Pos.X := X - _Offset.X + _SubImageInfoList[K].X;
            _Pos.Y := Y - _Offset.Y + _SubImageInfoList[K].Y;
            if (_ScreenBitmap.Canvas.Pixels[_Pos.X, _Pos.Y] = _SubImageInfoList
              [K].Color) then
              _MatchingPixels := _MatchingPixels + 1
            else
            begin
              _Pos.X := X - _Offset.X - 1 + _SubImageInfoList[K].X;
              _Pos.Y := Y - _Offset.Y + 1 + _SubImageInfoList[K].Y;
              if (_ScreenBitmap.Canvas.Pixels[_Pos.X, _Pos.Y]
                = _SubImageInfoList[K].Color) then
                _MatchingPixels := _MatchingPixels + 1
              else
              begin
                _MatchingPixels := 0;
                Break;
              end;
            end;
          end;
          if (_MatchingPixels - 1 = _SubImageInfoList.Count - 1) then
          begin
            Result.Left := X - _Offset.X;
            Result.Top := Y - _Offset.Y;

            Result.Width := _SubimageBitmap.Width;
            Result.Height := _SubimageBitmap.Height;
            Exit;
          end;
        end;
        Y := Y + 3;
      end;
      X := X + 3;
    end;

  finally
    FreeAndNil(_FirstPixels);
    FreeAndNil(_ScreenBitmap);
    FreeAndNil(_SubimageBitmap);
    FreeAndNil(_SubImageInfoList);
  end;
end;

它的作用是从文件中加载子图像并在屏幕上搜索它(它按角点颜色识别图像,然后如果匹配它在附加图像中搜索),但您可以轻松地对其进行调整。

enter image description here

结果将是PDF文件图标字母E旁边的屏幕坐标。

答案 4 :(得分:0)

如果您正在搜索完全匹配(即,您要查找的模式与您要查找的图像中的区域之间没有单个像素不同),您实际上可以使用Boyer Moore算法。将其用于寻找2D模式应该是非常简单的。

假设您寻找的图案大小为20x20像素。您构建一个表格,将灰度值(或颜色)映射到模式图像中的位置。从像素19/19开始,现在可以大幅度地浏览搜索图像:如果此像素包含模式中未包含的灰度值,则可以跳过此位置以及周围20x20区域中的所有位置。因此,您要检查的下一个像素将是搜索图像中的39/19。如果它包含一个像素,例如在图案图像的3个位置,您可以在搜索图像(39/19)中测试相对于当前位置的这三个位置。

请注意,此算法有两个假设:

  • 你只能找到确切的匹配。除非直接从搜索图像中提取图案图像,否则这对于真实世界的图像实际上是不可能的。如果源图像和图案图像是例如,它甚至不可能工作。使用相同的扫描仪从同一张照片扫描。如果在提取模式后使用有损压缩(如jpeg)压缩模式或源图像,它将无法工作。
  • 加速取决于使用的灰色值的数量。如果您在二进制图像中寻找二进制模式,则此算法将不会在O(n / m)时间内运行。

答案 5 :(得分:-1)

对于2D位置匹配,我会采用实际的方法解决这个问题: 它们可能是位图......

扫描较大位图中的每一行,从0到Larger.height - Smaller.Height,从0到Larger.Width - Smaller.Width,找到Smaller.TopLeft匹配像素。找到时:

如果Smaller.TopRight和smaller.bottomLeft和lower.bottomRight都等于较大位图中的相应像素(所有角都匹配),则启动该部分的完整比较。

确保所有比较都提前失败(不要在任何不匹配后继续比较)。

平均而言,您只需扫描少于50%的较大位图,并且不会启动许多失败的完整比较。