def pick_and_save(self, range_list: typing.List[VideoCutRange], frame_count: int, to_dir: str = None, compress_rate: float = None, *args, **kwargs) -> str: stage_list = list() for index, each_range in enumerate(range_list): picked = each_range.pick(frame_count, *args, **kwargs) logger.info(f'pick {picked} in range {each_range}') stage_list.append((index, picked)) # create parent dir if not to_dir: to_dir = toolbox.get_timestamp_str() os.makedirs(to_dir, exist_ok=True) for each_stage_id, each_frame_list in stage_list: # create sub dir each_stage_dir = os.path.join(to_dir, str(each_stage_id)) os.makedirs(each_stage_dir, exist_ok=True) with toolbox.video_capture(self.video_path) as cap: for each_frame_id in each_frame_list: each_frame_path = os.path.join(each_stage_dir, f'{uuid.uuid4()}.png') each_frame = toolbox.get_frame(cap, each_frame_id - 1) if compress_rate: each_frame = toolbox.compress_frame(each_frame, compress_rate) cv2.imwrite(each_frame_path, each_frame) logger.debug(f'frame [{each_frame_id}] saved to {each_frame_path}') return to_dir
def pick_and_save( self, range_list: typing.List[VideoCutRange], frame_count: int, to_dir: str = None, prune: float = None, # in kwargs # compress_rate: float = None, # target_size: typing.Tuple[int, int] = None, # to_grey: bool = None, *args, **kwargs) -> str: """ pick some frames from range, and save them as files :param range_list: VideoCutRange list :param frame_count: default to 3, and finally you will get 3 frames for each range :param to_dir: will saved to this path :param prune: float, 0-1. if set it 0.9, some stages which are too similar (ssim > 0.9) will be removed :param args: :param kwargs: :return: """ stage_list = list() # build tag and get frames for index, each_range in enumerate(range_list): picked = each_range.pick(frame_count, *args, **kwargs) picked_frames = each_range.get_frames(picked) logger.info(f'pick {picked} in range {each_range}') stage_list.append((str(index), picked_frames)) # prune if prune: stage_list = self._prune(prune, stage_list) # create parent dir if not to_dir: to_dir = toolbox.get_timestamp_str() os.makedirs(to_dir, exist_ok=True) for each_stage_id, each_frame_list in stage_list: # create sub dir each_stage_dir = os.path.join(to_dir, str(each_stage_id)) os.makedirs(each_stage_dir, exist_ok=True) for each_frame_object in each_frame_list: each_frame_path = os.path.join(each_stage_dir, f'{uuid.uuid4()}.png') compressed = toolbox.compress_frame(each_frame_object.frame, **kwargs) cv2.imwrite(each_frame_path, compressed) logger.debug( f'frame [{each_frame_object.frame_id}] saved to {each_frame_path}' ) return to_dir
def __init__( self, preload: bool = None, frame_count: int = None, result_path: str = None ): # config here self.preload: bool = preload or True self.frame_count: int = frame_count or 5 # result self.classifier_result: typing.Optional[ClassifierResult] = None self.result_path: str = result_path self.result_report_path: str = "" if not self.result_path: self.result_path = toolbox.get_timestamp_str() os.makedirs(self.result_path, exist_ok=True) logger.info(f"handler config: {self.__dict__}")
def pick_and_save(self, range_list: typing.List[VideoCutRange], frame_count: int, to_dir: str = None, # in kwargs # compress_rate: float = None, # target_size: typing.Tuple[int, int] = None, # to_grey: bool = None, *args, **kwargs) -> str: """ pick some frames from range, and save them as files :param range_list: VideoCutRange list :param frame_count: default to 3, and finally you will get 3 frames for each range :param to_dir: will saved to this path :param args: :param kwargs: :return: """ stage_list = list() for index, each_range in enumerate(range_list): picked = each_range.pick(frame_count, *args, **kwargs) logger.info(f'pick {picked} in range {each_range}') stage_list.append((index, picked)) # create parent dir if not to_dir: to_dir = toolbox.get_timestamp_str() os.makedirs(to_dir, exist_ok=True) for each_stage_id, each_frame_list in stage_list: # create sub dir each_stage_dir = os.path.join(to_dir, str(each_stage_id)) os.makedirs(each_stage_dir, exist_ok=True) with toolbox.video_capture(self.video_path) as cap: for each_frame_id in each_frame_list: each_frame_path = os.path.join(each_stage_dir, f'{uuid.uuid4()}.png') each_frame = toolbox.get_frame(cap, each_frame_id - 1) each_frame = toolbox.compress_frame(each_frame, **kwargs) cv2.imwrite(each_frame_path, each_frame) logger.debug(f'frame [{each_frame_id}] saved to {each_frame_path}') return to_dir
def pick_and_save( self, range_list: typing.List[VideoCutRange], frame_count: int, to_dir: str = None, prune: float = None, meaningful_name: bool = None, # in kwargs # compress_rate: float = None, # target_size: typing.Tuple[int, int] = None, # to_grey: bool = None, *args, **kwargs, ) -> str: """ pick some frames from range, and save them as files :param range_list: VideoCutRange list :param frame_count: default to 3, and finally you will get 3 frames for each range :param to_dir: will saved to this path :param prune: float, 0-1. if set it 0.9, some stages which are too similar (ssim > 0.9) will be removed :param meaningful_name: bool, False by default. if true, image names will become meaningful (with timestamp/id or something else) :param args: :param kwargs: :return: """ stage_list = list() # build tag and get frames for index, each_range in enumerate(range_list): picked = each_range.pick(frame_count, *args, **kwargs) picked_frames = each_range.get_frames(picked) logger.info(f"pick {picked} in range {each_range}") stage_list.append((str(index), picked_frames)) # prune if prune: stage_list = self._prune(prune, stage_list) # create parent dir if not to_dir: to_dir = toolbox.get_timestamp_str() logger.debug(f"try to make dirs: {to_dir}") os.makedirs(to_dir, exist_ok=True) for each_stage_id, each_frame_list in stage_list: # create sub dir each_stage_dir = os.path.join(to_dir, str(each_stage_id)) if os.path.isdir(each_stage_dir): logger.warning(f"sub dir [{each_stage_dir}] already existed") logger.warning( "NOTICE: make sure your data will not be polluted by accident" ) os.makedirs(each_stage_dir, exist_ok=True) # create image files for each_frame_object in each_frame_list: if meaningful_name: # - video name # - frame id # - frame timestamp image_name = ( f"{os.path.basename(os.path.splitext(self.video.path)[0])}" f"_" f"{each_frame_object.frame_id}" f"_" f"{each_frame_object.timestamp}" f".png" ) else: image_name = f"{uuid.uuid4()}.png" each_frame_path = os.path.join(each_stage_dir, image_name) compressed = toolbox.compress_frame(each_frame_object.data, **kwargs) cv2.imwrite(each_frame_path, compressed) logger.debug( f"frame [{each_frame_object.frame_id}] saved to {each_frame_path}" ) return to_dir
def draw( self, data_list: typing.List[ClassifierResult], report_path: str = None, cut_result: VideoCutResult = None, language: str = None, *args, **kwargs, ): """ draw report file :param data_list: classifierResult list, output of classifier :param report_path: your report will be there :param cut_result: more charts would be built :param language: 'en' or 'zh' :return: """ # draw line = self._draw_line(data_list) bar = self._draw_bar(data_list) # merge charts page = Page() page.add(line) page.add(bar) # calc time cost cost_dict = DataUtils.calc_changing_cost(data_list) # insert pictures if cut_result: # sim chart sim_line = self._draw_sim(cut_result) page.add(sim_line) _, unstable = cut_result.get_range(*args, **kwargs) # insert thumbnail if not self.thumbnail_list: logger.debug("auto insert thumbnail ...") for each in unstable: self.add_thumbnail( f"{each.start}({each.start_time}) - {each.end}({each.end_time}), " f"duration: {each.end_time - each.start_time}", cut_result.thumbnail(each, *args, **kwargs), ) # insert stable frames stable_stage_sample = self.get_stable_stage_sample(data_list, compress_rate=0.2) stable_stage_sample = toolbox.np2b64str(stable_stage_sample) # time stamp timestamp = toolbox.get_timestamp_str() # insert extras # default: zh_cn report if not language: language = "zh" template = Template(get_template(language)) template_content = template.render( chart=Markup(page.render_embed()), dir_link_list=self.dir_link_list, thumbnail_list=self.thumbnail_list, stable_sample=stable_stage_sample, extras=self.extra_dict, background_color=BACKGROUND_COLOR, cost_dict=cost_dict, timestamp=timestamp, version_code=__VERSION__, ) # save to file if not report_path: report_path = f"{timestamp}.html" with open(report_path, "w", encoding=constants.CHARSET) as fh: fh.write(template_content) logger.info(f"save report to {report_path}")
def draw( self, classifier_result: ClassifierResult, report_path: str = None, unstable_ranges: typing.List[VideoCutRange] = None, cut_result: VideoCutResult = None, compress_rate: float = None, *_, **__, ): """ draw report file :param classifier_result: classifierResult, output of classifier :param report_path: your report will be there :param unstable_ranges: for marking unstable ranges :param cut_result: more charts would be built :param compress_rate: :return: """ # default: compress_rate if not compress_rate: compress_rate = 0.2 if not unstable_ranges: unstable_ranges = [] # draw line = self._draw_line(classifier_result) bar = self._draw_bar(classifier_result) # merge charts page = Page() page.add(line) page.add(bar) # insert pictures if cut_result: # sim chart sim_line = self._draw_sim(cut_result) page.add(sim_line) # mark range for each in unstable_ranges: classifier_result.mark_range_unstable(each.start, each.end) offset = classifier_result.get_offset() stage_range = classifier_result.get_stage_range() for cur_index in range(len(stage_range)): each = stage_range[cur_index] middle = each[len(each) // 2] if middle.is_stable(): label = self.LABEL_STABLE frame = toolbox.compress_frame(middle.get_data(), compress_rate=compress_rate) else: # todo: looks not good enough. `unspecific` looks a little weird but I have no idea now if middle.stage == constants.UNKNOWN_STAGE_FLAG: label = self.LABEL_UNSPECIFIC else: label = self.LABEL_UNSTABLE # add a frame if cur_index + 1 < len(stage_range): new_each = [*each, stage_range[cur_index + 1][0]] else: new_each = each frame = np.hstack([ toolbox.compress_frame(i.get_data(), compress_rate=compress_rate) for i in new_each ]) first, last = each[0], each[-1] self.add_thumbnail( f"{label} range {first.frame_id}({first.timestamp}) - {last.frame_id}({last.timestamp + offset}), " f"duration: {last.timestamp - first.timestamp + offset}", frame, ) # calc time cost cost_dict = classifier_result.calc_changing_cost() # time stamp timestamp = toolbox.get_timestamp_str() # video self.add_extra("video path", classifier_result.video_path) self.add_extra("frame count", str(classifier_result.get_length())) self.add_extra("offset between frames", str(classifier_result.get_offset())) # insert extras template = Template(get_template()) template_content = template.render( chart=Markup(page.render_embed()), thumbnail_list=self.thumbnail_list, extras=self.extra_dict, background_color=constants.BACKGROUND_COLOR, cost_dict=cost_dict, timestamp=timestamp, version_code=__VERSION__, ) # default: write to current dir default_name = f"{timestamp}.html" if not report_path: report_path = default_name # somewhere specific # existed dir? elif os.path.isdir(report_path): report_path = os.path.join(report_path, default_name) logger.debug(f"trying to save report to {report_path}") # write file with open(report_path, "w", encoding=constants.CHARSET) as fh: fh.write(template_content) logger.info(f"save report to {report_path}")
def draw( self, classifier_result: ClassifierResult, report_path: str = None, unstable_ranges: typing.List[VideoCutRange] = None, cut_result: VideoCutResult = None, compress_rate: float = None, *_, **__, ): """ draw report file :param classifier_result: classifierResult, output of classifier :param report_path: your report will be there :param unstable_ranges: for marking unstable ranges :param cut_result: more charts would be built :param compress_rate: :return: """ # default: compress_rate if not compress_rate: compress_rate = 0.2 if not unstable_ranges: unstable_ranges = [] # draw line = self._draw_line(classifier_result) bar = self._draw_bar(classifier_result) # merge charts page = Page() page.add(line) page.add(bar) # insert pictures if cut_result: # sim chart sim_line = self._draw_sim(cut_result) page.add(sim_line) # mark range for each in unstable_ranges: classifier_result.mark_range_unstable(each.start, each.end) offset = classifier_result.get_offset() for each in classifier_result.get_stage_range(): middle = each[len(each) // 2] if middle.is_stable(): label = "stable" frame = toolbox.compress_frame(middle.get_data(), compress_rate=compress_rate) else: label = "unstable" frame = np.hstack([ toolbox.compress_frame(i.get_data(), compress_rate=compress_rate) for i in each ]) first, last = each[0], each[-1] self.add_thumbnail( f"{label} range {first.frame_id}({first.timestamp - offset}) - {last.frame_id}({last.timestamp}), " f"duration: {last.timestamp - first.timestamp + offset}", frame, ) # calc time cost cost_dict = classifier_result.calc_changing_cost() # time stamp timestamp = toolbox.get_timestamp_str() # video self.add_extra("video path", classifier_result.video_path) self.add_extra("frame count", str(classifier_result.get_length())) self.add_extra("offset between frames", str(classifier_result.get_offset())) # insert extras template = Template(get_template()) template_content = template.render( chart=Markup(page.render_embed()), thumbnail_list=self.thumbnail_list, extras=self.extra_dict, background_color=constants.BACKGROUND_COLOR, cost_dict=cost_dict, timestamp=timestamp, version_code=__VERSION__, ) # save to file if not report_path: report_path = f"{timestamp}.html" with open(report_path, "w", encoding=constants.CHARSET) as fh: fh.write(template_content) logger.info(f"save report to {report_path}")