Jump to content

Determinar cor mais semelhante à original


thoga31
 Share

Recommended Posts

Olá, pessoal.

Estou a fazer umas brincadeiras com Bitmaps e surgiu-me um problema.

Tenho um OpenFileDialog para abrir uma imagem a meu gosto, e carrego-a numa PictureBox denominada pb_Image.

Numa outra PictureBox, pb_Comparacao, tenho uma imagem feita por mim que mais não é uma série de pixéis coloridos. Esta é uma imagem 101x3.

A minha intenção é comparar cada pixel da pb_Imagem.Image com todos os pixéis de pb_Comparacao.Image e determinar qual é a cor desta última mais próxima da original.

Determinada a cor mais próxima, atribuo-lhe um valor, determinado pela coordenada do pixel onde se localiza essa cor: X+Y*100.

Até aqui, tudo bem. O problema é que os valores determinados quase nunca são os mais próximos! Por exemplo, em vez de ter branco (que é o que se assemelha a branco, logicamente), aparece-me vermelho...

Estou a utilizar o seguinte método para a comparação - uso dos métodos GetPixel e ToArgb:

Private Sub Comparar()
   'Imagem original e mapa de cores a comparar
   Dim Imagem As New Bitmap(Me.pb_Image.Image)
   Dim Comparacao As New Bitmap(Me.pb_compare.Image)

   'Variáveis necessárias
   Dim ImgPixel As Integer
   Dim CmpPixel(101, 3) As Integer
   Dim Dif As UInteger
   Dim FinalX, FinalY As Integer

   'Cria um array dos valores ARGB de cada pixel do mapa de comparação
   For x = 0 To Comparacao.Width - 1
       For y = 0 To Comparacao.Height - 1
           CmpPixel(x, y) = Comparacao.GetPixel(x, y).ToArgb
       Next
   Next

   'Compara imagem com mapa
   For y = 0 To Imagem.Height - 1
       For x = 0 To Imagem.Width - 1
           'Pixel a comparar
           ImgPixel = Imagem.GetPixel(x, y).ToArgb

           'Inicializa diferença de ARGB
           Dif = UInteger.MaxValue

           'Compara com todos os pixéis do mapa de comparação
           For j = 0 To Comparacao.Height - 1
               For i = 0 To Comparacao.Width - 1
                   'Se a diferença ARGB é menor, então é mais semelhante
                   If Math.Abs(CmpPixel(i, j) - ImgPixel) < Dif Then
                       Dif = Math.Abs(CmpPixel(i, j) - ImgPixel)
                       FinalX = i
                       FinalY = j
                   End If
               Next
           Next

           'Regista valor
           RichTextBox1.Text += NumberTo3Digits((FinalX + FinalY * 100)) '& ";"
       Next

       RichTextBox1.Text += vbNewLine
   Next
End Sub

Existe algum método que eu desconheça para determinar qual é a cor mais próxima?

Cumprimentos,

thoga31

Edited by thoga31

Knowledge is free!

Link to comment
Share on other sites

Para o tipo de comparação que estás a fazer, tens usar o produto de todos os canais, não o valor 32-bit da cor.

Compara longs provenientes da multiplicação de todos os canais.

128 x 128 x 128 x 128 = 268435456 é uma cor semelhante, de 130 x 130 x 130 x 130 = 285610000

Distam em 17174544 tons. Só tens de estipular offsets. Ficaste já a saber que a diferença de 2 valores em cada canal pode produzir 17174544 cores diferentes.

Com estes valores, estamos a falar de dois cinzentos muito próximos.

Na verdade são muito menos "cores", porque estamos a usar o canal alpha na equação, o que replica as 16777216 cores por cada um dos 256 níveis de alpha, totalizando 4294967296 possibilidades.

Edited by ribeiro55

Sérgio Ribeiro


"Great coders aren't born. They're compiled and released"
"Expert coders do not need a keyboard. They just throw magnets at the RAM chips"

Link to comment
Share on other sites

Obrigado, @ribeiro55, de facto foi parvo da minha parte não ter pensado dessa forma.

Para evitar erros de Overflow, e tendo em conta que não pretendo utilizar o Alpha, utilizei o tipo Int64 (ou Long), igualmente para facilitar o uso do método Abs da classe Math.

