Over the last two years, I have been writing computer applications in Python as a hobby. During this period the possibility of using Python for image processing has been in the back of my mind. At first, Python, since it is interpreted, seemed a totally inappropriate language. Then I started using the NumPy library. NumPy provides Python a capability very similar, although very different in syntax, to the commercial software package Matlab. All I needed was an idea for my first image processing application.
A paper Modular Line-Based Halftoning via Recursive Division, provided the spark. See Proceedings of the Workshop on Non-Photorealistic Animation and Rendering, 2014 Vancouver, published by ACM New York (For a copy of the paper click on the title above and then follow a second link to get a free copy from the ACM library)
The Photo to the right is the result of my first image processing attempt. It consists totally of black rectangles. Each rectangle contains the same amount of “black ink” in the original photo. The first step sliced the photo into five rectangles of equal black ink, each of these was sliced into five more, then five more …
The final list of divisors used was 5,5,5,5,3,3,2,2,2,2, generating ninety thousand rectangles. Total execution on my old 2011 MacBook Pro is 10 seconds. Clearly, Python with NumPy is an acceptable image processing platform.
My other favorite Python concept is Cairo. Cairo is a drawing package. Think of it like an alternative PostScript. The original form of the image on the right above was Scalable Vector Graphics (SVG)
Cairo supports many “back-ends” and one of them is SVG. Click here to see a full rez of the right image.
My next version will have better imaging slicing to break up patterns. Also I plan to upload an iPython/Jupyter Notebook implementation.
Thats all for now. The next section describes a few of the coding details. If your not interested skip to the bottom and leave your comments.
Software Details
My function sliceIm, the heart of this process, follows:
def sliceIm(image, rects, n):
nxtrects = []
for rect in rects:
top = rect[0]
bottom = rect[1]
left = rect[2]
right = rect[3]
im = image[top:bottom,left:right]
if (bottom - top) > (right - left):
rowsum = im.sum(axis=1)
cumrowsum = rowsum.cumsum()
xr = np.amin(cumrowsum)
mr = (np.amax(cumrowsum)-xr)/n
for i in range(1,n):
t = np.amin(np.nonzero(cumrowsum>=(xr+(mr*(i-1)))))
b = np.amin(np.nonzero(cumrowsum>=(xr+(mr*i))))
nxtrects.append( [top + t, top + b, left, right] )
nxtrects.append( [top + b, bottom , left, right] )
else:
colsum = im.sum(axis=0)
cumcolsum = colsum.cumsum()
xc = np.amin(cumcolsum)
mc = (np.amax(cumcolsum)-xc)/n
for i in range(1, n):
r = np.amin(np.nonzero(cumcolsum>=(xc+(mc*(i-1)))))
l = np.amin(np.nonzero(cumcolsum>=(xc+(mc*i))))
nxtrects.append( [top, bottom, left + r, left + l] )
nxtrects.append( [top, bottom, left + l, right] )
return nxtrects
[notes]
image: NumPy array of pixels (0.0 is white 1.0 is black)
rects: lists of lists. each element is a
rectangle [top, bottom, left, right]
example from second slicing of Mona Lisa:
[[0, 269, 0, 997], [269, 537, 0, 997],
[537, 792, 0, 997], [792, 1015, 0, 997],
[1015, 1246, 0, 997]]
n: number of slices to divide each rectangle
line 8: im = image[top:bottom,left:right]
im is assigned a slice of image
The slice is not copied, im is just mapped to the slice
NumPy Magic
line 10: rowsum = im.sum(axis=1) n by m slice summed over
each row returning an n by 1 array
line 11: cumrowsum = rowsum.cumsum() over slice of
sum of all rows above
line 15: np.nonzero(cumrowsum>=(xr+(mr*(i-1)))))
returns and n by 1 0/1 result for
row by row comparison
t is the min row or the t row of the new slice
line 16: finds b or the bottom of the new slice
line 20-28 repeat structure of line 10-18 for new horizontal slice
Finally here is lowest level cairo drawing routine for drawing all the rectangles.
def drawImg(rects, surface, ctx):
for rect in rects:
top = rect[0]*2
bottom = rect[1]*2
left = rect[2]*2
right = rect[3]*2
ctx.rectangle(left, top, right-left, bottom-top)
ctx.stroke()
[notes]
rects: lists of lists. each element is a
rectangle
surface: a Cairo drawing surface
ctx: a Cairo drawing context