Пример #1
0
    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()
Пример #2
0
    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 __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 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()
Пример #5
0
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')
Пример #6
0
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 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()