# GPixPod - the free and open source way to manage photos on your POD!
# Copyright (C) 2006 Flavio Gargiulo (FLAGAR.com)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
# Image formats conversions
import struct, gtk
 
def rotateCCW(pixels, width, height, pixelbytes=2):
    """ Rotate counter-clockwise a pixels data stream with given original width and height """
    pixels_len = len(pixels)
    if pixels_len != width*pixelbytes*height:
        raise "Width and/or height seem not correct for the pixels data stream received"
    newpixels = ''
    row = width*pixelbytes
    for wpos in range(width*pixelbytes, 0, -pixelbytes):
        for hpos in range(0, pixels_len, row):
            newpixels += pixels[hpos+wpos-pixelbytes:hpos+wpos]
    return newpixels
 
def rotateCW(pixels, width, height, pixelbytes=2):
    """ Rotate clockwise a pixels data stream with given original width and height """
#    pixels_len = len(pixels)
#    if pixels_len != width*pixelbytes*height:
#        raise "Width and/or height seem not correct for the pixels data stream received"
#    newpixels = ''
#    row = width*pixelbytes
#    for wpos in range(0, row+1, pixelbytes):
#        for hpos in range(width*pixelbytes*(height-1), -1, -row):
#            newpixels += pixels[hpos+wpos:hpos+wpos+pixelbytes]
#    return newpixels
    result = rotateCCW(pixels, width, height, pixelbytes)
    result = rotateCCW(result, height, width, pixelbytes)
    result = rotateCCW(result, width, height, pixelbytes)
    return result
 
def getPixbuf(filename, width, height):
    """ Return a gdk-pixbuf without alpha channel and scaled to the specified dimensions keeping ratio """
    # Opening and scaling keeping ratio
    pixbuf = gtk.gdk.pixbuf_new_from_file(filename)
    height_with_ratio = pixbuf.get_height()*width/pixbuf.get_width()
    if height > height_with_ratio:
        pixbuf = pixbuf.scale_simple(width, height_with_ratio, gtk.gdk.INTERP_BILINEAR)
        bgpixbuf = pixbuf.composite_color_simple(width, height, gtk.gdk.INTERP_BILINEAR, 0, 2, 0, 0)
        pixbuf.copy_area(0, 0, width, height_with_ratio, bgpixbuf, 0, (height-height_with_ratio)/2)
        pixbuf = bgpixbuf
    elif height < height_with_ratio:
        width_with_ratio = pixbuf.get_width()*height/pixbuf.get_height()
        pixbuf = pixbuf.scale_simple(width_with_ratio, height, gtk.gdk.INTERP_BILINEAR)
        bgpixbuf = pixbuf.composite_color_simple(width, height, gtk.gdk.INTERP_BILINEAR, 0, 2, 0, 0)
        pixbuf.copy_area(0, 0, width_with_ratio, height, bgpixbuf, (width-width_with_ratio)/2, 0)
        pixbuf = bgpixbuf
    else:
        pixbuf = pixbuf.scale_simple(width, height, gtk.gdk.INTERP_BILINEAR)
    rgbdata = pixbuf.get_pixels()
    # Managing alpha channel
    if pixbuf.get_has_alpha():
        rgbdata_noalpha = ''
        for apos in range(0, len(rgbdata), 4):
            rgbdata_noalpha += rgbdata[apos:apos+3]
        rgbdata = rgbdata_noalpha
        pixbuf = gtk.gdk.pixbuf_new_from_data(rgbdata, gtk.gdk.COLORSPACE_RGB, False, 8, width, height, width*3)
    return pixbuf
 
