ppinard / matplotlib-scalebar

Provides a new artist for matplotlib to display a scale bar, aka micron bar.
BSD 2-Clause "Simplified" License
146 stars 29 forks source link

Save image in original resolution #42

Open kolibril13 opened 3 years ago

kolibril13 commented 3 years ago

Hi! I want to save my plot with the ScaleBar, here is my code:

fig, ax = plt.subplots()
ax.axis("off")
ax.imshow(my_slice3, cmap="gray")
scalebar = ScaleBar(650, "nm", length_fraction=0.25, location= "lower right")
ax.add_artist(scalebar)
plt.savefig('foo.png')

When I save the image, the resolution is not anymore the resolution of the original image. At Stack Overflow I found the hint, that one can use imsave , but that is not working with the Scalebar. https://stackoverflow.com/questions/31544130/saving-an-imshow-like-image-while-preserving-resolution I know that I can increase the resolution e.g. with plt.rcParams['figure.dpi'] = 300 . But that is not leading to the exact pixel-by-pixel image. Any idea how to solve this?

hakonanes commented 3 years ago

Hi @kolibril13!

I won't say it's not possible, but I've never seen a way to do it. I say this as a daily user of Matplotlib. savefig() is ment to store the figure with labels, annotations, scalebars (!), and much much more (documentation), while imsave() cannot store any of those things (documentation).

Could I ask why you would want this? I assume the scalebar would look quite pixelated?

kolibril13 commented 3 years ago

hello @hakonanes !

Thanks for your explanation. What I am looking for is to use savefig() with my image and ScaleBar then. The reason I want to do this: I want to show images with a scalebar on a poster. As these images have a resolution about 2000x2000 pixel, I don't want to lose image quality when adding the scalebar. As I am making the poster with a vector program, I can simply save the image as a pdf. The image is then a raster image, and the Scalebar is still in vector format.

hakonanes commented 3 years ago

As these images have a resolution about 2000x2000 pixel, I don't want to lose image quality when adding the scalebar.

Aha, the scalebar would not be pixelated, then, wrong assumption on my end...

As I am making the poster with a vector program, I can simply save the image as a pdf. The image is then a raster image, and the Scalebar is still in vector format.

I would increase the dots per inch (DPI), as you do, to ensure that you don't loose much pixel resolution. Apart from that, I don't know what else can help, unfortunately. It might be that just adding a custom scalebar in your program yourself is the simplest solution when you want to keep the exact image resolution.

kolibril13 commented 3 years ago

Here is a solution I found:

  1. Find out display dpi here: https://dpi.lv/
  2. Run this script:
    
    import matplotlib.pyplot as plt
    import numpy as np

def export_figure_matplotlib(arr, f_name, dpi=200, resize_fact=1, plt_show=False): """ Export array as figure in original resolution :param arr: array of image to save in original resolution :param f_name: name of file where to save figure :param resize_fact: resize facter wrt shape of arr, in (0, np.infty) :param dpi: dpi of your screen :param plt_show: show plot or not """ fig = plt.figure(frameon=False) fig.set_size_inches(arr.shape[1]/dpi, arr.shape[0]/dpi) ax = plt.Axes(fig, [0., 0., 1., 1.]) ax.set_axis_off() fig.add_axes(ax) ax.imshow(arr) scalebar = ScaleBar(650, "nm", length_fraction=0.25, location= "lower right") ax.add_artist(scalebar) plt.savefig(f_name, dpi=(dpi * resize_fact)) if plt_show: plt.show() else: plt.close()

export_figure_matplotlib(my_slice3, "hello2.pdf", dpi=298, resize_fact=1, plt_show=True)


Inspired by https://stackoverflow.com/a/51438809/6933377
ppinard commented 2 years ago

Thank you for sharing the code. I would propose to add this snippet to the README. Can I clarify two things?

plt.figure(figsize=(image_width / dpi, image_height / dpi), frameon=False, dpi=dpi)
ax = fig.add_axes([0.0, 0.0, 1.0, 1.0])

So I would go with this snippet if @kolibril13 agrees:

fig = plt.figure(figsize=(arr.shape[1] / dpi, arr.shape[0] / dpi), frameon=False, dpi=dpi)

ax = fig.add_axes([0.0, 0.0, 1.0, 1.0])
ax.set_axis_off()

ax.imshow(arr)

scalebar = ScaleBar(650, "nm", length_fraction=0.25, location=  "lower right")
ax.add_artist(scalebar)