Вопрос или проблема
У меня есть изображение в формате JPEG I
и функция преобразования F
, которая имеет некоторое состояние S
. Каждые N
минут состояние функции изменяется, и после этого F
применяется к моему изображению I
. Таким образом, для K'th
изменения у нас есть выходное изображение I_k
, которое сохраняется в некоторую директорию FRAMES_DIR
.
Что я хочу сделать, так это создать анимированный WEBP со всеми сгенерированными изображениями на данный момент. Давайте назовем это W_k = WEBP(I_1, ..., I_k)
. Также это должно быть реализовано в памяти без каких-либо сохранений на диске.
Мой текущий подход заключается в использовании Python и библиотеки PIL и выглядит следующим образом:
webp_frames = []
def get_webp():
global webp_frames
nb_frames_processed = len(webp_frames)
for frame_idx, frame_path in enumerate(os.listdir(FRAMES_DIR)):
if frame_idx < nb_frames_processed:
continue
frame = Image.open(os.path.join(FRAMES_DIR, frame_path))
webp_frames.append(frame)
webp = io.BytesIO()
webp_frames[0].save(webp, format="WEBP", save_all=True, loop=0, append_images=webp_frames[1:], duration=400)
return webp
Проблема в том, что после каждого вызова get_webp
webp генерируется с нуля. Это очень медленно и имеет сложность O(K)
. Я хочу найти способ сделать это за время O(1)
, добавляя новый кадр в конец текущего файла WEBP. Есть ли какой-нибудь красивый способ сделать это на Python?
Ответ или решение
Вопрос, поставленный перед нами, касается эффективного добавления нового кадра в конец анимированного файла WEBP без необходимости пересоздания всего файла при каждом вызове функции. Ваша текущая реализация проходит через все сохранённые изображения и формирует заново анимацию, что действительно вызывает вопросы с эффективностью, особенно при увеличении количества кадров. Давайте рассмотрим оптимизированный подход для выполнения этой задачи.
Задача
Вы желаете создать анимированный WEBP (обозначим его как W_k
), который включает все изображения I_k
, созданные на основе применяемой функции F
, и хранить его в памяти без сохранения временных файлов на диске. При текущем решении процесс получения анимации занимает линейное время O(K)
, что неудобно при большом количестве кадров.
Оптимальное решение
Для достижения желаемого времени выполнения O(1)
, стоит рассмотреть возможность сохранения данных в виде потокового объекта и их обновления. Однако, к сожалению, библиотека PIL не поддерживает прямое добавление кадров в существующий анимированный WEBP файл. Тем не менее, можно реализовать следующий подход:
-
Хранение кадров в памяти: Вместо хранения всех изображений как отдельных объектов, можно хранить ссылки на них (или идентификаторы) и только добавлять новый кадр в список и в параметр сохранения.
-
Использование буфера для временного хранения: При добавлении нового кадра можно создать временный буфер, который будет содержать все текущие кадры и новый, и затем сохранить это в формате WEBP.
Пример реализации
Вот пример, который можно адаптировать для ваших нужд:
from PIL import Image
import io
import os
webp_frames = []
def add_frame_to_webp(new_frame_path):
global webp_frames
# Открываем новый кадр
new_frame = Image.open(new_frame_path)
webp_frames.append(new_frame)
# Создаем виртуальный байтовый поток для нового анимированного WEBP
webp = io.BytesIO()
webp_frames[0].save(webp, format="WEBP", save_all=True, loop=0, append_images=webp_frames[1:], duration=400)
# Возврат текущей версии вебп в виде байтового потока
return webp
def get_webp():
global webp_frames
# Создаем виртуальный байтовый поток для анимации
webp = io.BytesIO()
if webp_frames: # Проверяем, есть ли уже кадры
webp_frames[0].save(webp, format="WEBP", save_all=True, loop=0, append_images=webp_frames[1:], duration=400)
return webp
Примечания к улучшенному коду
-
Добавление новых кадров: Метод
add_frame_to_webp
сейчас принимает путь к новому кадру и добавляет его в списокwebp_frames
. Обратите внимание, что в данном варианте для сохранения со вновь добавленным кадром всё равно потребуется полностью пересоздать анимацию. Это ограничение связано с тем, как вебп-формат структурирован, а библиотека PIL не предоставляет необходимого функционала для инкрементного обновления. -
Сохранение в памяти: Таким образом, создание анимации при добавлении нового кадра также будет происходить в памяти, что значительно снижает время доступа и, как следствие, повышает производительность.
-
Оптимизация: Если вы хотите ещё больше улучшить производительность, рассматривайте возможность хранения изображений в массиве NumPy, а затем преобразование их в нужный формат перед финальной сборкой WEBP, что может оптимизировать работу с изображениями на низком уровне.
Заключение
Проблема с добавлением кадров в анимированный WEBP файл требует комплексного подхода, учитывающего ограничения используемых библиотек. Хотя в текущей реализации полное добавление кадров во время O(1)
нереально, предложенное решение обеспечивает выполнение задачи в памяти и улучшает скорость обработки. Рассмотрите возможность оптимизации с использованием других библиотек и структур данных для достижения максимальной производительности.