Cumprimentos 😉

Knowledge is free!

Link to comment
Share on other sites

Não estava à espera que o resultado fosse este... não estou a entender muito bem como é possível, sendo a escala P&B a menos passível de erros... :/

Do lado esquerdo está a imagem original, e do lado direito está o produto da comparação.

comparacao.jpg

Poderá estar a haver alguma falha da minha parte no que diz respeito ao cálculo da diferença de tonalidades?

Knowledge is free!

Link to comment
Share on other sites

Penso que há cores que têm mais peso que outras no RGB. Existe uma propriedade chamada luminância (se não me engano), que dá maior peso para determinadas cores. Penso que será essa a propriedade que te está a afectar as comparações. Os pesos são os seguintes:

R: 29.9%

G: 58.7%

B: 11.4%

Experimenta utilizar estes pesos e vê que resultados consegues obter.

“There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”

-- Tony Hoare

Link to comment
Share on other sites

Penso que há cores que têm mais peso que outras no RGB. Existe uma propriedade chamada luminância (se não me engano), que dá maior peso para determinadas cores. Penso que será essa a propriedade que te está a afectar as comparações. Os pesos são os seguintes:

R: 29.9%

G: 58.7%

B: 11.4%

Experimenta utilizar estes pesos e vê que resultados consegues obter.

Utilizar directamente estes factores na multiplicação de nada vão servir. Assim de repente não estou a ver onde aplicar estes factores, apesar de suspeitar que seja estupidamente fácil :S

Knowledge is free!

Link to comment
Share on other sites

Tens que separar as componentes e aplicar os pesos a cada uma para achares a diferença:

diff = 0.299 * abs(c1.red - c2.red) + 0.587 * abs(c1.green - c2.green) + 0.114 * abs(c1.blue - c2.blue)

“There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”

-- Tony Hoare

Link to comment
Share on other sites

Sim, existem pesos por canal quando queres determinar a luminosidade (penso que luminosidade seja o mais correcto, e foi também o nome que dei na minha imago), e estes afectam a forma como uma cor se relaciona com outra, vá, o como dizes que determinada cor é de facto mais escura do que outra. Não os sei de cor, nem conssigo confirmar esses do KTachyon, mas posso espreitar a source da minha lib. Na altura fiz umas deep researches.

Como deves usar os pesos, depende um pouco do que queres fazer.

Eu uso muitas vezes o cálculo de luminosidade para detectar quando devo usar letras brancas ou pretas, dependendo da luminosidade da cor de fundo.

Dei-te a solução straightforward porque não percebi sinceramente que filtro estás a tentar aplicar.

Consegues explicar de outra forma qual seria o resultado que esperas? Estas a tentar implementar algum filtro em particular?

EDIT: se puderes explica-me à mesma melhor o que estás a tentar aplicar, agora que o KTachyon te deu uma fórmula de pesos na diferença.

Edited by ribeiro55

Sérgio Ribeiro


"Great coders aren't born. They're compiled and released"
"Expert coders do not need a keyboard. They just throw magnets at the RAM chips"

Link to comment
Share on other sites

Estou a fazer uma pequena aplicação que transforma uma imagem num TXT onde cada píxel é definido com um número de 3 dígitos.

Criei uma imagem 101x3 com as cores-padrão, uma em cada pixel.

Isto para quê?

Não consigo desencantar nenhum método que mostre uma imagem em Pascal, pelo que estou a tentar fazer um método que o faça.

Tenho a função em pascal a funcionar: lê o TXT, e converte cada número num pixel de cor correspondente que ele suporta.

Ora, tenho de ter uma forma de converter as imagens nos TXTs, e visto que VB.NET tem estas funções de uso simples e rápido, decidi fazer o conversor em VB.NET.

Mas os problemas vieram com a comparação da cor de cada pixel da imagem original com as 256 cores do "mapa de comparação" (a tal imagem 101x3).

Ele cria correctamente o TXT segundo o formato padrão que defini, mas não está a comparar as cores correctamente.

Não sei se estão a entender bem o meu objectivo. O produto que mostrei na imagem é produzido pelo meu programa em Pascal, e não, nesse não há erros que eu já fiz a experiência a fundo.

