2014-11-08 3 views
5

Я пытаюсь понять функцию:imresize - пытаясь понять бикубической

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 

Я не очень понимаю, что это означает, что эта линия

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

моя шкала является 0,5
Ядро кубическое.

Но кроме этого, что это значит? Я думаю, что это похоже на создание функции, которая будет называться позже?

ответ

10

imresize выполняет сглаживание при уменьшении изображения путем простого расширения кубического ядра, а не дискретного этапа предварительной обработки.

В течение kernel_width из 4 пикселей (8 после того, как повторно масштабируется), где функция contributions использует 10 соседей для каждого пикселя, то kernel против h (масштабируются ядра) выглядит как (ненормированная, игнорируемая ось х):

enter image description here

Это проще, чем сначала выполнить фильтр нижних частот или гауссову свертку на отдельном этапе предварительной обработки.

Кубический ядро ​​определено в нижней части imresize.m как:

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.

Соответствующая часть является уравнением (15):

enter image description here

Это специфический версия общих интерполяционных уравнений для a = -0.5 в следующих уравнениях:

enter image description here

a обычно устанавливается на -0.5 или -0.75. Обратите внимание, что a = -0.5 соответствует Cubic Hermite spline, который будет непрерывным и имеет непрерывный первый производный. OpenCV seems to use -0.75.

Однако, если вы редактируете [OPENCV_SRC] \ Modules \ imgproc \ SRC \ imgwarp.cpp и изменить код:

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

к:

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

и восстановить OpenCV (подсказка: отключить CUDA и модуль gpu для короткого времени компиляции), то вы получите те же результаты. См. Соответствующий вывод в my other answer связанному с ним вопросу.

+1

+1 - Круто! ... так вот откуда взялись эти ключи! – rayryeng

+2

@ Gilad Я помню, что вы изучали кубическую интерполяцию MATLAB и OpenCV, и кажется, что разница составляет = -0,5 для MATLAB и [a = -0,75 для OpenCV] (https://github.com/Itseez/opencv/ блоб/ведущий/модули/imgproc/SRC/imgwarp.cpp # L155). – chappjc

+2

@chappjc: +1 хорошая находка. Я на самом деле писал ответ об этом :) – Amro

9

Это своего рода наблюдения вашему previousquestions о разнице между imresize в MATLAB и cv::resize в OpenCV дан бикубической.

Мне было интересно узнать, почему есть разница.Это мои выводы (поскольку я понял алгоритмы, пожалуйста, исправьте меня, если я ошибаюсь).


Подумайте изменения размера изображения в виде плоское преобразования из входного изображения размера M-by-N с выходным изображением размера scaledM-by-scaledN.

Проблема в том, что точки не обязательно вписываются в дискретную сетку, поэтому для получения интенсивностей пикселей на выходном изображении нам необходимо интерполировать значения некоторых из соседних выборок (обычно выполняемых в обратном порядке, то есть для каждого выходного пикселя, мы находим соответствующую нецелую точку во входном пространстве и интерполируем вокруг нее).

Здесь интерполяционные алгоритмы различаются, выбирая размер окрестности и весовые коэффициенты, дающие каждой точке в этой окрестности. Связь может быть первым или более высоким порядком (где задействуемая переменная - это расстояние от обратного отображенного нецелого образца до дискретных точек в исходной сетке изображения). Обычно вы назначаете более высокие веса для более близких точек.

Глядя на imresize в MATLAB, вот функции весов для линейных и кубических ядер:

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 

(Это в основном возвращают вес интерполяции образца, основанный на том, как далеко он находится от точки интерполяции.)

Это, как эти функции выглядит следующим образом:

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

interpolation_kernels

Обратите внимание, что линейное ядро ​​(кусочно-линейные функции на отрезках [-1,0] и [0,1] и нули в другом месте) работает на 2-соседних точках, а кубическое ядро ​​(кусочно кубические функции на отрезках [-2, -1], [-1,1] и [1,2] и нули в другом месте) работают на 4-соседних точках.

Вот иллюстрация для 1-мерного случая, показывающего, как интерполировать значение x из дискретных точек f(x_k) используя кубическое ядро:

1d_interpolation

Ядра функции h(x) центрирована в x, расположение точки, подлежащей интерполяции. Интерполированное значение f(x) представляет собой взвешенную сумму дискретных соседних точек (2 слева и 2 справа), масштабируемую по значению интерполяционной функции в этих дискретных точках.

Скажем, если расстояние между x и ближайшей точкой является d (0 <= d < 1), интерполированное значение на месте x будет:

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

, где порядок точек изображен ниже (обратите внимание, что x(k+1)-x(k) = 1):

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

Теперь, поскольку точки являются дискретными и образцы через равные промежутки времени, а ширина ядра обычно мала, интерполяция может быть сформулирована сжато как свертки операция:

interp_conv_equation

Концепция продолжается до 2-й измерений просто сначала интерполирования по одному измерению, а затем интерполирование через другое измерение с использованием результатов предыдущего шага.

Ниже приведен пример билинейной интерполяции, который в 2D рассматривает 4 соседних точек:

bilinear_interpolation

бикубического интерполяция в 2D использует 16 соседних точек:

bicubic

Сначала мы интерполировать вдоль строк (красные точки) с использованием 16 выборок сетки (розовый). Затем мы интерполируем по другому размеру (красную линию), используя интерполированные точки с предыдущего шага. На каждом шаге выполняется обычная интерполяция 1D. В этом уравнение слишком длинное и сложное для меня, чтобы разобраться вручную!


Теперь, если мы вернемся к cubic функции в MATLAB, он фактически совпадает с определением ядра свертки, показанного в reference paper в уравнении (4). Вот то же самое, взяты из Wikipedia:

conv_kernel

Вы можете видеть, что в приведенном выше определении, MATLAB выбрал значение a=-0.5.

Теперь разница между реализацией в MATLAB и OpenCV заключается в том, что OpenCV выбрал значение 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]; 
} 

Это может быть не очевидно сразу, но код делает вычисления условия кубической функции свертки (перечислены сразу после уравнения (25) в статье):

bicubic_kernel

Мы можно проверить, что с помощью символического 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; 

Эти выражения можно переписать в виде:

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

которые соответствуют условиям из приведенного выше уравнения.

Очевидно, что разница между MATLAB и OpenCV сводится к использованию другого значения для свободного термина a. Согласно авторам статьи, предпочтительным является значение 0.5, потому что оно подразумевает лучшие свойства ошибки аппроксимации, чем любой другой выбор для a.

+0

Отличный ответ. Спасибо за проверку соответствия уравнений с помощью инструмента Symbolic Math. Хорошо стоит усилий! – chappjc

+1

@chappjc: Спасибо. Слишком плохо значение параметра 'a' является жестким кодом в обеих реализациях. Если мы хотим, чтобы они совпадали, в MATLAB вам придется изменить встроенную функцию «imresize» (которую я никогда не делаю), а в OpenCV вам придется скомпилировать все это из источников только для того, чтобы перевернуть зубная ценность! Было бы интересно посмотреть, изменит ли OP «a» в OpenCV на '-0.5' и проверяет, что мы получаем одинаковые результаты между двумя реализациями. В прошлый раз, когда я это пробовал, я помню, что он собирал 10 минут для компиляции OpenCV из царапина. – Amro

+0

@Amro OMG большое вам спасибо! Мне нужно 2 дня, чтобы прочитать все ваши выводы! – Gilad

Смежные вопросы