Vizinho mais próximo de interpolação algoritmo em MATLAB
-
20-09-2019 - |
Pergunta
Eu estou tentando escrever a minha própria função para a ampliação de uma imagem de entrada usando o vizinho mais próximo algoritmo de interpolação.A parte ruim é que eu sou capaz de ver como ele funciona, mas não é possível localizar o algoritmo em si.Eu vou ser muito grato por qualquer ajuda.
Aqui está o que eu tentei para aumentar a escala da imagem de entrada por um fator de 2:
function output = nearest(input)
[x,y]=size(input);
output = repmat(uint8(0),x*2,y*2);
[newwidth,newheight]=size(output);
for i=1:y
for j=1:x
xloc = round ((j * (newwidth+1)) / (x+1));
yloc = round ((i * (newheight+1)) / (y+1));
output(xloc,yloc) = input(j,i);
end
end
Aqui está a saída, depois de Marca's sugestão
Solução
Um tempo atrás, passei pelo código do imresize
função no Caixa de ferramentas de processamento de imagem MATLAB Para criar uma versão simplificada apenas para a interpolação de imagens mais próxima. Veja como seria aplicado ao seu problema:
%# Initializations:
scale = [2 2]; %# The resolution scale factors: [rows columns]
oldSize = size(inputImage); %# Get the size of your image
newSize = max(floor(scale.*oldSize(1:2)),1); %# Compute the new image size
%# Compute an upsampled set of indices:
rowIndex = min(round(((1:newSize(1))-0.5)./scale(1)+0.5),oldSize(1));
colIndex = min(round(((1:newSize(2))-0.5)./scale(2)+0.5),oldSize(2));
%# Index old image to get new image:
outputImage = inputImage(rowIndex,colIndex,:);
Outra opção seria usar o embutido interp2
função, embora você tenha mencionado não querer usar funções internas em um de seus comentários.
Editar: Explicação
Caso alguém esteja interessado, pensei em explicar como a solução acima funciona ...
newSize = max(floor(scale.*oldSize(1:2)),1);
Primeiro, para obter os tamanhos da nova linha e coluna, os tamanhos antigos da linha e da coluna são multiplicados pelo fator de escala. Este resultado é arredondado para o número inteiro mais próximo com floor
. Se o fator de escala for menor que 1, você pode acabar com um caso estranho de um dos valores de tamanho sendo 0, e é por isso que a chamada para max
Existe para substituir algo menos que 1 por 1.
rowIndex = min(round(((1:newSize(1))-0.5)./scale(1)+0.5),oldSize(1));
colIndex = min(round(((1:newSize(2))-0.5)./scale(2)+0.5),oldSize(2));
Em seguida, um novo conjunto de índices é calculado para as linhas e colunas. Primeiro, é calculado um conjunto de índices para a imagem upsampled: 1:newSize(...)
. Cada imagem pixel é considerada como tendo uma dada largura, de modo que o pixel 1 se abre de 0 a 1, pixel 2 de 1 a 2, etc. A "coordenada" do pixel é assim tratada como o centro, e é por isso que 0.5 é subtraído dos índices. Essas coordenadas são então divididas pelo fator de escala para fornecer um conjunto de coordenadas de pixel-centro para a imagem original, que depois adicionou 0,5 a elas e são arredondadas para obter um conjunto de índices inteiros para a imagem original. A chamada para min
garante que nenhum desses índices seja maior que o tamanho da imagem original oldSize(...)
.
outputImage = inputImage(rowIndex,colIndex,:);
Finalmente, a nova imagem upsamplada é criada simplesmente indexando na imagem original.
Outras dicas
Esta resposta é mais explicativa do que tentar ser concisa e eficiente. Eu penso gnoviceA solução é melhor nesse sentido. Caso você esteja tentando entender como funciona, continue lendo ...
Agora, o problema do seu código é que você está mapeando locais da imagem de entrada para a imagem de saída, e é por isso que você está recebendo o Spotty resultado. Considere um exemplo em que a imagem de entrada é toda branca e a saída é inicializada para preto, obtemos o seguinte:
O que você deve fazer é o oposto (da saída para a entrada). Para ilustrar, considere a seguinte notação:
1 c 1 scaleC*c
+-----------+ 1 +----------------------+ 1
| | | | | |
|----o | <=== | | |
| (ii,jj) | |--------o |
+-----------+ r | (i,j) |
inputImage | |
| |
+----------------------+ scaleR*r
ouputImage
Note: I am using matrix notation (row/col), so:
i ranges on [1,scaleR*r] , and j on [1,scaleC*c]
and ii on [1,r], jj on [1,c]
A ideia é que para cada local (i,j)
Na imagem de saída, queremos mapeá -la para o local "mais próximo" nas coordenadas da imagem de entrada. Como este é um mapeamento simples, usamos a fórmula que mapeia um determinado x
para y
(dado todos os outros parâmetros):
x-minX y-minY
--------- = ---------
maxX-minX maxY-minY
no nosso caso, x
é o i
/j
coordenar e y
é o ii
/jj
coordenada. Portanto, substituir para cada um nos dá:
jj = (j-1)*(c-1)/(scaleC*c-1) + 1
ii = (i-1)*(r-1)/(scaleR*r-1) + 1
Juntando peças, obtemos o seguinte código:
% read a sample image
inputI = imread('coins.png');
[r,c] = size(inputI);
scale = [2 2]; % you could scale each dimension differently
outputI = zeros(scale(1)*r,scale(2)*c, class(inputI));
for i=1:scale(1)*r
for j=1:scale(2)*c
% map from output image location to input image location
ii = round( (i-1)*(r-1)/(scale(1)*r-1)+1 );
jj = round( (j-1)*(c-1)/(scale(2)*c-1)+1 );
% assign value
outputI(i,j) = inputI(ii,jj);
end
end
figure(1), imshow(inputI)
figure(2), imshow(outputI)
O Matlab já fez isso para você. Usar imresize:
output = imresize(input,size(input)*2,'nearest');
ou se você quiser escalar os dois x & y igualmente,
output = imresize(input,2,'nearest');
Você só precisa de uma mais generalizada fórmula para o cálculo xloc e yloc.
xloc = (j * (newwidth+1)) / (x+1);
yloc = (i * (newheight+1)) / (y+1);
Isso pressupõe que as variáveis têm alcance suficiente para a multiplicação dos resultados.