Pesquisar Subimagem dentro de Imagem

02/01/2024

0

Tudo bem? Gostaria de uma mão em algo que estou fazendo: Tenho um bitmap 5x5 de uma cor solid especifica e pesquiso ele em uma area especifica do desktop. O problema é o seguinte: Não funciona 100%, as vezes essa "area" aparece com um tipo de transparência e muda a cor de alguns px e a minha função não acha. Estou batendo cabeça pra tentar resolver e como não tenho experiencia nesse tipo de aplicação estou dando voltas e não saio do lugar! Derrepente se alguém puder só me clarear com uma idéia do que posso fazer! Desde já agradeço.
André Miranda

André Miranda

Responder

Posts

02/01/2024

Arthur Heinrich

O reconhecimento de um padrão é muito utilizado. Nas máquinas de auto atendimento, onde colocamos notas de dinheiro, a máquina utiliza esse tipo de reconhecimento, para identificar se o que foi introduzido é dinheiro verdadeiro e o valor, apesar de o dinheiro muitas vezes estar sujo e rasurado.

A técnica para isso é comparar a sua imagem "padrão" com outra imagem de mesmo formato, utilizando o produto escalar entre dois vetores.

Imagine que sua imagem 5x5 é um vetor de 75 dimensões (25 pixels com 3 componentes cada um RGB).

Você precisa normalizar o seu vetor, dividindo cada componente/dimensão pelo comprimento do vetor, de forma a obter um vetor de comprimento unitário. Supondo "v" um vetor dinâmico de 75 posições, de 0 a 74, contendo os componentes RGB de cada pixel, para normalizá-lo, você faz:

  d:=0;
  for i:=0 to Pred(v) do
    d:=d+sqr(v[i]);
  d:=sqrt(d);
  for i:=0 to Pred(v) do
    v[i]:=v[i]/d;


Depois de normalizar o vetor da sua imagem padrão e da porção de tela que você quer comparar, você executa o produto escalar destes dois vetores, para checar a semelhança entre eles:

  v:=0;
  for i:=0 to Pred(v1) do
    v:=v+v1[i]*v2[i];


O resultado dessa conta será um número real "v", tal que: -1 <= v <= 1

Se o v se aproximar de 0, as imagens são totalmente diferentes. Se ele se aproximar de 1, as imagens serão semelhantes.
Dois vetores idênticos, mas em direções opostas produzirão um valor -1. Portanto, para efeito de análise, podemos utilizar abs(v).

Aí, basta estipular um threshold de tolerância. Digamos que valores de abs(v) >= 0,9 seja considerado igual. Você pode experimentar valores diferentes, de forma a ser mais ou menos tolerante.

Ao normalizar os vetores, eliminamos a luminosidade da comparação. Se considerarmos 3 quadrados, sendo um preto, outro cinza e um branco, ambos são idênticos do ponto de vista do padrão. Mas não são iguais a quadrados de cores distintas, que não façam parte da escala de cinza, onde os componentes RGB tem a mesma proporção.

A única situação em que não é possível executar este procedimento é quando um vetor possui dimensão zero. É o caso particular de um quadrado preto, onde o RGB é (0,0,0). Mas, se quiser, pode introduzir um pequeno erro, somando um resíduo em cada componente, de forma que, ao invés de capturar o RGB dentro de valores de 0 a 255, você obtenha, por exemplo 0,001 a 255,001. Este erro provavelmente não vai interferir significativamente na análise, mas vai permitir a comparação de uma imagem composta somente de preto (0,0,0).

Em uma situação real, pode ser que as imagens a serem comparadas não sejam do mesmo tamanho (resolução) ou estejam rotacionadas. Nestas situações, é necessário primeiro girar a imagem, recortar a porção desejada e redimensiona-la para a dimensão do padrão, antes que possa ser comparada.
Responder

02/01/2024

Arthur Heinrich

Onde se lê "Pred(v)" eu pretendia escrever "Pred(Length(v))" ou "High(v)", para indicar o último elemento.
Responder

02/01/2024

André Miranda

Beleza Arthur? Valeu pelo feedback, vou estudar e tentar entender essa questão e testar! Vou aproveitar e postar o code.

function ImageSearch(const SubImageFile: String): TRect;
var
X, Y: Integer;
SubimageColor: TColor;
ScreenBitmap: TBitmap;
SubImageBitmap: TBitmap;
begin
Result := Rect(-1, -1, -1, -1);

if not FileExists(SubImageFile) then
Exit;

