Description
===========

TDeintMod is a combination of TDeint and TMM, which are both ported from tritical's AviSynth plugin http://bengal.missouri.edu/~kes25c/.

IsCombed is a utility function to check whether or not a frame is combed and stores the result (0 or 1) as a frame property named _Combed. It's intended to be used within `std.FrameEval` to process only combed frames and leave non-combed frames untouched.

Only a few functionality of TDeint is kept in TDeintMod, either because some use inline asm and there is no equivalent C code in the source, or some are very rarely used by people nowadays. For example, the biggest change is that TDeint's internal building of motion mask is entirely dropped, and be replaced with TMM's motion mask. The second is that only cubic interpolation is kept as the only one internal interpolation method, all the others (ELA interpolation, kernel interpolation and blend interpolation) are dropped. Cubic interpolation is kept only for testing purpose, and people should really specify an externally interpolated clip via `edeint` argument for practical use.


Usage
=====

    tdm.TDeintMod(clip clip, int order[, int field=-1, int mode=0, int length=10, int mtype=1, int ttype=1, int mtql=-1, int mthl=-1, int mtqc=-1, int mthc=-1, int nt=2, int minthresh=4, int maxthresh=75, int cstr=4, int athresh=-1, int metric=0, int expand=0, bint link=True, bint show=False, clip edeint=None, int opt=0, int[] planes])

* clip: Clip to process. Only planar format with integer sample type of 8-16 bit depth and chroma subsampling 1x-2x is supported.

* order: Sets the field order of the video. The filter will use the field order specified in the source frames and will only fall back to the specified order if not present.
  * 0 = bottom field first (bff)
  * 1 = top field first (tff)

* field: When in mode 0, this sets the field to be interpolated. When in mode 1, this setting does nothing.
  * -1 = set field equal to order
  * 0 = interpolate top field (keep bottom field)
  * 1 = interpolate bottom field (keep top field)

* mode: Sets the mode of operation.
  * 0 = same rate output
  * 1 = double rate output (bobbing)

* length: Sets the number of fields required for declaring pixels as stationary. length=6 means six fields (3 top/3 bottom), length=8 means 8 fields (4 top/4 bottom), etc... This can be any value greater than or equal to 6 (can be even or odd). A larger value for length will prevent more motion-adaptive related artifacts, but will result in fewer pixels being weaved.

* mtype: Sets whether or not both vertical neighboring lines in the current field of the line in the opposite parity field attempting to be weaved have to agree on both stationarity and direction.
  * 0 = no
  * 1 = no for across, but yes for backwards/forwards
  * 2 = yes

  0 will result in the most pixels being weaved, while 2 will have the least artifacts.

* ttype: Sets how to determine the per-pixel adaptive (quarter pel/half pel) motion thresholds.
  * 0 = 4 neighbors, diff to center pixel, compensated
  * 1 = 8 neighbors, diff to center pixel, compensated
  * 2 = 4 neighbors, diff to center pixel, uncompensated
  * 3 = 8 neighbors, diff to center pixel, uncompensated
  * 4 = 4 neighbors, range (max-min) of neighborhood
  * 5 = 8 neighbors, range (max-min) of neighborhood

  compensated means adjusted for distance differences due to field vs frames and chroma downsampling. The compensated versions will always result in thresholds <= to the uncompensated versions.

* mtql/mthl/mtqc/mthc: These parameters allow the specification of hard thresholds instead of using per-pixel adaptive motion thresholds. mtql sets the quarter pel threshold for luma, mthl sets the half pel threshold for luma, mtqc/mthc are the same but for chroma. Setting all four parameters to -2 will disable motion adaptation (i.e. every pixel will be declared moving) allowing for a dumb bob. If these parameters are set to -1 then an adaptive threshold is used. Otherwise, if they are between 0 and 255 (inclusive) then the value of the parameter is used as the threshold for every pixel.

* nt/minthresh/maxthresh: nt sets the noise threshold, which will be added to the value of each per-pixel threshold when determining if a pixel is stationary or not. After the addition of 'nt', any threshold less than minthresh will be increased to minthresh and any threshold greater than maxthresh will be decreased to maxthresh.

* cstr: Sets the number of required neighbor pixels (3x3 neighborhood) in the quarter pel mask, of a pixel marked as moving in the quarter pel mask, but stationary in the half pel mask, marked as stationary for the pixel to be marked as stationary in the combined mask.

* athresh: Area combing threshold used for spatial adaptation. Setting to -1 will disable spatial adaptation. Lower value will detect more combing, but will also result in more false positives. If your source is pure interlaced video you may want to simply disable spatial adaptation so that any moving pixels are counted as combed.

* metric: Sets which spatial combing metric is used to detect combed pixels.

