Ошибка интерполяции переназначения OpenCV?

Я использую функцию переназначения opencv, чтобы отобразить изображение в другую систему координат. Тем не менее, мои первоначальные тесты показывают, что есть некоторые проблемы с интерполяцией. Здесь я приведу простой пример постоянного сдвига в 0,1 пикселя для изображения, которое равно 0 везде, но в положении [50,50].

import cv2
import numpy as np

prvs = np.zeros((100,80), dtype=np.float32)
prvs[50:51, 50:51] = 1.

grid_x, grid_y = np.meshgrid(np.arange(prvs.shape[1]), np.arange(prvs.shape[0]))
grid_y = grid_y.astype(np.float32)
grid_x = grid_x.astype(np.float32) + 0.1

prvs_remapped = cv2.remap(prvs, grid_x, grid_y, interpolation=cv2.INTER_LINEAR)

print(prvs_remapped[50,50])
print(prvs_remapped[50,49])

дает

0.90625
0.09375

Тем не менее, я бы ожидал 0,9 и 0,1 вместо этого, учитывая метод линейной интерполяции. Я делаю что-то не так или это какая-то цифровая проблема? Существуют ли более точные алгоритмы переназначения?

Благодарю.

1 ответ

Решение

Хорошо поймал. На мой взгляд, ваши ожидания верны, о чем свидетельствует np.interp дающий 0.1 а также 0.9 ценности.

Давайте нарисуем пирамиду (интерполирующую в диапазон 49:51 квадратных пикселей):

import numpy as np
import cv2
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

prvs = np.zeros((100,80), dtype=np.float32)
prvs[50:51, 50:51] = 1

lin = np.linspace(49,51,200)
grid_x,grid_y = np.meshgrid(lin,lin)
grid_x = grid_x.astype(np.float32)
grid_y = grid_y.astype(np.float32)
prvs_zoommapped = cv2.remap(prvs, grid_x, grid_y, interpolation=cv2.INTER_LINEAR)

fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.plot_surface(grid_x,grid_y,prvs_zoommapped,cmap='viridis')
plt.show()

результат: пирамида с неровными краями

Заметили что-нибудь? При построении сетки 200x200 на пирамиде видны очень заметные ступени. Давайте посмотрим на сечение нашего результата:

fig,ax = plt.subplots()
ax.plot(prvs_zoommapped[100,:],'x-')
ax.grid('on')
plt.show()

результат: четко кусочно-постоянная функция

Как видите, результатом является кусочно-постоянная функция, то есть огромная ошибка дискретизации в выходных данных. Чтобы быть точным, мы видим шаги 0.03125 == 1/32 в результате.

Я подозреваю, что cv2.remap не предназначен для использования для субпиксельных манипуляций, но для более масштабного отображения из одной сетки в другую. Другой вариант заключается в том, что внутренняя точность была принесена в жертву улучшениям производительности. В любом случае, вы не сходите с ума: вы должны видеть 0.1 а также 0.9 в результате точной (би) линейной интерполяции.

Если вы не привязаны к openCV из-за других задач, это отображение, т.е. 2d-интерполяция, может выполняться с различными битами scipy.interpolateа именно его части сделаны для 2d интерполяции. Для вашего особого случая линейной интерполяции на регулярной сетке, scipy.interpolate.RegularGridInterpolator или что-то подобное может быть уместным.

Или даже лучше (но я еще не использовал этот подмодуль): scipy.ndimage.map_coordinates кажется, что именно то, что вы ищете:

from scipy import ndimage
ndimage.map_coordinates(prvs, [[50.1, 49.1], [50, 50]], order=1)
# output: array([ 0.89999998,  0.1       ], dtype=float32)

Применительно к примеру пирамиды:

import numpy as np
import cv2
from scipy import ndimage
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

prvs = np.zeros((100,80), dtype=np.float32)
prvs[50:51, 50:51] = 1

lin = np.linspace(49,51,200)
grid_x,grid_y = np.meshgrid(lin,lin)
prvs_zoommapped = ndimage.map_coordinates(prvs, [grid_x, grid_y], order=1)

fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.plot_surface(grid_x,grid_y,prvs_zoommapped,cmap='viridis')
plt.show()

довольно гладкая пирамида

Намного лучше.

Другие вопросы по тегам