Skip to content
AkarinVS edited this page May 13, 2022 · 9 revisions

CAMBI

akarin.Cambi(clip clip[, int window_size = 63, float topk = 0.6, float tvi_threshold = 0.019, bint scores = False, float scaling = 1.0/window_size])

Computes the CAMBI banding score as CAMBI frame property. Unlike VapourSynth-VMAF, this filter is online (no need to batch process the whole video) and provides raw cambi scores (when scores == True).

  • clip: Clip to calculate CAMBI score. Only Gray/YUV format with integer sample type of 8/10-bit depth (subsampling can be arbitrary as cambi only uses the Y channel.)
  • window_size (min: 15, max: 127, default: 63): Window size to compute CAMBI. (default: 63 corresponds to ~1 degree at 4K resolution and 1.5H)
  • topk (min: 0.0001, max: 1.0, default: 0.6): Ratio of pixels for the spatial pooling computation.
  • tvi_threshold (min: 0.0001, max: 1.0, default: 0.019): Visibilty threshold for luminance ΔL < tvi_threshold*L_mean for BT.1886.
  • scores (default: False): if True, for scale i (0 <= i < 5), the GRAYS c-score frame will be stored as frame property "CAMBI_SCALE%d" % i.
  • scaling: scaling factor used to normalize the c-scores for each scale returned when scores=True.

Example usage

src = core.lsmas.LWLibavSource(filename)  # 8-bit or 10-bit YUV/Gray video supported
cambi = src.akarin.Cambi(scores=True)     # scores=True makes it output per-scale c-score maps in addition to the per-frame CAMBI score
cambi.text.FrameProps("CAMBI").set_output(10)
for i in range(5):
    scale = cambi.std.PropToClip('CAMBI_SCALE%d' % i)
    scale.set_output(11+i)

Scale i c-score will be a GRAYS clip with width = src8.width / 2i, and similarly for height.

scale-1 is probably best used as a banding artefact mask.

Batch processing

In case you do want to batch process the whole clip and find the top CAMBI score frames, you can use this cambi.vpy script:

from vapoursynth import core
log_file = 'cambi.log'
cambi = core.lsmas.LWLibavSource(filename).akarin.Cambi() # no need to set scores=True here.
def append_cambi(n, f): 
    with open(log_file, 'a') as fo:
        fo.write('%d %.4f\n' % (n, f.props['CAMBI']))
    return cambi
core.std.FrameEval(cambi, append_cambi, cambi).set_output()

And then run the script with vspipe -p cambi.vpy -a filename=INPUT.m2ts .. Then you can sort the output file cambi.log based on the 2nd column (i.e. sort -nr -k2,2 cambi.log if you are using Unix sort.) The first column will be the corresponding frame number.

Additional Notes

The cambi algorithm is very sensitive to dithering in the input, and it has a very rudimentary 2x2 lowpass dedither filter built-in if the input is 8-bit (it won't apply dedither to 10-bit inputs). However, if the output c-score map looks very much like a dither pattern, you should perform dedither first and apply Cambi to the 10-bit dedithered clip instead.

Clone this wiki locally