PIL to OpenCV image

An example suggested this code:

        cv_img1 = cv.CreateImageHeader(img1.size, cv.IPL_DEPTH_8U, 1)
        cv.SetData(cv_img1, img1.tostring(), img1.width*3)

But the image kept getting stretch along the width. Playing with the numbers didn’t help. The above code is suppose to convert an RGB image to single band, but I finally decided to convert the PIL image to a single band first and then it worked. Remove the width multiplier.

        img1 = img1.convert('L')
        cv_img1 = cv.CreateImageHeader(img1.size, cv.IPL_DEPTH_8U, 1)
        cv.SetData(cv_img1, img1.tostring(), img1.width  )

Google App Engine Installed

Installing the Google App Engine was pretty easy. And there are also tutorials on the website.

To get an account with Google App Engine, you need to be able to receive a phone text. I don’t have a cell phone number to give out so I used the web-based Pinger service. When I gave the number to Google App Engine registration to get the password, it said there was an error in sending the text, but it actually went through and I got the verification code off Pinger.

I want to note one thing that confused me about uploading apps. “Application ID” vs. “Application title”. In the Google App Engine Launcher, the name column is actually the App ID and is the same as the first line of the ‘app.yaml’, just called application: and should also be the same as the Application Identifier that you register on Google App Engine. The Application Title seems to do nothing but you can make it the same as the python application.

The bottom line of ‘app.yaml’ says script:, and this is where the name of the python module goes, except you replace the extension .py with .app.

To summarize, app.yaml has two important lines (for a beginner like me):

application: HelloWorld

this name after application in app.yaml must be the same as the registered name on the App Engine: HelloWorld.appspot.com. The name might be taken already so be prepared to change the application name in app.yaml.

  script: helloworld.app

calls the helloworld.py program that must(?) reside in the same folder as app.yaml.

Udacity

I signed up for two courses on Udacity; CS373 Programming a Robotic Car, which is in its second phase self-paced incarnation, and CS253 Web Application Engineering. I signed up for the web app class initially because I think it could lead to a great occupation and also because I believe I have a great idea for a facebook rival and an idea for a research oriented ‘timeline’ application.

I wanted to sign up for the robotic car because it sounds very interesting, but I wasn’t sure if it would take time away from my schoolwork. I decided it doesn’t have to be a serious commitment because it seems that a lot of people don’t finish it. Turns out that it is very applicable, I’m already understanding the machine learning concepts from a class I was sitting in on. That class was a second part to a previous class that I missed and too difficult, but Thrun is so much clearer and takes it in easy steps. Now I have a better grip on Bayes theory, which it not all that complicated and something that I was already naturally doing. It’s just a formal definition of obvious logic.

Now I’m installing the Google App Engine (v1.6.4 for windows) for Python. It said it found Python 2.5 while installing. I hope that won’t be a problem since I use 2.7 mostly.

Thrun’s use of Python seems a little backwards and doesn’t use the special nuances of Python. I don’t know if he thinks it’s easier for the students or because he is use to it from other languages. There is no NumPy in the online Python IDE. I have to learn how to manipulate lists in ways that would be so simple with arrays. Although I think it is pointless since array processing is computationally faster than lists. All this time I’ve been trying hard to avoid for loops, especially nested for loops, and now I am forced to write them, haha, but that’s okay.

OpenCV install

I can finally use OpenCV in Python after several attempts over the last few months. Previously, I could not import or use it even though it appeared to be installed along with Python XY.

In a nutshell:
1) Installed PythonXY 2.7.2.1
2) import cv2.cv as cv (for running older cv examples)

I tried searching for a simple solution but all I found were complicated install instructions involving ‘builds’ or ‘building’ on your system and other sources that made this all seem easy.
Finally, I came across the revision listing for Python XY and found that the newest version solves some issue with it. So I updated from 2.6 to 2.7.
But no where did I see anything about having to ‘import cv2.cv’. All of the code snippets I’ve seen just ‘import cv’. I finally came across a code that had ‘import cv2.cv’ and tried it and everything finally worked, the door was finally unlocked. Except for having seen that import statement in some code, how was I to know that I needed to import cv2 instead of cv?

MATLAB to Python

In case I need to convert MatLab code to Python again…

origunits(1:3)
->    
origunits[:3]
## Because MatLab starts with 1 and includes the last number
## and Python does neither
fgetl(fid)  
->    
fid.readline().rstrip()
strcmp(unt(1:3),'ori')
->    
unt[:3] == 'ori'
error('Unknown units')
->    
raise Exception('Unknown units')
switch unt(1:3),
    case 'fee',
       ...
    case 'met',
       ...
->    
if unt[:3] == 'fee':
    ...
elif unt[:3] == 'met':
    ...
