5.12. Limiarização binária¶
Muitos pipelines de processamento de imagem se resumem a uma pergunta sobre cada pixel: este brilho está dentro da faixa que significa “primeiro plano”? Esta cor está próxima o suficiente do vermelho para ser o marcador que a aplicação está rastreando? Este pixel faz parte do conjunto candidato que o próximo estágio do pipeline deve examinar? A limiarização é a operação que transforma essas perguntas em uma resposta binária em cada posição – ligado se o pixel corresponde, desligado se não – e reduz toda a imagem a uma máscara contra a qual o restante do pipeline pode trabalhar.
5.12.1. O método binary¶
O método binary() executa essa classificação em todos os pixels em uma única chamada. Ele recebe uma lista de faixas de limiar – as condições que um pixel pode corresponder para contar como “ligado” – e reescreve a imagem de modo que todo pixel que correspondeu a pelo menos uma das faixas seja definido com o valor máximo do formato, e todo pixel que não correspondeu seja definido como zero. O resultado é a máscara binária que o restante do pipeline pode usar diretamente.
Na forma mais simples, a lista de limiar tem uma faixa e a chamada retorna uma máscara de pixels nessa faixa:
img.binary([(120, 255)])
A forma de lista é o que torna binary poderoso. Um pipeline que deseja rastrear dois marcadores coloridos, ou uma faixa de brilho mais um pico isolado de saturação, passa ambas as faixas na mesma lista e obtém uma única máscara de saída cobrindo todas as correspondências.
A limiarização transforma uma imagem de valores contínuos em uma máscara binária: cada pixel dentro da faixa de limiar torna-se o máximo do formato, cada pixel fora dela torna-se zero.¶
5.12.2. A tupla de escala de cinza¶
Para uma imagem em escala de cinza, cada entrada na lista de limiar é uma tupla de dois elementos (lo, hi) descrevendo uma faixa de brilho inclusiva. Pixels com valores entre lo e hi (inclusive) correspondem; tudo fora dessa faixa não corresponde. Os padrões naturais são diretos:
(0, 60)corresponde a pixels escuros – tudo do preto até o cinza profundo.(180, 255)corresponde a pixels claros – tudo do cinza-claro até o branco.(100, 160)corresponde a pixels cinza-médio – uma banda no meio da faixa de brilho.
A ordem dos dois valores dentro de uma tupla não importa; o método os troca internamente se lo for maior que hi, então (60, 0) funciona da mesma forma que (0, 60).
5.12.3. A tupla LAB para cor¶
Para uma imagem RGB565, cada entrada é uma tupla de seis elementos (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) descrevendo uma faixa inclusiva no espaço de cor LAB em vez de diretamente em vermelho, verde e azul. Os limiares são L (luminosidade), A (eixo cromático verde-vermelho) e B (eixo cromático azul-amarelo), cada um comparado com o valor do pixel naquele canal.
A razão para passar pelo LAB em vez de limiarizar RGB diretamente é a propriedade em torno da qual o espaço de cor LAB foi projetado: o LAB separa a luminosidade da cromaticidade. Dois pixels que mostram a mesma cor mas em brilhos diferentes acabam em valores L diferentes, mas em valores A e B aproximadamente iguais. Essa separação permite que as faixas de limiar descrevam uma cor por sua posição nos eixos A e B e deixem a faixa L bem aberta para aceitar essa cor em todos os brilhos, da sombra ao realce. Um limiar baseado em RGB não consegue fazer isso – qualquer mudança na iluminação move os três valores R, G, B de uma vez, e um rastreador construído sobre limiares RGB quebra na primeira vez que uma nuvem passa na frente do sol.
O padrão prático: escolha as faixas A e B que descrevem a cor que a aplicação está rastreando, e deixe a faixa L ampla – frequentemente (0, 100) para aceitar qualquer brilho – a menos que a aplicação queira especificamente limiarizar tanto pelo brilho quanto pela cor.
Para tuplas com menos de seis valores, os componentes ausentes assumem por padrão a faixa máxima (nenhuma restrição naquele eixo). Uma tupla de dois elementos (l_lo, l_hi) em uma lista de limiar RGB565, portanto, limiariza apenas pela luminosidade e corresponde a todas as cores.
Nota
Uma faixa L verdadeiramente bem aberta tem uma armadilha na extremidade inferior. À medida que a luminosidade cai em direção a zero, todas as cores convergem para o preto, com os valores A e B colapsando em direção a zero e tornando-se dominados por ruído – de modo que pixels escuros podem entrar nas faixas A e B e ser rastreados como a cor-alvo. Se regiões pretas da cena aparecerem como correspondências, aumente l_lo até que elas saiam.
5.12.4. Flags¶
Três argumentos de palavra-chave controlam a saída:
invert=Trueinverte o resultado. Todo pixel que teria correspondido torna-se zero, e todo pixel que teria sido zero torna-se o valor máximo. Útil quando a forma natural de descrever o primeiro plano é por aquilo que ele não é.zero=Truemuda o modo de operação: os pixels correspondentes são zerados e os pixels não correspondentes mantêm seus valores originais. Use isso quando o objetivo é apagar os pixels correspondentes da imagem em vez de reduzir a imagem a uma máscara binária deles.to_bitmap=Trueretorna o resultado como uma imagemBINARYem vez de sobrescrever o formato existente da origem. O resultado de um bit por pixel é o que os argumentos de máscara posteriores aceitam diretamente, e a conversão frequentemente poupa a pressão de memória de carregar uma máscara de formato completo.
A máscara e a ROI seguem a mesma convenção do restante da interface: um retângulo roi limita a operação a uma subárea, uma imagem mask a limita a um padrão arbitrário de posições.
5.12.5. No local por padrão¶
Como as operações aritméticas, binary é executado no local por padrão: os pixels da imagem de origem são sobrescritos com a saída binária, e os valores originais desaparecem após a chamada. A forma to_bitmap=True é a alternativa quando a origem precisa ser preservada e a saída deve ser uma imagem BINARY recém-alocada. A forma copy=True também é aceita para um resultado de mesmo formato em um novo buffer.