[casetta] Implemented the Image-to-Casio-Basic Function

[ Thread Index | Date Index | More lists.tuxfamily.org/casetta Archives ]


Hello casetta developers,

I requested a new function ofr casetta in the wishlist and i had the pleasure to see that it was added to the roadmap. I began to implement this function (See pictures.py in the attachment). I put this function in the pictures.py but I don't know if it is the good location.
This function takes a normalized picture and returns a Casio Basic Program which draws the picture.
This function only use the F-line instruction (and only Horizontal F-lines) and Plot-On instructions. Therefore, it is not very optimised to produce Basic Programs which consume few memory.

I have also reported a bug #2 about casetta : here is a video to explain what I get. The script I used to test this function is :
#-*- coding: utf-8 -*-

import casetta.pictures as pic
import Image
from os import chdir
path='/home/fabien_kubuntu/tux_casetta.png'

im=Image.open(path)
im_table=list(im.getdata())
print len(imn)
im_basic=pic.img_to_basic(im_table)
print im_basic

programme_final="%Header Record\nFormat:TXT\nData Type:PG\nFile Name:TestF\nPassword:\n%Data Record\n"+"\Cls\n"+im_basic
chdir('/home/fabien_kubuntu/')
file=open('TestF.newcat', 'w')
file.write(programme_final)
file.close()
--
Fabien ANDRE aka Xion345
Microsoft is not the answer. Microsoft is the question. NO (or Linux) is the answer. ( Unknown, Not dated )


# -*- coding: utf-8 -*-
#
############################################################################
#
# (c) 2006 Florian Birée aka Thesa <florian.biree@xxxxxxxxxxx>
# (c) 2006 Achraf Cherti aka Asher256 <achrafcherti@xxxxxxxxx>
#
# Website: <URL:http://casetta.tuxfamily.org>
#
# Version 0.3.0
#
# changelog:
#
# # 0.3.0 version:
#
#   o First release
#
############################################################################
#
# 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.
#
# http://www.gnu.org/copyleft/gpl.html
#
############################################################################
"""Pictures manager for casetta"""

__author__ = "Florian Birée, Achraf Cherti"
__version__ = "0.3.0"
__copyright__ = "Copyright (c) 2006, Florian Birée, Achraf Cherti"
__license__ = "GPL"
__revision__ = "$Revision: $"
# $Source: $
__date__ = "$Date: $"

import Image
import os
import data as datacls

# Constants:
ORANGE = (255, 128, 0)
GREEN = (0, 128, 0)
BLUE = (0, 0, 128)
WHITE = (255, 255, 255)
CASIO_SIZE = (128, 64)
COLORS = (ORANGE, GREEN, BLUE, WHITE)
_COLOR_DIC = {'o' : ORANGE,
              'g' : GREEN,
              'b' : BLUE,
              'w' : WHITE}
DEFAULT_PALLET = ['b', 'g', 'w', 'o']

# Conversion functions
def normalize(image, colors=COLORS):
    """Convert an image object in a casio-like format.
    
    This will:
    resize image to 128x64
    convert color to 4-color mode
    return a data list.
    pal is a 4-color tuple.
    """
    image.thumbnail(CASIO_SIZE)
    data = list(image.getdata())
    out = []
    # color conversion
    for pixel in data:
        red, green, blue = pixel[0], pixel[1], pixel[2]
        if red > green and red > blue:
            pixel = colors[0] # orange
        elif green > red and green > blue:
            pixel = colors[1] # green
        elif blue > green and blue > red:
            pixel = colors[2] # blue
        elif red == green and red > blue:
            pixel = colors[1] # green
        elif green == blue and green > red:
            pixel = colors[2] # blue
        elif red == blue and red > green:
            pixel = colors[2] # blue
        elif (red, green, blue) == (255, 255, 255):
            pixel = colors[3] # white
        elif (red, green, blue) == (0, 0, 0):
            pixel = colors[2] # blue
        out.append(pixel)
    return out

