2014-11-08 16 views
5

sto cercando di capire la funzione:imresize - cercando di capire l'interpolazione bicubica

function [weights, indices] = contributions(in_length, out_length, ... 
              scale, kernel, ... 
              kernel_width, antialiasing) 


if (scale < 1) && (antialiasing) 
    % Use a modified kernel to simultaneously interpolate and 
    % antialias. 
    h = @(x) scale * kernel(scale * x); 
    kernel_width = kernel_width/scale; 
else 
    % No antialiasing; use unmodified kernel. 
    h = kernel; 
end 

Io non capisco che cosa fa questa linea significa

h = @(x) scale * kernel(scale * x); 

mia scala è di 0,5
Il kernel è cubico.

Ma a parte questo cosa significa? Penso che sia come creare una funzione che verrà chiamata più tardi?

risposta

10

imresize esegue l'anti-aliasing durante il ridimensionamento di un'immagine semplicemente allargando il kernel cubico, piuttosto che una fase di pre-elaborazione discreta.

Per un kernel_width di 4 pixel (8 dopo ridimensionato), dove la funzione contributions utilizza 10 vicini per ogni pixel, il kernel vs h (kernel scala) assomigliare (non normalizzata, ignorare asse x):

enter image description here

Questo è più facile del primo un filtro passa basso o una convoluzione gaussiana in una fase di pre-elaborazione separata.

Il kernel cubico è definito nella parte inferiore imresize.m come:

function f = cubic(x) 
% See Keys, "Cubic Convolution Interpolation for Digital Image 
% Processing," IEEE Transactions on Acoustics, Speech, and Signal 
% Processing, Vol. ASSP-29, No. 6, December 1981, p. 1155. 

absx = abs(x); 
absx2 = absx.^2; 
absx3 = absx.^3; 

f = (1.5*absx3 - 2.5*absx2 + 1) .* (absx <= 1) + ... 
       (-0.5*absx3 + 2.5*absx2 - 4*absx + 2) .* ... 
       ((1 < absx) & (absx <= 2)); 

PDF of the referenced paper.

La parte rilevante è l'equazione (15):

enter image description here

Questa è una specifica versione delle equazioni di interpolazione generali per a = -0.5 nelle seguenti equazioni:

enter image description here

a è di solito impostato a -0.5, o -0.75. Si noti che a = -0.5 corrisponde allo Cubic Hermite spline, che sarà continuo e avrà un primo derivato continuo. OpenCV seems to use -0.75.

Tuttavia, se si modifica [OPENCV_SRC] \ moduli \ ImgProc \ src \ imgwarp.cpp e modificare il codice:

static inline void interpolateCubic(float x, float* coeffs) 
{ 
    const float A = -0.75f; 
    ... 

a:

static inline void interpolateCubic(float x, float* coeffs) 
{ 
    const float A = -0.50f; 
    ... 

e ricostruire OpenCV (suggerimento: disable CUDA e il modulo gpu per tempi di compilazione brevi), quindi ottieni gli stessi risultati. Vedere l'output corrispondente in my other answer a una domanda correlata dall'OP.

+1

+1 - Cool! ... ecco da dove vengono queste chiavi! – rayryeng

+2

@Gilad Ricordo che stavi esaminando l'interpolazione cubica di MATLAB rispetto a OpenCV e sembra che la differenza sia a = -0.5 per MATLAB e [a = -0.75 per OpenCV] (https://github.com/Itseez/opencv/ blob/master/modules/ImgProc/src/imgwarp.cpp # L155). – chappjc

+2

@chappjc: +1 buona ricerca. In realtà stavo scrivendo una risposta a riguardo :) – Amro

9

Questa è una sorta di un follow-up alla tua previousquestions la differenza tra imresize in MATLAB e cv::resize in OpenCV dato un interpolazione bicubica.

Ero interessato a scoprire perché c'è una differenza.Queste sono le mie conclusioni (poiché ho compreso gli algoritmi, correggimi se faccio degli errori).


Pensate ridimensionamento di un'immagine come una trasformazione planare da un'immagine input di dimensione M-by-N ad un'immagine di uscita di dimensioni scaledM-by-scaledN.

Il problema è che i punti non si adattano necessariamente alla griglia discreta, pertanto per ottenere intensità di pixel nell'immagine di output, è necessario interpolare i valori di alcuni dei campioni adiacenti (solitamente eseguiti nell'ordine inverso, cioè per ogni pixel di uscita, troviamo il corrispondente punto non intero nello spazio di input e interpolato attorno ad esso).