## Python does not have a 'switch-case' control
fprintf('Station: %s\n',pred.station);
->    
print( 'Station: {0}'.format(pred.station) )
## Newline char not needed
clear pred
->    
del pred
xtide=struct('name',repmat(' ',ncon,8),'speed',zeros(ncon,1),...
             'startyear',0,'equilibarg',zeros(ncon,68),...
             'nodefactor',zeros(ncon,68));
->
xtide = dict({'name':numpy.tile(' ',(ncon,8)), 
              'speed':zeros((ncon,1)),
    	      'startyear':0,
              'equilibarg':zeros((ncon,68)),
              'nodefactor':zeros((ncon,68)) })
## Numpy.tile replaces 'repmat'. Add parenthesis for 'zeros'.

Monty Hall Python

Monty Hall problem simulation.
Player picks one of three doors (to win a car). Without opening the players door, the host opens a non-winning door (or win a goat). The player then can switch to the other closed door and stay with the first choice.

import random

def main(trials=12345, number_doors=3):
    # SHOW THE RANDOMNESS OF THE GAME SETUP
    show_game_randomness(trials, number_doors)
    
    # TEST NEVER SWITCHING AND ALWAYS SWITCHING
    # TRY NEVER SWITCHING
    trues = 0
    for trial in xrange(trials):
        is_car = play_a_game(SWITCH=False, number_doors=number_doors)
        if is_car: trues += 1
    print '{0} games and never switching: {1} wins ({2:.2%})'.format(
           trials, trues, trues/float(trials))
    # TRY ALWAYS SWITCHING
    trues = 0
    for trial in xrange(trials):
        is_car = play_a_game(SWITCH=True, number_doors=number_doors)
        if is_car: trues += 1
    print '{0} games and always switching: {1} wins ({2:.2%})'.format(
           trials, trues, trues/float(trials))
    
    
def play_a_game(SWITCH=False, number_doors=3):
    doors = newgame(number_doors)
    whose_door = ['']*number_doors
    # PLAYER SELECTS A DOOR
    whose_door[random.randint(0,number_doors-1)] = 'player'
    
    # HOST SHOWS A GOAT (SELECTS A GOAT DOOR)
    while True:
        host_sel = random.randint(0,number_doors-1)
        if doors[host_sel] != 'car' and whose_door[host_sel] != 'player':
            whose_door[host_sel] = 'host'
            break
        
    # SWITCH DOORS?
    if SWITCH:
        while True:
            player_sel = random.randint(0,number_doors-1)
            if whose_door[player_sel] != 'host' and whose_door[player_sel] != 'player':
                break
        whose_door[whose_door.index('player')] = 'nevermind'
        whose_door[player_sel] = 'player'
        
    # RETURN RESULT
    return True if doors[whose_door.index('player')] == 'car' else False
        
     
def newgame(number_doors=3):
    car_index = random.randint(0,number_doors-1)
    doors = ['goat']*number_doors
    doors[car_index] = 'car'
    return doors
    
    
def show_game_randomness(trials, number_doors=3):
    trial_results = [0]*number_doors    
    while True:
        doors = newgame(number_doors)
        trial_results[doors.index('car')] += 1
        
        if sum(trial_results) >= trials:
            print 'Car occurance:',
            print trial_results, 'times.',
            print ['{0:.2%}'.format(x/float(sum(trial_results))) for x in trial_results]
            break


if __name__ == '__main__':
    main()

When I read this problem and how switching doors is the best solution, it was a little hard to believe. I realized why switching is a good idea when programming the host’s selection process to show a goat. Since 2/3rds of the time, the host is constrained in his choice of the two remaining doors, therefor, 2/3rds of the time, the car is behind the door the host does not pick.

Saving a canvas animation

The only built-in image save function for Tkinter canvas is a postscript save. This didn’t work for me and instead of finding out why, I thought of another way which works great.
Create a frameless canvas window using ‘overrideredirect(1)’, place the canvas in the top corner and use PIL’s ImageGrab and save the images. Finally, ‘destroy’ the window when finished, since there is no kill button in the top corner. Here is an example that animates a polygon moving on the canvas.

from Tkinter import *
from PIL import ImageGrab
from numpy import array
import os


class App(Tk):
    def __init__(self, parent):
        Tk.__init__(self, parent)
        self.overrideredirect(1)
        
        self.width = 900
        self.height = 640
        self.initialize()
        
    def initialize(self):
        self.c = Canvas(self, width=self.width, height=self.height, background='white')
        self.c.pack(side=RIGHT, expand=YES, fill=BOTH)
        self.update()
        
        self.run_anim()
        self.destroy()
        
        
    def run_anim(self):
        print os.getcwd()
        c = self.c
        polyo = array([34,60,226,15,419,60,359,151,91,151])
        polyd = array([205,253,296,187,388,253,353,360,239,360])
        trantime = 20
        for i in xrange(trantime):
            c.delete('pol')
            ptrans = (float(i)/trantime)*polyd+(trantime-float(i))/trantime*polyo
            c.create_polygon(list(ptrans), width=4, outline='black', fill='red', tags='pol')


            self.update()
            savename = 'im_{0:0>6}'.format(i)
            ImageGrab.grab((0,0,900,640)).save(savename + '.jpg')
            

