Re: [casetta] Important : Normalize function and strange behaviour...or not + NEW version of img_to_basic

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


Hello,

2007/3/16, Florian Birée <florian.biree@xxxxxxxxx>:
Hi,

2007/3/16, Fabien ANDRE <fabien.andre.g@xxxxxxxxxx>:
Florian Birée a écrit :
Hello,

2007/3/15, Fabien ANDRE <fabien.andre.g@xxxxxxxxxx>:
Hello,

2 things :

1st: I have included the modifications you made in the img_to_basic function old version and made that working for me :
The changelog until the last version I sent you is :
* Changed indent to 4-spaces and lines to 80-characters max.
* Changeg color constants to the system of dictionnaries/variables
* Included your special condition in the end F-line block : I didn't understand what it is useful for but as
it didn't include new bugs, I included this
* Corrected the bug of a line not drawn if it goes to the end of a line (Yes I know this is ugly)

I still don't know the problem there was in the modified version you sent me. Maybe and indentation problem...

I succeded in transmitting my tux2.png on my calculator with your pic2prog.py script. I hope this will work for you.

I will try it this week end, and send you if it work with me.

2nd: Sometimes, I believe that the normalize function has a strange behaviour. For example, the image in attachments contains white (By Thesa).
And in the imagen variable sent to the img_to_basic function, there is no white pixel (and of course on my calculator I get no white). I may be wrong ;-)

Pixels are white only if the color  is exactly (255,255,255), and not values like (254, 255, 255). I know with issue, and I'll change the color detection to be less strict.

I have also some feature requests (not added in the wishlist, because I want your opinion before : I don't know if it is easy of not !!!)
1- Could you make the dialog window in casetta (such as : "Mettez votre calculatrice en réception et cliquez sur valider"), as it (traduction incertaine de comme çà), you have just to press enter (yes, this is another unimportant detail :-).

I'm currently writing a totally new  transfer tool (so cafix and its bug will ne longer be needed), and its almost ready to be added in the repository. And I'll also rewrite the interface for it (when I'll work on casetta_gtk, ie when casetta 0.3 will be almost finished).

2-For backups, could you add the ability to casetta_gtk to show the content ? (Name of programs, matrix,pictures etc ...)

It's an interesting idea, and not really to made (data is more or less stored in the same way that it is send by the calculator). A function which convert a backup into a casio file may just be a collection of regular _expression_, each can extract a kind of data. (Its already do to extract passwords from a backup).
But this function need to know how all data are stored in the casio memory, and for the moment, I didn't know all types of data (you can see http://casetta.tuxfamily.org/formats/ for more information about formats). And I think the support of all data is the first objective. So I prefer to work on a function like this in a later version. But if you want to try to do this... ;-)

3-For image/screencapture could you display them in casetta and add an export function to export to png...

Yes, a picture editor is planned in casetta_gtk. For the png export, it must already work in the svn version, as its only a casetta work, and casetta can import/export all picture formats managed in the Python Image Library (and there are a lot).

Normally, the img_to_basic function is working and ready to be integrated in the svn :-).

Ok, I'll see it this week-end.

"Longue vie à casetta"

Merci !

--
Thesa ~ Florian Birée
e-mail : florian.biree@xxxxxxxxx
Messagerie Instantanée Jabber/XMPP/Google Talk : florian.biree@xxxxxxxxx
Site web et blog : http://filyb.info/
"And I think the support of all data is the first objective. So I prefer to work on a function like this in a later version. But if you want to try to do this... ;-)"
Yes, you're right, support of all data is more important than these secondary functions. But if we can get both that's better :-) So let's go I'm trying to do that.A small revison of regexp will not be bad for me !!

For the png, export it works with the svn version but when you click save it proposes newcat as format and it is not obsvious for "simple" user to change extension to PNG.

When you save a file, when you show the "other directory" panel, a list let you choice your format, and picture formats are already inside this list. The behaviour of the save as window follow the behaviour of all Gnome applications

"I'm currently writing a totally new  transfer tool (so cafix and its bug will ne longer be needed), and its almost ready to be added in the repository. And I'll also rewrite the interface for it (when I'll work on casetta_gtk, ie when casetta 0.3 will be almost finished)."

Wow ! But why didn't you contact cafix author to ask him to correct the bugs and to add support for new USB calculators ? He didn't answered ? Cafix is no longer maintained ?

Yes, cafix is not maintained since 2002 or 2003. Add a lot of technical choice of the cafix project are very bad (the file format, details about how cafix work with the calculator) - in my opinion, of course. And cafix work only under GNU/Linux operating system, not my transfer tool which use the pyserial module.

