changeset 0:63af49cca5d2

initial import
author pmeerw@pan
date Tue, 06 May 2008 23:01:28 +0200
parents
children 9aa2dd7d0de7
files .hgignore README pydct/__init__.py pydct/dct.py pydct/perceptual.py pydct/t4x4.py pydct/t8x8.py pydct/tNxM.py pydct/tNxN.py setup.py
diffstat 9 files changed, 317 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Tue May 06 23:01:28 2008 +0200
@@ -0,0 +1,23 @@
+syntax: glob
+
+*.elc
+*.orig
+*.rej
+*~
+*.mergebackup
+*.o
+*.so
+*.pyc
+*.swp
+*.prof
+tests/.coverage*
+tests/annotated
+tests/*.err
+build
+MANIFEST
+.DS_Store
+tags
+cscope.*
+
+syntax: regexp
+^\.pc/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pydct/__init__.py	Tue May 06 23:01:28 2008 +0200
@@ -0,0 +1,6 @@
+from dct import fdct, idct, zigzag
+from t4x4 import fdct4x4, idct4x4
+from t8x8 import fdct8x8, idct8x8, fdct8x8x8, idct8x8x8, do_f8x8, do_i8x8, build_vector_from_8x8, set_8x8_from_vector
+from tNxM import fdct2d, idct2d
+from tNxN import fdctNxN, idctNxN
+from perceptual import watson_slack
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pydct/dct.py	Tue May 06 23:01:28 2008 +0200
@@ -0,0 +1,54 @@
+import numpy, math
+
+__dctN = {}
+
+def __init_dctN(n):
+    global __dctN
+    if not __dctN.has_key(n): 
+        c = numpy.empty((n, n), numpy.float)
+        c[0,:] = 1.0 / math.sqrt(n)
+        __cosf = lambda i, j: numpy.cos(math.pi * (2*j + 1) * (i + 1) / (2.0 * n))
+        c[1:,:] = math.sqrt(2.0 / n) * numpy.fromfunction(__cosf, (n-1, n))
+        __dctN[n] = c
+    return __dctN[n]
+
+def fdct(v):
+    '''
+    Forward DCT on vector.
+    '''
+    c = __init_dctN(len(v))
+    return numpy.dot(c, v)
+
+def idct(v):
+    '''
+    Inverse DCT on vector.
+    '''
+    c = __init_dctN(len(v))
+    return numpy.dot(numpy.transpose(c), v)
+
+def zigzag(x):
+    """Generates zig-zag scan sequence for two dimensional array."""
+    j = 0
+    i = 0
+    while True:
+
+        if i == x.shape[0]: j += 2; i = x.shape[0]-1
+        else: j = 0
+        while i >= 0 and j < x.shape[1]:
+            # run up
+            yield i, j
+            i -= 1
+            j += 1
+
+        if j > x.shape[1]: break
+
+        if j == x.shape[1]: j = x.shape[1]-1; i += 2
+        else: i = 0
+        while j >= 0 and i < x.shape[0]:
+            # run down
+            yield i, j
+            i += 1
+            j -= 1
+
+        if i > x.shape[0]: break
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pydct/perceptual.py	Tue May 06 23:01:28 2008 +0200
@@ -0,0 +1,39 @@
+import numpy
+
+JPEG_luma_Q = numpy.array(
+    [[16,  11,  10,  16,  24,  40,  51,  61],
+    [12,  12,  14,  19,  26,  58,  60,  55],
+    [14,  13,  16,  24,  40,  57,  69,  56],
+    [14,  17,  22,  29,  51,  87,  80,  62],
+    [18,  22,  37,  56,  68, 109, 103,  77],
+    [24,  35,  55,  64,  81, 104, 113,  92],
+    [49,  64,  78,  87, 103, 121, 120, 101],
+    [72,  92,  95,  98, 112, 100, 103,  99]])
+
+JPEG_chroma_Q = numpy.array(
+    [[17,  18,  24,  47,  99,  99,  99,  99],
+    [18,  21,  26,  66,  99,  99,  99,  99],
+    [24,  26,  56,  99,  99,  99,  99,  99],
+    [47,  66,  99,  99,  99,  99,  99,  99],
+    [99,  99,  99,  99,  99,  99,  99,  99],
+    [99,  99,  99,  99,  99,  99,  99,  99],
+    [99,  99,  99,  99,  99,  99,  99,  99],
+    [99,  99,  99,  99,  99,  99,  99,  99]])
+
+def watson_slack(b, c00):
+    # table of sensitivity values
+    t = numpy.array( \
+        [1.404,  1.011,  1.169,  1.664,  2.408,  3.433,  4.796,  6.563,\
+         1.011,  1.452,  1.323,  1.529,  2.006,  2.716,  3.679,  4.939,\
+         1.169,  1.323,  2.241,  2.594,  2.988,  3.649,  4.604,  5.883,\
+         1.664,  1.529,  2.594,  3.773,  4.559,  5.305,  6.281,  7.600,\
+         2.408,  2.006,  2.988,  4.559,  6.152,  7.463,  8.713, 10.175,\
+         3.433,  2.716,  3.649,  5.305,  7.463,  9.625, 11.588, 13.519,\
+         4.796,  3.679,  4.604,  6.281,  8.713, 11.588, 14.500, 17.294,\
+         6.563,  4.939,  5.883,  7.600, 10.175, 13.519, 17.294, 21.156])
+
+    tl = t * math.pow(b.ravel()[0] / c00, 0.649)
+  
+    tl[1:] = numpy.maximum(tl[1:], (numpy.abs(b.ravel()[1:])**0.7) * (tl[1:]**0.3))
+    return tl.reshape((8, 8))
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pydct/t4x4.py	Tue May 06 23:01:28 2008 +0200
@@ -0,0 +1,39 @@
+import numpy
+
+def fdct4x4(b):
+    '''
+    Compute the approximate 4x4 DCT coefficients of an array 
+    as defined by H.264/AVC.
+    '''
+    c, e = \
+        numpy.array(
+            [[ 1,  1,  1,  1], 
+             [ 2,  1, -1, -2], 
+             [ 1, -1, -1,  1], 
+             [ 1, -2,  2, -1]], numpy.float), \
+        numpy.array(
+            [[0.25, 0.15811388, 0.25, 0.15811388], 
+             [0.15811388, 0.1, 0.15811388, 0.1],
+             [0.25, 0.15811388, 0.25, 0.15811388], 
+             [0.15811388, 0.1, 0.15811388, 0.1]], numpy.float)
+        
+    return numpy.dot(c, numpy.dot(b, numpy.transpose(c))) * e
+
+def idct4x4(b):
+    '''
+    Compute the inverse 4x4 DCT of the array.
+    '''
+    c, e = \
+        numpy.array(
+            [[1, 1, 1, 0.5], 
+             [1, 0.5, -1, -1], 
+             [1, -0.5, -1, 1], 
+             [1, -1, 1, -0.5]], numpy.float), \
+        numpy.array(
+            [[0.25, 0.31622777, 0.25, 0.31622777], 
+             [0.31622777, 0.4, 0.31622777, 0.4], 
+             [0.25, 0.31622777, 0.25, 0.31622777],
+             [0.31622777, 0.4, 0.31622777, 0.4]], numpy.float)
+
+    return numpy.dot(c, numpy.dot(b*e, numpy.transpose(c)))
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pydct/t8x8.py	Tue May 06 23:01:28 2008 +0200
@@ -0,0 +1,75 @@
+import tNxN, numpy
+
+def fdct8x8(b):
+    '''
+    Forward 8x8 DCT.
+    '''
+    return fdctNxN(b)    
+    
+def idct8x8(b):
+    '''
+    Inverse 8x8 DCT.
+    '''
+    return idctNxN(b)
+
+def fdct8x8x8(b):
+    '''
+    Forward 8x8x8 DCT.
+    '''
+    d = numpy.empty((8, 8, 8), numpy.float)
+    for i in xrange(8):
+        d[i,:,:] = fdct8x8(b[i,:,:])    
+    for r in xrange(8):
+        for c in xrange(8):
+            d[:,r,c] = fdct(d[:,r,c])            
+    return d
+    
+def idct8x8x8(b):
+    '''
+    Inverse 8x8x8 DCT.
+    '''
+    d = numpy.empty((8, 8, 8), numpy.float)
+    for r in xrange(8):
+        for c in xrange(8):
+            d[:,r,c] = idct(b[:,r,c])            
+    for i in xrange(8):
+        d[i,:,:] = idct8x8(d[i,:,:])    
+    return d
+
+def do_f8x8(s):
+    for r in xrange(0, s.shape[0], 8):
+        for c in xrange(0, s.shape[1], 8): 
+            s[r:r+8,c:c+8] = fdct8x8(s[r:r+8,c:c+8])
+    return s
+
+def _get_zz():
+    zz = []
+    for i, j in wm.zigzag(numpy.empty((8,8))):
+        zz.append(i*8 + j)
+    return zz
+
+def build_vector_from_8x8(s):
+    v = numpy.zeros((64, (s.shape[0]//8)*(s.shape[1]//8)), numpy.float)
+    i = 0
+    zz = _get_zz()
+    for r in xrange(0, s.shape[0], 8):
+        for c in xrange(0, s.shape[1], 8): 
+            v[:,i] = s[r:r+8,c:c+8].flat[zz]
+            i += 1
+    return v
+
+def do_i8x8(s):
+    for r in xrange(0, s.shape[0], 8):
+        for c in xrange(0, s.shape[1], 8): 
+            s[r:r+8,c:c+8] = idct8x8(s[r:r+8,c:c+8])
+    return s
+
+def set_8x8_from_vector(s, v):
+    i = 0
+    zz = _get_zz()
+    for r in xrange(0, s.shape[0], 8):
+        for c in xrange(0, s.shape[1], 8): 
+            s[r:r+8,c:c+8].flat[zz] = v[:,i]
+            i += 1
+    return s            
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pydct/tNxM.py	Tue May 06 23:01:28 2008 +0200
@@ -0,0 +1,56 @@
+import numpy, math
+
+__dctNxM = {}
+
+def __init_dctNxM(b):
+    global __dctNxM
+    if not __dctNxM.has_key(b.shape): 
+        c = numpy.sqrt([2.0 / b.shape[0], 2.0 / b.shape[1]])
+        __cosf0 = lambda j, i: numpy.cos((math.pi * (2*i + 1) * j) / (2.0 * b.shape[0]))
+        y = c[0] * numpy.fromfunction(__cosf0, (b.shape[0], b.shape[0]))
+        __cosf1 = lambda j, i: numpy.cos((math.pi * (2*i + 1) * j) / (2.0 * b.shape[1]))
+        x = c[1] * numpy.fromfunction(__cosf1, (b.shape[1], b.shape[1]))
+        __dctNxM[b.shape] = (y, x), c / math.sqrt(2.0)
+    return __dctNxM[b.shape]
+    
+def fdct2d(b):
+    '''
+    Compute forward 2D DCT on the array of arbitrary size.
+    Note: slow.
+    '''
+    global __dctNxM
+    ct, c = __init_dctNxM(b)
+    
+    d = numpy.empty(b.shape, numpy.float)
+    d[0][0] = c[0] * c[1] * numpy.sum(numpy.sum(b))
+
+    for i in xrange(1, b.shape[1]):
+        d[0][i] = c[0] * numpy.sum(ct[1][i] * numpy.sum(b, 0))
+
+    for j in xrange(1, b.shape[0]):
+        d[j][0] = c[1] * numpy.sum(ct[0][j] * numpy.sum(b, 1))
+
+    bt = numpy.transpose(b)
+    for j in xrange(1, b.shape[0]):
+        d[j,1:] = numpy.sum(ct[1][1:] * numpy.sum(bt * ct[0][j], 1), 1)
+    
+    return d
+
+def idct2d(b):
+    '''
+    Compute inverse 2D DCT on array of arbitrary size.
+    '''
+    global __dctNxM
+    ct, c = __init_dctNxM(b)
+
+    p0 = c[0] * c[1] * b[0][0]
+    bt = numpy.transpose(b[1:,1:])
+    ctt = ct[1][1:,:].transpose()
+    d = numpy.empty(b.shape, numpy.float)
+    for y in xrange(b.shape[0]):
+        d[y,:] = p0 + \
+            c[0] * numpy.sum(b[0,1:] * ctt, 1) + \
+            c[1] * numpy.sum(b[1:,0] * ct[0][1:,y]) + \
+            numpy.sum(numpy.sum(bt * ct[0][1:,y],1) * ctt,1)
+    return d
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pydct/tNxN.py	Tue May 06 23:01:28 2008 +0200
@@ -0,0 +1,15 @@
+import dct, numpy
+
+def fdctNxN(b):
+    '''
+    Forward 2D DCT on NxN array.
+    '''
+    c = __init_dctN(b.shape[0])
+    return numpy.dot(c, numpy.dot(b, numpy.transpose(c)))
+
+def idctNxN(b):
+    '''
+    Inverse 2D DCT on NxN array.
+    '''
+    c = __init_dctN(b.shape[0])
+    return numpy.dot(numpy.transpose(c), numpy.dot(b, c))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/setup.py	Tue May 06 23:01:28 2008 +0200
@@ -0,0 +1,10 @@
+from distutils.core import setup
+
+setup (name = 'pydct',
+       version = '1.0',
+       author = 'Peter Meerwald',
+       author_email = 'pmeerw@pmeerw.net',
+       description = 'pydct: Discrete Cosine Transform routines for Python',
+       packages=['pydct'],
+       license = 'BSD'
+)

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.