def toRGB565(filename, width, height, swap_bytes=True, rotate=False):
    """ Resize an image and convert it to RGB565 swapped bytes format """
    thumbsizes = [(50, 41), (320, 240), (130, 88)]  # 4100, 153600, 22880 dim  # iPod 5G
    thumbsizes.extend([(220, 176), (42, 30)])  # 77440, 2520 dim  # iPod Photo, Color
    thumbsizes.extend([(176, 132), (42, 37)])  # 46464, 3108 dim  # iPod Nano
    if (width, height) not in thumbsizes:
        print "Size not recognized"
    pixbuf = getPixbuf(filename, width, height)
    rgbdata = pixbuf.get_pixels()
    if rotate:
        rgbdata_rotated = rotateCCW(rgbdata, width, height, 3)
        pixbuf = gtk.gdk.pixbuf_new_from_data(rgbdata_rotated, gtk.gdk.COLORSPACE_RGB, False, 8, height, width, height*3)
        rgbdata = pixbuf.get_pixels()
        width = pixbuf.get_width()
        height = pixbuf.get_height()
    rowstride = pixbuf.get_rowstride()
    # With GDK-PIXBUF occasionally happens that rowstride != width*3.
    # It always happens when scaling either to 50x41 or to 130x88.
    # I have not figured out why yet.
    # This is required! Thus, we delete the exceeding bytes.
    if rowstride > (width*3):
        newrgbdata = ''
        right_row = width*3
        diff_row = rowstride - right_row
        for pos in range(0, len(rgbdata), rowstride):
            newrgbdata += rgbdata[pos:pos+right_row]
        rgbdata = newrgbdata
    rgb565data = ''
    # Actually converting to RGB565 swapped bytes
    for pos in range(0, len(rgbdata), 3):
        r = struct.unpack('B', rgbdata[pos:pos+1])[0]
        g = struct.unpack('B', rgbdata[pos+1:pos+2])[0]
        b = struct.unpack('B', rgbdata[pos+2:pos+3])[0]
        r = r >> 3
        g = g >> 2
        b = b >> 3
        g2 = g & 7
        byte1 = (r << 3) + (g >> 3)  # RRRRRGGG
        byte2 = (g2 << 5) + b # GGGBBBBB
        if swap_bytes:
            rgb565data += struct.pack('B', byte2)
            rgb565data += struct.pack('B', byte1)
        else:
            rgb565data += struct.pack('B', byte1)
            rgb565data += struct.pack('B', byte2)
    return rgb565data
 
def fromRGB565(filename, width, height, swap_bytes=True, rotate=False):
    """ Convert an image from RGB565 swapped bytes format to PNG """
    thumbsizes = {4100:(50, 41), 153600:(320, 240), 22880:(130, 88)}
    # Adding iPod Photo, Color thumb dimensions
    thumbsizes[77440] = (220, 176)
    thumbsizes[2520] = (42, 30)
    # Adding iPod Nano thumb dimensions
    thumbsizes[46464] = (176, 132)
    thumbsizes[3108] = (42, 37)
    # Begin processing
    origthumb = open(filename, 'rb')
    origthumb.seek(0, 2)
    origthumbsize = origthumb.tell()
    origthumb.seek(0)
    rgbdata = ''
    try:
        width = thumbsizes[origthumbsize][0]
        height = thumbsizes[origthumbsize][1]
    except KeyError:
        print "Not recognized input format"
    for pos in range(0, origthumbsize, 2):
        if swap_bytes:
            byte2 = struct.unpack('B', origthumb.read(1))[0]
            byte1 = struct.unpack('B', origthumb.read(1))[0]
        else:
            byte1 = struct.unpack('B', origthumb.read(1))[0]
            byte2 = struct.unpack('B', origthumb.read(1))[0]
        r = byte1 >> 3
        b = byte2 & 31
        g1 = byte1 & 7
        g2 = byte2 >> 5
        g1 = g1 << 3
        g = g1 + g2
        r = r << 3
        g = g << 2
        b = b << 3
        rgbdata += struct.pack('B', r)
        rgbdata += struct.pack('B', g)
        rgbdata += struct.pack('B', b)
    origthumb.close()
    if rotate:
        pixbuf = gtk.gdk.pixbuf_new_from_data(rotateCW(rgbdata, width, height, 3), gtk.gdk.COLORSPACE_RGB, False, 8, height, width, (height*3))
    else:
        pixbuf = gtk.gdk.pixbuf_new_from_data(rgbdata, gtk.gdk.COLORSPACE_RGB, False, 8, width, height, (width*3))
    pixbuf.save('%s.png' % filename, 'png')
 
def unsign(n):
    """ Round a number to unsigned integer """
    # I don't know if there is an equivalent built-in one in Python!
    return abs(int(n))
 
def limit(n):
    """ Keep a number within the range 0-255 """
    if n < 0:
        return 0
    elif n > 255:
        return 255
    else:
        return n
 