def data_to_sheets(data, wanted_pallet=DEFAULT_PALLET):
    """Make raw picture sheets from *normalized* data
    
    wanted_pallet is a list of color id as 'b', 'g', 'w' or 'o'
    Return a list of data sheets, sorted as wanted_pallet, with the
    color byte at 0."""
    sheets = []
    for color_id in wanted_pallet :
        color = _COLOR_DIC[color_id]
        rev_sheet = '0' * (128 * 64)
        for index in range(len(data)):
            if data[index] == color:
                line = index / 128
                col = (index % 128) / 8
                car = (index % 128) % 8
                pos = car + line * 8 + col * (8 * 64)
                rev_sheet = rev_sheet[:pos] + '1' + rev_sheet[pos + 1:]
        # reverse and compact the rev_sheet
        sheet = ''
        for index in range(0, len(data), 8):
            car = chr(int(rev_sheet[index : index + 8], 2))
            sheet = car + sheet
        sheets.append(chr(wanted_pallet.index(color_id) + 1) + sheet)
    return sheets

def sheets_to_raw(sheets, headers = None):
    """Join sheets to make raw data
    
    The function can add a sheet header form the headers list (one string by 
    sheet).
    """
    raw_img = ''
    for index in range(len(sheets)):
        if headers != None:
            raw_img += headers[index]
        raw_img += sheets[index]
    return raw_img

def data_to_raw(data, wanted_pallet=DEFAULT_PALLET, headers = None):
    """Work as sheets_to_raw(data_to_sheets(data)), with same arguments"""
    return sheets_to_raw(data_to_sheets(data, wanted_pallet), headers)

def raw_to_data(raw_picture, color_byte=0, pal=DEFAULT_PALLET, \
                colors=COLORS, background=WHITE):
    """Make image data from a raw picture
    
    colors is a 4-color tuple,
    color_byte is the index of the color_byte (last byte of a sheet header)
    pal is the ordoned list of the color corresponding to each sheet
    background is the default color of a pixel."""
    #make color dic:
    color_dic = { 'o' : colors[0],
                  'g' : colors[1],
                  'b' : colors[2],
                  'w' : colors[3]}
    #extract raw sheets from raw_picture
    raw_sheets = []
    sh_len = 0x400 + color_byte + 1
    for index in range(len(pal)):
        offset = index * sh_len
        raw_sheets.append(raw_picture[offset: offset + sh_len])
    #convert raw sheets to list data
    data = [background] * (CASIO_SIZE[0] * CASIO_SIZE[1])
    for raw_sheet in raw_sheets:
        color = ord(raw_sheet[color_byte]) - 1
        raw_sheet = raw_sheet[color_byte + 1:]
        for index in range(len(raw_sheet)):
            byte = bin(ord(raw_sheet[index]))
            col, line = (15 - ((index) / 64)) * 8, 63 - ((index) % 64)
            data_pos = line * 128 + (col )
            for bit_index in range(len(byte)):
                if byte[bit_index] == '1':
                    bit_pos = data_pos + bit_index 
                    data[bit_pos] = color_dic[pal[color]]
    return data

def raw_to_sheets(raw_picture, color_byte = 0, sheet_number = 4):
    """Split raw data into sheets"""
    raw_sheets = []
    sh_len = 0x400 + color_byte + 1
    for index in range(sheet_number):
        offset = index * sh_len
        raw_sheets.append(raw_picture[offset + color_byte: offset + sh_len])
    return raw_sheets

# File format management functions
def open_img(filename):
    """Open an image in any PIL-managed format, return a filedata"""
    # Build the FileData
    file_data = datacls.FileData()
    new_index = file_data.new_data()
    # Write metadata
    file_data[new_index].name = 'Picture1'
    file_data[new_index].dType = 'picture'
    file_data[new_index].pallet = DEFAULT_PALLET
    file_data[new_index].color_byte = 0
    # Write data
    img = Image.open(filename)
    file_data.data[new_index].raw_data = data_to_raw(normalize(img), \
                                                     DEFAULT_PALLET)
    return file_data

