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(>hresh,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