2014-11-08 14 views
5

Ich versuche, um die Funktion zu verstehen:imresize - versuchen, die bikubische Interpolation zu verstehen

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 

Ich verstehe nicht wirklich, was diese Linie

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

meine Waage ist nicht bedeutet 0,5
Kernel ist kubisch.

Aber was anderes bedeutet das? Ich denke, es ist wie das Erstellen einer Funktion, die später aufgerufen wird?

Antwort

10

imresize führt Anti-Aliasing durch, wenn ein Bild verkleinert wird, indem einfach der kubische Kern erweitert wird und nicht ein diskreter Vorverarbeitungsschritt.

Für ein kernel_width von 4 Pixeln (8 nach neu skaliert), wobei die contributions Funktion 10 Nachbarn für jedes Pixel verwendet, die kernel vs h (skalierte kernel) aussehen wie (nicht normalisierte, ignorieren x-Achse):

enter image description here

Dies ist einfacher als die erste Durchführung eines Tiefpassfilters oder einer Gaußschen Faltung in einem separaten Vorverarbeitungsschritt.

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.

Der relevante Teil ist Gleichung (15)::

Die kubische Kern wird am Boden des imresize.m definiert

enter image description here

Dies ist eine spezifische Version der allgemeinen Interpolationsgleichungen für a = -0.5 in den folgenden Gleichungen:

enter image description here

a ist in der Regel auf -0,5 oder -0,75 eingestellt. Beachten Sie, dass a = -0.5 dem Cubic Hermite spline entspricht, der kontinuierlich ist und ein kontinuierliches erstes Derivat aufweist. OpenCV seems to use -0.75.

Wenn Sie jedoch bearbeiten [OPENCV_SRC] \ Module \ imgproc \ src \ imgwarp.cpp und ändern Sie den Code:

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

zu:

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

und wieder aufzubauen OpenCV (Tipp: Deaktivieren CUDA und das GPU-Modul für kurze Kompilierzeit), dann erhalten Sie die gleichen Ergebnisse. Siehe die übereinstimmende Ausgabe in my other answer zu einer verwandten Frage vom OP.

+1

+1 - Cool! ... da kamen die Schlüssel her! – rayryeng

+2

@Gilad Ich erinnere mich, dass Sie MATLAB vs OpenCV kubische Interpolation untersucht haben und es scheint, dass der Unterschied a = -0,5 für MATLAB und [a = -0,75 für OpenCV] ist (https://github.com/Itseez/opencv/ Blob/Master/Module/imgproc/src/imgwarp.cpp # L155). – chappjc

+2

@chappjc: +1 gute finden. Ich schrieb tatsächlich eine Antwort darüber :) – Amro

9

Dies ist eine Art Nachgang zu Ihrer previousquestions über den Unterschied zwischen imresize in MATLAB und cv::resize in OpenCV eine bikubische Interpolation gegeben.

Ich war selbst daran interessiert herauszufinden, warum es einen Unterschied gibt.Das sind meine Ergebnisse (wie ich die Algorithmen verstanden habe, korrigiere mich bitte, wenn ich Fehler mache).


denkt, dass ein Bild als eine planar-Transformation von einem Eingangsbild der Größe der Größenänderung M-by-N zu einem Ausgabebild der Größe scaledM-by-scaledN.

Das Problem ist, dass die Punkte nicht unbedingt auf das diskrete Gitter passen, daher müssen wir, um Intensitäten von Pixeln im Ausgabebild zu erhalten, die Werte einiger benachbarter Proben interpolieren (normalerweise in umgekehrter Reihenfolge), Das heißt, für jedes Ausgabepixel finden wir den entsprechenden nicht-ganzzahligen Punkt im Eingabefeld und interpolieren ihn().

Hier unterscheiden sich die Interpolationsalgorithmen, indem die Größe der Nachbarschaft und die Gewichtungskoeffizienten für jeden Punkt in dieser Umgebung ausgewählt werden. Die Beziehung kann von erster oder höherer Ordnung sein (wobei die involvierte Variable die Entfernung von der invers abgebildeten nicht ganzzahligen Probe zu den diskreten Punkten auf dem Originalbildgitter ist). In der Regel weisen Sie engeren Punkten höhere Gewichte zu.

bei imresize in MATLAB Sehen, hier sind die Gewicht Funktionen für die linearen und kubischen Kern:

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 

(Diese im Grunde die Interpolation Gewicht einer Probe zurückkehrt basierend darauf, wie weit es von einem interpolierten Punkt ist.)

Dies ist, wie diese Funktionen wie folgt aussehen:

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

interpolation_kernels

