1 /*
2 keyspillm0pup.c
3
4 This Frei0r plugin cleans key color residue from composited video
5
6 Version 0.1 mar 2012
7
8 Copyright (C) 2012 Marko Cebokli http://lea.hamradio.si/~s57uuu
9
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
25 */
26
27 //Version 0.2
28
29 //compile: gcc -c -fPIC -Wall keyspillm0pup.c -o keyspillm0pup.o
30 //link: gcc -shared -o keyspillm0pup.so keyspillm0pup.o
31
32 //*******************************************************************
33
34 #include <stdio.h>
35 #include <math.h>
36 #include <frei0r.h>
37 #include <stdlib.h>
38 #include <math.h>
39 #include <assert.h>
40 #include <string.h>
41
42 double PI=3.14159265358979;
43
44 typedef struct
45 {
46 float r;
47 float g;
48 float b;
49 float a;
50 } float_rgba;
51
52 //----------------------------------------------------
RGBA8888_2_float(const uint32_t * in,float_rgba * out,int w,int h)53 void RGBA8888_2_float(const uint32_t* in, float_rgba *out, int w, int h)
54 {
55 uint8_t *cin;
56 int i;
57 float f1;
58
59 cin=(uint8_t *)in;
60 f1=1.0/255.0;
61 for (i=0;i<w*h;i++)
62 {
63 out[i].r=f1*(float)*cin++;
64 out[i].g=f1*(float)*cin++;
65 out[i].b=f1*(float)*cin++;
66 out[i].a=f1*(float)*cin++;
67 }
68 }
69
70 //------------------------------------------------------------------
float_2_RGBA8888(const float_rgba * in,uint32_t * out,int w,int h)71 void float_2_RGBA8888(const float_rgba *in, uint32_t* out, int w, int h)
72 {
73 uint8_t *cout;
74 int i;
75
76 cout=(uint8_t *)out;
77
78 for (i=0;i<w*h;i++)
79 {
80 *cout++=(uint8_t)(in[i].r*255.0);
81 *cout++=(uint8_t)(in[i].g*255.0);
82 *cout++=(uint8_t)(in[i].b*255.0);
83 *cout++=(uint8_t)(in[i].a*255.0);
84 }
85 }
86
87 //------------------------------------------------
88 //color coeffs according to rec 601 or rec 701
cocos(int cm,float * kr,float * kg,float * kb)89 void cocos(int cm, float *kr, float *kg, float *kb)
90 {
91 *kr=0.30; *kg=0.59; *kb=0.11; //da compiler ne jamra
92 switch (cm)
93 {
94 case 0:
95 {
96 *kr=0.30; *kg=0.59; *kb=0.11; //rec 601
97 break;
98 }
99 case 1:
100 {
101 *kr=0.2126; *kg=0.7152; *kb=0.0722; //rec 709
102 break;
103 }
104 default:
105 {
106 fprintf(stderr,"Unknown color model %d\n",cm);
107 break;
108 }
109 }
110 }
111
112 //-------------------------------------------------
113 //premakne barvo radialno stran od key
114 //sorazmerno maski
115 //*mask=float maska [0...1]
116 //k=key
117 //am=amount [0...1]
clean_rad_m(float_rgba * s,int w,int h,float_rgba k,float * mask,float am)118 void clean_rad_m(float_rgba *s, int w, int h, float_rgba k, float *mask, float am)
119 {
120 int i;
121 float aa,min;
122 min=0.5; //min aa = max color change
123
124 for (i=0;i<w*h;i++)
125 {
126 if (mask[i]==0.0) continue;
127 aa=1.0-am*(1.0-min)*mask[i];
128 s[i].r=(s[i].r-(1.0-aa)*k.r)/aa;
129 s[i].g=(s[i].g-(1.0-aa)*k.g)/aa;
130 s[i].b=(s[i].b-(1.0-aa)*k.b)/aa;
131 if (s[i].r<0.0) s[i].r=0.0;
132 if (s[i].g<0.0) s[i].g=0.0;
133 if (s[i].b<0.0) s[i].b=0.0;
134 if (s[i].r>1.0) s[i].r=1.0;
135 if (s[i].g>1.0) s[i].g=1.0;
136 if (s[i].b>1.0) s[i].b=1.0;
137 }
138 }
139
140 //-------------------------------------------------
141 //premakne barvo proti target
142 //sorazmerno maski
143 //*mask=float maska [0...1]
144 //k=key
145 //am=amount [0...1]
clean_tgt_m(float_rgba * s,int w,int h,float_rgba k,float * mask,float am,float_rgba tgt)146 void clean_tgt_m(float_rgba *s, int w, int h, float_rgba k, float *mask, float am, float_rgba tgt)
147 {
148 int i;
149 float a,aa,min;
150
151 min=0.5; //min aa = max color change
152
153 for (i=0;i<w*h;i++)
154 {
155 if (mask[i]==0.0) continue;
156
157 a=mask[i];
158 aa=a*am;
159 s[i].r=s[i].r+(tgt.r-s[i].r)*aa;
160 s[i].g=s[i].g+(tgt.g-s[i].g)*aa;
161 s[i].b=s[i].b+(tgt.b-s[i].b)*aa;
162 if (s[i].r<0.0) s[i].r=0.0;
163 if (s[i].g<0.0) s[i].g=0.0;
164 if (s[i].b<0.0) s[i].b=0.0;
165 if (s[i].r>1.0) s[i].r=1.0;
166 if (s[i].g>1.0) s[i].g=1.0;
167 if (s[i].b>1.0) s[i].b=1.0;
168 }
169 }
170
171 //----------------------------------------------------------
172 //desaturate colors according to mask
desat_m(float_rgba * s,int w,int h,float * mask,float des,int cm)173 void desat_m(float_rgba *s, int w, int h, float *mask, float des, int cm)
174 {
175 float a,y,cr,cb,kr,kg,kb,ikg;
176 int i;
177 float ds;
178
179 cocos(cm,&kr,&kg,&kb);
180 ikg=1.0/kg;
181
182 for (i=0;i<w*h;i++)
183 {
184 if (mask[i]==0.0) continue;
185
186 a=mask[i];
187 //separate luma/chroma
188 y=kr*s[i].r+kg*s[i].g+kb*s[i].b;
189 cr=s[i].r-y; // +-
190 cb=s[i].b-y; // +-
191 //desaturate
192 ds=1.0-des*a;
193 ds=ds*ds;
194 cr=cr*ds; cb=cb*ds;
195 //back to RGB
196 s[i].r=cr+y;
197 s[i].b=cb+y;
198 s[i].g=(y-kr*s[i].r-kb*s[i].b)*ikg;
199
200 if (s[i].r<0.0) s[i].r=0.0;
201 if (s[i].g<0.0) s[i].g=0.0;
202 if (s[i].b<0.0) s[i].b=0.0;
203 if (s[i].r>1.0) s[i].r=1.0;
204 if (s[i].g>1.0) s[i].g=1.0;
205 if (s[i].b>1.0) s[i].b=1.0;
206 }
207 }
208
209 //----------------------------------------------------------
210 //adjust luma according to mask
luma_m(float_rgba * s,int w,int h,float * mask,float lad,int cm)211 void luma_m(float_rgba *s, int w, int h, float *mask, float lad, int cm)
212 {
213 float a,m,mm,y,cr,cb,kr,kg,kb,ikg;
214 int i;
215
216 cocos(cm,&kr,&kg,&kb);
217 ikg=1.0/kg;
218 m=2.0*lad;
219
220 for (i=0;i<w*h;i++)
221 {
222 if (mask[i]==0.0) continue;
223
224 a=mask[i];
225 //separate luma/chroma
226 y=kr*s[i].r+kg*s[i].g+kb*s[i].b;
227 cr=s[i].r-y; // +-
228 cb=s[i].b-y; // +-
229 //adjust luma
230 mm=(m-1.0)*a+1.0;
231 if (m>=1.0) y=mm-1.0+y*(2.0-mm); else y=mm*y;
232 //back to RGB
233 s[i].r=cr+y;
234 s[i].b=cb+y;
235 s[i].g=(y-kr*s[i].r-kb*s[i].b)*ikg;
236
237 if (s[i].r<0.0) s[i].r=0.0;
238 if (s[i].g<0.0) s[i].g=0.0;
239 if (s[i].b<0.0) s[i].b=0.0;
240 if (s[i].r>1.0) s[i].r=1.0;
241 if (s[i].g>1.0) s[i].g=1.0;
242 if (s[i].b>1.0) s[i].b=1.0;
243 }
244 }
245
246 //---------------------------------------------------------
247 //do the blur for edge mask
248 //This is the fibe1o_8 function from the IIRblur plugin
249 //converted to scalar float (for planar color)
250 // 1-tap IIR v 4 smereh
251 //optimized for speed
252 //loops rearanged for more locality (better cache hit ratio)
253 //outer (vertical) loop 2x unroll to break dependency chain
254 //simplified indexes
fibe1o_f(float * s,int w,int h,float a,int ec)255 void fibe1o_f(float *s, int w, int h, float a, int ec)
256 {
257 int i,j;
258 float b,g,g4,avg,avg1,cr,g4a,g4b;
259 int p,pw,pj,pwj,pww,pmw;
260
261 avg=8; //koliko vzorcev za povprecje pri edge comp
262 avg1=1.0/avg;
263
264 g=1.0/(1.0-a);
265 g4=1.0/g/g/g/g;
266
267 //predpostavimo, da je "zunaj" crnina (nicle)
268 b=1.0/(1.0-a)/(1.0+a);
269
270 //prvih avg vrstic
271 for (i=0;i<avg;i++)
272 {
273 p=i*w;pw=p+w;
274 if (ec!=0)
275 {
276 cr=0.0;
277 for (j=0;j<avg;j++)
278 cr=cr+s[p+j];
279 cr=cr*avg1;
280 s[p]=cr*g+b*(s[p]-cr);
281 }
282
283 for (j=1;j<w;j++) //tja
284 s[p+j]=s[p+j]+a*s[p+j-1];
285
286 if (ec!=0)
287 {
288 cr=0.0;
289 for (j=w-avg;j<w;j++)
290 cr=cr+s[p+j];
291 cr=cr*avg1;
292 s[pw-1]=cr*g+b*(s[pw-1]-cr);
293 }
294 else
295 s[pw-1]=b*s[pw-1];
296
297 for (j=w-2;j>=0;j--) //nazaj
298 s[p+j]=a*s[p+j+1]+s[p+j];
299 }
300
301 //prvih avg vrstic samo navzdol (nazaj so ze)
302 for (i=0;i<w;i++)
303 {
304 if (ec!=0)
305 {
306 cr=0.0;
307 for (j=0;j<avg;j++)
308 cr=cr+s[i+w*j];
309 cr=cr*avg1;
310 s[i]=cr*g+b*(s[i]-cr);
311 }
312 for (j=1;j<avg;j++) //dol
313 s[i+j*w]=s[i+j*w]+a*s[i+w*(j-1)];
314 }
315
316 for (i=avg;i<h-1;i=i+2) //po vrsticah navzdol
317 {
318 p=i*w; pw=p+w; pww=pw+w; pmw=p-w;
319 if (ec!=0)
320 {
321 cr=0.0;
322 for (j=0;j<avg;j++)
323 cr=cr+s[p+j];
324 cr=cr*avg1;
325 s[p]=cr*g+b*(s[p]-cr);
326 cr=0.0;
327 for (j=0;j<avg;j++)
328 cr=cr+s[pw+j];
329 cr=cr*avg1;
330 s[pw]=cr*g+b*(s[pw]-cr);
331 }
332 for (j=1;j<w;j++) //tja
333 {
334 pj=p+j;pwj=pw+j;
335 s[pj]=s[pj]+a*s[pj-1];
336 s[pwj]=s[pwj]+a*s[pwj-1];
337 }
338
339 if (ec!=0)
340 {
341 cr=0.0;
342 for (j=w-avg;j<w;j++)
343 cr=cr+s[p+j];
344 cr=cr*avg1;
345 s[pw-1]=cr*g+b*(s[pw-1]-cr);
346 cr=0.0;
347 for (j=w-avg;j<w;j++)
348 cr=cr+s[pw+j];
349 cr=cr*avg1;
350 s[pww-1]=cr*g+b*(s[pww-1]-cr);
351 }
352 else
353 {
354 s[pw-1]=b*s[pw-1]; //rep H
355 s[pww-1]=b*s[pww-1];
356 }
357
358 //zacetek na desni
359 s[pw-2]=s[pw-2]+a*s[pw-1]; //nazaj
360 s[pw-1]=s[pw-1]+a*s[p-1]; //dol
361
362 for (j=w-2;j>=1;j--) //nazaj
363 {
364 pj=p+j;pwj=pw+j;
365 s[pj-1]=a*s[pj]+s[pj-1];
366 s[pwj]=a*s[pwj+1]+s[pwj];
367 //zdaj naredi se en piksel vertikalno dol, za vse stolpce
368 //dva nazaj, da ne vpliva na H nazaj
369 s[pj]=s[pj]+a*s[pmw+j];
370 s[pwj+1]=s[pwj+1]+a*s[pj+1];
371 }
372 //konec levo
373 s[pw]=s[pw]+a*s[pw+1]; //nazaj
374 s[p]=s[p]+a*s[pmw]; //dol
375 s[pw+1]=s[pw+1]+a*s[p+1]; //dol
376 s[pw]=s[pw]+a*s[p]; //dol
377 }
378
379 //ce je sodo stevilo vrstic, moras zadnjo posebej
380 if (i!=h)
381 {
382 p=i*w; pw=p+w;
383 for (j=1;j<w;j++) //tja
384 s[p+j]=s[p+j]+a*s[p+j-1];
385
386 s[pw-1]=b*s[pw-1]; //rep H
387
388 for (j=w-2;j>=0;j--) //nazaj in dol
389 {
390 s[p+j]=a*s[p+j+1]+s[p+j];
391 //zdaj naredi se en piksel vertikalno dol, za vse stolpce
392 //dva nazaj, da ne vpliva na H nazaj
393 s[p+j+1]=s[p+j+1]+a*s[p-w+j+1];
394 }
395 //levi piksel vert
396 s[p]=s[p]+a*s[p-w];
397 }
398
399 //zadnja vrstica (h-1)
400 g4b=g4*b;
401 g4a=g4/(1.0-a);
402 p=(h-1)*w;
403 if (ec!=0)
404 {
405 for (i=0;i<w;i++) //po stolpcih
406 {
407 cr=0.0;
408 for (j=h-avg;j<h;j++)
409 cr=cr+s[i+w*j];
410 cr=cr*avg1;
411 s[i+p]=g4a*cr+g4b*(s[i+p]-cr);
412 }
413 }
414 else
415 {
416 for (j=0;j<w;j++) //po stolpcih
417 s[j+p]=g4b*s[j+p]; //rep V
418 }
419
420 for (i=h-2;i>=0;i--) //po vrsticah navzgor
421 {
422 p=i*w; pw=p+w;
423 for (j=0;j<w;j++) //po stolpcih
424 s[p+j]=a*s[pw+j]+g4*s[p+j];
425 }
426
427 }
428
429 //----------------------------------------------------------
430 //mask based on euclidean RGB distance (alpha independent)
431 //mask values [0...1]
432 //fo=1 foreground only (alpha>0.005)
rgb_mask(float_rgba * s,int w,int h,float * mask,float_rgba k,float t,float p,int fo)433 void rgb_mask(float_rgba *s, int w, int h, float *mask, float_rgba k, float t, float p, int fo)
434 {
435 int i;
436 float dr,dg,db,d,ip,tr,a,de;
437
438 ip = (p>0.000001) ? 1.0/p : 1000000.0;
439 tr=1.0/3.0;
440 for (i=0;i<w*h;i++)
441 {
442 if ((fo==1)&&(s[i].a<0.005)) {mask[i]=0.0; continue;}
443
444 //euclidean RGB distance
445 dr=s[i].r-k.r;
446 dg=s[i].g-k.g;
447 db=s[i].b-k.b;
448 de=dr*dr+dg*dg+db*db;
449 de=de*tr; //max mozno=1.0
450
451 d=de;
452
453 if (d>(t+p))
454 a=1.0; //notranjost (alfa=1)
455 else
456 a=(d-t)*ip;
457 if (d<t) a=0.0; //blizu key, max efekt
458
459 mask[i]=1.0-a;
460 }
461 }
462
463 //----------------------------------------------------------
464 //mask based on hue difference (alpha independent)
465 //mask values [0...1]
466 //fo=1 foreground only (alpha>0.005)
hue_mask(float_rgba * s,int w,int h,float * mask,float_rgba k,float t,float p,int fo)467 void hue_mask(float_rgba *s, int w, int h, float *mask, float_rgba k, float t, float p, int fo)
468 {
469 int i;
470 float d,ip,tr,a;
471 float ka,k32,kh,kbb,ipi,b,hh;
472 float sa;
473
474 k32=sqrtf(3.0)/2.0;
475 ipi=1.0/PI;
476 ka=k.r-0.5*k.g-0.5*k.b;
477 kbb=k32*(k.g-k.b);
478 kh=atan2f(kbb,ka)*ipi; // [-1...+1]
479 sa=0.0; //da compiler ne jamra
480 //printf("color mask, key hue = %6.3f\n",kh);
481 //printf("color mask, key = %6.3f %6.3f %6.3f\n",k.r, k.g, k.b);
482 //printf("color mask, key a.b = %6.3f %6.3f\n",ka,kbb);
483
484 ip = (p>0.000001) ? 1.0/p : 1000000.0;
485 tr=1.0/3.0;
486 for (i=0;i<w*h;i++)
487 {
488 if ((fo==1)&&(s[i].a<0.005)) {mask[i]=0.0; continue;}
489
490 //difference of hue 2.6X pocasneje kot RGB...
491 a=s[i].r-0.5*s[i].g-0.5*s[i].b;
492 b=k32*(s[i].g-s[i].b);
493 hh=atan2f(b,a)*ipi; // [-1...1]
494 d = (hh>kh) ? hh-kh : kh-hh; // [0...2]
495 d = (d>1.0) ? 2.0-d : d; // [0...1] cir
496 // sa=hypotf(b,a)/(s[i].r+s[i].g+s[i].b+1.0E-6)*3.0;
497
498 if (d>(t+p))
499 a=1.0; //notranjost (alfa=1)
500 else
501 a=(d-t)*ip;
502 if (d<t) a=0.0; //blizu key, max efekt
503
504 mask[i]=1.0-a;
505 // mask[i]=hh; //debug
506 }
507 }
508
509 //----------------------------------------------------------
510 //mask values [0...1]
edge_mask(float_rgba * s,int w,int h,float * mask,float wd,int io)511 void edge_mask(float_rgba *s, int w, int h, float *mask, float wd, int io)
512 {
513 int i;
514 float a;
515 float lim;
516
517 lim=0.05; //clear mask below this value (good for speed)
518
519 //fully opaque areas
520 for (i=0;i<w*h;i++)
521 if (s[i].a>0.996) mask[i]=1.0; else mask[i]=0.0;
522
523 //blur mask
524 a=expf(logf(lim)/wd);
525 fibe1o_f(mask, w, h, a, 1);
526
527 //select edge
528 if (io==-1) //inside
529 for (i=0;i<w*h;i++)
530 {
531 if (mask[i]>0.5) mask[i]=2.0*(1.0-mask[i]); else mask[i]=0.0; //inside only
532 if (mask[i]<lim) mask[i]=0.0;
533 }
534
535 if (io==1) //outside
536 for (i=0;i<w*h;i++)
537 {
538 if (mask[i]<0.5) mask[i]=2.0*mask[i]; else mask[i]=0.0; //outside only AURA
539 if (mask[i]<lim) mask[i]=0.0;
540 }
541
542 }
543
544 //----------------------------------------------------------
545 //mask values [0...1]
546 //partially transparent areas
547 //useful as additional clean after key or edge
548 //amp=amplify mask for lower transparencies [0...1]
trans_mask(float_rgba * s,int w,int h,float * mask,float amp)549 void trans_mask(float_rgba *s, int w, int h, float *mask, float amp)
550 {
551 int i;
552 float ia;
553 //partially transparent areas
554 ia=1.0-amp;
555 for (i=0;i<w*h;i++)
556 if ((s[i].a<0.996)&&(s[i].a>0.004))
557 mask[i]=1.0-ia*s[i].a;
558 else
559 mask[i]=0.0;
560 }
561
562 //------------------------------------------------
563 //gate the mask based on similarity of hue to key
hue_gate(float_rgba * s,int w,int h,float * mask,float_rgba k,float t,float p)564 void hue_gate(float_rgba *s, int w, int h, float *mask, float_rgba k, float t, float p)
565 {
566 float k32,ka,kb,kh,ipi2,a,b,hh,d,aa,ip;
567 int i;
568
569 k32=sqrtf(3.0)/2.0;
570 ipi2=0.5/PI;
571 ip = (p>0.000001) ? 1.0/p : 1000000.0;
572
573 ka=k.r-0.5*k.g-0.5*k.b;
574 kb=k32*(k.g-k.b);
575 kh=atan2f(kb,ka)*ipi2; // +- 1.0
576
577 for (i=0;i<w*h;i++)
578 {
579 if (mask[i]==0.0) continue;
580
581 a=s[i].r-0.5*s[i].g-0.5*s[i].b;
582 b=k32*(s[i].g-s[i].b);
583 hh=atan2f(b,a)*ipi2;
584
585 d = (hh>kh) ? hh-kh : kh-hh; // [0...2]
586 d = (d>1.0) ? 2.0-d : d;
587
588 if (d>(t+p)) {mask[i]=0.0; continue;}
589 if (d<t) continue;
590
591 aa=1.0-(d-t)*ip;
592 mask[i]=mask[i]*aa;
593 }
594 }
595
596 //------------------------------------------------
597 //reduce the mask based on a saturation threshold
598 //intensity normalized saturation
sat_thres(float_rgba * s,int w,int h,float * mask,float th)599 void sat_thres(float_rgba *s, int w, int h, float *mask, float th)
600 {
601 float k32,ipi2,a,b,ip,sa;
602 float t1,t2;
603 int i;
604 float p=0.1; //sirina prehodnega pasu
605
606 t2=(1.0+p)*th; //above this, no mask change
607 t1=t2-p; //below this, mask=0
608
609 k32=sqrtf(3.0)/2.0;
610 ipi2=0.5/PI;
611 ip = (p>0.000001) ? 1.0/p : 1000000.0;
612
613 for (i=0;i<w*h;i++)
614 {
615 if (mask[i]==0.0) continue;
616
617 a=s[i].r-0.5*s[i].g-0.5*s[i].b;
618 b=k32*(s[i].g-s[i].b);
619 sa=hypotf(b,a)/(s[i].r+s[i].g+s[i].b+1.0E-6);
620
621 if (sa>t2) continue;
622 if (sa<t1) {mask[i]=0.0; continue;}
623 mask[i]=mask[i]*(sa-t1)*ip;
624 }
625 }
626
627 //--------------------------------------------------
copy_mask_i(float_rgba * sl,int w,int h,float * mask)628 void copy_mask_i(float_rgba *sl, int w, int h, float *mask)
629 {
630 int i;
631
632 for (i=0;i<w*h;i++)
633 {
634 sl[i].r=mask[i];
635 sl[i].g=mask[i];
636 sl[i].b=mask[i];
637 sl[i].a=1.0;
638 }
639 }
640
641 //--------------------------------------------------
copy_mask_a(float_rgba * sl,int w,int h,float * mask)642 void copy_mask_a(float_rgba *sl, int w, int h, float *mask)
643 {
644 int i;
645
646 for (i=0;i<w*h;i++)
647 {
648 sl[i].a=mask[i];
649 }
650 }
651
652 //***********************************************************
653 // FREI0R FUNKCIJE
654
655 //----------------------------------------
656 //struktura za instanco efekta
657 typedef struct
658 {
659 //status
660 int w,h;
661
662 //parameters
663 f0r_param_color_t key;
664 f0r_param_color_t tgt;
665 int maskType;
666 float tol;
667 float slope;
668 float Hgate;
669 float Sthresh;
670 int op1;
671 float am1;
672 int op2;
673 float am2;
674 int showmask;
675 int m2a;
676 int fo; //foreground only (speed)
677 int cm; //color model 0=rec601 1=rec 709
678
679 //internal variables
680 float_rgba krgb;
681 float_rgba trgb;
682 char *liststr;
683
684 } inst;
685
686 //-----------------------------------------------------
687 //stretch [0...1] to parameter range [min...max] linear
map_value_forward(double v,float min,float max)688 float map_value_forward(double v, float min, float max)
689 {
690 return min+(max-min)*v;
691 }
692
693 //-----------------------------------------------------
694 //collapse from parameter range [min...max] to [0...1] linear
map_value_backward(float v,float min,float max)695 double map_value_backward(float v, float min, float max)
696 {
697 return (v-min)/(max-min);
698 }
699
700 //***********************************************
701 // OBVEZNE FREI0R FUNKCIJE
702
703 //-----------------------------------------------
f0r_init()704 int f0r_init()
705 {
706 return 1;
707 }
708
709 //------------------------------------------------
f0r_deinit()710 void f0r_deinit()
711 {
712 }
713
714 //-----------------------------------------------
f0r_get_plugin_info(f0r_plugin_info_t * info)715 void f0r_get_plugin_info(f0r_plugin_info_t* info)
716 {
717
718 info->name="keyspillm0pup";
719 info->author="Marko Cebokli";
720 info->plugin_type=F0R_PLUGIN_TYPE_FILTER;
721 info->color_model=F0R_COLOR_MODEL_RGBA8888;
722 info->frei0r_version=FREI0R_MAJOR_VERSION;
723 info->major_version=0;
724 info->minor_version=3;
725 info->num_params=13;
726 info->explanation="Reduces the visibility of key color spill in chroma keying";
727 }
728
729 //--------------------------------------------------
f0r_get_param_info(f0r_param_info_t * info,int param_index)730 void f0r_get_param_info(f0r_param_info_t* info, int param_index)
731 {
732 switch(param_index)
733 {
734 case 0:
735 info->name = "Key color";
736 info->type = F0R_PARAM_COLOR;
737 info->explanation = "Key color that was used for chroma keying";
738 break;
739 case 1:
740 info->name = "Target color";
741 info->type = F0R_PARAM_COLOR;
742 info->explanation = "Desired color to replace key residue with";
743 break;
744 case 2:
745 info->name = "Mask type";
746 info->type = F0R_PARAM_STRING;
747 info->explanation = "Which mask to apply [0,1,2,3]";
748 break;
749 case 3:
750 info->name = "Tolerance";
751 info->type = F0R_PARAM_DOUBLE;
752 info->explanation = "Range of colors around the key, where effect is full strength";
753 break;
754 case 4:
755 info->name = "Slope";
756 info->type = F0R_PARAM_DOUBLE;
757 info->explanation = "Range of colors around the key where effect gradually decreases";
758 break;
759 case 5:
760 info->name = "Hue gate";
761 info->type = F0R_PARAM_DOUBLE;
762 info->explanation = "Restrict mask to hues close to key";
763 break;
764 case 6:
765 info->name = "Saturation threshold";
766 info->type = F0R_PARAM_DOUBLE;
767 info->explanation = "Restrict mask to saturated colors";
768 break;
769 case 7:
770 info->name = "Operation 1";
771 info->type = F0R_PARAM_STRING;
772 info->explanation = "First operation 1 [0,1,2]";
773 break;
774 case 8:
775 info->name = "Amount 1";
776 info->type = F0R_PARAM_DOUBLE;
777 info->explanation = "";
778 break;
779 case 9:
780 info->name = "Operation 2";
781 info->type = F0R_PARAM_STRING;
782 info->explanation = "Second operation 2 [0,1,2]";
783 break;
784 case 10:
785 info->name = "Amount 2";
786 info->type = F0R_PARAM_DOUBLE;
787 info->explanation = "";
788 break;
789 case 11:
790 info->name = "Show mask";
791 info->type = F0R_PARAM_BOOL;
792 info->explanation = "Replace image with the mask";
793 break;
794 case 12:
795 info->name = "Mask to Alpha";
796 info->type = F0R_PARAM_BOOL;
797 info->explanation = "Replace alpha channel with the mask";
798 break;
799 }
800 }
801
802 //----------------------------------------------
f0r_construct(unsigned int width,unsigned int height)803 f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
804 {
805 inst *in;
806
807 in=calloc(1,sizeof(inst));
808 in->w=width;
809 in->h=height;
810
811 //defaults
812 in->key.r = 0.1;
813 in->key.g = 0.8;
814 in->key.b = 0.1;
815 in->tgt.r = 0.78;
816 in->tgt.g = 0.5;
817 in->tgt.b = 0.4;
818 in->maskType=0;
819 in->tol=0.12;
820 in->slope=0.2;
821 in->Hgate=0.25;
822 in->Sthresh=0.15;
823 in->op1=1;
824 in->am1=0.55;
825 in->op2=0;
826 in->am2=0.0;
827 in->showmask=0;
828 in->m2a=0;
829 in->fo=1;
830 in->cm=1;
831
832 const char* sval = "0";
833 in->liststr = (char*)malloc( strlen(sval) + 1 );
834 strcpy( in->liststr, sval );
835
836 return (f0r_instance_t)in;
837 }
838
839 //---------------------------------------------------
f0r_destruct(f0r_instance_t instance)840 void f0r_destruct(f0r_instance_t instance)
841 {
842 free(instance);
843 }
844
845 //-----------------------------------------------------
f0r_set_param_value(f0r_instance_t instance,f0r_param_t parm,int param_index)846 void f0r_set_param_value(f0r_instance_t instance, f0r_param_t parm, int param_index)
847 {
848 inst *p;
849 double tmpf;
850 int chg,tmpi,nc;
851 f0r_param_color_t tmpc;
852 char *tmpch;
853
854 p=(inst*)instance;
855
856 chg=0;
857 switch(param_index)
858 {
859 case 0: //key color
860 tmpc=*(f0r_param_color_t*)parm;
861 if ((tmpc.r!=p->key.r) || (tmpc.g!=p->key.g) || (tmpc.b!=p->key.b))
862 chg=1;
863 p->key=tmpc;
864 p->krgb.r=p->key.r;
865 p->krgb.g=p->key.g;
866 p->krgb.b=p->key.b;
867 break;
868 case 1: //target color
869 tmpc=*(f0r_param_color_t*)parm;
870 if ((tmpc.r!=p->tgt.r) || (tmpc.g!=p->tgt.g) || (tmpc.b!=p->tgt.b))
871 chg=1;
872 p->tgt=tmpc;
873 p->trgb.r=p->tgt.r;
874 p->trgb.g=p->tgt.g;
875 p->trgb.b=p->tgt.b;
876 break;
877 case 2: //Mask type (list)
878 tmpch = (*(f0r_param_string*)parm);
879 if (strcmp(p->liststr, tmpch)) {
880 p->liststr = realloc( p->liststr, strlen(tmpch) + 1 );
881 strcpy( p->liststr, tmpch );
882 }
883 nc=sscanf(p->liststr,"%d",&tmpi);
884 // if ((nc<=0)||(tmpi<0)||(tmpi>3)) tmpi=1;
885 if ((nc<=0)||(tmpi<0)||(tmpi>3)) break;
886 if (p->maskType != tmpi) chg=1;
887 p->maskType = tmpi;
888 break;
889 case 3: //tolerance
890 tmpf=map_value_forward(*((double*)parm), 0.0, 0.5);
891 if (p->tol != tmpf) chg=1;
892 p->tol = tmpf;
893 break;
894 case 4: //slope
895 tmpf=map_value_forward(*((double*)parm), 0.0, 0.5);
896 if (p->slope != tmpf) chg=1;
897 p->slope = tmpf;
898 break;
899 case 5: //Hue gate
900 tmpf=*(double*)parm;
901 if (tmpf!=p->Hgate) chg=1;
902 p->Hgate=tmpf;
903 break;
904 case 6: //Saturation threshold
905 tmpf=*(double*)parm;
906 if (tmpf!=p->Sthresh) chg=1;
907 p->Sthresh=tmpf;
908 break;
909 case 7: //Operation 1 (list)
910 tmpch = (*(f0r_param_string*)parm);
911 if (strcmp(p->liststr, tmpch)) {
912 p->liststr = realloc( p->liststr, strlen(tmpch) + 1 );
913 strcpy( p->liststr, tmpch );
914 }
915 nc=sscanf(p->liststr,"%d",&tmpi);
916 // if ((nc<=0)||(tmpi<0)||(tmpi>4)) tmpi=0;
917 if ((nc<=0)||(tmpi<0)||(tmpi>4)) break;
918 if (p->op1 != tmpi) chg=1;
919 p->op1 = tmpi;
920 break;
921 case 8: //Amount 1
922 tmpf=*(double*)parm;
923 if (tmpf!=p->am1) chg=1;
924 p->am1=tmpf;
925 break;
926 case 9: //Operation 2 (list)
927 tmpch = (*(f0r_param_string*)parm);
928 if (strcmp(p->liststr, tmpch)) {
929 p->liststr = realloc( p->liststr, strlen(tmpch) + 1 );
930 strcpy( p->liststr, tmpch );
931 }
932 nc=sscanf(p->liststr,"%d",&tmpi);
933 // if ((nc<=0)||(tmpi<0)||(tmpi>4)) tmpi=0;
934 if ((nc<=0)||(tmpi<0)||(tmpi>4)) break;
935 if (p->op2 != tmpi) chg=1;
936 p->op2 = tmpi;
937 break;
938 case 10: //Amount 2
939 tmpf=*(double*)parm;
940 if (p->am2 != tmpf) chg=1;
941 p->am2 = tmpf;
942 break;
943 case 11: //Show mask (bool)
944 tmpf=*(double*)parm;
945 tmpi=roundf(tmpf);
946 if (tmpi!=p->showmask) chg=1;
947 p->showmask=tmpi;
948 break;
949 case 12: //Mask to Alpha (bool)
950 tmpf=*(double*)parm;
951 tmpi=roundf(tmpf);
952 if (tmpi!=p->m2a) chg=1;
953 p->m2a=tmpi;
954 break;
955 }
956
957 if (chg==0) return;
958
959 }
960
961 //--------------------------------------------------
f0r_get_param_value(f0r_instance_t instance,f0r_param_t param,int param_index)962 void f0r_get_param_value(f0r_instance_t instance, f0r_param_t param, int param_index)
963 {
964 inst *p;
965
966 p=(inst*)instance;
967
968 switch(param_index)
969 {
970 case 0: //key color
971 *((f0r_param_color_t*)param)=p->key;
972 break;
973 case 1: //target color
974 *((f0r_param_color_t*)param)=p->tgt;
975 break;
976 case 2: //Mask type (list)
977 p->liststr=realloc(p->liststr,16);
978 sprintf(p->liststr,"%d",p->maskType);
979 *((char**)param) = p->liststr;
980 break;
981 case 3: //tolerance
982 *((double*)param)=map_value_backward(p->tol, 0.0, 0.5);
983 break;
984 case 4: //slope
985 *((double*)param)=map_value_backward(p->slope, 0.0, 0.5);
986 break;
987 case 5: //Hue gate
988 *((double*)param)=p->Hgate;
989 break;
990 case 6: //Saturation threshold
991 *((double*)param)=p->Sthresh;
992 break;
993 case 7: //Operation 1 (list)
994 p->liststr=realloc(p->liststr,16);
995 sprintf(p->liststr,"%d",p->op1);
996 *((char**)param) = p->liststr;
997 break;
998 case 8: //Amount 1
999 *((double*)param)=p->am1;
1000 break;
1001 case 9: //Operation 2 (list)
1002 p->liststr=realloc(p->liststr,16);
1003 sprintf(p->liststr,"%d",p->op2);
1004 *((char**)param) = p->liststr;
1005 break;
1006 case 10: //Amount 2
1007 *((double*)param)=p->am2;
1008 break;
1009 case 11: //Show mask (bool)
1010 *((double*)param)=(double)p->showmask;
1011 break;
1012 case 12: //Mask 2 Alpha (bool)
1013 *((double*)param)=(double)p->m2a;
1014 break;
1015 }
1016 }
1017
1018 //==============================================================
f0r_update(f0r_instance_t instance,double time,const uint32_t * inframe,uint32_t * outframe)1019 void f0r_update(f0r_instance_t instance, double time, const uint32_t* inframe, uint32_t* outframe)
1020 {
1021 inst *in;
1022 //video buffers
1023 float *mask;
1024 float_rgba *sl;
1025
1026 assert(instance);
1027 in=(inst*)instance;
1028
1029 sl = calloc(in->w * in->h, sizeof(float_rgba));
1030 mask = calloc(in->w * in->h, sizeof(float));
1031
1032 RGBA8888_2_float(inframe, sl, in->w, in->h);
1033
1034 switch(in->maskType) //GENERATE MASK
1035 {
1036 case 0: //Color distance based mask
1037 {
1038 rgb_mask(sl, in->w, in->h, mask, in->krgb, in->tol, in->slope, in->fo);
1039 break;
1040 }
1041 case 1: //Transparency based mask
1042 {
1043 trans_mask(sl, in->w, in->h, mask, in->tol);
1044 break;
1045 }
1046 case 2: //Edge based mask inwards
1047 {
1048 edge_mask(sl, in->w, in->h, mask, in->tol*200.0, -1);
1049 break;
1050 }
1051 case 3: //Edge based mask outwards
1052 {
1053 edge_mask(sl, in->w, in->h, mask, in->tol*200.0, 1);
1054 break;
1055 }
1056 }
1057
1058 hue_gate(sl, in->w, in->h, mask, in->krgb, in->Hgate, 0.5*in->Hgate);
1059 sat_thres(sl, in->w, in->h, mask, in->Sthresh);
1060
1061 switch(in->op1) //OPERATION 1
1062 {
1063 case 0: break;
1064 case 1: //De-Key
1065 {
1066 clean_rad_m(sl, in->w, in->h, in->krgb, mask, in->am1);
1067 break;
1068 }
1069 case 2: //Target
1070 {
1071 clean_tgt_m(sl, in->w, in->h, in->krgb, mask, in->am1, in->trgb);
1072 break;
1073 }
1074 case 3: //Desaturate
1075 {
1076 desat_m(sl, in->w, in->h, mask, in->am1, in->cm);
1077 break;
1078 }
1079 case 4: //Luma adjust
1080 {
1081 luma_m(sl, in->w, in->h, mask, in->am1, in->cm);
1082 break;
1083 }
1084 }
1085
1086 switch(in->op2) //OPERATION 2
1087 {
1088 case 0: break;
1089 case 1: //De-Key
1090 {
1091 clean_rad_m(sl, in->w, in->h, in->krgb, mask, in->am2);
1092 break;
1093 }
1094 case 2: //Target
1095 {
1096 clean_tgt_m(sl, in->w, in->h, in->krgb, mask, in->am2, in->trgb);
1097 break;
1098 }
1099 case 3: //Desaturate
1100 {
1101 desat_m(sl, in->w, in->h, mask, in->am2, in->cm);
1102 break;
1103 }
1104 case 4: //Luma adjust
1105 {
1106 luma_m(sl, in->w, in->h, mask, in->am2, in->cm);
1107 break;
1108 }
1109 }
1110
1111 if (in->showmask) //REPLACE IMAGE WITH THE MASK
1112 {
1113 copy_mask_i(sl, in->w, in->h, mask);
1114 }
1115
1116 if (in->m2a) //REPLACE ALPHA WITH THE MASK
1117 {
1118 copy_mask_a(sl, in->w, in->h, mask);
1119 }
1120
1121
1122 float_2_RGBA8888(sl, outframe, in->w, in->h);
1123 free(mask);
1124 free(sl);
1125 }
1126