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