Questo è dove gli algoritmi di interpolazione differiscono, scegliendo la dimensione del vicinato e i coefficienti di peso che danno a ciascun punto in quel quartiere. La relazione può essere di ordine primo o superiore (dove la variabile coinvolta è la distanza dal campione non intero inverso mappato ai punti discreti sulla griglia dell'immagine originale). Solitamente si assegnano pesi maggiori a punti più vicini.

Guardando imresize in MATLAB, ecco i pesi funzioni lineari e noccioli cubi:

function f = triangle(x) 
    % or simply: 1-abs(x) for x in [-1,1] 
    f = (1+x) .* ((-1 <= x) & (x < 0)) + ... 
     (1-x) .* ((0 <= x) & (x <= 1)); 
end 

function f = cubic(x) 
    absx = abs(x); 
    absx2 = absx.^2; 
    absx3 = absx.^3; 
    f = (1.5*absx3 - 2.5*absx2 + 1) .* (absx <= 1) + ... 
     (-0.5*absx3 + 2.5*absx2 - 4*absx + 2) .* ((1 < absx) & (absx <= 2)); 
end 

(tratta in sostanza restituire il peso interpolazione di un campione in base a quanto dista da un punto interpolato.)

Questo è come queste funzioni assomigliano:

>> subplot(121), ezplot(@triangle,[-2 2]) % triangle 
>> subplot(122), ezplot(@cubic,[-3 3])  % Mexican hat 

interpolation_kernels

Si noti che il kernel lineare (le funzioni lineari a tratti su intervalli [-1,0] e [0,1] e gli zero in altri casi) funzionano sui punti a 2 punti vicini, mentre il kernel cubico (pezzo per pezzo le funzioni cubiche sugli intervalli [-2, -1], [-1,1] e [1,2] e zeri altrove) funzionano su 4 punti adiacenti.

Qui è un'illustrazione per il caso 1-dimensionale, che mostra come interpolare il valore x dai punti discreti f(x_k) usando un kernel cilindrata:

1d_interpolation

Il kernel funzione h(x) è centrata a x, la posizione del punto da interpolare. Il valore interpolato f(x) è la somma ponderata dei punti adiacenti discreti (2 a sinistra e 2 a destra) ridimensionati dal valore della funzione di interpolazione in quei punti discreti.

Say se la distanza tra x e il punto più vicino è d (0 <= d < 1), il valore interpolato in posizione x sarà:

f(x) = f(x1)*h(-d-1) + f(x2)*h(-d) + f(x3)*h(-d+1) + f(x4)*h(-d+2) 

dove l'ordine dei punti è descritta sotto (si noti che x(k+1)-x(k) = 1):

x1  x2 x x3  x4 
o--------o---+----o--------o 
     \___/ 
     distance d 

Ora, poiché i punti sono discreti e campionato a intervalli uniformi, e la larghezza del kernel è solitamente di piccole dimensioni, l'interpolazione può essere formulato sinteticamente come convoluzione funzionamento:

interp_conv_equation

Il concetto si estende a 2 dimensioni semplicemente prima interpolazione lungo una dimensione, e quindi interpolando attraverso l'altra dimensione utilizzando i risultati della fase precedente.

Ecco un esempio di interpolazione bilineare, che in 2D considera 4 punti vicini:

bilinear_interpolation

L'interpolazione bicubico in 2D utilizza 16 punti vicini:

bicubic

Innanzitutto interpolare lungo le righe (i punti rossi) usando i 16 campioni della griglia (rosa). Quindi interpoliamo lungo l'altra dimensione (linea rossa) usando i punti interpolati del passo precedente. In ogni fase viene eseguita un'interpolazione 1D regolare. In questo le equazioni sono troppo lunghe e complicate per me da elaborare a mano!


Ora, se torniamo alla funzione cubic in MATLAB, in realtà corrisponde alla definizione del nucleo di convoluzione mostrato nella reference paper come l'equazione (4). Qui è la stessa cosa tratta da Wikipedia:

conv_kernel

Si può vedere che nella definizione di cui sopra, MATLAB ha scelto un valore di a=-0.5.

Ora la differenza tra l'attuazione in MATLAB e OpenCV è che OpenCV scelto un valore di a=-0.75.

static inline void interpolateCubic(float x, float* coeffs) 
{ 
    const float A = -0.75f; 

    coeffs[0] = ((A*(x + 1) - 5*A)*(x + 1) + 8*A)*(x + 1) - 4*A; 
    coeffs[1] = ((A + 2)*x - (A + 3))*x*x + 1; 
    coeffs[2] = ((A + 2)*(1 - x) - (A + 3))*(1 - x)*(1 - x) + 1; 
    coeffs[3] = 1.f - coeffs[0] - coeffs[1] - coeffs[2]; 
} 

Questo potrebbe non essere evidente subito, ma il codice non calcolare i termini della funzione di convoluzione cubica (elencate subito dopo l'equazione (25) nella carta):

bicubic_kernel

Noi in grado di verificare che con l'aiuto della Symbolic Math Toolbox:

A = -0.5; 
syms x 
c0 = ((A*(x + 1) - 5*A)*(x + 1) + 8*A)*(x + 1) - 4*A; 
c1 = ((A + 2)*x - (A + 3))*x*x + 1; 
c2 = ((A + 2)*(1 - x) - (A + 3))*(1 - x)*(1 - x) + 1; 
c3 = 1 - c0 - c1 - c2; 

Quelle espressioni possono essere riscritti come:

012.351.641,061 mila
>> expand([c0;c1;c2;c3]) 
ans = 
     - x^3/2 + x^2 - x/2 
(3*x^3)/2 - (5*x^2)/2 + 1 
- (3*x^3)/2 + 2*x^2 + x/2 
      x^3/2 - x^2/2 

che corrispondono alle condizioni dall'equazione sopra.

Ovviamente la differenza tra MATLAB e OpenCV si riduce all'utilizzo di un valore diverso per il termine libero a. Secondo gli autori del documento, un valore di 0.5 è la scelta preferita perché implica proprietà migliori per l'errore di approssimazione rispetto a qualsiasi altra scelta per a.

+0

Ottima risposta. Grazie per aver verificato che le equazioni corrispondano alla toolbox Simbolico simbolico. Ne vale la pena! – chappjc

+1

@chappjc: Grazie. Peccato che il valore del parametro 'a' sia un codice difficile in entrambe le implementazioni. Se vogliamo che corrispondano, in MATLAB dovresti modificare la funzione incorporata 'imresize' (che non mi piace mai), e in OpenCV dovresti compilare il tutto da fonti solo per lanciare un valore dei denti! Sarebbe interessante vedere se OP cambia 'a' in OpenCV a' -0.5' e verifica che otteniamo risultati identici tra le due implementazioni. L'ultima volta che l'ho provato, ricordo che ci sono voluti 10 minuti per compilare OpenCV da graffiare. – Amro

+0

@Amro OMG grazie mille! Mi servono 2 giorni per leggere i tuoi risultati completi! – Gilad

Problemi correlati