Reflections

One of my favorite photography  tricks is to point my lens at a store window and capture both the window display and the street scene. Here are some examples. Click on the photos to see full screen.

 

Two windows of Prada, Milan

OLYMPUS DIGITAL CAMERA

Beach restaurant, Barcelona

Paris, where else?

img_0385

Window of “art?” across the street from the Louvre, Paris

 

 Lucca, Italy

Store Windows Florence, Italy

OLYMPUS DIGITAL CAMERA

 Store across street from ‎Filippo Brunelleschi’s dome, Florence

Two Essays

Earlier this year reading two essays caused me to pose new questions when viewing works of art. The first, by Teju Cole, Shadows in São Paulo,  the second by André Aciman, My Monet Moment.

Both authors journey to find the source location of an image.  Cole is chasing the source of a René Burri photograph in San Paulo,  and Aciman is trying to understand the scene which was the source of a Monet Painting.

Cole asks, can we identify the location and subject of photos in the real world.  Fifty years after the photo was taken, he tries and succeeds.  However, his initial efforts are complicated by assuming the lens focal length was close to “normal”.  For more background, click here “Men on a Rooftop“.

Aciman discovers that elements of a painting may be changed from the real world. Elements of the painting are rearranged for a better visual composition.  For a look at the painting,  Maybe it’s the right one ;), click here.

As these essays illustrate, when looking at a painting or photograph several questions may increase your enjoyment or understanding.

  1. Where is the source of this image?
  2. Would my eye see the same perspective, or is it wider or foreshortened?
  3. Are the elements of image grouped in a special way to enhance the composition?

Non Photo-Realistic Mona Lisa

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