Вы проснулись сегодня утром с желанием превратить ваши видео на YouTube в анимированные классические картины? Или во что-то более дикое? Если да, то вы попали в нужное место. В этом кратком руководстве я продемонстрирую, как преобразовать видео в анимацию по вашему выбору.

Например, вот современное искусство:

Используя это в качестве желаемого базового стиля, давайте преобразим водопад в парке. Первая половина этого видео - оригинал, а вторая половина - стилизованная версия.

Вот еще один пример, на этот раз более городской сцены. Я стилизовал это видео, используя картину Моне, а затем принт Эшера.

Общая техника, лежащая в основе этих визуализаций, - это трансферное обучение. Трансферное обучение позволяет нам использовать предварительно обученные нейронные сети для решения совершенно разных задач. Здесь мы перепрофилировали сеть vgg19. Эта сеть обучена классифицировать миллионы изображений по одной из 1000 категорий. Нас не интересуют эти классификации, но нас интересует то, что сеть узнала на своем пути в своих скрытых слоях. Эти скрытые слои кодируют знания об изображении (восприятие краев, цветов, стилей и т. Д.) На возрастающих уровнях абстракции. Задача алгоритма стилизации - повторно использовать эту информацию при создании новых изображений.

Прочтите это для презентации идеи.

Реализация в этой статье требует изрядной работы. Однако наши друзья из Google упростили для нас вещи и создали модуль Tensorflow Hub, который инкапсулирует эту логику. Вы найдете этот модуль здесь. Учитывая этот модуль стилей, все, что нам нужно сделать, это загрузить наше исходное изображение и изображение целевого стиля, а затем запустить их через хаб. В результате получается изображение, преобразованное заданным стилем.

В нашем случае нас интересуют видео, а не изображения. Поэтому нам нужно сначала превратить наши видео в последовательности изображений, стилизовать эти изображения с помощью модели стилей концентратора, а затем перестроить видео из полученных стилизованных кадров.

Давайте посмотрим на код.

Основная функция ниже. Процесс выглядит следующим образом: мы берем в качестве входных данных исходное видео и изображение, которое хотим использовать в качестве стиля (например, jpeg Моне или Эшера). Затем мы извлекаем звуковую дорожку из введенного нами видео и затем распаковываем видео в последовательность изображений. По мере продвижения мы также запускаем модуль стиля для каждого изображения. Результатом является каталог стилизованных изображений. Наконец, мы рекомбинируем эти изображения обратно в видеофайл и повторно подключаем звуковую дорожку.

if len(sys.argv) != 3:
    print('usage: video.py video style')
    exit()
name_original = sys.argv[1]
name_style = sys.argv[2]
# load and cache the styling dnn
hub_module = hub.load(HUB_URL)
# extract audio from the video
extract_mp3(PATH_VIDEOS + name_original)
# extract all frames from the video, style them
# and put results into tmp
generate_frames(PATH_VIDEOS + name_original, 
    PATH_STYLES + name_style)
# regenerate the video from the styled frames
output_name = os.path.splitext(name_original)[0] 
    + '.' + os.path.splitext(name_style)[0] + '.mp4'
generate_video(PATH_OUTPUTS + output_name)
# recombine the extracted audio into the newly-styled video
input_name = output_name
output_name = os.path.splitext(name_original)[0] 
    + '.' + os.path.splitext(name_style)[0] + '.audio.mp4'
add_mp3(PATH_OUTPUTS + input_name, PATH_OUTPUTS + output_name)

Теперь давайте посмотрим на основные функции.

Во-первых, вот метод извлечения mp3. Для этой работы мы используем ffmpeg напрямую через подпроцесс, так как нет хорошей привязки к Python. В результате получается mp3, который мы сохраняем на потом.

def extract_mp3(path_video):
    print('Extracting audio: ', path_video, PATH_TMP_MP3)
    command = 'ffmpeg -i {0} -f mp3 -ab 192000 
        -vn {1}'.format(path_video, PATH_TMP_MP3)
    subprocess.call(command, shell=True)

