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

Assista grátis a nossa aula inaugural

Assitir aula

Saiba por que programar é uma questão de
sobrevivência e como aprender sem riscos

Assistir agora

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

Aceitar