Skip to content

Commit 0d9820d

Browse files
committed
add 1st version of plotting
1 parent 532e945 commit 0d9820d

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

ipyplot/plotting.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import base64
2+
import io
3+
import uuid
4+
5+
import numpy as np
6+
from numpy import str_
7+
from PIL import Image
8+
9+
from .img_helpers import resize_with_aspect_ratio
10+
11+
try:
12+
from IPython.display import display, HTML
13+
except Exception as e:
14+
raise Exception('IPython not detected. Plotting without IPython is not possible') # NOQA E501
15+
16+
17+
def plot_class_tabs(images, labels, max_imgs_per_tab=50, img_width=220):
18+
"""
19+
Efficient and convenient way of displaying images
20+
grouped by labels/clusters.
21+
It's using IPython.display function and HTML
22+
23+
Args:
24+
images (numpy.ndarray):
25+
Numpy array of image file paths or PIL.Image objects
26+
labels (numpy.ndarray):
27+
Numpy array of corresponding classes
28+
max_imgs_per_tab (int, optional):
29+
How many samples from each cluster/class to display.
30+
Defaults to 50.
31+
img_width (int, optional):
32+
Image width.
33+
Adjust to change the number of images per row.
34+
Defaults to 220.
35+
"""
36+
assert(len(images) == len(labels))
37+
assert(type(images) is np.ndarray)
38+
assert(type(labels) is np.ndarray)
39+
assert(type(max_imgs_per_tab) is int)
40+
assert(type(img_width) is int)
41+
42+
html = _create_tabs_html(images, labels, max_imgs_per_tab, img_width)
43+
44+
_display(html)
45+
46+
47+
def plot_images(images, labels=None, max_images=100, img_width=300):
48+
"""
49+
Displays images based on the provided paths
50+
51+
Args:
52+
images (array):
53+
Array of coresponding file paths.
54+
labels (array, optional):
55+
Array of labels/cluster names.
56+
If None it will just assign numbers going from 0
57+
Defaults to None.
58+
max_images (int, optional):
59+
Max number of images to display.
60+
Defaults to 100.
61+
img_width (int, optional):
62+
Width of the displayed image.
63+
Defaults to 300.
64+
"""
65+
assert(type(max_images) is int)
66+
assert(type(img_width) is int)
67+
68+
if labels is None:
69+
labels = list(range(0, len(images)))
70+
html = _create_imgs_list_html(images, labels, max_images, img_width)
71+
72+
_display(html)
73+
74+
75+
def plot_class_representations(
76+
images, labels,
77+
ignore_list=['-1', 'unknown'],
78+
img_width=150):
79+
"""
80+
Function used to display first image from each cluster/class
81+
82+
Args:
83+
images (array):
84+
Array of image file paths or PIL.Image objects.
85+
labels (array):
86+
Array of labels/classes names.
87+
ignore_list (list, optional):
88+
List of classes to ignore when plotting.
89+
Defaults to ['-1', 'unknown'].
90+
img_width (int, optional):
91+
Width of the displayed image.
92+
Defaults to 150.
93+
"""
94+
assert(len(images) == len(labels))
95+
assert(type(ignore_list) is list or ignore_list is None)
96+
assert(type(img_width) is int)
97+
98+
uniques = np.unique(labels, return_index=True)
99+
labels = uniques[0]
100+
not_labeled_mask = np.isin(labels, ignore_list)
101+
labels = labels[~not_labeled_mask]
102+
idices = uniques[1][~not_labeled_mask]
103+
104+
group = []
105+
for img_path in images[idices]:
106+
group.append(img_path)
107+
108+
plot_images(
109+
group,
110+
labels=labels,
111+
max_images=len(group),
112+
img_width=img_width)
113+
114+
115+
def _create_tabs_html(images, labels, max_imgs_per_tab, img_width):
116+
html = '<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>' # NOQA E501
117+
html += '<div><ul class="nav nav-pills">'
118+
119+
unique_labels = np.unique(labels)
120+
tab_ids = [uuid.uuid4() for label in unique_labels]
121+
122+
active_tab = True
123+
for i, label in zip(tab_ids, unique_labels):
124+
html += '<li class="active">' if active_tab else '<li>'
125+
active_tab = False
126+
html += '<a href="#%s" data-toggle="tab">%s</a></li>' % (i, label)
127+
html += '</ul><div class="tab-content clearfix">'
128+
129+
active_tab = True
130+
for i, label in zip(tab_ids, unique_labels):
131+
html += '<div class="tab-pane active" id="%s">' % i if active_tab else '<div class="tab-pane" id="%s">' % i # NOQA E501
132+
active_tab = False
133+
img_ids = list(range(0, len(images)))
134+
html += ''.join([
135+
_create_img_html(x, img_width, label=y)
136+
for x, y in zip(images[labels == label][:max_imgs_per_tab], img_ids)
137+
])
138+
html += '</div>'
139+
140+
html += '</div></div>'
141+
142+
return html
143+
144+
145+
def _display(html):
146+
return display(HTML(html))
147+
148+
149+
def _img_to_base64(image, max_size):
150+
if type(image) is np.ndarray:
151+
image = Image.fromarray(image)
152+
output = io.BytesIO()
153+
image = resize_with_aspect_ratio(image, max_size)
154+
image.save(output, format='PNG')
155+
b64 = str(base64.b64encode(output.getvalue()).decode('utf-8'))
156+
return b64
157+
158+
159+
def _create_img_html(image, width, label):
160+
html = (
161+
'<div style="display: inline-block; width: %spx; text-align: center;">' % width +
162+
'<h4 style="font-size: 12px">%s</h4>' % label
163+
)
164+
if type(image) is str or type(image) is str_:
165+
html += '<h4 style="font-size: 9px; padding-left: 15px; padding-right: 15px; width: 100%%; word-wrap: break-word; white-space: normal;">%s</h4>' % (image) # NOQA E501
166+
html += '<img src="%s" style="margin: 1px; width: %spx; border: 2px solid #ddd;"/>' % (image, width) # NOQA E501
167+
else:
168+
html += '<img src="data:image/png;base64,%s" style="margin: 1px; width: %spx; border: 2px solid #ddd;"/>' % (
169+
_img_to_base64(image, width*2), width) # NOQA E501
170+
171+
return html + '</div>'
172+
173+
174+
def _create_imgs_list_html(images, labels, max_images, img_width):
175+
html = ''.join([
176+
_create_img_html(x, img_width, label=y)
177+
for x, y in zip(images[:max_images], labels[:max_images])
178+
])
179+
return html

0 commit comments

Comments
 (0)