segmentação - tons de cinza python




Problemas com o uso de um algoritmo de escala de cinza áspero? (5)

As imagens parecem bem parecidas , mas seus olhos podem ver a diferença, especialmente se você colocar uma no lugar da outra:

Por exemplo, você pode observar que as flores no fundo parecem mais brilhantes na conversão de média.

Não é que haja algo intrinsecamente "ruim" na média dos três canais. A razão para essa fórmula é que nós não percebemos o vermelho, o verde e o azul igualmente, então suas contribuições para as intensidades em uma imagem em escala de cinza não devem ser as mesmas; já que percebemos o verde mais intensamente, os pixels verdes devem ficar mais brilhantes em escala de cinza. No entanto, como comentou Mark, não existe uma conversão perfeita para tons de cinza, pois vemos a cor e, de qualquer forma, a visão de todos é ligeiramente diferente, portanto qualquer fórmula tentará fazer uma aproximação tão intensa que os pixels se sintam "certos" para a maioria pessoas.

Então, estou criando alguns programas para editar fotos em python usando PIL e um deles estava convertendo uma imagem em escala de cinza (estou evitando o uso de qualquer função do PIL ).

O algoritmo que empreguei é simples: para cada pixel (profundidade de cor é 24), calculei a média dos valores R , G e B e defino os valores RGB para essa média.

Meu programa estava produzindo imagens em escala de cinza que pareciam precisas, mas eu queria saber se eu empreguei o algoritmo correto, e me deparei com essa resposta a uma pergunta, onde parece que o algoritmo 'correto' é calcular 0.299 R + 0.587 G + 0.114 B

