fix motion mapping, issue #42

This commit is contained in:
c0ntrol
2016-03-18 21:23:28 +01:00
parent 4be19bde83
commit 443742ad3c
2 changed files with 230 additions and 254 deletions

View File

@@ -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)

View File

@@ -1,7 +1,7 @@
/*
* Linux VeeJay
*
* Copyright(C)2007 Niels Elburg <nwelburg@gmail.com>
* Copyright(C)2007-2016 Niels Elburg <nwelburg@gmail.com>
*
* 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 <config.h>
#include <stdint.h>
#include <stdio.h>
#include <limits.h>
#include <libvjmem/vjmem.h>
#include <libvjmsg/vj-msg.h>
#include "motionmap.h"
#include "common.h"
#include "softblur.h"
#include "opacity.h"
#include <veejay/vj-task.h>
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;
}