2

I have three numpy arrays, X, Y, and Z.

X and Y are coordinates of a spatial grid and each grid point (X, Y) has an intensity Z. I would like to save a PNG image using this data. Interpolation is not needed, as X and Y are guaranteed to cover each grid point between min(X) and max(Y).

I'm guessing the solution lies within numpy's meshgrid() function, but I can't figure out how to reshape the Z array to NxM intensity data.

How can I do that?


To clarify the input data structure, this is what it looks like:

   X   |    Y    |    Z
-----------------------------
0.1    | 0.1     | something..
0.1    | 0.2     | something..
0.1    | 0.3     | something..
...
0.2    | 0.1     | something..
0.2    | 0.2     | something..
0.2    | 0.3     | something..
...

0.2    | 0.1     | something..
0.1    | 0.2     | something..
0.3    | 0.3     | something..
...
3
  • What is the shape of Z? Commented Apr 29, 2016 at 8:49
  • 1
    I'm sorry, I made a mistake asking the question. All three arrays have dimension N, and X and Y contain duplicate values. I'll add an example to the original question. Commented Apr 29, 2016 at 9:25
  • how manage duplicate values ? max, first, mean ? Commented Apr 29, 2016 at 12:36

3 Answers 3

2

To begin with, you should run this piece of code:

import numpy as np

X = np.asarray(<X data>)
Y = np.asarray(<Y data>)
Z = np.asarray(<Z data>)

Xu = np.unique(X)
Yu = np.unique(Y)

Then you could apply any of the following approaches. It is worth noting that all of them would work fine even if the data are NOT sorted (in contrast to the currently accepted answer):

1) A for loop and numpy.where() function

This is perhaps the simplest and most readable solution:

Zimg = np.zeros((Xu.size, Yu.size), np.uint8)
for i in range(X.size):
    Zimg[np.where(Xu==X[i]), np.where(Yu==Y[i])] = Z[i]

2) A list comprehension and numpy.sort() funtion

This solution - which is a bit more involved than the previous one - relies on Numpy's structured arrays:

data_type = [('x', np.float), ('y', np.float), ('z', np.uint8)]
XYZ = [(X[i], Y[i], Z[i]) for i in range(len(X))]
table = np.array(XYZ, dtype=data_type)
Zimg = np.sort(table, order=['y', 'x'])['z'].reshape(Xu.size, Yu.size)

3) Vectorization

Using lexsort is an elegant and efficient way of performing the required task:

Zimg = Z[np.lexsort((Y, X))].reshape(Xu.size, Yu.size)

4) Pure Python, not using NumPy

You may want to check out this link for a pure Python solution without any third party dependencies.


To end up, you have different options to save Zimg as an image:

from PIL import Image
Image.fromarray(Zimg).save('z-pil.png')

import matplotlib.pyplot as plt
plt.imsave('z-matplotlib.png', Zimg)

import cv2
cv2.imwrite('z-cv2.png', Zimg)

import scipy.misc
scipy.misc.imsave('z-scipy.png', Zimg)
Sign up to request clarification or add additional context in comments.

3 Comments

Hi, Z is a one dimensional array, just like X and Y.
Thank you for your suggestion. I guess that would work, however, I was hoping for a more pythonic way of doing it. But until I find something suitable, I'll stick to your code.
If there is a large amount of data, consider using Pandas groupby method to speed up this operation: pandas.pydata.org/pandas-docs/stable/groupby.html
1

You said you needed no interpolation is needed since every grid point is covered. So I assume the points are equally spaced.

If your table is already sorted primary by increasing x and secondary by y you can simply take the Z array directly and save it using PIL:

import numpy as np
# Find out what shape your final array has (if you already know just hardcode these)
x_values = np.unique(X).size 
y_values = np.unique(Y).size 
img = np.reshape(Z, (x_values, y_values))

# Maybe you need to cast the dtype to fulfill png restrictions
#img = img.astype(np.uint) # alter it if needed

# Print image
from PIL import Image
Image.fromarray(img).save('filename.png')

If your input isn't sorted (it looks like it is but who knows) you have to sort it before you start. Depending on your input this can be easy or really hard.

8 Comments

reshape() is what I was looking for. Perfect, now it's a one-liner as I can simply do np.unique(x).size... Thank you!
I can't understand how this code works. y_values is a tuple, how reshape can manage that ???
Thanks. But I think it works only if Z.size =x_values*y_values, what is not garanteed if there are duplicates.....
From the question "each grid point (X, Y) has an intensity Z" and "X and Y are guaranteed to cover each grid point between min(X) and max(Y)" so it sounds like there are no duplicates and no missing values.
Ok, probably for the OP. that's not the case in the sample data.
|
0

np.ufunc.at is a good tool to manage duplicates in vectorized way.

Suppose these data :

In [3]: X,Y,Z=rand(3,10).round(1)

(array([ 0.4,  0.2,  0.1,  0.8,  0.4,  0.1,  0.5,  0.2,  0.6,  0.2]),
 array([ 0.5,  0.3,  0.5,  0.9,  0.9,  0.5,  0.3,  0.6,  0.4,  0.4]),
 array([ 0.4,  0.6,  0.6,  0.4,  0.1,  0.1,  0.2,  0.6,  0.9,  0.8]))

First scale the image (scale=3 here) :

In [4]: indices=[ (3*c).astype(int) for c in (X,Y)]

[array([1, 0, 0, 2, 1, 0, 1, 0, 1, 0]), array([1, 0, 1, 2, 2, 1, 0, 1, 1, 1])]

Make a empty image : image=zeros((3,3)), according to indices bounds.

Then build. Here we keep the maximum.

In [5]: np.maximum.at(image,indices,Z)  # in place

array([[ 0.6,  0.8,  0. ],
       [ 0.2,  0.9,  0.1],
       [ 0. ,  0. ,  0.4]])

Finally save in PNG : matplotlib.pyplot.imsave('img.png',image)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.