diff --git a/libvmaf/src/feature/feature_extractor.c b/libvmaf/src/feature/feature_extractor.c index 3546a76b5..c27de1356 100644 --- a/libvmaf/src/feature/feature_extractor.c +++ b/libvmaf/src/feature/feature_extractor.c @@ -30,9 +30,9 @@ extern VmafFeatureExtractor vmaf_fex_float_psnr; extern VmafFeatureExtractor vmaf_fex_float_ansnr; extern VmafFeatureExtractor vmaf_fex_float_adm; extern VmafFeatureExtractor vmaf_fex_float_vif; -extern VmafFeatureExtractor vmaf_fex_integer_motion; +extern VmafFeatureExtractor vmaf_fex_integer_motion2; extern VmafFeatureExtractor vmaf_fex_integer_vif; -extern VmafFeatureExtractor vmaf_fex_float_motion; +extern VmafFeatureExtractor vmaf_fex_float_motion2; extern VmafFeatureExtractor vmaf_fex_float_ms_ssim; extern VmafFeatureExtractor vmaf_fex_float_moment; @@ -44,9 +44,9 @@ static VmafFeatureExtractor *feature_extractor_list[] = { &vmaf_fex_float_ansnr, &vmaf_fex_float_adm, &vmaf_fex_float_vif, - &vmaf_fex_integer_motion, + &vmaf_fex_integer_motion2, &vmaf_fex_integer_vif, - &vmaf_fex_float_motion, + &vmaf_fex_float_motion2, &vmaf_fex_float_ms_ssim, &vmaf_fex_float_moment, NULL diff --git a/libvmaf/src/feature/float_motion.c b/libvmaf/src/feature/float_motion2.c similarity index 98% rename from libvmaf/src/feature/float_motion.c rename to libvmaf/src/feature/float_motion2.c index 7dd45f3ce..34146620b 100644 --- a/libvmaf/src/feature/float_motion.c +++ b/libvmaf/src/feature/float_motion2.c @@ -139,8 +139,8 @@ static const char *provided_features[] = { NULL }; -VmafFeatureExtractor vmaf_fex_float_motion = { - .name = "float_motion", +VmafFeatureExtractor vmaf_fex_float_motion2 = { + .name = "float_motion2", .init = init, .extract = extract, .flush = flush, diff --git a/libvmaf/src/feature/integer_motion.c b/libvmaf/src/feature/integer_motion2.c similarity index 99% rename from libvmaf/src/feature/integer_motion.c rename to libvmaf/src/feature/integer_motion2.c index 1b524a1c5..fce5a5ad9 100644 --- a/libvmaf/src/feature/integer_motion.c +++ b/libvmaf/src/feature/integer_motion2.c @@ -345,8 +345,8 @@ static const char *provided_features[] = { NULL }; -VmafFeatureExtractor vmaf_fex_integer_motion = { - .name = "motion", +VmafFeatureExtractor vmaf_fex_integer_motion2 = { + .name = "motion2", .init = init, .extract = extract, .flush = flush, diff --git a/libvmaf/src/meson.build b/libvmaf/src/meson.build index 0fe942342..6f408977b 100644 --- a/libvmaf/src/meson.build +++ b/libvmaf/src/meson.build @@ -164,8 +164,8 @@ libvmaf_rc_feature_sources = [ feature_src_dir + 'feature_collector.c', feature_src_dir + 'float_psnr.c', feature_src_dir + 'float_ansnr.c', - feature_src_dir + 'integer_motion.c', - feature_src_dir + 'float_motion.c', + feature_src_dir + 'integer_motion2.c', + feature_src_dir + 'float_motion2.c', feature_src_dir + 'float_ssim.c', feature_src_dir + 'float_ms_ssim.c', feature_src_dir + 'float_vif.c', diff --git a/libvmaf/test/test_feature_extractor.c b/libvmaf/test/test_feature_extractor.c index cd305948e..71b959d96 100644 --- a/libvmaf/test/test_feature_extractor.c +++ b/libvmaf/test/test_feature_extractor.c @@ -87,9 +87,9 @@ static char *test_feature_extractor_flush() int err = 0; VmafFeatureExtractor *fex; - fex = vmaf_get_feature_extractor_by_name("float_motion"); + fex = vmaf_get_feature_extractor_by_name("float_motion2"); mu_assert("problem vmaf_get_feature_extractor_by_name", - !strcmp(fex->name, "float_motion")); + !strcmp(fex->name, "float_motion2")); VmafFeatureExtractorContext *fex_ctx; err = vmaf_feature_extractor_context_create(&fex_ctx, fex, NULL); mu_assert("problem during vmaf_feature_extractor_context_create", !err); diff --git a/python/test/vmafrc_feature_extractor_test.py b/python/test/vmafrc_feature_extractor_test.py new file mode 100644 index 000000000..ad280c742 --- /dev/null +++ b/python/test/vmafrc_feature_extractor_test.py @@ -0,0 +1,105 @@ +import unittest + +from test.testutil import set_default_576_324_videos_for_testing + +from vmaf.core.vmafrc_feature_extractor import FloatMotion2FeatureExtractor, IntegerMotion2FeatureExtractor, FloatVifFeatureExtractor, FloatAdmFeatureExtractor, IntegerVifFeatureExtractor, IntegerPsnrFeatureExtractor + + +class FeatureExtractorTest(unittest.TestCase): + + def tearDown(self): + if hasattr(self, 'fextractor'): + self.fextractor.remove_results() + pass + + def test_run_float_motion2_fextractor(self): + ref_path, dis_path, asset, asset_original = set_default_576_324_videos_for_testing() + self.fextractor = FloatMotion2FeatureExtractor( + [asset, asset_original], + None, fifo_mode=False, + result_store=None + ) + self.fextractor.run() + results = self.fextractor.results + self.assertAlmostEqual(results[0]['float_motion2_feature_motion2_score'], 3.8953518541666665, places=8) + self.assertAlmostEqual(results[1]['float_motion2_feature_motion2_score'], 3.8953518541666665, places=8) + + def test_run_integer_motion2_fextractor(self): + ref_path, dis_path, asset, asset_original = set_default_576_324_videos_for_testing() + self.fextractor = IntegerMotion2FeatureExtractor( + [asset, asset_original], + None, fifo_mode=False, + result_store=None + ) + self.fextractor.run() + results = self.fextractor.results + self.assertAlmostEqual(results[0]['integer_motion2_feature_motion2_score'], 3.895345229166667, places=8) + self.assertAlmostEqual(results[1]['integer_motion2_feature_motion2_score'], 3.895345229166667, places=8) + + def test_run_float_vif_fextractor(self): + ref_path, dis_path, asset, asset_original = set_default_576_324_videos_for_testing() + self.fextractor = FloatVifFeatureExtractor( + [asset, asset_original], + None, fifo_mode=False, + result_store=None + ) + self.fextractor.run() + results = self.fextractor.results + self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale0_score'], 0.3634208125, places=6) + self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale1_score'], 0.7666474166666667, places=6) + self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale2_score'], 0.8628533333333334, places=6) + self.assertAlmostEqual(results[0]['float_VIF_feature_vif_scale3_score'], 0.9159719583333334, places=6) + self.assertAlmostEqual(results[1]['float_VIF_feature_vif_scale0_score'], 1.0, places=6) + self.assertAlmostEqual(results[1]['float_VIF_feature_vif_scale1_score'], 1.0, places=6) + self.assertAlmostEqual(results[1]['float_VIF_feature_vif_scale2_score'], 1.0, places=6) + self.assertAlmostEqual(results[1]['float_VIF_feature_vif_scale3_score'], 1.0, places=5) + + def test_run_integer_vif_fextractor(self): + ref_path, dis_path, asset, asset_original = set_default_576_324_videos_for_testing() + self.fextractor = IntegerVifFeatureExtractor( + [asset, asset_original], + None, fifo_mode=False, + result_store=None + ) + self.fextractor.run() + results = self.fextractor.results + self.assertAlmostEqual(results[0]['integer_VIF_feature_vif_scale0_score'], 0.3634233125, places=6) + self.assertAlmostEqual(results[0]['integer_VIF_feature_vif_scale1_score'], 0.7666542916666667, places=6) + self.assertAlmostEqual(results[0]['integer_VIF_feature_vif_scale2_score'], 0.862872125, places=6) + self.assertAlmostEqual(results[0]['integer_VIF_feature_vif_scale3_score'], 0.9159956249999999, places=6) + self.assertAlmostEqual(results[1]['integer_VIF_feature_vif_scale0_score'], 1.000002, places=6) + self.assertAlmostEqual(results[1]['integer_VIF_feature_vif_scale1_score'], 1.0000023541666667, places=6) + self.assertAlmostEqual(results[1]['integer_VIF_feature_vif_scale2_score'], 1.0000022916666667, places=6) + self.assertAlmostEqual(results[1]['integer_VIF_feature_vif_scale3_score'], 1.0, places=5) + + def test_run_float_adm_fextractor(self): + ref_path, dis_path, asset, asset_original = set_default_576_324_videos_for_testing() + self.fextractor = FloatAdmFeatureExtractor( + [asset, asset_original], + None, fifo_mode=False, + result_store=None + ) + self.fextractor.run() + results = self.fextractor.results + self.assertAlmostEqual(results[0]['float_ADM_feature_adm2_score'], 0.9345877291666667, places=6) + self.assertAlmostEqual(results[1]['float_ADM_feature_adm2_score'], 1.0, places=6) + + def test_run_integer_psnr_fextractor(self): + ref_path, dis_path, asset, asset_original = set_default_576_324_videos_for_testing() + self.fextractor = IntegerPsnrFeatureExtractor( + [asset, asset_original], + None, fifo_mode=False, + result_store=None + ) + self.fextractor.run() + results = self.fextractor.results + self.assertAlmostEqual(results[0]['integer_PSNR_feature_psnr_y_score'], 30.755063979166664, places=4) + self.assertAlmostEqual(results[0]['integer_PSNR_feature_psnr_cb_score'], 38.4494410625, places=4) + self.assertAlmostEqual(results[0]['integer_PSNR_feature_psnr_cr_score'], 40.99191027083334, places=4) + self.assertAlmostEqual(results[1]['integer_PSNR_feature_psnr_y_score'], 60.0, places=4) + self.assertAlmostEqual(results[1]['integer_PSNR_feature_psnr_cb_score'], 60.0, places=4) + self.assertAlmostEqual(results[1]['integer_PSNR_feature_psnr_cr_score'], 60.0, places=4) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/python/vmaf/__init__.py b/python/vmaf/__init__.py index 341bd647d..d54624e02 100644 --- a/python/vmaf/__init__.py +++ b/python/vmaf/__init__.py @@ -111,7 +111,7 @@ def call_vmafrc_multi_features(features, yuv_type, ref_path, dis_path, w, h, log # --reference python/test/resource/yuv/src01_hrc00_576x324.yuv # --distorted python/test/resource/yuv/src01_hrc01_576x324.yuv # --width 576 --height 324 --pixel_format 420 --bitdepth 8 - # --output /dev/stdout --xml --no_prediction --feature float_motion --feature integer_motion + # --output /dev/stdout --xml --no_prediction --feature float_motion2 --feature integer_motion2 pixel_format, bitdepth = convert_pixel_format_ffmpeg2vmafrc(yuv_type) diff --git a/python/vmaf/core/vmafrc_feature_extractor.py b/python/vmaf/core/vmafrc_feature_extractor.py new file mode 100644 index 000000000..7491bbb69 --- /dev/null +++ b/python/vmaf/core/vmafrc_feature_extractor.py @@ -0,0 +1,181 @@ +from vmaf import ExternalProgramCaller +from vmaf.core.feature_extractor import VmafrcFeatureExtractorMixin, FeatureExtractor + + +class FloatMotion2FeatureExtractor(VmafrcFeatureExtractorMixin, FeatureExtractor): + + TYPE = "float_motion2_feature" + VERSION = "1.0" + + ATOM_FEATURES = ['motion2'] + + ATOM_FEATURES_TO_VMAFRC_KEY_DICT = { + 'motion2': 'motion2', + } + + def _generate_result(self, asset): + # routine to call the command-line executable and generate quality + # scores in the log file. + + quality_width, quality_height = asset.quality_width_height + log_file_path = self._get_log_file_path(asset) + + yuv_type=self._get_workfile_yuv_type(asset) + ref_path=asset.ref_procfile_path + dis_path=asset.dis_procfile_path + w=quality_width + h=quality_height + logger = self.logger + + ExternalProgramCaller.call_vmafrc_single_feature('float_motion2', yuv_type, ref_path, dis_path, w, h, log_file_path, logger) + + +class IntegerMotion2FeatureExtractor(VmafrcFeatureExtractorMixin, FeatureExtractor): + + TYPE = "integer_motion2_feature" + VERSION = "1.0" + + ATOM_FEATURES = ['motion2'] + + ATOM_FEATURES_TO_VMAFRC_KEY_DICT = { + 'motion2': 'integer_motion2', + } + + def _generate_result(self, asset): + # routine to call the command-line executable and generate quality + # scores in the log file. + + quality_width, quality_height = asset.quality_width_height + log_file_path = self._get_log_file_path(asset) + + yuv_type=self._get_workfile_yuv_type(asset) + ref_path=asset.ref_procfile_path + dis_path=asset.dis_procfile_path + w=quality_width + h=quality_height + logger = self.logger + + ExternalProgramCaller.call_vmafrc_single_feature('motion2', yuv_type, ref_path, dis_path, w, h, log_file_path, logger) + + +class FloatVifFeatureExtractor(VmafrcFeatureExtractorMixin, FeatureExtractor): + + TYPE = "float_VIF_feature" + VERSION = "1.0" + + ATOM_FEATURES = ['vif_scale0', 'vif_scale1', 'vif_scale2', 'vif_scale3', + ] + + ATOM_FEATURES_TO_VMAFRC_KEY_DICT = { + 'vif_scale0': 'vif_scale0', + 'vif_scale1': 'vif_scale1', + 'vif_scale2': 'vif_scale2', + 'vif_scale3': 'vif_scale3', + } + + def _generate_result(self, asset): + # routine to call the command-line executable and generate quality + # scores in the log file. + + quality_width, quality_height = asset.quality_width_height + log_file_path = self._get_log_file_path(asset) + + yuv_type=self._get_workfile_yuv_type(asset) + ref_path=asset.ref_procfile_path + dis_path=asset.dis_procfile_path + w=quality_width + h=quality_height + logger = self.logger + + ExternalProgramCaller.call_vmafrc_single_feature('float_vif', yuv_type, ref_path, dis_path, w, h, log_file_path, logger) + + +class IntegerVifFeatureExtractor(VmafrcFeatureExtractorMixin, FeatureExtractor): + + TYPE = "integer_VIF_feature" + VERSION = "1.0" + + ATOM_FEATURES = ['vif_scale0', 'vif_scale1', 'vif_scale2', 'vif_scale3', + ] + + ATOM_FEATURES_TO_VMAFRC_KEY_DICT = { + 'vif_scale0': 'integer_vif_scale0', + 'vif_scale1': 'integer_vif_scale1', + 'vif_scale2': 'integer_vif_scale2', + 'vif_scale3': 'integer_vif_scale3', + } + + def _generate_result(self, asset): + # routine to call the command-line executable and generate quality + # scores in the log file. + + quality_width, quality_height = asset.quality_width_height + log_file_path = self._get_log_file_path(asset) + + yuv_type=self._get_workfile_yuv_type(asset) + ref_path=asset.ref_procfile_path + dis_path=asset.dis_procfile_path + w=quality_width + h=quality_height + logger = self.logger + + ExternalProgramCaller.call_vmafrc_single_feature('vif', yuv_type, ref_path, dis_path, w, h, log_file_path, logger) + + +class FloatAdmFeatureExtractor(VmafrcFeatureExtractorMixin, FeatureExtractor): + + TYPE = "float_ADM_feature" + VERSION = "1.0" + + ATOM_FEATURES = ['adm2', + ] + + ATOM_FEATURES_TO_VMAFRC_KEY_DICT = { + 'adm2': 'adm2', + } + + def _generate_result(self, asset): + # routine to call the command-line executable and generate quality + # scores in the log file. + + quality_width, quality_height = asset.quality_width_height + log_file_path = self._get_log_file_path(asset) + + yuv_type=self._get_workfile_yuv_type(asset) + ref_path=asset.ref_procfile_path + dis_path=asset.dis_procfile_path + w=quality_width + h=quality_height + logger = self.logger + + ExternalProgramCaller.call_vmafrc_single_feature('float_adm', yuv_type, ref_path, dis_path, w, h, log_file_path, logger) + + +class IntegerPsnrFeatureExtractor(VmafrcFeatureExtractorMixin, FeatureExtractor): + + TYPE = 'integer_PSNR_feature' + VERSION = "1.0" + + ATOM_FEATURES = ['psnr_y', 'psnr_cb', 'psnr_cr'] + + ATOM_FEATURES_TO_VMAFRC_KEY_DICT = { + 'psnr_y': 'psnr_y', + 'psnr_cb': 'psnr_cb', + 'psnr_cr': 'psnr_cr', + } + + def _generate_result(self, asset): + # routine to call the command-line executable and generate quality + # scores in the log file. + + quality_width, quality_height = asset.quality_width_height + log_file_path = self._get_log_file_path(asset) + + yuv_type=self._get_workfile_yuv_type(asset) + ref_path=asset.ref_procfile_path + dis_path=asset.dis_procfile_path + w=quality_width + h=quality_height + logger = self.logger + + ExternalProgramCaller.call_vmafrc_single_feature('psnr', yuv_type, ref_path, dis_path, w, h, log_file_path, logger)