5.18. Histogramas e estatísticas¶
Além das operações que alteram os pixels de uma imagem, a classe Image traz uma família de métodos que os medem – resumem a distribuição dos valores dos pixels, retornam o brilho médio e mediano, encontram o ponto de corte ideal entre pixels escuros e claros, relatam a dispersão dos canais de cor. As medições alimentam aplicações de duas maneiras: como entradas para o código que decide qual limiar usar, qual ganho definir, qual a aparência do perfil tonal da cena; e como sinais de diagnóstico – “a cena está clara o suficiente?” – sobre os quais uma aplicação pode agir sem tomar uma decisão sobre qualquer pixel específico.
O ponto de partida para quase toda medição é o histograma.
5.18.1. O histograma¶
Um histograma de uma imagem é uma contagem de quantos pixels têm cada valor de brilho possível. Para uma imagem em escala de cinza, isso é uma lista de contagens indexada pelos valores 0 a 255. Para uma imagem colorida, são três dessas listas – uma por canal.
get_histogram() computa um:
h = img.get_histogram()
O objeto retornado é um resultado histogram que expõe as listas de bins por canal e algumas consultas de alto nível sobre elas. As contagens dos bins são normalizadas de modo que somem 1.0 – o histograma descreve o perfil da distribuição em vez da contagem absoluta de pixels, o que torna as medições comparáveis entre imagens de tamanhos diferentes.
Para imagens em escala de cinza, o histograma tem um canal de bins, disponível como h.bins() (ou equivalentemente h[0]). Para imagens RGB565, o histograma é computado no espaço de cores LAB introduzido na página de limiarização binária, com três canais de bins disponíveis como h.l_bins(), h.a_bins(), h.b_bins() (ou h[0], h[1], h[2]). LAB é a mesma escolha que os métodos de limiar e rastreamento usam; os histogramas concordam com os limiares sobre em qual espaço a cor está sendo medida.
5.18.2. Bins e a contagem de bins¶
O histograma padrão tem um bin por valor de pixel possível – 256 bins para um canal de 8 bits. Às vezes essa é uma resolução mais fina do que a aplicação precisa. Um classificador que só se importa com o perfil aproximado da distribuição pode ser melhor servido por uma contagem de bins menor – 32 ou até 8 bins – que tanto roda mais rápido quanto produz um resultado mais limpo contra o ruído. A palavra-chave bins (e as por canal l_bins, a_bins, b_bins para cor) define a contagem:
h = img.get_histogram(bins=32)
A delimitação por ROI e limiar funciona da mesma forma que em todos os outros métodos de medição. Passe um roi para confinar o histograma a um retângulo de pixels; passe uma lista thresholds para incluir apenas os pixels que correspondem a essas faixas. A forma com limiar é o que torna “computar o histograma apenas dos pixels correspondentes” uma operação de uma única chamada – um padrão comum quando uma aplicação quer caracterizar a textura de uma região já detectada sem ter que percorrer os pixels por conta própria.
Um histograma em escala de cinza com três medições de resumo sobrepostas: o limiar de Otsu (o ponto de corte que melhor separa os agrupamentos escuro e claro), a média e a mediana. Cada medição diz algo diferente sobre a mesma distribuição.¶
5.18.3. Estatísticas¶
Um histograma é uma descrição da prevalência de cada valor; estatísticas são os resumos numéricos derivados dele. O objeto statistics retornado por get_statistics() traz o conjunto padrão:
mean– a média aritmética dos valores dos pixels.median– o valor abaixo do qual está a metade dos pixels.mode– o valor único mais comum.stdev– o desvio padrão, uma medida da dispersão em torno da média.minemax– os valores de pixel mais claro e mais escuro presentes.lqeuq– os pontos de corte do quartil inferior e superior.
Para uma imagem RGB565, as formas por canal (l_mean, a_median, b_mode, e assim por diante) entregam as mesmas medições canal a canal.
A maioria desses números surge em contextos específicos. mean e stdev juntos dão uma estimativa de ruído: uma cena que deveria ser uniforme tem stdev pequeno, enquanto um sensor ruidoso dá à mesma cena um stdev maior. min e max dão o contraste da imagem: quanto mais próximos estiverem, mais plana é a cena; quanto mais distantes, maior a faixa dinâmica com que o algoritmo tem que trabalhar. median é o centro robusto quando a distribuição tem outliers (alguns poucos pixels muito brilhantes não puxam a mediana da forma como puxam a média). mode é o valor único mais comum, útil para encontrar o nível de fundo de uma imagem cujo fundo cobre a maioria dos pixels.
get_statistics() executa a passagem do histograma internamente e depois o resume; passar os mesmos argumentos thresholds e roi de um histograma computado anteriormente produz as estatísticas para o mesmo conjunto de pixels.
5.18.4. Percentis e consultas de CDF¶
O objeto histogram expõe um método get_percentile() que transforma uma fração em um valor de pixel – o valor abaixo do qual está a fração solicitada de pixels. h.get_percentile(0.5) é a mediana; h.get_percentile(0.05) e h.get_percentile(0.95) juntos dão um min/max robusto que ignora os 5% inferiores e superiores como outliers.
Essa é a forma que uma aplicação usa quando quer caracterizar a faixa de valores dos pixels sem deixar que um punhado de pixels claros ou escuros isolados distorça a resposta. O min/max robusto dos percentis 5 e 95 também é a entrada natural para uma passagem de alongamento de contraste – o remapeamento por pixel que as Correções tonais abordam.
5.18.5. O método de Otsu¶
Os histogramas respondem a outra pergunta que vale a pena destacar por si só: dada uma imagem cujos pixels se dividem em um agrupamento “escuro” e um “claro”, qual é o ponto de corte entre eles? A página de limiarização já nomeou o mecanismo por seu resultado – um único limiar global que a aplicação pode entregar a binary() – mas adiou o como. O como é o método de Otsu, e ele vive no histograma.
A intuição: uma imagem com um primeiro plano e um fundo bem definidos tem dois agrupamentos em seu histograma de brilho, com um vale entre eles. O lugar certo para limiarizar é o fundo do vale – o valor onde os dois agrupamentos estão melhor separados. O método de Otsu pesquisa cada ponto de corte possível e escolhe aquele em que as variâncias intra-agrupamento são as menores (o que é o mesmo que dizer que a variância entre agrupamentos é a maior), e o resultado é a divisão binária ideal para a distribuição daquela imagem específica.
O objeto histogram expõe Otsu através de get_threshold:
h = img.get_histogram()
t = h.get_threshold()
O objeto threshold retornado tem atributos value (para escala de cinza) ou l_value / a_value / b_value (para cor) que carregam o ponto de corte escolhido. Alimentar o resultado de volta diretamente em binary() dá um limiar global autoajustável cujo ponto de corte é escolhido pela própria imagem:
img.binary([(t.value, 255)])
Esse padrão não resolve o problema de iluminação desigual que o limiar adaptativo baseado em filtro resolve; o que ele resolve é a pergunta “em que valor devo cortar?” quando a limiarização global já é a abordagem correta. Para uma cena cuja distinção entre primeiro plano e fundo é bem definida, o valor que Otsu escolhe geralmente está dentro de algumas poucas unidades do que um humano escolheria a olho nu.
5.18.6. Computando sobre uma imagem de diferença¶
Um detalhe útil sobre get_histogram() e get_statistics(): ambos aceitam uma palavra-chave difference que recebe outra Image e computa o histograma (ou as estatísticas) da diferença por pixel entre a fonte e essa imagem, sem alocar uma imagem de diferença separada. Essa é a maneira barata de perguntar “quanto a cena mudou desde o quadro de referência?” sem pagar por uma chamada explícita a difference() para produzir uma imagem cujo único propósito é ser medida. Para um script de detecção de movimento que roda continuamente, a economia se acumula.