Python - NumPy - tuples as elements of an array

Thomas picture Thomas · Nov 20, 2010 · Viewed 43.5k times · Source

I'm a CS major in university working on a programming project for my Calc III course involving singular-value decomposition. The idea is basically to convert an image of m x n dimensions into an m x n matrix wherein each element is a tuple representing the color channels (r, g, b) of the pixel at point (m, n). I'm using Python because it's the only language I've really been (well-)taught so far.

From what I can tell, Python generally doesn't like tuples as elements of an array. I did a little research of my own and found a workaround, namely, pre-allocating the array as follows:

def image_to_array(): #converts an image to an array  
    aPic = loadPicture("zorak_color.gif")  
    ph = getHeight(aPic)  
    pw = getWidth(aPic)  
    anArray = zeros((ph,pw), dtype='O')  
    for h in range(ph):  
         for w in range(pw):             
            p = getPixel(aPic, w, h)  
            anArray[h][w] = (getRGB(p))  
    return anArray

This worked correctly for the first part of the assignment, which was simply to convert an image to a matrix (no linear algebra involved).

The part with SVD, though, is where it gets trickier. When I call the built-in numPy svd function, using the array I built from my image (where each element is a tuple), I get the following error:

Traceback (most recent call last):
  File "<pyshell#5>", line 1, in -toplevel-
    svd(x)
  File "C:\Python24\Lib\site-packages\numpy\linalg\linalg.py", line 724, in svd
    a = _fastCopyAndTranspose(t, a)
  File "C:\Python24\Lib\site-packages\numpy\linalg\linalg.py", line 107, in _fastCopyAndTranspose
    cast_arrays = cast_arrays + (_fastCT(a.astype(type)),)
ValueError: setting an array element with a sequence.

This is the same error I was getting initially, before I did some research and found that I could pre-allocate my arrays to allow tuples as elements.

The issue now is that I am only in my first semester of (college-level) programming, and these numPy functions written by and for professional programmers are a little too black-box for me (though I'm sure they're much clearer to those with experience). So editing these functions to allow for tuples is a bit more complicated than when I did it on my own function. Where do I need to go from here? I assume I should copy the relevant numPy functions into my own program, and modify them accordingly?

Thanks in advance.

Answer

nimrodm picture nimrodm · Nov 20, 2010

Instead of setting the array element type to 'O' (object) you should set it to a tuple. See the SciPy manual for some examples.

In your case, easiest is to use something like

a = zeros((ph,pw), dtype=(float,3))

Assuming your RGB values are tuples of 3 floating point numbers.

This is similar to creating a 3d array (as Steve suggested) and, in fact, the tuple elements are accessed as a[n,m][k] or z[n,m,k] where k is the element in the tuple.

Of course, the SVD is defined for 2d matrices and not 3d arrays so you cannot use linalg.svd(a). You have to decide SVD of what matrix (of the three possible ones: R G and B) you need.

If, for example, you want the SVD of the "R" matrix (assuming that is the first element of the tuple) use something like:

linalg.svd(a[:,:,1])