def save(filename, file_data, export=False):
    """Save a picture"""
    folder = os.path.dirname(filename)
    ext = os.path.splitext(filename)
    # For each data
    if export:
        index_list = file_data.export
    else:
        index_list = range(len(file_data.data))
    for data in [file_data.data[index] for index in index_list]:
        if data.dType == 'picture' or data.dType == 'screencapture' :
            # make a filename from the name
            if len(index_list) == 1 and os.path.basename(filename) != '':
                # try to use filename as name
                data_filename = filename
            else:
                data_filename = os.path.join(folder, data.name, ext)
            # write data
            img = Image.new('RGB', CASIO_SIZE) 
            img.putdata(raw_to_data(data.raw_data, data.color_byte,
                                    data.pallet))
            img.save(data_filename)

# Misc functions
def bin(number, total_length = 8):
    """Convert a number in binary, filled with 0 to reach total_length."""
    out = ''
    while number != 0: 
        number, out = number >> 1, `number & 1` + out
    out = '0' * (total_length - len(out)) + out
    return out

############## NEW Function added by Xion345 aka Fabien ANDRE ###################
## Sorry, I didn't really understand how did you manage to obtain a table from 
## a "classic" image (png, jpeg etc...). So I may have rewritten some functions, that you have already implemented !
 
def img_to_basic(imagen):
   """Convert a *normalized* image into a Casio Basic program
   This function only returns Horinzontal F-line. It may not be really
   optimised for size"""
   # Splits the BIG list given by normalize into a list of list
   # in order to have a table of 128 columns per 64 lines
   pictureTable=[]
   numberLine=0
   basicPicture='' # A picture under the form of a basic program / Returned by the function
   line=0
   while numberLine < 64:
      pictureTable.append(imagen[numberLine*128:((numberLine+1)*128)])
      numberLine += 1
   print len(pictureTable)
   while line <= 63:
      print "Ligne :", line
      col=0
      currentColor=0 # This var contains the previously saved color
      orig_x=0
      orig_y=0
      end_x=0
      end_y=0 # In fact this varible is totally unuseful because the table is browsed line per line so
      # end_y = orig_y. It is just used to make my function clearer
      while col <= 127:
         #print "pixel ("+str(col)+","+str(line)+")"
         if pictureTable[line][col] != (255,255,255) and pictureTable[line][col] != currentColor:
            # New F-line to add detected
            print "Nouvelle F-ligne détectée en "+str(line)+","+str(col)
            currentColor=pictureTable[line][col]
            end_x=col
            orig_x=col
            end_y=line 
            orig_y=line
         if pictureTable[line][col] != (255,255,255) and pictureTable[line][col] == currentColor:
            # Continue the F-line
            end_x +=1
         if pictureTable[line][col] == (255,255,255) and pictureTable[line][col-1] == currentColor:
            # If you get a blank pixel and if before you have a colored pixel, that's because you encountered the
            #end of the F-line
            end_x -= 1
            if currentColor == (255, 128, 0):
               basicPicture += "\\Orange "
            if currentColor == (0, 128, 0):
               basicPicture += "\\Green "
            # The origin of the reference mark is different on the casio graph and on the picture
            # On casio origin is bottom left-hand corner / On the picture and my table, it top left-hand corner
            # That's why it must do casio_line = 63-line
            casio_orig_y = 63-orig_y 
            casio_end_y = 63-end_y
            if end_x != orig_x:
               basicPicture += "\\F-Line "+str(orig_x)+","+str(casio_orig_y)+","+str(end_x)+","+str(casio_end_y)+"\n"
            else:
               basicPicture += "\\PlotOn "+str(orig_x)+","+str(casio_orig_y)+"\n"
            currentColor=0 # This var contains the previously saved color
            orig_x=0
            orig_y=0
            end_x=0
            end_y=0 
         col += 1
      line += 1
   return basicPicture


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/