Оптимизация производительности сшивания изображений Python OpenCV в реальном времени (n = 5)

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

Я следую подходу из этого репозитория github:

Начиная с центрального изображения, вы сначала сшиваете слева, а затем сшиваете остальные изображения справа.

Код из этого репозитория работает, но очень медленно. Мне удалось значительно улучшить его производительность (коэффициент 300), но по-прежнему требуется 0,25 секунды, чтобы сшить панораму из пяти изображений (на Macbook Pro 2015 года).

Медленная часть: применение каждого результата cv2.warpPerspective(...) к изображениям, сшитым до этого момента. В настоящее время я делаю это, используя альфа-канал и смешивая два изображения, вдохновленные это ТАК ответ. Именно это смешивание замедляет сшивку.

(Псевдо) код:

def blend_transparent(background, foreground):
    overlay_img = foreground[:, :, :3]  # Grab the BRG planes 
    overlay_mask = foreground[:, :, 3:]  # And the alpha plane

    # Again calculate the inverse mask
    background_mask = 255 - overlay_mask

    # Turn the masks into three channel, so we can use them as weights
    overlay_mask = cv2.cvtColor(overlay_mask, cv2.COLOR_GRAY2BGR)
    background_mask = cv2.cvtColor(background_mask, cv2.COLOR_GRAY2BGR)

    # Create a masked out background image, and masked out overlay
    # We convert the images to floating point in range 0.0 - 1.0
    background_part = (background * (1 / 255.0)) * (background_mask * (1 / 255.0))
    overlay_part = (overlay_img * (1 / 255.0)) * (overlay_mask * (1 / 255.0))

    # And finally just add them together, and rescale it back to an 8bit integer image
    return np.uint8(
        cv2.addWeighted(background_part, 255.0, overlay_part, 255.0, 0.0)
    )


for image in right_images:
    warped_image = cv2.warpPerspective(image, ...)
    mask = np.zeros(
        (warped_image.shape[0], warped_image.shape[1], 4), 
        dtype="uint8"
    )
    mask[0 : previously_stitched.shape[0], 0 : previously_stitched.shape[1]] = previously_stitched
    mask_rgb = mask[:, :, :3]  # Grab the BRG planes
    previously_stitched = blend_transparent(mask_rgb, warped_image)

Итак, мой вопрос: есть ли способ более эффективно применить деформированное изображение к существующей панораме?

Мой полный рабочий код находится в в этом репозитории.

Отказ от ответственности: я веб-разработчик, и мои знания о компьютерном зрении очень ограничены.


person creimers    schedule 10.01.2019    source источник
comment
Для меня непонятно, какая часть занимает время. Это вызов cv2.warpPerspective(...) или функция blend_transparent? В вашем проекте вы используете запатентованный алгоритм, который по умолчанию не включен в пакеты Python. Это не помогает проверить вашу проблему.   -  person T.Lucas    schedule 10.01.2019
comment
Этап смешивания занимает много времени. Я обновил вопрос. Что касается патентов: если вы устанавливаете python-opencv в версии, закрепленной в requirements.txt, рассматриваемый алгоритм будет включен.   -  person creimers    schedule 10.01.2019


Ответы (1)


Альфа-канал полезен, когда ваше изображение имеет прозрачность, но здесь вы вручную добавляете альфа-канал путем преобразования. Этот канал можно использовать для хранения вычислений, но я думаю, что вы потеряете производительность. Я предлагаю следующую функцию для blend_transparent:

def blend_transparent(self, background, foreground):
    # Split out the transparency mask from the colour info
    overlay_img = foreground[:, :, :3]  # Grab the BRG planes

    res = background

    only_right = np.nonzero((np.sum(overlay_img, 2) != 0) * (np.sum(background,2) == 0))
    left_and_right = np.nonzero((np.sum(overlay_img, 2) != 0) * (np.sum(background,2) != 0))

    res[only_right] = overlay_img[only_right]
    res[left_and_right] = res[left_and_right]*0.5 + overlay_img[left_and_right]*0.5
    return res

Здесь вы устанавливаете в результате значение правых пикселей изображения, если в настоящее время значение не установлено. Если значение уже установлено, вы вычисляете среднее значение слева и справа. Время вычисления делится на коэффициент 1,6.

Поскольку ваша проекция заморожена, не нужно каждый раз вычислять индексы only_right и left_and_right, мы можем вычислить их один раз и сохранить. Сделайте это, и вы должны разделить время вычисления на коэффициент 4.

person T.Lucas    schedule 10.01.2019
comment
Это умно! Кэширование индексов сократило время вычислений до 0,05 секунды. - person creimers; 10.01.2019