Beachten Sie, dass der lineare Kern (stückweise lineare Funktionen auf [-1,0] und [0,1] Intervallen, und Nullen anderswo) an den 2-benachbarten Punkten arbeitet, während der kubische Kern (stückweise kubische Funktionen in den Intervallen [-2, -1], [-1,1] und [1,2], und Nullen anderswo) arbeiten an 4 benachbarten Punkten.

Hier ist eine Darstellung für die 1-dimensionalen Fall, das zeigt, wie der Wert x von den diskreten Punkten f(x_k) unter Verwendung eines kubischen kernel zu interpolieren: bei x,

1d_interpolation

Die Kernfunktion h(x) zentriert die Position des zu interpolierenden Punktes. Der interpolierte Wert f(x) ist die gewichtete Summe der diskreten benachbarten Punkte (2 links und 2 rechts), skaliert durch den Wert der Interpolationsfunktion an diesen diskreten Punkten.

sagen, wenn der Abstand zwischen x und dem nächstgelegenen Punkt d (0 <= d < 1) ist, an der Stelle der interpolierte Wert x wird:

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

wo die Reihenfolge der Punkte wird unten dargestellt (man beachte, dass x(k+1)-x(k) = 1):

Da nun die Punkte diskret sind und in gleichmäßigen Intervallen abgetastet werden und die Kernbreite normalerweise klein ist, kann die Interpolation formuliert werden prägnanter als Faltungsoperation:

interp_conv_equation

das Konzept zu 2 Dimensionen erstreckt einfach, indem zuerst entlang einer Dimension interpoliert, und dann über die andere Dimension der Ergebnisse des vorherigen Schrittes unter Verwendung von interpoliert wird. Hier

ist ein Beispiel für eine bilineare Interpolation, die in 2D 4 benachbarte Punkte betrachtet:

bilinear_interpolation

Die bikubische Interpolation in 2D verwendet 16 benachbarten Punkten:

bicubic

Zuerst wir interpoliere entlang der Zeilen (die roten Punkte) mit den 16 Rasterproben (pink). Dann interpolieren wir entlang der anderen Dimension (rote Linie) mit den interpolierten Punkten aus dem vorherigen Schritt. In jedem Schritt wird eine reguläre 1D-Interpolation durchgeführt. In diesem Fall sind die Gleichungen zu lang und zu kompliziert für mich, um mit der Hand zu arbeiten!


Wenn wir nun auf die cubic Funktion in MATLAB gehen zurück, sie paßt eigentlich die Definition des Faltungskernes in den reference paper als Gleichung (4) gezeigt. Hier ist die gleiche Sache aus Wikipedia genommen:

conv_kernel

Sie können sehen, dass in der obigen Definition, MATLAB einen Wert von a=-0.5 gewählt hat.

Jetzt ist der Unterschied zwischen der Implementierung in MATLAB und OpenCV OpenCV wählte einen Wert von 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]; 
} 

Dies nicht sofort offensichtlich sein könnte, aber der Code die Bedingungen der kubischen Faltungsfunktion nicht berechnen (rechts nach Gleichung (25) in dem Papier aufgeführt):

bicubic_kernel

Wir mit Hilfe der Symbolic Math Toolbox kann man erkennen, überprüfen:

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; 

Diese Ausdrücke wie folgt umgeschrieben werden:

>> 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 

die mit den Termen aus der obigen Gleichung übereinstimmen.

Offensichtlich läuft der Unterschied zwischen MATLAB und OpenCV darauf hinaus, einen anderen Wert für den freien Begriff a zu verwenden. Den Autoren der Arbeit zufolge ist ein Wert von 0.5 die bevorzugte Wahl, weil er bessere Eigenschaften für den Approximationsfehler als jede andere Wahl für a impliziert.

+0

Ausgezeichnete Antwort. Vielen Dank für die Überprüfung, ob die Gleichungen mit der Symbolic Math-Toolbox übereinstimmen. Es lohnt sich die Mühe! – chappjc

+1

@chappjc: Danke. Schade, dass der Wert des Parameters "a" in beiden Implementierungen ein fester Code ist. Wenn wir wollen, dass sie übereinstimmen, müssen Sie in MATLAB die eingebaute 'imresize' -Funktion ändern (was ich nie tun möchte), und in OpenCV müssen Sie das Ganze aus Quellen kompilieren, nur um einen zu spiegeln Zinkenwert! Es wäre interessant zu sehen, ob OP "a" in OpenCV zu "-0.5" ändert und überprüft, ob wir identische Ergebnisse zwischen den beiden Implementierungen erhalten. Als ich es das letzte Mal ausprobiert habe, dauert es 10 Minuten, um OpenCV zu kompilieren kratzen. – Amro

+0

@Amro OMG vielen Dank! Ich brauche 2 Tage, um deine gesamten Ergebnisse zu lesen! – Gilad

Verwandte Themen