Urgente : Algoritmo de interpolação bicubia para zoom em imagem

Estou tentando implementar o algoritmo de interpolação bicubica mas estou tendo muita dificuldade com o acerto dos valores interpolados( não é relacionado a pegar os valores da matriz original e sim o relacionamento dos pesos das distâncias).

Seguindo a ideia desse rapaz aqui (esse caso é para a interpolação bilinear mas ajuda a entender a ideia dos pesos) :
https://stackoverflow.com/questions/16341167/bilinear-interpolation

Nesse caso, os pesos para uma imagem de ampliação de fator 2 ficaria : 0 0.5 1( aqui mudaria para próximo pixel da imagem original).

Mas em uma interpolação bicubica são usados 16 pixels como mostrado na imagem a seguir :

inserir a descrição da imagem aqui

O que cria tipo uma " mascara 4x4 " dos pixels originais, ou seja só mudaria o index quando x ou y passar de 3. Fiz uma lógica para esse index e funciona mas a minha duvida está relacionado ao dx e ao dy pois eles terão que ir de 0 a 3 ( pois é a distância horizontal e vertical ) ponderados entre 0 e 1 certo ? ou seja, para uma ampliação de fator 2 eles terão que ir de 0 0.5 1 1.5 2 2.5 3( ponderado seria 0 0.1666 0.25 0.5 0.666 0.83 1 ) ?

Estou fazendo com esses pesos e jogando no polinômio, que pode ser visto aqui : http://www.paulinternet.nl/?page=bicubic

Mas os valores estão muito a quem do desejado ( embora esteja pegando os pixels certinho e fazendo o algoritmo certo).

A ideia da manipulação dos pesos ponderados é assim mesmo ? Ou está incorreto ?

Se puder explicar de forma mais exemplificada esse algoritmo para ver se estou pensando da forma correta eu agradeceria.

EDITADO :

Por exemplo, considere a matriz a seguir :

inserir a descrição da imagem aqui

Se formos aumentá-la em ordem 2 ficaria com seus respectivos pesos ficaria algo do tipo :

inserir a descrição da imagem aqui

Se fizermos

P= Cubo(pixel[index],pixel[index+1],pixel[index+2],pixel[index+3],dx);
Q= Cubo(pixel[index+4],pixel[index+5],pixel[index+6],pixel[index+7],dx);
R= Cubo(pixel[index+8],pixel[index+9],pixel[index+10],pixel[index+11],dx);
S= Cubo(pixel[index+12],pixel[index+13],pixel[index+14],pixel[index+15],dx);

matriz[i][j] =Cubo(P,Q,R,S,dy);

onde o cubo seria essa função :

Cubo(double[] p, double x) { 
 return p[1] + 0.5 * x*(p[2] - p[0] + x*(2.0*p[0] - 5.0*p[1] + 4.0*p[2] - p[3] + x*(3.0*(p[1] - p[2]) + p[3] - p[0]))); 
}

Se fizermos isso para o valor da linha 0 e coluna 1 ( dx = 0.1666 e dy=0) vai gerar { 5.04 ,1.98, 1.69, 3.02} e o resultado disso matriz[i][j] =Cubo(5.04 ,1.98, 1.69, 3.02, dy) vai ser 1.98 enquanto que deveria dar algo entre 8 e 5.

Alguém sabe o que está acontecendo de errado ?

1 curtida

É isso mesmo, nada errado

8 5 7 9
4 2 3 5
6 1 9 7
5 3 4 2

A interpolação cúbica tem maior aproximação no centro onde estão os quatro números em destaque. Se fosse uma interpolação bilinear, usaria:

2 3
1 9

Lembrando que o x e y da função varia de 0.0 a 1.0, portanto ao usar a função, o resultado será entre aqueles 4 números centrais

1 curtida

Acho que tem algo errado sim pois o valor, no meu entendimento, deveria dar algo entre 8 e 5 para o pixel da matriz[0][1] que seria esse com x :

