jpeg2bmp.py

# coding:utf-8
# jpeg2bmp.py 2012.10.28
import logging
import math
import BaseJpegDecode
import bmp24

zigzag = [ 0,
           1, 8,
          16, 9, 2,
           3,10,17,24,
          32,25,18,11, 4,
          5,12,19,26,33,40,
          48,41,34,27,20,13,6,
           7,14,21,28,35,42,49,56,
          57,50,43,36,29,22,15,
          23,30,37,44,51,58,
          59,52,45,38,31,
          39,46,53,60,
          61,54,47,
          55,62,
          63]

def adjust(value):
    value += 128
    if value < 0:
        return 0
    elif value > 255:
        return 255
    return int(value)

class jpeg2bmp(BaseJpegDecode.BaseJpegDecode):
    def __init__(self):
        super(jpeg2bmp, self).__init__()
        self._values = [0]*64
        self.bmp = bmp24.bmp24()

    def bmp_output_string(self):
        self.bmp.width = self.width
        self.bmp.height = self.height
        return self.bmp.output_string()

    def outputDot(self, mcu, block, x, y, value):
        rgb = (value, value, value)
        mcu_x = mcu % (self.width/16)
        mcu_y = mcu / (self.width/16)
        if self._yblock == 2:
            if block == 0:
                self.bmp.point(mcu_x * 16 + x, mcu_y * 8 + y, rgb)
            elif block == 1:
                self.bmp.point(mcu_x * 16 + 8 + x, mcu_y * 8 + y, rgb)
        elif self._yblock == 4:
            if block <= 3:
                ox, oy = [(0,0),(8,0),(0,8),(8,8)][block]
                self.bmp.point(mcu_x * 16 + ox + x, mcu_y * 16 + oy + y, rgb)
        else:
            raise ValueError('yblock error')

    def _rDCT(self, mcu, block, s):
        N=8
        for y in range(N):
            for x in range(N):
                sum = 0.0
                for v in range(N):
                    cv = 1.0
                    if v == 0:
                        cv /= math.sqrt(2)
                    for u in range(N):
                        cu = 1.0
                        if u == 0:
                            cu /= math.sqrt(2)
                        vu = v*8+u
                        cosxu = math.cos((x*2+1)*u*math.pi/(N*2))
                        cosyv = math.cos((y*2+1)*v*math.pi/(N*2))
                        sum += cu * cv * s[vu] * cosxu * cosyv
                sum /= (N/2)
                self.outputDot(mcu, block, x, y, adjust(sum))

    def _output(self, mcu, block, scan, value):
        if self._yblock == 2:
            sc = [0,0,1,1][block]
        else:
            sc = [0,0,0,0,1,1][block]
        self._values[zigzag[scan]] = value * self.qt[sc][scan] # 逆量子化
        if scan == 63: # last
            self._rDCT(mcu, block, self._values)

    def outputDC(self, mcu, block, value):
        self._values = [0]*64
        self._output(mcu, block, 0, value)
        if block == 0:
            print("%02X" % adjust(value * self.qt[0][0] / 8)),
            if (mcu % (self.width/16)) == (self.width/16)-1:
                print

    def outputAC(self, mcu, block, scan, value):
        self._output(mcu, block, scan, value)

    def outputMARK(self, c):
        pass

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)

    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('infiles', nargs='*')
    args = parser.parse_args()

    decode = jpeg2bmp()

    for i, filename in enumerate(args.infiles):
        with open(filename, "rb") as f:
            data = f.read()
        print("%s %d" % (filename, len(data)))
        decode.clear()
        for c in data:
            decode.input(ord(c))
        image_data = decode.bmp_output_string()
        output_filename = "output%d.bmp" % i
        with open(output_filename, "wb") as f:
            f.write(image_data)
        print output_filename