Я использую пакет OpenCV для извлечения всех изображений из видео. Мы снимаем видео и затем распаковываем каждое изображение в рабочий каталог. При этом мы применяем концентратор стиля к каждому изображению. Результатом является каталог, содержащий исходные кадры изображения для видео вместе со стилизованными кадрами.

def generate_frames(path_input_video, path_image_style):
    video_capture = cv2.VideoCapture(path_input_video)
    image_style = load_image(path_image_style);
    for count in range(MAX_FRAMES):
        success, image = video_capture.read()
        if success == False: break
        path_frame = PATH_TMP + (str(count).zfill(5)) + '.jpg'
        path_converted_frame = PATH_TMP + 
            'x' + (str(count).zfill(5)) + '.jpg'
        cv2.imwrite(path_frame, image)
        image = load_image(path_frame)
        results = hub_module(tf.constant(image), 
                tf.constant(image_style))
        image = tf.squeeze(results[0], axis=0)
        mpl.image.imsave(path_converted_frame, image)
        print(count, path_frame, path_converted_frame)

Теперь у нас есть все стилизованные изображения для видео. Следующий шаг - перебрать эти стилизованные изображения и преобразовать их обратно в видео. Это делается следующим образом:

def generate_video(path_output_video):
    image_list = []
    count = 0
    path_converted_frame = PATH_TMP + 'x' + 
        (str(count).zfill(5)) + '.jpg'
    image = cv2.imread(path_converted_frame)
    height, width, layers = image.shape
    size = (width,height)
    print('size: ', size)
    converted_files = [file_name for file_name in 
        os.listdir(PATH_TMP) if 'x' in file_name]
    converted_files.sort()
    for file_name in converted_files:
        path_converted_frame = PATH_TMP + file_name
        image = cv2.imread(path_converted_frame)
        print(path_converted_frame)
        image_list.append(image)
    video_writer = cv2.VideoWriter(path_output_video, 
        cv2.VideoWriter_fourcc(*'mp4v'), VIDEO_FPS, size)
    for i in range(len(image_list)):
        video_writer.write(image_list[i])
    video_writer.release()
    print('video generated: ', path_output_video)

Теперь у нас есть стилизованное видео. Последний шаг - повторно прикрепить звуковую дорожку. Мы снова применяем ffmpeg для этой задачи:

def add_mp3(path_input_video, path_output_video):
    print('Adding audio: ', PATH_TMP_MP3, 
        path_input_video, 
        path_output_video)
    command = 'ffmpeg -i {0} -i {1} -c:v copy -c:a 
        aac -strict experimental {2} '.
        format(path_input_video, 
            PATH_TMP_MP3, 
            path_output_video)
    subprocess.call(command, shell=True)

Вот и все! Вы можете увидеть полный листинг кода здесь. А если вы хотите взглянуть на другие проекты AI, загляните на мой сайт.

По сравнению с большинством проектов машинного обучения этот код относительно быстр. Вы можете делать полезные вещи даже на медленных машинах. Например, я создал эти видео на более дешевом экземпляре t2.large ec2. Ни GPU, ни TPU, ничего. В этой среде 15-секундное видео обычно отображается менее чем за час. Впечатляет, учитывая объем обработки. Более быстрые машины, конечно, пройдут мимо этого эталона.

Я обнаружил, что одни стили работают лучше, чем другие. Цвета, стиль обводки и штриховка передаются достаточно хорошо, в то время как крупномасштабные структуры минимизируются или теряются. Поэтому что-то вроде картины Моне переводится довольно хорошо. Напротив, Пикассо или Эшер потеряют характер из-за своей необычной геометрии. Это может иметь для вас значение, а может и не иметь. Кроме того, важно использовать изображения стиля с разумным разрешением. Для моих тестовых случаев я обнаружил, что все, что имеет размер около 512x512, работает достаточно хорошо. Ваш пробег может отличаться.

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