8 X 5

Mas o valor é 1.98. Se aplicar isso ai em uma imagem vai ficar distorcido pois o valor do pixel interpolado não é o esperado. Deveria dar algo parecido com o da interpolação bilinear que o valor para o mesmo exemplo é de 6.5.
Lembrando que do jeito que estou fazendo o index é 0 ( pois só haverá uma mascara de cubo para esse exemplo especifico ) então eu começo a pegar os pixels da posição 0 e vou até a 15 do vetor que corresponde a imagem original.

Nesse caso acho que estou pegando os valores errados. Como é que procederia para pegar os valores corretos para passar para o polinômio ?

O resultado está dando 1.98 por causa que vc está calculando entre o 2 e o 3 (o 1 abaixo deve estar diminuindo o valor). Os pontos são os centrais. Se fosse bilinear, a matriz usada como parâmetro para calcular o pixel [0,0] na interpolada, seria:

8 5
4 2

Ou seja, somente os 4 pontos centrais

Vc deve estar se confundindo, pois a imagem exemplo tem tamanho 4x4, o parâmetro da função também é 4x4, pois pega os 4 pontos centrais e os 12 pontos vizinhos.

supondo a imagem

8 5 7 9 8 5
4 2 3 5 4 2
6 1 9 7 1 1
5 3 4 2 2 2
4 5 3 1 2 2
1 1 2 4 9 8

A primeira matriz (parametro) para pegar o resultado da posição [0,0] na interpolada será:

? ? ? ?
? 8 5 7
? 4 2 3
? 6 1 9

Observe que são os 4 pontos são centrais e os 12 pontos externos, entretanto os ? estão fora da imagem pois se referem as posições negativas. Não sei como é decidido os ?, vou copiar então os valores vizinhos

8 8 5 7
8 8 5 7
4 4 2 3
6 6 1 9

Portanto, sem fazer conta, eu sei que o ponto [0,0] na imagem interpolada será próximo de 8.

Se a escala é 6, então os parâmetros para o ponto [1,0] na interpolada seria a matriz:

8 8 5 7
8 8 5 7
4 4 2 3
6 6 1 9

com dx = 0.1666 e dy = 0;

quando dx chegar a 1.0, ou seja na posição [6,0], os parâmetros serão:

8 5 7 9
8 5 7 9
4 2 3 5
6 1 9 7

com dx = 0 e dy = 0; pois a matriz foi atualizada

Se vc olhar a imagem que vc postou, verá que o calculo é referente aos pontos centrais.

Para obter os valores:

Imagem interpolada;
Imagem original;
matriz4x4 mat;
double escala = 1.0 * original.tamanho / interpolada.tamanho;

for (y < interpolada.tamanho; y++) {
  for (x < interpolada.tamanho; x++) {
    // copia os pontos centrais no x, y e os vizinhos
    copiarParaMatriz(original, mat, (int) (escala * x), (int) (escala * y));
    int resultado = calcular(mat, flutuante(escala * x), flutuante(escala * x));
    interpolada.set(x, y, resultado);
  }
}

double flutuante(valor) {
  // retorna a parte flutuante do valor
  return valor - (int)(valor);
}

Deixe-me ver se entendi bem essa parte da escolha dos pontos (estava confuso essa parte de pegar os pontos em regiões fora da imagem mas replicar os vizinhos faz muito sentido):

Se for para interpolar o valor entre 7 e 9

7 x 9

Da imagem :

 8 5 7 9
 4 2 3 5
 6 1 9 7
 5 3 4 2

A mascara será :

5 7 9 9
5 7 9 9
2 3 5 5
1 9 7 7

Com dx=0.83 e dy =0 ?

Sim, eu replicaria, não sei qual seria o correto. Se fosse linear, para dx = 0, estaria mais próximo do 7 e dx = 1.0 estaria mais próximo do 9 para a mesma máscara.

Ok diego12, obrigado pela ajuda.