コード例 #1
0
    def handle(self, video_path: str) -> bool:
        super(KerasHandler, self).handle(video_path)
        video = VideoObject(video_path)
        if self.preload:
            video.load_frames()

        # --- cutter ---
        cutter = VideoCutter()
        res = cutter.cut(video)
        stable, unstable = res.get_range(threshold=0.98, offset=3)

        # --- classify ---
        cl = KerasClassifier()
        if self.model_path:
            logger.info("load existed pre-train model")
            cl.load_model(self.model_path)
        else:
            data_home = res.pick_and_save(stable,
                                          self.frame_count,
                                          to_dir=self.result_path)
            cl.train(data_home)
        self.classifier_result = cl.classify(video, stable)

        # --- draw ---
        r = Reporter()
        r.draw(self.classifier_result, report_path=self.result_report_path)
        return True
コード例 #2
0
def test_boost():
    video = VideoObject(VIDEO_PATH)
    video.load_frames()
    # test cut
    res, data_home = _cut(video)

    # test classify
    classify_result = _classify(video, data_home)

    # --- draw ---
    r = Reporter()
    r.draw(
        classify_result,
        report_path=os.path.join(data_home, "report.html"),
        cut_result=res,
    )

    # test compressing
    r = Reporter()
    r.draw(
        classify_result,
        report_path=os.path.join(data_home, "report.html"),
        cut_result=res,
        compress_rate=0.1,
    )

    r = Reporter()
    r.draw(
        classify_result,
        report_path=os.path.join(data_home, "report.html"),
        cut_result=res,
        target_size=(600, 800),
    )
コード例 #3
0
ファイル: test_video.py プロジェクト: zhanghongfeng/stagesepx
def test_read_from_mem():
    v = VideoObject(VIDEO_PATH)
    v.load_frames()
    count = 0
    for f in v:
        assert isinstance(f, VideoFrame)
        count += 1
    assert count == 30
コード例 #4
0
ファイル: test_video.py プロジェクト: williamfzc/stagesepx
def test_read_from_mem():
    v = VideoObject(VIDEO_PATH)
    print(str(v))
    v.load_frames()
    count = 0
    for f in v:
        assert isinstance(f, VideoFrame)
        print(str(f))
        count += 1
    assert count == 30

    v.clean_frames()
    assert not v.data
コード例 #5
0
def test_boost():
    video = VideoObject(VIDEO_PATH)
    video.load_frames()
    # test cut
    res, data_home = _cut(video)

    # test classify
    classify_result = _classify(video, data_home)

    # --- draw ---
    r = Reporter()
    r.draw(
        classify_result,
        report_path=os.path.join(data_home, "report.html"),
        cut_result=res,
    )
コード例 #6
0
    def handle(self, video_path: str) -> bool:
        super(NormalHandler, self).handle(video_path)
        video = VideoObject(video_path)
        if self.preload:
            video.load_frames()

        # --- cutter ---
        cutter = VideoCutter()
        res = cutter.cut(video)
        stable, unstable = res.get_range(threshold=0.98, offset=3)
        data_home = res.pick_and_save(stable,
                                      self.frame_count,
                                      to_dir=self.result_path)

        # --- classify ---
        cl = SVMClassifier()
        cl.load(data_home)
        cl.train()
        self.classifier_result = cl.classify(video, stable)

        # --- draw ---
        r = Reporter()
        r.draw(self.classifier_result, report_path=self.result_report_path)
        return True
コード例 #7
0
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)
コード例 #8
0
ファイル: test_video.py プロジェクト: williamfzc/stagesepx
def test_preload_with_hook():
    v = VideoObject(VIDEO_PATH)
    hook = ExampleHook()
    v.add_preload_hook(hook)
    v.load_frames()
コード例 #9
0
ファイル: test_video.py プロジェクト: williamfzc/stagesepx
def test_contain_image():
    v = VideoObject(VIDEO_PATH)
    v.load_frames()
    ret = v.data[0].contain_image(image_path=IMAGE_PATH)
    assert ret["ok"]
コード例 #10
0
ファイル: test_video.py プロジェクト: williamfzc/stagesepx
def test_convert_first():
    v = VideoObject(VIDEO_PATH, fps=30)
    v.load_frames()
    assert len(v.data) == 36
