5.18. Histogramas e estatísticas¶
Para além das operações que alteram os pixels de uma imagem, a classe Image disponibiliza um conjunto de métodos que os medem – resumem a distribuição dos valores de pixel, devolvem o brilho médio e mediano, encontram o limiar ótimo entre pixels escuros e claros, e indicam a dispersão dos canais de cor. As medições alimentam as aplicações de duas formas: como entradas para o código que decide que limiar usar, que ganho definir e qual é o perfil tonal da cena; e como sinais de diagnóstico – «a cena tem brilho suficiente?» – que uma aplicação pode usar sem ter de tomar uma decisão sobre qualquer pixel individual.
O ponto de partida para quase qualquer medição é o histograma.
5.18.1. O histograma¶
O histograma de uma imagem é uma contagem do número de pixels com cada valor de brilho possível. Para uma imagem em escala de cinzentos, é uma lista de contagens indexadas pelos valores 0 a 255. Para uma imagem a cores, são três listas desse tipo – uma por canal.
get_histogram() calcula um:
h = img.get_histogram()
O objeto devolvido é 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 a somarem 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 cinzentos, o histograma tem um canal de bins, disponível como h.bins() (ou equivalentemente h[0]). Para imagens RGB565, o histograma é calculado no espaço de cores LAB apresentado 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 utilizam; os histogramas concordam com os limiares quanto ao espaço em que a cor está a ser medida.
5.18.2. Bins e o número de bins¶
O histograma predefinido tem um bin por valor de pixel possível – 256 bins para um canal de 8 bits. Por vezes, essa resolução é maior do que a aplicação necessita. Um classificador que só se preocupa com o perfil geral da distribuição pode ser melhor servido por um número menor de bins – 32 ou mesmo 8 – o que é mais rápido e produz um resultado mais limpo face ao ruído. A palavra-chave bins (e as equivalentes por canal l_bins, a_bins, b_bins para cor) define essa contagem:
h = img.get_histogram(bins=32)
O âmbito por ROI e por 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 correspondam a esses intervalos. A forma com limiar é o que torna «calcular o histograma apenas dos pixels correspondentes» uma operação de uma só chamada – um padrão comum quando uma aplicação pretende caracterizar a textura de uma região já detetada sem ter de percorrer os pixels manualmente.
Um histograma em escala de cinzentos com três medições resumo sobrepostas: o limiar de Otsu (o corte que melhor separa os grupos 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; as estatísticas são os resumos numéricos derivados dele. O objeto statistics devolvido por get_statistics() contém o conjunto padrão:
mean– a média aritmética dos valores de pixel.median– o valor abaixo do qual metade dos pixels se encontra.mode– o valor individual mais comum.stdev– o desvio padrão, uma medida da dispersão em torno da média.minemax– os valores de pixel mais brilhante e mais escuro presentes.lqeuq– os cortes do quartil inferior e superior.
Para uma imagem RGB565, as formas por canal (l_mean, a_median, b_mode, etc.) entregam as mesmas medições canal a canal.
A maioria desses valores surge em contextos específicos. mean e stdev em conjunto fornecem uma estimativa do ruído: uma cena que deveria ser uniforme tem um stdev pequeno, ao passo que um sensor ruidoso dá à mesma cena um stdev maior. min e max indicam o contraste da imagem: quanto mais próximos estiverem, mais plana é a cena; quanto mais afastados, maior a gama dinâmica com que o algoritmo pode trabalhar. median é o centro robusto quando a distribuição tem valores atípicos (alguns pixels muito brilhantes não deslocam a mediana como deslocam a média). mode é o valor único mais comum, útil para encontrar o nível de fundo de uma imagem cujo fundo ocupa a maior parte dos pixels.
get_statistics() executa internamente a passagem pelo histograma e depois resume-o; passar os mesmos argumentos thresholds e roi de um histograma previamente calculado produz as estatísticas para o mesmo conjunto de pixels.
5.18.4. Percentis e pesquisas na CDF¶
O objeto histogram expõe um método get_percentile() que transforma uma fração num valor de pixel – o valor abaixo do qual se encontra a fração de pixels solicitada. h.get_percentile(0.5) é a mediana; h.get_percentile(0.05) e h.get_percentile(0.95) em conjunto fornecem um mín/máx robusto que ignora os 5% inferiores e superiores como valores atípicos.
Esta é a forma que uma aplicação usa quando pretende caracterizar o intervalo de valores de pixel sem deixar que um punhado de pixels aleatórios brilhantes ou escuros distorçam a resposta. O mín/máx robusto dos percentis 5 e 95 é também a entrada natural para uma passagem de estiramento de contraste – o remapeamento por pixel que as correções tonais abordam.
5.18.5. Método de Otsu¶
Os histogramas respondem a outra questão que vale a pena destacar por si só: dada uma imagem cujos pixels se dividem num grupo «escuro» e num grupo «claro», qual é o corte entre eles? A página de limiarização já nomeou o mecanismo pelo seu resultado – um limiar global único que a aplicação pode passar a binary() – mas adiou o como. O como é o método de Otsu, e reside no histograma.
A intuição: uma imagem com um primeiro plano e um fundo bem definidos tem dois grupos no seu histograma de brilho, com um vale entre eles. O local correto para o limiar é o fundo do vale – o valor onde os dois grupos estão melhor separados. O método de Otsu pesquisa todos os cortes possíveis e escolhe aquele onde as variâncias intra-grupo são menores (o que equivale a dizer que a variância inter-grupos é maior), e o resultado é a divisão binária ótima para a distribuição dessa imagem em particular.
O objeto histogram expõe o método de Otsu através de get_threshold:
h = img.get_histogram()
t = h.get_threshold()
O objeto threshold devolvido tem atributos value (para escala de cinzentos) ou l_value / a_value / b_value (para cor) com o corte escolhido. Passar o resultado diretamente para binary() produz um limiar global auto-ajustável cujo corte é escolhido pela própria imagem:
img.binary([(t.value, 255)])
Esse padrão não resolve o problema de iluminação não uniforme que o limiar adaptativo baseado em filtros resolve; o que resolve é a questão «a que valor devo fazer o corte?» quando a limiarização global já é a abordagem correta. Para uma cena cuja distinção entre primeiro plano e fundo está bem definida, o valor que Otsu escolhe está normalmente a poucos valores do que um humano escolheria a olho.
5.18.6. Calcular 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 calcula 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. É a forma económica de perguntar «quanto mudou a cena desde o fotograma 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 deteção de movimento em execução contínua, a poupança acumula.