Bicubi c скорость интерполяции; почему Numba медленнее, чем OpenCV? - PullRequest
0 голосов
/ 29 апреля 2020

Я реализовал интерполяцию bicubi c в стиле OpenCV в Python с нумбой для ускорения. Это прекрасно работает, но все же в 5-10 раз медленнее, чем соответствующий вызов OpenCV. Мои вопросы:

  • Почему нумба медленнее? Использует ли OpenCV разные методы? Или это так же хорошо, как и с numba?
  • Могу ли я предпринять какие-либо другие шаги, чтобы ускорить процесс?

Я повторно реализовал, потому что хочу для некоторых случаев добавьте нестандартную интерполяцию.

Я уже преобразовал код в двухпроходный алгоритм, который экономит около 25% времени. При передаче входного изображения как типа double все выглядит быстрее, вероятно, потому что требуется меньше преобразований типов. Я пытался играть с типами данных для разных переменных. Я использую полные назначения, такие как first_pass[row, col, 0] = вместо first_pass[row, col, :] =, что, кажется, дает небольшое преимущество.

Время на моей машине:

scale complete: 0.6035717999911867
opencv scale complete: 0.0807620002888143

Вот код:

from numba import uint8, double, jit
import cv2
import numpy as np
import timeit

@jit(
    uint8[:,:,:](double[:,:,:], uint8), 
    nopython=True
)
def scale_image_cubic_opencv_style_so(image, up_scale_factor):

    # Initialisations and helping numba with type inference
    scaled_shape = (image.shape[0]*up_scale_factor, image.shape[1]*up_scale_factor, image.shape[2])
    scaled_image = np.empty(scaled_shape, dtype=np.uint8)
    # need more than uint8 to prevent overflows
    first_pass = np.empty(scaled_shape, dtype=np.int32)
    bicubic_interpolation = np.asarray([0,0,0],dtype=np.float64)

    row_scale = float(image.shape[0]) / float(scaled_image.shape[0])
    col_scale = float(image.shape[1]) / float(scaled_image.shape[1])

    # First pass
    for col in range(scaled_image.shape[1] - up_scale_factor-1):

        xf = max((col+0.5) * col_scale - 0.5, 0.0)
        x1 = int(xf)
        x0 = max(x1-1, 0)
        x2 = min(x1+1, image.shape[1]-1)
        x3 = min(x1+2, image.shape[1]-1)

        # according to opencv, see method interpolateCubic in 
        # https://github.com/opencv/opencv/blob/master/modules/imgproc/src/resize.cpp
        A = -0.75
        x = xf-x1
        coeffs_x0 = ((A*(x + 1) - 5*A)*(x + 1) + 8*A)*(x + 1) - 4*A
        coeffs_x1 = ((A + 2)*x - (A + 3))*x*x + 1
        coeffs_x2 = ((A + 2)*(1 - x) - (A + 3))*(1 - x)*(1 - x) + 1
        coeffs_x3 = 1.0 - coeffs_x0 - coeffs_x1 - coeffs_x2


        for row in range(scaled_image.shape[0] - up_scale_factor-1):

            yf = max((row+0.5) * row_scale - 0.5, 0.0)
            y1 = int(yf)

            # Calculate cubic interpolation for the y1 row
            # The calculations and even more likely the assignement
            # below take a lot of the time in this mehtod
            first_pass[row, col, 0] = coeffs_x0*image[y1, x0, 0] + coeffs_x1*image[y1, x1, 0] + coeffs_x2*image[y1, x2, 0] + coeffs_x3*image[y1, x3, 0]
            first_pass[row, col, 1] = coeffs_x0*image[y1, x0, 1] + coeffs_x1*image[y1, x1, 1] + coeffs_x2*image[y1, x2, 1] + coeffs_x3*image[y1, x3, 1]
            first_pass[row, col, 2] = coeffs_x0*image[y1, x0, 2] + coeffs_x1*image[y1, x1, 2] + coeffs_x2*image[y1, x2, 2] + coeffs_x3*image[y1, x3, 2]

    # Second pass
    for row in range(scaled_image.shape[0] - up_scale_factor-1):

        yf = max((row+0.5) * row_scale - 0.5, 0.0)
        y1 = int(yf)
        y0 = max(y1-1, 0)
        y2 = min(y1+1, image.shape[0]-1)
        y3 = min(y1+2, image.shape[0]-1)

        # according to opencv, see method interpolateCubic in 
        # https://github.com/opencv/opencv/blob/master/modules/imgproc/src/resize.cpp
        A = -0.75
        y = yf-y1
        coeffs_y0 = ((A*(y + 1) - 5*A)*(y + 1) + 8*A)*(y + 1) - 4*A
        coeffs_y1 = ((A + 2)*y - (A + 3))*y*y + 1
        coeffs_y2 = ((A + 2)*(1 - y) - (A + 3))*(1 - y)*(1 - y) + 1
        coeffs_y3 = 1.0 - coeffs_y0 - coeffs_y1 - coeffs_y2


        for col in range(scaled_image.shape[1] - up_scale_factor-1):

            # Calculate bicubic interpolation

            # The calculations and even more likely the assignement
            # below take a lot of the time in this mehtod
            # rint = round to nearest integer, needed so we get the same values as opencv
            bicubic_interpolation[0] = np.rint(coeffs_y0*first_pass[y0*up_scale_factor, col, 0] + coeffs_y1*first_pass[y1*up_scale_factor, col, 0] + coeffs_y2*first_pass[y2*up_scale_factor, col, 0] + coeffs_y3*first_pass[y3*up_scale_factor, col, 0])
            bicubic_interpolation[1] = np.rint(coeffs_y0*first_pass[y0*up_scale_factor, col, 1] + coeffs_y1*first_pass[y1*up_scale_factor, col, 1] + coeffs_y2*first_pass[y2*up_scale_factor, col, 1] + coeffs_y3*first_pass[y3*up_scale_factor, col, 1])
            bicubic_interpolation[2] = np.rint(coeffs_y0*first_pass[y0*up_scale_factor, col, 2] + coeffs_y1*first_pass[y1*up_scale_factor, col, 2] + coeffs_y2*first_pass[y2*up_scale_factor, col, 2] + coeffs_y3*first_pass[y3*up_scale_factor, col, 2])

            # bicubic values can be negative, bring them back into the allowed range
            # np.clip not supported by numba so using min/max which adds 0.1-0.15s
            scaled_image[row, col, 0] = max(min(bicubic_interpolation[0],255),0)
            scaled_image[row, col, 1] = max(min(bicubic_interpolation[1],255),0)
            scaled_image[row, col, 2] = max(min(bicubic_interpolation[2],255),0)

    return scaled_image


up_scale_factor = 16
image = np.random.rand(384,256,3) * 255

start = timeit.default_timer()
scaled_image = scale_image_cubic_opencv_style_so(
    image.astype(np.float64), 
    up_scale_factor
)
print('scale complete:', timeit.default_timer()-start)
# cv2.imwrite('./scale.png', scaled_image)

# pure opencv for comparision timings
start = timeit.default_timer()
scaled_image = cv2.resize(
    image, 
    (image.shape[1]*up_scale_factor, image.shape[0]*up_scale_factor), 
    interpolation=cv2.INTER_CUBIC
)

print('opencv scale complete:', timeit.default_timer()-start)
# cv2.imwrite('./scale-opencv.png', scaled_image)
...