diff --git a/veejay-current/veejay-server/libvje/effects/common.c b/veejay-current/veejay-server/libvje/effects/common.c index 2eccc66a..93873dc8 100644 --- a/veejay-current/veejay-server/libvje/effects/common.c +++ b/veejay-current/veejay-server/libvje/effects/common.c @@ -1389,34 +1389,6 @@ void vje_load_mask(uint8_t val) :: "r" (m) ); } -void vje_diff_plane( uint8_t *A, uint8_t *B, uint8_t *O, int val, int len ) -{ - unsigned int i; - uint8_t mask[8] = { val,val,val,val, val,val,val,val }; - uint8_t *m = (uint8_t*)&mask; - - __asm __volatile( - "movq (%0), %%mm7\n\t" - :: "r" (m) ); - - - for( i = len; i > 8; i -= 8 ) { - __asm __volatile( - "\n\t movq %[srcA], %%mm0" - "\n\t movq %[srcB], %%mm1" - "\n\t psubusb %%mm1,%%mm0" - "\n\t psubusb %%mm2,%%mm1" - "\n\t psubusb %%mm1,%%mm0" - "\n\t psubusb %%mm7,%%mm0" - "\n\t movq %%mm0, %[dest]" - : [dest] "=m" (*(O + i)) - : [srcA] "m" (*(A + i )) - , [srcB] "m" (*(B + i ))); - } - - do_emms; -} - void vje_mmx_negate( uint8_t *dst, uint8_t *in ) { __asm __volatile( @@ -1490,14 +1462,6 @@ void binarify_1src( uint8_t *dst, uint8_t *src, uint8_t v, int reverse,int w, in #else -void vje_diff_plane( uint8_t *A, uint8_t *B, uint8_t *O, int threshold, int len ) -{ - unsigned int i; - for( i = 0; i < len; i ++ ) { - O[i] = ( abs( A[i] - B[i] ) > threshold ? 0xff : 0 ); - } -} - void vje_mmx_negate_frame(uint8_t *dst, uint8_t *in, uint8_t val, int len ) { unsigned int i; @@ -1522,6 +1486,14 @@ void binarify_1src( uint8_t *dst, uint8_t *src, uint8_t threshold,int reverse, i #endif +void vje_diff_plane( uint8_t *A, uint8_t *B, uint8_t *O, int threshold, int len ) +{ + unsigned int i; + for( i = 0; i < len; i ++ ) { + O[i] = ( abs( B[i] - A[i] ) > threshold ? 0xff : 0 ); + } +} + void binarify( uint8_t *bm, uint8_t *bg, uint8_t *src,int threshold,int reverse, const int len ) { if(!reverse) diff --git a/veejay-current/veejay-server/libvje/effects/motionmap.c b/veejay-current/veejay-server/libvje/effects/motionmap.c index 4b6d2f83..775e8c0e 100644 --- a/veejay-current/veejay-server/libvje/effects/motionmap.c +++ b/veejay-current/veejay-server/libvje/effects/motionmap.c @@ -1,7 +1,7 @@ /* * Linux VeeJay * - * Copyright(C)2007 Niels Elburg + * Copyright(C)2007-2016 Niels Elburg * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,11 +21,14 @@ There are 5 parameters: p0 = Threshold - p1 = Acitivity Maximum Level - p2 = Draw difference frame (no processing) - p3 = Ringbuffer length N - p4 = Opacity level + p1 = Motion Energy Threshold + p2 = Draw difference frame (no processing) + p3 = Motion Energy Histogram + p4 = Decay +*/ +/* REVIEW / FIXME ?: + This filter detects the amount of motion in a frame. It keeps an internal buffer to average (smoothen) the acitivity levels over N frames At each step in N , a new value is linearly interpolated which is later @@ -38,18 +41,17 @@ #include #include #include +#include #include #include #include "motionmap.h" #include "common.h" #include "softblur.h" #include "opacity.h" +#include -typedef int (*morph_func)(uint8_t *kernel, uint8_t mt[9] ); -#define HIS_DEFAULT 15 +#define HIS_DEFAULT 6 #define HIS_LEN (8*25) -#define ACT_TOP 4000 -#define MAXCAPBUF 55 vj_effect *motionmap_init(int w, int h) { vj_effect *ve = (vj_effect *) vj_calloc(sizeof(vj_effect)); @@ -58,66 +60,58 @@ vj_effect *motionmap_init(int w, int h) ve->defaults = (int *) vj_calloc(sizeof(int) * ve->num_params); /* default values */ ve->limits[0] = (int *) vj_calloc(sizeof(int) * ve->num_params); /* min */ ve->limits[1] = (int *) vj_calloc(sizeof(int) * ve->num_params); /* max */ - ve->limits[0][0] = 0; // motionmap - ve->limits[1][0] = 255; - ve->limits[0][1] = 50; // motion energy - ve->limits[1][1] = 10000; + ve->limits[0][0] = 0; + ve->limits[1][0] = 255; /* threshold */ + ve->limits[0][1] = 1; + ve->limits[1][1] = (w*h)/2; ve->limits[0][2] = 0; ve->limits[1][2] = 1; - ve->limits[0][4] = 0; // buffer - ve->limits[1][4] = 255; - ve->limits[0][3] = HIS_DEFAULT; + ve->limits[0][4] = 1; + ve->limits[1][4] = HIS_LEN; + ve->limits[0][3] = 1; ve->limits[1][3] = HIS_LEN; ve->defaults[0] = 40; - ve->defaults[1] = ACT_TOP; + ve->defaults[1] = 1000; ve->defaults[2] = 1; ve->defaults[3] = HIS_DEFAULT; - ve->defaults[4] = 0; + ve->defaults[4] = HIS_DEFAULT; ve->description = "Motion Mapping"; ve->sub_format = 1; ve->extra_frame = 0; ve->has_user = 0; ve->n_out = 2; - ve->param_description = vje_build_param_list( ve->num_params, "Difference Threshold", "Maximum Motion Energy","Draw Motion Map","History in frames" ,"Capture length"); + ve->param_description = vje_build_param_list( ve->num_params, "Difference Threshold", "Maximum Motion Energy","Draw Motion Map","History in frames" ,"Decay"); return ve; } +static int32_t histogram_[HIS_LEN]; + +static uint8_t *bg_image = NULL; static uint8_t *binary_img = NULL; -static uint8_t *original_img = NULL; -static uint8_t *previous_img = NULL; -static uint32_t histogram_[HIS_LEN]; -static uint8_t *large_buf = NULL; -static uint32_t nframe_ =0; -static uint32_t max_d = ACT_TOP; -static int current_his_len = HIS_DEFAULT; -static uint32_t key1_ = 0, key2_ = 0, keyv_ = 0, keyp_ = 0; -static int have_bg = 0; -static int n_captured = 0; -static int n_played = 0; -static int capturing = 0; -static int playing = 0; +static uint8_t *prev_img = NULL; static uint8_t *interpolate_buf = NULL; + +static int32_t max = 0; +static uint32_t nframe_ =0; +static int current_his_len = HIS_DEFAULT; +static int current_decay = HIS_DEFAULT; +static uint32_t key1_ = 0, key2_ = 0, keyv_ = 0, keyp_ = 0; +static int have_bg = 0; static int running = 0; int motionmap_malloc(int w, int h ) { - binary_img = (uint8_t*) vj_malloc(sizeof(uint8_t) * RUP8(w * h * 3) ); - original_img = binary_img + RUP8(w*h); - previous_img = original_img + RUP8(w*h); - large_buf = vj_malloc(sizeof(uint8_t) * RUP8(w*h*3) * (MAXCAPBUF+1)); - if(!large_buf) - { - veejay_msg(0, "Memory allocation error for Motion Mapping. Too large: %ld bytes",(long) ((RUP8(w*h*3)*(MAXCAPBUF+1)))); - return 0; - } + bg_image = (uint8_t*) vj_malloc( sizeof(uint8_t) * RUP8(w * h)); + binary_img = (uint8_t*) vj_malloc(sizeof(uint8_t) * RUP8(w * h)); + prev_img = (uint8_t*) vj_malloc(sizeof(uint8_t) * RUP8(w*h)); interpolate_buf = vj_malloc( sizeof(uint8_t) * RUP8(w*h*3)); + veejay_msg(2, "This is 'Motion Mapping'"); veejay_msg(2, "This FX calculates motion energy activity levels over a period of time to scale FX parameters"); veejay_msg(2, "Add any of the following to the FX chain (if not already present)"); veejay_msg(2, "\tBathroom Window, Displacement Mapping, Multi Mirrors, Magic Mirror, Sinoids"); veejay_msg(2, "\tSlice Window , Smear, ChameleonTV and TimeDistort TV"); - veejay_msg(2, "Using %2.2fMb for large buffer", (RUP8(w*h*3)*(MAXCAPBUF+1))/1048576.0f); - veejay_memset( histogram_, 0, sizeof(uint32_t) * HIS_LEN ); + veejay_memset( histogram_, 0, sizeof(int32_t) * HIS_LEN ); nframe_ = 0; running = 0; return 1; @@ -125,12 +119,15 @@ int motionmap_malloc(int w, int h ) void motionmap_free(void) { - if(binary_img) - free(binary_img); - if(large_buf) - free(large_buf); if( interpolate_buf ) - free(interpolate_buf); + free(interpolate_buf); + if( bg_image ) + free(bg_image); + if( binary_img ) + free(binary_img); + if( prev_img ) + free(prev_img); + have_bg = 0; interpolate_buf = NULL; nframe_ = 0; @@ -138,19 +135,7 @@ void motionmap_free(void) keyv_ = 0; keyp_ = 0; binary_img = NULL; - previous_img = NULL; -} - -#ifndef MIN -#define MIN(a,b) ( (a)>(b) ? (b) : (a) ) -#endif -#ifndef MAX -#define MAX(a,b) ( (a)>(b) ? (a) : (b) ) -#endif - -static void update_bgmask( uint8_t *dst,uint8_t *in, uint8_t *src, int len, int threshold ) -{ - vje_diff_plane( in, src, dst, threshold, len ); + prev_img = NULL; } uint8_t *motionmap_interpolate_buffer() @@ -175,66 +160,51 @@ uint32_t motionmap_activity() void motionmap_scale_to( int p1max, int p2max, int p1min, int p2min, int *p1val, int *p2val, int *pos, int *len ) { - if(nframe_ <= 1) - return; - - if( keyv_ > max_d ) + if( keyv_ > max ) { - keyv_ = max_d; + keyv_ = max; } - int n = ((nframe_-1) % current_his_len)+1; - - float q = 1.0f / (float) current_his_len * n; + int n = (nframe_ % current_decay) + 1; + float q = 1.0f / (float) current_decay * n; float diff = (float) keyv_ - (float) keyp_ ; float pu = keyp_ + (q * diff); - - float m = (float) max_d; + float m = (float) max; if( pu > m ) pu = m; - - float w = 1.0 / max_d; + float w = 1.0 / max; float pw = w * pu; *p1val = p1min + (int) ((p1max-p1min) * pw); *p2val = p2min + (int) ((p2max-p2min) * pw); - *len = current_his_len; + *len = current_decay; *pos = n; - - veejay_msg(VEEJAY_MSG_DEBUG, - "Change from [%d-%d] to %d [%d-%d] to %d in %d frames", - p1min,p1max,*p1val,p2min,p2max,*p2val,current_his_len); - -// veejay_msg(0, "%s:%s p1=%d,p2=%d, len=%d,pos=%d", -// __FILE__,__FUNCTION__,*p1val,*p2val, *len, *pos ); - } void motionmap_lerp_frame( VJFrame *cur, VJFrame *prev, int N, int n ) { unsigned int i; - int n1 = (( n-1) % N ) + 1; - float frac = 1.0f / (float) N * n1; + const int n1 = (( n-1) % N ) + 1; + const float frac = 1.0f / (float) N * n1; const int len = cur->len; - uint8_t *Y0 = cur->data[0]; - uint8_t *Y1 = prev->data[0]; - uint8_t *U0 = cur->data[1]; - uint8_t *U1 = prev->data[1]; - uint8_t *V0 = cur->data[2]; - uint8_t *V1 = prev->data[2]; + uint8_t *__restrict__ Y0 = cur->data[0]; + const uint8_t *__restrict__ Y1 = prev->data[0]; + uint8_t *__restrict__ U0 = cur->data[1]; + const uint8_t *__restrict__ U1 = prev->data[1]; + uint8_t *__restrict__ V0 = cur->data[2]; + const uint8_t *__restrict__ V1 = prev->data[2]; - for ( i = 0; i < len ; i ++ ) - { - Y0[i] = Y1[i] + ( frac * (Y0[i] - Y1[i])); - U0[i] = U1[i] + ( frac * (U0[i] - U1[i])); - V0[i] = V1[i] + ( frac * (V0[i] - V1[i])); - } + for ( i = 0; i < len ; i ++ ) + { + Y0[i] = Y1[i] + ( frac * (Y0[i] - Y1[i])); + U0[i] = U1[i] + ( frac * (U0[i] - U1[i])); + V0[i] = V1[i] + ( frac * (V0[i] - V1[i])); + } } - void motionmap_store_frame( VJFrame *fx ) { uint8_t *dest[4] = { @@ -249,19 +219,111 @@ void motionmap_interpolate_frame( VJFrame *fx, int N, int n ) return; VJFrame prev; - veejay_memcpy(&prev, fx, sizeof(VJFrame)); - prev.data[0] = interpolate_buf; - prev.data[1] = interpolate_buf + (fx->len); - prev.data[2] = interpolate_buf + (2*fx->len); + veejay_memcpy(&prev, fx, sizeof(VJFrame)); + prev.data[0] = interpolate_buf; + prev.data[1] = interpolate_buf + (fx->len); + prev.data[2] = interpolate_buf + (2*fx->len); - motionmap_lerp_frame( fx, &prev, N, n ); + motionmap_lerp_frame( fx, &prev, N, n ); +} + +static void motionmap_blur( uint8_t *Y, int width, int height ) +{ + const unsigned int len = (width * height); + int r,c; + for (r = 0; r < len; r += width) { + for (c = 1; c < width-1; c++) { + Y[c + r] = (Y[r + c - 1] + Y[r + c] + Y[r + c + 1] ) / 3; + } + } +} + +static int32_t motionmap_activity_level( uint8_t *I, int width, int height ) +{ + const unsigned int len = (width * height); + int32_t level = 0; + int r,c; + for (r = 0; r < len; r += width) { + for ( c = 0; c < width; c ++ ) { + if( I[r + c] > 0 ) + level ++; + } + } + return level; +} + +void motionmap_find_diff( uint8_t *bg, uint8_t *prev_img, uint8_t *img, const int len, const int threshold ) +{ + unsigned int i; + uint8_t p1,p2; + for( i = 0; i < len; i ++ ) + { + uint8_t q1 = 0, q2 = 0; + p1 = abs( bg[i] - img[i] ); + if( p1 > threshold ) { + q1 = 1; + } + + p2 = abs( bg[i] - prev_img[i] ); + if( p2 > threshold ) { + q2 = 1; + } + + if( (!q1 && q2) || (!q2 && q1) ) { + binary_img[i] = 0xff; + } + else { + binary_img[i] = (binary_img[i] >> 1); //@ decay + } + + prev_img[i] = img[i]; + } +} + +void motionmap_find_diff_job( void *arg ) +{ + vj_task_arg_t *t = (vj_task_arg_t*) arg; + + uint8_t *t_bg = t->input[0]; + uint8_t *t_img = t->input[1]; + uint8_t *t_prev_img = t->input[2]; + uint8_t *t_binary_img = t->output[0]; + + const int len = t->strides[0]; + const int threshold = t->iparams[0]; + + unsigned int i; + uint8_t p1,p2; + for( i = 0; i < len; i ++ ) + { + uint8_t q1 = 0, q2 = 0; + p1 = abs( t_bg[i] - t_img[i] ); + if( p1 > threshold ) { + q1 = 1; + } + + p2 = abs( t_bg[i] - t_prev_img[i] ); + if( p2 > threshold ) { + q2 = 1; + } + + if( (!q1 && q2) || (!q2 && q1) ) { + t_binary_img[i] = 0xff; + } + else { + t_binary_img[i] = (t_binary_img[i] >> 1); //@ decay in 7 frames + } + + t_prev_img[i] = t_img[i]; + } } int motionmap_prepare( uint8_t *map[4], int width, int height ) { - if(!previous_img) - return 0; - vj_frame_copy1( map[0], previous_img, width * height ); + vj_frame_copy1( map[0], bg_image, width * height ); + motionmap_blur( bg_image, width,height ); + veejay_memcpy( prev_img, bg_image, width * height ); + have_bg = 1; nframe_ = 0; running = 0; @@ -269,12 +331,10 @@ int motionmap_prepare( uint8_t *map[4], int width, int height ) return 1; } -static int stop_capture_ = 0; -static int reaction_ready_ = 0; -void motionmap_apply( VJFrame *frame, int width, int height, int threshold, int reverse, int draw, int history, int capbuf ) +void motionmap_apply( VJFrame *frame, int width, int height, int threshold, int limit, int draw, int history, int decay ) { unsigned int i; - int len = (width * height); + const unsigned int len = (width * height); uint8_t *Cb = frame->data[1]; uint8_t *Cr = frame->data[2]; if(!have_bg) { @@ -282,44 +342,35 @@ void motionmap_apply( VJFrame *frame, int width, int height, int threshold, int return; } - vj_frame_copy1( frame->data[0], original_img, len ); + /* run difference algorithm over multiple threads */ + if( vj_task_available() ) { + VJFrame task; + task.stride[0] = len; /* plane length */ + task.stride[1] = len; + task.stride[2] = len; + task.stride[3] = 0; + task.data[0] = bg_image; /* plane 0 = background image */ + task.data[1] = frame->data[0]; /* plane 1 = luminance channel */ + task.data[2] = prev_img; /* plane 2 = luminance channel of previous frame */ + task.data[3] = NULL; + task.ssm = 1; /* all planes are the same size */ + task.format = frame->format; /* not important, but cannot be 0 */ + task.shift_v = 0; + task.shift_h = 0; + task.uv_width = 0; + task.uv_height = 0; + task.width = width; /* dimensions */ + task.height = height; - softblur_apply( frame, width,height,0 ); - update_bgmask( binary_img, previous_img, frame->data[0], len , threshold); + uint8_t *dst[4] = { binary_img, binary_img, binary_img, NULL }; - uint32_t sum = 0,min=0xffff,max=0; - uint64_t activity_level1 = 0; - uint64_t activity_level2 = 0; - uint64_t activity_level3 = 0; - uint64_t activity_level4 = 0; - for( i = 0; i < len; i += 4 ) - { - - activity_level1 += binary_img[i]; - activity_level2 += binary_img[i+1]; - activity_level3 += binary_img[i+2]; - activity_level4 += binary_img[i+3]; + vj_task_set_from_frame( &task ); + vj_task_set_param( threshold, 0 ); + + vj_task_run( task.data, dst, NULL,NULL,3, (performer_job_routine) &motionmap_find_diff_job ); } - uint32_t activity_level = ( (activity_level1>>8) + (activity_level2>>8) + (activity_level3>>8) + (activity_level4>>8)); - - max_d = reverse; - - current_his_len = history; - - histogram_[ (nframe_%current_his_len) ] = activity_level; - - for( i = 0; i < current_his_len; i ++ ) - { - sum += histogram_[i]; - if(histogram_[i] > max ) max = histogram_[i]; - if(histogram_[i] < min ) min = histogram_[i]; - } - if( (nframe_ % current_his_len)==0 ) - { - key1_ = min; - key2_ = max; - keyp_ = keyv_; - keyv_ = (sum > 0 ? (sum/current_his_len):0 ); + else { + motionmap_find_diff( bg_image, prev_img, frame->data[0], len, threshold ); } if( draw ) @@ -327,87 +378,40 @@ void motionmap_apply( VJFrame *frame, int width, int height, int threshold, int vj_frame_clear1( Cb, 128, len ); vj_frame_clear1( Cr, 128, len ); vj_frame_copy1( binary_img, frame->data[0], len ); - nframe_++; + running = 0; return; } - - if( capbuf ) + int32_t activity_level = motionmap_activity_level( binary_img, width, height ); + int32_t avg_actlvl = 0; + int32_t min = INT_MAX; + + current_his_len = history; + current_decay = decay; + + histogram_[ (nframe_%current_his_len) ] = activity_level; + + for( i = 0; i < current_his_len; i ++ ) { - if(!capturing) - { - if( keyv_ > max_d ) - { - if( !reaction_ready_ ) - { - stop_capture_ = keyv_ / 5; - capturing = 1; - } - else - { - playing = 1; - } - } - } + avg_actlvl += histogram_[i]; + if(histogram_[i] > max ) max = histogram_[i]; + if(histogram_[i] < min ) min = histogram_[i]; + } - if( stop_capture_ && !reaction_ready_) - { - if( keyv_ < stop_capture_ || n_captured >= MAXCAPBUF ) - { - capturing = 0; - stop_capture_= 0; - reaction_ready_ =1; - } - } - } - else - { - capturing = 0; playing = 0; stop_capture_ = 0; reaction_ready_ = 0; n_captured = 0; - } + avg_actlvl = avg_actlvl / current_his_len; - if( capturing ) - { - uint8_t *dst[4]; - dst[0] = large_buf + ( n_captured * (len*3) ); - dst[1] = dst[0] + len; - dst[2] = dst[1] + len; - dst[3] = NULL; - int strides[4] = { len, len, len, 0 }; - vj_frame_copy( frame->data, dst, strides ); - n_captured ++; - if( n_captured >= MAXCAPBUF ) - { - capturing = 0; - stop_capture_ = 0; - } - } - else if (playing ) - { - uint8_t *src[4]; - src[0] = large_buf + ( n_played * (len*3)); - src[1] = src[0]+ len; - src[2] = src[1]+ len; - src[3] = NULL; - /* veejay_memcpy( frame->data[0], src[0], len ); - veejay_memcpy( frame->data[1], src[1], len ); - veejay_memcpy( frame->data[2], src[2], len );*/ + if( avg_actlvl < limit ) + avg_actlvl = 0; - VJFrame b; - veejay_memcpy(&b, frame, sizeof(VJFrame)); - b.data[0] = src[0]; b.data[1] = src[1]; b.data[2] = src[2]; - opacity_applyN( frame, &b, frame->width,frame->height, capbuf ); - - - n_played ++; - if( n_played >= n_captured) - { - n_played = 0; n_captured = 0; playing = 0; capturing = 0; - reaction_ready_ = 0; - } - nframe_++; - return; - } - vj_frame_copy1( original_img, frame->data[0], len ); nframe_ ++; + + if( (nframe_ % current_his_len)==0 ) + { + key1_ = min; + key2_ = max; + keyp_ = keyv_; + keyv_ = avg_actlvl; + } + running = 1; }