3939#include "feature_extractor.h"
4040#include "feature_name.h"
4141#include "integer_motion.h"
42+ #include "motion_blend_tools.h"
4243
4344#define MIN (x , y ) (((x) < (y)) ? (x) : (y))
4445
@@ -62,21 +63,68 @@ typedef struct MotionV2State {
6263 unsigned w , h , bpc ;
6364 motion_pipeline_fn pipeline ;
6465 double motion_max_val ;
66+ double motion_blend_factor ;
67+ double motion_blend_offset ;
68+ double motion_fps_weight ;
6569 bool motion_five_frame_window ;
70+ bool motion_moving_average ;
71+ bool motion_force_zero ;
6672 VmafDictionary * feature_name_dict ;
6773} MotionV2State ;
6874
6975static const VmafOption options [] = {
76+ {
77+ .name = "motion_force_zero" ,
78+ .alias = "force_0" ,
79+ .help = "forces motion score to be 0" ,
80+ .offset = offsetof(MotionV2State , motion_force_zero ),
81+ .type = VMAF_OPT_TYPE_BOOL ,
82+ .default_val .b = false,
83+ .flags = VMAF_OPT_FLAG_FEATURE_PARAM ,
84+ },
85+ {
86+ .name = "motion_blend_factor" ,
87+ .alias = "mbf" ,
88+ .help = "blend motion score given an offset" ,
89+ .offset = offsetof(MotionV2State , motion_blend_factor ),
90+ .type = VMAF_OPT_TYPE_DOUBLE ,
91+ .default_val .d = 1.0 ,
92+ .min = 0.0 ,
93+ .max = 1.0 ,
94+ .flags = VMAF_OPT_FLAG_FEATURE_PARAM ,
95+ },
96+ {
97+ .name = "motion_blend_offset" ,
98+ .alias = "mbo" ,
99+ .help = "blend motion score starting from this offset" ,
100+ .offset = offsetof(MotionV2State , motion_blend_offset ),
101+ .type = VMAF_OPT_TYPE_DOUBLE ,
102+ .default_val .d = 40.0 ,
103+ .min = 0.0 ,
104+ .max = 1000.0 ,
105+ .flags = VMAF_OPT_FLAG_FEATURE_PARAM ,
106+ },
107+ {
108+ .name = "motion_fps_weight" ,
109+ .alias = "mfw" ,
110+ .help = "fps-aware multiplicative weight/correction" ,
111+ .offset = offsetof(MotionV2State , motion_fps_weight ),
112+ .type = VMAF_OPT_TYPE_DOUBLE ,
113+ .default_val .d = 1.0 ,
114+ .min = 0.0 ,
115+ .max = 5.0 ,
116+ .flags = VMAF_OPT_FLAG_FEATURE_PARAM ,
117+ },
70118 {
71119 .name = "motion_max_val" ,
120+ .alias = "mmxv" ,
72121 .help = "maximum value allowed; larger values will be clipped to this value" ,
73122 .offset = offsetof(MotionV2State , motion_max_val ),
74123 .type = VMAF_OPT_TYPE_DOUBLE ,
75124 .default_val .d = DEFAULT_MOTION_MAX_VAL ,
76125 .min = 0.0 ,
77126 .max = 10000.0 ,
78127 .flags = VMAF_OPT_FLAG_FEATURE_PARAM ,
79- .alias = "mmxv" ,
80128 },
81129 {
82130 .name = "motion_five_frame_window" ,
@@ -87,6 +135,15 @@ static const VmafOption options[] = {
87135 .default_val .b = false,
88136 .flags = VMAF_OPT_FLAG_FEATURE_PARAM ,
89137 },
138+ {
139+ .name = "motion_moving_average" ,
140+ .alias = "mma" ,
141+ .help = "smooth motion3 with a 2-frame moving average" ,
142+ .offset = offsetof(MotionV2State , motion_moving_average ),
143+ .type = VMAF_OPT_TYPE_BOOL ,
144+ .default_val .b = false,
145+ .flags = VMAF_OPT_FLAG_FEATURE_PARAM ,
146+ },
90147 { 0 }
91148};
92149
@@ -249,6 +306,12 @@ static int extract(VmafFeatureExtractor *fex,
249306 (void ) ref_pic_90 ;
250307 (void ) dist_pic_90 ;
251308
309+ if (s -> motion_force_zero ) {
310+ return vmaf_feature_collector_append_with_dict (feature_collector ,
311+ s -> feature_name_dict ,
312+ "VMAF_integer_feature_motion_v2_sad_score" , 0. , index );
313+ }
314+
252315 const unsigned min_idx = s -> motion_five_frame_window ? 2 : 1 ;
253316 if (index < min_idx ) {
254317 return vmaf_feature_collector_append_with_dict (feature_collector ,
@@ -275,7 +338,8 @@ static int extract(VmafFeatureExtractor *fex,
275338
276339 return vmaf_feature_collector_append_with_dict (feature_collector ,
277340 s -> feature_name_dict ,
278- "VMAF_integer_feature_motion_v2_sad_score" , MIN (score , s -> motion_max_val ), index );
341+ "VMAF_integer_feature_motion_v2_sad_score" ,
342+ MIN (score * s -> motion_fps_weight , s -> motion_max_val ), index );
279343}
280344
281345static int close_fex (VmafFeatureExtractor * fex )
@@ -311,6 +375,19 @@ static int flush(VmafFeatureExtractor *fex,
311375 const unsigned min_idx = s -> motion_five_frame_window ? 2 : 1 ;
312376 if (n_frames == 0 ) return 1 ;
313377
378+ double stamp_value = 0. ;
379+ if (n_frames > min_idx ) {
380+ double sad_at_min_idx ;
381+ if (!vmaf_feature_collector_get_score (feature_collector , sad_name ,
382+ & sad_at_min_idx , min_idx )) {
383+ stamp_value = MIN (motion_blend (sad_at_min_idx ,
384+ s -> motion_blend_factor ,
385+ s -> motion_blend_offset ),
386+ s -> motion_max_val );
387+ }
388+ }
389+
390+ double prev_processed = 0. ;
314391 for (unsigned i = 0 ; i < n_frames ; i ++ ) {
315392 double motion2 ;
316393
@@ -335,6 +412,25 @@ static int flush(VmafFeatureExtractor *fex,
335412 vmaf_feature_collector_append_with_dict (feature_collector ,
336413 s -> feature_name_dict ,
337414 "VMAF_integer_feature_motion2_v2_score" , motion2 , i );
415+
416+ double motion3 ;
417+ if (i < min_idx ) {
418+ motion3 = stamp_value ;
419+ prev_processed = stamp_value ;
420+ } else {
421+ double processed = MIN (motion_blend (motion2 ,
422+ s -> motion_blend_factor ,
423+ s -> motion_blend_offset ),
424+ s -> motion_max_val );
425+ motion3 = s -> motion_moving_average
426+ ? (processed + prev_processed ) / 2.0
427+ : processed ;
428+ prev_processed = processed ;
429+ }
430+
431+ vmaf_feature_collector_append_with_dict (feature_collector ,
432+ s -> feature_name_dict ,
433+ "VMAF_integer_feature_motion3_v2_score" , motion3 , i );
338434 }
339435
340436 return 1 ;
@@ -343,6 +439,7 @@ static int flush(VmafFeatureExtractor *fex,
343439static const char * provided_features [] = {
344440 "VMAF_integer_feature_motion_v2_sad_score" ,
345441 "VMAF_integer_feature_motion2_v2_score" ,
442+ "VMAF_integer_feature_motion3_v2_score" ,
346443 NULL
347444};
348445
0 commit comments