1 /***************************************************************************
2 ADM_vidDecTelecide - description
3 -------------------
4
5 email : fixounet@free.fr
6
7 Port of Donal Graft Decimate which is (c) Donald Graft
8 http://www.neuron2.net
9 http://puschpull.org/avisynth/decomb_reference_manual.html
10
11 ***************************************************************************/
12
13 /*
14 Decimate plugin for Avisynth -- performs 1-in-N
15 decimation on a stream of progressive frames, which are usually
16 obtained from the output of my Telecide plugin for Avisynth.
17 For each group of N successive frames, this filter deletes the
18 frame that is most similar to its predecessor. Thus, duplicate
19 frames coming out of Telecide can be removed using Decimate. This
20 filter adjusts the frame rate of the clip as
21 appropriate. Selection of the cycle size is selected by specifying
22 a parameter to Decimate() in the Avisynth scipt.
23
24 Copyright (C) 2003 Donald A. Graft
25
26 This program is free software; you can redistribute it and/or modify
27 it under the terms of the GNU General Public License as published by
28 the Free Software Foundation.
29
30 This program is distributed in the hope that it will be useful,
31 but WITHOUT ANY WARRANTY; without even the implied warranty of
32 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 GNU General Public License for more details.
34
35 You should have received a copy of the GNU General Public License
36 along with this program; if not, write to the Free Software
37 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
38
39 The author can be contacted at:
40 Donald Graft
41 neuron2@attbi.com.
42 */
43
44 #include "ADM_default.h"
45 #include "decimate.h"
46 //#define BENCH 1
47 typedef uint32_t decimateDeltaLine(uint8_t *ptr1,uint8_t *ptr2,int size,int inc,unsigned int *sums);
48 /**
49 \fn decimateDeltaLineC
50 \brief C version
51 */
decimateDeltaLineC(uint8_t * ptr1,uint8_t * ptr2,int size,int inc,unsigned int * sums)52 static inline uint32_t decimateDeltaLineC(uint8_t *ptr1,uint8_t *ptr2,int size,int inc,unsigned int *sums)
53 {
54 uint32_t total=0;
55 for (int x = 0; x < size;)
56 {
57 unsigned int quadsum=0;
58 quadsum += abs((int)ptr1[x+0] - (int)ptr2[x+0]);
59 quadsum += abs((int)ptr1[x+1] - (int)ptr2[x+1]);
60 quadsum += abs((int)ptr1[x+2] - (int)ptr2[x+2]);
61 quadsum += abs((int)ptr1[x+3] - (int)ptr2[x+3]);
62 sums[(x+0)/BLKSIZE]+=quadsum; // 4 increment , BLKSIZE=32 => they are all in the same block
63 #ifdef BENCH
64 total+=quadsum;
65 #endif
66 x+=inc;
67 }
68 return total;
69 }
70 #ifdef ADM_CPU_X86
71 /**
72 \fn decimateDeltaLineSSE
73 \brief SSE/MMX version
74 Only works for inc=4, BLKSIZE=32
75 */
decimateDeltaLineSSE(uint8_t * ptr1,uint8_t * ptr2,int size,int inc,unsigned int * sums)76 static inline uint32_t decimateDeltaLineSSE(uint8_t *ptr1,uint8_t *ptr2,int size,int inc,unsigned int *sums)
77 {
78 uint32_t total1=0,total2=0;
79 #ifdef BENCH
80 total2=decimateDeltaLineC(ptr1,ptr2,size,inc,sums);
81 #endif
82 int size32=size>>5;
83 int left=(size & 31);
84 ADM_assert(inc==4);
85 ADM_assert(BLKSIZE==32);
86 uint64_t out;
87 uint64_t sum;
88 for(int i=0;i<size32;i++)
89 {
90
91 __asm__(
92 "pxor %%mm2,%%mm2 \n"
93 "\n"
94 "movq 0(%1),%%mm0 \n"
95 "movq 0(%2),%%mm1 \n"
96 "psadbw %%mm1,%%mm0 \n"
97 "paddq %%mm0,%%mm2 \n"
98 "\n"
99 "movq 8(%1),%%mm0 \n"
100 "movq 8(%2),%%mm1 \n"
101 "psadbw %%mm1,%%mm0 \n"
102 "paddq %%mm0,%%mm2 \n"
103 "\n"
104 "movq 16(%1),%%mm0 \n"
105 "movq 16(%2),%%mm1 \n"
106 "psadbw %%mm1,%%mm0 \n"
107 "paddq %%mm0,%%mm2 \n"
108 "\n"
109 "movq 24(%1),%%mm0 \n"
110 "movq 24(%2),%%mm1 \n"
111 "psadbw %%mm1,%%mm0 \n"
112 "paddq %%mm0,%%mm2 \n"
113 // move mm2 to sum
114 "movq %%mm2,%0\n"
115 : "=m"(sum): "r" (ptr1),"r" (ptr2));
116 sums[i]+=sum;
117 total1+=sum;
118 // printf("Sum : %d\n",(int)sum);
119 ptr1+=32;
120 ptr2+=32;
121 }
122 // collect leftover
123 for(int x=0;x<left;)
124 {
125 unsigned int quadsum=0;
126 quadsum += abs((int)ptr1[x+0] - (int)ptr2[x+0]);
127 quadsum += abs((int)ptr1[x+1] - (int)ptr2[x+1]);
128 quadsum += abs((int)ptr1[x+2] - (int)ptr2[x+2]);
129 quadsum += abs((int)ptr1[x+3] - (int)ptr2[x+3]);
130 sums[size32+(x+0)/BLKSIZE]+=quadsum; // 4 increment , BLKSIZE=32 => they are all in the same block
131 x+=inc;
132 total1+=quadsum;
133 }
134
135 __asm__(
136 "emms\n"
137 ::
138 );
139 #ifdef BENCH
140 if(total1!=total2)
141 {
142 ADM_error("SSE version does not match %d(C) vs %d(SSE)\n",(int)total2,(int)total1);
143 }else
144 {
145 ADM_info("SSE matches C version\n");
146 }
147 #endif
148 return total1;
149 }
150 #endif
151 /**
152 \fn computeDiff
153 \brief compute difference between image and its predecessor
154 */
computeDiff(ADMImage * current,ADMImage * previous)155 uint32_t Decimate::computeDiff(ADMImage *current,ADMImage *previous)
156 {
157 uint8_t *prevY = previous->GetReadPtr(PLANAR_Y);
158 uint8_t *currY = current->GetReadPtr(PLANAR_Y);
159 uint32_t prevPitch=previous->GetPitch(PLANAR_Y);
160 uint32_t curPitch=current->GetPitch(PLANAR_Y);
161 deciMate *_param=&configuration;
162 // Zero
163 memset(sum,0,sizeof(unsigned int)*xblocks*yblocks);
164 // Raw diff
165 int height=info.height;
166 int width=info.width;
167
168 if (_param->quality == 0 || _param->quality == 1) // subsampled
169 {
170 for (int y = 0; y < height; y++)
171 {
172 unsigned int *xsum=sum+((y/BLKSIZE)*xblocks);
173 decimateDeltaLineC(currY,prevY,width,4+12,xsum);
174 prevY += prevPitch;
175 currY += curPitch;
176 }
177 }else
178 {
179 int inc=4;
180 decimateDeltaLine *func=decimateDeltaLineC;
181 #ifdef ADM_CPU_X86
182 if(CpuCaps::hasSSE())
183 func=decimateDeltaLineSSE;
184 #endif
185
186 for (int y = 0; y < height; y++)
187 {
188 unsigned int *xsum=sum+((y/BLKSIZE)*xblocks);
189 func(currY,prevY,width,4,xsum);
190 prevY += prevPitch;
191 currY += curPitch;
192 }
193 }
194 //#warning DO CHROMA SAMPLING
195 #if 0
196 if (_param->quality == 1 || _param->quality == 3)
197 {
198 // also do u & v
199 prevU = storepU[f-1];
200 prevV = storepV[f-1];
201 currU = storepU[f];
202 currV = storepV[f];
203 for (y = 0; y < heightUV; y++)
204 {
205 for (x = 0; x < row_sizeUV;)
206 {
207 sum[((2*y)/BLKSIZE)*xblocks+(2*x)/BLKSIZE] += abs((int)currU[x] - (int)prevU[x]);
208 sum[((2*y)/BLKSIZE)*xblocks+(2*x)/BLKSIZE] += abs((int)currV[x] - (int)prevV[x]);
209 x++;
210 if (_param->quality == 1)
211 {
212 if (!(x%4)) x += 12;
213 }
214 }
215 prevU += pitchUV;
216 currU += pitchUV;
217 prevV += pitchUV;
218 currV += pitchUV;
219
220 }
221 }
222 #endif
223 uint32_t highest_sum = 0;
224 for (int y = 0; y < yblocks; y++)
225 {
226 for (int x = 0; x < xblocks; x++)
227 {
228 if (sum[y*xblocks+x] > highest_sum)
229 {
230 highest_sum = sum[y*xblocks+x];
231 }
232 }
233 }
234 return highest_sum;
235 }
236 /**
237 \fn FindDuplicate
238 */
FindDuplicate(int frame,int * chosen,double * metric,bool * forced)239 void Decimate::FindDuplicate(int frame, int *chosen, double *metric, bool *forced)
240 {
241 int f;
242 ADMImage * store[MAX_CYCLE_SIZE+1];
243 deciMate *_param=&configuration;
244 int lowest_index, div;
245 unsigned int count[MAX_CYCLE_SIZE], lowest;
246 bool found;
247 unsigned int highest_sum=0;
248
249 /* Only recalculate differences when a new set is needed. */
250 if (frame == last_request)
251 {
252 *chosen = last_result;
253 *metric = last_metric;
254 return;
255 }
256 last_request = frame;
257
258 /* Get cycle+1 frames starting at the one before the asked-for one. */
259 ADMImage *lastImage=NULL;
260 for (f = 0; f <= _param->cycle; f++)
261 {
262 GETFRAME(frame + f - 1, store[f]);
263 if(store[f]) lastImage=store[f];
264 else store[f]=lastImage;
265 hints_invalid=GetHintingData(lastImage->GetReadPtr(PLANAR_Y),&hints[f]);
266 }
267
268 if(!lastImage)
269 {
270 *chosen=-1;
271 ADM_info("Cannot get input image\n");
272 return;
273 }
274
275 int row_sizeY = info.width; //store[0]->GetRowSize(PLANAR_Y);
276 int heightY = info.height; //store[0]->GetHeight(PLANAR_Y);
277
278 int use_quality=_param->quality;
279
280
281 switch (use_quality)
282 {
283 case 0: // subsample, luma only
284 div = (BLKSIZE * BLKSIZE / 4) * 219;
285 break;
286 case 1: // subsample, luma and chroma
287 div = (BLKSIZE * BLKSIZE / 4) * 219 + ( (BLKSIZE * BLKSIZE / 8)) * 224;
288 break;
289 case 2: // fully sample, luma only
290 div = (BLKSIZE * BLKSIZE) * 219;
291 break;
292 case 3: // fully sample, luma and chroma
293 div = (BLKSIZE * BLKSIZE) * 219 + ( BLKSIZE * BLKSIZE/2) * 224;
294 break;
295 }
296
297 xblocks = row_sizeY / BLKSIZE;
298 if (row_sizeY % BLKSIZE) xblocks++;
299 yblocks = heightY / BLKSIZE;
300 if (heightY % BLKSIZE) yblocks++;
301
302 /* Compare each frame to its predecessor. */
303 for (f = 1; f <= _param->cycle; f++)
304 {
305 count[f-1] = computeDiff(store[f],store[f-1]);
306 showmetrics[f-1] = (count[f-1] * 100.0) / div;
307 }
308
309 /* Find the frame with the lowest difference count but
310 don't use the artificial duplicate at frame 0. */
311 if (frame == 0)
312 {
313 lowest = count[1];
314 lowest_index = 1;
315 }
316 else
317 {
318 lowest = count[0];
319 lowest_index = 0;
320 }
321 for (int x = 1; x < _param->cycle; x++)
322 {
323 if (count[x] < lowest)
324 {
325 lowest = count[x];
326 lowest_index = x;
327 }
328 }
329 last_result = frame + lowest_index;
330 if (_param->quality == 1 || _param->quality == 3)
331 last_metric = (lowest * 100.0) / div;
332 else
333 last_metric = (lowest * 100.0) / div;
334 *chosen = last_result;
335 *metric = last_metric;
336
337 found = false;
338 last_forced = false;
339 return;
340 }
341 /**
342 \fn FindDuplicate2
343 \brief only used for anime mode (find longest dupe sequence)
344 */
FindDuplicate2(int frame,int * chosen,bool * forced)345 void Decimate::FindDuplicate2(int frame, int *chosen, bool *forced)
346 {
347 int f, g, fsum, bsum, highest, highest_index;
348 ADMImage * store[MAX_CYCLE_SIZE+1];
349 const unsigned char *prevY, *prevU, *prevV, *currY, *currU, *currV;
350 int x, y;
351 double lowest;
352 unsigned int lowest_index;
353 char buf[255];
354 unsigned int highest_sum;
355 bool found;
356 #define BLKSIZE 32
357 deciMate *_param=&configuration;
358 /* Only recalculate differences when a new cycle is started. */
359 if (frame == last_request)
360 {
361 *chosen = last_result;
362 *forced = last_forced;
363 return;
364 }
365 last_request = frame;
366
367 if (firsttime == true || frame == 0)
368 {
369 firsttime = false;
370 for (f = 0; f < MAX_CYCLE_SIZE; f++) Dprev[f] = -1;
371 for (f = 1; f <= _param->cycle; f++)
372 {
373 GETFRAME(frame + f - 1, store[f]);
374 }
375
376 int row_sizeY = info.width; //store[0]->GetRowSize(PLANAR_Y);
377 int heightY = info.height; //store[0]->GetHeight(PLANAR_Y);
378
379 switch (_param->quality)
380 {
381 case 0: // subsample, luma only
382 div = (BLKSIZE * BLKSIZE / 4) * 219;
383 break;
384 case 1: // subsample, luma and chroma
385 div = (BLKSIZE * BLKSIZE / 4) * 219 + (BLKSIZE * BLKSIZE / 8) * 224;
386 break;
387 case 2: // fully sample, luma only
388 div = (BLKSIZE * BLKSIZE) * 219;
389 break;
390 case 3: // fully sample, luma and chroma
391 div = (BLKSIZE * BLKSIZE) * 219 + (BLKSIZE * BLKSIZE / 2) * 224;
392 break;
393 }
394 xblocks = row_sizeY / BLKSIZE;
395 if (row_sizeY % BLKSIZE) xblocks++;
396 yblocks = heightY / BLKSIZE;
397 if (heightY % BLKSIZE) yblocks++;
398
399 /* Compare each frame to its predecessor. */
400 for (f = 1; f <= _param->cycle; f++)
401 {
402 highest_sum = computeDiff(store[f],store[f-1]);
403 metrics[f-1] = (highest_sum * 100.0) / div;
404 }
405
406 Dcurr[0] = 1;
407 for (f = 1; f < _param->cycle; f++)
408 {
409 if (metrics[f] < _param->threshold2) Dcurr[f] = 0;
410 else Dcurr[f] = 1;
411 }
412
413 if (configuration.debug)
414 {
415 OutputDebugString(buf,"Decimate: %d: %3.2f %3.2f %3.2f %3.2f %3.2f\n",
416 0, metrics[0], metrics[1], metrics[2], metrics[3], metrics[4]);
417
418 }
419 } // / !frame || first time
420 else
421 {
422 GETFRAME(frame + _param->cycle - 1, store[0]);
423 for (f = 0; f < MAX_CYCLE_SIZE; f++) Dprev[f] = Dcurr[f];
424 for (f = 0; f < MAX_CYCLE_SIZE; f++) Dcurr[f] = Dnext[f];
425 }
426 for (f = 0; f < MAX_CYCLE_SIZE; f++) Dshow[f] = Dcurr[f];
427 for (f = 0; f < MAX_CYCLE_SIZE; f++) showmetrics[f] = metrics[f];
428
429 for (f = 1; f <= _param->cycle; f++)
430 {
431 GETFRAME(frame + f + _param->cycle - 1, store[f]);
432 }
433
434 /* Compare each frame to its predecessor. */
435 for (f = 1; f <= _param->cycle; f++)
436 {
437 highest_sum=computeDiff(store[f],store[f-1]);
438 metrics[f-1] = (highest_sum * 100.0) / div;
439 }
440
441 /* Find the frame with the lowest difference count but
442 don't use the artificial duplicate at frame 0. */
443 if (frame == 0)
444 {
445 lowest = metrics[1];
446 lowest_index = 1;
447 }
448 else
449 {
450 lowest = metrics[0];
451 lowest_index = 0;
452 }
453 for (f = 1; f < _param->cycle; f++)
454 {
455 if (metrics[f] < lowest)
456 {
457 lowest = metrics[f];
458 lowest_index = f;
459 }
460 }
461
462 for (f = 0; f < _param->cycle; f++)
463 {
464 if (metrics[f] < _param->threshold2) Dnext[f] = 0;
465 else Dnext[f] = 1;
466 }
467
468 if (configuration.debug)
469 {
470 OutputDebugString("Decimate: %d: %3.2f %3.2f %3.2f %3.2f %3.2f\n",
471 frame + 5, metrics[0], metrics[1], metrics[2], metrics[3], metrics[4]);
472
473 }
474
475 if (configuration.debug)
476 {
477 OutputDebugString("Decimate: %d: %d %d %d %d %d\n",
478 frame, Dcurr[0], Dcurr[1], Dcurr[2], Dcurr[3], Dcurr[4]);
479 // sprintf(buf,"Decimate: %d: %d %d %d %d %d - %d %d %d %d %d - %d %d %d %d %d\n",
480 // frame, Dprev[0], Dprev[1], Dprev[2], Dprev[3], Dprev[4],
481 // Dcurr[0], Dcurr[1], Dcurr[2], Dcurr[3], Dcurr[4],
482 // Dnext[0], Dnext[1], Dnext[2], Dnext[3], Dnext[4]);
483
484 }
485
486 /* Find the longest strings of duplicates and decimate a frame from it. */
487 highest = -1;
488 for (f = 0; f < _param->cycle; f++)
489 {
490 if (Dcurr[f] == 1)
491 {
492 bsum = 0;
493 fsum = 0;
494 }
495 else
496 {
497 bsum = 1;
498 g = f;
499 while (--g >= 0)
500 {
501 if (Dcurr[g] == 0)
502 {
503 bsum++;
504 }
505 else break;
506 }
507 if (g < 0)
508 {
509 g = _param->cycle;
510 while (--g >= 0)
511 {
512 if (Dprev[g] == 0)
513 {
514 bsum++;
515 }
516 else break;
517 }
518 }
519 fsum = 1;
520 g = f;
521 while (++g < _param->cycle)
522 {
523 if (Dcurr[g] == 0)
524 {
525 fsum++;
526 }
527 else break;
528 }
529 if (g >= _param->cycle)
530 {
531 g = -1;
532 while (++g < _param->cycle)
533 {
534 if (Dnext[g] == 0)
535 {
536 fsum++;
537 }
538 else break;
539 }
540 }
541 }
542 if (bsum + fsum > highest)
543 {
544 highest = bsum + fsum;
545 highest_index = f;
546 }
547 // sprintf(buf,"Decimate: bsum %d, fsum %d\n", bsum, fsum);
548 // OutputDebugString(buf);
549 }
550
551 f = highest_index;
552 if (Dcurr[f] == 1)
553 {
554 /* No duplicates were found! Act as if mode=0. */
555 *chosen = last_result = frame + lowest_index;
556 }
557 else
558 {
559 /* Prevent this decimated frame from being considered again. */
560 Dcurr[f] = 1;
561 *chosen = last_result = frame + highest_index;
562 }
563 last_forced = false;
564 if (configuration.debug)
565 {
566 OutputDebugString("Decimate: dropping frame %d\n", last_result);
567
568 }
569
570
571 found = false;
572
573 if (found == true)
574 {
575 *chosen = last_result ;
576 *forced = last_forced = true;
577 if (configuration.debug)
578 {
579 OutputDebugString("Decimate: overridden drop frame -- drop %d\n", last_result);
580 }
581 }
582 }
583 /**
584 \fn DrawShow
585 */
DrawShow(ADMImage * src,int useframe,bool forced,int dropframe,double metric,int inframe)586 void Decimate::DrawShow(ADMImage *src, int useframe, bool forced, int dropframe,
587 double metric, int inframe)
588 {
589 char buf[80];
590 deciMate *_param=&configuration;
591 int start = (useframe / _param->cycle) * _param->cycle;
592 #define pg(i) (hints[i] & PROGRESSIVE) ? "p" : "i"
593 if (configuration.show == true)
594 {
595 sprintf(buf, "Decimate %d", 0); DrawString(src, 0, 0, buf);
596 sprintf(buf, "Copyright 2003 Donald Graft"); DrawString(src, 0, 1, buf);
597 sprintf(buf,"%d: [%s] %3.2f", start + 0,pg(0), showmetrics[0]);DrawString(src, 0, 3, buf);
598 sprintf(buf,"%d: [%s] %3.2f", start + 1,pg(1), showmetrics[1]);DrawString(src, 0, 4, buf);
599 sprintf(buf,"%d: [%s] %3.2f", start + 2,pg(2), showmetrics[2]);DrawString(src, 0, 5, buf);
600 sprintf(buf,"%d: [%s] %3.2f", start + 3,pg(3), showmetrics[3]);DrawString(src, 0, 6, buf);
601 sprintf(buf,"%d: [%s] %3.2f", start + 4,pg(4), showmetrics[4]);DrawString(src, 0, 7, buf);
602 if (all_video_cycle == false)
603 {
604 sprintf(buf,"in frm %d, use frm %d", inframe, useframe);
605 DrawString(src, 0, 8, buf);
606 if (forced == false)
607 sprintf(buf,"chose %d, dropping", dropframe);
608 else
609 sprintf(buf,"chose %d, dropping, forced!", dropframe);
610 DrawString(src, 0, 9, buf);
611 }
612 else
613 {
614 sprintf(buf,"in frm %d", inframe); DrawString(src, 0, 8, buf);
615 sprintf(buf,"chose %d, decimating all-video cycle", dropframe); DrawString(src, 0, 9, buf);
616 }
617 }
618 if (configuration.debug)
619 {
620 if (!(inframe%_param->cycle))
621 {
622 OutputDebugString(buf,"Decimate: %d: %3.2f\n", start, showmetrics[0]);
623 OutputDebugString(buf,"Decimate: %d: %3.2f\n", start + 1, showmetrics[1]);
624 OutputDebugString(buf,"Decimate: %d: %3.2f\n", start + 2, showmetrics[2]);
625 OutputDebugString(buf,"Decimate: %d: %3.2f\n", start + 3, showmetrics[3]);
626 OutputDebugString(buf,"Decimate: %d: %3.2f\n", start + 4, showmetrics[4]);
627
628 }
629 if (all_video_cycle == false)
630 {
631 OutputDebugString(buf,"Decimate: in frm %d useframe %d\n", inframe, useframe);
632 if (forced == false)
633 {
634 OutputDebugString("Decimate: chose %d, dropping\n", dropframe);
635 }
636 else
637 {
638 OutputDebugString("Decimate: chose %d, dropping, forced!\n", dropframe);
639 }
640 }
641 else
642 {
643 OutputDebugString("Decimate: in frm %d\n", inframe);
644 OutputDebugString("Decimate: chose %d, decimating all-video cycle\n", dropframe);
645 }
646 }
647 }
648 // EOF
649
650