コード例 #11
0
def get_range(_train_or_forecast, _forecast_file_name, _param, _picture_path):
    file_name = re.search(r'\\(.*).mp4', str(_forecast_file_name),
                          re.M | re.I).group(1)
    video = VideoObject(_forecast_file_name, pre_load=True)
    # 新建帧,计算视频总共有多少帧,每帧多少ms
    video.load_frames()
    # 直接切割视频
    # 压缩视频
    cutter = VideoCutter(compress_rate=_param[0])

    # 添加Hook
    '''
    convert_size_and_offset:1zai 51 - origin size: ((468, 216)) 显示的是整张图的尺寸,坐标原点在左上角。
    468是高,216是宽。(468, 216)是整个画面的右下角
    stagesepx.hook:convert_size_and_offset:153 - size: (23.4, 216) 显示的是被屏蔽或者是被选择的区域的高和宽。高是23.4,宽是216。
    convert_size_and_offset:160。final range h: (0, 23), w: (0, 216) 显示的是被屏蔽或者是被选择的区域。
    从左上角开始计算,屏蔽掉0到23高度,整个宽度的区域。
    
    如果要统计竖屏切换到横屏场景的耗时。为了防止录屏文件中,横屏后的"五日分数图"内容被压缩的太小,建议在录屏时选择横屏录制。
    横屏录制场景下,某一帧图像中,App的内容处于竖屏状态下时,该帧图像表现为,左边一部分黑屏,中间是手机竖屏页面,右边还是一部分黑屏。
    此时如果要屏蔽或者选择某一块区域,要把黑色区域的尺寸也算在内。
    如果只通过中间部分有手机内容图像的尺寸,来选择或者屏蔽部分区域,则会出现被选择的区域和预期不符的情况。
    
    # 屏蔽盘口数据 size=(0.1, 1),
    # 屏蔽手机导航栏 size=(0.05, 1),
    '''
    '''
    # 屏蔽帧的右边
    hook_ignore1 = IgnoreHook(
        size=(1, 1),
        offset=(0, 0.55),
        overwrite=True,
    )
    # 屏蔽帧的左边
    hook_ignore2 = IgnoreHook(
        size=(1, 0.45),
        overwrite=True,
    )
    # 屏蔽帧的上边
    hook_ignore3 = IgnoreHook(
        size=(0.35, 1),
        overwrite=True,
    )
    # 屏蔽帧的下边
    hook_ignore4 = IgnoreHook(
        size=(1, 1),
        offset=(0.6, 0),
        overwrite=True,
    )
    hook_crop = CropHook(
        size=(0.2, 0.2),
        overwrite=True,
    )
    '''

    # cutter.add_hook(hook_ignore1)
    # cutter.add_hook(hook_ignore2)
    # cutter.add_hook(hook_ignore3)
    # cutter.add_hook(hook_ignore4)
    # cutter.add_hook(hook_crop)
    # hook_save_frame = FrameSaveHook('../frame_save_dir')
    # cutter.add_hook(hook_save_frame)

    # 计算每一帧视频的每一个block的ssim和psnr。block=4则算16个part的得分
    # res = cutter.cut(video)
    res = cutter.cut(video, block=_param[3])
    # 计算出哪些区间是稳定的,哪些是不稳定的。判断A帧到B帧之间是稳定还是不稳定
    # 是不是在这里就决定,把稳定的A到B帧,放到一个文件夹。如果offset大,就扩大A到B的间隔。
    stable, unstable = res.get_range(threshold=_param[1], offset=_param[2])
    # stable, unstable = res.get_range(threshold=_param[1], offset=_param[2], limit=5,)
    # stable, unstable = res.get_range(threshold=_param[1], offset=_param[2], psnr_threshold=0.85)
    # stable, unstable = res.get_range(threshold=_param[1])
    if _train_or_forecast == 'train':
        print("pick_and_save")
        # 把分好类的稳定阶段的图片存本地
        res.pick_and_save(stable,
                          20,
                          to_dir=_picture_path + '/train_stable_' + file_name,
                          meaningful_name=True)
        # 把分好类的不稳定阶段的图片存本地
        res.pick_and_save(unstable,
                          40,
                          to_dir=_picture_path + '/train_unstable_' +
                          file_name,
                          meaningful_name=True)
    else:
        res.pick_and_save(stable,
                          20,
                          to_dir=_picture_path + '/forecast_stable_' +
                          file_name,
                          meaningful_name=True)
        # 把分好类的不稳定阶段的图片存本地
        res.pick_and_save(unstable,
                          40,
                          to_dir=_picture_path + '/forecast_unstable_' +
                          file_name,
                          meaningful_name=True)

    return stable
コード例 #12
0
ファイル: test_video.py プロジェクト: Hanlen520/stagesepx
def test_sync_timestamp():
    v = VideoObject(VIDEO_PATH)
    v.load_frames()
    v.sync_timestamp()
コード例 #13
0
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,
    )