Add vivtc build to the wscript.
[vapoursynth-svn.git] / src / filters / victc / vivtc.c
blob32b13fb4da58f5f4cd9557aabf1cf2cfcf465763
1 /*
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
23 #include <stdint.h>
24 #include <math.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <minmax.h>
28 #include "VapourSynth.h"
29 #include "VSHelper.h"
31 // Shared
33 static int isPowerOf2(int i) {
34 while (i > 0) {
35 if (i & 1) {
36 if (i == 1)
37 return 1;
38 else
39 return 0;
41 i >>= 1;
43 return 0;
46 // VFM
48 typedef struct {
49 const VSNodeRef *node;
50 const VSNodeRef *clip2;
51 const VSVideoInfo *vi;
52 VSFrameRef *map;
53 VSFrameRef *cmask;
54 double scthresh;
55 int *cArray;
56 uint8_t *tbuffer;
57 int tpitchy;
58 int tpitchuv;
59 int order;
60 int field;
61 int mode;
62 int chroma;
63 int mchroma;
64 int cthresh;
65 int mi;
66 int blockx;
67 int blocky;
68 int y0;
69 int y1;
70 int mmsco;
71 int micout;
72 int lastn;
73 int lastscdiff;
74 } VFMData;
76 static void BitBlt(uint8_t *dstp, int dst_stride, const uint8_t *srcp, int src_stride, int row_size, int height) {
77 int i;
78 for (i = 0; i < height; i++) {
79 memcpy(dstp, srcp, row_size);
80 dstp += dst_stride;
81 srcp += src_stride;
85 static void copyField(VSFrameRef *dst, const VSFrameRef *src, int field, const VSAPI *vsapi) {
86 const VSFormat *fi = vsapi->getFrameFormat(src);
87 int plane;
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);
101 int x, y;
102 int64_t acc = 0;
103 for (y = 0; y < height; y++) {
104 for (x = 0; x < width; x++) {
105 int diff = abs(srcp1[x] - srcp2[x]);
106 acc += diff;
108 srcp1 += stride;
109 srcp2 += stride;
111 return acc;
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) {
119 int y, x;
120 for (y=0; y<height; ++y) {
121 for (x=0; x<width; x++)
122 tbuffer[x] = abs(prvp[x]-nxtp[x]);
124 prvp += src_pitch;
125 nxtp += src_pitch;
126 tbuffer += tpitch;
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)
134 int ret = 0;
135 const int cthresh6 = cthresh*6;
136 int plane;
137 int x, y, u, v;
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)
151 cmkp[x] = 0xFF;
154 srcp += src_pitch;
155 cmkp += cmk_pitch;
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)
161 cmkp[x] = 0xFF;
164 srcp += src_pitch;
165 cmkp += cmk_pitch;
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)
173 cmkp[x] = 0xFF;
176 srcp += src_pitch;
177 cmkp += cmk_pitch;
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)
185 cmkp[x] = 0xFF;
188 srcp += src_pitch;
189 cmkp += cmk_pitch;
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)
194 cmkp[x] = 0xFF;
198 if (chroma) {
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) {
210 cmkpp += cmk_pitch;
211 cmkp += cmk_pitch;
212 cmkpn += cmk_pitch;
213 cmkpnn += cmk_pitch;
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];
263 cmkpp += cmk_pitch;
264 cmkp += cmk_pitch;
265 cmkpn += cmk_pitch;
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;
275 int sum = 0;
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;
281 cmkppT += cmk_pitch;
282 cmkpT += cmk_pitch;
283 cmkpnT += cmk_pitch;
285 if (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;
299 int sum = 0;
300 for (u=0; u<yhalf; ++u) {
301 if (cmkppT[x] == 0xFF && cmkpT[x] == 0xFF &&
302 cmkpnT[x] == 0xFF) ++sum;
303 cmkppT += cmk_pitch;
304 cmkpT += cmk_pitch;
305 cmkpnT += cmk_pitch;
307 if (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];
333 cmkpp += cmk_pitch;
334 cmkp += cmk_pitch;
335 cmkpn += cmk_pitch;
337 for (x=0; x<arraysize; ++x) {
338 if (cArray[x] > ret) {
339 ret = cArray[x];
340 if (blockN)
341 *blockN = x;
345 return 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) {
362 diff = dp[x];
363 if (diff > 3) {
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;
369 if (count > 1) {
370 ++dstp[x];
371 if (diff > 19) {
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; }
378 if (count > 3) {
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)
384 upper2 = 1;
385 if (dp[u-tpitch] > 19)
386 upper = 1;
387 if (dp[u+tpitch] > 19)
388 lower = 1;
389 if (y != Height-4 && dp[u+2*tpitch] > 19)
390 lower2 = 1;
392 if ((upper && (lower || upper2)) ||
393 (lower && (upper || lower2)))
394 dstp[x] += 2;
395 else if (count > 5)
396 dstp[x] += 4;
398 else dstp[x] += 2;
404 dp += tpitch;
405 dstp += dst_pitch;
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)
412 int plane, ret;
413 const unsigned char *prvp, *srcp, *nxtp;
414 const unsigned char *curpf, *curf, *curnf;
415 const unsigned char *prvpf, *prvnf, *nxtpf, *nxtnf;
416 unsigned char *mapp;
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;
424 float c1, c2, mr;
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;
439 if (plane == 0) {
440 y0a = y0;
441 y1a = y1;
442 tp = tpitchy;
443 } else {
444 y0a = y0>>1;
445 y1a = y1>>1;
446 tp = tpitchuv;
448 if (match1 < 3) {
449 curf = srcp + ((3-field)*src_stride);
450 mapp = mapp + ((field == 1 ? 1 : 2)*map_pitch);
452 if (match1 == 0) {
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);
467 if (match2 == 0) {
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;
483 map_pitch <<= 1;
484 if ((match1 >= 3 && field == 1) || (match1 < 3 && field != 1))
485 buildDiffMap(prvpf,nxtpf,mapp,curf_pitch,map_pitch,Height,Width,tp,tbuffer,vsapi);
486 else
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)))
496 accumPc += temp2;
497 if (temp2 > 42) {
498 if ((mapp[x]&2) || (mapp[x + map_pitch]&2))
499 accumPm += temp2;
500 if ((mapp[x]&4) || (mapp[x + map_pitch]&4))
501 accumPml += temp2;
503 temp2 = abs(3*(nxtpf[x]+nxtnf[x])-temp1);
504 if (temp2 > 23 && ((mapp[x]&1) || (mapp[x + map_pitch]&1)))
505 accumNc += temp2;
506 if (temp2 > 42) {
507 if ((mapp[x]&2) || (mapp[x + map_pitch]&2))
508 accumNm += temp2;
509 if ((mapp[x]&4) || (mapp[x + map_pitch]&4))
510 accumNml += temp2;
515 prvpf += curf_pitch;
516 prvnf += curf_pitch;
517 curpf += curf_pitch;
518 curf += curf_pitch;
519 curnf += curf_pitch;
520 nxtpf += curf_pitch;
521 nxtnf += curf_pitch;
522 mapp += map_pitch;
525 if (accumPm < 500 && accumNm < 500 && (accumPml >= 500 || accumNml >= 500) &&
526 max(accumPml,accumNml) > 3*min(accumPml,accumNml))
528 accumPm = accumPml;
529 accumNm = 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))
543 if (mtn1 > mtn2)
544 ret = match2;
545 else
546 ret = match1;
547 } else if (mr > 0.005 && max(mtn1,mtn2) > 150 && (mtn1*2 < mtn2*1 || mtn2*2 < mtn1*1)) {
548 if (mtn1 > mtn2)
549 ret = match2;
550 else
551 ret = match1;
552 } else {
553 if (norm1 > norm2)
554 ret = match2;
555 else
556 ret = match1;
558 return ret;
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);
566 if (match == 0) {
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);
580 return dst;
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) {
586 if (*m1mic < 0) {
587 if (!genFrames[m1])
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);
592 if (*m2mic < 0) {
593 if (!genFrames[m2])
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)
600 return m2;
601 else
602 return m1;
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);
610 typedef enum {
611 mP = 0,
612 mC = 1,
613 mN = 2,
614 mB = 3,
615 mU = 4
616 } FMP;
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) {
622 if (n > 0) {
623 vsapi->requestFrameFilter(n-1, vfm->node, frameCtx);
624 if (vfm->clip2)
625 vsapi->requestFrameFilter(n-1, vfm->clip2, frameCtx);
627 vsapi->requestFrameFilter(n, vfm->node, frameCtx);
628 if (vfm->clip2)
629 vsapi->requestFrameFilter(n-1, vfm->clip2, frameCtx);
630 if (n < vfm->vi->numFrames - 1) {
631 vsapi->requestFrameFilter(n+1, vfm->node, frameCtx);
632 if (vfm->clip2)
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;
645 int match;
646 int i;
647 const VSFrameRef *genFrames[] = { NULL, NULL, NULL, NULL, NULL };
648 int blockN;
649 const VSFrameRef *dst1;
650 VSFrameRef *dst2;
651 VSMap *m;
652 int sc = 0;
654 // check if it's a scenechange so micmatching can be used
655 if (vfm->mmsco)
656 sc = calcAbsDiff(prv, src, vsapi) > vfm->scthresh || calcAbsDiff(src, nxt, vsapi) > vfm->scthresh;
658 // p/c selection
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
661 if (vfm->mode >= 4)
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
667 if (vfm->micout) {
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);
698 if (vfm->clip2) {
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);
706 } else {
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);
726 return dst2;
728 return NULL;
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);
735 free(vfm);
738 static void VS_CC createVFM(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
739 int err;
740 VFMData vfm;
741 VFMData *vfmd ;
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);
747 if (err)
748 vfm.field = vfm.order;
749 vfm.mode = int64ToIntS(vsapi->propGetInt(in, "mode", 0, &err));
750 if (err)
751 vfm.mode = 1;
752 vfm.mchroma = !!vsapi->propGetInt(in, "mchroma", 0, &err);
753 if (err)
754 vfm.mchroma = 1;
755 vfm.cthresh = int64ToIntS(vsapi->propGetInt(in, "cthresh", 0, &err));
756 if (err)
757 vfm.cthresh = 9;
758 vfm.mi = int64ToIntS(vsapi->propGetInt(in, "mi", 0, &err));
759 if (err)
760 vfm.mi = 80;
761 vfm.chroma = !!vsapi->propGetInt(in, "chroma", 0, &err);
762 if (err)
763 vfm.chroma = 1;
764 vfm.blockx = int64ToIntS(vsapi->propGetInt(in, "blockx", 0, &err));
765 if (err)
766 vfm.blockx = 16;
767 vfm.blocky = int64ToIntS(vsapi->propGetInt(in, "blocky", 0, &err));
768 if (err)
769 vfm.blocky = 16;
770 vfm.y0 = int64ToIntS(vsapi->propGetInt(in, "y0", 0, &err));
771 if (err)
772 vfm.y0 = 16;
773 vfm.y1 = int64ToIntS(vsapi->propGetInt(in, "y1", 0, &err));
774 if (err)
775 vfm.y1 = 16;
776 vfm.scthresh = vsapi->propGetFloat(in, "scthresh", 0, &err);
777 if (err)
778 vfm.scthresh = 12.0;
779 vfm.mmsco = !!vsapi->propGetInt(in, "mmsco", 0, &err);
780 if (err)
781 vfm.mmsco = 1;
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");
786 return;
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");
791 return;
794 if (vfm.mi < 0 || vfm.mi > vfm.blockx * vfm.blocky) {
795 vsapi->setError(out, "VFM: Invalid mi threshold specified");
796 return;
799 if (vfm.scthresh < 0 || vfm.scthresh > 100) {
800 vsapi->setError(out, "VFM: Invalid scthresh specified");
801 return;
804 if (vfm.scthresh < 0 || vfm.scthresh > 255) {
805 vsapi->setError(out, "VFM: invalid scthresh specified");
806 return;
809 if (vfm.cthresh < -1 || vfm.cthresh > 255) {
810 vsapi->setError(out, "VFM: invalid cthresh specified");
811 return;
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);
823 return;
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);
830 return;
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);
837 if (vfm.field == -1)
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));
847 *vfmd = vfm;
848 cref = vsapi->createFilter(in, out, "VFM", vfmInit, vfmGetFrame, vfmFree, fmParallelRequests, 0, vfmd, core);
849 vsapi->propSetNode(out, "clip", cref, paReplace);
852 // VDecimate
854 typedef struct {
855 int maxbdiff;
856 uint64_t totdiff;
857 } VDInfo;
859 typedef struct {
860 const VSNodeRef *node;
861 const VSNodeRef *clip2;
862 VSVideoInfo vi;
863 int cycle;
864 int chroma;
865 double dupthresh;
866 double scthresh;
867 int blockx;
868 int blocky;
869 int nxblocks;
870 int nyblocks;
871 int bnormalize;
872 int fnormalize;
873 int bdiffsize;
874 int *bdiffs;
875 VDInfo *vmi;
876 } VDecimateData;
878 static int calcMetric(const VSFrameRef *f1, const VSFrameRef *f2, uint64_t *totdiff, VDecimateData *vdm, const VSAPI *vsapi) {
879 int *bdiffs = vdm->bdiffs;
880 int plane;
881 int x, y, xl;
882 int i, j;
883 int numplanes = vdm->chroma ? 3 : 1;
884 int maxdiff = -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
898 if (plane > 0) {
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;
907 int xdest = 0;
908 for (x = 0; x < width; x+= hblockx) {
909 int acc = 0;
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;
914 xdest++;
917 f1p += stride;
918 f2p += stride;
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];
925 if (tmp > maxdiff)
926 maxdiff = tmp;
930 *totdiff = 0;
931 for (i = 0; i < vdm->bdiffsize; i++)
932 *totdiff += bdiffs[i];
933 return maxdiff;
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;
943 int hasall = 1;
944 int i;
946 if (activationReason == arInitial) {
947 int cyclestart = (n / (vdm->cycle - 1)) * vdm->cycle;
948 int cycleend = cyclestart + vdm->cycle;
949 int prevreqd = 0;
951 for (i = cyclestart; i < cycleend; i++) {
952 if (vdm->vmi[i].maxbdiff < 0) {
953 hasall = 0;
954 if (!prevreqd && i-1 >= 0)
955 vsapi->requestFrameFilter(i - 1, vdm->node, frameCtx);
956 vsapi->requestFrameFilter(i, vdm->node, frameCtx);
957 prevreqd = 1;
958 } else {
959 prevreqd = 0;
962 *frameData = (void *)-1;
965 if (activationReason == arAllFramesReady || (hasall && activationReason == arInitial)) {
966 int fin, fout, fcut;
967 int cyclestart, cycleend, lowest;
968 int scpos = -1;
969 int duppos = -1;
971 int rframe = (int)*frameData;
972 if(rframe >= 0) {
973 if (vdm->clip2)
974 return vsapi->getFrameFilter(rframe, vdm->clip2, frameCtx);
975 else
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);
992 // make a decision
993 // precalculate the position of the lowest dup metric frame
994 // the last sc and the lowest dup, if any
996 lowest = cyclestart;
997 for (i = cyclestart + 1; i < cycleend; i++) {
998 if (vdm->vmi[i].totdiff/(double)vdm->fnormalize > vdm->scthresh)
999 scpos = i;
1000 if (vdm->vmi[i].maxbdiff < vdm->vmi[lowest].maxbdiff)
1001 lowest = i;
1004 if (vdm->vmi[lowest].maxbdiff/(double)vdm->bnormalize < vdm->dupthresh)
1005 duppos = lowest;
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);
1012 fcut;
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;
1016 } else {
1017 fcut = lowest % vdm->cycle;
1019 fout = cyclestart + (fcut > fin ? fin : (fin + 1));
1021 if (vdm->clip2) {
1022 vsapi->requestFrameFilter(fout, vdm->clip2, frameCtx);
1023 } else {
1024 const VSFrameRef *ftry = vsapi->getFrameFilter(fout, vdm->node, frameCtx);
1025 if (ftry)
1026 return ftry;
1027 vsapi->requestFrameFilter(fout, vdm->node, frameCtx);
1029 *frameData = (void *)fout;
1032 return NULL;
1035 static void VS_CC vdecimateFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
1036 VDecimateData *vdm = (VDecimateData *)instanceData;
1037 free(vdm->bdiffs);
1038 free(vdm->vmi);
1039 free(vdm);
1042 static void VS_CC createVDecimate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
1043 VDecimateData vdm;
1044 VDecimateData *d;
1045 const VSNodeRef *cref;
1046 const VSVideoInfo *vi;
1047 int i, err;
1049 vdm.cycle = int64ToIntS(vsapi->propGetInt(in, "cycle", 0, &err));
1050 if (err)
1051 vdm.cycle = 5;
1052 vdm.chroma = !!vsapi->propGetInt(in, "chroma", 0, &err);
1053 if (err)
1054 vdm.chroma = 1;
1055 vdm.blockx = int64ToIntS(vsapi->propGetInt(in, "blockx", 0, &err));
1056 if (err)
1057 vdm.blockx = 32;
1058 vdm.blocky = int64ToIntS(vsapi->propGetInt(in, "blocky", 0, &err));
1059 if (err)
1060 vdm.blocky = 32;
1061 vdm.dupthresh = vsapi->propGetFloat(in, "dupthresh", 0, &err);
1062 if (err)
1063 vdm.dupthresh = 1.1;
1064 vdm.scthresh = vsapi->propGetFloat(in, "scthresh", 0, &err);
1065 if (err)
1066 vdm.scthresh = 15.0;
1068 if (vdm.cycle < 2 || vdm.cycle > 25) {
1069 vsapi->setError(out, "VDecimate: Invalid cycle size specified");
1070 return;
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");
1075 return;
1078 if (vdm.dupthresh < 0 || vdm.dupthresh > 255) {
1079 vsapi->setError(out, "VDecimate: invalid dupthresh specified");
1080 return;
1083 if (vdm.scthresh < 0 || vdm.scthresh > 255) {
1084 vsapi->setError(out, "VDecimate: invalid scthresh specified");
1085 return;
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);
1097 return;
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);
1104 return;
1107 vdm.fnormalize = vdm.vi.width * vdm.vi.height;
1108 vdm.bnormalize = vdm.blockx * vdm.blocky;
1109 if (vdm.chroma) {
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));
1129 *d = 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);