1 /***************************************************************************
2
3 Hard ivtc removal for image
4
5 A B C D E -> A BC CD D E
6
7 copyright : (C) 2005 by mean
8 email : fixounet@free.fr
9 ***************************************************************************/
10
11 /***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 ***************************************************************************/
19
20 #include "ADM_default.h"
21 #include "ADM_videoFilterDynamic.h"
22
23 #include <math.h>
24
25 #include "ADM_videoFilter.h"
26
27 #include "DIA_enter.h"
28 #include "DIA_factory.h"
29
30 #define MUL 1
31 // Set it to 2 for post separate field
32
33 #include "ADM_vidBlendRemoval_param.h"
34
35 #define PROGRESSIVE 0x00000001
36 #define MAGIC_NUMBER (0xdeadbeef)
37 #define IN_PATTERN 0x00000002
38
39 class vidHardPDRemoval:public AVDMGenericVideoStream
40 {
41
42 protected:
43 virtual char *printConf ( void );
44 VideoCache *vidCache;
45 BLEND_REMOVER_PARAM *_param;
46 uint32_t _lastRemoved;
47 ADMImage *cand1,*cand2,*rebuild;
48 public:
49
50 vidHardPDRemoval ( AVDMGenericVideoStream * in, CONFcouple * setup );
51 virtual ~vidHardPDRemoval ();
52 virtual uint8_t getFrameNumberNoAlloc ( uint32_t frame, uint32_t * len,
53 ADMImage * data, uint32_t * flags );
54 uint8_t configure ( AVDMGenericVideoStream * instream );
55 virtual uint8_t getCoupledConf ( CONFcouple ** couples );
56
57 };
58 //***************************
59 static FILTER_PARAM field_unblend_template =
60 { 4,"threshold","show","noise","identical"};
61
62 VF_DEFINE_FILTER ( vidHardPDRemoval,field_unblend_template,
63 hardivtcremove,
64 QT_TR_NOOP ( ""Hard pulldown removal"" ),
65 1,
66 VF_ITERLACING,
67 QT_TR_NOOP ( "Remove IVTC that has been analog captured or resized.") );
68
69 //*************************************
configure(AVDMGenericVideoStream * in)70 uint8_t vidHardPDRemoval::configure (AVDMGenericVideoStream * in)
71 {
72 _in=in;
73
74 #define PX(x) &(_param->x)
75
76 diaElemUInteger thresh(PX(threshold),QT_TR_NOOP("_Threshold:"),0,99,
77 QT_TR_NOOP("If value is smaller than threshold it is considered valid."
78 " Smaller value might mean more false positive"));
79 diaElemUInteger noise(PX(noise),QT_TR_NOOP("_Noise:"),0,99,QT_TR_NOOP("If pixels are closer than noise, they are considered to be the same"));
80 diaElemUInteger identical(PX(identical),QT_TR_NOOP("_Identical:"),0,99,QT_TR_NOOP("If metric is less than identical, images are considered identical"));
81 diaElemToggle show(PX(show),QT_TR_NOOP("_Show metrics"),QT_TR_NOOP("Show metric in image (debug)"));
82
83 diaElem *elems[]={&thresh,&noise,&identical,&show};
84
85 if( diaFactoryRun(QT_TR_NOOP("Hard IVTC Removal"),sizeof(elems)/sizeof(diaElem *),elems))
86 {
87 _lastRemoved=0xFFFFFFF;
88 return 1;
89 }
90 return 0;
91 }
92 /*************************************/
printConf(void)93 char *vidHardPDRemoval::printConf (void)
94 {
95 ADM_FILTER_DECLARE_CONF( " Field Unblend Thresh:%d Noise:%d",_param->threshold,_param->noise);
96 }
hint(ADMImage * img)97 static void hint(ADMImage *img)
98 {
99 unsigned int hint;
100
101 hint= PROGRESSIVE;
102
103 hint |= IN_PATTERN;
104
105 PutHintingData(YPLANE(img), hint);
106
107 }
108 #define MAX_BLOCKS 50
109 /*************************************/
vidHardPDRemoval(AVDMGenericVideoStream * in,CONFcouple * couples)110 vidHardPDRemoval::vidHardPDRemoval (AVDMGenericVideoStream * in, CONFcouple * couples)
111 {
112
113 _in = in;
114 memcpy (&_info, _in->getInfo (), sizeof (_info));
115 _info.encoding = 1;
116 vidCache = new VideoCache (10, in);
117 _uncompressed=new ADMImage(_info.width,_info.height);
118 cand1=new ADMImage(_info.width,_info.height);
119 cand2=new ADMImage(_info.width,_info.height);
120 rebuild=new ADMImage(_info.width,_info.height);
121
122 _param=new BLEND_REMOVER_PARAM;
123 _lastRemoved=0xFFFFFFF;
124 if(couples)
125 {
126 #undef GET
127 #define GET(x) couples->getCouple(#x,&(_param->x))
128 GET (threshold);
129 GET (show);
130 GET (noise);
131 GET (identical);
132 }
133 else
134 {
135 _param->threshold=10;
136 _param->show=0;
137 _param->noise=5;
138 _param->identical=2;
139 }
140 }
141 //____________________________________________________________________
~vidHardPDRemoval()142 vidHardPDRemoval::~vidHardPDRemoval ()
143 {
144
145 delete vidCache;
146 vidCache = NULL;
147 delete _uncompressed;
148 _uncompressed=NULL;
149 delete _param;
150 _param=NULL;
151 delete cand1;
152 delete cand2;
153 delete rebuild;
154 cand1=NULL;
155 cand2=NULL;
156 rebuild=NULL;
157 }
158
159
160
161
162 /*
163
164 src=blend of srcP and R => src= 1/2(srcP+R)
165 2*src-srcP=R
166
167 srcN=blend of srcNN and R => srcN=1/2 (srcNN+R)
168 2*srcN-srcNN=R
169
170 */
171
tinyRestore(uint8_t * dst,uint8_t * srcP,uint8_t * src,uint8_t * srcN,uint8_t * srcNN,uint32_t w,uint32_t h)172 static uint8_t tinyRestore(uint8_t *dst, uint8_t *srcP, uint8_t *src,uint8_t *srcN,uint8_t *srcNN,uint32_t w, uint32_t h)
173 {
174
175
176 uint8_t *s,*sp,*sn,*snn,*d1;
177 int a1,a2,a3,a4,sum,delta;
178
179 sp=srcP;
180 s=src;
181 sn=srcN;
182 snn=srcNN;
183
184 d1=dst;
185
186 for(int y=0;y<h;y++)
187 for(int x=0;x<w;x++)
188 {
189 a1=*sp;
190 a2=*s;
191 a3=*sn;
192 a4=*snn;
193
194 sum=2*a2+2*a3-a1-a4;
195 sum=sum/2;
196
197 a1=sum;
198
199 if(a1<0) a1=0;
200 if(a1>255) a1=255;
201 *d1=a1;
202
203 s++;
204 sp++;
205 sn++;
206 snn++;
207 d1++;
208 }
209 return 1;
210 }
211 #ifdef ADM_CPU_X86
tinyRestoreMMX(uint8_t * dst,uint8_t * srcP,uint8_t * src,uint8_t * srcN,uint8_t * srcNN,uint32_t w,uint32_t h)212 static uint8_t tinyRestoreMMX(uint8_t *dst, uint8_t *srcP, uint8_t *src,uint8_t *srcN,uint8_t *srcNN,uint32_t w, uint32_t h)
213 {
214
215
216 uint8_t *s,*sp,*sn,*snn,*d1;
217 int a1,a2,a3,a4,sum,delta,l,ll;
218
219 sp=srcP;
220 s=src;
221 sn=srcN;
222 snn=srcNN;
223
224 d1=dst;
225
226 l=w*h;
227 ll=l>>2;
228 #ifdef GCC_2_95_X
229 __asm__(
230 "pxor %mm7,%mm7"
231 ::
232 );
233 #else
234 __asm__(
235 "pxor %%mm7,%%mm7"
236 ::
237 );
238 #endif
239 for(int x=0;x<ll;x++)
240 {
241 __asm__(
242 "movd (%0),%%mm0 \n"
243 "movd (%1),%%mm1 \n"
244 "movd (%2),%%mm2 \n"
245 "movd (%3),%%mm3 \n"
246 "punpcklbw %%mm7,%%mm0 \n"
247 "punpcklbw %%mm7,%%mm1 \n"
248 "punpcklbw %%mm7,%%mm2 \n"
249 "punpcklbw %%mm7,%%mm3 \n" //sum=2*m1+2*m2-m0-m3;
250
251 "paddw %%mm2,%%mm1 \n"
252 "paddw %%mm1,%%mm1 \n"
253 "paddw %%mm3,%%mm0 \n"
254
255 "psubusw %%mm0,%%mm1 \n" // mm1=sum
256 "psraw $1,%%mm1 \n" //2
257 "packuswb %%mm1, %%mm1\n"
258 "movd %%mm1,(%4) \n"
259
260 : : "r" (sp),"r" (s),"r"(sn),"r"(snn),"r"(d1)
261 );
262
263 s+=4;
264 sp+=4;
265 sn+=4;
266 snn+=4;
267 d1+=4;
268 }
269 if(l&3) tinyRestore(d1, sp, s,sn,snn,l&3, 1);
270 return 1;
271 }
272 #endif
273
restore(ADMImage * tgt,ADMImage * srcP,ADMImage * src,ADMImage * srcN,ADMImage * srcNN)274 static uint8_t restore(ADMImage *tgt,ADMImage *srcP,ADMImage *src,ADMImage *srcN,ADMImage *srcNN)
275 {
276 int delta;
277 uint32_t ww,hh;
278 uint8_t *s1,*s2,*d1;
279 int a1,a2,t1;
280
281 #ifdef ADM_CPU_X86
282 if(CpuCaps::hasMMX())
283 {
284 tinyRestoreMMX(YPLANE(tgt),YPLANE(srcP),YPLANE(src),YPLANE(srcN),YPLANE(srcNN),tgt->_width,tgt->_height);
285 tinyRestoreMMX(UPLANE(tgt),UPLANE(srcP),UPLANE(src),UPLANE(srcN),UPLANE(srcNN),tgt->_width>>1,tgt->_height>>1);
286 tinyRestoreMMX(VPLANE(tgt),VPLANE(srcP),VPLANE(src),VPLANE(srcN),VPLANE(srcNN),tgt->_width>>1,tgt->_height>>1);
287 return 1;
288 }
289 #endif
290
291 tinyRestore(YPLANE(tgt),YPLANE(srcP),YPLANE(src),YPLANE(srcN),YPLANE(srcNN),tgt->_width,tgt->_height);
292 tinyRestore(UPLANE(tgt),UPLANE(srcP),UPLANE(src),UPLANE(srcN),UPLANE(srcNN),tgt->_width>>1,tgt->_height>>1);
293 tinyRestore(VPLANE(tgt),VPLANE(srcP),VPLANE(src),VPLANE(srcN),VPLANE(srcNN),tgt->_width>>1,tgt->_height>>1);
294 return 1;
295
296
297 }
298
getFrameNumberNoAlloc(uint32_t inframe,uint32_t * len,ADMImage * data,uint32_t * flags)299 uint8_t vidHardPDRemoval::getFrameNumberNoAlloc (uint32_t inframe,
300 uint32_t * len,
301 ADMImage * data, uint32_t * flags)
302 {
303
304
305 ADMImage *srcP,*srcN,*srcNN,*src,*final,*display;
306 float distMerged, distN,distP,distM,distR,skip=0;
307 char txt[255];
308
309 if(inframe>= _info.nb_frames) return 0;
310 if(inframe<1 || inframe>inframe>_info.nb_frames-3 )
311 {
312 skip=1;
313 }
314 if(inframe>_lastRemoved+1 && inframe<_lastRemoved+5 )
315 {
316 skip=1;
317 }
318
319 if(skip)
320 {
321 data->duplicate(vidCache->getImage(inframe));
322 hint(data);
323 vidCache->unlockAll();
324 return 1;
325 }
326
327 if(_lastRemoved==inframe-1)
328 {
329 data->duplicate(rebuild);
330 hint(data);
331 if(_param->show&&inframe)
332 {
333 sprintf(txt," Telecined 2");
334 drawString(data,2,4,txt);
335 }
336
337 return 1;
338 }
339
340 //data->duplicate(rebuild);
341
342 srcP=vidCache->getImage(inframe-1);
343 src=vidCache->getImage(inframe);
344 srcN=vidCache->getImage(inframe+1);
345 srcNN=vidCache->getImage(inframe+2);
346
347 if(!src || !srcP || !srcN || !srcNN)
348 {
349 data->duplicate(vidCache->getImage(inframe));
350 vidCache->unlockAll();
351 return 1;
352 }
353
354 // Let's rebuild the pseudo R, where we have A AR RB B
355 // If then we got R1 very close to R2, and that AR is very close to src
356 // Decide it is hard telecined (frame blending)
357 #if 1
358 restore(rebuild,srcP,src,srcN,srcNN);
359 #else
360 cand1->substract(src,srcP);
361 cand2->substract(srcN,srcNN);
362 rebuild->merge(cand1,cand2);
363 #endif
364 #if 0
365 data->duplicate(rebuild);
366 vidCache->unlockAll();
367 return 1;
368 #endif
369
370 // And remerge...
371 cand1->merge(srcP,rebuild);
372 cand2->merge(srcNN,rebuild);
373
374 distP=ADMImage::lumaDiff(cand1,src,_param->noise);
375 distN=ADMImage::lumaDiff(cand2,srcN,_param->noise);
376 distM=ADMImage::lumaDiff(src,srcP,_param->noise);
377 distR=ADMImage::lumaDiff(src,srcN,_param->noise);
378
379
380
381 double medium;
382
383 if(distM>1&&distR>1)
384 {
385 if(distM>distR) medium=distR;
386 else medium=distM;
387 //medium=min(distM,distR);
388
389 medium/=100;
390 distN/=medium;
391 distP/=medium;
392 distR/=medium;
393
394 }
395
396 medium=medium/(_info.width*_info.height);
397 medium*=1000;
398
399 if(medium<_param->identical)
400 {
401 data->duplicate(src);
402 vidCache->unlockAll();
403 if(_param->show)
404 {
405 sprintf(txt," %% %02.1f : Identical",medium);
406 drawString(data,2,3,txt);
407
408 }
409 return 1;
410 }
411 double mn;
412
413 if(inframe == _lastRemoved+5)
414 {
415 distN=(distN*7)/10;
416 distP=(distP*7)/10;
417
418 }
419 //data->duplicate(src);
420 if(distN<_param->threshold && distP<_param->threshold)
421 {
422 data->duplicate(rebuild);
423 hint(data);
424 _lastRemoved=inframe;
425 if(_param->show && inframe == _lastRemoved+5)
426 {
427 sprintf(txt," Fav");
428 drawString(data,2,5,txt);
429 }
430 }
431 else
432 data->duplicate(src);
433 if(_param->show)
434 {
435 display=data;
436
437 sprintf(txt," N %02.1f",distN);
438 drawString(display,2,0,txt);
439
440 sprintf(txt," P %02.1f",distP);
441 drawString(display,2,1,txt);
442
443 sprintf(txt," R %02.1f",distR);
444 drawString(display,2,2,txt);
445
446 sprintf(txt," %% %02.1f",medium);
447 drawString(display,2,3,txt);
448
449 if(_lastRemoved==inframe)
450 {
451 sprintf(txt," Telecined 1",distP);
452 drawString(display,2,4,txt);
453 }
454 }
455
456
457
458 vidCache->unlockAll();
459 return 1;
460 }
getCoupledConf(CONFcouple ** couples)461 uint8_t vidHardPDRemoval::getCoupledConf (CONFcouple ** couples)
462 {
463
464 ADM_assert (_param);
465 *couples = new CONFcouple (4);
466 #undef CSET
467 #define CSET(x) (*couples)->setCouple(#x,(_param->x))
468 CSET (threshold);
469 CSET (show);
470 CSET (noise);
471 CSET (identical);
472
473 return 1;
474 }
475
476
477 //EOF
478