def run(self): _file_list = self.file_prepare() threshold = input("请输入画面阈值(选择区间[10, 90],如无输入则默认27.5):") if not threshold: threshold = self.threshold_default crf = input("请输入crf值(选择区间[1, 51],如无输入则默认20):") if not crf: crf = self.crf_default for key in _file_list: scenes = self.find_scenes(key, threshold) print(scenes) file_path, full_name = os.path.split(key) f_name, ext = os.path.splitext(full_name) split_video_ffmpeg( [key], scenes, "$VIDEO_NAME - Scene $SCENE_NUMBER.mp4", self.output_dir + "/" + f_name, arg_override='-c:v libx264 -preset slow -crf ' + str(crf) + ' -c:a aac', hide_progress=False, suppress_output=False ) print("处理完毕~")
def find_scenes(video_path, threshold=30.0): # Create our video & scene managers, then add the detector. video_manager = VideoManager([video_path]) scene_manager = SceneManager() scene_manager.add_detector(ContentDetector(threshold=threshold)) # Improve processing speed by downscaling before processing. video_manager.set_downscale_factor() # Start the video manager and perform the scene detection. video_manager.start() time = scene_manager.detect_scenes(frame_source=video_manager) # Each returned scene is a tuple of the (start, end) timecode. times = scene_manager.get_scene_list(time) video_splitter.split_video_ffmpeg( video_path, times, '$VIDEO_NAME - Scene $SCENE_NUMBER', video_path, arg_override='-c:v libx264 -preset fast -crf 21 -c:a aac', hide_progress=False, suppress_output=False)
def make_elements(video_path, video_name, save_dir): # type: (str) -> List[Tuple[FrameTimecode, FrameTimecode]] video_manager = VideoManager([video_path]) stats_manager = StatsManager() # Construct our SceneManager and pass it our StatsManager. scene_manager = SceneManager(stats_manager) # Add ContentDetector algorithm (each detector's constructor # takes detector options, e.g. threshold). scene_manager.add_detector(ContentDetector(threshold=threshold)) base_timecode = video_manager.get_base_timecode() # We save our stats file to {VIDEO_PATH}.stats.csv. stats_file_path = 'stats/%s.stats.csv' % video_name scene_list = [] try: # If stats file exists, load it. if os.path.exists(stats_file_path): # Read stats from CSV file opened in read mode: with open(stats_file_path, 'r') as stats_file: stats_manager.load_from_csv(stats_file, base_timecode) # Set downscale factor to improve processing speed. video_manager.set_downscale_factor() # Start video_manager. video_manager.start() # Perform scene detection on video_manager. scene_manager.detect_scenes(frame_source=video_manager) # Obtain list of detected scenes. scene_list = scene_manager.get_scene_list(base_timecode) # Each scene is a tuple of (start, end) FrameTimecodes. finally: video_manager.release() video_dir = os.path.join(save_dir, video_name) if not os.path.exists(video_dir): os.makedirs(video_dir) split_video_ffmpeg([video_path], scene_list, os.path.join(video_dir,"${VIDEO_NAME}-${SCENE_NUMBER}.mp4"), video_name)
def chunk_videos(vid_paths): """Chunks videos into different scenes based on their content for later processing.""" for vp in vid_paths: try: # Setup the different managers for chunking the scenes. video_manager = VideoManager([str(vp)]) stats_manager = StatsManager() scene_manager = SceneManager(stats_manager) # Add ContentDetector algorithm (constructor takes detector options like threshold). scene_manager.add_detector(ContentDetector()) base_timecode = video_manager.get_base_timecode() # Set downscale factor to improve processing speed (no args means default). video_manager.set_downscale_factor() # Set the duration to be however long the video is and start the video manager. video_manager.set_duration() video_manager.start() # Perform scene detection on video_manager and grab the scenes. scene_manager.detect_scenes(frame_source=video_manager) scene_list = scene_manager.get_scene_list(base_timecode) # If the output dir of the chunked videos does not exist, create it. if not (vp.parent / "chunks").exists(): (vp.parent / "chunks").mkdir() # Split the video into chunks based on the scene list and save to the "chunks" folder. split_video_ffmpeg( [vp], scene_list, str(vp.parent / "chunks/$VIDEO_NAME-$SCENE_NUMBER.mp4"), "chunk" #, arg_override = args ) with open("stats.csv", 'w') as stats_file: stats_manager.save_to_csv(stats_file, base_timecode) finally: # Close out the video_manager. video_manager.release()
def process_input(self): # type: () -> None """ Process Input: Processes input video(s) and generates output as per CLI commands. Run after all command line options/sub-commands have been parsed. """ logging.debug('Processing input...') if not self.options_processed: logging.debug( 'Skipping processing, CLI options were not parsed successfully.' ) return self.check_input_open() if not self.scene_manager.get_num_detectors() > 0: logging.error( 'No scene detectors specified (detect-content, detect-threshold, etc...),\n' ' or failed to process all command line arguments.') return # Handle scene detection commands (detect-content, detect-threshold, etc...). self.video_manager.start() base_timecode = self.video_manager.get_base_timecode() start_time = time.time() logging.info('Detecting scenes...') num_frames = self.scene_manager.detect_scenes( frame_source=self.video_manager, frame_skip=self.frame_skip, show_progress=not self.quiet_mode) duration = time.time() - start_time logging.info('Processed %d frames in %.1f seconds (average %.2f FPS).', num_frames, duration, float(num_frames) / duration) # Handle -s/--statsfile option. if self.stats_file_path is not None: if self.stats_manager.is_save_required(): with open(self.stats_file_path, 'wt') as stats_file: logging.info('Saving frame metrics to stats file: %s', os.path.basename(self.stats_file_path)) self.stats_manager.save_to_csv(stats_file, base_timecode) else: logging.debug( 'No frame metrics updated, skipping update of the stats file.' ) # Get list of detected cuts and scenes from the SceneManager to generate the required output # files with based on the given commands (list-scenes, split-video, save-images, etc...). cut_list = self.scene_manager.get_cut_list(base_timecode) scene_list = self.scene_manager.get_scene_list(base_timecode) video_paths = self.video_manager.get_video_paths() video_name = os.path.basename(video_paths[0]) if video_name.rfind('.') >= 0: video_name = video_name[:video_name.rfind('.')] # Ensure we don't divide by zero. if scene_list: logging.info( 'Detected %d scenes, average shot length %.1f seconds.', len(scene_list), sum([(end_time - start_time).get_seconds() for start_time, end_time in scene_list]) / float(len(scene_list))) else: logging.info('No scenes detected.') # Handle list-scenes command. if self.scene_list_output: scene_list_filename = Template( self.scene_list_name_format).safe_substitute( VIDEO_NAME=video_name) if not scene_list_filename.lower().endswith('.csv'): scene_list_filename += '.csv' scene_list_path = get_and_create_path( scene_list_filename, self.scene_list_directory if self.scene_list_directory is not None else self.output_directory) logging.info('Writing scene list to CSV file:\n %s', scene_list_path) with open(scene_list_path, 'wt') as scene_list_file: write_scene_list(scene_list_file, scene_list, cut_list) # Handle `list-scenes`. if self.print_scene_list: logging.info( """Scene List: ----------------------------------------------------------------------- | Scene # | Start Frame | Start Time | End Frame | End Time | ----------------------------------------------------------------------- %s ----------------------------------------------------------------------- """, '\n'.join([ ' | %5d | %11d | %s | %11d | %s |' % (i + 1, start_time.get_frames(), start_time.get_timecode(), end_time.get_frames(), end_time.get_timecode()) for i, (start_time, end_time) in enumerate(scene_list) ])) if cut_list: logging.info('Comma-separated timecode list:\n %s', ','.join([cut.get_timecode() for cut in cut_list])) # Handle save-images command. if self.save_images: self._generate_images( scene_list=scene_list, video_name=video_name, image_name_template=self.image_name_format, output_dir=self.image_directory, downscale_factor=self.video_manager.get_downscale_factor()) # Handle export-html command. if self.export_html: html_filename = Template( self.html_name_format).safe_substitute(VIDEO_NAME=video_name) if not html_filename.lower().endswith('.html'): html_filename += '.html' html_path = get_and_create_path( html_filename, self.image_directory if self.image_directory is not None else self.output_directory) logging.info('Exporting to html file:\n %s:', html_path) if not self.html_include_images: self.image_filenames = None write_scene_list_html(html_path, scene_list, cut_list, image_filenames=self.image_filenames, image_width=self.image_width, image_height=self.image_height) # Handle split-video command. if self.split_video: # Add proper extension to filename template if required. dot_pos = self.split_name_format.rfind('.') if self.split_mkvmerge and not self.split_name_format.endswith( '.mkv'): self.split_name_format += '.mkv' # Don't add if we find an extension between 2 and 4 characters elif not (dot_pos >= 0) or (dot_pos >= 0 and not ((len(self.split_name_format) - (dot_pos + 1) <= 4 >= 2))): self.split_name_format += '.mp4' output_file_prefix = get_and_create_path( self.split_name_format, self.split_directory if self.split_directory is not None else self.output_directory) mkvmerge_available = is_mkvmerge_available() ffmpeg_available = is_ffmpeg_available() if mkvmerge_available and (self.split_mkvmerge or not ffmpeg_available): if not self.split_mkvmerge: logging.warning( 'ffmpeg not found, falling back to fast copy mode (split-video -c/--copy).' ) split_video_mkvmerge(video_paths, scene_list, output_file_prefix, video_name, suppress_output=self.quiet_mode or self.split_quiet) elif ffmpeg_available: if self.split_mkvmerge: logging.warning( 'mkvmerge not found, falling back to normal splitting' ' mode (split-video).') split_video_ffmpeg(video_paths, scene_list, output_file_prefix, video_name, arg_override=self.split_args, hide_progress=self.quiet_mode, suppress_output=self.quiet_mode or self.split_quiet) else: if not (mkvmerge_available or ffmpeg_available): error_strs = [ "ffmpeg/mkvmerge is required for split-video [-c/--copy]." ] else: error_strs = [ "{EXTERN_TOOL} is required for split-video{EXTRA_ARGS}." .format(EXTERN_TOOL='mkvmerge' if self.split_mkvmerge else 'ffmpeg', EXTRA_ARGS=' -c/--copy' if self.split_mkvmerge else '') ] error_strs += [ "Install one of the above tools to enable the split-video command." ] error_str = '\n'.join(error_strs) logging.debug(error_str) raise click.BadParameter(error_str, param_hint='split-video') if scene_list: logging.info( 'Video splitting completed, individual scenes written to disk.' )
def process_input(self): # type: () -> None """ Process Input: Processes input video(s) and generates output as per CLI commands. Run after all command line options/sub-commands have been parsed. """ self.logger.debug('Processing input...') if not self.options_processed: self.logger.debug( 'Skipping processing, CLI options were not parsed successfully.' ) return self.check_input_open() assert self.scene_manager.get_num_detectors() >= 0 if self.scene_manager.get_num_detectors() == 0: self.logger.error( 'No scene detectors specified (detect-content, detect-threshold, etc...),\n' ' or failed to process all command line arguments.') return # Display a warning if the video codec type seems unsupported (#86). if int(abs(self.video_manager.get(cv2.CAP_PROP_FOURCC))) == 0: self.logger.error( 'Video codec detection failed, output may be incorrect.\nThis could be caused' ' by using an outdated version of OpenCV, or using codecs that currently are' ' not well supported (e.g. VP9).\n' 'As a workaround, consider re-encoding the source material before processing.\n' 'For details, see https://github.com/Breakthrough/PySceneDetect/issues/86' ) # Handle scene detection commands (detect-content, detect-threshold, etc...). self.video_manager.start() start_time = time.time() self.logger.info('Detecting scenes...') num_frames = self.scene_manager.detect_scenes( frame_source=self.video_manager, frame_skip=self.frame_skip, show_progress=not self.quiet_mode) # Handle case where video fails with multiple audio tracks (#179). # TODO: Using a different video backend as per #213 may also resolve this issue, # as well as numerous other timing related issues. if num_frames <= 0: self.logger.critical( 'Failed to read any frames from video file. This could be caused' ' by the video having multiple audio tracks. If so, please try' ' removing the audio tracks or muxing to mkv via:\n' ' ffmpeg -i input.mp4 -c copy -an output.mp4\n' 'or:\n' ' mkvmerge -o output.mkv input.mp4\n' 'For details, see https://pyscenedetect.readthedocs.io/en/latest/faq/' ) return duration = time.time() - start_time self.logger.info( 'Processed %d frames in %.1f seconds (average %.2f FPS).', num_frames, duration, float(num_frames) / duration) # Handle -s/--statsfile option. if self.stats_file_path is not None: if self.stats_manager.is_save_required(): with open(self.stats_file_path, 'wt') as stats_file: self.logger.info('Saving frame metrics to stats file: %s', os.path.basename(self.stats_file_path)) base_timecode = self.video_manager.get_base_timecode() self.stats_manager.save_to_csv(stats_file, base_timecode) else: self.logger.debug( 'No frame metrics updated, skipping update of the stats file.' ) # Get list of detected cuts and scenes from the SceneManager to generate the required output # files with based on the given commands (list-scenes, split-video, save-images, etc...). cut_list = self.scene_manager.get_cut_list() scene_list = self.scene_manager.get_scene_list() # Handle --drop-short-scenes. if self.drop_short_scenes and self.min_scene_len > 0: scene_list = [ s for s in scene_list if (s[1] - s[0]) >= self.min_scene_len ] video_paths = self.video_manager.get_video_paths() video_name = self.video_manager.get_video_name() if scene_list: # Ensure we don't divide by zero. self.logger.info( 'Detected %d scenes, average shot length %.1f seconds.', len(scene_list), sum([(end_time - start_time).get_seconds() for start_time, end_time in scene_list]) / float(len(scene_list))) else: self.logger.info('No scenes detected.') # Handle list-scenes command. if self.scene_list_output: scene_list_filename = Template( self.scene_list_name_format).safe_substitute( VIDEO_NAME=video_name) if not scene_list_filename.lower().endswith('.csv'): scene_list_filename += '.csv' scene_list_path = get_and_create_path( scene_list_filename, self.scene_list_directory if self.scene_list_directory is not None else self.output_directory) self.logger.info('Writing scene list to CSV file:\n %s', scene_list_path) with open(scene_list_path, 'wt') as scene_list_file: write_scene_list(output_csv_file=scene_list_file, scene_list=scene_list, include_cut_list=not self.skip_cuts, cut_list=cut_list) if self.print_scene_list: self.logger.info( """Scene List: ----------------------------------------------------------------------- | Scene # | Start Frame | Start Time | End Frame | End Time | ----------------------------------------------------------------------- %s ----------------------------------------------------------------------- """, '\n'.join([ ' | %5d | %11d | %s | %11d | %s |' % (i + 1, start_time.get_frames(), start_time.get_timecode(), end_time.get_frames(), end_time.get_timecode()) for i, (start_time, end_time) in enumerate(scene_list) ])) if cut_list: self.logger.info( 'Comma-separated timecode list:\n %s', ','.join([cut.get_timecode() for cut in cut_list])) # Handle save-images command. if self.save_images: image_output_dir = self.output_directory if self.image_directory is not None: image_output_dir = self.image_directory image_filenames = save_images( scene_list=scene_list, video_manager=self.video_manager, num_images=self.num_images, frame_margin=self.frame_margin, image_extension=self.image_extension, encoder_param=self.image_param, image_name_template=self.image_name_format, output_dir=image_output_dir, show_progress=not self.quiet_mode, scale=self.scale, height=self.height, width=self.width) # Handle export-html command. if self.export_html: html_filename = Template( self.html_name_format).safe_substitute(VIDEO_NAME=video_name) if not html_filename.lower().endswith('.html'): html_filename += '.html' html_path = get_and_create_path( html_filename, self.image_directory if self.image_directory is not None else self.output_directory) self.logger.info('Exporting to html file:\n %s:', html_path) if not self.html_include_images: image_filenames = None write_scene_list_html(html_path, scene_list, cut_list, image_filenames=image_filenames, image_width=self.image_width, image_height=self.image_height) # Handle split-video command. if self.split_video: output_path_template = self.split_name_format # Add proper extension to filename template if required. dot_pos = output_path_template.rfind('.') extension_length = 0 if dot_pos < 0 else len( output_path_template) - (dot_pos + 1) # If using mkvmerge, force extension to .mkv. if self.split_mkvmerge and not output_path_template.endswith( '.mkv'): output_path_template += '.mkv' # Otherwise, if using ffmpeg, only add an extension if one doesn't exist. elif not 2 <= extension_length <= 4: output_path_template += '.mp4' output_path_template = get_and_create_path( output_path_template, self.split_directory if self.split_directory is not None else self.output_directory) # Ensure the appropriate tool is available before handling split-video. check_split_video_requirements(self.split_mkvmerge) if self.split_mkvmerge: split_video_mkvmerge(video_paths, scene_list, output_path_template, video_name, suppress_output=self.quiet_mode or self.split_quiet) else: split_video_ffmpeg(video_paths, scene_list, output_path_template, video_name, arg_override=self.split_args, hide_progress=self.quiet_mode, suppress_output=self.quiet_mode or self.split_quiet) if scene_list: self.logger.info( 'Video splitting completed, individual scenes written to disk.' )
def find_scenes(video_path): start_time = time.time() print("Analyzing video " + video_path) # type: (str) -> List[Tuple[FrameTimecode, FrameTimecode]] video_manager = VideoManager([video_path]) stats_manager = StatsManager() # Pass StatsManager to SceneManager to accelerate computing time scene_manager = SceneManager(stats_manager) # Add ContentDetector algorithm (each detector's constructor # takes detector options, e.g. threshold). scene_manager.add_detector(ContentDetector()) base_timecode = video_manager.get_base_timecode() # We save our stats file to {VIDEO_PATH}.stats.csv. stats_file_path = '%s.stats.csv' % (video_path) scene_list = [] folder = os.path.splitext(video_path)[0] if os.path.exists(folder): print( '--- STOP : The folder for this video already exists, it is probably already split.' ) else: try: # If stats file exists, load it. if os.path.exists(stats_file_path): # Read stats from CSV file opened in read mode: with open(stats_file_path, 'r') as stats_file: stats_manager.load_from_csv(stats_file, base_timecode) if video_splitter.is_ffmpeg_available(): # Set downscale factor to improve processing speed. video_manager.set_downscale_factor() # Start video_manager. video_manager.start() # Perform scene detection on video_manager. scene_manager.detect_scenes(frame_source=video_manager) # Obtain list of detected scenes. scene_list = scene_manager.get_scene_list(base_timecode) # Each scene is a tuple of (start, end) FrameTimecodes. print('%s scenes obtained' % len(scene_list)) if len(scene_list) > 0: # STATISTICS : Store scenes length with open(FILE_SCENE_LENGH, 'a') as myfile: for i, scene in enumerate(scene_list): myfile.write( '%s, %d, %f\n' % (os.path.splitext( os.path.basename(video_path))[0], scene[1].get_frames() - scene[0].get_frames(), (scene[1] - scene[0]).get_seconds())) # STATISTICS : Store number of scenes with open(FILE_SCENE_NUMBER, 'a') as myfile: myfile.write('%s,%d\n' % (os.path.splitext( os.path.basename(video_path))[0], len(scene_list))) # Split the video print('Splitting the video. Put scenes in %s/%s' % (folder, VIDEO_SPLIT_TEMPLATE)) os.mkdir(folder) video_splitter.split_video_ffmpeg( [video_path], scene_list, folder + "/" + VIDEO_SPLIT_TEMPLATE + ".mp4", os.path.basename(folder), suppress_output=True) print("-- Finished video splitting in {:.2f}s --".format( time.time() - start_time)) else: print( 'Ffmpeg is not installed on your computer. Please install it before running this code' ) finally: video_manager.release() return scene_list
def process_input(self): # type: () -> None """ Process Input: Processes input video(s) and generates output as per CLI commands. Run after all command line options/sub-commands have been parsed. """ logging.debug('Processing input...') if not self.options_processed: logging.debug( 'Skipping processing, CLI options were not parsed successfully.' ) return self.check_input_open() if not self.scene_manager.get_num_detectors() > 0: logging.error( 'No scene detectors specified (detect-content, detect-threshold, etc...).' ) return # Handle scene detection commands (detect-content, detect-threshold, etc...). self.video_manager.start() base_timecode = self.video_manager.get_base_timecode() start_time = time.time() logging.info('Detecting scenes...') num_frames = self.scene_manager.detect_scenes( frame_source=self.video_manager, start_time=self.start_frame, frame_skip=self.frame_skip, show_progress=not self.quiet_mode) duration = time.time() - start_time logging.info('Processed %d frames in %.1f seconds (average %.2f FPS).', num_frames, duration, float(num_frames) / duration) # Handle -s/--statsfile option. if self.stats_file_path is not None: if self.stats_manager.is_save_required(): with open(self.stats_file_path, 'wt') as stats_file: logging.info('Saving frame metrics to stats file: %s', os.path.basename(self.stats_file_path)) self.stats_manager.save_to_csv(stats_file, base_timecode) else: logging.debug( 'No frame metrics updated, skipping update of the stats file.' ) # Get list of detected cuts and scenes from the SceneManager to generate the required output # files with based on the given commands (list-scenes, split-video, save-images, etc...). cut_list = self.scene_manager.get_cut_list(base_timecode) scene_list = self.scene_manager.get_scene_list(base_timecode) video_paths = self.video_manager.get_video_paths() video_name = os.path.basename(video_paths[0]) if video_name.rfind('.') >= 0: video_name = video_name[:video_name.rfind('.')] # Handle list-scenes command. # Handle `list-scenes -o`. if self.scene_list_path is not None: with open(self.scene_list_path, 'wt') as scene_list_file: write_scene_list(scene_list_file, cut_list, scene_list) # Handle `list-scenes`. list_length = len(scene_list) if len(scene_list) else 1 logging.info( 'Detected %d scenes, average shot length %.1f seconds.', list_length, sum([(end_time - start_time).get_seconds() for start_time, end_time in scene_list]) / float(list_length)) if self.print_scene_list: logging.info( """ Scene List: ----------------------------------------------------------------------- | Scene # | Start Frame | Start Time | End Frame | End Time | ----------------------------------------------------------------------- %s ----------------------------------------------------------------------- """, '\n'.join([ ' | %5d | %11d | %s | %11d | %s |' % (i + 1, start_time.get_frames(), start_time.get_timecode(), end_time.get_frames(), end_time.get_timecode()) for i, (start_time, end_time) in enumerate(scene_list) ])) if cut_list: logging.info('Comma-separated timecode list:\n %s', ','.join([cut.get_timecode() for cut in cut_list])) # Handle save-images command. if self.save_images: self._generate_images(scene_list=scene_list, image_prefix=video_name, output_dir=self.image_directory) # Handle split-video command. if self.split_video: output_file_name = self.get_output_file_path( video_name, output_dir=self.split_directory) mkvmerge_available = is_mkvmerge_available() ffmpeg_available = is_ffmpeg_available() if mkvmerge_available and (self.split_mkvmerge or not ffmpeg_available): if not self.split_mkvmerge: logging.info('ffmpeg not found.') logging.info('Splitting input video%s using mkvmerge...', 's' if len(video_paths) > 1 else '') split_video_mkvmerge(video_paths, scene_list, output_file_name, suppress_output=self.quiet_mode or self.split_quiet) elif ffmpeg_available: logging.info('Splitting input video%s using ffmpeg...', 's' if len(video_paths) > 1 else '') split_video_ffmpeg(video_paths, scene_list, output_file_name, arg_override=self.split_args, hide_progress=self.quiet_mode or self.split_quiet, suppress_output=self.quiet_mode or self.split_quiet) else: error_strs = [ "ffmpeg/mkvmerge is required for video splitting.", "Install one of the above tools to enable the split-video command." ] error_str = '\n'.join(error_strs) logging.debug(error_str) raise click.BadParameter(error_str, param_hint='split-video') logging.info( 'Video splitting completed, individual scenes written to disk.' )
def make_dataset(video_path, video_name, timecodes, save_dir): # type: (str) -> List[Tuple[FrameTimecode, FrameTimecode]] video_manager = VideoManager([video_path]) stats_manager = StatsManager() # Construct our SceneManager and pass it our StatsManager. scene_manager = SceneManager(stats_manager) # Add ContentDetector algorithm (each detector's constructor # takes detector options, e.g. threshold). scene_manager.add_detector(ContentDetector()) base_timecode = video_manager.get_base_timecode() # We save our stats file to {VIDEO_PATH}.stats.csv. stats_file_path = 'stats/%s.stats.csv' % video_name scene_list = [] try: # If stats file exists, load it. if os.path.exists(stats_file_path): # Read stats from CSV file opened in read mode: with open(stats_file_path, 'r') as stats_file: stats_manager.load_from_csv(stats_file, base_timecode) # Set downscale factor to improve processing speed. video_manager.set_downscale_factor() # Start video_manager. video_manager.start() # Perform scene detection on video_manager. scene_manager.detect_scenes(frame_source=video_manager) # We only write to the stats file if a save is required: if stats_manager.is_save_required(): with open(stats_file_path, 'w') as stats_file: stats_manager.save_to_csv(stats_file, base_timecode) start_timecode = "" start_content_val = 0 end_timecode = "" end_content_val = 0 metric_keys = sorted( list( stats_manager._registered_metrics.union( stats_manager._loaded_metrics))) frame_keys = sorted(stats_manager._frame_metrics.keys()) for frame_key in frame_keys: frame_timecode = base_timecode + frame_key timecode = frame_timecode.get_timecode() if timecode > timecodes[0] and timecode < timecodes[1]: content_val = stats_manager.get_metrics( frame_key, metric_keys)[0] if start_content_val < content_val: start_content_val = content_val start_timecode = timecode if timecode > timecodes[2] and timecode < timecodes[3]: content_val = stats_manager.get_metrics( frame_key, metric_keys)[0] if end_content_val < content_val: end_content_val = content_val end_timecode = timecode threshold = min(start_content_val, end_content_val) print(f"Start Time: {start_timecode}, End Time: {end_timecode}") finally: video_manager.release() video_manager = VideoManager([video_path]) stats_manager = StatsManager() scene_manager = SceneManager(stats_manager) scene_manager.add_detector(ContentDetector(threshold=threshold)) base_timecode = video_manager.get_base_timecode() scene_list = [] try: # If stats file exists, load it. if os.path.exists(stats_file_path): # Read stats from CSV file opened in read mode: with open(stats_file_path, 'r') as stats_file: stats_manager.load_from_csv(stats_file, base_timecode) # Set downscale factor to improve processing speed. video_manager.set_downscale_factor() # Start video_manager. video_manager.start() # Perform scene detection on video_manager. scene_manager.detect_scenes(frame_source=video_manager) # Obtain list of detected scenes. scene_list = scene_manager.get_scene_list(base_timecode) # Each scene is a tuple of (start, end) FrameTimecodes. start_video_num = 0 end_video_num = 0 for i, scene in enumerate(scene_list): if scene[0].get_timecode( ) >= start_timecode and start_video_num == 0: start_video_num = i print(f"start video: {start_video_num}") if scene[1].get_timecode() >= end_timecode and end_video_num == 0: end_video_num = i print(f"end video: {end_video_num}") finally: video_manager.release() video_dir = os.path.join(save_dir, video_name) if not os.path.exists(video_dir): os.makedirs(video_dir) split_video_ffmpeg([video_path], scene_list, os.path.join(video_dir, "${VIDEO_NAME}-${SCENE_NUMBER}.mp4"), video_name) return start_video_num, end_video_num, len(scene_list)
print('List of scenes obtained:') final_scene_list = [] for i, scene in enumerate(scene_list): temp = list(scene) # print(temp) temp[0] = temp[0] + 1 temp[1] = temp[1] - 1 scene = tuple(temp) final_scene_list.append(scene) finally: video_manager.release() return final_scene_list def file_prepare(): file_list = [] for root, dirs, files in os.walk(input_dir): file_list = [input_dir+"/"+i for i in files] return file_list _file_list = file_prepare() for key in _file_list: scenes = find_scenes(key) print(scenes) file_path, full_name = os.path.split(key) f_name, ext = os.path.splitext(full_name) split_video_ffmpeg([key], scenes, "$VIDEO_NAME - Scene $SCENE_NUMBER.mp4", output_dir+"/"+f_name, arg_override='-c:v libx264 -preset slow -crf 20 -c:a aac', hide_progress=False, suppress_output=False)
def split_video(video_path, video_name, save_dir_path, threshold): """ Oversegment the input video using PySceneDetect Patameters ---------- video_path : string path to input video video_name : string name of input video save_dir_path : string path to saving split elements threshold : int PySceneDetect threshold """ cv2.setNumThreads(1) # type: (str) -> List[Tuple[FrameTimecode, FrameTimecode]] video_manager = VideoManager([video_path]) stats_manager = StatsManager() # Construct our SceneManager and pass it our StatsManager. scene_manager = SceneManager(stats_manager) # Add ContentDetector algorithm (each detector's constructor # takes detector options, e.g. threshold). scene_manager.add_detector( ContentDetector(threshold=threshold, min_scene_len=180)) base_timecode = video_manager.get_base_timecode() # We save our stats file to {VIDEO_PATH}.stats.csv. stats_file_path = 'stats/%s.stats.csv' % video_name scene_list = [] try: # If stats file exists, load it. if os.path.exists(stats_file_path): # Read stats from CSV file opened in read mode: with open(stats_file_path, 'r') as stats_file: stats_manager.load_from_csv(stats_file, base_timecode) # Set downscale factor to improve processing speed. video_manager.set_downscale_factor() # Start video_manager. video_manager.start() # Perform scene detection on video_manager. scene_manager.detect_scenes(frame_source=video_manager) # Obtain list of detected scenes. scene_list = scene_manager.get_scene_list(base_timecode) # Each scene is a tuple of (start, end) FrameTimecodes. finally: video_manager.release() if not os.path.exists(save_dir_path): os.makedirs(save_dir_path) split_video_ffmpeg( [video_path], scene_list, os.path.join(save_dir_path, "${VIDEO_NAME}-${SCENE_NUMBER}.mp4"), video_name[:-4], arg_override='-threads 1 -c:v libx264 -preset fast -crf 21 -c:a aac') timecode_list = [] for scene in scene_list: start = scene[0].get_timecode() end = scene[1].get_timecode() timecode_list.append((start, end)) return timecode_list
def process_input(self): # type: () -> None """ Process Input: Processes input video(s) and generates output as per CLI commands. Run after all command line options/sub-commands have been parsed. """ logging.debug('Processing input...') if not self.options_processed: logging.debug('Skipping processing, CLI options were not parsed successfully.') return self.check_input_open() if not self.scene_manager.get_num_detectors() > 0: logging.error( 'No scene detectors specified (detect-content, detect-threshold, etc...),\n' ' or failed to process all command line arguments.') return # Handle scene detection commands (detect-content, detect-threshold, etc...). self.video_manager.start() base_timecode = self.video_manager.get_base_timecode() start_time = time.time() logging.info('Detecting scenes...') num_frames = self.scene_manager.detect_scenes( frame_source=self.video_manager, frame_skip=self.frame_skip, show_progress=not self.quiet_mode) duration = time.time() - start_time logging.info('Processed %d frames in %.1f seconds (average %.2f FPS).', num_frames, duration, float(num_frames)/duration) # Handle -s/--statsfile option. if self.stats_file_path is not None: if self.stats_manager.is_save_required(): with open(self.stats_file_path, 'wt') as stats_file: logging.info('Saving frame metrics to stats file: %s', os.path.basename(self.stats_file_path)) self.stats_manager.save_to_csv( stats_file, base_timecode) else: logging.debug('No frame metrics updated, skipping update of the stats file.') # Get list of detected cuts and scenes from the SceneManager to generate the required output # files with based on the given commands (list-scenes, split-video, save-images, etc...). cut_list = self.scene_manager.get_cut_list(base_timecode) scene_list = self.scene_manager.get_scene_list(base_timecode) video_paths = self.video_manager.get_video_paths() video_name = os.path.basename(video_paths[0]) if video_name.rfind('.') >= 0: video_name = video_name[:video_name.rfind('.')] # Ensure we don't divide by zero. if scene_list: logging.info('Detected %d scenes, average shot length %.1f seconds.', len(scene_list), sum([(end_time - start_time).get_seconds() for start_time, end_time in scene_list]) / float(len(scene_list))) else: logging.info('No scenes detected.') # Handle list-scenes command. if self.scene_list_output: scene_list_filename = Template(self.scene_list_name_format).safe_substitute( VIDEO_NAME=video_name) if not scene_list_filename.lower().endswith('.csv'): scene_list_filename += '.csv' scene_list_path = self.get_output_file_path( scene_list_filename, self.scene_list_directory) logging.info('Writing scene list to CSV file:\n %s', scene_list_path) with open(scene_list_path, 'wt') as scene_list_file: write_scene_list(scene_list_file, scene_list, cut_list) # Handle `list-scenes`. if self.print_scene_list: logging.info("""Scene List: ----------------------------------------------------------------------- | Scene # | Start Frame | Start Time | End Frame | End Time | ----------------------------------------------------------------------- %s ----------------------------------------------------------------------- """, '\n'.join( [' | %5d | %11d | %s | %11d | %s |' % ( i+1, start_time.get_frames(), start_time.get_timecode(), end_time.get_frames(), end_time.get_timecode()) for i, (start_time, end_time) in enumerate(scene_list)])) if cut_list: logging.info('Comma-separated timecode list:\n %s', ','.join([cut.get_timecode() for cut in cut_list])) # Handle save-images command. if self.save_images: self._generate_images(scene_list=scene_list, video_name=video_name, image_name_template=self.image_name_format, output_dir=self.image_directory) # Handle export-html command. if self.export_html: html_filename = Template(self.html_name_format).safe_substitute( VIDEO_NAME=video_name) if not html_filename.lower().endswith('.html'): html_filename += '.html' html_path = self.get_output_file_path( html_filename, self.image_directory) logging.info('Exporting to html file:\n %s:', html_path) if not self.html_include_images: self.image_filenames = None write_scene_list_html(html_path, scene_list, cut_list, image_filenames=self.image_filenames, image_width=self.image_width, image_height=self.image_height) # Handle split-video command. if self.split_video: # Add proper extension to filename template if required. dot_pos = self.split_name_format.rfind('.') if self.split_mkvmerge and not self.split_name_format.endswith('.mkv'): self.split_name_format += '.mkv' # Don't add if we find an extension between 2 and 4 characters elif not (dot_pos >= 0) or ( dot_pos >= 0 and not ((len(self.split_name_format) - (dot_pos+1) <= 4 >= 2))): self.split_name_format += '.mp4' output_file_prefix = self.get_output_file_path( self.split_name_format, output_dir=self.split_directory) mkvmerge_available = is_mkvmerge_available() ffmpeg_available = is_ffmpeg_available() if mkvmerge_available and (self.split_mkvmerge or not ffmpeg_available): if not self.split_mkvmerge: logging.warning( 'ffmpeg not found, falling back to fast copy mode (split-video -c/--copy).') split_video_mkvmerge(video_paths, scene_list, output_file_prefix, video_name, suppress_output=self.quiet_mode or self.split_quiet) elif ffmpeg_available: if self.split_mkvmerge: logging.warning('mkvmerge not found, falling back to normal splitting' ' mode (split-video).') split_video_ffmpeg(video_paths, scene_list, output_file_prefix, video_name, arg_override=self.split_args, hide_progress=self.quiet_mode, suppress_output=self.quiet_mode or self.split_quiet) else: if not (mkvmerge_available or ffmpeg_available): error_strs = ["ffmpeg/mkvmerge is required for split-video [-c/--copy]."] else: error_strs = [ "{EXTERN_TOOL} is required for split-video{EXTRA_ARGS}.".format( EXTERN_TOOL='mkvmerge' if self.split_mkvmerge else 'ffmpeg', EXTRA_ARGS=' -c/--copy' if self.split_mkvmerge else '')] error_strs += ["Install one of the above tools to enable the split-video command."] error_str = '\n'.join(error_strs) logging.debug(error_str) raise click.BadParameter(error_str, param_hint='split-video') if scene_list: logging.info('Video splitting completed, individual scenes written to disk.')
def spliter(file_name, frame_rate=25, threshold=55): print('detecting scenes...') STATS_FILE_PATH = './' + os.path.basename( os.path.splitext(file_name)[0]) + '.csv' # print(STATS_FILE_PATH) # print(file_name) scenes = list() # print("Running PySceneDetect API test...") # print("PySceneDetect version being used: %s" % str(scenedetect.__version__)) # Create a video_manager point to video file testvideo.mp4. Note that multiple # videos can be appended by simply specifying more file paths in the list # passed to the VideoManager constructor. Note that appending multiple videos # requires that they all have the same frame size, and optionally, framerate. video_manager = VideoManager([file_name]) stats_manager = StatsManager() scene_manager = SceneManager(stats_manager) # Add ContentDetector algorithm (constructor takes detector options like threshold). scene_manager.add_detector( ContentDetector(threshold=55.0, min_scene_len=288)) base_timecode = video_manager.get_base_timecode() # print(base_timecode) try: # If stats file exists, load it. if os.path.exists(STATS_FILE_PATH): # Read stats from CSV file opened in read mode: with open(STATS_FILE_PATH, 'r') as stats_file: stats_manager.load_from_csv(stats_file, base_timecode) start_time = base_timecode + 20 # 00:00:00.667 end_time = base_timecode + 20.0 # 00:00:20.000 # Set video_manager duration to read frames from 00:00:00 to 00:00:20. video_manager.set_duration(start_time=start_time) # , end_time=end_time # Set downscale factor to improve processing speed. video_manager.set_downscale_factor() # Start video_manager. video_manager.start() # Perform scene detection on video_manager. scene_manager.detect_scenes(frame_source=video_manager, start_time=start_time) # Obtain list of detected scenes. scene_list = scene_manager.get_scene_list(base_timecode) # Like FrameTimecodes, each scene in the scene_list can be sorted if the # list of scenes becomes unsorted. # print('List of scenes obtained:') # for i, scene in enumerate(scene_list): # print(' Scene %2d: Start %s / Frame %d, End %s / Frame %d' % ( # i+1, # scene[0].get_timecode(), scene[0].get_frames(), # scene[1].get_timecode(), scene[1].get_frames(),)) # print('scene[0] is {}, scene[1] is {}, i+1 is {}'.format(scene[0], scene[1], i+1) ) # scenes.append((scene[0], scene[1])) # print('-------------------------------------------------------------------------------------------------') # print('ffmpeg -ss {} -i testvideo.mp4 -c:v libx264 -t {} -an test-scene-{}.mp4'.format(scene[0], # scene[1], i + 1)) # We only write to the stats file if a save is required: if stats_manager.is_save_required(): with open(STATS_FILE_PATH, 'w') as stats_file: stats_manager.save_to_csv(stats_file, base_timecode) # is_ffmpeg_available() # print(scene_list) scenes = split_video_ffmpeg( [file_name], scene_list, '$VIDEO_NAME-Scene-$SCENE_NUMBER.mp4', os.path.basename(os.path.splitext(file_name)[0]) + '-reference', '-c:v libx264 -preset fast -crf 18 -an') finally: video_manager.release() return scenes