ScreenBitmap := TBitmap.Create;
SubImageBitmap := TBitmap.Create;
try
SubImageBitmap.LoadFromFile(SubImageFile);

if (SubImageBitmap.Height < 3) or (SubImageBitmap.Width < 3) then
Exit; // Acima de 3 px

ScreenBitmap.PixelFormat := pf24bit; // 24 bits
ScreenBitmap.Width := Screen.Width;
ScreenBitmap.Height := Screen.Height;

BitBlt(ScreenBitmap.Canvas.Handle, 0, 0, ScreenBitmap.Width, ScreenBitmap.Height, GetDC(0), 0, 0, SRCCOPY);

for X := ScreenBitmap.Width div 2 to ScreenBitmap.Width - SubImageBitmap.Width do
begin
for Y := ScreenBitmap.Height div 3 to ScreenBitmap.Height - SubImageBitmap.Height do
begin
{for Y := ScreenBitmap.Height - SubImageBitmap.Height downto 0 do
begin
for X := ScreenBitmap.Width - SubImageBitmap.Width downto 0 do
begin}
SubimageColor := SubImageBitmap.Canvas.Pixels[0, 0];

if (ScreenBitmap.Canvas.Pixels[X, Y] = SubimageColor) and
(ScreenBitmap.Canvas.Pixels[X + SubImageBitmap.Width - 1, Y] = SubImageBitmap.Canvas.Pixels[SubImageBitmap.Width - 1, 0]) and
(ScreenBitmap.Canvas.Pixels[X, Y + SubImageBitmap.Height - 1] = SubImageBitmap.Canvas.Pixels[0, SubImageBitmap.Height - 1]) and
(ScreenBitmap.Canvas.Pixels[X + SubImageBitmap.Width - 1, Y + SubImageBitmap.Height - 1] = SubImageBitmap.Canvas.Pixels[SubImageBitmap.Width - 1, SubImageBitmap.Height - 1]) then
begin
Result := Rect(X, Y, X + SubImageBitmap.Width, Y + SubImageBitmap.Height);
Exit;
end;
end;
end;
//ShowMessage('Imagem não encontrada na tela.');
finally
ScreenBitmap.Free;
SubImageBitmap.Free;
end;
end;

procedure Teste;
var
Rect1: TRect;
Center: TPoint;
begin
Rect := ImageSearch('Teste1.bmp'); // 24 bits

if (Rect2.Left <> -1) and (Rect2.Top <> -1) then
begin
// Encontrar o ponto central da posição
Center.X := (Rect2.Left + Rect2.Right) div 2;
Center.Y := (Rect2.Top + Rect2.Bottom) div 2;

SetCursorPos(Center.X, Center.Y);

Sleep(100);

mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(100);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
end;
end;
Responder

03/01/2024

Arthur Heinrich

Um comentário sobre seu código.

Vi que você utilizou "GetDC(0)" para obter o handle da tela e poder executar a cópia (bitblt) para o seu TBitmap.
Se não me engano, quando você faz isso, o handle fica associado à sua aplicação e você precisa liberar o handle depois de utilizar, usando ReleaseDC(...).
Responder

04/01/2024

André Miranda

Opa, Arthur, valeu pelo toque passou despercebido. Mas já abandonei esse código, não deu certo para a minha finalidade a imagem buscada nem sempre aparece com os mesmos pixels, aparece com alpha e isso dai modifica os pixels, dependendo da abordagem da muito falso positivo, e se apertar demais na % de proximidade não acha. Não achei um meio termo. Enfim, dei uma parada pra refrescar um pouco a cabeça e tentar de novo posteriormente. Obrigado!
Responder

04/01/2024

Arthur Heinrich

Se quiser pesquisar na internet por um algoritmo pronto, não procure por comparação de imagens, pois a maioria dos casos vai comparar exatamente, que é o caso em que falha para você. Procure por similaridade de imagens.
Responder

04/01/2024

André Miranda

Cara, nem os prontos, nem os que eu fiz, nada resolveu. É dificil buscar precisão quando o que você quer não é preciso. O botão aparecer de várias formas na tela, por ter transparêcia e tal, teria que criar um padrão, mas não tem como porque com o alpha o que tem atrás atrapalha a precisão modificando os pixels. Quando não tem alpha até da pra ter padrão porque é um botão com fundo rbg(0, 0, 0), e a cor da fonte (255, 255, 255) isso é um padrao, mas nem sempre o botão aparece assim. Sinceramente estou bem desanimado kkkk por isso nunca vi nada do tipo, porque deve ser impossível. rsrsr
Responder

Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.

Aceitar