5.2. Coordenadas e regiões¶
O processamento de imagem actua sobre pixels e, para actuar num pixel, um algoritmo tem de o endereçar por coordenada. Para actuar sobre um rectângulo de pixels, o mesmo se aplica – o rectângulo tem de ser descrito de uma forma em que o algoritmo e o código da aplicação concordem. A convenção que o módulo image utiliza para coordenadas e rectângulos é simples, com um detalhe que apanha quem está habituado à convenção matemática em vez da convenção de computação gráfica, e que vale a pena esclarecer desde o início.
5.2.1. A grelha de pixels¶
O pixel (0, 0) é o canto superior esquerdo de uma imagem. O eixo x aponta para a direita, pelo que valores maiores de x significam mais à direita. O eixo y aponta para baixo, pelo que valores maiores de y significam mais abaixo na imagem. Uma imagem de largura por altura contém pixels em coordenadas inteiras de (0, 0) até (width - 1, height - 1); não existe nenhum pixel em (width, 0) ou (0, height) – essas posições correspondem às margens direita e inferior, um passo além do último pixel real em cada direcção.
O eixo y descendente é o detalhe mencionado acima. Quem está habituado à geometria de papel quadriculado espera que valores de y maiores signifiquem mais acima; aqui essa intuição é exactamente invertida. A razão para a inversão é que tanto os sensores digitais como os monitores digitais trabalham a partir do canto superior esquerdo e percorrem cada linha da esquerda para a direita, de cima para baixo, e dispor os pixels na memória pela mesma ordem torna a relação entre «posição i no buffer» e «linha r, coluna c da imagem» uma operação aritmética tão simples quanto possível – a posição i do pixel (x, y) é simplesmente y * width + x. Todas as bibliotecas de processamento de imagem concordaram nessa disposição há décadas pela mesma razão, e o custo é um pequeno ajuste mental na primeira vez que se trabalha com imagens.
O sistema de coordenadas da imagem: origem no canto superior esquerdo, x a apontar para a direita, y a apontar para baixo. Uma região rectangular no interior da imagem é identificada pelo seu canto superior esquerdo (x, y) e pelas suas dimensões (w, h).¶
5.2.2. Rectângulos¶
A maioria das operações sobre uma imagem interessa-se menos por um único pixel do que por um rectângulo de pixels – uma área para pesquisar, uma região para extrair, um fotograma dentro de um fotograma sobre o qual calcular estatísticas. A forma de identificar um rectângulo recorre à extensão mais simples possível da convenção do pixel individual: indicar a coordenada do canto superior esquerdo, seguida das dimensões do rectângulo, empacotadas numa quádrupla (x, y, w, h). Os pixels dentro do rectângulo estão nas colunas x a x + w - 1 e nas linhas y a y + h - 1.
O detalhe que vale a pena esclarecer aqui é que w e h são tamanhos, não coordenadas do canto inferior direito. O rectângulo (10, 20, 4, 3) abrange as colunas 10, 11, 12, 13 e as linhas 20, 21, 22 – doze pixels no total – e não uma região que vai de (10, 20) a (4, 3). A convenção é uniforme em todo o módulo, pelo que, uma vez interiorizada, os erros deixam de ocorrer, mas costuma apanhar as pessoas na primeira vez.
A forma (x, y, w, h) aparece em três contextos que parecem distintos mas partilham a mesma convenção. O primeiro é quando uma imagem descreve a sua própria área: o rectângulo que cobre toda a imagem é (0, 0, width, height). O segundo é quando um método de deteção devolve um resultado com uma caixa delimitadora – um blob, um rect, um apriltag – e a caixa é reportada como (x, y, w, h). O terceiro é quando um método precisa de ser instruído a trabalhar numa sub-região da imagem em vez de todo o fotograma; o argumento de palavra-chave roi que delimita a operação aceita a mesma quádrupla.
Pegar na caixa delimitadora de um método e passá-la para o argumento roi do método seguinte é um dos padrões mais comuns no processamento de imagem. A caixa delimitadora de uma primeira deteção grosseira restringe a área de pesquisa para uma segunda deteção mais refinada, e o vocabulário uniforme entre os resultados de deteção e os argumentos dos métodos é o que torna esse padrão tão simples – uma forma de quádrupla, utilizada da mesma forma em ambos os lados da transição.
5.2.3. Endereços inteiros, centróides fraccionários¶
Os endereços de pixels são inteiros. Um pixel existe ou não existe numa dada coluna e linha inteira, e perguntar o que existe na coordenada (40.5, 30.7) não é uma questão bem formulada – não há nenhum pixel exactamente nessa posição. No entanto, um punhado de quantidades que o módulo image deriva de posições de pixels são fraccionárias, e vale a pena compreender porquê, para que a distinção não apanhe a aplicação mais tarde.
O caso mais comum é o centróide – o centro de massa de uma região. Para uma região conexa de pixels, o centróide em forma de vírgula flutuante é a média das posições dos pixels membros, ponderada pela sua densidade. Uma região cujos pixels abrangem duas colunas terá um centróide x de, digamos, 41.6 – uma posição real que o olho descreveria como «o meio dessa região», mesmo que nenhum pixel real esteja exactamente nesse x. Os objectos de resultados de deteção transportam ambas as formas como propriedades só de leitura: um par inteiro (cx / cy, útil para alimentar a posição de volta a algo que espera coordenadas inteiras de pixels) e um par de vírgula flutuante (cxf / cyf, útil quando a posição vai entrar num ciclo de controlo que beneficia de resolução sub-pixel).
O outro caso é o deslocamento entre dois fotogramas medido no domínio da frequência. As técnicas que analisam o conteúdo espectral de uma imagem em vez dos seus pixels directamente podem resolver deslocamentos mais finos do que um pixel, e reportam esses deslocamentos como valores de vírgula flutuante (dx, dy).
A regra geral: os endereços de pixels são inteiros; as posições e os deslocamentos que saem de um algoritmo podem ser vírgulas flutuantes. Os métodos de desenho aceitam qualquer forma e arredondam para baixo os valores de vírgula flutuante para o pixel inteiro mais próximo quando o resultado tem de ficar na grelha.
5.2.4. Cartesiano e polar¶
O sistema descrito até agora é Cartesiano: cada pixel é identificado pelo seu deslocamento horizontal e vertical em relação à origem. É o sistema em que os bytes são armazenados – o pixel i no buffer corresponde ao pixel na coluna i % width e na linha i // width, percorrendo as linhas a partir do topo – e é o sistema em que todos os métodos operam por omissão.
Uma segunda representação vale a pena conhecer porque alguns algoritmos funcionam muito melhor nela. As coordenadas polares identificam cada pixel pela sua distância a um ponto central escolhido e pelo ângulo entre ele e uma direcção de referência. Os pixels da imagem não se moveram – os bytes continuam no mesmo buffer por linhas – mas o esquema de endereçamento passou de «quão à direita e quão abaixo» para «quão longe do centro e a que ângulo em volta dele.»
O mesmo ponto P, identificado de duas formas: Cartesiano (x, y) a partir da origem no canto superior esquerdo, polar (r, theta) a partir de um centro escolhido.¶
Porque vale a pena mudar? Por causa de duas identidades que transformam pesquisas difíceis em fáceis.
Nas coordenadas polares, rodar a imagem em torno do centro escolhido é a mesma operação que transladar os seus pixels ao longo do eixo do ângulo – a direcção x na imagem reprojectada. Uma cópia rodada é o original deslocado para a esquerda ou para a direita na forma polar.
Na variante log-polar – o eixo da distância usa uma escala logarítmica, o eixo do ângulo mantém-se linear – escalar a imagem em torno do centro escolhido é a mesma operação que transladar os seus pixels ao longo do eixo da distância – a direcção y. Uma cópia escalada é o original deslocado para cima ou para baixo na forma log-polar.
Assim, um algoritmo que tem de reconhecer um padrão conhecido sob rotação ou escala pode efectuar a sua pesquisa no espaço polar, onde ambas as transformações se tornam translações simples. As translações são muito mais baratas para pesquisar do que as rotações e escalas, e a reprojecção polar é o que torna a substituição disponível.
As coordenadas polares não substituem as Cartesianas para armazenar pixels; os bytes residem sempre na grelha Cartesiana. O módulo disponibiliza um par de métodos que reprojectam uma imagem de forma Cartesiana para forma polar sob pedido, o algoritmo que necessita de coordenadas polares efectua o seu trabalho, e ou o resultado é reprojectado de volta ou a medição no espaço polar é utilizada directamente. Esse mecanismo é a única razão pela qual as coordenadas polares aparecem em qualquer parte da interface do módulo.
Com coordenadas Cartesianas para identificar pixels individuais, a quádrupla (x, y, w, h) para identificar rectângulos de pixels, e coordenadas polares disponíveis quando um algoritmo beneficia delas, uma aplicação dispõe de um vocabulário completo para identificar onde numa imagem algo se encontra. O que está efectivamente armazenado em cada uma dessas posições é a próxima camada da fundação.