O Algoritmo Cooperativo de Marr & Poggio – versão Flatland
18 de setembro de 2026
A vós, que ao mesmo tempo haveis recebido a bênção da sombra e da luz, que tendes a alegria de ter dois olhos e a noção de perspectiva, que podeis distinguir a beleza das várias cores e ver, de facto ver, um ângulo, que podeis contemplar a circunferência completa de um Círculo lá na vossa feliz terra das Três Dimensões, como fazer-vos compreender as extremas dificuldades por que passamos para nos reconhecermos uns aos outros? (Abbott, 1884)
O algoritmo cooperativo para resolução de disparidades binoculares, proposto por David Marr e Tomaso Poggio, em 1976, constitui um marco incontornável na investigação sobre percepção binocular. Este mostra, pela primeira vez, de que forma o chamado Problema da correspondência – que elementos da imagem óptica de um dos olhos correspondem a que elementos da imagem óptica do outro olho, um problema inverso e subdeterminado – pode ser solucionável em termos computacionais.
Ainda que a solução proposta por Marr e Poggio não seja a única na literatura relevante e, de resto, possa não ser de todo aquela efectivamente implementada pelo sistema visual (para uma revisão geral, ainda que não recente, ver Bruce, Green, & Georgeson, 2003), representa uma oportunidade pedagógica singular ao mostrar de que forma a noção clássica de inferências inconscientes, proposta por von Helmholtz no final do Século XIX, pode ser concretizada num algoritmo computacional neurofisiologicamente plausível (cf. Palmer, 1999). Nesse sentido, e numa unidade curricular sobre percepção, o algoritmo cooperativo pode mostrar-se um momento importante e de charneira entre concepções clássicas e abordagens modernas ao estudo da percepção, revelando as continuidades conceptuais entre teorias cronologicamente distantes. Acresce que, dada a sua simplicidade, uma versão acessível pode perfeitamente ser programada com apenas algumas linhas de código no MATLAB, fomentando metodologias activas de ensino e aprendizagem, como se mostrará de seguida.
Dada a sua finalidade essencialmente pedagógica, iremos considerar aqui não seres que se movem num mundo tridimensional, como nós (uma função MATLAB que implementa o algoritmo cooperativo na resolução de estereogramas de pontos aleatórios, tridimensionais, pode ser encontrada aqui), mas sim habitantes da hipotética Flatland (Abbott, 1884). Esta simplificação, útil para o ensino e compreensão do algoritmo, não compromete a ideia nuclear, sendo quase apenas uma questão de escala expandir o algoritmo de um mundo bidimensional para o mundo tridimensional. Antes de guiar o leitor pela implementação do algoritmo no MATLAB, importa fornecer algumas explicações prévias – um leitor informado poderá apenas passar os olhos por estas e passar à secção seguinte; inversamente, um leitor que apenas deseje compreender a lógica de base do algoritmo cooperativo, pode focar-se na secção seguinte e ignorar aquela que se segue. Obviamente, espera-se que um leitor interessado beneficie da leitura atenta de ambas as secções.
O Problema da Correspondência e o Algoritmo Cooperativo
Para concretizar o problema de base que o algoritmo visa solucionar, considere-se aquele que é talvez o mais famoso habitante da Flatland, o estimável advogado quadrado A. Square. Na Figura 1 vemos, ao fundo, a parte dianteira de A. Square, repetida em todos os três painéis. No seu interior, conseguimos ver as retinas de ambos os seus "olhos", fixados no ponto assinalado com uma cruz mais acima na figura e, logo, imediatamente à frente de A. Square (obviamente, ignoraremos aqui qualquer consideração biológica e/ou biomecânica do sistema visual dos habitantes da Flatland; Abbott, no seu texto ficcional, sugere que esses seres possuem apenas um único olho, facto que iremos ignorar para bem do tópico sobre o qual nos debruçamos). Por "olhos" (doravante deixaremos de usar as aspas) entendemos aqui dois segmentos de recta, constituídos por sequências de células fotorreceptoras, representados na Figura 1 como linhas de pequenos quadrados. Como no mundo tridimensional, essas células respondem à luz que sobre elas incide de direcções visuais particulares, representadas na Figura pelas setas tracejadas. Quando A. Square fixa um qualquer ponto do seu mundo, a luz reflectida por linhas no interior do seu espaço bidimensional (o equivalente de superfícies no nosso mundo) incide sobre as células fotorreceptoras no interior dos seus olhos gerando, em cada um desses, uma imagem óptica. Na Figura 1, podemos ver que os seus dois olhos estão a registar duas imagens ópticas – algumas das células da retina sinalizam um ponto dourado, outras um ponto azulado. Contudo, e este é o aspecto importante, as duas imagens ópticas não são exactamente iguais entre si – há pequenas diferenças que correspondem a disparidades binoculares. Por exemplo, a quinta célula a contar da esquerda regista, no olho esquerdo, um ponto dourado, ao passo que a célula correspondente no olho direito regista um ponto azulado – isto ocorre quando um objecto distal, no campo visual de A. Square, se encontra mais próximo ou mais distante que o ponto de fixação. Tal significa que o ponto dourado na quinta célula do olho esquerdo está a captar a mesma luz que uma qualquer das células douradas do olho direito, a qual pode estar mais para a esquerda (disparidade cruzada, caso o ponto distal esteja mais próximo de A. Square) ou mais para a direita desse olho (disparidade não-cruzada, caso o ponto distal esteja mais distante de A. Square).
Determinar qual dos pontos da retina do olho esquerdo/direito corresponde a que ponto da retina do olho direito/esquerdo é o que chamamos de Problema da Correspondência. A razão pela qual este é um problema subdeterminado reside no facto de que um qualquer ponto de um dos olhos pode corresponder a qualquer outro ponto do outro olho. Na Figura 1, isto significa que o cruzamento entre quaisquer duas direcções visuais de ambos os olhos (intersecções das setas tracejadas) pode, potencialmente, ser uma correspondência verídica. Esta ambiguidade multiplica-se quando se consideram todos os pontos – dadas quaisquer duas imagens ópticas, uma de cada olho, há inúmeras possíveis soluções (que pontos no espaço bidimensional são ou não ocupados por um objecto) das quais apenas uma é verídica. Cada um dos três painéis da Figura 1 mostra, para as imagens ópticas representadas, uma das inúmeras soluções possíveis – note-se que as imagens ópticas, isto é, o que A. Square efectivamente vê por cada um dos seus olhos, são sempre exactamente iguais em todos os três painéis; contudo, o que efectivamente está à frente de A. Square, no seu mundo bidimensional, pode ser uma nuvem de pontos a diferentes profundidades, como no painel A, vários segmentos de linha em diferentes profundidades, como no painel B, ou uma linha mais próxima de si e duas laterais um pouco mais atrás, como no painel A. Importa enfatizar que estas são apenas três possíveis soluções, entre inúmeras possibilidades, razão pela qual o problema parece, à partida, virtualmente insolúvel.
Resolver o Problema da Correspondência requer, pois, diminuir o universo de possíveis soluções, idealmente até que reste apenas uma que, pelo menos na maioria das situações, traduza a configuração verídica de pontos distais, no mundo. Um primeiro constrangimento já foi por nós considerado na Figura 1, pese embora implicitamente: um ponto no mundo não pode aparentar uma dada coloração num olho e uma outra, distinta, no outro – ou seja, pontos que resultem da intersecção de direcções visuais com luminâncias distintas podem ser excluídos (na Figura 1, qualquer intersecção entre setas douradas com setas azuladas e vice-versa). Isto reduz parcialmente o conjunto de potenciais soluções, mas não resolve o Problema da Correspondência, pois restam ainda inúmeras soluções plausíveis. Um segundo constrangimento, que não será por nós aqui considerado, consiste em delimitar uma zona no espaço em torno do ponto de fixação (ou, a rigor, do horóptero) e considerar apenas as soluções nesse contidas – a lógica aqui é excluir pontos excessivamente próximos ou distantes do observador em relação ao ponto de fixação, através de um limiar de fusão estereoscópica, sob o argumento de que, caso exista algum objecto mais ou menos distante que o actual ponto de fixação, este último pode ser ajustado, fixando um novo ponto no mundo. Tal é, de facto, o que acontece, mas, ainda assim, o Problema da Correspondência mantém-se, pois permanecem inúmeras potenciais soluções dentro desse limiar.
A intuição fundamental de Marr e Poggio foi que a própria natureza do mundo que habitamos fornece constrangimentos adicionais, sob a forma de regularidades naturais – na medida em que o sistema visual assenta nalgumas assunções, plausíveis, mas ainda assim inferenciais, acerca das soluções expectáveis no mundo, um número considerável de potenciais soluções pode ser eliminado, solucionando assim o problema. Esta ideia de base é, essencialmente, equivalente à noção de Inferências Inconscientes de von Helmholtz.
Concretamente, no algoritmo cooperativo, Marr e Poggio, propõem dois constrangimentos: (i) assunção de continuidade e (ii) constrangimento de unicidade ou assunção de opacidade. O primeiro – assunção de continuidade – assenta na observação de que, no nosso mundo (e, presumivelmente, na Flatland) a matéria é coesiva e, logo, os objectos no mundo são constituídos por superfícies (ou linhas, na Flatland) contínuas. Dito de outra forma, se um qualquer ponto é uma correspondência verídica então é mais provável que pontos adjacentes e a uma distância similar também o sejam. Este constrangimento é corporalizado no algoritmo por ligações excitatórias entre localizações equidistantes e adjacentes, conforme se verá adiante. O segundo constrangimento – assunção de opacidade – resulta da observação de que, no nosso mundo (e, uma vez mais, na Flatland) a matéria tende a ser opaca (excepção feita a vidros e superfícies translúcidas – não consigo conceber um equivalente na Flatland, pelo que podemos ignorá-los completamente) e, logo, ao longo de qualquer direcção visual somente um único ponto poderá ser uma correspondência verídica (unicidade). Isto é, e por outras palavras, se um ponto é uma correspondência verídica, então correspondências imediatamente atrás, vistas por um ou outro olho, não o poderão ser (pois são por aquele ocludidas); de forma similar, correspondências imediatamente à frente daquela, ao longo da mesma direcção visual, não poderão ser verídicas pois, caso contrário, veríamos esta última e não a anterior (que ficaria ocludida). Na Figura 1, a solução do painel B respeita a assunção de continuidade, mas viola a assunção de opacidade – as correspondências assinaladas constituem segmentos de linha localmente contínuos, mas com uns em frente a outros, o que só seria uma solução válida se esses fossem inteiramente transparentes (e, logo, invisíveis). Já a solução do painel A viola a assunção de continuidade, ainda que não a de opacidade – todos os pontos da nuvem ocupam uma posição única em cada direcção visual, mas encontram-se isolados entre si, sem pontos adjacentes. Finalmente, a solução do painel C respeita ambas as assunções – por isso, talvez tenha parecido ao leitor como a solução mais provável ou plausível e agora deverão ser claras as razões para tal.
A Figura 2 esquematiza a forma como estas assunções podem ser implementadas num algoritmo. O painel A representa o espaço no qual serão feitas as computações necessárias – este resulta da intersecção das direcções visuais do olho esquerdo (linhas da matriz) e do olho direito (colunas da matriz) de A. Square. Cada uma das células desta matriz corresponde a uma dada localização no espaço distal. A diagonal principal, sinalizada com uma linha azul contínua, representa aqueles pontos do campo distal que estimulam pontos correspondentes nas retinas de ambos os olhos (o primeiro receptor do olho esquerdo e o primeiro do olho direito, o segundo receptor do olho esquerdo e o segundo do olho direito, e assim sucessivamente). Esta linha é designada por horóptero e compreende todos os pontos que se encontram à mesma distância do observador que o ponto de fixação. Consequentemente, pontos mais próximos do canto inferior esquerdo e do canto superior direito da matriz que o horóptero representam, respectivamente, localizações mais próximas do observador (resultarão em disparidades cruzadas) e localizações mais distantes que o observador (resultarão em disparidades não-cruzadas), conforme indicado pela seta bidireccional azul e tracejada. Esta matriz pode ser pensada como a representação de uma população de neurónios no cérebro de A. Square – a activação de um qualquer destes neurónios traduz a percepção de um ponto distal com a profundidade correspondente.
O painel B da Figura 2, por sua vez, mostra a mesma matriz, mas agora com algumas células neuronais activas (representadas com cor azul). Os neurónios activos são aqueles que possuem a mesma luminância em ambos os olhos, o que fornece uma primeira aproximação a uma solução. Considere-se a célula H10 (coluna H e linha 10). Na mesma coluna (H), ou seja, na mesma direcção visual do fotorreceptor H do olho direito, existem outras 9 células também activas. De forma similar, na mesma linha (direcção visual do fotorreceptor 10 do olho esquerdo) existem 9 outras células também activas. No total, temos, pois, 18 células que partilham a mesma direcção visual que a célula H10 – em concordância com a assunção de opacidade, esta última será "punida" pelas restantes (e.g., através de ligações inibitórias) por estar activa (claro, ela própria também contribui para a "punição" aplicada às restantes), o que fará diminuir a sua activação proporcionalmente ao número de ligações inibitórias. Por outro lado, a célula H10 possui, em localizações adjacentes e à mesma distância, células também activas (G9 e I11) – estas células, pela assunção de continuidade, reforçarão a célula H10 (ligações excitatórias), o que fará aumentar a sua activação. Considere-se agora a célula P14 – pelo mesmo raciocínio, também 18 células nas mesmas direcções visuais irão diminuir a sua activação (assunção de opacidade); contudo, e ao contrário da célula H10, esta não tem nenhuma célula adjacente e à mesma distância que promova a sua activação. Significa isto que tanto a célula H10 como a P14 terão a mesma magnitude de inibição, mas somente a primeira terá algumas ligações excitatórias que compensem o grau de inibição. O balanço entre as conexões excitatórias e inibitórias, dependente do peso que se atribui a cada uma dessas, determinará se a célula se desactiva ou, pelo contrário, permanece activa: a célula P14, provavelmente, ficará inactiva; a célula H10, provavelmente, manter-se-á activa.
O algoritmo realiza estas computações, não apenas num par de células, mas sim em todas elas. Adicionalmente, o algoritmo é iterativo – ou seja, as mesmas computações são realizadas sucessivamente sobre o resultado da iteração anterior, até que seja atingida uma solução estável.
Versão para a Flatland do Algoritmo Cooperativo – código MATLAB
Para implementar o algoritmo cooperativo, iremos criar um script que possa ser corrido com diferentes imagens ópticas de ambos os olhos (os dados de entrada com base nos quais opera o algoritmo). Para tal, comece-se por criar um novo script no MATLAB, a partir do menu Editor. Todas as porções de código que se seguem devem simplesmente ser copiadas para este script, mantendo a sua ordem.
A primeira parte do script, abaixo, irá definir alguns parâmetros básicos, como o sejam o peso e extensão (quão distantes podem estar células que inibam/excitem cada célula) das ligações excitatórias e inibitórias, o número de iterações, e alguns parâmetros para a função logística que determina a activação das células que codificam a profundidade percebida em cada iteração. Todos estes parâmetros podem ser alterados, o que poderá fazer com que o algoritmo convirja mais ou menos rapidamente para a solução (ou falhe completamente). Os valores pré-definidos foram determinados por mim, por tentativa e erro e parecem funcionar razoavelmente bem, mas certamente que poderão ser melhor afinados (aliás, o leitor será mais adiante convidado a alterá-los por forma a explorar a eficiência do algoritmo). De resto, a primeira linha serve somente para averiguar o tamanho das retinas de ambos os olhos com base na imagem óptica fornecida para o olho esquerdo (que deverá ser igual à do olho direito; estas imagens ópticas serão geradas mais adiante). E por fim, a última linha inicia a matriz de profundidades (similar àquela mostrada na Figura 2) sobre a qual será aplicado o algoritmo. Esta matriz, inicialmente, é apenas constituída por 0s, indicando que as células se encontram todas inactivas.
%% Parte 1 - Definir Parâmetros
tamRetina = size(OlhoEsq,2); % Averiguar o tamanho das retinas
extExc = 21; % Extensão das conexões excitatórias - deve ser um valor ímpar
pesoExc = 0.75; % Peso das ligações excitatórias
extInib = 101; % Extensão das conexões inibitórias - deve ser um valor ímpar
pesoInib = 0.1; % Peso das ligações inibitórias
limiar = 0.5; % Parâmetro central da função logística que governa o grau de activação das célula em cada interacção
declive = 0.05; % Parâmetro de declive da função logística que governa o grau de activação das célula em cada interacção
nIteracoes = 3; % Número de iterações
MapaProfundidades = zeros(tamRetina); % Iniciar a matriz de profundidades
De seguida, são determinadas quais as células desta matriz que, apenas com base na luminância registada nos fotorreceptores das retinas, representam correspondências possíveis (isto é, que células têm o mesmo valor em ambas as retinas).
%% Parte 2 - Activar células com iguais luminâncias em ambos os olhos
% Para cada célula da matriz de profundidades, activar (1) aquelas que têm
% o mesmo valor de luminância em ambos os olhos
for L = 1:tamRetina
for R = 1:tamRetina
if OlhoEsq(L) == OlhoDir(R)
MapaProfundidades(L,R) = 1;
end
end
end
A secção que se segue implementa, de facto, o algoritmo cooperativo e, por isso, é relativamente longa. Essencialmente, as linhas de código seguintes começam por gerar dois filtros, um inibitório e outro excitatório, que serão usados para fazer convoluções iterativas da matriz de profundidades (efectivamente calculando, para cada célula, a magnitude de inibição e de excitação). Estes filtros corporalizam as assunções de continuidade e opacidade. Dada a estrutura da matriz de profundidades, o filtro inibitório é apenas uma cruz (+) de 1s (as restantes células são 0s) – com a convolução, todas as células na mesma coluna e linha que a célula alvo (excluindo esta, para que não ocorra auto-inibição, e aquelas mais distantes que a extensão definida atrás) serão multiplicadas por 1; o somatório dos valores resultantes, traduz o total de conexões inibitórias da célula alvo (isto é, a célula será tão inibida quanta a activação total de outras células na mesma linha e coluna que essa). De forma similar, o filtro excitatório é dado por uma linha diagonal principal de 1s (as restantes células são 0s) – com a convolução, todas na mesma diagonal principal que a célula alvo (exceptuando esta última, para evitar auto-excitação, e aquelas mais distantes que a extensão definida atrás) serão multiplicadas por 1; o somatório dos valores resultantes traduz o total de conexões excitatórias da célula alvo (isto é, a célula será tão activada quanta a activação total de outras células na mesma diagonal principal que essa). Após as convoluções, serão obtidas duas novas matrizes: uma que traduz, para cada célula do mapa de profundidades, a magnitude de excitação e outra a magnitude de inibição. Estas duas matrizes serão ponderadas pelos pesos definidos atrás para rever o grau de activação de cada célula de profundidade e sujeitas a uma função logística que amplifica o grau de inibição/excitação, por forma a facilitar a convergência para uma solução e com base nos parâmetros previamente definidos. O mesmo processo é, depois, repetido sobre o resultado obtido, tantas vezes quanto o número de iterações especificado.
Para efeitos didáticos, as linhas que se seguem incluem também algumas instruções para gerar uma imagem que será actualizada em cada iteração (com um intervalo de meio segundo imposto), devidamente sinalizadas como tal com um comentário. Desta forma, poder-se-á ver a evolução da matriz de profundidades, com algumas células a desaparecerem, outras ficarem activas, até culminar na solução final. As linhas de código referentes à visualização podem, se se desejar, ser eliminadas (nesse caso, o código corre apenas o algoritmo).
Finalmente, uma pequena nota. Para o filtro excitatório é usada a função do MATLAB eye – esta simplesmente devolve uma matriz identidade com o tamanho especificado (isto é, uma matriz com 1s na diagonal principal; em cálculo matricial, a matriz identidade é o elemento neutro da multiplicação). No presente contexto, não se deve confundir o comando eye com algo que tenha que ver com os olhos virtuais que estão aqui a ser considerados.
%% Parte 3 - Algoritmo Cooperativo
% Filtro excitatório - Assunção de Continuidade
FiltroExc = eye(extExc); % Recompensar correspondências na mesma diagonal principal (equidistantes)
FiltroExc((extExc+1)/2,(extExc+1)/2) = 0; % Evitar auto-excitação
% Filtro inibitório - Assunção de Opacidade/Unicidade
FiltroInib = zeros(extInib); % Iniciar o filtro
FiltroInib(:,round(extInib/2)) = ones(1,extInib); % Punir correspondências na mesma linha (mesma direcção visual no olho esquerdo)
FiltroInib(round(extInib/2),:) = ones(extInib,1); % Punir correspondências na mesma coluna (mesma direcção visual no olho direito)
FiltroInib(round(extInib/2),round(extInib/2)) = 0; % Evitar auto-inibição
EstadoN = zeros(tamRetina,tamRetina,nIteracoes+1); % Iniciar uma matriz 3D que registará o estado da matriz de profundidade em cada iteração
EstadoN(:,:,1) = MapaProfundidades; % Guardar estado inicial (baseado em equiluminâncias)
% Algoritmo Cooperativo
for i = 1:nIteracoes
colormap gray % Apenas para visualização - eliminar se desnecessário
imagesc(EstadoN(:,:,i)) % Apenas para visualização - eliminar se desnecessário
axis equal % Apenas para visualização - eliminar se desnecessário
pbaspect([1 1 1]) % Apenas para visualização - eliminar se desnecessário
pause(0.5); % Apenas para visualização - eliminar se desnecessário
MapaExc = conv2(MapaProfundidades, FiltroExc, "same"); % Calcular, para cada célula, a magnitude de excitação
MapaInib = conv2(MapaProfundidades, FiltroInib, "same"); % Calcular, para cada célula, a magnitude de inibição
MapaProfundidades = MapaProfundidades + pesoExc * MapaExc - pesoInib * MapaInib; % Calcular a activação pela ponderação de excitações e inibições
MapaProfundidades = max(MapaProfundidades, 0); % Eliminar eventuais valores negativos (nenhuma célula pode ter uma activação negativa)
MapaProfundidades = MapaProfundidades ./ max(1e-6, max(MapaProfundidades(:))); % Normalizar os valores para uma escala de 0 a 1
MapaProfundidades = 1 ./ (1+exp(-((MapaProfundidades-limiar)/declive))); % Função logística para acelerar a convergência
EstadoN(:,:,i+1) = MapaProfundidades; % Guardar o resultado destra iteração
end
A secção de código que se segue é inteiramente opcional. Apenas gera uma figura mostrando, em painéis distintos, a solução inicial (determinada por equiluminâncias), a solução após ~1/3 do número de iterações, ~1/2 do número de iterações, e a solução final (após o término do número de iterações definido).
%% Parte 4 - Sumário da evolução ao longo das iterações
figure
colormap gray
subplot(2,2,1)
imagesc(EstadoN(:,:,1))
axis equal
pbaspect([1 1 1])
title('Mapa de disparidades inicial, baseado em correspondências de luminância')
xlabel('Imagem Óptica do Olho Direito')
ylabel('Imagem Óptica do Olho Esquerdo')
subplot(2,2,2)
imagesc(EstadoN(:,:,round((1/3) * nIteracoes)+1))
axis equal
pbaspect([1 1 1])
title(['Estado após ', num2str(round(((1/3) * nIteracoes))), ' iterações'])
xlabel('Imagem Óptica do Olho Direito')
ylabel('Imagem Óptica do Olho Esquerdo')
subplot(2,2,3)
imagesc(EstadoN(:,:,round((1/2) * nIteracoes)+1))
axis equal
pbaspect([1 1 1])
title(['Estado após ', num2str(round(((1/2) * nIteracoes))), ' iterações'])
xlabel('Imagem Óptica do Olho Direito')
ylabel('Imagem Óptica do Olho Esquerdo')
subplot(2,2,4)
imagesc(EstadoN(:,:,nIteracoes+1))
axis equal
pbaspect([1 1 1])
title(['Solução final (', num2str(nIteracoes), ' iterações)'])
xlabel('Imagem Óptica do Olho Direito')
ylabel('Imagem Óptica do Olho Esquerdo')
O script pode agora ser guardado na pasta activa com o nome que se deseje. A única coisa que resta para poder ver o algoritmo cooperativo em acção é dispôr de duas matrizes, constituída por apenas uma linha com uma qualquer extensão, representativas das imagens ópticas do olho esquerdo (designada por OlhoEsq) e direito (designada por OlhoDir) de A. Square. Por agora, o leitor pode simplesmente usar o exemplo que se segue, introduzindo as duas linhas que se seguem na janela de comandos do MATLAB (ou, alternativamente, nas primeiras linhas do script, imediatamente antes da "Parte 1") e, de seguida, correndo o script (botão Run no menu Editor, assegunrando-se que está aberto e seleccionado o respectivo ficheiro). Estas duas matrizes, constituídas por 0s e 1s, representam o equivalente na Flatland de um estereograma de pontos aleatórios, em que 0 se refere a um ponto negro e 1 a um ponto branco. Neste caso em particular, as disparidades introduzidas no estereograma deverão resultar na percepção de um segmento de linha, sensivelmente a meio do campo visual, mais próximo do observador e em frente a uma linha parcialmente ocludida no horóptero (similar ao painel C da Figura 1).
OlhoEsq = [1 1 1 0 0 1 1 0 1 0 1 0 1 0 0 0 1 0 0 1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 1 0 1 0 0 1 1 0 1 1 0 1 1 1 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 1 1 0 1 1 0 0 1 1 0 0 1 1 1 0 1 1 1 0 0 0 1 1 0 1 1 1];
OlhoDir = [1 1 1 0 0 1 1 0 1 0 1 0 1 0 0 0 1 0 0 1 0 0 1 0 1 0 0 1 1 0 1 1 0 1 1 1 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 1 1 0 1 1 0 0 0 1 1 0 0 1 0 1 1 0 0 0 1 1 1 0 1 1 0 0 1 1 0 0 1 1 1 0 1 1 1 0 0 0 1 1 0 1 1 1];
Caso o leitor pretenda gerar outros Estereogramas de pontos aleatórios, poderá usar o código que se segue, guardado num script à parte. Este gera duas imagens ópticas, uma para cada olho, codificando disparidades binoculares num segmento de recta definido por alguns parâmetros: DisparidadeLinha especifica a magnitude e direcção da disparidade binocular (valores negativos/positivos para a linha parecer mais próxima/distante e tanto mais quanto maior o valor absoluto); InicioLinha define a localização horizontal do segmentos de recta, mais para a esquerda ou direita relativamente ao observador; finalmente, TamanhoLinha permite definir a extensão do segmento de recta sujeito a disparidade binocular.
Retina = 100;
TamanhoLinha = 40;
InicioLinha = 40;
DisparidadeLinha = -20; % Valores negativos = mais próximo; valores positivos = mais distante
OlhoDir(InicioLinha+DisparidadeLinha:InicioLinha+TamanhoLinha+DisparidadeLinha-1) = OlhoEsq(InicioLinha:InicioLinha+TamanhoLinha-1);
if DisparidadeLinha < 0
OlhoDir(InicioLinha+TamanhoLinha+DisparidadeLinha:InicioLinha+TamanhoLinha-1) = randi([0 1],1,-DisparidadeLinha);
end
if DisparidadeLinha > 0
OlhoDir(InicioLinha:InicioLinha+DisparidadeLinha-1) = randi([0 1],1,DisparidadeLinha);
end
O leitor é convidado a gerar vários estereogramas e testá-los no algoritmo iterativo, alterando os parâmetros do mesmo. Deixo aqui algumas questões que dessa forma poderão ser exploradas:
- O que acontece quando a inibição ou excitação têm demasiado peso?
- Quão eficaz/ineficaz é o algoritmo quando se diminui/aumenta a extensão das ligações inibitórias ou excitatórias?
- De que forma o peso da inibição e/ou excitação interage com a extensão das ligações inibitórias e excitatórias?
- Quão comprido deve ser o segmento de recta sujeito a disparidade para que seja correctamente detectado pelo algoritmo?
- Em que condições são necessárias mais ou menos iterações?
- Quão melhor/pior é o desempenho do algoritmo quando se alteram os parâmetros da função logística que determina o grau de activação das células em cada iteração?
Algumas notas finais
Como se disse logo no início, esta implementação do Algoritmo Cooperativo tem um propósito inteiramente pedagógico e, por isso, apresenta várias simplificações. A primeira delas, pese embora inócua, é, naturalmente, o facto de se aplicar a habitantes da Flatland. Outra, relacionada com a forma como é implementada a assunção de continuidade, contudo, merece algumas linhas. No caso do presente, apenas pontos que estejam exactamente à mesma distância beneficiam de excitação mútua, o que implica que apenas linhas ortogonais com a linha de visão ciclópica sejam percebidas. Os pontos de uma linha com uma ligeira angulação apenas estão sujeitos a inibição, resultando numa solução não verídica. Uma implementação mais cuidadosa do algoritmo implicaria a consideração de uma maior densidade de conexões excitatórias não apenas entre pontos exactamente à mesma distância mas também a distâncias ligeiramente maiores/menores. O mesmo vale, de resto, para a versão tridimensional do algoritmo: nem todas as superfícies são paralelas com o plano fronto-paralelo, podendo perfeitamente serem vistas com alguma angulação que deve ser tida em conta. Esta ressalva, pertinente do ponto de vista computacional, traduz-se tão-somente nesta pequena nota do ponto de vista didático.
Bibliografia
-
Abbott, E. A. (1884/2006). Flatland: Uma Aventura em Muitas Dimensões. Assírio & Alvim.
-
Bruce, V., Green, P. R., & Georgeson, M. A. (2003). Visual Perception: Physiology, Psychology and Ecology (4th ed.). Psychology Press.
-
Marr, D., & Poggio, T. (1976). Cooperative Computation of Stereo Disparity. Science, 194(4262), 283–287.
-
Palmer, S. E. (1999). Vision Science: Photons to Phenomenology. The MIT Press.