Это своего рода наблюдения вашему 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
Обратите внимание, что линейное ядро (кусочно-линейные функции на отрезках [-1,0] и [0,1] и нули в другом месте) работает на 2-соседних точках, а кубическое ядро (кусочно кубические функции на отрезках [-2, -1], [-1,1] и [1,2] и нули в другом месте) работают на 4-соседних точках.
Вот иллюстрация для 1-мерного случая, показывающего, как интерполировать значение x
из дискретных точек f(x_k)
используя кубическое ядро:
Ядра функции 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
Теперь, поскольку точки являются дискретными и образцы через равные промежутки времени, а ширина ядра обычно мала, интерполяция может быть сформулирована сжато как свертки операция:
Концепция продолжается до 2-й измерений просто сначала интерполирования по одному измерению, а затем интерполирование через другое измерение с использованием результатов предыдущего шага.
Ниже приведен пример билинейной интерполяции, который в 2D рассматривает 4 соседних точек:
бикубического интерполяция в 2D использует 16 соседних точек:
Сначала мы интерполировать вдоль строк (красные точки) с использованием 16 выборок сетки (розовый). Затем мы интерполируем по другому размеру (красную линию), используя интерполированные точки с предыдущего шага. На каждом шаге выполняется обычная интерполяция 1D. В этом уравнение слишком длинное и сложное для меня, чтобы разобраться вручную!
Теперь, если мы вернемся к cubic
функции в MATLAB, он фактически совпадает с определением ядра свертки, показанного в reference paper в уравнении (4). Вот то же самое, взяты из Wikipedia:
Вы можете видеть, что в приведенном выше определении, 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) в статье):
Мы можно проверить, что с помощью символического 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
.
+1 - Круто! ... так вот откуда взялись эти ключи! – rayryeng
@ Gilad Я помню, что вы изучали кубическую интерполяцию MATLAB и OpenCV, и кажется, что разница составляет = -0,5 для MATLAB и [a = -0,75 для OpenCV] (https://github.com/Itseez/opencv/ блоб/ведущий/модули/imgproc/SRC/imgwarp.cpp # L155). – chappjc
@chappjc: +1 хорошая находка. Я на самом деле писал ответ об этом :) – Amro