Displaying different images with actual size in matplotlib subplot
Asked Answered
I

5

29

I'm working on some image processing algorithms using python and matplotlib. I'd like to display the original image and the output image in a figure using a subplot (e.g. the original image next to the output image). The output image(s) are of different size than the original image. I'd like to have the subplot display the images in their actual size (or uniformly scaled) so that I can compare "apples to apples". I currently use:

plt.figure()
plt.subplot(2,1,1)
plt.imshow(originalImage)
plt.subplot(2,1,2)
plt.imshow(outputImage)
plt.show()

The result is that I get the subplot, but both images are scaled so that they are the same size (despite the fact that the axes on the output image are different than the axes of the input image). Just to be explicit: if the input image is 512x512 and the output image is 1024x1024 then both images are displayed as though they are the same size.

Is there a way to force matplotlib to either display the images at their respective actual sizes (preferable solution so that matplotlib's dynamic rescaling doesn't effect the displayed image) or to scale the images such that they are displayed with sizes proportional to their actual sizes?

Interline answered 2/3, 2015 at 17:35 Comment(3)
I think figimage may be useful to you ... this question possibly a duplicate of this question...Flashgun
Thank you. I will check it out. Yes looks like a duplicateish post. Guess I didn't see that one when searching. Thanks!Interline
use sharex and sharey to share axes. See my answer belowLehet
B
23

This is the answer that you are looking for:

def display_image_in_actual_size(im_path):

    dpi = 80
    im_data = plt.imread(im_path)
    height, width, depth = im_data.shape

    # What size does the figure need to be in inches to fit the image?
    figsize = width / float(dpi), height / float(dpi)

    # Create a figure of the right size with one axes that takes up the full figure
    fig = plt.figure(figsize=figsize)
    ax = fig.add_axes([0, 0, 1, 1])

    # Hide spines, ticks, etc.
    ax.axis('off')

    # Display the image.
    ax.imshow(im_data, cmap='gray')

    plt.show()

display_image_in_actual_size("./your_image.jpg")

Adapted from here.

Bovid answered 18/2, 2017 at 11:53 Comment(3)
The question asked for the case of displaying two images side-by-side in one figure.Bligh
Please read the question again. It says "different images". Yor code uses a single image.. Which is OK but not what the question asks. I see 22 upvotes eheras your answer should be instead downvoted (which I didn't).Olinger
No way. The displayed image is much larger! Besides, the question asks for TWO IMAGES. (I really wonder how totally wrong and failing answers like this one are upvoted instead of being downvoted!)Olinger
L
14

If you are looking to show images at their actual size, so the actual pixel size is the same for both images in subplots, you probably just want to use the options sharex and sharey in the subplot definition

fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(15, 7), dpi=80, sharex=True, sharey=True)
ax[1].imshow(image1, cmap='gray')
ax[0].imshow(image2, cmap='gray')

results in:

enter image description here

Where the second image is 1/2 size of the first one.

Lehet answered 24/1, 2019 at 6:9 Comment(7)
How do you add a title for each plot ?Opuntia
Take eaxh ax[n] object and use the title functionLehet
How do you adjust the axes of the small picture to match its smaller size ?Appendix
What do you mean? The trick here uses sharex and sharey to share the axes so the images show at the right scale. That is, same axes = real pixel size.Lehet
The other trick is to call the imshow with the larger picture last, as that will define the width/height of both plots. Right?Athanasia
This is not only definitely the correct answer but also an elegant one. It should get the most upvotes! But it is a common phenomenon in this place that wrong answers get the most upvotes. This is a mystery to me ...Olinger
Where do you base the figure size on (15, 7)? Shouldn't you consult the image dimensions? And then, why do you set a dpi of 80 and not 100, which makes for smaller figure size (if you consult image dimensions)?Olinger
I
13

Adapting Joseph's answer here: Apparently the default dpi changed to 100, so to be safe in the future you can directly access the dpi from the rcParams as

