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