在一个 IPython 笔记本单元格中显示多个图像?

如果有多个图像(以 NumPy 数组的形式加载) ,如何在一个 IPython Notebook 单元格中显示图像?

我知道我可以使用 plt.imshow(ima)显示 图像... 但我想显示一个以上的时间。

我试过了:

 for ima in images:
display(Image(ima))

但我刚刚得到一个破碎的图像链接:

enter image description here

113087 次浏览

Short answer:

call plt.figure() to create new figures if you want more than one in a cell:

for ima in images:
plt.figure()
plt.imshow(ima)

But to clarify the confusion with Image:

IPython.display.Image is for displaying Image files, not array data. If you want to display numpy arrays with Image, you have to convert them to a file-format first (easiest with PIL):

from io import BytesIO
import PIL
from IPython.display import display, Image


def display_img_array(ima):
im = PIL.Image.fromarray(ima)
bio = BytesIO()
im.save(bio, format='png')
display(Image(bio.getvalue(), format='png'))


for ima in images:
display_img_array(ima)

A notebook illustrating both approaches.

You can display multiple images in one IPython Notebook cell by using display and HTML functions. You need to create the set of html img tags as a string as follows

from IPython.display import Image, HTML, display
from glob import glob
imagesList=''.join( ["<img style='width: 120px; margin: 0px; float: left; border: 1px solid black;' src='%s' />" % str(s)
for s in sorted(glob('yourimage*.png')) ])
display(HTML(imagesList))

See a example of use from http://nbviewer.ipython.org/github/PBrockmann/Dodecahedron

You may need to refresh your browser (shift + load) to see new images if they have been changed from a previous cell.

This is easier and works:

from IPython.display import Image
from IPython.display import display
x = Image(filename='1.png')
y = Image(filename='2.png')
display(x, y)

Somehow related to this question (and since I was directed to this answer when I was trying to solve it), I was able to solve a similar problem by simply typing the full file-path when calling Image(). In my case, I had to choose a random image from different folder paths stored in a list your_folder and display them.

import random, os
for i in range(len(your_folder)):
ra1 = "../"+your_folder[i]+"/"+random.choice(os.listdir(your_folder[i]))
image = Image(ra1)
display(image)

Horizontal layout

Horizontal layout demonstration

Short answer

plt.figure(figsize=(20,10))
columns = 5
for i, image in enumerate(images):
plt.subplot(len(images) / columns + 1, columns, i + 1)
plt.imshow(image)

Long answer

import glob
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
%matplotlib inline


images = []
for img_path in glob.glob('images/*.jpg'):
images.append(mpimg.imread(img_path))


plt.figure(figsize=(20,10))
columns = 5
for i, image in enumerate(images):
plt.subplot(len(images) / columns + 1, columns, i + 1)
plt.imshow(image)

enter image description here

from matplotlib.pyplot import figure, imshow, axis
from matplotlib.image import imread


mypath='.'
hSize = 5
wSize = 5
col = 4


def showImagesMatrix(list_of_files, col=10):
fig = figure( figsize=(wSize, hSize))
number_of_files = len(list_of_files)
row = number_of_files/col
if (number_of_files%col != 0):
row += 1
for i in range(number_of_files):
a=fig.add_subplot(row,col,i+1)
image = imread(mypath+'/'+list_of_files[i])
imshow(image,cmap='Greys_r')
axis('off')


showImagesMatrix(listOfImages,col)

based on @Michael answer

based on @ChaosPredictor answer

from matplotlib.pyplot import figure, imshow, axis
from matplotlib.image import imread


def showImagesMatrix(list_of_files, col=10, wSize=5, hSize=5, mypath='.'):
fig = figure(figsize=(wSize, hSize))
number_of_files = len(list_of_files)
row = number_of_files / col
if (number_of_files % col != 0):
row += 1
for i in range(number_of_files):
a=fig.add_subplot(row, col, i + 1)
image = imread(mypath + '/' + list_of_files[i])
imshow(image, cmap='Greys_r')
axis('off')

