5.5. Regiões e máscaras

Por omissão, todas as operações do módulo image afetam todos os pixels da imagem de origem. Este é o comportamento mais simples de descrever, e o correto quando o algoritmo atua genuinamente sobre o fotograma inteiro – uma correção de cor uniforme, um histograma global, uma passagem de codificação para transmissão. Mas a maioria dos algoritmos, na prática, precisa de analisar menos do que isso. Um rastreador de manchas que monitoriza um marcador colorido preocupa-se apenas com a parte da cena onde o marcador pode aparecer, não com a parede atrás dele. Uma passagem de limpeza morfológica só é segura sobre os pixels que uma etapa anterior identificou como candidatos. Um detetor de faces pode apenas ser executado dentro da caixa delimitadora que um detetor menos preciso já restringiu. O módulo image suporta esse trabalho através de dois mecanismos que limitam uma operação a um subconjunto de pixels: regiões de interesse retangulares e máscaras binárias. Estes mecanismos compõem-se livremente, e quase todos os métodos que tocam em pixels aceitam um ou outro – ou ambos – como argumento de palavra-chave.

5.5.1. Regiões de interesse

Uma região de interesse é um retângulo de pixels identificado pelo quádruplo (x, y, w, h) apresentado na página de coordenadas. Cerca de trinta métodos da superfície aceitam um argumento de palavra-chave roi; quando presente, a operação é executada apenas sobre os pixels dentro desse retângulo e deixa o resto da imagem intocado. Quando roi é None ou omitido, a operação é executada sobre toda a imagem – o mesmo que se tivesse sido passado roi=(0, 0, width, height).

No código, o argumento de palavra-chave aparece a par dos restantes argumentos que a operação aceita:

# Compute a histogram over a centred crop of the image.
h = img.get_histogram(roi=(64, 64, 128, 128))

O primeiro benefício das ROIs é o controlo de falsos positivos. Um rastreador de cores que só analisa a mesa nunca será acionado pelo casaco que passa à sua frente; um detetor de arestas que só é executado dentro da área de trabalho definida nunca reportará as arestas do próprio suporte da câmara. Reduzir a área de pesquisa à parte da cena que o algoritmo realmente considera é a melhoria mais económica que um pipeline pode fazer à sua própria fiabilidade.

O segundo benefício é o pipeline do grosseiro para o fino. Os objetos de resultado de deteção – um blob, um rect, um apriltag, entre outros – expõem as suas caixas delimitadoras como o mesmo quádruplo (x, y, w, h) que roi aceita. Assim, uma primeira etapa grosseira pode devolver uma caixa delimitadora, essa caixa é passada diretamente para o roi da etapa seguinte, e a segunda etapa é executada sobre a área mais restrita. Cada refinamento progressivo tanto acelera a etapa seguinte como torna os seus resultados mais fiáveis, porque o espaço de pesquisa já foi filtrado.

5.5.2. Máscaras binárias

Um retângulo é a forma adequada quando a área de interesse está alinhada com os eixos. Quando não está – uma região curva, uma região não convexa, os pixels que uma etapa anterior classificou como «correspondências» – a operação tem de ser instruída a limitar-se a um padrão arbitrário de pixels. O mecanismo para isso é uma máscara binária: uma Image separada, com as mesmas dimensões que a origem, usada como um interruptor ligado/desligado por pixel. Um pixel diferente de zero na máscara indica «incluir o pixel de origem correspondente»; um pixel a zero indica «deixar o pixel de origem intocado».

Uma máscara é normalmente uma imagem BINARY – o formato de um bit por pixel que existe precisamente para este propósito – mas qualquer imagem de canal único funciona, porque o consumidor trata qualquer valor diferente de zero como ligado.

Os métodos de filtragem, limiarização e aritmética aceitam um argumento de palavra-chave mask. A forma é a mesma em cada um: uma imagem binária alocada separadamente, com as mesmas dimensões que a origem, passada diretamente.

As ROIs e as máscaras compõem-se. Passando ambas, a operação é executada apenas sobre os pixels que estão dentro da ROI e ativos na máscara. Os dois mecanismos fornecem ao código da aplicação controlos independentes – um para a área retangular de interesse, outro para o padrão arbitrário dentro dela – sem que nenhuma das formas herde restrições da outra.

A small grid representing an image. A dashed rectangle drawn across the upper-middle portion of the grid labels the ROI: only pixels inside this rectangle are considered. Inside the ROI, a roughly circular set of filled cells labels the mask: only those filled cells are actually modified. The remaining cells are shaded lightly to indicate they are untouched.

Uma ROI confina uma operação a um retângulo alinhado com os eixos. Uma máscara restringe-a ainda mais a um padrão arbitrário de pixels. Os dois compõem-se: apenas os pixels dentro da ROI e ativos na máscara são modificados.

5.5.3. Construção de máscaras

Três métodos de Image constroem geometrias de máscara comuns no próprio objeto, zerando os pixels fora da região escolhida:

Cada um aceita (x, y, w, h) (para o retângulo e a elipse) ou (x, y, radius) (para o círculo). Chamar qualquer um deles sem argumentos centra a geometria e dimensiona-a para preencher a imagem, que é a forma usada por uma aplicação quando o objetivo é um óvalo ou círculo simples sobre toda a imagem que oculta apenas os cantos.

mask = image.Image(img.width(), img.height(), image.BINARY)
mask.clear()              # start from all zeros
mask.mask_ellipse()       # centred, full-size oval

As máscaras mais interessantes raramente resultam apenas dos métodos mask_*. Provêm de etapas anteriores do pipeline: uma passagem de limiarização produz uma imagem binária cujos pixels diferentes de zero marcam as correspondências, exatamente a forma certa para alimentar o argumento mask= da etapa seguinte. Uma passagem de limpeza morfológica refina essa máscara sem alterar a sua forma. Qualquer resultado que seja uma imagem de canal único é, em si mesmo, uma máscara válida.

5.5.4. Como as operações modificam a imagem

Um padrão visível em todos os fragmentos de código das últimas páginas – a operação devolver o mesmo img para encadeamento – merece ser explicitado para não ter de ser repetido cada vez que um novo método é apresentado. Na superfície de Image surgem três famílias de métodos, cada uma tratando a imagem de origem de forma diferente:

  • Métodos de operação modificam os pixels da origem no próprio objeto e devolvem a mesma imagem para encadeamento. As famílias de desenho, aritmética, limiar e filtro comportam-se desta forma. img.gaussian(1) desfoca img e devolve o mesmo img; reatribuir – img = img.gaussian(1) – é inócuo mas desnecessário.

  • Métodos de conversão operam no próprio objeto por omissão, da mesma forma que os métodos de operação, mas aceitam copy=True e copy_to_fb=True para alocar uma imagem de resultado separada quando a origem precisa de ser preservada. As conversões de formato e as cópias geométricas são os principais membros desta família.

  • Métodos de inspeção leem os pixels e devolvem um objeto de resultado – uma lista de características detetadas, um histograma, um conjunto de estatísticas – sem modificar a imagem de origem.

Esta tricotomia é consistente em toda a superfície. Saber a que família pertence um método indica à aplicação o que esperar de uma chamada: se os pixels da origem vão sobreviver intactos, se uma imagem de resultado separada vai ser alocada, e se o valor de retorno é a própria origem ou outra coisa.