Skip to content

Commit 13c869b

Browse files
committed
video_handling.py - small RAM optimisation and saving improved videos with a same name
1 parent 566122f commit 13c869b

File tree

1 file changed

+45
-10
lines changed

1 file changed

+45
-10
lines changed

src/video/video_handling.py

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import asyncio
2+
import gc
23
import glob
34
import multiprocessing
45
import os
56
import time
7+
from pathlib import Path
68

79
import cv2
810

9-
from src.config.settings import BATCH_VIDEO_PATH, OUTPUT_BATCHES_DIR, TMP_VIDEO_PATH
11+
from src.config.settings import BATCH_VIDEO_PATH, TMP_VIDEO_PATH, UPSCALED_BATCHES_DIR
1012
from src.files.file_actions import delete_dir, delete_file
1113
from src.utils.logger import logger
1214
from src.video.video_exceptions import (
@@ -26,6 +28,16 @@ class VideoHandler:
2628
def __init__(self, fps: float):
2729
self.fps = fps
2830
self.video_queue = multiprocessing.Queue()
31+
self.final_videos_same_name = 1
32+
33+
@staticmethod
34+
def _force_memory_cleanup() -> None:
35+
"""Принудительно очищает память."""
36+
try:
37+
gc.collect()
38+
logger.debug("Выполнена принудительная очистка памяти")
39+
except Exception as e:
40+
logger.warning(f"Ошибка при очистке памяти: {e}")
2941

3042
def build_short_video(self, frame_batches: list) -> None:
3143
logger.debug("Запуск асинхронного сбора короткого видео")
@@ -80,7 +92,7 @@ def generate_video_from_frames(
8092
frame_paths: list, batch_range_start: str, batch_range_end: str, fps: float
8193
) -> str:
8294
"""
83-
Создает видео из списка фреймов, используя OpenCV.
95+
Создает видео из списка фреймов, используя OpenCV с периодической очисткой памяти.
8496
:param frame_paths: Список абсолютных путей к фреймам.
8597
:param batch_range_start: Начальный номер батча для именования видео.
8698
:param batch_range_end: Конечный номер батча для именования видео.
@@ -97,6 +109,7 @@ def generate_video_from_frames(
97109
logger.error(f"Не удалось прочитать первый кадр: {frame_paths[0]}")
98110
raise VideoReadFrameError(frame_paths[0])
99111
height, width, _ = first_frame.shape
112+
100113
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
101114
out = cv2.VideoWriter(
102115
filename=video_path,
@@ -115,6 +128,11 @@ def generate_video_from_frames(
115128
continue
116129
out.write(frame)
117130

131+
# Очищаем память каждые 100 кадров для больших видео
132+
if i % 100 == 0 and i > 0:
133+
del frame
134+
gc.collect()
135+
118136
# выводим прогресс каждые 500 кадров
119137
frame_num = i + 1
120138
if frame_num % 500 == 0 or frame_num == total_frames:
@@ -129,6 +147,8 @@ def generate_video_from_frames(
129147
raise VideoReadFrameError(frame_path)
130148
finally:
131149
out.release()
150+
# Очищаем память после завершения
151+
gc.collect()
132152
return video_path
133153

134154
@staticmethod
@@ -140,7 +160,7 @@ def collect_video_batches(batches_list: list) -> list:
140160
"""
141161
frame_paths = []
142162
for batch in batches_list:
143-
batch_path = str(os.path.join(OUTPUT_BATCHES_DIR, batch))
163+
batch_path = str(os.path.join(UPSCALED_BATCHES_DIR, batch))
144164
frames = sorted(glob.glob(os.path.join(batch_path, "frame*.jpg")))
145165
frame_paths.extend(frames)
146166
logger.debug(f"Собрано {len(frames)} фреймов из {batch}")
@@ -170,7 +190,7 @@ async def safe_delete_dir(dir_path: str) -> None:
170190

171191
def _handle_merging(self, video_paths: list) -> str:
172192
"""
173-
Обрабатывает объединение видео из списка путей к видеофайлам.
193+
Обрабатывает объединение видео из списка путей к видеофайлам с периодической очисткой памяти.
174194
:param video_paths: Список путей к видеофайлам для объединения.
175195
:return: Путь к созданному видеофайлу.
176196
"""
@@ -180,11 +200,21 @@ def _handle_merging(self, video_paths: list) -> str:
180200
f"merged_{first_num}-{last_num}", TMP_VIDEO_PATH
181201
)
182202

203+
if Path(output_path).exists():
204+
self.final_videos_same_name += 1
205+
output_path = f"{output_path.split(".mp4")[0]}_{str(self.final_videos_same_name)}.mp4"
206+
183207
cap = cv2.VideoCapture(video_paths[0])
184208
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
185209
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
186210
cap.release()
187211

212+
# Подсчитываем общее количество кадров
213+
total_frames = sum(
214+
int(cv2.VideoCapture(p).get(cv2.CAP_PROP_FRAME_COUNT))
215+
for p in video_paths
216+
)
217+
188218
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
189219
out = cv2.VideoWriter(
190220
filename=output_path,
@@ -194,10 +224,6 @@ def _handle_merging(self, video_paths: list) -> str:
194224
frameSize=(width, height),
195225
)
196226
try:
197-
total_frames = sum(
198-
int(cv2.VideoCapture(p).get(cv2.CAP_PROP_FRAME_COUNT))
199-
for p in video_paths
200-
)
201227
start_time = time.time()
202228
self.__merge_videos(video_paths, out, total_frames, start_time)
203229
total_time = time.time() - start_time
@@ -210,18 +236,20 @@ def _handle_merging(self, video_paths: list) -> str:
210236
raise VideoMergingError(f"Ошибка при объединении видео: {e}")
211237
finally:
212238
out.release()
239+
# Очищаем память после завершения
240+
self._force_memory_cleanup()
213241

214242
return output_path
215243

216-
@staticmethod
217244
def __merge_videos(
245+
self,
218246
video_paths: list,
219247
out: cv2.VideoWriter,
220248
total_frames: int,
221249
start_time: float,
222250
) -> None:
223251
"""
224-
Объединяет видео из списка путей к видеофайлам в один выходной файл.
252+
Объединяет видео из списка путей к видеофайлам в один выходной файл с очисткой памяти.
225253
:param video_paths: Список путей к видеофайлам для объединения.
226254
:param out: Объект VideoWriter для записи выходного видео.
227255
:param total_frames: Общее количество кадров для отслеживания прогресса.
@@ -244,6 +272,11 @@ def __merge_videos(
244272
out.write(frame)
245273
processed_frames += 1
246274

275+
# Очищаем память каждые 500 кадров для больших видео
276+
if processed_frames % 500 == 0:
277+
del frame
278+
self._force_memory_cleanup()
279+
247280
# Обновляем прогресс каждые 1000 кадров
248281
if processed_frames % 1000 == 0:
249282
elapsed = time.time() - start_time
@@ -257,6 +290,8 @@ def __merge_videos(
257290
f"Осталось: {remaining:.1f}сек"
258291
)
259292
cap.release()
293+
# Очищаем память после обработки каждого видео
294+
self._force_memory_cleanup()
260295

261296
@staticmethod
262297
def __build_video_path(video_name: str, path=BATCH_VIDEO_PATH):

0 commit comments

Comments
 (0)