#!/usr/bin/env python
from collections import deque
import sys
import numpy as np
from skimage import color
import y4m

# Assuming BT.709
yuv2rgb = np.array([
    [1., 0., 1.28033], [1., -0.21482, -0.38059], [1., 2.12798, 0.]
])

# Simple box filter
box2 = np.ones((2, 2))


def decode_y4m_buffer(frame):
    W, H, buf = frame.headers['W'], frame.headers['H'], frame.buffer
    A, div2 = W * H, (H // 2, W // 2)
    Y = (np.ndarray((H, W), 'uint8', buf) - 16.) / 219.
    Cb = (np.ndarray(div2, 'uint8', buf, A) - 128.) / 224.
    Cr = (np.ndarray(div2, 'uint8', buf, A + A // 4) - 128.) / 224.
    YCbCr444 = np.dstack((Y, np.kron(Cb, box2), np.kron(Cr, box2)))
    return np.dot(YCbCr444, yuv2rgb.T)


scores = []


def process_pair(ref, recons):
    ref_lab = color.rgb2lab(decode_y4m_buffer(ref))
    recons_lab = color.rgb2lab(decode_y4m_buffer(recons))
    # "Color Image Quality Assessment Based on CIEDE2000"
    # Yang Yang, Jun Ming and Nenghai Yu, 2012
    # http://dx.doi.org/10.1155/2012/273723
    dE = color.deltaE_ciede2000(ref_lab, recons_lab, kL=0.65, kC=1.0, kH=4.0)
    scores.append(45. - 20. * np.log10(dE.mean()))
    print('%08d: %2.4f' % (ref.count, scores[-1]))


ref_frames = deque()
recons_frames = deque()


def process_ref(frame):
    ref_frames.append(frame)
    if recons_frames:
        process_pair(ref_frames.popleft(), recons_frames.popleft())


def process_recons(frame):
    recons_frames.append(frame)
    if ref_frames:
        process_pair(ref_frames.popleft(), recons_frames.popleft())


def main(args):
    OPENING = 'Opening %s...'
    BLOCK_SIZE = 4 * 1024 * 1024
    ref_parser = y4m.Reader(process_ref)
    recons_parser = y4m.Reader(process_recons)
    print(OPENING % args[1])
    with open(args[1], 'r') as ref:
        print(OPENING % args[2])
        with open(args[2], 'r') as recons:
            try:
                ref_buf, recons_buf = ref.buffer, recons.buffer
            except:
                ref_buf, recons_buf = ref, recons
            while True:
                data = ref_buf.read(BLOCK_SIZE)
                if not data: break
                ref_parser.decode(data)
                data = recons_buf.read(BLOCK_SIZE)
                if not data: break
                recons_parser.decode(data)
    print('Total: %2.4f' % np.array(scores).mean())


if __name__ == '__main__':
    main(sys.argv)

@@ -14,16 +14,17 @@
 v python
+3
 %0Afrom co
@@ -308,16 +308,19 @@
    W, H,
+ C,
  buf = f
@@ -357,16 +357,36 @@
 rs%5B'H'%5D,
+ frame.headers%5B'C'%5D,
  frame.b
@@ -402,16 +402,30 @@
  A, div2
+, dtype, scale
  = W * H
@@ -442,16 +442,101 @@
  W // 2)
+, 'uint8', 1.%0A    if C.endswith('p10'):%0A        dtype, scale, A = 'uint16', 4., A * 2
 %0A    Y =
@@ -556,23 +556,21 @@
 (H, W), 
-'uint8'
+dtype
 , buf) -
@@ -577,16 +577,34 @@
  16.
+ * scale
 ) / 
+(
 219.
+ * scale)
 %0A   
@@ -619,39 +619,37 @@
 p.ndarray(div2, 
-'uint8'
+dtype
 , buf, A) - 128.
@@ -644,32 +644,50 @@
 f, A) - 128.
+ * scale
 ) / 
+(
 224.
+ * scale)
 %0A    Cr = (n
@@ -702,23 +702,21 @@
 y(div2, 
-'uint8'
+dtype
 , buf, A
@@ -736,16 +736,34 @@
 128.
+ * scale
 ) / 
+(
 224.
+ * scale)
 %0A   
@@ -1689,24 +1689,222 @@
 popleft())%0A%0A
+class Reader(y4m.Reader):%0A    def _frame_size(self):%0A        pixels = super()._frame_size()%0A        if self._stream_headers%5B'C'%5D.endswith('p10'):%0A            return 2 * pixels%0A        return pixels%0A
 %0Adef main(ar
@@ -1980,36 +1980,32 @@
    ref_parser = 
-y4m.
 Reader(process_r
@@ -2028,20 +2028,16 @@
 arser = 
-y4m.
 Reader(p
@@ -2367,16 +2367,55 @@
 e True:%0A
+                if not ref_frames:%0A    
         
@@ -2446,32 +2446,36 @@
 ead(BLOCK_SIZE)%0A
+    
                 
@@ -2505,24 +2505,28 @@
             
+    
 ref_parser.d
@@ -2533,24 +2533,66 @@
 ecode(data)%0A
+                if not recons_frames:%0A    
             
@@ -2638,32 +2638,36 @@
                 
+    
 if not data: bre
@@ -2661,32 +2661,36 @@
 not data: break%0A
+    
                 

Add 420p10 support to dump_ciede2000.py