--
Fabien ANDRE aka Xion345
Linux User #418689 -- fabien.andre.g@xxxxxxxxxx -- xion345@xxxxxxxxxxxxx
When asked why he called the new software, 'git,' British slang meaning 'a rotten person,' he said. 'I'm an egotistical bastard, so I name all my projects after myself. First Linux, now git.' ( Linus Torvalds, Not dated )



--
Thesa ~ Florian Birée
e-mail : florian.biree@xxxxxxxxx
Messagerie Instantanée Jabber/XMPP/Google Talk : florian.biree@xxxxxxxxx
Site web et blog : http://filyb.info/

I've test your function, the result is very good. Some details was bad (as the By Thesa in your picture) because of my normalize function. I've changed it, and now programs made by your functions display pictures perfectly. I've join the new pictures.py file. It'll be uploaded in the svn repository soon.

Thank you for this great contribution!

(When I'll begin the work on casetta_gtk, I may move your function in the casetta_gtk code, since it's casetta_gtk which will used the function. For the moment, I don't know.)

--
Thesa ~ Florian Birée
e-mail : florian.biree@xxxxxxxxx
Messagerie Instantanée Jabber/XMPP/Google Talk : florian.biree@xxxxxxxxx
Site web et blog : http://filyb.info/
# -*- coding: utf-8 -*-
#
############################################################################
#
# (c) 2006 Florian Birée aka Thesa <florian.biree@xxxxxxxxxxx>
# (c) 2006 Achraf Cherti aka Asher256 <achrafcherti@xxxxxxxxx>
# function img_to_basic is (c) 2007 Xion345 aka Fabien ANDRE 
#                              <fabien.andre.g@xxxxxxxxxx>
#
# 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']
WHITE_LIMIT = 210
# If all color of a pixel are superior of WHITE_LIMITE, they will be white.

# 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.
    """
    if image.size != CASIO_SIZE:
        image = image.resize(CASIO_SIZE, Image.ANTIALIAS)
    data = list(image.getdata())
    out = []
    # color conversion
    for pixel in data:
        red, green, blue = pixel[0], pixel[1], pixel[2]
        if red > WHITE_LIMIT and green > WHITE_LIMIT and blue > WHITE_LIMIT:
            pixel = colors[3] # white
        elif 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) == (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

def change_sheets_pallet(sheets, old_pallet, new_pallet):
    """Resort sheets from old_pallet to new_pallet
    
    If a collor in new_pallet is not in old_pallet, a empty sheet
    will be made for this color.
    """
    sheet_dic = {}
    for index in range(len(old_pallet)):
        sheet_dic[old_pallet[index]] = sheets[index][1:]
    new_sheets = []
    for index in range(len(new_pallet)):
        new_sheets.append(chr(index + 1) + sheet_dic.get(new_pallet[index], \
                                                         '\x00' * (16 * 64)))
    return new_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)[1]
    # 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

# This function is a contribution of Xion345 aka Fabien ANDRE.
# Xion345, thank you! 
def img_to_basic(imagen, colors=COLORS):
    """Convert a *normalized* image into a Casio Basic program
    This function only returns Horinzontal F-line. It may not be really
    optimised for size"""
    program_header = "\Cls\n\ViewWindow 0,127,1,0,63,1\n"
    orange, green, blue, white = colors # colors dictionnary
    # 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 line <= 63:
        pictureTable.append(imagen[line*128:((line+1)*128)])
        line += 1
    line=0
    while line <= 63:
        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:
            if pictureTable[line][col] != currentColor and \
            pictureTable[line][col-1] == currentColor \
            and currentColor in colors[:3] or col==127 and currentColor != \
            white and pictureTable[line][col] == currentColor:
            # If you get a pixel which color is different from currentColor 
            # and if before you have a colored pixel, 
            #that's because you encountered the end of the F-line
           
                # The second condition after the or is an UGLY correction of a
                # BUG ^W Feature
                # If an F-line goes at the lats pixel of a pictureTable line,
                # the line is not drawn because the first confition (end line
                # see above ), waits for the pixel just after the end of the
                # line. This blocks checks if were are at the last pixel of the
                # line and if a F-line is being drawn (currentColor != white) 
                
                if col != 127:
                    end_x -= 1
                if currentColor == orange:
                    basicPicture += "\\Orange "
                if currentColor == green:
                    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's 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
            if pictureTable[line][col] != white and \
            pictureTable[line][col] != currentColor:
                # New F-line to add detected
                currentColor=pictureTable[line][col]
                end_x=col
                orig_x=col
                end_y=line 
                orig_y=line
            if pictureTable[line][col] !=  white and \
            pictureTable[line][col] == currentColor:
                # Continues the F-line
                end_x +=1
            col += 1
        line += 1
    return program_header+basicPicture


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