Создаем YouTube видео из кода


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

Если вы сможете добраться до этой точки, в этой статье я проведу вас по оставшемуся пути. Шаг за шагом вы научитесь создавать видеофайлы в формате MP4. В итоге вы получите файл, подходящий для загрузки на YouTube, например, как этот:

План

Вот перечень шагов для создания такого видео: 

  • Написать код на C++, производящий изображение в массиве в памяти. Я использую простой генератор изображений множества Мандельброта, но, повторюсь, это может быть что угодно. 
  • Использовать LodePNG с открытым кодом для сохранения изображения на диск в формате PNG. 
  • Код повторяет первые два шага сотни раз, создавая по одному кадру за раз. Каждый PNG-файл содержит один неподвижный кадр видеоклипа. 
  • На Windows или Linux конвертируем серию PNG-изображений в MP4-видео, используя ffmpeg.

Изображения множества Мандельброта

Множество Мандельброта — один из самых знаменитых фрактальных объектов. Видео выше постепенно увеличивает приближение множества Мандельброта с 1 до 100 миллионов раз. Видео занимает 30 секунд, скорость воспроизведения 30 кадров в секунду, в общей сложности 900 быстро воспроизводимых изображений. Замечательно, что вся эта красота возникает при повторении маленькой формулы: 

Функция Mandelbrot в исходном файле mandelzoom.cpp повторяет эту формулу до тех пор, пока либо комплексное значение z не выйдет за пределы круга радиусом в 2 раза больше исходного, либо n (счетчик повторений) не достигнет максимального предела. Итоговое значение n определяет цвет определенного пикселя на экране. 

static int Mandelbrot(double cr, double ci, int limit) { int count = 0; double zr = 0.0; double zi = 0.0; double zr2 = 0.0; double zi2 = 0.0; while ((count < limit) && (zr2 + zi2 < 4.001)) { double tzi = 2.0*zr*zi + ci; zr = zr2 - zi2 + cr; zi = tzi; zr2 = zr*zr; zi2 = zi*zi; ++count; } return count; }

Взгляните на функцию Palette, чтобы увидеть, как счетчик повторений конвертируется в значения красного, зеленого и синего цветов. 

Создание изображения в памяти 

Функция GenerateZoomFrames производит серию PNG-файлов. Каждый PNG-файл содержит изображение множества Мандельброта с другим увеличением. Разрешение изображений —  1280 на 720, стандартное для HD-видео.

