11import asyncio
2+ import gc
23import glob
34import multiprocessing
45import os
56import time
7+ from pathlib import Path
68
79import 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
1012from src .files .file_actions import delete_dir , delete_file
1113from src .utils .logger import logger
1214from 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