app = App(None)
app.mainloop()

Tkinter notes

I keep getting tripped up by the same problems so this note on Tkinter is long overdue.

Most importantly, remember to keep a global reference to all images that you want to have persist on the canvas or tk window. Also, make sure that the ‘global’ reference is called at the beginning of any method using those images. I keep forgetting one or the other and getting stuck over why my images are not appearing. Any images created within a method will not appear unless referenced by a more permanent variable. I need to remember that you cannot just create images on the fly and throw them onto the canvas (although this seems to work for lines and ovals). So:
1) Make sure there is a permanent reference, and
2) Make sure to use ‘global’ or pass the image as an argument for editing.

Another problem I keep encountering is editing PIL images and getting the ImageTk.PhotoImage method to accept an image.
Getting subwindow of image:

subwin = imjpg.copy().crop( (box coordinates) )

You can chain the copy with crop.
im.load():
The load method seems to only give you pixel access to an image for editing, so I think it is not much use since you can’t do slicing. ImageDraw is better for drawing figures.

Masking:

# MAKE COPY IF YOU WANT TO KEEP ORIGINAL.
imcopy = im.copy()
# COLOR: 0 = TRANSPARENT, 255 = OPAQUE.
mask = Image.new('L', imjpg.size, color=100)
# GET A VIEW OF IMAGE FOR DRAWING. EDITING 'DRAW' ALSO CHANGES 'MASK'.
draw = ImageDraw.Draw(mask)
...
# OVERWRITE OR ADD ALPHA LAYER TO IMAGE ('RGB' -> 'RGBA')
imcopy.putalpha(mask)
# SAVE TO GLOBALLY REFERENCED VARIABLE AS A Tk COMPATIBLE IMAGE.
immasked = ImageTk.PhotoImage(imcopy)

C++ enum to Python namedtuple (cont.)

Here is a way to convert a C++ enum with values to Python.
C++ code:

typedef enum
{
   LADYBUG_RAW_CAM0           = ( 0x1 << 0 ),
   LADYBUG_RAW_CAM1           = ( 0x1 << 1 ),
   LADYBUG_RAW_CAM2           = ( 0x1 << 2 ),
   LADYBUG_RAW_CAM3           = ( 0x1 << 3 ),
   LADYBUG_RAW_CAM4           = ( 0x1 << 4 ),
   LADYBUG_RAW_CAM5           = ( 0x1 << 5 ),
   LADYBUG_ALL_RAW_IMAGES     =  0x0000003F,

   ...

   LADYBUG_PANORAMIC          = ( 0x1 << 12 ),

   LADYBUG_DOME               = ( 0x1 << 13 ),

   LADYBUG_SPHERICAL          = ( 0x1 << 14 ),

   LADYBUG_ALL_CAMERAS_VIEW   = ( 0x1 << 15 ),

   LADYBUG_ALL_OUTPUT_IMAGE   = 0x7FFFFFFF,

} LadybugOutputImage

The Python version:

LadybugOutputImage = dict(
   LADYBUG_RAW_CAM0           = ( 0x1 << 0 ),
   LADYBUG_RAW_CAM1           = ( 0x1 << 1 ),
   LADYBUG_RAW_CAM2           = ( 0x1 << 2 ),
   LADYBUG_RAW_CAM3           = ( 0x1 << 3 ),
   LADYBUG_RAW_CAM4           = ( 0x1 << 4 ),
   LADYBUG_RAW_CAM5           = ( 0x1 << 5 ),
   LADYBUG_ALL_RAW_IMAGES     =  0x0000003F,

   ...

   LADYBUG_PANORAMIC          = ( 0x1 << 12 ),

   LADYBUG_DOME               = ( 0x1 << 13 ),

   LADYBUG_SPHERICAL          = ( 0x1 << 14 ),

   LADYBUG_ALL_CAMERAS_VIEW   = ( 0x1 << 15 ),

   LADYBUG_ALL_OUTPUT_IMAGE   = 0x7FFFFFFF  )
LadybugOutputImage = namedtuple('Enum',
                       LadybugOutputImage.keys() )(**LadybugOutputImage)

Or even better, just get rid of all the packaging and commas on this type if it’s not too long and you have the patience. But you will also have to correct the indenting. Maybe there is a program out there that can do this editing for you.

   LADYBUG_RAW_CAM0           = ( 0x1 << 0 )
   LADYBUG_RAW_CAM1           = ( 0x1 << 1 )
   LADYBUG_RAW_CAM2           = ( 0x1 << 2 )
   LADYBUG_RAW_CAM3           = ( 0x1 << 3 )
   ...
   LADYBUG_ALL_CAMERAS_VIEW   = ( 0x1 << 15 )
   LADYBUG_ALL_OUTPUT_IMAGE   = 0x7FFFFFFF