import matplotlib as mpl
import matplotlib.pyplot as plt

def display_image_in_actual_size(im_path):

    dpi = mpl.rcParams['figure.dpi']
    im_data = plt.imread(im_path)
    height, width, depth = im_data.shape

    # What size does the figure need to be in inches to fit the image?
    figsize = width / float(dpi), height / float(dpi)

    # Create a figure of the right size with one axes that takes up the full figure
    fig = plt.figure(figsize=figsize)
    ax = fig.add_axes([0, 0, 1, 1])

    # Hide spines, ticks, etc.
    ax.axis('off')

    # Display the image.
    ax.imshow(im_data, cmap='gray')

    plt.show()

display_image_in_actual_size("./your_image.jpg")
Insipid answered 17/12, 2018 at 13:33 Comment(5)
this is useful but could be shorter by just saying that the default dpi can be accessed with dpi = matplotlib.rcParams['figure.dpi']Negativism
True, but I mainly wanted to accomodate copy-pasters here to have a ready-made function. I agree it's bloated.Insipid
To define plt add from matplotlib import pyplot as plt at the top.Steamy
You adapted the wrong answer! That answer is not what the question asks.Olinger
That answer is what people are looking for when they land here through Google, which is how most people use stackoverflow. It's how I found this thread 6 years ago, 3 years after the question was asked. And apparently people found it useful, otherwise they wouldn't upvote. I suggest to accept that this is how people use this site instead of berating them. Congrats on providing the correct answer to the original question. You could've left it at that instead of going into the comments of other answers.Insipid
O
0

One must consult the image dimensions and use the larger ones in order to create an apprropriate figure size. So, here we go:

dpi = 100

(h1,w1),(h2,w2) = originalImage.shape[:2],outputImage.shape[:2]
w,h = max(w1,w2),max(h1,h2) # Get largest width and largest height
figsize = (2*(w/dpi),h/dpi) # Set the figure size acc/ly

fig, ax = plt.subplots(nrows=1, ncols=2, figsize=figsize, dpi=dpi, sharex=True, sharey=True)
ax[0].imshow(originalImage, cmap='gray')
ax[1].imshow(outputImage, cmap='gray')
plt.show()
Olinger answered 17/3 at 11:30 Comment(0)
C
0

Here is my solution, the basic idea comes from @MrE. However, in my case the relative size among figures across height and width may be different, thus whatever the display order we choose, either width or height of the first figure will become incomplete. (In another word, fig1's height is greater than fig2, while the fig1's width is smaller than fig2.)

The situation becomes more tricky when we need to display multiple images, and we couldn't simply reverse the display order of the images since the relative relationship of size is agnostic within a batch loop.

In this case, we could declare a blank image with the maximum height and width among all of the iamges, and don't neccessarily display it by set_visible to False.

def plot_image(ax, rgb_array, title, subplot_index):
   image = Image.fromarray(rgb_array)
   ax[subplot_index].imshow(image)
   ax[subplot_index].set_title(title + f" {rgb_array.shape}", fontsize=22)
   ax[subplot_index].axis("off")
   return None

# Assuming resize_array is already defined
max_height = max(rgb_array.shape[0], resize_array.shape[0])
max_width = max(rgb_array.shape[1], resize_array.shape[1])

fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(max_height//10, max_width//10), dpi=80, sharex=True, sharey=True)
    
plot_image(ax, rgb_array, "original", 0)
plot_image(ax, resize_array, "resized", 1)
    
# larger one should be plotted on the later
blank_array = np.full((max_height, max_width, 3), 255, dtype=np.uint8) 
plot_image(ax, blank_array, "blank", 2)
ax[2].set_visible(False)

btw, the figure size is irrelevant to the problem due to the autoscale mechanism, the only trick here is to display the largest one last contributed by @MrE based on my tests.

Cetus answered 10/5 at 3:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.