├── README.md ├── decoder.py ├── encoder.py ├── lena.bmp ├── main.py └── utils.py /README.md: -------------------------------------------------------------------------------- 1 | # JPEG 编解码器 2 | 3 | 仅实现从 RGB 图像到熵编码的过程,且为了方便查看,二进制串以字符串形式存储。 4 | 5 | ## 依赖 6 | 7 | - Python 8 | - Numpy 9 | - Pillow 10 | - tqdm 11 | 12 | ## 运行 13 | 14 | 在 `main.py` 中修改 `input_file` 为图片路径后执行即可。 15 | -------------------------------------------------------------------------------- /decoder.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PIL import Image 3 | from tqdm import tqdm 4 | 5 | from utils import BitObject, RLEObject, zigzag_points, get_quantization_tables, get_inverse_huffman_tables, BIT_MASK 6 | 7 | 8 | class JpegDecoder(object): 9 | 10 | def __init__(self): 11 | self.quantization_tables = get_quantization_tables() 12 | self.inverse_huffman_tables = get_inverse_huffman_tables() 13 | 14 | 15 | def decode(self, bitstream, height, width): 16 | ycbcr = np.zeros([height, width, 3], dtype=np.int32) 17 | 18 | rle_objects_group = self.parse_bitstream(bitstream) 19 | progress_bar = tqdm(total=height // 8 * width // 8, desc='decode') 20 | 21 | block_idx = 0 22 | for row in range(0, height, 8): 23 | for col in range(0, width, 8): 24 | progress_bar.set_postfix({'row': row, 'col': col}) 25 | progress_bar.update() 26 | for component in range(3): 27 | zigzag = self.entropy_decode(rle_objects_group[block_idx]) 28 | quantized_matrix = self.zigzag_to_matrix(zigzag) 29 | dct_matrix = self.dequantize(quantized_matrix, component) 30 | block = self.idct(dct_matrix) 31 | ycbcr[row:row+8, col:col+8, component] = block 32 | block_idx += 1 33 | 34 | progress_bar.close() 35 | 36 | image = self.postprocess(ycbcr) 37 | return image 38 | 39 | 40 | def parse_bitstream(self, bitstream): 41 | rle_objects_group = [] 42 | component = 0 43 | bit_idx = 0 44 | while bit_idx < len(bitstream): 45 | block_rle_objects = [] 46 | dc_inv_ht = self.inverse_huffman_tables['lum_dc'] if component == 0 else self.inverse_huffman_tables['chrom_dc'] 47 | ac_inv_ht = self.inverse_huffman_tables['lum_ac'] if component == 0 else self.inverse_huffman_tables['chrom_ac'] 48 | 49 | # dc 50 | bitstring = '' 51 | while bit_idx < len(bitstream): 52 | bitstring += bitstream[bit_idx] 53 | bit_idx += 1 54 | 55 | ht_value = dc_inv_ht.get(bitstring) 56 | if ht_value is not None: 57 | num_zero = (ht_value >> 4) & 0xF 58 | length = ht_value & 0xF 59 | value_bit_string = bitstream[bit_idx:bit_idx+length] 60 | bit_idx += length 61 | 62 | value = int(value_bit_string, 2) 63 | if value_bit_string[0] == '0': 64 | value = value + 1 - (1 << length) 65 | rle_obj = RLEObject(num_zero=num_zero, value=value) 66 | block_rle_objects.append(rle_obj) 67 | break 68 | 69 | # ac 70 | eob = False 71 | cnt = 0 72 | while cnt < 63: 73 | bitstring = '' 74 | while bit_idx < len(bitstream): 75 | bitstring += bitstream[bit_idx] 76 | bit_idx += 1 77 | ht_value = ac_inv_ht.get(bitstring) 78 | if ht_value is not None: 79 | if ht_value == 0: 80 | eob = True 81 | break 82 | 83 | num_zero = (ht_value >> 4) & 0xF 84 | length = ht_value & 0xF 85 | cnt += num_zero + 1 86 | 87 | if num_zero == 0xF: 88 | rle_obj = RLEObject(num_zero=num_zero, value=0) 89 | bit_idx += 1 90 | else: 91 | value_bit_string = bitstream[bit_idx:bit_idx+length] 92 | value = int(value_bit_string, 2) 93 | bit_idx += length 94 | if value_bit_string[0] == '0': 95 | value = value + 1 - (1 << length) 96 | rle_obj = RLEObject(num_zero=num_zero, value=value) 97 | 98 | block_rle_objects.append(rle_obj) 99 | break 100 | if eob: 101 | break 102 | 103 | rle_objects_group.append(block_rle_objects) 104 | component = (component + 1) % 3 105 | 106 | return rle_objects_group 107 | 108 | 109 | def entropy_decode(self, rle_objects): 110 | zigzag = [] 111 | 112 | for rle_obj in rle_objects: 113 | zigzag.extend([0 for _ in range(rle_obj.num_zero)] + [rle_obj.value]) 114 | 115 | diff = 64 - len(zigzag) 116 | if diff > 0: 117 | zigzag.extend([0 for _ in range(diff)]) 118 | 119 | return zigzag 120 | 121 | 122 | def get_rle_obj(self, bit_objects): 123 | value_first_bit = bit_objects[1].value & BIT_MASK[bit_objects[1].length - 1] 124 | if value_first_bit == 1: 125 | value = bit_objects[1].value 126 | else: 127 | value = bit_objects[1].value + 1 - (1 << bbit_objects[1].length) 128 | 129 | num_zero = 0 130 | return RLEObject(num_zero=num_zero, value=value) 131 | 132 | 133 | def zigzag_to_matrix(self, zigzag): 134 | matrix = np.zeros([8, 8], dtype=np.int32) 135 | for idx, point in enumerate(zigzag_points(*matrix.shape)): 136 | matrix[point] = zigzag[idx] 137 | return matrix 138 | 139 | 140 | def dequantize(self, matrix, component): 141 | return matrix * self.quantization_tables[component] 142 | 143 | 144 | def idct(self, matrix): 145 | transform = np.zeros([8, 8], dtype=np.float32) 146 | for i in range(8): 147 | for j in range(8): 148 | c = np.sqrt(0.125) if i == 0 else 0.5 149 | transform[i, j] = c * np.cos((j + 0.5) * np.pi * i / 8.0) 150 | 151 | f = np.matmul(np.matmul(transform.T, matrix), transform) 152 | return f 153 | 154 | 155 | def postprocess(self, ycbcr): 156 | ycbcr = (ycbcr + 128).astype(np.uint8) 157 | rgb = self.ycbcr2rgb(ycbcr) 158 | image = Image.fromarray(rgb, 'RGB') 159 | return image 160 | 161 | 162 | def ycbcr2rgb(self, ycbcr): 163 | # R = Y + + (Cr - 128) * 1.40200 164 | # G = Y + (Cb - 128) * -0.34414 + (Cr - 128) * -0.71414 165 | # B = Y + (Cb - 128) * 1.77200 166 | matrix = np.array([[1.0, 0.0, 1.40200], 167 | [1.0, -0.34414, -0.71414], 168 | [1.0, 1.77200, 0.0]]) 169 | ycbcr = np.array(ycbcr, dtype=np.float) 170 | ycbcr[:, :, 1:] -= 128 171 | rgb = np.dot(ycbcr, matrix.T) 172 | rgb = np.clip(rgb, a_min=0, a_max=255) 173 | return rgb.astype(np.uint8) 174 | -------------------------------------------------------------------------------- /encoder.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | from PIL import Image 4 | from tqdm import tqdm 5 | 6 | from utils import BitObject, RLEObject, zigzag_points, get_quantization_tables, get_huffman_tables, BIT_MASK 7 | 8 | 9 | class JpegEncoder(object): 10 | 11 | def __init__(self): 12 | self.quantization_tables = get_quantization_tables() 13 | self.huffman_tables = get_huffman_tables() 14 | self.total_bit_objects = [] 15 | 16 | 17 | def encode(self, image_path): 18 | ycbcr = self.preprocess(image_path) 19 | height, width, _ = ycbcr.shape 20 | progress_bar = tqdm(total=height // 8 * width // 8, desc='encode') 21 | 22 | self.total_bit_objects = [] 23 | for row in range(0, height, 8): 24 | for col in range(0, width, 8): 25 | progress_bar.set_postfix({'row': row, 'col': col}) 26 | progress_bar.update() 27 | for component in range(3): 28 | block = ycbcr[row:row+8, col:col+8, component] 29 | dct_matrix = self.dct(block) 30 | quantized_matrix = self.quantize(dct_matrix, component) 31 | zigzag = self.matrix_to_zigzag(quantized_matrix) 32 | bit_objects = self.entropy_encode(zigzag, component) 33 | self.total_bit_objects.extend(bit_objects) 34 | progress_bar.close() 35 | return self.convert_bitstream(), height, width 36 | 37 | 38 | def preprocess(self, image_path): 39 | image = Image.open(image_path) 40 | image = np.array(image) 41 | ycbcr = self.rgb2ycbcr(image) - 128 42 | return ycbcr 43 | 44 | 45 | def rgb2ycbcr(self, rgb): 46 | # Y = R * 0.29900 + G * 0.58700 + B * 0.11400 47 | # Cb = R * -0.16874 + G * -0.33126 + B * 0.50000 + 128 48 | # Cr = R * 0.50000 + G * -0.41869 + B * -0.08131 + 128 49 | matrix = np.array([[0.29900, 0.58700, 0.11400], 50 | [-0.16784, -0.33126, 0.50000], 51 | [0.50000, -0.41869, -0.08131]]) 52 | ycbcr = np.dot(rgb, matrix.T) 53 | ycbcr[:, :, 1:] += 128 54 | ycbcr = np.clip(ycbcr, a_min=0, a_max=255) 55 | return ycbcr.astype(np.uint8) 56 | 57 | 58 | def dct(self, block): 59 | transform = np.zeros([8, 8], dtype=np.float32) 60 | for i in range(8): 61 | for j in range(8): 62 | c = np.sqrt(0.125) if i == 0 else 0.5 63 | transform[i, j] = c * np.cos((j + 0.5) * np.pi * i / 8.0) 64 | 65 | uv = np.matmul(np.matmul(transform, block), transform.T) 66 | return uv 67 | 68 | 69 | def quantize(self, block, component): 70 | return (block / self.quantization_tables[component]).round().astype(np.int32) 71 | 72 | 73 | def matrix_to_zigzag(self, matrix): 74 | return np.array([matrix[point] for point in zigzag_points(*matrix.shape)]) 75 | 76 | 77 | def entropy_encode(self, zigzag, component): 78 | if component == 0: 79 | dc_ht = self.huffman_tables['lum_dc'] 80 | ac_ht = self.huffman_tables['lum_ac'] 81 | else: 82 | dc_ht = self.huffman_tables['chrom_dc'] 83 | ac_ht = self.huffman_tables['chrom_ac'] 84 | 85 | rle_objects = self.run_length_code(zigzag) 86 | bit_objects = [] 87 | 88 | # dc 89 | dc_bit_object = self.get_bit_code(rle_objects[0].value) 90 | bit_objects.append(dc_ht[dc_bit_object.length]) 91 | bit_objects.append(dc_bit_object) 92 | 93 | # ac 94 | for rle_obj in rle_objects[1:]: 95 | ac_bit_object = self.get_bit_code(rle_obj.value) 96 | bit_objects.append(ac_ht[rle_obj.num_zero << 4 | ac_bit_object.length]) 97 | bit_objects.append(ac_bit_object) 98 | 99 | if bit_objects[-1] == BitObject(0, 0): 100 | bit_objects.pop(-1) 101 | 102 | return bit_objects 103 | 104 | 105 | def run_length_code(self, zigzag): 106 | rle_objects = [] 107 | 108 | end_idx = 63 109 | while end_idx > 0 and zigzag[end_idx] == 0: 110 | end_idx -= 1 111 | 112 | zero_cnt = 0 113 | for idx, val in enumerate(zigzag): 114 | if idx > end_idx: 115 | rle_objects.append(RLEObject(num_zero=0, value=0)) 116 | break 117 | elif val == 0 and zero_cnt < 15: 118 | zero_cnt += 1 119 | else: 120 | rle_objects.append(RLEObject(num_zero=zero_cnt, value=val)) 121 | zero_cnt = 0 122 | return rle_objects 123 | 124 | 125 | def get_bit_code(self, value): 126 | tmp = value if value > 0 else -value 127 | length = 0 128 | while tmp > 0: 129 | tmp >>= 1 130 | length += 1 131 | bit_value = value if value > 0 else (BIT_MASK[length] + value - 1) 132 | return BitObject(length=length, value=bit_value) 133 | 134 | 135 | def convert_bitstream(self): 136 | bitstream = '' 137 | for bit_obj in self.total_bit_objects: 138 | value = bit_obj.value & (BIT_MASK[bit_obj.length] - 1) 139 | bitstream += bin(value)[2:].zfill(bit_obj.length) 140 | return bitstream 141 | -------------------------------------------------------------------------------- /lena.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Els-y/jpeg-python/9d05d7c9687c94eb515e8578a4614af488585f5a/lena.bmp -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from encoder import JpegEncoder 2 | from decoder import JpegDecoder 3 | 4 | 5 | def main(): 6 | input_file = 'lena.bmp' 7 | 8 | encoder = JpegEncoder() 9 | decoder = JpegDecoder() 10 | 11 | bitstream, height, width = encoder.encode(input_file) 12 | print('bitstream len:', len(bitstream)) 13 | restore_image = decoder.decode(bitstream, height=height, width=width) 14 | restore_image.show() 15 | 16 | 17 | if __name__ == "__main__": 18 | main() 19 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | import numpy as np 3 | 4 | RLEObject = namedtuple('RLEObject', 'num_zero value') 5 | 6 | class BitObject(object): 7 | 8 | def __init__(self, value, length): 9 | self.value = value 10 | self.length = length 11 | 12 | def bitstring(self): 13 | value = self.value & ((1 << self.length) - 1) 14 | return bin(value)[2:].zfill(self.length) 15 | 16 | def __repr__(self): 17 | return 'BitObject(value={}, length={}, bitstring={})'.format( 18 | self.value, self.length, self.bitstring()) 19 | 20 | def __eq__(self, obj): 21 | return obj.value == self.value and obj.length == self.length 22 | 23 | 24 | BIT_MASK = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536] 25 | 26 | # Quantization Table for: Photoshop - (Save As 10) 27 | # http://www.impulseadventure.com/photo/jpeg-quantization.html 28 | # https://www.impulseadventure.com/photo/jpeg-quantization-lookup.html?src1=10323 29 | 30 | LUMINANCE_QUANTIZATION_TABLE = np.array([ 31 | [2, 2, 3, 4, 5, 6, 8, 11], 32 | [2, 2, 2, 4, 5, 7, 9, 11], 33 | [3, 2, 3, 5, 7, 9, 11, 12], 34 | [4, 4, 5, 7, 9, 11, 12, 12], 35 | [5, 5, 7, 9, 11, 12, 12, 12], 36 | [6, 7, 9, 11, 12, 12, 12, 12], 37 | [8, 9, 11, 12, 12, 12, 12, 12], 38 | [11, 11, 12, 12, 12, 12, 12, 12] 39 | ]) 40 | 41 | CHROMINANCE_QUANTIZATION_TABLE = np.array([ 42 | [3, 3, 7, 13, 15, 15, 15, 15], 43 | [3, 4, 7, 13, 14, 12, 12, 12], 44 | [7, 7, 13, 14, 12, 12, 12, 12], 45 | [13, 13, 14, 12, 12, 12, 12, 12], 46 | [15, 14, 12, 12, 12, 12, 12, 12], 47 | [15, 12, 12, 12, 12, 12, 12, 12], 48 | [15, 12, 12, 12, 12, 12, 12, 12], 49 | [15, 12, 12, 12, 12, 12, 12, 12] 50 | ]) 51 | 52 | ZIGZAG_TABLES = [ 53 | 0, 1, 5, 6, 14, 15, 27, 28, 54 | 2, 4, 7, 13, 16, 26, 29, 42, 55 | 3, 8, 12, 17, 25, 30, 41, 43, 56 | 9, 11, 18, 24, 31, 40, 44, 53, 57 | 10, 19, 23, 32, 39, 45, 52, 54, 58 | 20, 22, 33, 38, 46, 51, 55, 60, 59 | 21, 34, 37, 47, 50, 56, 59, 61, 60 | 35, 36, 48, 49, 57, 58, 62, 63 61 | ] 62 | 63 | LUMINANCE_DC_NRCODES = [0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] 64 | LUMINANCE_DC_VALUES = [4, 5, 3, 2, 6, 1, 0, 7, 8, 9, 10, 11] 65 | 66 | CHROMINANCE_DC_NRCODES = [0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0] 67 | CHROMINANCE_DC_VALUES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 68 | 69 | LUMINANCE_AC_NOCODES = [0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d] 70 | LUMINANCE_AC_VALUES = [ 71 | 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 72 | 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 73 | 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 74 | 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 75 | 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 76 | 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 77 | 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 78 | 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 79 | 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 80 | 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 81 | 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 82 | 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 83 | 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 84 | 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 85 | 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 86 | 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 87 | 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 88 | 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 89 | 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 90 | 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 91 | 0xf9, 0xfa] 92 | 93 | CHROMINANCE_AC_NRCODES = [0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77] 94 | CHROMINANCE_AC_VALUES = [ 95 | 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 96 | 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 97 | 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 98 | 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 99 | 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 100 | 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 101 | 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 102 | 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 103 | 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 104 | 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 105 | 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 106 | 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 107 | 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 108 | 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 109 | 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 110 | 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 111 | 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 112 | 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 113 | 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 114 | 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 115 | 0xf9, 0xfa 116 | ] 117 | 118 | 119 | def build_huffman_table(nr_codes, values): 120 | huffman_table = {} 121 | inverse_huffman_table = {} 122 | pos_in_table = 0 123 | code_value = 0 124 | for k in range(1, 17): 125 | for j in range(1, nr_codes[k - 1] + 1): 126 | bit_object = BitObject(value=code_value, length=k) 127 | huffman_table[values[pos_in_table]] = bit_object 128 | inverse_huffman_table[bit_object.bitstring()] = values[pos_in_table] 129 | pos_in_table += 1 130 | code_value += 1 131 | code_value <<= 1 132 | 133 | return huffman_table, inverse_huffman_table 134 | 135 | 136 | LUMINANCE_DC_HUFFMAN_TABLE, LUMINANCE_DC_INVERSE_HUFFMAN_TABLE = build_huffman_table(LUMINANCE_DC_NRCODES, LUMINANCE_DC_VALUES) 137 | LUMINANCE_AC_HUFFMAN_TABLE, LUMINANCE_AC_INVERSE_HUFFMAN_TABLE = build_huffman_table(LUMINANCE_AC_NOCODES, LUMINANCE_AC_VALUES) 138 | CHROMINANCE_DC_HUFFMAN_TABLE, CHROMINANCE_DC_INVERSE_HUFFMAN_TABLE = build_huffman_table(CHROMINANCE_DC_NRCODES, CHROMINANCE_DC_VALUES) 139 | CHROMINANCE_AC_HUFFMAN_TABLE, CHROMINANCE_AC_INVERSE_HUFFMAN_TABLE = build_huffman_table(CHROMINANCE_AC_NRCODES, CHROMINANCE_AC_VALUES) 140 | 141 | 142 | def get_quantization_tables(): 143 | return [LUMINANCE_QUANTIZATION_TABLE, 144 | CHROMINANCE_QUANTIZATION_TABLE, 145 | CHROMINANCE_QUANTIZATION_TABLE] 146 | 147 | 148 | def get_huffman_tables(): 149 | tables = { 150 | 'lum_dc': LUMINANCE_DC_HUFFMAN_TABLE, 151 | 'lum_ac': LUMINANCE_AC_HUFFMAN_TABLE, 152 | 'chrom_dc': CHROMINANCE_DC_HUFFMAN_TABLE, 153 | 'chrom_ac': CHROMINANCE_AC_HUFFMAN_TABLE 154 | } 155 | return tables 156 | 157 | 158 | def get_inverse_huffman_tables(): 159 | tables = { 160 | 'lum_dc': LUMINANCE_DC_INVERSE_HUFFMAN_TABLE, 161 | 'lum_ac': LUMINANCE_AC_INVERSE_HUFFMAN_TABLE, 162 | 'chrom_dc': CHROMINANCE_DC_INVERSE_HUFFMAN_TABLE, 163 | 'chrom_ac': CHROMINANCE_AC_INVERSE_HUFFMAN_TABLE 164 | } 165 | return tables 166 | 167 | 168 | def zigzag_points(rows, cols): 169 | # constants for directions 170 | UP, DOWN, RIGHT, LEFT, UP_RIGHT, DOWN_LEFT = range(6) 171 | 172 | # move the point in different directions 173 | def move(direction, point): 174 | return { 175 | UP: lambda point: (point[0] - 1, point[1]), 176 | DOWN: lambda point: (point[0] + 1, point[1]), 177 | LEFT: lambda point: (point[0], point[1] - 1), 178 | RIGHT: lambda point: (point[0], point[1] + 1), 179 | UP_RIGHT: lambda point: move(UP, move(RIGHT, point)), 180 | DOWN_LEFT: lambda point: move(DOWN, move(LEFT, point)) 181 | }[direction](point) 182 | 183 | # return true if point is inside the block bounds 184 | def inbounds(point): 185 | return 0 <= point[0] < rows and 0 <= point[1] < cols 186 | 187 | # start in the top-left cell 188 | point = (0, 0) 189 | 190 | # True when moving up-right, False when moving down-left 191 | move_up = True 192 | 193 | for i in range(rows * cols): 194 | yield point 195 | if move_up: 196 | if inbounds(move(UP_RIGHT, point)): 197 | point = move(UP_RIGHT, point) 198 | else: 199 | move_up = False 200 | if inbounds(move(RIGHT, point)): 201 | point = move(RIGHT, point) 202 | else: 203 | point = move(DOWN, point) 204 | else: 205 | if inbounds(move(DOWN_LEFT, point)): 206 | point = move(DOWN_LEFT, point) 207 | else: 208 | move_up = True 209 | if inbounds(move(DOWN, point)): 210 | point = move(DOWN, point) 211 | else: 212 | point = move(RIGHT, point) --------------------------------------------------------------------------------