2 * Copyright (c) 2012 Fredrik Mellbin
4 * This file is part of VapourSynth.
6 * VapourSynth is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * VapourSynth is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with Libav; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 // Some metrics calculation used is directly based on TIVTC
28 #include "VapourSynth.h"
33 static int isPowerOf2(int i
) {
49 const VSNodeRef
*node
;
50 const VSNodeRef
*clip2
;
51 const VSVideoInfo
*vi
;
76 static void BitBlt(uint8_t *dstp
, int dst_stride
, const uint8_t *srcp
, int src_stride
, int row_size
, int height
) {
78 for (i
= 0; i
< height
; i
++) {
79 memcpy(dstp
, srcp
, row_size
);
85 static void copyField(VSFrameRef
*dst
, const VSFrameRef
*src
, int field
, const VSAPI
*vsapi
) {
86 const VSFormat
*fi
= vsapi
->getFrameFormat(src
);
88 for (plane
=0; plane
<fi
->numPlanes
; ++plane
) {
89 BitBlt(vsapi
->getWritePtr(dst
, plane
)+field
*vsapi
->getStride(dst
, plane
),vsapi
->getStride(dst
, plane
)*2,
90 vsapi
->getReadPtr(src
, plane
)+field
*vsapi
->getStride(src
, plane
),vsapi
->getStride(src
, plane
)*2,
91 vsapi
->getFrameWidth(src
, plane
),vsapi
->getFrameHeight(src
,plane
)/2);
95 static int64_t calcAbsDiff(const VSFrameRef
*f1
, const VSFrameRef
*f2
, const VSAPI
*vsapi
) {
96 const uint8_t *srcp1
= vsapi
->getReadPtr(f1
, 0);
97 const uint8_t *srcp2
= vsapi
->getReadPtr(f2
, 0);
98 int stride
= vsapi
->getStride(f1
, 0);
99 int width
= vsapi
->getFrameWidth(f1
, 0);
100 int height
= vsapi
->getFrameHeight(f1
, 0);
103 for (y
= 0; y
< height
; y
++) {
104 for (x
= 0; x
< width
; x
++) {
105 int diff
= abs(srcp1
[x
] - srcp2
[x
]);
114 // the secret is that tbuffer is an interlaced, offset subset of all the lines
115 static void buildABSDiffMask(const unsigned char *prvp
, const unsigned char *nxtp
,
116 int src_pitch
, int tpitch
, unsigned char *tbuffer
, int width
, int height
,
117 const VSAPI
*vsapi
) {
120 for (y
=0; y
<height
; ++y
) {
121 for (x
=0; x
<width
; x
++)
122 tbuffer
[x
] = abs(prvp
[x
]-nxtp
[x
]);
131 int calcMI(const VSFrameRef
*src
, const VSAPI
*vsapi
,
132 int *blockN
, int chroma
, int cthresh
, int MI
, VSFrameRef
*cmask
, int *cArray
, int blockx
, int blocky
)
135 const int cthresh6
= cthresh
*6;
138 for (plane
=0; plane
< (chroma
? 3 : 1); plane
++) {
139 const unsigned char *srcp
= vsapi
->getReadPtr(src
, plane
);
140 const int src_pitch
= vsapi
->getStride(src
, plane
);
141 const int Width
= vsapi
->getFrameWidth(src
, plane
);
142 const int Height
= vsapi
->getFrameHeight(src
, plane
);
143 unsigned char *cmkp
= vsapi
->getWritePtr(cmask
, plane
);
144 const int cmk_pitch
= vsapi
->getStride(cmask
, plane
);
145 if (cthresh
< 0) { memset(cmkp
,255,Height
*cmk_pitch
); continue; }
146 memset(cmkp
,0,Height
*cmk_pitch
);
147 for (x
=0; x
<Width
; ++x
) {
148 const int sFirst
= srcp
[x
] - srcp
[x
+ src_pitch
];
149 if (sFirst
> cthresh
|| sFirst
< -cthresh
) {
150 if (abs(srcp
[x
+ 2*src_pitch
]+(srcp
[x
]<<2)+srcp
[x
+ 2*src_pitch
]-(3*(srcp
[x
+ src_pitch
]+srcp
[x
+ src_pitch
]))) > cthresh6
)
156 for (x
=0; x
<Width
; ++x
) {
157 const int sFirst
= srcp
[x
] - srcp
[x
- src_pitch
];
158 const int sSecond
= srcp
[x
] - srcp
[x
+ src_pitch
];
159 if ((sFirst
> cthresh
&& sSecond
> cthresh
) || (sFirst
< -cthresh
&& sSecond
< -cthresh
)) {
160 if (abs(srcp
[x
+ 2*src_pitch
]+(srcp
[x
]<<2)+srcp
[x
+ 2*src_pitch
]-(3*(srcp
[x
- src_pitch
]+srcp
[x
+ src_pitch
]))) > cthresh6
)
167 for (y
=2; y
<Height
-2; ++y
) {
168 for (x
=0; x
<Width
; ++x
) {
169 const int sFirst
= srcp
[x
] - srcp
[x
- src_pitch
];
170 const int sSecond
= srcp
[x
] - srcp
[x
+ src_pitch
];
171 if ((sFirst
> cthresh
&& sSecond
> cthresh
) || (sFirst
< -cthresh
&& sSecond
< -cthresh
)) {
172 if (abs(srcp
[x
- 2*src_pitch
]+(srcp
[x
]<<2)+srcp
[x
+ 2*src_pitch
]-(3*(srcp
[x
- src_pitch
]+srcp
[x
+ src_pitch
]))) > cthresh6
)
180 for (x
=0; x
<Width
; ++x
) {
181 const int sFirst
= srcp
[x
] - srcp
[x
- src_pitch
];
182 const int sSecond
= srcp
[x
] - srcp
[x
+ src_pitch
];
183 if ((sFirst
> cthresh
&& sSecond
> cthresh
) || (sFirst
< -cthresh
&& sSecond
< -cthresh
)) {
184 if (abs(srcp
[x
- 2*src_pitch
]+(srcp
[x
]<<2)+srcp
[x
+ 2*src_pitch
]-(3*(srcp
[x
- src_pitch
]+srcp
[x
+ src_pitch
]))) > cthresh6
)
190 for (x
=0; x
<Width
; ++x
) {
191 const int sFirst
= srcp
[x
] - srcp
[x
- src_pitch
];
192 if (sFirst
> cthresh
|| sFirst
< -cthresh
) {
193 if (abs(2*srcp
[x
- 2*src_pitch
]+(srcp
[x
]<<2)-(6*srcp
[x
- src_pitch
])) > cthresh6
)
199 unsigned char *cmkp
= vsapi
->getWritePtr(cmask
, 0);
200 unsigned char *cmkpU
= vsapi
->getWritePtr(cmask
, 1);
201 unsigned char *cmkpV
= vsapi
->getWritePtr(cmask
, 2);
202 const int Width
= vsapi
->getFrameWidth(cmask
, 2);
203 const int Height
= vsapi
->getFrameHeight(cmask
, 2);
204 const int cmk_pitch
= vsapi
->getStride(cmask
, 0) * 2;
205 const int cmk_pitchUV
= vsapi
->getStride(cmask
, 2);
206 unsigned char *cmkpp
= cmkp
- (cmk_pitch
>>1);
207 unsigned char *cmkpn
= cmkp
+ (cmk_pitch
>>1);
208 unsigned char *cmkpnn
= cmkpn
+ (cmk_pitch
>>1);
209 for (y
=1; y
<Height
-1; ++y
) {
214 cmkpV
+= cmk_pitchUV
;
215 cmkpU
+= cmk_pitchUV
;
216 for (x
=1; x
<Width
-1; ++x
) {
217 if ((cmkpV
[x
] == 0xFF && (cmkpV
[x
-1] == 0xFF || cmkpV
[x
+1] == 0xFF ||
218 cmkpV
[x
-1 - cmk_pitchUV
] == 0xFF || cmkpV
[x
- cmk_pitchUV
] == 0xFF || cmkpV
[x
+1 - cmk_pitchUV
] == 0xFF ||
219 cmkpV
[x
-1 + cmk_pitchUV
] == 0xFF || cmkpV
[x
+ cmk_pitchUV
] == 0xFF || cmkpV
[x
+1 + cmk_pitchUV
] == 0xFF)) ||
220 (cmkpU
[x
] == 0xFF && (cmkpU
[x
-1] == 0xFF || cmkpU
[x
+1] == 0xFF ||
221 cmkpU
[x
-1 - cmk_pitchUV
] == 0xFF || cmkpU
[x
- cmk_pitchUV
] == 0xFF || cmkpU
[x
+1 - cmk_pitchUV
] == 0xFF ||
222 cmkpU
[x
-1 + cmk_pitchUV
] == 0xFF || cmkpU
[x
+ cmk_pitchUV
] == 0xFF || cmkpU
[x
+1 + cmk_pitchUV
] == 0xFF)))
224 ((unsigned short*)cmkp
)[x
] = (unsigned short) 0xFFFF;
225 ((unsigned short*)cmkpn
)[x
] = (unsigned short) 0xFFFF;
226 if (y
&1) ((unsigned short*)cmkpp
)[x
] = (unsigned short) 0xFFFF;
227 else ((unsigned short*)cmkpnn
)[x
] = (unsigned short) 0xFFFF;
233 int xhalf
= blockx
/2;
234 int yhalf
= blocky
/2;
235 const int cmk_pitch
= vsapi
->getStride(cmask
, 0);
236 const unsigned char *cmkp
= vsapi
->getReadPtr(cmask
, 0) + cmk_pitch
;
237 const unsigned char *cmkpp
= cmkp
- cmk_pitch
;
238 const unsigned char *cmkpn
= cmkp
+ cmk_pitch
;
239 const int Width
= vsapi
->getFrameWidth(cmask
, 0);
240 const int Height
= vsapi
->getFrameHeight(cmask
, 0);
241 const int xblocks
= ((Width
+xhalf
)/blockx
) + 1;
242 const int xblocks4
= xblocks
<<2;
243 const int yblocks
= ((Height
+yhalf
)/blocky
) + 1;
244 const int arraysize
= (xblocks
*yblocks
)<<2;
245 int Heighta
= (Height
/(blocky
/2))*(blocky
/2);
246 const int Widtha
= (Width
/(blockx
/2))*(blockx
/2);
247 if (Heighta
== Height
)
248 Heighta
= Height
-yhalf
;
249 memset(&cArray
[0],0,arraysize
*sizeof(int));
250 for (y
=1; y
<yhalf
; ++y
) {
251 const int temp1
= (y
/blocky
)*xblocks4
;
252 const int temp2
= ((y
+yhalf
)/blocky
)*xblocks4
;
253 for (x
=0; x
<Width
; ++x
) {
254 if (cmkpp
[x
] == 0xFF && cmkp
[x
] == 0xFF && cmkpn
[x
] == 0xFF) {
255 const int box1
= (x
/blockx
)*4;
256 const int box2
= ((x
+xhalf
)/blockx
)*4;
257 ++cArray
[temp1
+box1
+0];
258 ++cArray
[temp1
+box2
+1];
259 ++cArray
[temp2
+box1
+2];
260 ++cArray
[temp2
+box2
+3];
267 for (y
=yhalf
; y
<Heighta
; y
+=yhalf
) {
268 const int temp1
= (y
/blocky
)*xblocks4
;
269 const int temp2
= ((y
+yhalf
)/blocky
)*xblocks4
;
271 for (x
=0; x
<Widtha
; x
+=xhalf
) {
272 const unsigned char *cmkppT
= cmkpp
;
273 const unsigned char *cmkpT
= cmkp
;
274 const unsigned char *cmkpnT
= cmkpn
;
276 for (u
=0; u
<yhalf
; ++u
) {
277 for (v
=0; v
<xhalf
; ++v
) {
278 if (cmkppT
[x
+v
] == 0xFF && cmkpT
[x
+v
] == 0xFF &&
279 cmkpnT
[x
+v
] == 0xFF) ++sum
;
286 const int box1
= (x
/blockx
)*4;
287 const int box2
= ((x
+xhalf
)/blockx
)*4;
288 cArray
[temp1
+box1
+0] += sum
;
289 cArray
[temp1
+box2
+1] += sum
;
290 cArray
[temp2
+box1
+2] += sum
;
291 cArray
[temp2
+box2
+3] += sum
;
295 for (x
=Widtha
; x
<Width
; ++x
) {
296 const unsigned char *cmkppT
= cmkpp
;
297 const unsigned char *cmkpT
= cmkp
;
298 const unsigned char *cmkpnT
= cmkpn
;
300 for (u
=0; u
<yhalf
; ++u
) {
301 if (cmkppT
[x
] == 0xFF && cmkpT
[x
] == 0xFF &&
302 cmkpnT
[x
] == 0xFF) ++sum
;
308 const int box1
= (x
/blockx
)*4;
309 const int box2
= ((x
+xhalf
)/blockx
)*4;
310 cArray
[temp1
+box1
+0] += sum
;
311 cArray
[temp1
+box2
+1] += sum
;
312 cArray
[temp2
+box1
+2] += sum
;
313 cArray
[temp2
+box2
+3] += sum
;
316 cmkpp
+= cmk_pitch
*yhalf
;
317 cmkp
+= cmk_pitch
*yhalf
;
318 cmkpn
+= cmk_pitch
*yhalf
;
320 for (y
=Heighta
; y
<Height
-1; ++y
) {
321 const int temp1
= (y
/blocky
)*xblocks4
;
322 const int temp2
= ((y
+yhalf
)/blocky
)*xblocks4
;
323 for (x
=0; x
<Width
; ++x
) {
324 if (cmkpp
[x
] == 0xFF && cmkp
[x
] == 0xFF && cmkpn
[x
] == 0xFF) {
325 const int box1
= (x
/blockx
)*4;
326 const int box2
= ((x
+xhalf
)/blockx
)*4;
327 ++cArray
[temp1
+box1
+0];
328 ++cArray
[temp1
+box2
+1];
329 ++cArray
[temp2
+box1
+2];
330 ++cArray
[temp2
+box2
+3];
337 for (x
=0; x
<arraysize
; ++x
) {
338 if (cArray
[x
] > ret
) {
349 // build a map over which pixels differ a lot/a little
350 static void buildDiffMap(const unsigned char *prvp
, const unsigned char *nxtp
,
351 unsigned char *dstp
,int src_pitch
, int dst_pitch
, int Height
,
352 int Width
, int tpitch
, unsigned char *tbuffer
, const VSAPI
*vsapi
)
354 const unsigned char *dp
= tbuffer
+tpitch
;
355 int x
, y
, u
, diff
, count
;
357 buildABSDiffMask(prvp
-src_pitch
, nxtp
-src_pitch
, src_pitch
,
358 tpitch
, tbuffer
, Width
, Height
>>1, vsapi
);
360 for (y
=2; y
<Height
-2; y
+=2) {
361 for (x
=1; x
<Width
-1; ++x
) {
364 for (count
=0,u
=x
-1; u
<x
+2 && count
<2; ++u
) {
365 if (dp
[u
-tpitch
] > 3) ++count
;
366 if (dp
[u
] > 3) ++count
;
367 if (dp
[u
+tpitch
] > 3) ++count
;
372 int upper
= 0, lower
= 0;
373 for (count
=0, u
=x
-1; u
<x
+2 && count
<6; ++u
) {
374 if (dp
[u
-tpitch
] > 19) { ++count
; upper
= 1; }
375 if (dp
[u
] > 19) ++count
;
376 if (dp
[u
+tpitch
] > 19) { ++count
; lower
= 1; }
379 if (!upper
|| !lower
) {
380 int upper2
= 0, lower2
= 0;
381 for (u
=max(x
-4,0); u
<min(x
+5,Width
); ++u
)
383 if (y
!= 2 && dp
[u
-2*tpitch
] > 19)
385 if (dp
[u
-tpitch
] > 19)
387 if (dp
[u
+tpitch
] > 19)
389 if (y
!= Height
-4 && dp
[u
+2*tpitch
] > 19)
392 if ((upper
&& (lower
|| upper2
)) ||
393 (lower
&& (upper
|| lower2
)))
409 static int compareFieldsSlow(const VSFrameRef
*prv
, const VSFrameRef
*src
, const VSFrameRef
*nxt
, VSFrameRef
*map
, int match1
,
410 int match2
, int mchroma
, int field
, int y0
, int y1
, uint8_t *tbuffer
, int tpitchy
, int tpitchuv
, const VSAPI
*vsapi
)
413 const unsigned char *prvp
, *srcp
, *nxtp
;
414 const unsigned char *curpf
, *curf
, *curnf
;
415 const unsigned char *prvpf
, *prvnf
, *nxtpf
, *nxtnf
;
417 int src_stride
, Width
, Height
;
418 int curf_pitch
, stopx
, map_pitch
;
419 int x
, y
, temp1
, temp2
, startx
, y0a
, y1a
, tp
;
420 int stop
= mchroma
? 3 : 1;
421 unsigned long accumPc
= 0, accumNc
= 0, accumPm
= 0;
422 unsigned long accumNm
= 0, accumPml
= 0, accumNml
= 0;
423 int norm1
, norm2
, mtn1
, mtn2
;
426 for (plane
=0; plane
<stop
; ++plane
) {
427 mapp
= vsapi
->getWritePtr(map
, plane
);
428 map_pitch
= vsapi
->getStride(map
, plane
);
429 prvp
= vsapi
->getReadPtr(prv
, plane
);
430 srcp
= vsapi
->getReadPtr(src
, plane
);
431 src_stride
= vsapi
->getStride(src
, plane
);
432 Width
= vsapi
->getFrameWidth(src
, plane
);
433 Height
= vsapi
->getFrameHeight(src
, plane
);
434 nxtp
= vsapi
->getReadPtr(nxt
, plane
);
435 memset(mapp
,0,Height
*map_pitch
);
436 startx
= (plane
== 0 ? 8 : 4);
437 stopx
= Width
- startx
;
438 curf_pitch
= src_stride
<<1;
449 curf
= srcp
+ ((3-field
)*src_stride
);
450 mapp
= mapp
+ ((field
== 1 ? 1 : 2)*map_pitch
);
453 prvpf
= prvp
+ ((field
== 1 ? 1 : 2)*src_stride
);
454 } else if (match1
== 1) {
455 prvpf
= srcp
+ ((field
== 1 ? 1 : 2)*src_stride
);
456 } else if (match1
== 2) {
457 prvpf
= nxtp
+ ((field
== 1 ? 1 : 2)*src_stride
);
458 } else if (match1
== 3) {
459 curf
= srcp
+ ((2+field
)*src_stride
);
460 prvpf
= prvp
+ ((field
== 1 ? 2 : 1)*src_stride
);
461 mapp
= mapp
+ ((field
== 1 ? 2 : 1)*map_pitch
);
462 } else if (match1
== 4) {
463 curf
= srcp
+ ((2+field
)*src_stride
);
464 prvpf
= nxtp
+ ((field
== 1 ? 2 : 1)*src_stride
);
465 mapp
= mapp
+ ((field
== 1 ? 2 : 1)*map_pitch
);
468 nxtpf
= prvp
+ ((field
== 1 ? 1 : 2)*src_stride
);
469 } else if (match2
== 1) {
470 nxtpf
= srcp
+ ((field
== 1 ? 1 : 2)*src_stride
);
471 } else if (match2
== 2) {
472 nxtpf
= nxtp
+ ((field
== 1 ? 1 : 2)*src_stride
);
473 } else if (match2
== 3) {
474 nxtpf
= prvp
+ ((field
== 1 ? 2 : 1)*src_stride
);
475 } else if (match2
== 4)
477 nxtpf
= nxtp
+ ((field
== 1 ? 2 : 1)*src_stride
);
479 prvnf
= prvpf
+ curf_pitch
;
480 curpf
= curf
- curf_pitch
;
481 curnf
= curf
+ curf_pitch
;
482 nxtnf
= nxtpf
+ curf_pitch
;
484 if ((match1
>= 3 && field
== 1) || (match1
< 3 && field
!= 1))
485 buildDiffMap(prvpf
,nxtpf
,mapp
,curf_pitch
,map_pitch
,Height
,Width
,tp
,tbuffer
,vsapi
);
487 buildDiffMap(prvnf
,nxtnf
,mapp
+ map_pitch
,curf_pitch
,map_pitch
,Height
,Width
,tp
,tbuffer
,vsapi
);
489 for (y
=2; y
<Height
-2; y
+=2) {
490 if (y0a
== y1a
|| y
< y0a
|| y
> y1a
) {
491 for (x
=startx
; x
<stopx
; x
++) {
492 if (mapp
[x
] > 0 || mapp
[x
+ map_pitch
] > 0) {
493 temp1
= curpf
[x
]+(curf
[x
]<<2)+curnf
[x
];
494 temp2
= abs(3*(prvpf
[x
]+prvnf
[x
])-temp1
);
495 if (temp2
> 23 && ((mapp
[x
]&1) || (mapp
[x
+ map_pitch
]&1)))
498 if ((mapp
[x
]&2) || (mapp
[x
+ map_pitch
]&2))
500 if ((mapp
[x
]&4) || (mapp
[x
+ map_pitch
]&4))
503 temp2
= abs(3*(nxtpf
[x
]+nxtnf
[x
])-temp1
);
504 if (temp2
> 23 && ((mapp
[x
]&1) || (mapp
[x
+ map_pitch
]&1)))
507 if ((mapp
[x
]&2) || (mapp
[x
+ map_pitch
]&2))
509 if ((mapp
[x
]&4) || (mapp
[x
+ map_pitch
]&4))
525 if (accumPm
< 500 && accumNm
< 500 && (accumPml
>= 500 || accumNml
>= 500) &&
526 max(accumPml
,accumNml
) > 3*min(accumPml
,accumNml
))
531 norm1
= (int)((accumPc
/ 6.0f
) + 0.5f
);
532 norm2
= (int)((accumNc
/ 6.0f
) + 0.5f
);
533 mtn1
= (int)((accumPm
/ 6.0f
) + 0.5f
);
534 mtn2
= (int)((accumNm
/ 6.0f
) + 0.5f
);
535 c1
= ((float)max(norm1
,norm2
))/((float)max(min(norm1
,norm2
),1));
536 c2
= ((float)max(mtn1
,mtn2
))/((float)max(min(mtn1
,mtn2
),1));
537 mr
= ((float)max(mtn1
,mtn2
))/((float)max(max(norm1
,norm2
),1));
538 if (((mtn1
>= 500 || mtn2
>= 500) && (mtn1
*2 < mtn2
*1 || mtn2
*2 < mtn1
*1)) ||
539 ((mtn1
>= 1000 || mtn2
>= 1000) && (mtn1
*3 < mtn2
*2 || mtn2
*3 < mtn1
*2)) ||
540 ((mtn1
>= 2000 || mtn2
>= 2000) && (mtn1
*5 < mtn2
*4 || mtn2
*5 < mtn1
*4)) ||
541 ((mtn1
>= 4000 || mtn2
>= 4000) && c2
> c1
))
547 } else if (mr
> 0.005 && max(mtn1
,mtn2
) > 150 && (mtn1
*2 < mtn2
*1 || mtn2
*2 < mtn1
*1)) {
562 VSFrameRef
*createWeaveFrame(const VSFrameRef
*prv
, const VSFrameRef
*src
,
563 const VSFrameRef
*nxt
, const VSAPI
*vsapi
, VSCore
*core
, int match
, int field
) {
564 VSFrameRef
*dst
= vsapi
->newVideoFrame(vsapi
->getFrameFormat(src
), vsapi
->getFrameWidth(src
, 0), vsapi
->getFrameHeight(src
, 0), src
, core
);
567 copyField(dst
, src
, 1-field
, vsapi
);
568 copyField(dst
, prv
, field
, vsapi
);
569 } else if (match
== 2) {
570 copyField(dst
, src
, 1-field
, vsapi
);
571 copyField(dst
, nxt
, field
, vsapi
);
572 } else if (match
== 3) {
573 copyField(dst
, src
, field
, vsapi
);
574 copyField(dst
, prv
, 1-field
, vsapi
);
575 } else if (match
== 4) {
576 copyField(dst
, src
, field
, vsapi
);
577 copyField(dst
, nxt
, 1-field
, vsapi
);
584 static int checkmm(int m1
, int m2
, int *m1mic
, int *m2mic
, int *blockN
, int MI
, int field
, int chroma
, int cthresh
, const VSFrameRef
**genFrames
,
585 const VSFrameRef
*prv
, const VSFrameRef
*src
, const VSFrameRef
*nxt
, VSFrameRef
*cmask
, int *cArray
, int blockx
, int blocky
, const VSAPI
*vsapi
, VSCore
*core
) {
588 genFrames
[m1
] = createWeaveFrame(prv
, src
, nxt
, vsapi
, core
, m1
, field
);
589 *m1mic
= calcMI(genFrames
[m1
], vsapi
, blockN
, chroma
, cthresh
, MI
, cmask
, cArray
, blockx
, blocky
);
594 genFrames
[m2
] = createWeaveFrame(prv
, src
, nxt
, vsapi
, core
, m2
, field
);
595 *m2mic
= calcMI(genFrames
[m2
], vsapi
, blockN
, chroma
, cthresh
, MI
, cmask
, cArray
, blockx
, blocky
);
598 if (((*m2mic
)*3 < *m1mic
|| ((*m2mic
)*2 < *m1mic
&& *m1mic
> MI
)) &&
599 abs(*m2mic
-*m1mic
) >= 30 && *m2mic
< MI
)
605 static void VS_CC
vfmInit(VSMap
*in
, VSMap
*out
, void **instanceData
, VSNode
*node
, VSCore
*core
, const VSAPI
*vsapi
) {
606 VFMData
*vfm
= (VFMData
*)*instanceData
;
607 vsapi
->setVideoInfo(vfm
->vi
, node
);
618 static const VSFrameRef
*VS_CC
vfmGetFrame(int n
, int activationReason
, void **instanceData
, void **frameData
, VSFrameContext
*frameCtx
, VSCore
*core
, const VSAPI
*vsapi
) {
619 VFMData
*vfm
= (VFMData
*)*instanceData
;
620 n
= min(vfm
->vi
->numFrames
- 1, n
);
621 if (activationReason
== arInitial
) {
623 vsapi
->requestFrameFilter(n
-1, vfm
->node
, frameCtx
);
625 vsapi
->requestFrameFilter(n
-1, vfm
->clip2
, frameCtx
);
627 vsapi
->requestFrameFilter(n
, vfm
->node
, frameCtx
);
629 vsapi
->requestFrameFilter(n
-1, vfm
->clip2
, frameCtx
);
630 if (n
< vfm
->vi
->numFrames
- 1) {
631 vsapi
->requestFrameFilter(n
+1, vfm
->node
, frameCtx
);
633 vsapi
->requestFrameFilter(n
+1, vfm
->clip2
, frameCtx
);
635 } else if (activationReason
== arAllFramesReady
) {
636 const VSFrameRef
*prv
= vsapi
->getFrameFilter(n
> 0 ? n
-1 : 0, vfm
->node
, frameCtx
);
637 const VSFrameRef
*src
= vsapi
->getFrameFilter(n
, vfm
->node
, frameCtx
);
638 const VSFrameRef
*nxt
= vsapi
->getFrameFilter(n
< vfm
->vi
->numFrames
- 1 ? n
+1 : vfm
->vi
->numFrames
- 1, vfm
->node
, frameCtx
);
639 int mics
[] = { -1,-1,-1,-1,-1 };
641 // selected based on field^order
642 const int fxo0m
[] = { 0, 1, 2, 3, 4 };
643 const int fxo1m
[] = { 2, 1, 0, 4, 3};
644 const int *fxo
= vfm
->field
^vfm
->order
? fxo1m
: fxo0m
;
647 const VSFrameRef
*genFrames
[] = { NULL
, NULL
, NULL
, NULL
, NULL
};
649 const VSFrameRef
*dst1
;
654 // check if it's a scenechange so micmatching can be used
656 sc
= calcAbsDiff(prv
, src
, vsapi
) > vfm
->scthresh
|| calcAbsDiff(src
, nxt
, vsapi
) > vfm
->scthresh
;
659 match
= compareFieldsSlow(prv
, src
, nxt
, vfm
->map
, fxo
[mC
], fxo
[mP
], vfm
->mchroma
, vfm
->field
, vfm
->y0
, vfm
->y1
, vfm
->tbuffer
, vfm
->tpitchy
, vfm
->tpitchuv
, vsapi
);
660 // the mode has 3-way p/c/n matches
662 match
= compareFieldsSlow(prv
, src
, nxt
, vfm
->map
, match
, fxo
[mN
], vfm
->mchroma
, vfm
->field
, vfm
->y0
, vfm
->y1
, vfm
->tbuffer
, vfm
->tpitchy
, vfm
->tpitchuv
, vsapi
);
664 genFrames
[mC
] = vsapi
->cloneFrameRef(src
);
666 // calculate all values for mic output, checkmm calculates and prepares it for the two matches if not already done
668 checkmm(0, 1, &mics
[0], &mics
[1], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
669 checkmm(2, 3, &mics
[2], &mics
[3], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
670 checkmm(4, 0, &mics
[4], &mics
[0], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
673 // check the micmatches to see if one of the options are better
674 // here come the huge ass mode tables
676 if (!vfm
->mmsco
|| sc
) {
677 // here comes the conditional hell to try to approximate mode 0-5 in tfm
678 if (vfm
->mode
== 0) {
679 // maybe not completely approriate but go back and see if the discarded match is less sucky
680 match
= checkmm(match
, match
== fxo
[mP
] ? fxo
[mC
] : fxo
[mP
], &mics
[match
], &mics
[match
== fxo
[mP
] ? fxo
[mC
] : fxo
[mP
]], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
681 } else if (vfm
->mode
== 1) {
682 match
= checkmm(match
, fxo
[mN
], &mics
[match
], &mics
[fxo
[mN
]], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
683 } else if (vfm
->mode
== 2) {
684 match
= checkmm(match
, fxo
[mU
], &mics
[match
], &mics
[fxo
[mU
]], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
685 } else if (vfm
->mode
== 3) {
686 match
= checkmm(match
, fxo
[mN
], &mics
[match
], &mics
[fxo
[mN
]], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
687 match
= checkmm(match
, fxo
[mU
], &mics
[match
], &mics
[fxo
[mU
]], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
688 match
= checkmm(match
, fxo
[mB
], &mics
[match
], &mics
[fxo
[mB
]], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
689 } else if (vfm
->mode
== 4) {
690 // degenerate check because I'm lazy
691 match
= checkmm(match
, match
== fxo
[mP
] ? fxo
[mC
] : fxo
[mP
], &mics
[match
], &mics
[match
== fxo
[mP
] ? fxo
[mC
] : fxo
[mP
]], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
,vsapi
, core
);
692 } else if (vfm
->mode
== 5) {
693 match
= checkmm(match
, fxo
[mU
], &mics
[match
], &mics
[fxo
[mU
]], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
694 match
= checkmm(match
, fxo
[mB
], &mics
[match
], &mics
[fxo
[mB
]], &blockN
, vfm
->mi
, vfm
->field
, vfm
->chroma
, vfm
->cthresh
, genFrames
, prv
, src
, nxt
, vfm
->cmask
, &vfm
->cArray
[0], vfm
->blockx
, vfm
->blocky
, vsapi
, core
);
699 const VSFrameRef
*prv2
= vsapi
->getFrameFilter(n
> 0 ? n
-1 : 0, vfm
->clip2
, frameCtx
);
700 const VSFrameRef
*src2
= vsapi
->getFrameFilter(n
, vfm
->clip2
, frameCtx
);
701 const VSFrameRef
*nxt2
= vsapi
->getFrameFilter(n
< vfm
->vi
->numFrames
- 1 ? n
+1 : vfm
->vi
->numFrames
- 1, vfm
->clip2
, frameCtx
);
702 dst1
= createWeaveFrame(prv2
, src2
, nxt2
, vsapi
, core
, match
, vfm
->field
);
703 vsapi
->freeFrame(prv2
);
704 vsapi
->freeFrame(src2
);
705 vsapi
->freeFrame(nxt2
);
707 if (!genFrames
[match
])
708 genFrames
[match
] = createWeaveFrame(prv
, src
, nxt
, vsapi
, core
, match
, vfm
->field
);
709 dst1
= vsapi
->cloneFrameRef(genFrames
[match
]);
712 vsapi
->freeFrame(prv
);
713 vsapi
->freeFrame(src
);
714 vsapi
->freeFrame(nxt
);
716 for (i
= 0; i
< 5; i
++)
717 vsapi
->freeFrame(genFrames
[i
]);
719 dst2
= vsapi
->copyFrame(dst1
, core
);
720 vsapi
->freeFrame(dst1
);
721 m
= vsapi
->getFramePropsRW(dst2
);
722 for (i
= 0; i
< 5; i
++)
723 vsapi
->propSetInt(m
, "VFMMics", mics
[fxo
[i
]], paAppend
);
724 vsapi
->propSetInt(m
, "VFMMatch", fxo
[match
], paAppend
);
725 vsapi
->propSetInt(m
, "VFMSceneChange", sc
, paAppend
);
731 static void VS_CC
vfmFree(void *instanceData
, VSCore
*core
, const VSAPI
*vsapi
) {
732 VFMData
*vfm
= (VFMData
*)instanceData
;
733 vsapi
->freeFrame(vfm
->cmask
);
734 vsapi
->freeFrame(vfm
->map
);
738 static void VS_CC
createVFM(const VSMap
*in
, VSMap
*out
, void *userData
, VSCore
*core
, const VSAPI
*vsapi
) {
742 const VSNodeRef
*cref
;
743 const VSVideoInfo
*vi
;
745 vfm
.order
= !!vsapi
->propGetInt(in
, "order", 0, 0);
746 vfm
.field
= !!vsapi
->propGetInt(in
, "field", 0, &err
);
748 vfm
.field
= vfm
.order
;
749 vfm
.mode
= int64ToIntS(vsapi
->propGetInt(in
, "mode", 0, &err
));
752 vfm
.mchroma
= !!vsapi
->propGetInt(in
, "mchroma", 0, &err
);
755 vfm
.cthresh
= int64ToIntS(vsapi
->propGetInt(in
, "cthresh", 0, &err
));
758 vfm
.mi
= int64ToIntS(vsapi
->propGetInt(in
, "mi", 0, &err
));
761 vfm
.chroma
= !!vsapi
->propGetInt(in
, "chroma", 0, &err
);
764 vfm
.blockx
= int64ToIntS(vsapi
->propGetInt(in
, "blockx", 0, &err
));
767 vfm
.blocky
= int64ToIntS(vsapi
->propGetInt(in
, "blocky", 0, &err
));
770 vfm
.y0
= int64ToIntS(vsapi
->propGetInt(in
, "y0", 0, &err
));
773 vfm
.y1
= int64ToIntS(vsapi
->propGetInt(in
, "y1", 0, &err
));
776 vfm
.scthresh
= vsapi
->propGetFloat(in
, "scthresh", 0, &err
);
779 vfm
.mmsco
= !!vsapi
->propGetInt(in
, "mmsco", 0, &err
);
782 vfm
.micout
= !!vsapi
->propGetInt(in
, "micout", 0, &err
);
784 if (vfm
.mode
< 0 || vfm
.mode
> 5) {
785 vsapi
->setError(out
, "VFM: Invalid mode specified, only 0-5 allowed");
789 if (vfm
.blockx
< 4 || vfm
.blockx
> 512 || !isPowerOf2(vfm
.blockx
) || vfm
.blocky
< 4 || vfm
.blocky
> 512 || !isPowerOf2(vfm
.blocky
)) {
790 vsapi
->setError(out
, "VFM: invalid blocksize, must be between 4 and 512 and be a power of 2");
794 if (vfm
.mi
< 0 || vfm
.mi
> vfm
.blockx
* vfm
.blocky
) {
795 vsapi
->setError(out
, "VFM: Invalid mi threshold specified");
799 if (vfm
.scthresh
< 0 || vfm
.scthresh
> 100) {
800 vsapi
->setError(out
, "VFM: Invalid scthresh specified");
804 if (vfm
.scthresh
< 0 || vfm
.scthresh
> 255) {
805 vsapi
->setError(out
, "VFM: invalid scthresh specified");
809 if (vfm
.cthresh
< -1 || vfm
.cthresh
> 255) {
810 vsapi
->setError(out
, "VFM: invalid cthresh specified");
814 vfm
.node
= vsapi
->propGetNode(in
, "clip", 0, 0);
815 vfm
.clip2
= vsapi
->propGetNode(in
, "clip2", 0, &err
);
816 vfm
.vi
= vsapi
->getVideoInfo(vfm
.clip2
? vfm
.clip2
: vfm
.node
);
817 vi
= vsapi
->getVideoInfo(vfm
.node
);
819 if (!isConstantFormat(vfm
.vi
) || !vfm
.vi
->numFrames
|| vfm
.vi
->format
->id
!= pfYUV420P8
) {
820 vsapi
->setError(out
, "VFM: input clip must be constant format YUV420P8");
821 vsapi
->freeNode(vfm
.node
);
822 vsapi
->freeNode(vfm
.clip2
);
826 if (vi
->numFrames
!= vfm
.vi
->numFrames
) {
827 vsapi
->setError(out
, "VFM: the number of frames must be the same in both input clips");
828 vsapi
->freeNode(vfm
.node
);
829 vsapi
->freeNode(vfm
.clip2
);
833 vfm
.scthresh
*= (vi
->width
* vi
->height
) * 255.0 / 100.0;
835 vfm
.map
= vsapi
->newVideoFrame(vi
->format
, vi
->width
, vi
->height
, NULL
, core
);
836 vfm
.cmask
= vsapi
->newVideoFrame(vi
->format
, vi
->width
, vi
->height
, NULL
, core
);
838 vfm
.field
= vfm
.order
;
840 vfm
.tpitchy
= (vi
->width
&15) ? vi
->width
+16-(vi
->width
&15) : vi
->width
;
841 vfm
.tpitchuv
= ((vi
->width
>>1)&15) ? (vi
->width
>>1)+16-((vi
->width
>>1)&15) : (vi
->width
>>1);
843 vfm
.tbuffer
= (uint8_t *)malloc((vi
->height
>>1)*vfm
.tpitchy
*sizeof(uint8_t));
844 vfm
.cArray
= (int *)malloc((((vi
->width
+vfm
.blockx
/2)/vfm
.blockx
)+1)*(((vi
->height
+vfm
.blocky
/2)/vfm
.blocky
)+1)*4*sizeof(int));
846 vfmd
= (VFMData
*)malloc(sizeof(vfm
));
848 cref
= vsapi
->createFilter(in
, out
, "VFM", vfmInit
, vfmGetFrame
, vfmFree
, fmParallelRequests
, 0, vfmd
, core
);
849 vsapi
->propSetNode(out
, "clip", cref
, paReplace
);
860 const VSNodeRef
*node
;
861 const VSNodeRef
*clip2
;
878 static int calcMetric(const VSFrameRef
*f1
, const VSFrameRef
*f2
, uint64_t *totdiff
, VDecimateData
*vdm
, const VSAPI
*vsapi
) {
879 int *bdiffs
= vdm
->bdiffs
;
883 int numplanes
= vdm
->chroma
? 3 : 1;
885 memset(bdiffs
, 0, vdm
->bdiffsize
* sizeof(int));
886 for (plane
= 0; plane
< numplanes
; plane
++) {
887 int stride
= vsapi
->getStride(f1
, plane
);
888 const uint8_t *f1p
= vsapi
->getReadPtr(f1
, plane
);
889 const uint8_t *f2p
= vsapi
->getReadPtr(f2
, plane
);
890 const VSFormat
*fi
= vsapi
->getFrameFormat(f1
);
892 int width
= vsapi
->getFrameWidth(f1
, plane
);
893 int height
= vsapi
->getFrameHeight(f1
, plane
);
894 int hblockx
= vdm
->blockx
/2;
895 int hblocky
= vdm
->blocky
/2;
896 int nxblocks
, nyblocks
;
897 // adjust for subsampling
899 hblockx
/= 1 << fi
->subSamplingW
;
900 hblocky
/= 1 << fi
->subSamplingH
;
902 nxblocks
= vdm
->nxblocks
;
903 nyblocks
= vdm
->nyblocks
;
905 for (y
= 0; y
< height
; y
++) {
906 int ydest
= y
/ hblocky
;
908 for (x
= 0; x
< width
; x
+= hblockx
) {
910 int m
= min(width
, x
+ hblockx
);
911 for (xl
= x
; xl
< m
; xl
++)
912 acc
+= abs(f1p
[xl
] - f2p
[xl
]);
913 bdiffs
[ydest
* nxblocks
+ xdest
] += acc
;
922 for (i
= 0; i
< vdm
->nyblocks
- 1; i
++) {
923 for (j
= 0; j
< vdm
->nxblocks
- 1; j
++) {
924 int tmp
= bdiffs
[i
* vdm
->nxblocks
+ j
] + bdiffs
[i
* vdm
->nxblocks
+ j
+ 1] + bdiffs
[(i
+ 1) * vdm
->nxblocks
+ j
] + bdiffs
[(i
+ 1) * vdm
->nxblocks
+ j
+ 1];
931 for (i
= 0; i
< vdm
->bdiffsize
; i
++)
932 *totdiff
+= bdiffs
[i
];
936 static void VS_CC
vdecimateInit(VSMap
*in
, VSMap
*out
, void **instanceData
, VSNode
*node
, VSCore
*core
, const VSAPI
*vsapi
) {
937 VDecimateData
*vdm
= (VDecimateData
*)*instanceData
;
938 vsapi
->setVideoInfo(&vdm
->vi
, node
);
941 static const VSFrameRef
*VS_CC
vdecimateGetFrame(int n
, int activationReason
, void **instanceData
, void **frameData
, VSFrameContext
*frameCtx
, VSCore
*core
, const VSAPI
*vsapi
) {
942 VDecimateData
*vdm
= (VDecimateData
*)*instanceData
;
946 if (activationReason
== arInitial
) {
947 int cyclestart
= (n
/ (vdm
->cycle
- 1)) * vdm
->cycle
;
948 int cycleend
= cyclestart
+ vdm
->cycle
;
951 for (i
= cyclestart
; i
< cycleend
; i
++) {
952 if (vdm
->vmi
[i
].maxbdiff
< 0) {
954 if (!prevreqd
&& i
-1 >= 0)
955 vsapi
->requestFrameFilter(i
- 1, vdm
->node
, frameCtx
);
956 vsapi
->requestFrameFilter(i
, vdm
->node
, frameCtx
);
962 *frameData
= (void *)-1;
965 if (activationReason
== arAllFramesReady
|| (hasall
&& activationReason
== arInitial
)) {
967 int cyclestart
, cycleend
, lowest
;
971 int rframe
= (int)*frameData
;
974 return vsapi
->getFrameFilter(rframe
, vdm
->clip2
, frameCtx
);
976 return vsapi
->getFrameFilter(rframe
, vdm
->node
, frameCtx
);
979 // calculate all the needed metrics
980 cyclestart
= (n
/ (vdm
->cycle
- 1)) * vdm
->cycle
;
981 cycleend
= cyclestart
+ vdm
->cycle
;
982 for (i
= cyclestart
; i
< cycleend
; i
++) {
983 if (vdm
->vmi
[i
].maxbdiff
< 0) {
984 const VSFrameRef
*prv
= vsapi
->getFrameFilter(max(i
- 1, 0), vdm
->node
, frameCtx
);
985 const VSFrameRef
*cur
= vsapi
->getFrameFilter(i
, vdm
->node
, frameCtx
);
986 vdm
->vmi
[i
].maxbdiff
= calcMetric(prv
, cur
, &vdm
->vmi
[i
].totdiff
, vdm
, vsapi
);
987 vsapi
->freeFrame(prv
);
988 vsapi
->freeFrame(cur
);
993 // precalculate the position of the lowest dup metric frame
994 // the last sc and the lowest dup, if any
997 for (i
= cyclestart
+ 1; i
< cycleend
; i
++) {
998 if (vdm
->vmi
[i
].totdiff
/(double)vdm
->fnormalize
> vdm
->scthresh
)
1000 if (vdm
->vmi
[i
].maxbdiff
< vdm
->vmi
[lowest
].maxbdiff
)
1004 if (vdm
->vmi
[lowest
].maxbdiff
/(double)vdm
->bnormalize
< vdm
->dupthresh
)
1007 // if there is no scenechange simply drop the frame with lowest difference
1008 // if there is a scenechange see if any frame qualifies as a duplicate and drop it
1009 // otherwise drop the first frame right after the sc to keep the motion smooth
1011 fin
= n
% (vdm
->cycle
- 1);
1013 // no dups so drop the frame right after the sc to keep things smooth
1014 if (scpos
>= 0 && duppos
< 0) {
1015 fcut
= scpos
% vdm
->cycle
;
1017 fcut
= lowest
% vdm
->cycle
;
1019 fout
= cyclestart
+ (fcut
> fin
? fin
: (fin
+ 1));
1022 vsapi
->requestFrameFilter(fout
, vdm
->clip2
, frameCtx
);
1024 const VSFrameRef
*ftry
= vsapi
->getFrameFilter(fout
, vdm
->node
, frameCtx
);
1027 vsapi
->requestFrameFilter(fout
, vdm
->node
, frameCtx
);
1029 *frameData
= (void *)fout
;
1035 static void VS_CC
vdecimateFree(void *instanceData
, VSCore
*core
, const VSAPI
*vsapi
) {
1036 VDecimateData
*vdm
= (VDecimateData
*)instanceData
;
1042 static void VS_CC
createVDecimate(const VSMap
*in
, VSMap
*out
, void *userData
, VSCore
*core
, const VSAPI
*vsapi
) {
1045 const VSNodeRef
*cref
;
1046 const VSVideoInfo
*vi
;
1049 vdm
.cycle
= int64ToIntS(vsapi
->propGetInt(in
, "cycle", 0, &err
));
1052 vdm
.chroma
= !!vsapi
->propGetInt(in
, "chroma", 0, &err
);
1055 vdm
.blockx
= int64ToIntS(vsapi
->propGetInt(in
, "blockx", 0, &err
));
1058 vdm
.blocky
= int64ToIntS(vsapi
->propGetInt(in
, "blocky", 0, &err
));
1061 vdm
.dupthresh
= vsapi
->propGetFloat(in
, "dupthresh", 0, &err
);
1063 vdm
.dupthresh
= 1.1;
1064 vdm
.scthresh
= vsapi
->propGetFloat(in
, "scthresh", 0, &err
);
1066 vdm
.scthresh
= 15.0;
1068 if (vdm
.cycle
< 2 || vdm
.cycle
> 25) {
1069 vsapi
->setError(out
, "VDecimate: Invalid cycle size specified");
1073 if (vdm
.blockx
< 4 || vdm
.blockx
> 512 || !isPowerOf2(vdm
.blockx
) || vdm
.blocky
< 4 || vdm
.blocky
> 512 || !isPowerOf2(vdm
.blocky
)) {
1074 vsapi
->setError(out
, "VDecimate: invalid blocksize, must be between 4 and 512 and be a power of 2");
1078 if (vdm
.dupthresh
< 0 || vdm
.dupthresh
> 255) {
1079 vsapi
->setError(out
, "VDecimate: invalid dupthresh specified");
1083 if (vdm
.scthresh
< 0 || vdm
.scthresh
> 255) {
1084 vsapi
->setError(out
, "VDecimate: invalid scthresh specified");
1088 vdm
.node
= vsapi
->propGetNode(in
, "clip", 0, 0);
1089 vdm
.clip2
= vsapi
->propGetNode(in
, "clip2", 0, &err
);
1090 vdm
.vi
= *vsapi
->getVideoInfo(vdm
.clip2
? vdm
.clip2
: vdm
.node
);
1092 vi
= vsapi
->getVideoInfo(vdm
.node
);
1093 if (!isConstantFormat(vi
) || !vi
->numFrames
|| vi
->format
->id
!= pfYUV420P8
) {
1094 vsapi
->setError(out
, "VDecimate: input clip must be constant format YUV420P8");
1095 vsapi
->freeNode(vdm
.node
);
1096 vsapi
->freeNode(vdm
.clip2
);
1100 if (vi
->numFrames
!= vdm
.vi
.numFrames
) {
1101 vsapi
->setError(out
, "VDecimate: the number of frames must be the same in both input clips");
1102 vsapi
->freeNode(vdm
.node
);
1103 vsapi
->freeNode(vdm
.clip2
);
1107 vdm
.fnormalize
= vdm
.vi
.width
* vdm
.vi
.height
;
1108 vdm
.bnormalize
= vdm
.blockx
* vdm
.blocky
;
1110 vdm
.fnormalize
*= 3;
1111 vdm
.fnormalize
/= 2;
1112 vdm
.bnormalize
*= 3;
1113 vdm
.bnormalize
/= 2;
1115 vdm
.nxblocks
= (vdm
.vi
.width
+ vdm
.blockx
/2 - 1)/(vdm
.blockx
/2);
1116 vdm
.nyblocks
= (vdm
.vi
.height
+ vdm
.blocky
/2 - 1)/(vdm
.blocky
/2);
1117 vdm
.bdiffsize
= vdm
.nxblocks
* vdm
.nyblocks
;
1118 vdm
.bdiffs
= (int *)malloc(vdm
.bdiffsize
* sizeof(int));
1119 vdm
.vmi
= (VDInfo
*)malloc(vdm
.vi
.numFrames
* sizeof(VDInfo
));
1120 vdm
.vi
.numFrames
/= vdm
.cycle
;
1121 vdm
.vi
.numFrames
*= vdm
.cycle
- 1;
1123 for (i
= 0; i
< vdm
.vi
.numFrames
; i
++) {
1124 vdm
.vmi
[i
].maxbdiff
= -1;
1125 vdm
.vmi
[i
].totdiff
= -1;
1128 d
= (VDecimateData
*)malloc(sizeof(vdm
));
1131 cref
= vsapi
->createFilter(in
, out
, "VDecimate", vdecimateInit
, vdecimateGetFrame
, vdecimateFree
, fmSerial
, 0, d
, core
);
1132 vsapi
->propSetNode(out
, "clip", cref
, paReplace
);
1133 vsapi
->freeNode(cref
);
1136 VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc
, VSRegisterFunction registerFunc
, VSPlugin
*plugin
)
1138 configFunc("org.ivtc.v", "vivtc", "VFM", VAPOURSYNTH_API_VERSION
, 1, plugin
);
1139 // optimize sc calculation
1140 registerFunc("VFM", "clip:clip;order:int;field:int:opt;mode:int:opt;" \
1141 "mchroma:int:opt;cthresh:int:opt;mi:int:opt;" \
1142 "chroma:int:opt;blockx:int:opt;blocky:int:opt;y0:int:opt;y1:int:opt;" \
1143 "scthresh:float:opt;mmsco:int:opt;micout:int:opt;clip2:clip:opt;", createVFM
, NULL
, plugin
);
1144 // add metrics output
1145 registerFunc("VDecimate", "clip:clip;cycle:int:opt;" \
1146 "chroma:int:opt;dupthresh:float:opt;scthresh:float:opt;" \
1147 "blockx:int:opt;blocky:int:opt;clip2:clip:opt;", createVDecimate
, NULL
, plugin
);