class JpegEncoder(object): def __init__(self, image, quality, out, comment, ais): self.quality = quality self.jpeg_obj = JpegInfo(image, comment) self.image_width, self.image_height = image.size self.out = out self.dct = DCT(self.quality) self.huf = Huffman(*image.size) self.hasais = ais self.k_matrix = -1 def compress(self, embedded_data=None, password='******'): self.embedded_data = EmbedData( embedded_data) if embedded_data else None self.password = password self.write_headers() self.write_compressed_data() self.write_eoi() self.out.flush() def get_quality(self): return self.quality def set_quality(self, quality): self.quality = quality self.dct = DCT(quality) def write_array(self, data): length = ((data[2] & 0xff) << 8) + (data[3] & 0xff) + 2 self.out.write(bytearray(data[:length])) def write_marker(self, data): self.out.write(bytearray(data[:2])) def write_eoi(self): EOI = [0xff, 0xD9] self.write_marker(EOI) def write_headers(self): SOI = [0xff, 0xD8] self.write_marker(SOI) JFIF = [ 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00 ] self.write_array(JFIF) comment = self.jpeg_obj.get_comment() if comment: length = len(comment) + 2 COM = [0xff, 0xfe, length >> 8 & 0xff, length & 0xff] COM.extend(comment) self.write_array(COM) DQT = [0xff, 0xdb, 0x00, 0x84] for k in range(2): DQT.append(k) DQT.extend([ self.dct.quantum[k][JPEG_NATURAL_ORDER[i]] for i in range(64) ]) self.write_array(DQT) SOF = [ 0xff, 0xc0, 0x00, 0x11, self.jpeg_obj.precision, self.jpeg_obj.image_height >> 8 & 0xff, self.jpeg_obj.image_height & 0xff, self.jpeg_obj.image_width >> 8 & 0xff, self.jpeg_obj.image_width & 0xff, self.jpeg_obj.comp_num ] for i in range(self.jpeg_obj.comp_num): SOF.append(self.jpeg_obj.com_id[i]) SOF.append( eight_byte(self.jpeg_obj.hsamp_factor[i], self.jpeg_obj.vsamp_factor[i])) SOF.append(self.jpeg_obj.qtable_number[i]) self.write_array(SOF) DHT = [0xff, 0xc4, 0, 0] for i in range(4): DHT.extend(self.huf.BITS[i]) DHT.extend(self.huf.VAL[i]) DHT[2] = len(DHT) - 2 >> 8 & 0xff DHT[3] = len(DHT) - 2 & 0xff self.write_array(DHT) SOS = [0] * 14 SOS = [0xff, 0xda, 0x00, 0x0c, self.jpeg_obj.comp_num] for i in range(self.jpeg_obj.comp_num): SOS.append(self.jpeg_obj.com_id[i]) SOS.append( eight_byte(self.jpeg_obj.dctable_number[i], self.jpeg_obj.actable_number[i])) SOS.append(self.jpeg_obj.ss) SOS.append(self.jpeg_obj.se) SOS.append(eight_byte(self.jpeg_obj.ah, self.jpeg_obj.al)) self.write_array(SOS) def _get_coeff(self): dct_array1 = create_array(0.0, 8, 8) dct_array2 = create_array(0.0, 8, 8) dct_array3 = create_array(0, 64) coeff = [] for r in range(min(self.jpeg_obj.block_height)): for c in range(min(self.jpeg_obj.block_width)): xpos = c * 8 ypos = r * 8 for comp in range(self.jpeg_obj.comp_num): indata = self.jpeg_obj.components[comp] maxa = self.image_height / 2 * self.jpeg_obj.vsamp_factor[ comp] - 1 maxb = self.image_width / 2 * self.jpeg_obj.hsamp_factor[ comp] - 1 for i in range(self.jpeg_obj.vsamp_factor[comp]): for j in range(self.jpeg_obj.hsamp_factor[comp]): ia = ypos * self.jpeg_obj.vsamp_factor[comp] + i * 8 ib = xpos * self.jpeg_obj.hsamp_factor[comp] + j * 8 for a in range(8): for b in range(8): dct_array1[a][b] = indata[min( ia + a, maxa)][min(ib + b, maxb)] dct_array2 = self.dct.forward_dct(dct_array1) dct_array3 = self.dct.quantize_block( dct_array2, self.jpeg_obj.qtable_number[comp]) coeff.extend(dct_array3[:64]) return coeff def write_compressed_data(self): last_dc_value = create_array(0, self.jpeg_obj.comp_num) zero_array = create_array(0, 64) width, height = 0, 0 min_block_width = min(self.jpeg_obj.block_width) min_block_height = min(self.jpeg_obj.block_height) logger.info('DCT/quantisation starts') logger.info('%d x %d' % (self.image_width, self.image_height)) coeff = self._get_coeff() #量化后的系数,是整数 coeff_count = len(coeff) #导出未处理的QDCT if self.hasais: filename = 'unpro_ais.json' else: filename = 'unpro.json' with open(filename, 'w') as f_unprocess: json.dump(coeff, f_unprocess) #AIS处理 if self.hasais: size_secret = self.embedded_data.len ais = Ais(coeff, size_secret) #coeff被修改 ais.statistic() self.k_matrix = ais.fix() with open('aised.json', 'w') as f_aised: json.dump(coeff, f_aised) #嵌入——>再统计嵌入后的数据,决定是否继续做AIS处理 logger.info('got %d DCT AC/DC coefficients' % coeff_count) _changed, _embedded, _examined, _expected, _one, _large, _thrown, _zero = 0, 0, 0, 0, 0, 0, 0, 0 shuffled_index = 0 for i, cc in enumerate(coeff): if i % 64 == 0: continue if cc == 1 or cc == -1: _one += 1 elif cc == 0: _zero += 1 _large = coeff_count - _zero - _one - coeff_count / 64 #有效系数的个数 _expected = _large + int(0.49 * _one) #预期容量,shrinkage效应无法确定 logger.info('one=%d' % _one) logger.info('large=%d' % _large) logger.info('\nexpected capacity: %d bits\n' % _expected) logger.info('expected capacity with') for i in range(1, 8): n = (1 << i) - 1 #n=2^i-1 changed = _large - _large % (n + 1) changed = (changed + _one + _one / 2 - _one / (n + 1)) / (n + 1) usable = (_expected * i / n - _expected * i / n % n) / 8 if usable == 0: break logger.info( '%s code: %d bytes (efficiency: %d.%d bits per change)' % ('default' if i == 1 else '(1, %d, %d)' % (n, i), usable, usable * 8 / changed, usable * 80 / changed % 10)) #shuffles all coefficients using a permutation,使用排列对系数进行混洗 if self.embedded_data is not None: logger.info('permutation starts') random = F5Random(self.password) permutation = Permutation(coeff_count, random) next_bit_to_embed = 0 byte_to_embed = len(self.embedded_data) available_bits_to_embed = 0 logger.info('Embedding of %d bits (%d+4 bytes)' % (byte_to_embed * 8 + 32, byte_to_embed)) if byte_to_embed > 0x007fffff: byte_to_embed = 0x007ffff for i in range(1, 8): self.n = (1 << i) - 1 usable = (_expected * i / self.n - _expected * i / self.n % self.n) / 8 if usable < byte_to_embed + 4: break #确定(1,n,k) if self.k_matrix < 0: k = i - 1 else: k = self.k_matrix self.n = (1 << k) - 1 if self.n == 0: logger.info('using default code, file will not fit') self.n = 1 elif self.n == 1: logger.info('using default code') else: logger.info('using (1, %d, %d) code' % (self.n, k)) byte_to_embed |= k << 24 byte_to_embed ^= random.get_next_byte() byte_to_embed ^= random.get_next_byte() << 8 byte_to_embed ^= random.get_next_byte() << 16 byte_to_embed ^= random.get_next_byte() << 24 next_bit_to_embed = byte_to_embed & 1 byte_to_embed >>= 1 available_bits_to_embed = 31 _embedded += 1 for i, shuffled_index in enumerate(permutation.shuffled): if shuffled_index % 64 == 0 or coeff[shuffled_index] == 0: continue cc = coeff[shuffled_index] _examined += 1 if cc > 0 and (cc & 1) != next_bit_to_embed: coeff[shuffled_index] -= 1 _changed += 1 elif cc < 0 and (cc & 1) == next_bit_to_embed: coeff[shuffled_index] += 1 _changed += 1 if coeff[shuffled_index] != 0: if available_bits_to_embed == 0: if self.n > 1 or not self.embedded_data.available(): break byte_to_embed = self.embedded_data.read() byte_to_embed ^= random.get_next_byte() available_bits_to_embed = 8 next_bit_to_embed = byte_to_embed & 1 byte_to_embed >>= 1 available_bits_to_embed -= 1 _embedded += 1 else: _thrown += 1 if self.n > 1: try: is_last_byte = False filtered_index = FilteredCollection( permutation.shuffled[i + 1:], lambda index: index % 64 and coeff[index]) while not is_last_byte: k_bits_to_embed = 0 for i in range(k): if available_bits_to_embed == 0: if not self.embedded_data.available(): is_last_byte = True break byte_to_embed = self.embedded_data.read() byte_to_embed ^= random.get_next_byte() available_bits_to_embed = 8 next_bit_to_embed = byte_to_embed & 1 byte_to_embed >>= 1 available_bits_to_embed -= 1 k_bits_to_embed |= next_bit_to_embed << i _embedded += 1 code_word = filtered_index.offer(self.n) while True: vhash = 0 for i, index in enumerate(code_word): if coeff[index] > 0: extracted_bit = coeff[index] & 1 else: extracted_bit = 1 - (coeff[index] & 1) if extracted_bit == 1: vhash ^= i + 1 i = vhash ^ k_bits_to_embed if not i: break i -= 1 coeff[code_word[i]] += 1 if coeff[ code_word[i]] < 0 else -1 _changed += 1 if not coeff[code_word[i]]: _thrown += 1 code_word[i:i + 1] = [] code_word.extend(filtered_index.offer(1)) else: break except FilteredCollection.ListNotEnough: pass if _examined > 0: logger.info('%d coefficients examined' % _examined) if _changed > 0: logger.info( '%d coefficients changed (efficiency: %d.%d bits per change' % (_changed, _embedded / _changed, _embedded * 10 / _changed % 10)) logger.info('%d coefficients thrown (zeroed)' % _thrown) logger.info('%d bits (%d bytes) embedded' % (_embedded, _embedded / 8)) #导出嵌入后的系数coeff if self.hasais: filename2 = 'embeded_ais.json' else: filename2 = 'embeded.json' with open(filename2, 'w') as f_embeded: json.dump(coeff, f_embeded) logger.info('starting hufman encoding') shuffled_index = 0 for r in range(min_block_height): for c in range(min_block_width): for comp in range(self.jpeg_obj.comp_num): for i in range(self.jpeg_obj.vsamp_factor[comp]): for j in range(self.jpeg_obj.hsamp_factor[comp]): dct_array3 = coeff[shuffled_index:shuffled_index + 64] self.huf.huffman_block_encoder( self.out, dct_array3, last_dc_value[comp], self.jpeg_obj.dctable_number[comp], self.jpeg_obj.actable_number[comp]) last_dc_value[comp] = dct_array3[0] shuffled_index += 64 self.huf.flush_buffer(self.out) logger.info('hufman encode end')
class JpegEncoder(object): def __init__(self, image, quality, out, comment): self.quality = quality self.jpeg_obj = JpegInfo(image, comment) self.image_width, self.image_height = image.size self.out = out self.dct = DCT(self.quality) self.huf = Huffman(*image.size) def compress(self, embedded_data=None, password='******'): self.embedded_data = EmbedData(embedded_data) if embedded_data else None self.password = password self.write_headers() self.write_compressed_data() self.write_eoi() self.out.flush() def get_quality(self): return self.quality def set_quality(self, quality): self.quality = quality self.dct = DCT(quality) def write_array(self, data): length = ((data[2] & 0xff) << 8) + (data[3] & 0xff) + 2 self.out.write(bytearray(data[:length])) def write_marker(self, data): self.out.write(bytearray(data[:2])) def write_eoi(self): EOI = [0xff, 0xD9] self.write_marker(EOI) def write_headers(self): SOI = [0xff, 0xD8] self.write_marker(SOI) JFIF = [0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00] self.write_array(JFIF) comment = self.jpeg_obj.get_comment() if comment: length = len(comment) + 2 COM = [0xff, 0xfe, length >> 8 & 0xff, length & 0xff] COM.extend(comment) self.write_array(COM) DQT = [0xff, 0xdb, 0x00, 0x84] for k in range(2): DQT.append(k) DQT.extend([self.dct.quantum[k][JPEG_NATURAL_ORDER[i]] for i in range(64)]) self.write_array(DQT) SOF = [0xff, 0xc0, 0x00, 0x11, self.jpeg_obj.precision, self.jpeg_obj.image_height >> 8 & 0xff, self.jpeg_obj.image_height & 0xff, self.jpeg_obj.image_width >> 8 & 0xff, self.jpeg_obj.image_width & 0xff, self.jpeg_obj.comp_num] for i in range(self.jpeg_obj.comp_num): SOF.append(self.jpeg_obj.com_id[i]) SOF.append(eight_byte(self.jpeg_obj.hsamp_factor[i], self.jpeg_obj.vsamp_factor[i])) SOF.append(self.jpeg_obj.qtable_number[i]) self.write_array(SOF) DHT = [0xff, 0xc4, 0, 0] for i in range(4): DHT.extend(self.huf.BITS[i]) DHT.extend(self.huf.VAL[i]) DHT[2] = len(DHT) - 2 >> 8 & 0xff DHT[3] = len(DHT) - 2 & 0xff self.write_array(DHT) SOS = [0] * 14 SOS = [0xff, 0xda, 0x00, 0x0c, self.jpeg_obj.comp_num] for i in range(self.jpeg_obj.comp_num): SOS.append(self.jpeg_obj.com_id[i]) SOS.append(eight_byte(self.jpeg_obj.dctable_number[i], self.jpeg_obj.actable_number[i])) SOS.append(self.jpeg_obj.ss) SOS.append(self.jpeg_obj.se) SOS.append(eight_byte(self.jpeg_obj.ah, self.jpeg_obj.al)) self.write_array(SOS) def _get_coeff(self): dct_array1 = create_array(0.0, 8, 8) dct_array2 = create_array(0.0, 8, 8) dct_array3 = create_array(0, 64) coeff = [] for r in range(min(self.jpeg_obj.block_height)): for c in range(min(self.jpeg_obj.block_width)): xpos = c * 8 ypos = r * 8 for comp in range(self.jpeg_obj.comp_num): indata = self.jpeg_obj.components[comp] maxa = self.image_height / 2 * self.jpeg_obj.vsamp_factor[comp] - 1 maxb = self.image_width / 2 * self.jpeg_obj.hsamp_factor[comp] - 1 for i in range(self.jpeg_obj.vsamp_factor[comp]): for j in range(self.jpeg_obj.hsamp_factor[comp]): ia = ypos * self.jpeg_obj.vsamp_factor[comp] + i * 8 ib = xpos * self.jpeg_obj.hsamp_factor[comp] + j * 8 for a in range(8): for b in range(8): dct_array1[a][b] = indata[min(ia+a, maxa)][min(ib+b, maxb)] dct_array2 = self.dct.forward_dct(dct_array1) dct_array3 = self.dct.quantize_block(dct_array2, self.jpeg_obj.qtable_number[comp]) coeff.extend(dct_array3[:64]) return coeff def write_compressed_data(self): tmp = 0 last_dc_value = create_array(0, self.jpeg_obj.comp_num) zero_array = create_array(0, 64) width, height = 0, 0 min_block_width = min(self.jpeg_obj.block_width) min_block_height = min(self.jpeg_obj.block_height) logger.info('DCT/quantisation starts') logger.info('%d x %d' % (self.image_width, self.image_height)) coeff = self._get_coeff() coeff_count = len(coeff) logger.info('got %d DCT AC/DC coefficients' % coeff_count) _changed, _embedded, _examined, _expected, _one, _large, _thrown, _zero = 0, 0, 0, 0, 0, 0, 0, 0 shuffled_index = 0 for i, cc in enumerate(coeff): if i % 64 == 0: continue if cc == 1 or cc == -1: _one += 1 elif cc == 0: _zero += 1 _large = coeff_count - _zero - _one - coeff_count / 64 _expected = _large + int(0.49 * _one) logger.info('one=%d' % _one) logger.info('large=%d' % _large) logger.info('expected capacity: %d bits' % _expected) logger.info('expected capacity with') for i in range(1, 8): n = (1 << i) - 1 changed = _large - _large % (n + 1) changed = (changed + _one + _one / 2 - _one / (n + 1)) / (n + 1) usable = (_expected * i / n - _expected * i / n % n) / 8 if usable == 0: break logger.info('%s code: %d bytes (efficiency: %d.%d bits per change)' % ('default' if i == 1 else '(1, %d, %d)' % (n, i), usable, usable * 8 / changed, usable * 80 / changed % 10)) if self.embedded_data is not None: logger.info('permutation starts') random = F5Random(self.password) permutation = Permutation(coeff_count, random) next_bit_to_embed = 0 byte_to_embed = len(self.embedded_data) available_bits_to_embed = 0 logger.info('Embedding of %d bits (%d+4 bytes)' % (byte_to_embed * 8 + 32, byte_to_embed)) if byte_to_embed > 0x007fffff: byte_to_embed = 0x007ffff for i in range(1, 8): self.n = (1 << i) - 1 usable = (_expected * i / self.n - _expected * i / self.n % self.n) / 8 if usable < byte_to_embed + 4: break k = i - 1 self.n = (1 << k) - 1 if self.n == 0: logger.info('using default code, file will not fit') self.n = 1 elif self.n == 1: logger.info('using default code') else: logger.info('using (1, %d, %d) code' % (self.n, k)) byte_to_embed |= k << 24 byte_to_embed ^= random.get_next_byte() byte_to_embed ^= random.get_next_byte() << 8 byte_to_embed ^= random.get_next_byte() << 16 byte_to_embed ^= random.get_next_byte() << 24 next_bit_to_embed = byte_to_embed & 1 byte_to_embed >>= 1 available_bits_to_embed = 31 _embedded += 1 for i, shuffled_index in enumerate(permutation.shuffled): if shuffled_index % 64 == 0 or coeff[shuffled_index] == 0: continue cc = coeff[shuffled_index] _examined += 1 if cc > 0 and (cc & 1) != next_bit_to_embed: coeff[shuffled_index] -= 1 _changed +=1 elif cc < 0 and (cc & 1) == next_bit_to_embed: coeff[shuffled_index] += 1 _changed += 1 if coeff[shuffled_index] != 0: if available_bits_to_embed == 0: if self.n > 1 or not self.embedded_data.available(): break byte_to_embed = self.embedded_data.read() byte_to_embed ^= random.get_next_byte() available_bits_to_embed = 8 next_bit_to_embed = byte_to_embed & 1 byte_to_embed >>= 1 available_bits_to_embed -= 1 _embedded += 1 else: _thrown += 1 if self.n > 1: try: is_last_byte = False filtered_index = FilteredCollection(permutation.shuffled[i+1:], lambda index: index % 64 and coeff[index]) while not is_last_byte: k_bits_to_embed = 0 for i in range(k): if available_bits_to_embed == 0: if not self.embedded_data.available(): is_last_byte = True break byte_to_embed = self.embedded_data.read() byte_to_embed ^= random.get_next_byte() available_bits_to_embed = 8 next_bit_to_embed = byte_to_embed & 1 byte_to_embed >>= 1 available_bits_to_embed -= 1 k_bits_to_embed |= next_bit_to_embed << i _embedded += 1 code_word = filtered_index.offer(self.n) while True: vhash = 0 for i, index in enumerate(code_word): if coeff[index] > 0: extracted_bit = coeff[index] & 1 else: extracted_bit = 1 - (coeff[index] & 1) if extracted_bit == 1: vhash ^= i + 1 i = vhash ^ k_bits_to_embed if not i: break i -= 1 coeff[code_word[i]] += 1 if coeff[code_word[i]] < 0 else -1 _changed += 1 if not coeff[code_word[i]]: _thrown += 1 code_word[i:i+1] = [] code_word.extend(filtered_index.offer(1)) else: break except FilteredCollection.ListNotEnough: pass if _examined > 0: logger.info('%d coefficients examined' % _examined) if _changed > 0: logger.info('%d coefficients changed (efficiency: %d.%d bits per change' % (_changed, _embedded / _changed, _embedded * 10 / _changed % 10)) logger.info('%d coefficients thrown (zeroed)' % _thrown) logger.info('%d bits (%d bytes) embedded' % (_embedded, _embedded / 8)) logger.info('starting hufman encoding') shuffled_index = 0 for r in range(min_block_height): for c in range(min_block_width): for comp in range(self.jpeg_obj.comp_num): for i in range(self.jpeg_obj.vsamp_factor[comp]): for j in range(self.jpeg_obj.hsamp_factor[comp]): dct_array3 = coeff[shuffled_index:shuffled_index+64] self.huf.huffman_block_encoder(self.out, dct_array3, last_dc_value[comp], self.jpeg_obj.dctable_number[comp], self.jpeg_obj.actable_number[comp]) last_dc_value[comp] = dct_array3[0] shuffled_index += 64 self.huf.flush_buffer(self.out)
class HuffmanDecode(object): APP = [ 0xe0, 0xe1, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef ] DRI, DNL, EOI = 0xdd, 0xdc, 0xd9 deZZ = [[0, 0], [0, 1], [1, 0], [2, 0], [1, 1], [0, 2], [0, 3], [1, 2], [2, 1], [3, 0], [4, 0], [3, 1], [2, 2], [1, 3], [0, 4], [0, 5], [1, 4], [2, 3], [3, 2], [4, 1], [5, 0], [6, 0], [5, 1], [4, 2], [3, 3], [2, 4], [1, 5], [0, 6], [0, 7], [1, 6], [2, 5], [3, 4], [4, 3], [5, 2], [6, 1], [7, 0], [7, 1], [6, 2], [5, 3], [4, 4], [3, 5], [2, 6], [1, 7], [2, 7], [3, 6], [4, 5], [5, 4], [6, 3], [7, 2], [7, 3], [6, 4], [5, 5], [4, 6], [3, 7], [4, 7], [5, 6], [6, 5], [7, 4], [7, 5], [6, 6], [5, 7], [6, 7], [7, 6], [7, 7]] de_zig_zag = [ 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 ] def __init__(self, data): self.huffval = create_array([], 4) self.valptr = create_array([], 4) self.mincode = create_array([], 4) self.maxcode = create_array([], 4) self.zz = create_array(0, 64) self.qnt = create_array(0, 4, 64) self.data = EmbedData(data) self.size = len(self.data) self.ri = 0 while True: if self.get_byte() == 255: b = self.get_byte() if b == 192: self.sof0() elif b == 196: self.dht() elif b == 219: self.dqt() elif b == 217 or b == 218: break elif b in self.APP: self.skip_variable() elif b == self.DRI: self.dri() def available(self): return self.data.available() def get_double_four_bits(self): b = self.get_byte() return b >> 4, b & 0x0f def get_byte(self): return self.data.read() def get_int(self): return (self.get_byte() << 8) ^ self.get_byte() def get_next_bit(self): if not self.CNT: self.CNT = 8 self.B = self.get_byte() if self.B == 255: self.get_byte() BIT = self.B & 0x80 BIT >>= 7 self.CNT -= 1 self.B <<= 1 return BIT def get_block_count(self): square = lambda x: x * x if self.nf == 1: return square((self.x + 7) / 8) elif self.nf == 3: return 6 * square((self.x + 15) / 16) else: logger.error('nf is not 1 neither 3') def _internal_decode(self): i, cd = 1, self.get_next_bit() while cd > self.maxcode[self.hftbl][i]: cd = (cd << 1) + self.get_next_bit() i += 1 j = self.valptr[self.hftbl][i] j += cd - self.mincode[self.hftbl][i] return self.huffval[self.hftbl][j] def receive(self, sss): v, i = 0, 0 while i != sss: i += 1 v = (v << 1) + self.get_next_bit() return v def extend(self, v, t): if t == 0: return v vt = 0x01 << t - 1 if v < vt: vt = (-1 << t) + 1 v += vt return v def decode_ac_coefficients(self): k = 1 self.zz = [0] * 64 while True: rs = self._internal_decode() ssss = rs % 16 r = rs >> 4 if ssss == 0: if r == 15: k += 16 else: return else: k += r self.zz[k] = self.extend(self.receive(ssss), ssss) if k == 63: return else: k += 1 def decode(self): pred = create_array(0, self.nf) self.CNT = 0 self.ls = self.get_int() self.ns = self.get_byte() cs = create_array(0, self.ns) td = create_array(0, self.ns) ta = create_array(0, self.ns) for lp in range(self.ns): cs[lp] = self.get_byte() td[lp], ta[lp] = self.get_double_four_bits() self.ss = self.get_byte() self.se = self.get_byte() self.ah, self.al = self.get_double_four_bits() buff = create_array(0, 2 * 8 * 8 * self.get_block_count()) pos, mcu_count = 0, 0 while True: for n_comp in range(0, self.nf): for cnt in range(self.h[n_comp] * self.v[n_comp]): self.hftbl = td[n_comp] * 2 tmp = self._internal_decode() self.diff = self.receive(tmp) self.zz[0] = pred[0] + self.extend(self.diff, tmp) pred[n_comp] = self.zz[0] self.hftbl = ta[n_comp] * 2 + 1 self.decode_ac_coefficients() for lp in range(64): buff[pos] = self.zz[lp] pos += 1 mcu_count += 1 if mcu_count == self.ri: mcu_count = 0 self.CNT = 0 pred[n_comp] = create_array(0, self.nf) self.get_byte() tmp_b = self.get_byte() if tmp_b == EOI: break if self.available() <= 2: if self.available() == 2: self.get_byte() if self.get_byte() != self.EOI: logger.error('file does not end with EOI') else: if self.available() == 1: logger.error('last byte: %X' % self.get_byte()) logger.error('file does not end with EOI') break return buff[:pos] def sof0(self): self.lf = self.get_int() self.p = self.get_byte() self.y = self.get_int() self.x = self.get_int() self.nf = self.get_byte() self.c = create_array(0, self.nf) self.h = create_array(0, self.nf) self.v = create_array(0, self.nf) self.t = create_array(0, self.nf) for lp in range(self.nf): self.c[lp] = self.get_byte() self.h[lp], self.v[lp] = self.get_double_four_bits() self.t[lp] = self.get_byte() def dht(self): self.lh = self.get_int() def _fill_value(index): ht = HuffTable(self.data, self.lh) self.lh -= ht.get_len() self.huffval[index] = ht.get_huffval() self.valptr[index] = ht.get_valptr() self.maxcode[index] = ht.get_maxcode() self.mincode[index] = ht.get_mincode() while self.lh > 0: self.tc, self.th = self.get_double_four_bits() if self.th == 0: if self.tc == 0: _fill_value(0) else: _fill_value(1) else: if self.tc == 0: _fill_value(2) else: _fill_value(3) def dqt(self): self.lq = self.get_int() self.pq, self.tq = self.get_double_four_bits() if self.tq in range(4): for lp in range(64): self.qnt[self.tq][lp] = self.get_byte() def dri(self): self.get_int() self.ri = self.get_int() def skip_variable(self): for i in range(self.get_int() - 2): self.get_byte()
class HuffmanDecode(object): APP = [0xE0, 0xE1, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF] DRI, DNL, EOI = 0xDD, 0xDC, 0xD9 deZZ = [ [0, 0], [0, 1], [1, 0], [2, 0], [1, 1], [0, 2], [0, 3], [1, 2], [2, 1], [3, 0], [4, 0], [3, 1], [2, 2], [1, 3], [0, 4], [0, 5], [1, 4], [2, 3], [3, 2], [4, 1], [5, 0], [6, 0], [5, 1], [4, 2], [3, 3], [2, 4], [1, 5], [0, 6], [0, 7], [1, 6], [2, 5], [3, 4], [4, 3], [5, 2], [6, 1], [7, 0], [7, 1], [6, 2], [5, 3], [4, 4], [3, 5], [2, 6], [1, 7], [2, 7], [3, 6], [4, 5], [5, 4], [6, 3], [7, 2], [7, 3], [6, 4], [5, 5], [4, 6], [3, 7], [4, 7], [5, 6], [6, 5], [7, 4], [7, 5], [6, 6], [5, 7], [6, 7], [7, 6], [7, 7], ] de_zig_zag = [ 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63, ] def __init__(self, data): self.huffval = create_array([], 4) self.valptr = create_array([], 4) self.mincode = create_array([], 4) self.maxcode = create_array([], 4) self.zz = create_array(0, 64) self.qnt = create_array(0, 4, 64) self.data = EmbedData(data) self.size = len(self.data) self.ri = 0 while True: if self.get_byte() == 255: b = self.get_byte() if b == 192: self.sof0() elif b == 196: self.dht() elif b == 219: self.dqt() elif b == 217 or b == 218: break elif b in self.APP: self.skip_variable() elif b == self.DRI: self.dri() def available(self): return self.data.available() def get_double_four_bits(self): b = self.get_byte() return b >> 4, b & 0x0F def get_byte(self): return self.data.read() def get_int(self): return (self.get_byte() << 8) ^ self.get_byte() def get_next_bit(self): if not self.CNT: self.CNT = 8 self.B = self.get_byte() if self.B == 255: self.get_byte() BIT = self.B & 0x80 BIT >>= 7 self.CNT -= 1 self.B <<= 1 return BIT def get_block_count(self): square = lambda x: x * x if self.nf == 1: return square((self.x + 7) / 8) elif self.nf == 3: return 6 * square((self.x + 15) / 16) else: logger.error("nf is not 1 neither 3") def _internal_decode(self): i, cd = 1, self.get_next_bit() while cd > self.maxcode[self.hftbl][i]: cd = (cd << 1) + self.get_next_bit() i += 1 j = self.valptr[self.hftbl][i] j += cd - self.mincode[self.hftbl][i] return self.huffval[self.hftbl][j] def receive(self, sss): v, i = 0, 0 while i != sss: i += 1 v = (v << 1) + self.get_next_bit() return v def extend(self, v, t): if t == 0: return v vt = 0x01 << t - 1 if v < vt: vt = (-1 << t) + 1 v += vt return v def decode_ac_coefficients(self): k = 1 self.zz = [0] * 64 while True: rs = self._internal_decode() ssss = rs % 16 r = rs >> 4 if ssss == 0: if r == 15: k += 16 else: return else: k += r self.zz[k] = self.extend(self.receive(ssss), ssss) if k == 63: return else: k += 1 def decode(self): pred = create_array(0, self.nf) self.CNT = 0 self.ls = self.get_int() self.ns = self.get_byte() cs = create_array(0, self.ns) td = create_array(0, self.ns) ta = create_array(0, self.ns) for lp in range(self.ns): cs[lp] = self.get_byte() td[lp], ta[lp] = self.get_double_four_bits() self.ss = self.get_byte() self.se = self.get_byte() self.ah, self.al = self.get_double_four_bits() buff = create_array(0, 2 * 8 * 8 * self.get_block_count()) pos, mcu_count = 0, 0 while True: for n_comp in range(0, self.nf): for cnt in range(self.h[n_comp] * self.v[n_comp]): self.hftbl = td[n_comp] * 2 tmp = self._internal_decode() self.diff = self.receive(tmp) self.zz[0] = pred[0] + self.extend(self.diff, tmp) pred[n_comp] = self.zz[0] self.hftbl = ta[n_comp] * 2 + 1 self.decode_ac_coefficients() for lp in range(64): buff[pos] = self.zz[lp] pos += 1 mcu_count += 1 if mcu_count == self.ri: mcu_count = 0 self.CNT = 0 pred[n_comp] = create_array(0, self.nf) self.get_byte() tmp_b = self.get_byte() if tmp_b == EOI: break if self.available() <= 2: if self.available() == 2: self.get_byte() if self.get_byte() != self.EOI: logger.error("file does not end with EOI") else: if self.available() == 1: logger.error("last byte: %X" % self.get_byte()) logger.error("file does not end with EOI") break return buff[:pos] def sof0(self): self.lf = self.get_int() self.p = self.get_byte() self.y = self.get_int() self.x = self.get_int() self.nf = self.get_byte() self.c = create_array(0, self.nf) self.h = create_array(0, self.nf) self.v = create_array(0, self.nf) self.t = create_array(0, self.nf) for lp in range(self.nf): self.c[lp] = self.get_byte() self.h[lp], self.v[lp] = self.get_double_four_bits() self.t[lp] = self.get_byte() def dht(self): self.lh = self.get_int() def _fill_value(index): ht = HuffTable(self.data, self.lh) self.lh -= ht.get_len() self.huffval[index] = ht.get_huffval() self.valptr[index] = ht.get_valptr() self.maxcode[index] = ht.get_maxcode() self.mincode[index] = ht.get_mincode() while self.lh > 0: self.tc, self.th = self.get_double_four_bits() if self.th == 0: if self.tc == 0: _fill_value(0) else: _fill_value(1) else: if self.tc == 0: _fill_value(2) else: _fill_value(3) def dqt(self): self.lq = self.get_int() self.pq, self.tq = self.get_double_four_bits() if self.tq in range(4): for lp in range(64): self.qnt[self.tq][lp] = self.get_byte() def dri(self): self.get_int() self.ri = self.get_int() def skip_variable(self): for i in range(self.get_int() - 2): self.get_byte()