Aqui vamos supor que se deseja converter uma imagem colorida em escala (tons) de cinza, tal qual fizemos anteriormente em
Convertendo Imagens para Preto e Branco, em
Halftone Floyd-Steinberg e em
Halftone Floyd-Steinberg - Falso e Verdadeiro, mas desejamos manter os tons de uma determinada cor na imagem.
Para que seja definido quais cores (tonalidades) serão convertidas para tons de cinza e quais permanecerão na tonalidade escolhida, leva-se em conta um pouco de subjetividade, mas também é preciso calcular a "distância" entre a cor que vamos usar como referência para que sejam mantidos os tons e a cor da imagem original, estipulando uma tolerância (um limiar), pois dependendo da abrangência mínima ou máxima poderão ser atingidas tonalidades próximas a outras cores e essas serão (ou não) afetadas também no processo da conversão, ou seja, cores não desejadas podem ser mantidas coloridas ao invés de convertidas em tons de cinza.
O mais comum para estabelecer a "distância" ou "diferença" entre 2 cores R1G1B1 e R2G2B2 é o uso da fórmula Euclidiana:
Tentando estabelecer uma fórmula que gere tons com maior afinidade com a percepção do olho humano, uma das propostas é o uso de pesos na fórmula Euclidana:
Uma proposta alternativa (
CompuPhase) é a de levar em consideração, na fórmula de pesos Euclidiana, o quanto de vermelho há em um determinado tom de cor. Primeiro, calcula-se o nível médio de vermelho entre as cores e depois os deltas RGB entre as cores como função desse nível médio. Ainda persiste a questão subjetiva da escolha da cor de referência a ser comparada com cada tom na imagem, mas os resultados são aceitáveis e com bom desempenho.
Uma possível implementação segue abaixo, onde a função
FSbutRGB recebe os parâmetros
$Img (imagem colorida),
$cor (cor de referência) e
$tolerancia (se a diferença das cores for menor que a tolerância, mantém a cor original).
Function FSbutRGB([System.Drawing.Image]$Img, [System.Drawing.Color]$cor, [Int32]$tolerancia) {
Write-Host -ForegroundColor Green "Convertendo para escala de cinza, com tolerância: $tolerancia"
Write-Host -ForegroundColor DarkYellow "Será mantida na imagem tons da cor:"$cor.Name
Write-Host -ForegroundColor Green "Será salvo na mesma pasta um arquivo NomeOriginal_FSbutRGB.jpg"
[Int32]$p=0
Foreach($y in (0..($Img.Height-1))) {
$p++
If ($p -gt (($Img.Height)*0.05)) {
Write-Host -NoNewLine '.'
$p = 0
}
Foreach($x in (0..($Img.Width-1))) {
$ColorPixel = $Img.GetPixel($x,$y)
## Algoritmo Color Metric
## https://www.compuphase.com/cmetric.htm
$rMed = (($ColorPixel.R)+$cor.R)/2
$deltaR = (($ColorPixel.R)-$cor.R)
$deltaG = (($ColorPixel.G)-$cor.G)
$deltaB = (($ColorPixel.B)-$cor.B)
$deltaC = [math]::Sqrt((2+$rMed/256)*$deltaR*$deltaR+4*$deltaG*$deltaG+(2+(255-$rMed)/256)*$deltaB*$deltaB)
If ($deltaC -le $tolerancia) {
$R = $ColorPixel.R
$G = $ColorPixel.G
$B = $ColorPixel.B
}
Else {
$R = (0.2125*$ColorPixel.R)+(0.7154*$ColorPixel.G)+(0.0721*$ColorPixel.B)
$G = $R
$B = $R
}
$A = $ColorPixel.A
$Img.SetPixel($x,$y,[System.Drawing.Color]::FromArgb([Int32]$A,[Int32]$R,[Int32]$G,[Int32]$B))
} ## $x
} ## $y
} ## EndFunction FSbutRGB
$Arquivo = New-Object System.Windows.Forms.OpenFileDialog
$Arquivo.filter = "Imagens (*.PNG;*.BMP;*.JPG;*.JPEG;*.GIF)|*.PNG;*.BMP;*.JPG;*.JPEG;*.GIF"
$Arquivo.ShowDialog() | Out-Null
$ArquivoSemExt = ($Arquivo.filename).Substring(0,($Arquivo.filename).Length-4)
$Img = [System.Drawing.Image]::FromFile($Arquivo.filename)
Clear-Host
Write-Host "##############################################"
Write-Host "Arquivo de Imagem:"$Arquivo.filename
Write-Host "##############################################"
FSbutRGB $Img '0x00007F' 100
Write-Host ""
Write-Host -ForegroundColor Yellow "Conversão concluída!"
$Img.Save($ArquivoSemExt+"_FSbutRGB.jpg","JPEG")
$Img.Dispose()
Supondo a seguinte imagem colorida:
No código-exemplo, estamos convertendo essa imagem para tons (escala) de cinza, e gostaríamos de manter a cor de referência cujo valor hexa é
0x00007F, com uma tolerância de valor 100. O resultado é o seguinte:
Utilizando para a cor de referência um valor hexa de
0x004401, mantendo-se a mesma tolerância.
Agora para "pegar" a cor vermelha, através de uma cor de referência, em hexa,
0xB40608 foi preciso ajustar a tolerância para 150.
O caso da cor amarela é mais interessante de ser observado, pois há 3 tons de amarelo na imagem. Um deles próximo à cor verde, outro próximo a cor vermelha e, finalmente, o tom de amarelo que decidimos manter, com uma cor hexa de referência
0xF7D600.
Primeiro experimento com uma tolerância de valor 150, produz o seguinte:
Segundo experimento com uma tolerância de valor 100, produz o seguinte:
No terceiro experimento encontramos um valor de tolerância que atinge o objetivo (70):