1 
2 /***************************************************************************
3     Port of Donal Graft Telecide which is (c) Donald Graft
4     http://www.neuron2.net
5     http://puschpull.org/avisynth/decomb_reference_manual.html
6 
7  ***************************************************************************/
8 /*
9 	Telecide plugin for Avisynth -- recovers original progressive
10 	frames from telecined streams. The filter operates by matching
11 	fields and automatically adapts to phase/pattern changes.
12 
13 	Copyright (C) 2003 Donald A. Graft
14 
15 	This program is free software; you can redistribute it and/or modify
16 	it under the terms of the GNU General Public License as published by
17 	the Free Software Foundation.
18 
19 	This program is distributed in the hope that it will be useful,
20 	but WITHOUT ANY WARRANTY; without even the implied warranty of
21 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 	GNU General Public License for more details.
23 
24 	You should have received a copy of the GNU General Public License
25 	along with this program; if not, write to the Free Software
26 	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 */
28 #include "ADM_default.h"
29 #include "Telecide.h"
30 #include "DIA_factory.h"
31 #include "Telecide_debug.h"
32 /**
33     \fn PutChosen
34 */
PutChosen(int frame,unsigned int chosen)35 void Telecide::PutChosen(int frame, unsigned int chosen)
36         {
37                 int f;
38 
39                 f = frame % CACHE_SIZE;
40                 if (frame < 0 /*|| frame > _info.nb_frames - 1*/ || cache[f].frame != frame)
41                         return;
42                 cache[f].chosen = chosen;
43         }
44 /**
45     \fn CacheInsert
46 */
47 
CacheInsert(int frame,unsigned int p,unsigned int pblock,unsigned int c,unsigned int cblock)48 void Telecide::CacheInsert(int frame, unsigned int p, unsigned int pblock,
49                                                                 unsigned int c, unsigned int cblock)
50 {
51         int f;
52 
53         f = frame % CACHE_SIZE;
54         if (frame < 0 )//|| frame > _info.nb_frames - 1)
55                 ADM_assert(0);
56         cache[f].frame = frame;
57         cache[f].metrics[P] = p;
58         if (f) cache[f-1].metrics[N] = p;
59         cache[f].metrics[C] = c;
60         cache[f].metrics[PBLOCK] = pblock;
61         cache[f].metrics[CBLOCK] = cblock;
62         cache[f].chosen = 0xff;
63 }
64 /**
65     \fn CachePurge
66 */
CachePurge(void)67 bool Telecide::CachePurge(void)
68 {
69         aprintf("Cache purge\n");
70         for (int i = 0; i < CACHE_SIZE; i++)
71 		{
72 			cache[i].frame = 0xffffffff;
73 			cache[i].chosen = 0xff;
74 		}
75         return true;
76 }
77 /**
78     \fn CacheQuery
79 */
CacheQuery(int frame,unsigned int * p,unsigned int * pblock,unsigned int * c,unsigned int * cblock)80 bool Telecide::CacheQuery(int frame, unsigned int *p, unsigned int *pblock,
81                                                                 unsigned int *c, unsigned int *cblock)
82 {
83         int f;
84 
85         f = frame % CACHE_SIZE;
86         if (frame < 0) // || frame > _info.nb_frames - 1)
87         {
88                 printf("Frame %d is out! \n",frame); //,_info.nb_frames-1);
89                 ADM_assert(0);
90         }
91         if (cache[f].frame != frame)
92         {
93                 return false;
94         }
95         *p = cache[f].metrics[P];
96         *c = cache[f].metrics[C];
97         *pblock = cache[f].metrics[PBLOCK];
98         *cblock = cache[f].metrics[CBLOCK];
99         return true;
100 }
101 /**
102     \fn Show
103 */
Show(ADMImage * dst,int frame)104 void Telecide::Show(ADMImage *dst, int frame)
105 {
106 	char use;
107 	teleCide *_param=&configuration;
108 
109 	if (chosen == P) use = 'p';
110 	else if (chosen == C) use = 'c';
111 	else use = 'n';
112 
113 	sprintf(buf, "Telecide %s", "ADM"); // VERSION
114 	DrawString(dst, 0, 0, buf);
115 
116 	sprintf(buf, "Copyright 2003 Donald A. Graft");
117 	DrawString(dst, 0, 1, buf);
118 
119 	sprintf(buf,"frame %d:", frame);
120 	DrawString(dst, 0, 3, buf);
121 
122 	sprintf(buf, "matches: %d  %d  %d", p, c, np);
123 	DrawString(dst, 0, 4, buf);
124 
125 	if (_param->post != POST_NONE)
126 	{
127 		sprintf(buf,"vmetrics: %d  %d  %d [chosen=%d]", pblock, cblock, npblock, vmetric);
128 		DrawString(dst, 0, 5, buf);
129 	}
130 
131 	if (_param->guide != GUIDE_NONE)
132 	{
133 		sprintf(buf, "pattern mismatch=%0.2f%%", mismatch);
134 		DrawString(dst, 0, 5 + (_param->post != POST_NONE), buf);
135 	}
136 
137 	sprintf(buf,"[%s %c]%s %s",
138 		found == true ? "forcing" : "using", use,
139 		_param->post != POST_NONE ? (film == true ? " [progressive]" : " [interlaced]") : "",
140 		_param->guide != GUIDE_NONE ? status : "");
141 	DrawString(dst, 0, 5 + (_param->post != POST_NONE) + (_param->guide != GUIDE_NONE), buf);
142 
143         sprintf(buf,"%s %s",
144 		(film == true ? " [progressive]" : " [interlaced]") ,
145 		status);
146 	DrawString(dst, 0, 6 + (_param->post != POST_NONE) + (_param->guide != GUIDE_NONE), buf);
147 }
148 /**
149     \fn Debug
150 */
Debug(int frame)151 void Telecide::Debug(int frame)
152 {
153 	char use;
154 
155 	if (chosen == P) use = 'p';
156 	else if (chosen == C) use = 'c';
157 	else use = 'n';
158 	sprintf(buf,"Telecide: frame %d: matches: %d %d %d", frame, p, c, np);
159 	OutputDebugString(buf);
160 	if (configuration.post != POST_NONE)
161 	{
162 		sprintf(buf,"Telecide: frame %d: vmetrics: %d %d %d [chosen=%d]", frame, pblock, cblock, npblock, vmetric);
163 		OutputDebugString(buf);
164 	}
165 	sprintf(buf,"Telecide: frame %d: [%s %c]%s %s", frame, found == true ? "forcing" : "using", use,
166 		configuration.post  != POST_NONE ? (film == true ? " [progressive]" : " [interlaced]") : "",
167 		configuration.guide != GUIDE_NONE ? status : "");
168 	OutputDebugString(buf);
169 }
170 /**
171     \fn configure
172 */
configure(void)173 bool Telecide::configure(void)
174 {
175 #define PX(x) &(configuration.x)
176 #define SZT(x) sizeof(x)/sizeof(diaMenuEntry )
177 
178     teleCide *_param=&configuration;
179 
180     ELEM_TYPE_FLOAT vthresh=(ELEM_TYPE_FLOAT)_param->vthresh;
181     ELEM_TYPE_FLOAT bthresh=(ELEM_TYPE_FLOAT)_param->bthresh;
182     ELEM_TYPE_FLOAT dthresh=(ELEM_TYPE_FLOAT)_param->dthresh;
183     ELEM_TYPE_FLOAT gthresh=(ELEM_TYPE_FLOAT)_param->gthresh;
184 
185          diaMenuEntry tStrategy[]={
186                              {GUIDE_NONE,   QT_TRANSLATE_NOOP("telecide","No strategy"),NULL},
187                              {GUIDE_32,     QT_TRANSLATE_NOOP("telecide","3:2 pulldown"),NULL},
188                              {GUIDE_22,     QT_TRANSLATE_NOOP("telecide","PAL/SECAM"),NULL},
189                              {GUIDE_32322,  QT_TRANSLATE_NOOP("telecide","NTSC converted from PAL"),NULL}
190                           };
191 
192           diaMenuEntry tField[]={
193                              {1,QT_TRANSLATE_NOOP("telecide","Top"),NULL},
194                              {0,QT_TRANSLATE_NOOP("telecide","Bottom"),NULL}
195           };
196 
197           diaMenuEntry tBackward[]={
198                              {NO_BACK,QT_TRANSLATE_NOOP("telecide","Never"),NULL},
199                              {BACK_ON_COMBED,QT_TRANSLATE_NOOP("telecide","If still combed"),NULL},
200                              {ALWAYS_BACK,QT_TRANSLATE_NOOP("telecide","Always"),NULL}
201           };
202 
203           diaMenuEntry tPostproc[]={
204                              {POST_NONE,      QT_TRANSLATE_NOOP("telecide","None"),NULL},
205                              {POST_METRICS,   QT_TRANSLATE_NOOP("telecide","None but compute"),NULL},
206                              {POST_FULL,      QT_TRANSLATE_NOOP("telecide","Postproc on best match"),NULL},
207                              {POST_FULL_MAP,  QT_TRANSLATE_NOOP("telecide","Postproc and show zones (debug)"),NULL},
208                              {POST_FULL_NOMATCH,QT_TRANSLATE_NOOP("telecide","Process image (not fields)"),NULL},
209                              {POST_FULL_NOMATCH_MAP,QT_TRANSLATE_NOOP("telecide","Process image (not fields), debug"),NULL}
210           };
211 
212 
213     diaElemMenu menuMode(PX(guide),   QT_TRANSLATE_NOOP("telecide","_Strategy:"), SZT(tStrategy),tStrategy);
214     diaElemMenu menuField(PX(order),  QT_TRANSLATE_NOOP("telecide","_Field order:"), SZT(tField),tField);
215     diaElemMenu menuPost(PX(post),    QT_TRANSLATE_NOOP("telecide","_Postprocessing:"), SZT(tPostproc),tPostproc);
216     diaElemMenu menuBackward(PX(back),QT_TRANSLATE_NOOP("telecide","_Try backward:"), SZT(tBackward),tBackward);
217 
218     diaElemFloat direct(&dthresh,QT_TRANSLATE_NOOP("telecide","_Direct threshold:"),0,200. );
219     diaElemFloat backward(&bthresh,QT_TRANSLATE_NOOP("telecide","_Backward threshold:"),0,200. );
220     diaElemFloat noise(&gthresh,QT_TRANSLATE_NOOP("telecide","_Noise threshold:"),0,200. );
221     diaElemFloat post(&vthresh,QT_TRANSLATE_NOOP("telecide","Postp_rocessing threshold:"),0,200. );
222 
223     diaElemToggle chroma(PX(chroma),QT_TRANSLATE_NOOP("telecide","_Use chroma to decide"));
224     diaElemToggle show(PX(show),QT_TRANSLATE_NOOP("telecide","Sho_w info"));
225     diaElemToggle debug(PX(debug),QT_TRANSLATE_NOOP("telecide","Debu_g"));
226     diaElemToggle blend(PX(blend),QT_TRANSLATE_NOOP("telecide","Bl_end"));
227 
228 
229 
230     diaElem *elems[]={&menuMode,&menuField,&menuPost,&menuBackward,
231         &direct,&backward,&noise,&post,&blend,
232         &chroma,&show,&debug    };
233 
234   if(diaFactoryRun(QT_TRANSLATE_NOOP("telecide","Decomb Telecide"),12,elems))
235   {
236 
237       _param->vthresh=(float)vthresh;
238       _param->bthresh=(float)bthresh;
239       _param->dthresh=(float)dthresh;
240       _param->gthresh=(float)gthresh;
241 
242     return 1;
243   }
244   return 0;
245 }
246 /**
247     \fn getConfiguration
248 */
getConfiguration(void)249 const char *Telecide::getConfiguration( void )
250 {
251 static char buf[100];
252  snprintf(buf,99," Decomb telecide");
253  return buf;
254 }
255 
256 #define PROGRESSIVE  0x00000001
257 #define IN_PATTERN   0x00000002
258 
259 /*
260   uint8_t PutHintingData(unsigned char *video, unsigned int hint);
261   uint8_t GetHintingData(unsigned char *video, unsigned int *hint);
262 */
263 /**
264     \fn WriteHints
265 */
WriteHints(unsigned char * dst,bool film,bool inpattern)266 void Telecide::WriteHints(unsigned char *dst, bool film, bool inpattern)
267         {
268                 unsigned int hint;
269 
270                 if (GetHintingData(dst, &hint) == true) hint = 0;
271                 if (film == true) hint |= PROGRESSIVE;
272                 else hint &= ~PROGRESSIVE;
273                 if (inpattern == true) hint |= IN_PATTERN;
274                 else hint &= ~IN_PATTERN;
275                 PutHintingData(dst, hint);
276         }
277 /**
278     \fn PredictHardYUY2
279 */
PredictHardYUY2(int frame,unsigned int * predicted,unsigned int * predicted_metric)280 bool Telecide::PredictHardYUY2(int frame, unsigned int *predicted, unsigned int *predicted_metric)
281         {
282                 // Look for pattern in the actual delivered matches of the previous cycle of frames.
283                 // If a pattern is found, use that to predict the current match.
284                 if (configuration.guide == GUIDE_22)
285                 {
286                         if (cache[(frame-cycle)%CACHE_SIZE  ].chosen == 0xff ||
287                                 cache[(frame-cycle+1)%CACHE_SIZE].chosen == 0xff)
288                                 return false;
289                         switch ((cache[(frame-cycle)%CACHE_SIZE  ].chosen << 4) +
290                                         (cache[(frame-cycle+1)%CACHE_SIZE].chosen))
291                         {
292                         case 0x11:
293                                 *predicted = C;
294                                 *predicted_metric = cache[frame%CACHE_SIZE].metrics[C];
295                                 break;
296                         case 0x22:
297                                 *predicted = N;
298                                 *predicted_metric = cache[frame%CACHE_SIZE].metrics[N];
299                                 break;
300                         default: return false;
301                         }
302                 }
303                 else if (configuration.guide == GUIDE_32)
304                 {
305                         if (cache[(frame-cycle)%CACHE_SIZE  ].chosen == 0xff ||
306                                 cache[(frame-cycle+1)%CACHE_SIZE].chosen == 0xff ||
307                                 cache[(frame-cycle+2)%CACHE_SIZE].chosen == 0xff ||
308                                 cache[(frame-cycle+3)%CACHE_SIZE].chosen == 0xff ||
309                                 cache[(frame-cycle+4)%CACHE_SIZE].chosen == 0xff)
310                                 return false;
311 
312                         switch ((cache[(frame-cycle)%CACHE_SIZE  ].chosen << 16) +
313                                         (cache[(frame-cycle+1)%CACHE_SIZE].chosen << 12) +
314                                         (cache[(frame-cycle+2)%CACHE_SIZE].chosen <<  8) +
315                                         (cache[(frame-cycle+3)%CACHE_SIZE].chosen <<  4) +
316                                         (cache[(frame-cycle+4)%CACHE_SIZE].chosen))
317                         {
318                         case 0x11122:
319                         case 0x11221:
320                         case 0x12211:
321                         case 0x12221:
322                         case 0x21122:
323                         case 0x11222:
324                                 *predicted = C;
325                                 *predicted_metric = cache[frame%CACHE_SIZE].metrics[C];
326                                 break;
327                         case 0x22111:
328                         case 0x21112:
329                         case 0x22112:
330                         case 0x22211:
331                                 *predicted = N;
332                                 *predicted_metric = cache[frame%CACHE_SIZE].metrics[N];
333                                 break;
334                         default: return false;
335                         }
336                 }
337                 else if (configuration.guide == GUIDE_32322)
338                 {
339                         if (cache[(frame-cycle)%CACHE_SIZE  ].chosen == 0xff ||
340                                 cache[(frame-cycle+1)%CACHE_SIZE].chosen == 0xff ||
341                                 cache[(frame-cycle+2)%CACHE_SIZE].chosen == 0xff ||
342                                 cache[(frame-cycle+3)%CACHE_SIZE].chosen == 0xff ||
343                                 cache[(frame-cycle+4)%CACHE_SIZE].chosen == 0xff ||
344                                 cache[(frame-cycle+5)%CACHE_SIZE].chosen == 0xff)
345                                 return false;
346 
347                         switch ((cache[(frame-cycle)%CACHE_SIZE  ].chosen << 20) +
348                                         (cache[(frame-cycle+1)%CACHE_SIZE].chosen << 16) +
349                                         (cache[(frame-cycle+2)%CACHE_SIZE].chosen << 12) +
350                                         (cache[(frame-cycle+3)%CACHE_SIZE].chosen <<  8) +
351                                         (cache[(frame-cycle+4)%CACHE_SIZE].chosen <<  4) +
352                                         (cache[(frame-cycle+5)%CACHE_SIZE].chosen))
353                         {
354                         case 0x111122:
355                         case 0x111221:
356                         case 0x112211:
357                         case 0x122111:
358                         case 0x111222:
359                         case 0x112221:
360                         case 0x122211:
361                         case 0x222111:
362                                 *predicted = C;
363                                 *predicted_metric = cache[frame%CACHE_SIZE].metrics[C];
364                                 break;
365                         case 0x221111:
366                         case 0x211112:
367 
368                         case 0x221112:
369                         case 0x211122:
370                                 *predicted = N;
371                                 *predicted_metric = cache[frame%CACHE_SIZE].metrics[N];
372                                 break;
373                         default: return false;
374                         }
375                 }
376 #ifdef DEBUG_PATTERN_GUIDANCE
377                 sprintf(buf, "%d: HARD: predicted = %d\n", frame, *predicted);
378                 OutputDebugString(buf);
379 #endif
380                 return true;
381         }
382 /**
383     \fn PredictSoftYUY2
384 */
PredictSoftYUY2(int frame)385 struct PREDICTION *Telecide::PredictSoftYUY2(int frame)
386         {
387                 // Use heuristics to look forward for a match.
388                 int i, j, y, c, n, phase;
389                 unsigned int metric;
390 
391                 pred[0].metric = 0xffffffff;
392                 if (frame < 0 /*|| frame > _info.nb_frames - 1 - cycle*/) return pred;
393 
394                 // Look at the next cycle of frames.
395                 for (y = frame + 1; y <= frame + cycle; y++)
396                 {
397                         // Look for a frame where the current and next match values are
398                         // very close. Those are candidates to predict the phase, because
399                         // that condition should occur only once per cycle. Store the candidate
400                         // phases and predictions in a list sorted by goodness. The list will
401                         // be used by the caller to try the phases in order.
402                         c = cache[y%CACHE_SIZE].metrics[C];
403                         n = cache[y%CACHE_SIZE].metrics[N];
404                         if (c == 0) c = 1;
405                         metric = (100 * abs (c - n)) / c;
406                         phase = y % cycle;
407                         if (metric < 5)
408                         {
409                                 // Place the new candidate phase in sorted order in the list.
410                                 // Find the insertion point.
411                                 i = 0;
412                                 while (metric > pred[i].metric) i++;
413                                 // Find the end-of-list marker.
414                                 j = 0;
415                                 while (pred[j].metric != 0xffffffff) j++;
416                                 // Shift all items below the insertion point down by one to make
417                                 // room for the insertion.
418                                 j++;
419                                 for (; j > i; j--)
420                                 {
421                                         pred[j].metric = pred[j-1].metric;
422                                         pred[j].phase = pred[j-1].phase;
423                                         pred[j].predicted = pred[j-1].predicted;
424                                         pred[j].predicted_metric = pred[j-1].predicted_metric;
425                                 }
426                                 // Insert the new candidate data.
427                                 pred[j].metric = metric;
428                                 pred[j].phase = phase;
429                                 if (configuration.guide == GUIDE_32)
430                                 {
431                                         switch ((frame % cycle) - phase)
432                                         {
433                                         case -4: pred[j].predicted = N; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[N]; break;
434                                         case -3: pred[j].predicted = N; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[N]; break;
435                                         case -2: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
436                                         case -1: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
437                                         case  0: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
438                                         case +1: pred[j].predicted = N; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[N]; break;
439                                         case +2: pred[j].predicted = N; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[N]; break;
440                                         case +3: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
441                                         case +4: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
442                                         }
443                                 }
444                                 else if (configuration.guide == GUIDE_32322)
445                                 {
446                                         switch ((frame % cycle) - phase)
447                                         {
448                                         case -5: pred[j].predicted = N; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[N]; break;
449                                         case -4: pred[j].predicted = N; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[N]; break;
450                                         case -3: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
451                                         case -2: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
452                                         case -1: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
453                                         case  0: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
454                                         case +1: pred[j].predicted = N; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[N]; break;
455                                         case +2: pred[j].predicted = N; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[N]; break;
456                                         case +3: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
457                                         case +4: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
458                                         case +5: pred[j].predicted = C; pred[j].predicted_metric = cache[frame%CACHE_SIZE].metrics[C]; break;
459                                         }
460                                 }
461                         }
462 #ifdef DEBUG_PATTERN_GUIDANCE
463                         sprintf(buf,"%d: metric = %d phase = %d\n", frame, metric, phase);
464                         OutputDebugString(buf);
465 #endif
466                 }
467                 return pred;
468         }
469 
470 /**
471     \fn CalculateMetrics
472 */
473 #define nt      configuration.nt
474 #define y0      configuration.y0
475 #define y1      configuration.y1
476 #define chroma  configuration.chroma
477 #define post    configuration.post
478 
479 #define GetPlane GetReadPtr
480 
CalculateMetrics(int frame,ADMImage * fcurrent,ADMImage * fprevious)481 void Telecide::CalculateMetrics(int frame, ADMImage *fcurrent, ADMImage *fprevious)
482 {
483 //int frame, unsigned char *fcrp, unsigned char *fcrpU, unsigned char *fcrpV,
484 //									unsigned char *fprp, unsigned char *fprpU, unsigned char *fprpV,
485 
486         int x, y, p, c, tmp1, tmp2, skip;
487         bool vc;
488         unsigned char *currbot0, *currbot2, *prevbot0, *prevbot2;
489         unsigned char *prevtop0, *prevtop2, *prevtop4, *currtop0, *currtop2, *currtop4;
490         unsigned char *a0, *a2, *b0, *b2, *b4;
491         unsigned int diff, index;
492 #define T 4
493 
494         /* Clear the block sums. */
495         for (y = 0; y < yblocks; y++)
496         {
497                 for (x = 0; x < xblocks; x++)
498                 {
499 #ifdef WINDOWED_MATCH
500                         matchp[y*xblocks+x] = 0;
501                         matchc[y*xblocks+x] = 0;
502 #endif
503                         sump[y*xblocks+x] = 0;
504                         sumc[y*xblocks+x] = 0;
505                 }
506         }
507 
508         /* Find the best field match. Subsample the frames for speed. */
509         uint32_t cpitch=fcurrent->GetPitch(PLANAR_Y);
510         uint32_t ppitch=fprevious->GetPitch(PLANAR_Y);
511         ADM_assert(cpitch==ppitch)
512 
513 
514         uint32_t cpitchu=fcurrent->GetPitch(PLANAR_U);
515         uint32_t cpitchv=fcurrent->GetPitch(PLANAR_V);
516 
517         ADM_assert(cpitchu==fprevious->GetPitch(PLANAR_U));
518         ADM_assert(cpitchv==fprevious->GetPitch(PLANAR_V));
519 
520         ADM_assert(cpitchu==cpitchv);
521 
522         uint32_t cpitchtimes4=cpitch<<2;
523 
524 
525         uint8_t  *fcrp=fcurrent->GetPlane(PLANAR_Y);
526         uint8_t  *fprp=fprevious->GetPlane(PLANAR_Y);
527 
528         uint8_t *fcrpU=fcurrent->GetPlane(PLANAR_U);
529         uint8_t *fcrpV=fcurrent->GetPlane(PLANAR_V);
530 
531         uint8_t *fprpU=fprevious->GetPlane(PLANAR_U);
532         uint8_t *fprpV=fprevious->GetPlane(PLANAR_V);
533 
534         uint32_t w=info.width;
535         uint32_t h=info.height;
536 
537         currbot0  = fcrp + cpitch;
538         currbot2  = fcrp + 3 * cpitch;
539         currtop0 = fcrp;
540         currtop2 = fcrp + 2 * cpitch;
541         currtop4 = fcrp + 4 * cpitch;
542         prevbot0  = fprp + ppitch;
543         prevbot2  = fprp + 3 * ppitch;
544         prevtop0 = fprp;
545         prevtop2 = fprp + 2 * ppitch;
546         prevtop4 = fprp + 4 * ppitch;
547         if (tff == true)
548         {
549                 a0 = prevbot0;
550                 a2 = prevbot2;
551                 b0 = currtop0;
552                 b2 = currtop2;
553                 b4 = currtop4;
554         }
555         else
556         {
557                 a0 = currbot0;
558                 a2 = currbot2;
559                 b0 = prevtop0;
560                 b2 = prevtop2;
561                 b4 = prevtop4;
562         }
563         p = c = 0;
564 
565         // Calculate the field match and film/video metrics.
566         //if (vi.IsYV12()) skip = 1;
567         if(1) skip=1;
568         else skip = 1 + (chroma == false);
569         for (y = 0, index = 0; y < h - 4; y+=4)
570         {
571                 /* Exclusion band. Good for ignoring subtitles. */
572                 if (y0 == y1 || y < y0 || y > y1)
573                 {
574                         for (x = 0; x < w;)
575                         {
576                                 if (1) //vi.IsYV12())
577                                         index = (y/BLKSIZE)*xblocks + x/BLKSIZE;
578                                 else
579                                         index = (y/BLKSIZE)*xblocks + x/BLKSIZE_TIMES2;
580 
581                                 // Test combination with current frame.
582                                 tmp1 = ((long)currbot0[x] + (long)currbot2[x]);
583 //                              diff = abs((long)currtop0[x] - (tmp1 >> 1));
584                                 diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1);
585                                 if (diff > nt)
586                                 {
587                                         c += diff;
588 #ifdef WINDOWED_MATCH
589                                         matchc[index] += diff;
590 #endif
591                                 }
592 
593                                 tmp1 = currbot0[x] + T;
594                                 tmp2 = currbot0[x] - T;
595                                 vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) ||
596                                          (tmp2 > currtop0[x] && tmp2 > currtop2[x]);
597                                 if (vc)
598                                 {
599                                         sumc[index]++;
600                                 }
601 
602                                 // Test combination with previous frame.
603                                 tmp1 = ((long)a0[x] + (long)a2[x]);
604                                 diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1);
605                                 if (diff > nt)
606                                 {
607                                         p += diff;
608 #ifdef WINDOWED_MATCH
609                                         matchp[index] += diff;
610 #endif
611                                 }
612 
613                                 tmp1 = a0[x] + T;
614                                 tmp2 = a0[x] - T;
615                                 vc = (tmp1 < b0[x] && tmp1 < b2[x]) ||
616                                          (tmp2 > b0[x] && tmp2 > b2[x]);
617                                 if (vc)
618                                 {
619                                         sump[index]++;
620                                 }
621 
622                                 x += skip;
623                                 if (!(x&3)) x += 4;
624                         }
625                 }
626                 currbot0 += cpitchtimes4;
627                 currbot2 += cpitchtimes4;
628                 currtop0 += cpitchtimes4;
629                 currtop2 += cpitchtimes4;
630                 currtop4 += cpitchtimes4;
631                 a0               += cpitchtimes4;
632                 a2               += cpitchtimes4;
633                 b0               += cpitchtimes4;
634                 b2               += cpitchtimes4;
635                 b4               += cpitchtimes4;
636         }
637 
638        // if (vi.IsYV12() && chroma == true)
639         if ( chroma == true)
640         {
641                 int z;
642 
643                 for (z = 0; z < 2; z++)
644                 {
645                         // Do the same for the U plane.
646                         if (z == 0)
647                         {
648                                 currbot0  = fcrpU + cpitchu;
649                                 currbot2  = fcrpU + 3 * cpitchu;
650                                 currtop0 = fcrpU;
651                                 currtop2 = fcrpU + 2 * cpitchu;
652                                 currtop4 = fcrpU + 4 * cpitchu;
653                                 prevbot0  = fprpU + cpitchu;
654                                 prevbot2  = fprpU + 3 * cpitchu;
655                                 prevtop0 = fprpU;
656                                 prevtop2 = fprpU + 2 * cpitchu;
657                                 prevtop4 = fprpU + 4 * cpitchu;
658                         }
659                         else
660                         {
661                                 currbot0  = fcrpV + cpitchv;
662                                 currbot2  = fcrpV + 3 * cpitchv;
663                                 currtop0 = fcrpV;
664                                 currtop2 = fcrpV + 2 * cpitchv;
665                                 currtop4 = fcrpV + 4 * cpitchv;
666                                 prevbot0  = fprpV + cpitchv;
667                                 prevbot2  = fprpV + 3 * cpitchv;
668                                 prevtop0 = fprpV;
669                                 prevtop2 = fprpV + 2 * cpitchv;
670                                 prevtop4 = fprpV + 4 * cpitchv;
671                         }
672                         if (tff == true)
673                         {
674                                 a0 = prevbot0;
675                                 a2 = prevbot2;
676                                 b0 = currtop0;
677                                 b2 = currtop2;
678                                 b4 = currtop4;
679                         }
680                         else
681                         {
682                                 a0 = currbot0;
683                                 a2 = currbot2;
684                                 b0 = prevtop0;
685                                 b2 = prevtop2;
686                                 b4 = prevtop4;
687                         }
688 
689                         for (y = 0, index = 0; y < h/2 - 4; y+=4)
690                         {
691                                 /* Exclusion band. Good for ignoring subtitles. */
692                                 if (y0 == y1 || y < y0/2 || y > y1/2)
693                                 {
694                                         for (x = 0; x < w/2;)
695                                         {
696                                                 if (1) //vi.IsYV12())
697                                                         index = (y/BLKSIZE)*xblocks + x/BLKSIZE;
698                                                 else
699                                                         index = (y/BLKSIZE)*xblocks + x/BLKSIZE_TIMES2;
700 
701                                                 // Test combination with current frame.
702                                                 tmp1 = ((long)currbot0[x] + (long)currbot2[x]);
703                                                 diff = abs((((long)currtop0[x] + (long)currtop2[x] + (long)currtop4[x])) - (tmp1 >> 1) - tmp1);
704                                                 if (diff > nt)
705                                                 {
706                                                         c += diff;
707 #ifdef WINDOWED_MATCH
708                                                         matchc[index] += diff;
709 #endif
710                                                 }
711 
712                                                 tmp1 = currbot0[x] + T;
713                                                 tmp2 = currbot0[x] - T;
714                                                 vc = (tmp1 < currtop0[x] && tmp1 < currtop2[x]) ||
715                                                          (tmp2 > currtop0[x] && tmp2 > currtop2[x]);
716                                                 if (vc)
717                                                 {
718                                                         sumc[index]++;
719                                                 }
720 
721                                                 // Test combination with previous frame.
722                                                 tmp1 = ((long)a0[x] + (long)a2[x]);
723                                                 diff = abs((((long)b0[x] + (long)b2[x] + (long)b4[x])) - (tmp1 >> 1) - tmp1);
724                                                 if (diff > nt)
725                                                 {
726                                                         p += diff;
727 #ifdef WINDOWED_MATCH
728                                                         matchp[index] += diff;
729 #endif
730                                                 }
731 
732                                                 tmp1 = a0[x] + T;
733                                                 tmp2 = a0[x] - T;
734                                                 vc = (tmp1 < b0[x] && tmp1 < b2[x]) ||
735                                                          (tmp2 > b0[x] && tmp2 > b2[x]);
736                                                 if (vc)
737                                                 {
738                                                         sump[index]++;
739                                                 }
740 
741                                                 x ++;
742                                                 if (!(x&3)) x += 4;
743                                         }
744                                 }
745                                 currbot0 += 4*cpitchv;
746                                 currbot2 += 4*cpitchv;
747                                 currtop0 += 4*cpitchv;
748                                 currtop2 += 4*cpitchv;
749                                 currtop4 += 4*cpitchv;
750                                 a0               += 4*cpitchv;
751                                 a2               += 4*cpitchv;
752                                 b0               += 4*cpitchv;
753                                 b2               += 4*cpitchv;
754                                 b4               += 4*cpitchv;
755                         }
756                 }
757         }
758 
759         // Now find the blocks that have the greatest differences.
760 #ifdef WINDOWED_MATCH
761         highest_matchp = 0;
762         for (y = 0; y < yblocks; y++)
763         {
764                 for (x = 0; x < xblocks; x++)
765                 {
766 if (frame == 45 && matchp[y * xblocks + x] > 2500)
767 {
768         sprintf(buf, "%d/%d = %d\n", x, y, matchp[y * xblocks + x]);
769         OutputDebugString(buf);
770 }
771                         if (matchp[y * xblocks + x] > highest_matchp)
772                         {
773                                 highest_matchp = matchp[y * xblocks + x];
774                         }
775                 }
776         }
777         highest_matchc = 0;
778         for (y = 0; y < yblocks; y++)
779         {
780                 for (x = 0; x < xblocks; x++)
781                 {
782 if (frame == 44 && matchc[y * xblocks + x] > 2500)
783 {
784         sprintf(buf, "%d/%d = %d\n", x, y, matchc[y * xblocks + x]);
785         OutputDebugString(buf);
786 }
787                         if (matchc[y * xblocks + x] > highest_matchc)
788                         {
789                                 highest_matchc = matchc[y * xblocks + x];
790                         }
791                 }
792         }
793 #endif
794         if (post != POST_NONE)
795         {
796                 highest_sump = 0;
797                 for (y = 0; y < yblocks; y++)
798                 {
799                         for (x = 0; x < xblocks; x++)
800                         {
801                                 if (sump[y * xblocks + x] > highest_sump)
802                                 {
803                                         highest_sump = sump[y * xblocks + x];
804                                 }
805                         }
806                 }
807                 highest_sumc = 0;
808                 for (y = 0; y < yblocks; y++)
809                 {
810                         for (x = 0; x < xblocks; x++)
811                         {
812                                 if (sumc[y * xblocks + x] > highest_sumc)
813                                 {
814                                         highest_sumc = sumc[y * xblocks + x];
815                                 }
816                         }
817                 }
818         }
819 #ifdef WINDOWED_MATCH
820         CacheInsert(frame, highest_matchp, highest_sump, highest_matchc, highest_sumc);
821 #else
822         CacheInsert(frame, p, highest_sump, c, highest_sumc);
823 #endif
824 }
825 /**
826     \fn blendPlane
827     \brief We do it inplace
828 */
blendPlane(ADMImage * final,ADMImage * src,ADM_PLANE plane)829 bool Telecide::blendPlane(ADMImage *final,ADMImage *src, ADM_PLANE plane)
830 {
831 uint8_t *finalp=final->GetWritePtr(plane);
832 uint8_t *dstp=src->GetReadPtr(plane);
833 
834 
835 uint32_t fpitch=final->GetPitch(plane);
836 uint32_t dpitch=src->GetPitch(plane);
837 
838 uint32_t h=final->GetHeight(plane);
839 uint32_t w=final->GetWidth(plane);
840 
841 uint8_t *dstpn,*dstn,*dstpp;
842 
843 uint8_t CLAMP=235;
844 
845     float dthresh=configuration.dthresh;
846     int v1,v2;
847 
848     if(plane!=0) CLAMP=128;
849 
850 // Do first and last lines.
851 
852     dstpn = dstp + dpitch;
853     for (int x = 0; x < w; x++)
854     {
855             finalp[x] = (((int)dstp[x] + (int)dstpn[x]) >> 1);
856     }
857     finalp = final->GetWritePtr(plane) + (h-1)*fpitch;
858     dstp = src->GetWritePtr(plane) + (h-1)*dpitch;
859 
860     dstpp = dstp - dpitch;
861     for (int x = 0; x < w; x++)
862     {
863             finalp[x] = (((int)dstp[x] + (int)dstpp[x]) >> 1);
864     }
865     // Now do the rest.
866     dstp = src->GetWritePtr(plane) + dpitch;
867     dstpp = dstp - dpitch;
868     dstpn = dstp + dpitch;
869     finalp = final->GetWritePtr(plane) + fpitch;
870     for (int y = 1; y < h - 1; y++)
871     {
872             for (int x = 0; x < w; x++)
873             {
874                     v1 = (int)(dstp[x] - dthresh);
875                     if (v1 < 0) v1 = 0;
876                     v2 = (int) (dstp[x] + dthresh);
877                     if (v2 > 235) v2 = 235;
878                     if ((v1 > dstpp[x] && v1 > dstpn[x]) || (v2 < dstpp[x] && v2 < dstpn[x]))
879                     {
880                             if (post == POST_FULL_MAP || post == POST_FULL_NOMATCH_MAP)
881                             {
882                                     finalp[x] = CLAMP;
883                             }
884                             else
885                                     finalp[x] = ((int)dstpp[x] + (int)dstpn[x] + (int)dstp[x] + (int)dstp[x]) >> 2;
886                     }
887                     else finalp[x] = dstp[x];
888             }
889             finalp += fpitch;
890             dstp += dpitch;
891             dstpp += dpitch;
892             dstpn += dpitch;
893     }
894     return true;
895 }
896 /**
897     \fn interpolatePlane
898 */
interpolatePlane(ADMImage * dst,ADM_PLANE plane)899 bool Telecide::interpolatePlane(ADMImage *dst, ADM_PLANE plane)
900 {
901 
902     // Interpolate mode.
903     // Luma plane.
904     uint32_t dpitch=dst->GetPitch(plane);
905     uint8_t *dstp = dst->GetWritePtr(plane) + dpitch;
906     uint32_t w=dst->GetWidth(plane);
907     uint32_t h=dst->GetHeight(plane);
908 
909     uint8_t *dstpp = dstp - dpitch;
910     uint8_t *dstpn = dstp + dpitch;
911 
912     uint8_t CLAMP=235;
913     int v1,v2;
914     if(plane!=0) CLAMP=128;
915 
916     float dthresh=configuration.dthresh;
917 
918     for (int y = 1; y < h - 1; y+=2)
919     {
920             for (int x = 0; x < w; x++)
921             {
922                     v1 = (int) (dstp[x] - dthresh);
923                     if (v1 < 0) v1 = 0;
924                     v2 = (int) dstp[x] + dthresh;
925                     if (v2 > 235) v2 = 235;
926                     if ((v1 > dstpp[x] && v1 > dstpn[x]) || (v2 < dstpp[x] && v2 < dstpn[x]))
927                     {
928                             if (post == POST_FULL_MAP || post == POST_FULL_NOMATCH_MAP)
929                             {
930                                     dstp[x] = CLAMP;
931                             }
932                             else
933                                     dstp[x] = (dstpp[x] + dstpn[x]) >> 1;
934                     }
935             }
936             dstp += 2*dpitch;
937             dstpp += 2*dpitch;
938             dstpn += 2*dpitch;
939     }
940 
941     return true;
942 }
943 
944 // EOF
945