tags:

views:

228

answers:

4

I'm using numpy to initialize a pixel array to a gray checkerboard (the classic representation for "no pixels", or transparent). It seems like there ought to be a whizzy way to do it with numpy's amazing array assignment/slicing/dicing operations, but this is the best I've come up with:

w, h = 600, 800
sq = 15    # width of each checker-square
self.pix = numpy.zeros((w, h, 3), dtype=numpy.uint8)
# Make a checkerboard
row = [[(0x99,0x99,0x99),(0xAA,0xAA,0xAA)][(i//sq)%2] for i in range(w)]
self.pix[[i for i in range(h) if (i//sq)%2 == 0]] = row
row = [[(0xAA,0xAA,0xAA),(0x99,0x99,0x99)][(i//sq)%2] for i in range(w)]
self.pix[[i for i in range(h) if (i//sq)%2 == 1]] = row

It works, but I was hoping for something simpler.

+1  A: 

Can't you use hstack and vstack? See here. Like this:

>>> import numpy as np
>>> b = np.array([0]*4)
>>> b.shape = (2,2)
>>> w = b + 0xAA
>>> r1 = np.hstack((b,w,b,w,b,w,b))
>>> r2 = np.hstack((w,b,w,b,w,b,w))
>>> board = np.vstack((r1,r2,r1,r2,r1,r2,r1))
telliott99
I don't get no respect here, but this is correct.http://telliott99.blogspot.com/2010/01/heres-question-on-so-about-how-to-make.html
telliott99
This doesn't make an array of the correct size, though it looks like you expanded your answer in your blog post. But we can't vote up the blog post! :) Sheesh!
Ned Batchelder
+1  A: 
import numpy as NP

def checkerboard(w, h) :
    re = NP.r_[ w*[0,1] ]
    ro = NP.r_[ w*[1,0] ]
    return NP.row_stack(h*(re, ro))


AB = checkerboard(5, 10)
doug
This is close, although you have to be careful of a few things: I wanted the checks to be more than 1 pixel across (I had them as 15), and you can't assume that the checks will fit evenly into the width and height of the checkerboard desired.
Ned Batchelder
+1  A: 

I'm not sure if this is better than what I had:

c = numpy.fromfunction(lambda x,y: ((x//sq) + (y//sq)) % 2, (w,h))
self.chex = numpy.array((w,h,3))
self.chex[c == 0] = (0xAA, 0xAA, 0xAA)
self.chex[c == 1] = (0x99, 0x99, 0x99)
Ned Batchelder
+1  A: 

Ned, I like your answer; it's quite readable. However, here's another way to do it using ogrid which is a bit faster:

import numpy as np
import Image

w,h = 600,800
sq=15
color1=(0xFF,0x80,0x00)
color2=(0x80,0xFF,0x00)
def use_ogrid():
    coords=np.ogrid[0:w,0:h]
    idx=(coords[0]//sq+coords[1]//sq)%2
    vals=np.array([color1,color2],dtype=np.uint8)
    img=vals[idx]
    return img

def use_fromfunction():
    img = np.zeros((w,h,3), dtype=np.uint8)
    c = np.fromfunction(lambda x,y: ((x//sq) + (y//sq)) % 2, (w,h))
    img[c==0]=color1
    img[c==1]=color2
    return img

if __name__=='__main__':
    for f in (use_ogrid,use_fromfunction):
        img=f()
        pilImage = Image.fromarray(img, 'RGB')
        pilImage.save('{0}.png'.format(f.func_name))

Here are the timeit results:

% python -mtimeit -s"import test" "test.use_fromfunction()"
10 loops, best of 3: 307 msec per loop
% python -mtimeit -s"import test" "test.use_ogrid()"
10 loops, best of 3: 129 msec per loop
unutbu
Can you adapt this so that it will work if my pixel colors aren't pure gray? Suppose I wanted the two colors to be (0xFF,0x80,0x00) and (0x80,0xFF,0x00)
Ned Batchelder
Sure. It was a bad design choice on my part to assume the color had to be gray...
unutbu