/*
keyspillm0pup.c
This Frei0r plugin cleans key color residue from composited video
Version 0.1 mar 2012
Copyright (C) 2012 Marko Cebokli http://lea.hamradio.si/~s57uuu
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
//Version 0.2
//compile: gcc -c -fPIC -Wall keyspillm0pup.c -o keyspillm0pup.o
//link: gcc -shared -o keyspillm0pup.so keyspillm0pup.o
//*******************************************************************
#include <stdio.h>
#include <math.h>
#include <frei0r.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
double PI=3.14159265358979;
typedef struct
{
float r;
float g;
float b;
float a;
} float_rgba;
//----------------------------------------------------
void RGBA8888_2_float(const uint32_t* in, float_rgba *out, int w, int h)
{
uint8_t *cin;
int i;
float f1;
cin=(uint8_t *)in;
f1=1.0/255.0;
for (i=0;i<w*h;i++)
{
out[i].r=f1*(float)*cin++;
out[i].g=f1*(float)*cin++;
out[i].b=f1*(float)*cin++;
out[i].a=f1*(float)*cin++;
}
}
//------------------------------------------------------------------
void float_2_RGBA8888(const float_rgba *in, uint32_t* out, int w, int h)
{
uint8_t *cout;
int i;
cout=(uint8_t *)out;
for (i=0;i<w*h;i++)
{
*cout++=(uint8_t)(in[i].r*255.0);
*cout++=(uint8_t)(in[i].g*255.0);
*cout++=(uint8_t)(in[i].b*255.0);
*cout++=(uint8_t)(in[i].a*255.0);
}
}
//------------------------------------------------
//color coeffs according to rec 601 or rec 701
void cocos(int cm, float *kr, float *kg, float *kb)
{
*kr=0.30; *kg=0.59; *kb=0.11; //da compiler ne jamra
switch (cm)
{
case 0:
{
*kr=0.30; *kg=0.59; *kb=0.11; //rec 601
break;
}
case 1:
{
*kr=0.2126; *kg=0.7152; *kb=0.0722; //rec 709
break;
}
default:
{
fprintf(stderr,"Unknown color model %d\n",cm);
break;
}
}
}
//-------------------------------------------------
//premakne barvo radialno stran od key
//sorazmerno maski
//*mask=float maska [0...1]
//k=key
//am=amount [0...1]
void clean_rad_m(float_rgba *s, int w, int h, float_rgba k, float *mask, float am)
{
int i;
float aa,min;
min=0.5; //min aa = max color change
for (i=0;i<w*h;i++)
{
if (mask[i]==0.0) continue;
aa=1.0-am*(1.0-min)*mask[i];
s[i].r=(s[i].r-(1.0-aa)*k.r)/aa;
s[i].g=(s[i].g-(1.0-aa)*k.g)/aa;
s[i].b=(s[i].b-(1.0-aa)*k.b)/aa;
if (s[i].r<0.0) s[i].r=0.0;
if (s[i].g<0.0) s[i].g=0.0;
if (s[i].b<0.0) s[i].b=0.0;
if (s[i].r>1.0) s[i].r=1.0;
if (s[i].g>1.0) s[i].g=1.0;
if (s[i].b>1.0) s[i].b=1.0;
}
}
//-------------------------------------------------
//premakne barvo proti target
//sorazmerno maski
//*mask=float maska [0...1]
//k=key
//am=amount [0...1]
void clean_tgt_m(float_rgba *s, int w, int h, float_rgba k, float *mask, float am, float_rgba tgt)
{
int i;
float a,aa,min;
min=0.5; //min aa = max color change
for (i=0;i<w*h;i++)
{
if (mask[i]==0.0) continue;
a=mask[i];
aa=a*am;
s[i].r=s[i].r+(tgt.r-s[i].r)*aa;
s[i].g=s[i].g+(tgt.g-s[i].g)*aa;
s[i].b=s[i].b+(tgt.b-s[i].b)*aa;
if (s[i].r<0.0) s[i].r=0.0;
if (s[i].g<0.0) s[i].g=0.0;
if (s[i].b<0.0) s[i].b=0.0;
if (s[i].r>1.0) s[i].r=1.0;
if (s[i].g>1.0) s[i].g=1.0;
if (s[i].b>1.0) s[i].b=1.0;
}
}
//----------------------------------------------------------
//desaturate colors according to mask
void desat_m(float_rgba *s, int w, int h, float *mask, float des, int cm)
{
float a,y,cr,cb,kr,kg,kb,ikg;
int i;
float ds;
cocos(cm,&kr,&kg,&kb);
ikg=1.0/kg;
for (i=0;i<w*h;i++)
{
if (mask[i]==0.0) continue;
a=mask[i];
//separate luma/chroma
y=kr*s[i].r+kg*s[i].g+kb*s[i].b;
cr=s[i].r-y; // +-
cb=s[i].b-y; // +-
//desaturate
ds=1.0-des*a;
ds=ds*ds;
cr=cr*ds; cb=cb*ds;
//back to RGB
s[i].r=cr+y;
s[i].b=cb+y;
s[i].g=(y-kr*s[i].r-kb*s[i].b)*ikg;
if (s[i].r<0.0) s[i].r=0.0;
if (s[i].g<0.0) s[i].g=0.0;
if (s[i].b<0.0) s[i].b=0.0;
if (s[i].r>1.0) s[i].r=1.0;
if (s[i].g>1.0) s[i].g=1.0;
if (s[i].b>1.0) s[i].b=1.0;
}
}
//----------------------------------------------------------
//adjust luma according to mask
void luma_m(float_rgba *s, int w, int h, float *mask, float lad, int cm)
{
float a,m,mm,y,cr,cb,kr,kg,kb,ikg;
int i;
cocos(cm,&kr,&kg,&kb);
ikg=1.0/kg;
m=2.0*lad;
for (i=0;i<w*h;i++)
{
if (mask[i]==0.0) continue;
a=mask[i];
//separate luma/chroma
y=kr*s[i].r+kg*s[i].g+kb*s[i].b;
cr=s[i].r-y; // +-
cb=s[i].b-y; // +-
//adjust luma
mm=(m-1.0)*a+1.0;
if (m>=1.0) y=mm-1.0+y*(2.0-mm); else y=mm*y;
//back to RGB
s[i].r=cr+y;
s[i].b=cb+y;
s[i].g=(y-kr*s[i].r-kb*s[i].b)*ikg;
if (s[i].r<0.0) s[i].r=0.0;
if (s[i].g<0.0) s[i].g=0.0;
if (s[i].b<0.0) s[i].b=0.0;
if (s[i].r>1.0) s[i].r=1.0;
if (s[i].g>1.0) s[i].g=1.0;
if (s[i].b>1.0) s[i].b=1.0;
}
}
//---------------------------------------------------------
//do the blur for edge mask
//This is the fibe1o_8 function from the IIRblur plugin
//converted to scalar float (for planar color)
// 1-tap IIR v 4 smereh
//optimized for speed
//loops rearanged for more locality (better cache hit ratio)
//outer (vertical) loop 2x unroll to break dependency chain
//simplified indexes
void fibe1o_f(float *s, int w, int h, float a, int ec)
{
int i,j;
float b,g,g4,avg,avg1,cr,g4a,g4b;
int p,pw,pj,pwj,pww,pmw;
avg=8; //koliko vzorcev za povprecje pri edge comp
avg1=1.0/avg;
g=1.0/(1.0-a);
g4=1.0/g/g/g/g;
//predpostavimo, da je "zunaj" crnina (nicle)
b=1.0/(1.0-a)/(1.0+a);
//prvih avg vrstic
for (i=0;i<avg;i++)
{
p=i*w;pw=p+w;
if (ec!=0)
{
cr=0.0;
for (j=0;j<avg;j++)
cr=cr+s[p+j];
cr=cr*avg1;
s[p]=cr*g+b*(s[p]-cr);
}
for (j=1;j<w;j++) //tja
s[p+j]=s[p+j]+a*s[p+j-1];
if (ec!=0)
{
cr=0.0;
for (j=w-avg;j<w;j++)
cr=cr+s[p+j];
cr=cr*avg1;
s[pw-1]=cr*g+b*(s[pw-1]-cr);
}
else
s[pw-1]=b*s[pw-1];
for (j=w-2;j>=0;j--) //nazaj
s[p+j]=a*s[p+j+1]+s[p+j];
}
//prvih avg vrstic samo navzdol (nazaj so ze)
for (i=0;i<w;i++)
{
if (ec!=0)
{
cr=0.0;
for (j=0;j<avg;j++)
cr=cr+s[i+w*j];
cr=cr*avg1;
s[i]=cr*g+b*(s[i]-cr);
}
for (j=1;j<avg;j++) //dol
s[i+j*w]=s[i+j*w]+a*s[i+w*(j-1)];
}
for (i=avg;i<h-1;i=i+2) //po vrsticah navzdol
{
p=i*w; pw=p+w; pww=pw+w; pmw=p-w;
if (ec!=0)
{
cr=0.0;
for (j=0;j<avg;j++)
cr=cr+s[p+j];
cr=cr*avg1;
s[p]=cr*g+b*(s[p]-cr);
cr=0.0;
for (j=0;j<avg;j++)
cr=cr+s[pw+j];
cr=cr*avg1;
s[pw]=cr*g+b*(s[pw]-cr);
}
for (j=1;j<w;j++) //tja
{
pj=p+j;pwj=pw+j;
s[pj]=s[pj]+a*s[pj-1];
s[pwj]=s[pwj]+a*s[pwj-1];
}
if (ec!=0)
{
cr=0.0;
for (j=w-avg;j<w;j++)
cr=cr+s[p+j];
cr=cr*avg1;
s[pw-1]=cr*g+b*(s[pw-1]-cr);
cr=0.0;
for (j=w-avg;j<w;j++)
cr=cr+s[pw+j];
cr=cr*avg1;
s[pww-1]=cr*g+b*(s[pww-1]-cr);
}
else
{
s[pw-1]=b*s[pw-1]; //rep H
s[pww-1]=b*s[pww-1];
}
//zacetek na desni
s[pw-2]=s[pw-2]+a*s[pw-1]; //nazaj
s[pw-1]=s[pw-1]+a*s[p-1]; //dol
for (j=w-2;j>=1;j--) //nazaj
{
pj=p+j;pwj=pw+j;
s[pj-1]=a*s[pj]+s[pj-1];
s[pwj]=a*s[pwj+1]+s[pwj];
//zdaj naredi se en piksel vertikalno dol, za vse stolpce
//dva nazaj, da ne vpliva na H nazaj
s[pj]=s[pj]+a*s[pmw+j];
s[pwj+1]=s[pwj+1]+a*s[pj+1];
}
//konec levo
s[pw]=s[pw]+a*s[pw+1]; //nazaj
s[p]=s[p]+a*s[pmw]; //dol
s[pw+1]=s[pw+1]+a*s[p+1]; //dol
s[pw]=s[pw]+a*s[p]; //dol
}
//ce je sodo stevilo vrstic, moras zadnjo posebej
if (i!=h)
{
p=i*w; pw=p+w;
for (j=1;j<w;j++) //tja
s[p+j]=s[p+j]+a*s[p+j-1];
s[pw-1]=b*s[pw-1]; //rep H
for (j=w-2;j>=0;j--) //nazaj in dol
{
s[p+j]=a*s[p+j+1]+s[p+j];
//zdaj naredi se en piksel vertikalno dol, za vse stolpce
//dva nazaj, da ne vpliva na H nazaj
s[p+j+1]=s[p+j+1]+a*s[p-w+j+1];
}
//levi piksel vert
s[p]=s[p]+a*s[p-w];
}
//zadnja vrstica (h-1)
g4b=g4*b;
g4a=g4/(1.0-a);
p=(h-1)*w;
if (ec!=0)
{
for (i=0;i<w;i++) //po stolpcih
{
cr=0.0;
for (j=h-avg;j<h;j++)
cr=cr+s[i+w*j];
cr=cr*avg1;
s[i+p]=g4a*cr+g4b*(s[i+p]-cr);
}
}
else
{
for (j=0;j<w;j++) //po stolpcih
s[j+p]=g4b*s[j+p]; //rep V
}
for (i=h-2;i>=0;i--) //po vrsticah navzgor
{
p=i*w; pw=p+w;
for (j=0;j<w;j++) //po stolpcih
s[p+j]=a*s[pw+j]+g4*s[p+j];
}
}
//----------------------------------------------------------
//mask based on euclidean RGB distance (alpha independent)
//mask values [0...1]
//fo=1 foreground only (alpha>0.005)
void rgb_mask(float_rgba *s, int w, int h, float *mask, float_rgba k, float t, float p, int fo)
{
int i;
float dr,dg,db,d,ip,tr,a,de;
ip = (p>0.000001) ? 1.0/p : 1000000.0;
tr=1.0/3.0;
for (i=0;i<w*h;i++)
{
if ((fo==1)&&(s[i].a<0.005)) {mask[i]=0.0; continue;}
//euclidean RGB distance
dr=s[i].r-k.r;
dg=s[i].g-k.g;
db=s[i].b-k.b;
de=dr*dr+dg*dg+db*db;
de=de*tr; //max mozno=1.0
d=de;
if (d>(t+p))
a=1.0; //notranjost (alfa=1)
else
a=(d-t)*ip;
if (d<t) a=0.0; //blizu key, max efekt
mask[i]=1.0-a;
}
}
//----------------------------------------------------------
//mask based on hue difference (alpha independent)
//mask values [0...1]
//fo=1 foreground only (alpha>0.005)
void hue_mask(float_rgba *s, int w, int h, float *mask, float_rgba k, float t, float p, int fo)
{
int i;
float d,ip,tr,a;
float ka,k32,kh,kbb,ipi,b,hh;
float sa;
k32=sqrtf(3.0)/2.0;
ipi=1.0/PI;
ka=k.r-0.5*k.g-0.5*k.b;
kbb=k32*(k.g-k.b);
kh=atan2f(kbb,ka)*ipi; // [-1...+1]
sa=0.0; //da compiler ne jamra
//printf("color mask, key hue = %6.3f\n",kh);
//printf("color mask, key = %6.3f %6.3f %6.3f\n",k.r, k.g, k.b);
//printf("color mask, key a.b = %6.3f %6.3f\n",ka,kbb);
ip = (p>0.000001) ? 1.0/p : 1000000.0;
tr=1.0/3.0;
for (i=0;i<w*h;i++)
{
if ((fo==1)&&(s[i].a<0.005)) {mask[i]=0.0; continue;}
//difference of hue 2.6X pocasneje kot RGB...
a=s[i].r-0.5*s[i].g-0.5*s[i].b;
b=k32*(s[i].g-s[i].b);
hh=atan2f(b,a)*ipi; // [-1...1]
d = (hh>kh) ? hh-kh : kh-hh; // [0...2]
d = (d>1.0) ? 2.0-d : d; // [0...1] cir
// sa=hypotf(b,a)/(s[i].r+s[i].g+s[i].b+1.0E-6)*3.0;
if (d>(t+p))
a=1.0; //notranjost (alfa=1)
else
a=(d-t)*ip;
if (d<t) a=0.0; //blizu key, max efekt
mask[i]=1.0-a;
// mask[i]=hh; //debug
}
}
//----------------------------------------------------------
//mask values [0...1]
void edge_mask(float_rgba *s, int w, int h, float *mask, float wd, int io)
{
int i;
float a;
float lim;
lim=0.05; //clear mask below this value (good for speed)
//fully opaque areas
for (i=0;i<w*h;i++)
if (s[i].a>0.996) mask[i]=1.0; else mask[i]=0.0;
//blur mask
a=expf(logf(lim)/wd);
fibe1o_f(mask, w, h, a, 1);
//select edge
if (io==-1) //inside
for (i=0;i<w*h;i++)
{
if (mask[i]>0.5) mask[i]=2.0*(1.0-mask[i]); else mask[i]=0.0; //inside only
if (mask[i]<lim) mask[i]=0.0;
}
if (io==1) //outside
for (i=0;i<w*h;i++)
{
if (mask[i]<0.5) mask[i]=2.0*mask[i]; else mask[i]=0.0; //outside only AURA
if (mask[i]<lim) mask[i]=0.0;
}
}
//----------------------------------------------------------
//mask values [0...1]
//partially transparent areas
//useful as additional clean after key or edge
//amp=amplify mask for lower transparencies [0...1]
void trans_mask(float_rgba *s, int w, int h, float *mask, float amp)
{
int i;
float ia;
//partially transparent areas
ia=1.0-amp;
for (i=0;i<w*h;i++)
if ((s[i].a<0.996)&&(s[i].a>0.004))
mask[i]=1.0-ia*s[i].a;
else
mask[i]=0.0;
}
//------------------------------------------------
//gate the mask based on similarity of hue to key
void hue_gate(float_rgba *s, int w, int h, float *mask, float_rgba k, float t, float p)
{
float k32,ka,kb,kh,ipi2,a,b,hh,d,aa,ip;
int i;
k32=sqrtf(3.0)/2.0;
ipi2=0.5/PI;
ip = (p>0.000001) ? 1.0/p : 1000000.0;
ka=k.r-0.5*k.g-0.5*k.b;
kb=k32*(k.g-k.b);
kh=atan2f(kb,ka)*ipi2; // +- 1.0
for (i=0;i<w*h;i++)
{
if (mask[i]==0.0) continue;
a=s[i].r-0.5*s[i].g-0.5*s[i].b;
b=k32*(s[i].g-s[i].b);
hh=atan2f(b,a)*ipi2;
d = (hh>kh) ? hh-kh : kh-hh; // [0...2]
d = (d>1.0) ? 2.0-d : d;
if (d>(t+p)) {mask[i]=0.0; continue;}
if (d<t) continue;
aa=1.0-(d-t)*ip;
mask[i]=mask[i]*aa;
}
}
//------------------------------------------------
//reduce the mask based on a saturation threshold
//intensity normalized saturation
void sat_thres(float_rgba *s, int w, int h, float *mask, float th)
{
float k32,ipi2,a,b,ip,sa;
float t1,t2;
int i;
float p=0.1; //sirina prehodnega pasu
t2=(1.0+p)*th; //above this, no mask change
t1=t2-p; //below this, mask=0
k32=sqrtf(3.0)/2.0;
ipi2=0.5/PI;
ip = (p>0.000001) ? 1.0/p : 1000000.0;
for (i=0;i<w*h;i++)
{
if (mask[i]==0.0) continue;
a=s[i].r-0.5*s[i].g-0.5*s[i].b;
b=k32*(s[i].g-s[i].b);
sa=hypotf(b,a)/(s[i].r+s[i].g+s[i].b+1.0E-6);
if (sa>t2) continue;
if (sa<t1) {mask[i]=0.0; continue;}
mask[i]=mask[i]*(sa-t1)*ip;
}
}
//--------------------------------------------------
void copy_mask_i(float_rgba *sl, int w, int h, float *mask)
{
int i;
for (i=0;i<w*h;i++)
{
sl[i].r=mask[i];
sl[i].g=mask[i];
sl[i].b=mask[i];
sl[i].a=1.0;
}
}
//--------------------------------------------------
void copy_mask_a(float_rgba *sl, int w, int h, float *mask)
{
int i;
for (i=0;i<w*h;i++)
{
sl[i].a=mask[i];
}
}
//***********************************************************
// FREI0R FUNKCIJE
//----------------------------------------
//struktura za instanco efekta
typedef struct
{
//status
int w,h;
//parameters
f0r_param_color_t key;
f0r_param_color_t tgt;
int maskType;
float tol;
float slope;
float Hgate;
float Sthresh;
int op1;
float am1;
int op2;
float am2;
int showmask;
int m2a;
int fo; //foreground only (speed)
int cm; //color model 0=rec601 1=rec 709
//internal variables
float_rgba krgb;
float_rgba trgb;
char *liststr;
} inst;
//-----------------------------------------------------
//stretch [0...1] to parameter range [min...max] linear
float map_value_forward(double v, float min, float max)
{
return min+(max-min)*v;
}
//-----------------------------------------------------
//collapse from parameter range [min...max] to [0...1] linear
double map_value_backward(float v, float min, float max)
{
return (v-min)/(max-min);
}
//***********************************************
// OBVEZNE FREI0R FUNKCIJE
//-----------------------------------------------
int f0r_init()
{
return 1;
}
//------------------------------------------------
void f0r_deinit()
{
}
//-----------------------------------------------
void f0r_get_plugin_info(f0r_plugin_info_t* info)
{
info->name="keyspillm0pup";
info->author="Marko Cebokli";
info->plugin_type=F0R_PLUGIN_TYPE_FILTER;
info->color_model=F0R_COLOR_MODEL_RGBA8888;
info->frei0r_version=FREI0R_MAJOR_VERSION;
info->major_version=0;
info->minor_version=3;
info->num_params=13;
info->explanation="Reduces the visibility of key color spill in chroma keying";
}
//--------------------------------------------------
void f0r_get_param_info(f0r_param_info_t* info, int param_index)
{
switch(param_index)
{
case 0:
info->name = "Key color";
info->type = F0R_PARAM_COLOR;
info->explanation = "Key color that was used for chroma keying";
break;
case 1:
info->name = "Target color";
info->type = F0R_PARAM_COLOR;
info->explanation = "Desired color to replace key residue with";
break;
case 2:
info->name = "Mask type";
info->type = F0R_PARAM_STRING;
info->explanation = "Which mask to apply [0,1,2,3]";
break;
case 3:
info->name = "Tolerance";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Range of colors around the key, where effect is full strength";
break;
case 4:
info->name = "Slope";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Range of colors around the key where effect gradually decreases";
break;
case 5:
info->name = "Hue gate";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Restrict mask to hues close to key";
break;
case 6:
info->name = "Saturation threshold";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "Restrict mask to saturated colors";
break;
case 7:
info->name = "Operation 1";
info->type = F0R_PARAM_STRING;
info->explanation = "First operation 1 [0,1,2]";
break;
case 8:
info->name = "Amount 1";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "";
break;
case 9:
info->name = "Operation 2";
info->type = F0R_PARAM_STRING;
info->explanation = "Second operation 2 [0,1,2]";
break;
case 10:
info->name = "Amount 2";
info->type = F0R_PARAM_DOUBLE;
info->explanation = "";
break;
case 11:
info->name = "Show mask";
info->type = F0R_PARAM_BOOL;
info->explanation = "Replace image with the mask";
break;
case 12:
info->name = "Mask to Alpha";
info->type = F0R_PARAM_BOOL;
info->explanation = "Replace alpha channel with the mask";
break;
}
}
//----------------------------------------------
f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
{
inst *in;
in=calloc(1,sizeof(inst));
in->w=width;
in->h=height;
//defaults
in->key.r = 0.1;
in->key.g = 0.8;
in->key.b = 0.1;
in->tgt.r = 0.78;
in->tgt.g = 0.5;
in->tgt.b = 0.4;
in->maskType=0;
in->tol=0.12;
in->slope=0.2;
in->Hgate=0.25;
in->Sthresh=0.15;
in->op1=1;
in->am1=0.55;
in->op2=0;
in->am2=0.0;
in->showmask=0;
in->m2a=0;
in->fo=1;
in->cm=1;
const char* sval = "0";
in->liststr = (char*)malloc( strlen(sval) + 1 );
strcpy( in->liststr, sval );
return (f0r_instance_t)in;
}
//---------------------------------------------------
void f0r_destruct(f0r_instance_t instance)
{
inst* in = (inst*)instance;
free(in->liststr);
free(instance);
}
//-----------------------------------------------------
void f0r_set_param_value(f0r_instance_t instance, f0r_param_t parm, int param_index)
{
inst *p;
double tmpf;
int chg,tmpi,nc;
f0r_param_color_t tmpc;
char *tmpch;
p=(inst*)instance;
chg=0;
switch(param_index)
{
case 0: //key color
tmpc=*(f0r_param_color_t*)parm;
if ((tmpc.r!=p->key.r) || (tmpc.g!=p->key.g) || (tmpc.b!=p->key.b))
chg=1;
p->key=tmpc;
p->krgb.r=p->key.r;
p->krgb.g=p->key.g;
p->krgb.b=p->key.b;
break;
case 1: //target color
tmpc=*(f0r_param_color_t*)parm;
if ((tmpc.r!=p->tgt.r) || (tmpc.g!=p->tgt.g) || (tmpc.b!=p->tgt.b))
chg=1;
p->tgt=tmpc;
p->trgb.r=p->tgt.r;
p->trgb.g=p->tgt.g;
p->trgb.b=p->tgt.b;
break;
case 2: //Mask type (list)
tmpch = (*(f0r_param_string*)parm);
if (tmpch && strcmp(p->liststr, tmpch)) {
p->liststr = realloc( p->liststr, strlen(tmpch) + 1 );
strcpy( p->liststr, tmpch );
}
nc=sscanf(p->liststr,"%d",&tmpi);
// if ((nc<=0)||(tmpi<0)||(tmpi>3)) tmpi=1;
if ((nc<=0)||(tmpi<0)||(tmpi>3)) break;
if (p->maskType != tmpi) chg=1;
p->maskType = tmpi;
break;
case 3: //tolerance
tmpf=map_value_forward(*((double*)parm), 0.0, 0.5);
if (p->tol != tmpf) chg=1;
p->tol = tmpf;
break;
case 4: //slope
tmpf=map_value_forward(*((double*)parm), 0.0, 0.5);
if (p->slope != tmpf) chg=1;
p->slope = tmpf;
break;
case 5: //Hue gate
tmpf=*(double*)parm;
if (tmpf!=p->Hgate) chg=1;
p->Hgate=tmpf;
break;
case 6: //Saturation threshold
tmpf=*(double*)parm;
if (tmpf!=p->Sthresh) chg=1;
p->Sthresh=tmpf;
break;
case 7: //Operation 1 (list)
tmpch = (*(f0r_param_string*)parm);
if (tmpch && strcmp(p->liststr, tmpch)) {
p->liststr = realloc( p->liststr, strlen(tmpch) + 1 );
strcpy( p->liststr, tmpch );
}
nc=sscanf(p->liststr,"%d",&tmpi);
// if ((nc<=0)||(tmpi<0)||(tmpi>4)) tmpi=0;
if ((nc<=0)||(tmpi<0)||(tmpi>4)) break;
if (p->op1 != tmpi) chg=1;
p->op1 = tmpi;
break;
case 8: //Amount 1
tmpf=*(double*)parm;
if (tmpf!=p->am1) chg=1;
p->am1=tmpf;
break;
case 9: //Operation 2 (list)
tmpch = (*(f0r_param_string*)parm);
if (tmpch && strcmp(p->liststr, tmpch)) {
p->liststr = realloc( p->liststr, strlen(tmpch) + 1 );
strcpy( p->liststr, tmpch );
}
nc=sscanf(p->liststr,"%d",&tmpi);
// if ((nc<=0)||(tmpi<0)||(tmpi>4)) tmpi=0;
if ((nc<=0)||(tmpi<0)||(tmpi>4)) break;
if (p->op2 != tmpi) chg=1;
p->op2 = tmpi;
break;
case 10: //Amount 2
tmpf=*(double*)parm;
if (p->am2 != tmpf) chg=1;
p->am2 = tmpf;
break;
case 11: //Show mask (bool)
tmpf=*(double*)parm;
tmpi=roundf(tmpf);
if (tmpi!=p->showmask) chg=1;
p->showmask=tmpi;
break;
case 12: //Mask to Alpha (bool)
tmpf=*(double*)parm;
tmpi=roundf(tmpf);
if (tmpi!=p->m2a) chg=1;
p->m2a=tmpi;
break;
}
if (chg==0) return;
}
//--------------------------------------------------
void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
{
inst *p;
p=(inst*)instance;
switch(param_index)
{
case 0: //key color
*((f0r_param_color_t*)param)=p->key;
break;
case 1: //target color
*((f0r_param_color_t*)param)=p->tgt;
break;
case 2: //Mask type (list)
p->liststr=realloc(p->liststr,16);
sprintf(p->liststr,"%d",p->maskType);
*((char**)param) = p->liststr;
break;
case 3: //tolerance
*((double*)param)=map_value_backward(p->tol, 0.0, 0.5);
break;
case 4: //slope
*((double*)param)=map_value_backward(p->slope, 0.0, 0.5);
break;
case 5: //Hue gate
*((double*)param)=p->Hgate;
break;
case 6: //Saturation threshold
*((double*)param)=p->Sthresh;
break;
case 7: //Operation 1 (list)
p->liststr=realloc(p->liststr,16);
sprintf(p->liststr,"%d",p->op1);
*((char**)param) = p->liststr;
break;
case 8: //Amount 1
*((double*)param)=p->am1;
break;
case 9: //Operation 2 (list)
p->liststr=realloc(p->liststr,16);
sprintf(p->liststr,"%d",p->op2);
*((char**)param) = p->liststr;
break;
case 10: //Amount 2
*((double*)param)=p->am2;
break;
case 11: //Show mask (bool)
*((double*)param)=(double)p->showmask;
break;
case 12: //Mask 2 Alpha (bool)
*((double*)param)=(double)p->m2a;
break;
}
}
//==============================================================
void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)
{
inst *in;
//video buffers
float *mask;
float_rgba *sl;
assert(instance);
in=(inst*)instance;
sl = calloc(in->w * in->h, sizeof(float_rgba));
mask = calloc(in->w * in->h, sizeof(float));
RGBA8888_2_float(inframe, sl, in->w, in->h);
switch(in->maskType) //GENERATE MASK
{
case 0: //Color distance based mask
{
rgb_mask(sl, in->w, in->h, mask, in->krgb, in->tol, in->slope, in->fo);
break;
}
case 1: //Transparency based mask
{
trans_mask(sl, in->w, in->h, mask, in->tol);
break;
}
case 2: //Edge based mask inwards
{
edge_mask(sl, in->w, in->h, mask, in->tol*200.0, -1);
break;
}
case 3: //Edge based mask outwards
{
edge_mask(sl, in->w, in->h, mask, in->tol*200.0, 1);
break;
}
}
hue_gate(sl, in->w, in->h, mask, in->krgb, in->Hgate, 0.5*in->Hgate);
sat_thres(sl, in->w, in->h, mask, in->Sthresh);
switch(in->op1) //OPERATION 1
{
case 0: break;
case 1: //De-Key
{
clean_rad_m(sl, in->w, in->h, in->krgb, mask, in->am1);
break;
}
case 2: //Target
{
clean_tgt_m(sl, in->w, in->h, in->krgb, mask, in->am1, in->trgb);
break;
}
case 3: //Desaturate
{
desat_m(sl, in->w, in->h, mask, in->am1, in->cm);
break;
}
case 4: //Luma adjust
{
luma_m(sl, in->w, in->h, mask, in->am1, in->cm);
break;
}
}
switch(in->op2) //OPERATION 2
{
case 0: break;
case 1: //De-Key
{
clean_rad_m(sl, in->w, in->h, in->krgb, mask, in->am2);
break;
}
case 2: //Target
{
clean_tgt_m(sl, in->w, in->h, in->krgb, mask, in->am2, in->trgb);
break;
}
case 3: //Desaturate
{
desat_m(sl, in->w, in->h, mask, in->am2, in->cm);
break;
}
case 4: //Luma adjust
{
luma_m(sl, in->w, in->h, mask, in->am2, in->cm);
break;
}
}
if (in->showmask) //REPLACE IMAGE WITH THE MASK
{
copy_mask_i(sl, in->w, in->h, mask);
}
if (in->m2a) //REPLACE ALPHA WITH THE MASK
{
copy_mask_a(sl, in->w, in->h, mask);
}
float_2_RGBA8888(sl, outframe, in->w, in->h);
free(mask);
free(sl);
}