```
Assume 5 neighboring pixels (a,b,c,d,e) positioned vertically.
    a
    b
    c
    d
    e

0:  d1 = c - b;
    d2 = c - d;
    if ((d1 > cthresh && d2 > cthresh) || (d1 < -cthresh && d2 < -cthresh))
    {
       if (abs(a+4*c+e-3*(b+d)) > cthresh*6) it's combed;
    }

1:  val = (b - c) * (d - c);
    if (val > cthresh*cthresh) it's combed;
```

  Metric 0 is what TDeint always used previous to v1.0 RC7. Metric 1 is the combing metric used in Donald Graft's FieldDeinterlace()/IsCombed() funtions in decomb.dll.

* expand: Sets the number of pixels to expand the comb mask horizontally on each side of combed pixels. Basically, if expand is greater than 0 then TDeintMod will consider all pixels within 'expand' distance horizontally of a detected combed pixel to be combed as well. Can be useful when some combed pixels got missed in spatial adaptation.

* link: Controls whether the luma plane is linked to chroma plane during comb mask creation.

* show: Displays the binary comb mask instead of the deinterlaced frame.

* edeint: Allows the specification of an external clip from which to take interpolated pixels instead of having TDeintMod use its internal interpolation method. If a clip is specified, then TDeintMod will process everything as usual except that instead of computing interpolated pixels itself it will take the needed pixels from the corresponding spatial positions in the same frame of the edeint clip. To disable the use of an edeint clip simply don't specify a value for edeint.

* opt: Sets which cpu optimizations to use.
  * 0 = auto detect
  * 1 = use c
  * 2 = use sse2
  * 3 = use avx2

* planes: A list of the planes to process. By default all planes are processed.

---

    tdm.IsCombed(clip clip[, int cthresh=6, int blockx=16, int blocky=16, bint chroma=False, int mi=64, int metric=0])

* clip: Clip to process. Only planar format with integer sample type of 8-16 bit depth and chroma subsampling 1x-4x is supported.

* cthresh: Area combing threshold used for combed frame detection. This essentially controls how "strong" or "visible" combing must be to be detected. Good values are from 6 to 12. If you know your source has a lot of combed frames set this towards the low end (6-7). If you know your source has very few combed frames set this higher (10-12). Going much lower than 5 to 6 or much higher than 12 is not recommended.

* blockx: Sets the x-axis size of the window used during combed frame detection. This has to do with the size of the area in which `mi` number of pixels are required to be detected as combed for a frame to be declared combed. See the `mi` parameter description for more info. Possible values are any number that is a power of 2 starting at 4 and going to 2048 (e.g. 4, 8, 16, 32, ... 2048).

* blocky: Sets the y-axis size of the window used during combed frame detection. This has to do with the size of the area in which `mi` number of pixels are required to be detected as combed for a frame to be declared combed. See the `mi` parameter description for more info. Possible values are any number that is a power of 2 starting at 4 and going to 2048 (e.g. 4, 8, 16, 32, ... 2048).

* chroma: Includes chroma combing in the decision about whether a frame is combed. Only use this if you have one of those weird sources where the chroma can be temporally separated from the luma (i.e. the chroma moves but the luma doesn't in a field). Otherwise, it will just help to screw up the decision most of the time.

* mi: The number of required combed pixels inside any of the `blockx` by `blocky` sized blocks on the frame for the frame to be considered combed. While `cthresh` controls how "visible" or "strong" the combing must be, this setting controls how much combing there must be in any localized area (a `blockx` by `blocky` sized window) on the frame. Min setting = 0, max setting = `blockx` x `blocky` (at which point no frames will ever be detected as combed).

* metric: Sets which spatial combing metric is used to detect combed pixels.

```
Assume 5 neighboring pixels (a,b,c,d,e) positioned vertically.
    a
    b
    c
    d
    e

0:  d1 = c - b;
    d2 = c - d;
    if ((d1 > cthresh && d2 > cthresh) || (d1 < -cthresh && d2 < -cthresh))
    {
       if (abs(a+4*c+e-3*(b+d)) > cthresh*6) it's combed;
    }

1:  val = (b - c) * (d - c);
    if (val > cthresh*cthresh) it's combed;
```

  Metric 0 is what TDeint always used previous to v1.0 RC7. Metric 1 is the combing metric used in Donald Graft's FieldDeinterlace()/IsCombed() funtions in decomb.dll.


Example usage of IsCombed
=========================

```python
from vapoursynth import core
import functools

clip = Whatever

def conditionalDeint(n, f, orig, deint):
    if f.props['_Combed']:
        return deint
    else:
        return orig

deint = core.tdm.TDeintMod(clip, order=1, edeint=core.nnedi3.nnedi3(clip, field=1))
combProps = core.tdm.IsCombed(clip)
clip = core.std.FrameEval(clip, functools.partial(conditionalDeint, orig=clip, deint=deint), combProps)
clip.set_output()
```

Note that it only makes sense to do so in same rate mode, because the output's number of frames from TDeintMod won't match those of the input in double rate mode.


Compilation
===========

```
meson build
ninja -C build
```

or

```
./autogen.sh
./configure
make
```