static int GenerateZoomFrames(const char *outdir, int numframes, double xcenter, double ycenter, double zoom) { try { // Создаем буфер видеокадров с разрешением 720p (1280x720). const int width = 1280; const int height = 720; VideoFrame frame(width, height); const int limit = 16000; double multiplier = pow(zoom, 1.0 / (numframes - 1.0)); double denom = 1.0; for (int f = 0; f < numframes; ++f) { // Рассчитываем действительный и мнимый диапазон // значений для каждого кадра. // Приближение экспоненциально. // В первом кадре масштаб таков, что меньший габарит // (высота) занимает 4 единицы от нижней части кадра // до верхней. // В последнем кадре масштаб равен количеству единиц, // разделенному на 'приближение'. double ver_span = 4.0 / denom; double hor_span = ver_span * (width - 1.0) / (height - 1.0); double ci_top = ycenter + ver_span/2.0; double ci_delta = ver_span / (height - 1.0); double cr_left = xcenter - hor_span/2.0; double cr_delta = hor_span / (width - 1.0); for (int x=0; x < width; ++x) { double cr = cr_left + x*cr_delta; for (int y=0; y < height; ++y) { double ci = ci_top - y*ci_delta; int count = Mandelbrot(cr, ci, limit); PixelColor color = Palette(count, limit); frame.SetPixel(x, y, color); } } // Создаем PNG-файл для вывода, имя в формате // "outdir/frame_12345.png". char number[20]; snprintf(number, sizeof(number), "%05d", f); std::string filename = std::string(outdir) + "/frame_" + number + ".png"; // Сохраняем видеокадр как PNG-файл. int error = frame.SavePng(filename.c_str()); if (error) return error; printf("Wrote %s\n", filename.c_str()); // Увеличиваем приближение для следующего кадра. denom *= multiplier; } return 0; } catch (const char *message) { fprintf(stderr, "EXCEPTION: %s\n", message); return 1; } } Сохранение изображения в PNG-файл

Класс VideoFrame может быть полезен для приложения генератора видео. Он отображает один кадр и знает, как сохранить его в PNG-файл. Функция GenerateZoomFrames использует VideoFrame для создания каждого кадра видео фрактального приближения. 

class VideoFrame { private: int width; int height; std::vector<unsigned char> buffer; public: VideoFrame(int _width, int _height) : width(_width) , height(_height) , buffer(4 * _width * _height, 255) {} void SetPixel(int x, int y, PixelColor color) { int index = 4 * (y*width + x); buffer[index] = color.red; buffer[index+1] = color.green; buffer[index+2] = color.blue; buffer[index+3] = color.alpha; } int SavePng(const char *outFileName) { unsigned error = lodepng::encode(outFileName, buffer, width, height); if (error) { fprintf(stderr, "ERROR: lodepng::encode returned %u\n", error); return 1; } return 0; } };

Функция-член VideoFrame::SetPixel должна вызываться приложением для каждого пикселя каждого кадра. Вы передаете структуру PixelColor, которая определяет красный, зеленый и синий компоненты пикселя. Эти значение — целые числа в диапазоне от 0 до 255.

PixelColor также содержит альфа-значение в диапазоне от 0 до 255, отображающее прозрачность пикселя. Для видео приложений следует всегда устанавливать это значение 255, что означает, что пиксель полностью непрозрачен. Я включил альфа-значение для универсальности, в случае, если вам захочется сгенерировать PNG-файлы с прозрачными областями.

Использование ffmpeg для создания видеоклипа

Я включил баш-скрипт run, автоматизирующий весь процесс создания программы mandelzoom из исходного кода, ее запуск и конвертирование полученных 900 PNG-файлов в файл видеоклипа с именем zoom.mp4. Последний шаг выполняется запуском программы ffmpeg. Вот скрипт run. он содержит несколько полезных комментариев с пояснениями к аргументам командной строки для ffmpeg.

#!/bin/bash Fail() { echo "FATAL($0): $1" exit 1 } # Компилируем исходный код для mandelzoom. Оптимизируем для # скорости. echo "Building C++ code..." g++ -Wall -Werror -Ofast -o mandelzoom mandelzoom.cpp lodepng.cpp || Fail "Error building C++ code." # Уничтожаем папку "movie" и ее содержимое, если она существует. rm -rf movie || Fail "Error deleting movie directory." # Создаем новую пустую папку "movie" для хранения файлов вывода. mkdir movie || Fail "Error creating movie directory." # Запускаем генератор mandelbrot. Он создает все PNG-файлы вывода. ./mandelzoom movie $((30*30)) -0.74498410019 -0.13523854817 1.0e8 || Fail "Error running Mandelbrot Zoom program." # Конвертируем PNG-файлы в видеофайл zoom.mp4. # Пояснение к аргументам командной строки ffmpeg: # "-r 30" означает 30 кадров в секунду. # "-f image2" : конвертирует серию кадров в видео. # "-s 1280x720" указывает размеры итогового видео в пикселях. # "-i movie/frame_%05d.png" задает имена png-файлов, которые будут # использоваться в качестве входных данных. # "-vcodec libx264": библиотека кодеков, источник: # https://www.videolan.org/developers/x264.html # "-crf 15" : коэффициент постоянной скорости, определяющий степень # сжатия с потерями. https://trac.ffmpeg.org/wiki/Encode/H.264 # "-pix_fmt yuv420p" определяет, как цвета кодируются в mp4-файле. # https://en.wikipedia.org/wiki/YUV # "zoom.mp4" - имя итогового файла. ffmpeg -r 30 -f image2 -s 1280x720 -i movie/frame_%05d.png -vcodec libx264 -crf 15 -pix_fmt yuv420p zoom.mp4 || Fail "Error in ffmpeg." echo "Created movie zoom.mp4" exit 0

И это все! Репозиторий mandelzoom с исходным кодом можно найти здесь. 

Еще один пример

Я упоминал, что этот метод можно использовать для создания любого видео, при условии, что вы сможете написать код, создающий каждый кадр. Я закончу статью другим моим видео. Оно основано на простой трассировке лучей, которую я создал для моей книги Основы трассировки лучей. Код в этом видео работает так же, как и код выше: он создает серии PNG-файлов, которые затем ffmpeg конвертирует в MP4-файл.


Перевод статьи Don Cross: Create a YouTube Video from Code


Поделиться статьей:


Вернуться к статьям

Комментарии

    Ничего не найдено.