I have a large 2D array (4000x3000) saved as a numpy array which I would like to display and save while keeping the ability to look at each individual pixels.
For the display part, I currently use matplotlib imshow() function which works very well.
For the saving part, it is not clear to me how I can save this figure and preserve the information contained in all 12M pixels. I tried adjusting the figure size and the resolution (dpi) of the saved image but it is not obvious which figsize/dpi settings should be used to match the resolution of the large 2D matrix displayed. Here is an example code of what I'm doing (arr is a numpy array of shape (3000,4000)):
fig = pylab.figure(figsize=(16,12))
pylab.imshow(arr,interpolation='nearest')
fig.savefig("image.png",dpi=500)
One option would be to increase the resolution of the saved image substantially to be sure all pixels will be properly recorded but this has the significant drawback of creating an image of extremely large size (at least much larger than the 4000x3000 pixels image which is all that I would really need). It also has the disadvantage that not all pixels will be of exactly the same size.
I also had a look at the Python Image Library but it is not clear to me how it could be used for this purpose, if at all.
Any help on the subject would be much appreciated!
I think I found a solution which works fairly well. I use figimage to plot the numpy array without resampling. If you're careful in the size of the figure you create, you can keep full resolution of your matrix whatever size it has.
I figured out that figimage plots a single pixel with size 0.01 inch (this number might be system dependent) so the following code will for example save the matrix with full resolution (arr is a numpy array of shape (3000,4000)):
rows = 3000
columns = 4000
fig = pylab.figure(figsize=(columns*0.01,rows*0.01))
pylab.figimage(arr,cmap=cm.jet,origin='lower')
fig.savefig("image.png")
Two issues I still have with this options:
there is no markers indicating column/row numbers making it hard to know which pixel is which besides the ones on the edges
if you decide to interactively look at the image, it is not possible to zoom in/out
A solution that also solves the above 2 issues would be terrific, if it exists.
The OpenCV library was designed for scientific analysis of images. Consequently, it doesn't "resample" images without your explicitly asking for it. To save an image:
import cv2
cv2.imwrite('image.png', arr)
where arr is your numpy array. The saved image will be the same size as your array arr.
You didn't mention the color-model that you are using. Pngs, like jpegs, are usually 8-bit per color channel. OpenCV will support up to 16-bits per channel if you request it.
Documentation on OpenCV's imwrite is here.
Related
I am working on a dataset that has two features, real and imaginary impedances. I applied data-to-image conversion using MTF in order to represent each one as an image (50x50). I was thinking of creating a 3-D image (50x50x2). I tried doing
Image = np.array([tag_gadf_re[0],tag_gadf_im[0]])
where tag_gadf_re[0] and tag_gadf_im[0] are the real and imaginary impedance image arrays. However, I tried saving the image using:
plt.imsave("Directory", Image)
However, I am getting the following error:
ValueError: Third dimension must be 3 or 4
Also note that the shape of Image is (2x50x50), when it should be (50x50x2). The solution seems simple, but I am a bit lost in the process. How can I combine both arrays appropriately and save the image, or do I need a 3rd layer in order to appropriately represent it as an RGB image?
If you want to store data as an image you need to be aware of its type and range so that you can choose an appropriate format. You also need to be aware of whether you can tolerate a "lossy" format which, when read, will not return identical values to those you stored.
If your data is integer and 16-bit or less, you can store it in a PNG. If it's multi-channel and 16-bit, you'll come unstuck with PIL. You can use tifffile though to store a 2-channel TIFF - maybe that can be greyscale + transparency or maybe 2 IFDs.
If your data is floating point, you pretty much have to use TIFF, PFM or EXR format. Again, tifffile can do this for you.
tifffile is here.
wand can also do whatever tifffile can do.
Of course, you might choose to represent your two arrays/images as one above the other in a double-height image. It's your data.
I am currently working on a summer research project and we have generated 360 slices of a tumor. I now need to compile (if that's the right word) these images into one large 3D image. Is there a way to do this with either a python module or an outside source? I would prefer to use a free software if that is possible.
Perhaps via matplotlib, but anyway may require preprocessing I suppose:
https://www.youtube.com/watch?v=5E5mVVsrwZw
In your case, the z axis (3rd dimension) should be specified by your vector of images. Nonetheless, before proceeding, I suppose you would need to extract the shapes of the object you want to reconstruct. For instance, if i take any image of the many 2D you have, I expect to find RGB value for each pixel, but then, for instance if you want to plot a skull like in the video link, as I understand you would need to extract the borders of your object and from each of its frame (2D shape) and then plot their serie. But anyway, the processing may depend on the encoding of the information you have. Perhaps is sufficient to simply plot the series of images.
Some useful link I found:
https://www.researchgate.net/post/How_to_reconstruct_3D_images_from_two_or_four_2D_images
Python: 3D contour from a 2D image - pylab and contourf
I'm trying to use blob detection in scikit-image. blob_log is supposed to return an array of either Nx3 for a 2D image, or an Nx4 for a 3D image (?) The first two values in a 2D image are the (x, y, sigma) and in 3D are (p, x, y sigma)
I'm attempting to load this image into my code which looks like it has quite a few observable blobs & it is a 2D image.
I've got a few questions:
(1) the blob_log is returning a Nx4 array which means its loading it as a 3D image. When I try to print it, it looks like its just a bunch of empty arrays which I don't understand how because when I plt.show() it is a 2D image.
(2) If N is the number of blobs it has calculated, then it is only giving me less than 10% of the total images - I believe this is due to the fact that the image is on a white background making it more difficult for the blob_log to notice - is that correct?
(3) I don't understand how the for loop at the end of the Blob documentation works? How is it plotting the circles over the image? I'm sorry if this is an elementary question, but its frustrating me because I think that would help me with some of the other things I was wondering about.
Attempts to figure out what is going on:
(1) Loading data.coins() and printing it gives me a nice array of values which I assume are the 2D, it still doesn't explain why the image I want to load isn't being recognized as 2D.
(2) I tried to load the data.coins() which should be an obviously provided image with circular images and fooled around with the sigma and threshold settings, I'm getting a variety of different values depending on the settings - is there a good way of figuring out which are the best without having to fool around with the settings until I get one that works?
Due to the length of my code and my question, below is just the applicable parts, but my entire code can be found here
from skimage import data, feature, exposure, io
import matplotlib
import matplotlib.pyplot as plt
img = data.coins()
#img = io.imread('gfp_test.png') #this is the image I linked above just in my dir
print(img)
print(type(img))
A = feature.blob_log(imgG, max_sigma = 30, num_sigma = 10, threshold = .4)
print (A)
Thank you for your help!
(1) You have a color image, while blob_* expect a grayscale image. Use skimage.color.rgb2gray to convert your image to grayscale before using the blob finding functions. See our crash course on NumPy for images for more details.
(2) Let's see if the above fixes your problem. I think blob finding is a local operation, so the white frame around the edges is probably not a problem.
(3) Yes, the variable naming could be clearer. The key is here: sequence = zip(blobs_list, colors, titles). If you look at what those individual variables are, they are length-3 lists with the results from the three different blob-finding methods, three different colors, and three different titles (the names of the three methods). So the outer for-loop is iterating through the methods, and the three panels of the figure. (You should look at the matplotlib documentation for subplots for more on this.)
The inner loop, then, is going through the results of a single blob-finding method and putting a circle around each result. You'll see the x/y transposed, and this is a consequence of the different coordinate conventions between our images (see the crash course linked above) and the matplotlib canvas. Then we create a circle with the appropriate radius for each blob, and add it to the matplotlib axes. See the examples linked from the Circle documentation for more information on adding patches.
Hope this helps!
I am saving images using
import matplotlib.pyplot as plt
plt.imsave(img_path,img_arr,cmap = 'gray') #shape (512,512)
...
img = plt.imread(img_path)
and the img.shape returns a (512,512,4) whereas i expect it to only be (512,512).
I thought maybe all the channels would be the same so I could just pick one but np.allclose(img[:,:,0],img_arr)
returns false no matter which index I choose. Printing the images, they are indeed the correct ones I am comparing as they do look almost identical(by eye), but are obviously not exactly identical.
I also tried saving the images with cv2 but that seems to save a black box for some reason. loading them with cv2.imread(img_path,0) does return a (512,512) array but something seems to be lost because again, np.allclose() tells me they're different.
I wanted to know if there is a good way to save grayscale images? Every method I try seems to convert it to RBG or RGBA which is really annoying. Also, I would like to preserve the dtype (int16) of the image as downsampling it loses important information.
Thanks in advance.
You cannot preserve a bit depth of 16 bit when saving images with matplotlib with any of the default colormaps which only have 256 colors (=8 bit).
And in addition, matplotlib converts the pixel values to floats, which may be a source for rounding errors.
In total, matplotlib does not seem to be the optimal tool in case you need to get perfect accuracy.
That being said, even PIL does not seem to allow for 16 bit single channel pngs. There is a possible solution in this question, but I haven't tested it.
In any case a bulletproof way to save your array without accuracy loss is to save it with numpy, np.save("arr.npy", im_arr).
I want to display an image file using imshow. It is an 1600x1200 grayscale image and I found out that matplotlib uses float32 to decode the values. It takes about 2 seconds to load the image and I would like to know if there is any way to make this faster. The point is that I do not really need a high resolution image, I just want to mark certain points and draw the image as a background. So,
First question: Is 2 seconds a good performance for such an image or
can I speed up.
Second question: If it is good performance how can I make the process
faster by reducing the resolution. Important point: I still want the
image to strech over 1600x1200 Pixel in the end.
My code:
import matplotlib
import numpy
plotfig = matplotlib.pyplot.figure()
plotwindow = plotfig.add_subplot(111)
plotwindow.axis([0,1600,0,1200])
plotwindow.invert_yaxis()
img = matplotlib.pyplot.imread("lowres.png")
im = matplotlib.pyplot.imshow(img,cmap=matplotlib.cm.gray,origin='centre')
plotfig.set_figwidth(200.0)
plotfig.canvas.draw()
matplotlib.pyplot.show()
This is what I want to do. Now if the picture saved in lowres.png has a lower resolution as 1600x1200 (i.e. 400x300) it is displayed in the upper corner as it should. How can I scale it to the whole are of 1600x1200 pixel?
If I run this program the slow part comes from the canvas.draw() command below. Is there maybe a way to speed up this command?
Thank you in advance!
According to your suggestions I have updated to the newest version of matplotlib
version 1.1.0svn, checkout 8988
And I also use the following code:
img = matplotlib.pyplot.imread(pngfile)
img *= 255
img2 = img.astype(numpy.uint8)
im = self.plotwindow.imshow(img2,cmap=matplotlib.cm.gray, origin='centre')
and still it takes about 2 seconds to display the image... Any other ideas?
Just to add: I found the following feature
zoomed_inset_axes
So in principle matplotlib should be able to do the task. There one can also plot a picture in a "zoomed" fashion...
The size of the data is independent of the pixel dimensions of the final image.
Since you say you don't need a high-resolution image, you can generate the image quicker by down-sampling your data. If your data is in the form of a numpy array, a quick and dirty way would be to take every nth column and row with data[::n,::n].
You can control the output image's pixel dimensions with fig.set_size_inches and plt.savefig's dpi parameter:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
data=np.arange(300).reshape((10,30))
plt.imshow(data[::2,::2],cmap=cm.Greys)
fig=plt.gcf()
# Unfortunately, had to find these numbers through trial and error
fig.set_size_inches(5.163,3.75)
ax=plt.gca()
extent=ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.savefig('/tmp/test.png', dpi=400,
bbox_inches=extent)
You can disable the default interpolation of imshow by adding the following line to your matplotlibrc file (typically at ~/.matplotlib/matplotlibrc):
image.interpolation : none
The result is much faster rendering and crisper images.
I found a solution as long as one needs to display only low-resolution images. One can do so using the line
im = matplotlib.pyplot.imshow(img,cmap=matplotlib.cm.gray, origin='centre',extent=(0,1600,0,1200))
where the extent-parameter tells matplotlib to plot the figure over this range. If one uses an image which has a lower resolution, this speeds up the process quite a lot. Nevertheless it would be great if somebody knows additional tricks to make the process even faster in order to use a higher resolution with the same speed.
Thanks to everyone who thought about my problem, further remarks are appreciated!!!