Cumprimentos, e obrigado por tudo até agora 😉

Knowledge is free!

Link to comment
Share on other sites

'Cha cá ver se percebi.

O objectivo é recriar uma imagem onde as cores têm de ser o mais aproximadas possível das disponíveis na tal palete 101x3.

É isto?

Ora manda-nos lá a palete...

Sérgio Ribeiro


"Great coders aren't born. They're compiled and released"
"Expert coders do not need a keyboard. They just throw magnets at the RAM chips"

Link to comment
Share on other sites

Pedi-te a palete porque achei o desafio interessante. Agora não posso, mas amanhã vou deslindar uma implementação e depois discutimos os resultados 😉

Percebi bem, certo? O objectivo é aplicar a palete em determinada imagem?

Sérgio Ribeiro


"Great coders aren't born. They're compiled and released"
"Expert coders do not need a keyboard. They just throw magnets at the RAM chips"

Link to comment
Share on other sites

Exactamente. O objectivo é, por pontos:

1) Determinar qual das 247 cores da palete é a mais semelhante à cor de cada pixel de uma imagem;

2) Determinada essa cor, dar-lhe o devido número segundo o código que forneci;

3) Gravar o resultado num TXT, que será simplesmente o conjunto de números de 3 dígitos.

Ter em atenção o padrão do TXT:

1) Cada linha corresponde a uma linha de píxeis;

2) Cada linha termina com um CRLF feito pelo vbNewLine - é mandatório ser assim, caso contrário a aplicação em Pascal não suporta o TXT

3) Cada píxel deve ter os 3 dígitos - a cor 5 será 005.

Também vou dormir agora, não consegui evoluir muito mais, estive a limar as arestas numa unit.

E obrigado pelo interesse! 😉

Cumprimentos, thoga31

Knowledge is free!

Link to comment
Share on other sites

Com muito, muito pouco tempo, escrevi um algoritmozito para apanhar a cor mais próxima.

Com essa palette indexada, obténs isso que obténs, com esta técnica de aproximação, que é a directa.

Existem outros métodos de aproximação que analisam pixeis vizinhos e determinam a melhor aproximação.

Tentei aproximações de luminosidade, e combinação de canais com luminosidade, sendo esta última, a mais aproximada.

Tens pano para mangas 😉

Lembrei-me também de uma forma de perceberes se o teu método de comparação directo está a trabalhar bem.

Usa como palette, a mesma imagem a converter. Se o output ficar exactamente igual, à partida estás a fazer comparações directas correctamente.

Sérgio Ribeiro


"Great coders aren't born. They're compiled and released"
"Expert coders do not need a keyboard. They just throw magnets at the RAM chips"

Link to comment
Share on other sites

Olá, @ribeiro55.

Não vou conseguir fazer exactamente essa experiência a não ser que crie um "script" em VB para analisar o output. O Pascal não suporta mais do que aquelas 248 cores, pelo que o meu Tester não funcemina. 😛

Fazendo contas rápidas de cabeça, o método a que chegámos da multiplicação vai dar o mesmo output, não haveria hipóteses de falhas, acho.

Acho que é melhor tentar experimentar outros métodos para aquilo que pretendo... Mas se o mais próximo a que chegaste foi aquela imagem a que eu cheguei, digo-te já que é mesmo a melhor imagem a que consigui chegar... 😛

Obrigado por tudo. 😉

thoga31

Edited by thoga31

Knowledge is free!

Link to comment
Share on other sites

Não. Cheguei a uma melhor determinando a menor diferença de canal AND a menor diferença de luminosidade.

Mas não tão melhor que valha a pena.

Sérgio Ribeiro


"Great coders aren't born. They're compiled and released"
"Expert coders do not need a keyboard. They just throw magnets at the RAM chips"

Link to comment
Share on other sites

Sinceramente não vale a pena tanto trabalho, não queimes mais neurónios com isto, era só uma experiência breve para ver se adaptava uma unit do Pascal. Depois investigo melhor o caso, de momento tenho outras coisas prioritárias com que me preocupar. 😛

Obrigado 😉

Edited by thoga31

Knowledge is free!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.