Eu decidi comparar meu programa com esse algoritmo. Gerei uma imagem em escala de cinza usando meu programa e outro (usando a mesma entrada) de um site on-line (o melhor resultado do Google para 'image to grayscale' .

A olho nu, parecia que eram exatamente iguais e, se havia alguma variação, eu não conseguia enxergar. No entanto, decidi usar este site (o melhor resultado do Google para 'compare two images online' ) para comparar minhas imagens em escala de cinza. Descobriu-se que, no fundo dos pixels, eles tinham pequenas variações, mas nenhum que fosse perceptível ao olho humano à primeira vista (diferenças podem ser vistas, mas geralmente apenas quando as imagens são colocadas umas sobre as outras ou alternadas entre milissegundos) .

Minhas perguntas (a primeira é a questão principal) :

  1. Há alguma desvantagem em usar meu algoritmo de escala de cinza "áspero"?
  2. Alguém tem alguma imagem de entrada em que meu algoritmo de escala de cinza produziria uma imagem visivelmente diferente daquela que seria "correta"?
  3. Existem combinações de cores / RBG para as quais meu algoritmo não funcionará tão bem?

Minha chave de código (se necessário):

def greyScale(pixelTuple):
    return tuple([round(sum(pixelTuple) / 3)] * 3)

O algoritmo 'correto' (que parece pesar muito em verde):

def greyScale(pixelTuple):
    return tuple([round(0.299 * pixelTuple[0] + 0.587 * pixelTuple[1] + 0.114 * pixelTuple[2])] * 3)

Minha imagem de entrada:

A imagem em escala de cinza que meu algoritmo produz:

A imagem em escala de cinzentos que é "correta":

Quando as imagens em escala de cinza são comparadas on-line (as diferenças destacadas em vermelho são as mesmas, usando um fuzz de 10%):

Apesar das variações nos pixels destacados acima, as imagens acima da escala de cinza aparecem quase exatamente iguais (pelo menos para mim).

Além disso, em relação à minha primeira pergunta, se alguém estiver interessado, este site fez algumas análises sobre diferentes algoritmos para conversões em tons de cinza e também possui alguns algoritmos personalizados.

EDITAR :

Em resposta à resposta do @Szulat, meu algoritmo realmente produz essa imagem (ignorar o corte ruim, a imagem original tinha três círculos, mas eu só precisava do primeiro):

Caso as pessoas se perguntem qual é o motivo da conversão para escala de cinzentos (como parece que o algoritmo depende do propósito), eu estou apenas fazendo algumas ferramentas simples de edição de fotos em python para que eu possa ter um mini-Photoshop e don ' Não precisa depender da Internet para aplicar filtros e efeitos.

Razão para a recompensa : respostas diferentes aqui estão cobrindo coisas diferentes, que são relevantes e úteis. Isso torna muito difícil escolher qual resposta aceitar. Eu comecei uma recompensa porque eu gosto de algumas respostas listadas aqui, mas também porque seria bom ter uma única resposta que cobre tudo que eu preciso para esta pergunta.


As respostas fornecidas são suficientes, mas quero discutir um pouco mais sobre esse assunto de uma maneira diferente.

Desde que aprendi pintura digital por interesse, mais frequentemente uso o HSV.

É muito mais controlável para usar o HSV durante a pintura, mas é curto, o ponto principal é o S: Saturation separando o conceito de cor da luz. E girando S para 0, já é a escala de cinza 'computador' da imagem.

from PIL import Image
import colorsys

def togrey(img):
    if isinstance(img,Image.Image):
        r,g,b = img.split()
        R = []
        G = []
        B = [] 
        for rd,gn,bl in zip(r.getdata(),g.getdata(),b.getdata()) :
            h,s,v = colorsys.rgb_to_hsv(rd/255.,gn/255.,bl/255.)
            s = 0
            _r,_g,_b = colorsys.hsv_to_rgb(h,s,v)
            R.append(int(_r*255.))
            G.append(int(_g*255.))
            B.append(int(_b*255.))
        r.putdata(R)
        g.putdata(G)
        b.putdata(B)
        return Image.merge('RGB',(r,g,b))
    else:
        return None

a = Image.open('../a.jpg')
b = togrey(a)
b.save('../b.jpg')

Este método realmente reservou o 'brilhante' da cor original. No entanto, sem considerar como o olho humano processa os dados .


Existem muitas fórmulas para a luminância, dependendo das cores primárias R, G, B:

Rec.601/NTSC: Y = 0.299*R + 0.587*G + 0.114*B , 

Rec.709/EBU:  Y = 0.213*R + 0.715*G + 0.072*B , 

Rec.2020/UHD: Y = 0.263*R + 0.678*G + 0.059*B . 

Isso tudo é porque nossos olhos são menos sensíveis ao azul do que ao vermelho do que ao verde.

Dito isto, você provavelmente está calculando Luma, não Luminance, então as fórmulas estão todas erradas de qualquer maneira. Para Constant-Luminance você deve converter para linear-luz

R = R' ^ 2.4 , G = G' ^ 2.4 , B = B' ^ 2.4 , 

aplicar a fórmula de luminância e converter de volta para o domínio gama

Y' = Y ^ (1/2.4) . 

Além disso, considere que a conversão de um espaço de cores 3D para uma quantidade 1D perde 2/3 das informações, o que pode afetá-lo nas próximas etapas de processamento. Dependendo do problema, às vezes uma fórmula diferente é melhor, como V = MAX (R, G, B) (do espaço de cor do HSV).

Como eu sei? Sou seguidora e amiga do Dr. Poynton.


Há muitos métodos diferentes para converter em escala de cinza, e eles fornecem resultados diferentes, embora as diferenças possam ser mais fáceis de ver com diferentes imagens coloridas de entrada.

Como não vemos realmente em escala de cinzentos, o método "melhor" é um pouco dependente da aplicação e um pouco no olho do espectador.

A fórmula alternativa a que você se refere baseia-se no olho humano, sendo mais sensível às variações nos tons verdes e, portanto, dando-lhes uma maior ponderação - semelhante a um array da Bayer em uma câmera onde há dois pixels verdes para cada vermelho e azul. Wiki - matriz Bayer


Você pode usar qualquer equação de conversão, escala, linearidade. Aquele que você encontrou:

I = 0.299 R + 0.587 G + 0.114 B

baseia-se na percepção da percepção da cor primária (R, G, B) da média dos olhos humanos (pelo menos para o período de tempo e população / HW em que foi criada; tenha em atenção que essas normas foram criadas antes do LED, TFT, etc. telas).

Existem vários problemas com os quais você está lutando:

  1. nossos olhos não são os mesmos

    Todos os humanos não percebem a cor da mesma maneira. Existem grandes discrepâncias entre os sexos e menores também entre as regiões; até geração e idade desempenham um papel. Então, mesmo uma média deve ser tratada como "média".

    Temos sensibilidade diferente à intensidade da luz em todo o espectro visível. A cor mais sensível é verde (daí o peso mais alto). Mas os picos da curva XYZ podem estar em comprimentos de onda diferentes para pessoas diferentes (como eu os fiz mudar um pouco causando diferença no reconhecimento de certos comprimentos de onda como alguns tons de Aqua - alguns os vêem como verdes como azuis mesmo que nenhum deles tenha nenhum deficiências do daltonismo ou qualquer outra coisa).

  2. monitores não usam os mesmos comprimentos de onda nem dispersão espectral

    Então, se você pegar dois monitores diferentes, eles podem usar comprimentos de onda ligeiramente diferentes para R, G, B ou mesmo diferentes larguras do filtro espectral ( basta usar um espectroscópio e ver ). Sim, eles devem ser "normalizados" pelo HW, mas isso não é o mesmo que usar comprimentos de onda normalizados. É semelhante a problemas usando fontes de luz de espectro RGB vs. ruído branco.

  3. monitorar a linearidade

    Os seres humanos não enxergam em uma escala linear: nós geralmente somos logarítmicos / exponenciais (depende de como você olha para ele) então sim podemos normalizar isso com HW (ou mesmo SW) mas o problema é se linearizarmos para um humano então significa que nós danificamos para outro.

Se você juntar tudo isso, poderá usar médias ... ou equipamentos especiais (e caros) para medir / normalizar em relação a um padrão ou contra uma pessoa calibrada (depende da indústria).

Mas isso é demais para lidar em condições de casa, então deixe tudo isso para indústria e use os pesos para "média" como a maior parte do mundo ... Felizmente nosso cérebro pode lidar com isso como você não pode ver a diferença a menos que você comece a comparar as duas imagens lado a lado ou em uma animação :). Então eu (faria):

I = 0.299 R + 0.587 G + 0.114 B
R = I
G = I
B = I






image-comparison