then

from pathlib import Path
p = Path('.')
num_images = 30
list_of_image_paths = [str(x) for x in list(p.glob('../input/train/images/*'))[:num_images]]


showImagesMatrix(list_of_image_paths)


# or with named args
showImagesMatrix(list_of_image_paths, wSize=20, hSize=10, col=5)

matplotlib image grid

If you don't mind an additional dependency here is a two liner using scikit-image:

from skimage.util import montage
plt.imshow(montage(np.array(images), multichannel=True))

Set multichannel=True for color images and multichannel=False for grayscale images.

The answers in this thread helped me: Combine several images horizontally with Python

The problem of using matplotlib was the displayed images' definition was really bad. I adapted one of the answers there to my needs:

The following code displays the images concatenated horizontaly in a jupyter notebook. Notice the commented line with the code to save the image if you'd like that.

import numpy as np
import PIL
from IPython.display import display


list_im = ['Test1.jpg', 'Test2.jpg', 'Test3.jpg']
imgs    = [ PIL.Image.open(i) for i in list_im ]
# pick the image which is the smallest, and resize the others to match it (can be arbitrary image shape here)
min_shape = sorted( [(np.sum(i.size), i.size ) for i in imgs])[0][1]
imgs_comb = np.hstack( (np.asarray( i.resize(min_shape) ) for i in imgs ) )


# save that beautiful picture
imgs_comb = PIL.Image.fromarray( imgs_comb)
#imgs_comb.save( 'combo.jpg' )


display(imgs_comb)

You can do it really fast and easy with IPyPlot:

import ipyplot


ipyplot.plot_images(images_array, max_images=20, img_width=150)

You would get a plot similar to this:
enter image description here

It's using IPython.display and HTML under the hood and it can take images in following formats:

  • string file paths
  • PIL.Image objects
  • numpy.ndarray objects representing images

It would take just a few seconds to display a numpy array of 500 images

Today, I faced this problem in google Colab and jupyter notebook and I would share a simple solution on MNIST dataset:

for index in range(1,6):
plt.imshow(train_set.dataset[index], cmap='gray')

The output only shows the last image:

enter image description here

Because the cell in both IDEs shows only the last image. Thus I added plt.show() to fix this issue:

for index in range(1,6):
plt.imshow(train_set.dataset[index], cmap='gray')
plt.show()

The output:

enter image description here

Last comment: I like this way because I can add input() and plot() functions together in the loop not like other methods I tried.

It's been a few years now since this question has been asked, but it's sad that this feature has not managed to get into the core of jupyter notebooks.

I would expect an imshow function that's simple enough that

  • accepts a single or multiple images as input
  • layouts them neatly for me even if there are 100 images

and looks something like:

imshow(img)
imshow(img1, img2)
imshow(*imgs[:100])

Some nice to have optional arguments:

  • title
  • size
  • rows and/or columns
  • cmap

Unfortunately, that function doesn't as part of the notebooks. Hopefully, that will be there in the future. For now, I have that exact function imshow implemented in the imshowtools package. It uses matplotlib under the hood.

Say you have MNIST and imshow loaded using

from imshowtools import imshow
import tensorflow as tf


(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

Then you can simply use

imshow(x_train[0])
imshow(x_train[0], x_train[1], x_train[2])
imshow(*x_train[:20], cmap='binary')
imshow(*x_train[:100], cmap='binary', size=(10, 10))

mnist_img

Sometimes, you want them in one row or in one column:

imshow(*x_train[:15], cmap='Purples', rows=1)
imshow(*x_train[:24], cmap='Greens', columns=4)

mnist_rows_cols

I used @dval suggestion to display network graphs build with graphviz, and it works directly (no need to create image)

from IPython.display import display
dot1 = Digraph()


# Add nodes
dot1.node('1')
dot1.node('3')
dot1.node('2')
dot1.node('5')


# Add edges
dot2= dot1.copy()
dot2.edges(['12', '13', '35'])
 

display(dot1,dot2)

enter image description here