5.12. Limiarização binária

Muitos pipelines de processamento de imagem resumem-se a uma pergunta sobre cada pixel: este brilho está dentro do intervalo que significa «primeiro plano»? Esta cor está suficientemente próxima do vermelho para ser o marcador que a aplicação está a seguir? Este pixel faz parte do conjunto candidato que a próxima etapa do pipeline deve analisar? A limiarização é a operação que transforma essas perguntas numa resposta binária em cada posição – ativado se o pixel corresponde, desativado se não corresponde – e reduz a imagem inteira a uma máscara com a qual o resto do pipeline pode trabalhar.

5.12.1. O método binary

O método binary() executa essa classificação em todos os pixels numa única chamada. Recebe uma lista de intervalos de limiar – as condições que um pixel pode satisfazer para contar como «ativo» – e reescreve a imagem de modo a que cada pixel que correspondeu a pelo menos um dos intervalos seja definido com o valor máximo do formato, e cada 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 limiares tem um intervalo e a chamada devolve uma máscara dos pixels nesse intervalo:

img.binary([(120, 255)])

A forma de lista é o que torna binary poderoso. Um pipeline que quer seguir dois marcadores coloridos, ou um intervalo de brilho mais um pico de saturação isolado, passa ambos os intervalos na mesma lista e obtém uma única máscara de saída que cobre todas as correspondências.

A horizontal grayscale gradient at the top labelled "input -- pixel values from 0 to 255". Below it, an inclusive threshold range from lo to hi is marked with brackets along the gradient, enclosing the range of brightness values that count as matches. A binary output bar at the bottom shows white inside the lo-to-hi range and black outside it.

A limiarização transforma uma imagem com valores contínuos numa máscara binária: cada pixel dentro do intervalo de limiar torna-se o máximo do formato, cada pixel fora torna-se zero.

5.12.2. O tuplo em escala de cinzentos

Para uma imagem em escala de cinzentos, cada entrada na lista de limiares é um tuplo de dois elementos (lo, hi) que descreve um intervalo de brilho inclusivo. Os pixels com valores entre lo e hi (inclusive) correspondem; tudo fora desse intervalo não corresponde. Os padrões naturais são diretos:

  • (0, 60) corresponde a pixels escuros – tudo desde o preto até ao cinzento escuro.

  • (180, 255) corresponde a pixels claros – tudo desde o cinzento claro até ao branco.

  • (100, 160) corresponde a pixels de cinzento médio – uma faixa no meio do intervalo de brilho.

A ordem dos dois valores dentro de um tuplo não importa; o método troca-os internamente se lo for maior do que hi, pelo que (60, 0) funciona da mesma forma que (0, 60).

5.12.3. O tuplo LAB para cor

Para uma imagem RGB565, cada entrada é um tuplo de seis elementos (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) que descreve um intervalo inclusivo no espaço de cores 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 nesse canal.

A razão para usar o LAB em vez de limiarizar o RGB diretamente é a propriedade para a qual o espaço de cores LAB foi concebido: o LAB separa a luminosidade da crominância. Dois pixels que mostram a mesma cor mas com diferentes brilhos acabam com valores L diferentes mas com valores A e B aproximadamente iguais. Essa separação permite que os intervalos de limiar descrevam uma cor pela sua posição nos eixos A e B e deixem o intervalo L completamente aberto para aceitar essa cor em qualquer brilho, da sombra ao realce. Um limiar baseado em RGB não consegue fazer isso – qualquer alteração na iluminação move os três valores R, G, B em simultâneo, e um seguidor construído com limiares RGB falha assim que uma nuvem passa pelo sol.

O padrão prático: escolher os intervalos A e B que descrevem a cor que a aplicação está a seguir, e deixar o intervalo L amplo – frequentemente (0, 100) para aceitar qualquer brilho – a menos que a aplicação queira especificamente limiarizar também em função do brilho.

Para tuplos com menos de seis valores, os componentes em falta assumem o intervalo máximo por defeito (sem restrição nesse eixo). Um tuplo de dois elementos (l_lo, l_hi) numa lista de limiares RGB565 limiariza portanto apenas em função da luminosidade e corresponde a qualquer cor.

Nota

Um intervalo L verdadeiramente amplo tem uma particularidade na extremidade inferior. À medida que a luminosidade se aproxima de zero, todas as cores convergem para o preto, com os valores A e B a colapsar para zero e a serem dominados pelo ruído – pelo que pixels escuros podem entrar nos intervalos A e B e ser seguidos como a cor alvo. Se regiões pretas da cena aparecerem como correspondências, aumente l_lo até que desapareçam.

5.12.4. Opções

Três argumentos de palavra-chave controlam a saída:

  • invert=True inverte o resultado. Cada pixel que teria correspondido torna-se zero, e cada pixel que teria sido zero torna-se o valor máximo. Útil quando a forma natural de descrever o primeiro plano é pelo que ele não é.

  • zero=True muda o modo de operação: os pixels correspondentes são colocados a zero e os pixels não correspondentes mantêm os seus valores originais. Use isto quando o objetivo é apagar os pixels correspondentes da imagem em vez de reduzir a imagem a uma máscara binária deles.

  • to_bitmap=True devolve o resultado como uma imagem BINARY em 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 poupa frequentemente a pressão de memória de transportar uma máscara em formato completo.

A máscara e o ROI seguem a mesma convenção que o resto da interface: um retângulo roi limita a operação a uma sub-área, uma imagem mask limita-a a um padrão arbitrário de posições.

5.12.5. Em vez por defeito

Tal como as operações aritméticas, binary é executado em vez por defeito: os pixels da imagem de origem são sobrescritos com a saída binária, e os valores originais perdem-se após a chamada. A forma to_bitmap=True é a alternativa quando a origem precisa de ser preservada e a saída deve ser uma imagem BINARY recentemente alocada. A forma copy=True também é aceite para um resultado no mesmo formato num novo buffer.