from stagesepx.cutter import VideoCutter from stagesepx.classifier import SVMClassifier from stagesepx.reporter import Reporter from stagesepx.video import VideoObject video_path = "../videos/long.mp4" video = VideoObject(video_path) video.load_frames() # --- cutter --- cutter = VideoCutter() res = cutter.cut(video) stable, unstable = res.get_range() data_home = res.pick_and_save(stable, 5) # --- classify --- cl = SVMClassifier(compress_rate=0.4) cl.load(data_home) cl.train() classify_result = cl.classify(video, stable, keep_data=True) result_dict = classify_result.to_dict() # --- draw --- r = Reporter() r.draw(classify_result)
from stagesepx.classifier import SVMClassifier from stagesepx.reporter import Reporter # 默认情况下使用 HoG 进行特征提取 # 你可以将其关闭从而直接对原始图片进行训练与测试:feature_type='raw' cl = SVMClassifier(feature_type='hog') # 基本与SSIM分类器的流程一致 # 但它对数据的要求可能有所差别,具体参见 cut.py 中的描述 data_home = './cut_result' cl.load(data_home) # 在加载数据完成之后需要先训练 cl.train() # # 在训练后你可以把模型保存起来 # cl.save_model('model.pkl') # # 或者直接读取已经训练好的模型 # cl.load_model('model.pkl') # 开始分类 res = cl.classify( '../test.mp4', # 步长,可以自行设置用于平衡效率与颗粒度 # 默认为1,即每帧都检测 step=1, ) # 为了更方便的可读性,stagesepx已经内置了图表绘制功能 # 你可以直接把分析结果绘制成图表 report = Reporter()
from stagesepx.reporter import Reporter video_list = [ '../test.mp4', # 把别的视频也配置在这里即可 ] for each_video_path in video_list: cutter = VideoCutter() res = cutter.cut(each_video_path) stable = res.get_stable_range() data_home = res.pick_and_save(stable, 3) print(stable) # classify cl = SVMClassifier() cl.load(data_home) cl.train() # 注意,如果在classify方法指定了范围 # 那么分析时只会分析处于范围内的帧! # 例如,这里只传入了stable的范围,那么非stable范围内的帧都会被忽略掉,标记为 -1 res = cl.classify( each_video_path, stable, # 步长,可以自行设置用于平衡效率与颗粒度 # 默认为1,即每帧都检测 step=1) # draw Reporter.draw(
# 采样结果保存的位置 # 不指定的话则会在当前位置生成文件夹并返回它的路径 # './cut_result', # prune被用于去除重复阶段(>=0.4.4) # float(0-1.0),设置为0.9时,如果两个stage相似度超过0.9,他们会合并成一个类别 prune=None, ) # --- classify --- cl = SVMClassifier( # 默认情况下使用 HoG 进行特征提取 # 你可以将其关闭从而直接对原始图片进行训练与测试:feature_type='raw' feature_type='hog', # 默认为0.2,即将图片缩放为0.2倍 # 主要为了提高计算效率 # 如果你担心影响分析效果,可以将其提高 compress_rate=0.2, ) # 加载数据 cl.load(data_home) # 在加载数据完成之后需要先训练 cl.train() # 在训练后你可以把模型保存起来 # cl.save_model('model.pkl') # 或者直接读取已经训练好的模型 # cl.load_model('model.pkl') # 注意,如果在classify方法指定了范围
""" 这是一个最小化的 stagesepx 使用例子 每一行的注释均可以在 cut_and_classify.py 中找到 """ from stagesepx.cutter import VideoCutter from stagesepx.classifier import SVMClassifier from stagesepx.reporter import Reporter from stagesepx.video import VideoObject video_path = "../demo.mp4" video = VideoObject(video_path) video.load_frames() # --- cutter --- cutter = VideoCutter() res = cutter.cut(video) stable, unstable = res.get_range() data_home = res.pick_and_save(stable, 5) # --- classify --- cl = SVMClassifier() cl.load(data_home) cl.train() classify_result = cl.classify(video, stable) # --- draw --- r = Reporter() r.draw(classify_result)
""" 这个例子描述了如何训练一个后续可用的模型 在 cut 流程之后,你应该能得到一个已经分拣好的训练集文件夹 我们将基于此文件夹进行模型的训练 """ from stagesepx.classifier import SVMClassifier DATA_HOME = './cut_result' cl = SVMClassifier() # 加载数据 cl.load(DATA_HOME) # 在加载数据完成之后需要先训练 cl.train() # 在训练后你可以把模型保存起来 cl.save_model('model.pkl')
# 采样结果保存的位置 # 不指定的话则会在当前位置生成文件夹并返回它的路径 # './cut_result', # prune被用于去除重复阶段(>=0.4.4) # float(0-1.0),设置为0.9时,如果两个stage相似度超过0.9,他们会合并成一个类别 prune=None, ) # --- classify --- cl = SVMClassifier( # 默认情况下使用 HoG 进行特征提取 # 你可以将其关闭从而直接对原始图片进行训练与测试:feature_type='raw' feature_type="hog", # 默认为0.2,即将图片缩放为0.2倍 # 主要为了提高计算效率 # 如果你担心影响分析效果,可以将其提高 compress_rate=0.2, # 或者直接指定尺寸 # 当压缩率与指定尺寸同时传入时,优先以指定尺寸为准 # target_size=(200, 400), ) # 加载数据 cl.load(data_home) # 在加载数据完成之后需要先训练 cl.train() # 在训练后你可以把模型保存起来 # cl.save_model('model.pkl') # 或者直接读取已经训练好的模型 # cl.load_model('model.pkl')
def test_boost(): cl = SVMClassifier() cl.load_model(MODEL_PATH) classify_result = cl.classify(VIDEO_PATH, boost_mode=True) assert classify_result
from stagesepx.cutter import VideoCutter from stagesepx.classifier import SVMClassifier from stagesepx.video import VideoObject video_path = "../videos/short.mp4" video = VideoObject(video_path) video.load_frames() # --- cutter --- cutter = VideoCutter() res = cutter.cut(video) stable, unstable = res.get_range() data_home = res.pick_and_save(stable, 5) # --- classify --- cl = SVMClassifier() cl.load("2020011600164596") cl.train() classify_result = cl.classify(video, stable, keep_data=True) result_dict = classify_result.to_dict() import pprint pprint.pprint(result_dict)
from stagesepx.classifier import SVMClassifier from stagesepx.cutter import VideoCutter from stagesepx.reporter import Reporter TARGET_VIDEO = '../../demo.mp4' # cut # 这里依旧使用了 cut,主要目的还是为了可以比较好的处理变化中的过程 # 但这次我们不需要用到 pick_and_save,因为这次 classifier 不会使用 cutter 的数据 cutter = VideoCutter() res = cutter.cut(TARGET_VIDEO) stable, _ = res.get_range() # classify # 这里的参数需要保持与train.py一致,如果你有改动的话 cl = SVMClassifier() cl.load_model('./model.pkl') classify_result = cl.classify( TARGET_VIDEO, stable, ) r = Reporter() r.draw( classify_result, report_path='report.html', cut_result=res, )
def run(config: typing.Union[dict, str]): """ run with config :param config: config file path, or a preload dict :return: """ class _VideoUserConfig(BaseModel): path: str pre_load: bool = True fps: int = None class _CutterUserConfig(BaseModel): threshold: float = None frame_count: int = None offset: int = None limit: int = None block: int = None # common compress_rate: float = None target_size: typing.Tuple[int, int] = None class _ClassifierType(Enum): SVM = "svm" KERAS = "keras" class _ClassifierUserConfig(BaseModel): boost_mode: bool = None classifier_type: _ClassifierType = _ClassifierType.SVM model: str = None # common compress_rate: float = None target_size: typing.Tuple[int, int] = None class _CalcOperatorType(Enum): BETWEEN = "between" DISPLAY = "display" class _CalcOperator(BaseModel): name: str calc_type: _CalcOperatorType args: dict = dict() class _CalcUserConfig(BaseModel): output: str = None ignore_error: bool = None operators: typing.List[_CalcOperator] = None class _ExtraUserConfig(BaseModel): save_train_set: str = None class UserConfig(BaseModel): output: str video: _VideoUserConfig cutter: _CutterUserConfig = _CutterUserConfig() classifier: _ClassifierUserConfig = _ClassifierUserConfig() calc: _CalcUserConfig = _CalcUserConfig() extras: _ExtraUserConfig = _ExtraUserConfig() if isinstance(config, str): # path config_path = pathlib.Path(config) assert config_path.is_file(), f"no config file found in {config_path}" # todo: support different types in the future assert config_path.as_posix().endswith( ".json"), "config file should be json format" with open(config_path, encoding=constants.CHARSET) as f: config = json.load(f) config = UserConfig(**config) logger.info(f"config: {config}") # main flow video = VideoObject( # fmt: off path=config.video.path, fps=config.video.fps, ) if config.video.pre_load: video.load_frames() # cut cutter = VideoCutter( # fmt: off compress_rate=config.cutter.compress_rate, target_size=config.cutter.target_size, ) res = cutter.cut( # fmt: off video=video, block=config.cutter.block, ) stable, unstable = res.get_range( # fmt: off threshold=config.cutter.threshold, offset=config.cutter.offset, ) with tempfile.TemporaryDirectory() as temp_dir: # classify if config.classifier.classifier_type is _ClassifierType.SVM: cl = SVMClassifier( # fmt: off compress_rate=config.classifier.compress_rate, target_size=config.classifier.target_size, ) elif config.classifier.classifier_type is _ClassifierType.KERAS: from stagesepx.classifier.keras import KerasClassifier cl = KerasClassifier( # fmt: off compress_rate=config.classifier.compress_rate, target_size=config.classifier.target_size, ) # validation has been applied by pydantic # so no `else` if config.classifier.model: # no need to retrain model_path = pathlib.Path(config.classifier.model) assert model_path.is_file(), f"file {model_path} not existed" cl.load_model(model_path) else: # train a new model train_set_dir = config.extras.save_train_set or temp_dir os.makedirs(train_set_dir, exist_ok=True) res.pick_and_save( # fmt: off stable, frame_count=config.cutter.frame_count, to_dir=train_set_dir, ) cl.train(data_path=train_set_dir) # start classifying classify_result = cl.classify( # fmt: off video, stable, boost_mode=config.classifier.boost_mode, ) # calc def _calc_display() -> dict: # jsonify return json.loads(classify_result.dumps()) def _calc_between(*, from_stage: str = None, to_stage: str = None) -> dict: assert classify_result.contain( from_stage), f"no stage {from_stage} found in result" assert classify_result.contain( to_stage), f"no stage {to_stage} found in result" from_frame = classify_result.last(from_stage) to_frame = classify_result.first(to_stage) cost = to_frame.timestamp - from_frame.timestamp return { "from": from_frame.frame_id, "to": to_frame.frame_id, "cost": cost, } _calc_func_dict = { _CalcOperatorType.BETWEEN: _calc_between, _CalcOperatorType.DISPLAY: _calc_display, } calc_output = config.calc.output if calc_output: output_path = pathlib.Path(calc_output) assert not output_path.is_file(), f"file {output_path} already existed" result = [] for each_calc in config.calc.operators: func = _calc_func_dict[each_calc.calc_type] try: func_ret = func(**each_calc.args) except Exception as e: if not config.calc.ignore_error: raise logger.warning(e) func_ret = traceback.format_exc() calc_ret = { "name": each_calc.name, "type": each_calc.calc_type.value, "result": func_ret, } result.append(calc_ret) with open(output_path, "w", encoding=constants.CHARSET) as f: json.dump(result, f) # draw r = Reporter() r.draw( # fmt: off classify_result, report_path=config.output, )
from stagesepx.cutter import VideoCutter from stagesepx.classifier import SVMClassifier import pprint video_path = '../0866.mp4' another_video_path = '../0867.mp4' cutter = VideoCutter() res = cutter.cut(video_path, compress_rate=0.1) res1 = cutter.cut(another_video_path, compress_rate=0.1) stable, _ = res.get_range(limit=3) stable1, _ = res1.get_range(limit=3) data_path = res.pick_and_save(stable, 3) data_path1 = res1.pick_and_save(stable1, 3) cl = SVMClassifier() cl1 = SVMClassifier() cl.load(data_path) cl1.load(data_path1) pprint.pprint(cl.diff(cl1))