Поиграв с различными тестовыми примерами, я думаю, что знаю ответ на вопрос, как OpenCV выбирает местоположения точек выборки. Как отметил @ChrisLuengo в комментарии, OpenCV, похоже, не применяет фильтр нижних частот перед понижающей дискретизацией, а использует только (би) линейную интерполяцию.
(возможно) Решение:
Давайте предположим, что у нас есть изображение 5x5, позиции пикселей которого представлены синими кружками на рисунке ниже. Теперь мы хотим уменьшить его до изображения 3x3 или 4x4, и нам нужно найти позиции образцов нового изображения с пониженной дискретизацией в исходной сетке изображений.
Похоже, что OpenCV использует пиксельное расстояние 1 для исходной сетки изображений и пиксельное расстояние (OLD_SIZE / NEW_SIZE), то есть здесь 5/3 и 5/4, для новой сетки изображений. Кроме того, он выравнивает обе сетки в центральной точке. Таким образом, алгоритмы детерминированной выборки OpenCV можно визуализировать следующим образом:
Визуализация от 5x5 до 3x3 :
Визуализация от 5x5 до 4x4 :
Пример кода (Python 2.7):
import numpy as np
import cv2
# 1. H_W is the height & width of the original image, using uniform H/W for this example
# resized_H_W is the height & width of the resized image, using uniform H/W for this example
H_W = 5
resized_H_W = 4
# 2. Create original image & Get OpenCV resized image:
img = np.zeros((H_W, H_W)).astype(np.float32)
counter = 1
for i in range(0, H_W):
for j in range(0, H_W):
img[i, j] = counter
counter += 1
img_resized_opencv = cv2.resize(img, (resized_H_W, resized_H_W), 0, 0, cv2.INTER_LINEAR).astype(np.float32)
# 3. Get own resized image:
img_resized_own = np.zeros((resized_H_W, resized_H_W)).astype(np.float32)
for i in range(0, resized_H_W):
for j in range(0, resized_H_W):
sample_x = (1.0 * H_W) / 2.0 - 0.50 + (i - (1.0 * resized_H_W - 1.0) / 2.0) * (1.0 * H_W) / (1.0 * resized_H_W)
sample_y = (1.0 * H_W) / 2.0 - 0.50 + (j - (1.0 * resized_H_W - 1.0) / 2.0) * (1.0 * H_W) / (1.0 * resized_H_W)
pixel_top_left = img[int(np.floor(sample_x)), int(np.floor(sample_y))]
pixel_top_right = img[int(np.floor(sample_x)), int(np.ceil(sample_y))]
pixel_bot_left = img[int(np.ceil(sample_x)), int(np.floor(sample_y))]
pixel_bot_right = img[int(np.ceil(sample_x)), int(np.ceil(sample_y))]
img_resized_own[i, j] = (1.0 - (sample_x - np.floor(sample_x))) * (1.0 - (sample_y - np.floor(sample_y))) * pixel_top_left + \
(1.0 - (sample_x - np.floor(sample_x))) * (sample_y - np.floor(sample_y)) * pixel_top_right + \
(sample_x - np.floor(sample_x)) * (1.0 - (sample_y - np.floor(sample_y))) * pixel_bot_left + \
(sample_x - np.floor(sample_x)) * (sample_y - np.floor(sample_y)) * pixel_bot_right
# 4. Print results:
print "\n"
print "Org. image: \n", img
print "\n"
print "Resized image (OpenCV): \n", img_resized_opencv
print "\n"
print "Resized image (own): \n", img_resized_own
print "\n"
print "MSE between OpenCV <-> Own: ", np.mean(np.square(img_resized_opencv - img_resized_own))
print "\n"
Отказ от ответственности:
Это только моя теория, которую я проверял с помощью ~ 10 контрольных примеров. Я не утверждаю, что это на 100% верно.