def toInterlacedUYVY(filename):
    """ Resize an image to 720x480 and convert it to interlaced UYVY (YUV 4:2:2) format """
    # Converting from PNG, JPG to RGB
    #pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filename, 720, 480)
    #pixbuf = pixbuf.composite_color_simple(720, 480, gtk.gdk.INTERP_TILES, 255, 2160, 0, 0)
    pixbuf = getPixbuf(filename, 720, 480)
    rgbdata = pixbuf.get_pixels()
    # Converting from RGB to RGB interlaced
    irgbdata1 = ''
    irgbdata2 = ''
    pos = 0
    rowstride = 720*3
    halfsize = 691200/2
    while pos < len(rgbdata):
        irgbdata1 += rgbdata[pos:pos+rowstride]
        pos += rowstride
        irgbdata2 += rgbdata[pos:pos+rowstride]
        pos += rowstride
    irgbdata = irgbdata1 + irgbdata2
    # Converting from RGB interlaced to YUV 4:2:2 (UYVY)
    yuvdata = ''
    for pos in range(0, len(irgbdata), 6):
        R0 = struct.unpack('B', irgbdata[pos:pos+1])[0]
        G0 = struct.unpack('B', irgbdata[pos+1:pos+2])[0]
        B0 = struct.unpack('B', irgbdata[pos+2:pos+3])[0]
        R1 = struct.unpack('B', irgbdata[pos+3:pos+4])[0]
        G1 = struct.unpack('B', irgbdata[pos+4:pos+5])[0]
        B1 = struct.unpack('B', irgbdata[pos+5:pos+6])[0]
        U0 = ((R0*-38 - G0*74 + B0*112 + 128) >> 8) + 128
        Y0 = ((R0*66 + G0*129 + B0*25 + 128) >> 8) + 16
        V0 = ((R0*112 - G0*94 - B0*18 + 128) >> 8) + 128
        Y1 = ((R1*66 + G1*129 + B1*25 + 128) >> 8) + 16
        yuvdata += struct.pack('B', U0)
        yuvdata += struct.pack('B', Y0)
        yuvdata += struct.pack('B', V0)
        yuvdata += struct.pack('B', Y1)
    return yuvdata
 
def fromInterlacedUYVY(filename):
    """ Convert an image from interlaced UYVY (YUV 4:2:2) format to PNG """
    origthumb = open(filename, 'rb')
    yuvdata = origthumb.read()
    origthumb.close()
    # From YUV to RGB...
    rgbdata = ''
    for pos in range(0, 691200, 4):
        U0 = U1 = struct.unpack('B', yuvdata[pos:pos+1])[0]
        Y0 = struct.unpack('B', yuvdata[pos+1:pos+2])[0]
        V0 = V1 = struct.unpack('B', yuvdata[pos+2:pos+3])[0]
        Y1 = struct.unpack('B', yuvdata[pos+3:pos+4])[0]
        # Maybe formulas should be converted to integers, to gain speed
        R0 = (Y0 - 16)*1.164 + (V0 - 128)*1.596
        G0 = (Y0 - 16)*1.164 - (V0 - 128)*0.813 - (U0 - 128)*0.391
        B0 = (Y0 - 16)*1.164 + (U0 - 128)*2.018
        R1 = (Y1 - 16)*1.164 + (V0 - 128)*1.596
        G1 = (Y1 - 16)*1.164 - (V0 - 128)*0.813 - (U0 - 128)*0.391
        B1 = (Y1 - 16)*1.164 + (U0 - 128)*2.018
        rgbdata += struct.pack('B', limit(R0))
        rgbdata += struct.pack('B', limit(G0))
        rgbdata += struct.pack('B', limit(B0))
        rgbdata += struct.pack('B', limit(R1))
        rgbdata += struct.pack('B', limit(G1))
        rgbdata += struct.pack('B', limit(B1))
    # Deinterlacing...
    newrgbdata = ''
    halfsize = len(rgbdata)/2
    rowstride = 720*3
    for pos in range(0, halfsize, rowstride):
        newrgbdata += rgbdata[pos:pos+rowstride]
        newrgbdata += rgbdata[pos+halfsize:pos+halfsize+rowstride]
    # Converting RGB to PNG
    pixbuf = gtk.gdk.pixbuf_new_from_data(newrgbdata, gtk.gdk.COLORSPACE_RGB, False, 8, 720, 480, (720*3))
    pixbuf.save('%s.png' % filename, 'png')
 
code/imgconvs-py.txt · Last modified: 2010/03/09 02:49 by 164.67.235.148
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki