Display graph without saving using pydot - python

I am trying to display a simple graph using pydot.
My question is that is there any way to display the graph without writing it to a file as currently I use write function to first draw and then have to use the Image module to show the files.
However is there any way that the graph directly gets printed on the screen without being saved ??
Also as an update I would like to ask in this same question that I observe that while the image gets saved very quickly when I use the show command of the Image module it takes noticeable time for the image to be seen .... Also sometimes I get the error that the image could'nt be opened because it was either deleted or saved in unavailable location which is not correct as I am saving it at my Desktop..... Does anyone know what's happening and is there a faster way to get the image loaded.....
Thanks a lot....

Here's a simple solution using IPython:
from IPython.display import Image, display
def view_pydot(pdot):
plt = Image(pdot.create_png())
display(plt)
Example usage:
import networkx as nx
to_pdot = nx.drawing.nx_pydot.to_pydot
pdot = to_pdot(nx.complete_graph(5))
view_pydot(pdot)

You can render the image from pydot by calling GraphViz's dot without writing any files to the disk. Then just plot it. This can be done as follows:
import io
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import networkx as nx
# create a `networkx` graph
g = nx.MultiDiGraph()
g.add_nodes_from([1,2])
g.add_edge(1, 2)
# convert from `networkx` to a `pydot` graph
pydot_graph = nx.drawing.nx_pydot.to_pydot(g)
# render the `pydot` by calling `dot`, no file saved to disk
png_str = pydot_graph.create_png(prog='dot')
# treat the DOT output as an image file
sio = io.BytesIO()
sio.write(png_str)
sio.seek(0)
img = mpimg.imread(sio)
# plot the image
imgplot = plt.imshow(img, aspect='equal')
plt.show()
This is particularly useful for directed graphs.
See also this pull request, which introduces such capabilities directly to networkx.

Based on this answer (how to show images in python), here's a few lines:
gr = ... <pydot.Dot instance> ...
import tempfile, Image
fout = tempfile.NamedTemporaryFile(suffix=".png")
gr.write(fout.name,format="png")
Image.open(fout.name).show()
Image is from the Python Imaging Library

IPython.display.SVG method embeds an SVG into the display and can be used to display graph without saving to a file.
Here, keras.utils.model_to_dot is used to convert a Keras model to dot format.
from IPython.display import SVG
from tensorflow import keras
#Create a keras model.
model = keras.models.Sequential()
model.add(keras.layers.Dense(units=2, input_shape=(2,1), activation='relu'))
model.add(keras.layers.Dense(units=1, activation='relu'))
#model visualization
SVG(keras.utils.model_to_dot(model).create(prog='dot', format='svg'))

This worked for me inside a Python 3 shell (requires the Pillow package):
import pydot
from PIL import Image
from io import BytesIO
graph = pydot.Dot(graph_type="digraph")
node = pydot.Node("Hello pydot!")
graph.add_node(node)
Image.open(BytesIO(graph.create_png())).show()
You can also add a method called _repr_html_ to an object with a pydot graph member to render a nice crisp SVG inside a Jupyter notebook:
class MyClass:
def __init__(self, graph):
self.graph = graph
def _repr_html_(self):
return self.graph.create_svg().decode("utf-8")

I'm afraid pydot uses graphviz to render the graphs. I.e., it runs the executable and loads the resulting image.
Bottom line - no, you cannot avoid creating the file.

It works well with AGraph Class as well
https://pygraphviz.github.io/documentation/latest/reference/agraph.html#pygraphviz.AGraph.draw
If path is None, the result is returned as a Bytes object.
So, just omit this argument to return the image data without saving it to disk
Using
from networkx.drawing.nx_agraph import graphviz_layout, to_agraph
g = nx.Graph()
...
A = to_agraph(g)
A.draw()
https://networkx.org/documentation/stable/reference/drawing.html#module-networkx.drawing.nx_agraph
In order to show the resulting image which is saved as Bytes object:
# create image without saving to disk
img = A.draw(format='png')
image = Image.open(BytesIO(img))
image.show(title="Graph")
it needs
from PIL import Image
from io import BytesIO

Related

How to save a pyplot image as .h5 file in python and visualize with HDFView?

I'm facing this problem: I would like to save the plot I produced inside an .h5 file.
What I'm doing now is::
import numpy as np
import matplotlib.pyplot as plt
data = np.random.randn(10,10)
fig = plt.scatter(data[0],data[1])
plt.savefig('image.jpg')
then I create the .h5 file as follow:
import cv2
import h5py
image = cv2.imread("image.jpg")
with h5py.File("h5_file.h5", 'a') as hf:
Xset = hf.create_dataset(
name="img",
data=image,
shape=image.shape,
maxshape=image.shape)
And, visualizing with HDFView, I can see the result as follow:
I need to open it "as Image" and I can see a picture for each "table" of RGB.
What I need instead is the result of Tools>Convert Image to HDF5 in the HDFView, which is the following:
Any suggestion? Or any other h5 visualizer to be used?
The problem is not the HDF5 data. I copied your code and ran to create the 2 files. A comparison of the data (as NumPy arrays) shows them to be identical. The difference is the attributes:
When you read the image with cv2.imread() and save the data, no attributes are created.
When you use Tools>Convert Image to HDF5 to create the file, 5 attributes are added to the dataset. (Notice this dataset has a different file icon in HDFView.) The attributes are shown on the Object Attribute Info panel in HDFView and/or you can get them with the following code:
Code:
for k,v in ds2.attrs.items():
print ('attr:',k,'=',v)
Output:
attr: CLASS = b'IMAGE'
attr: IMAGE_MINMAXRANGE = [ 0 255]
attr: IMAGE_SUBCLASS = b'IMAGE_TRUECOLOR'
attr: IMAGE_VERSION = b'1.2'
attr: INTERLACE_MODE = b'INTERLACE_PIXEL'
You can add these attributes to your first file (created with cv2.imread() ) using the code below:
Xset.attrs.create('CLASS','IMAGE',dtype='S6')
Xset.attrs.create('IMAGE_MINMAXRANGE',[0, 255],dtype=np.uint8)
Xset.attrs.create('IMAGE_SUBCLASS','IMAGE_TRUECOLOR',dtype='S16')
Xset.attrs.create('IMAGE_VERSION','1.2',dtype='S4')
Xset.attrs.create('INTERLACE_MODE','INTERLACE_PIXEL',dtype='S16')
Here is the image I see in HDFView after adding the attributes. It is almost the same - no more blurring, but the data point color is a different (gold vs blue). Not sure what causes that. Maybe something to do with the default pallete?

Display Animations in Google Colab

I'm trying to display animations in Google Colab. Specifically, I would like to animate a numpy array with cv2, eg drawing lines in a frame-based manner, and show the output in the cell. The closest I got was this, you can try this code in Colab:
from google.colab.patches import cv2_imshow
import IPython
from PIL import Image
import numpy as np
import cv2 as cv
import time
# Create a black image
img = np.zeros((512,512,3), np.uint8)
# Draw a diagonal blue line with thickness of 5 px
cv.line(img,(0,0),(511,511),(255,0,0),5)
cv2_imshow(img)
for i in range(100):
cv.line(img,(i,0),(511,511),(255,0,0),5)
cv2_imshow(img)
IPython.display.clear_output(wait=True)
time.sleep(1/60.0)
At some point of course this should happen without time.sleep, but with repeated callbacks so we don't block any other code execution. However, as you can see, the output flickers and is not smooth at all.
Here are a couple things I've tried:
ipycanvas. This is great in a local Jupyter notebook and is based on HTML5 canvas. It is a bit annoying to get the image data from javascript back to python, but it's possible. However, this does not run in Google Colab.
https://ipycanvas.readthedocs.io/
Matplotlib animations. eg this (not mine):
https://colab.research.google.com/drive/1lnl5UPFWVPrryaZZgEzd0theI6S94c3X#scrollTo=QLRBwgFqdr83
This is alright. However, it renders the whole animation before displaying it, which is not what I want. Especially, I want to be able to add some interactivity to animations, which this limitation rules out (eg clicking in the image or some button to make something happen in the animation).
Some way of explicitly creating an HTML5 canvas in javascript, eg as suggested here:
IPython: Adding Javascript scripts to IPython notebook
However, I'd like all my code to be python, especially my data to be numpy arrays or PIL images.
Any suggestions?
Here's an example using ipywidgets.Image. This approach doesn't flicker like using clear_output, but the updates seem pretty slow. This might be to do with the fact we're running remotely from Colab - it has to send image updates over the net. Looks like I'm getting 2 or 3 per second, and it seems like it "batches up" or discards intervening updates, rather than waiting for each one.
It's pretty smooth running locally on regular Jupyter.
Hope someone can improve on this - it's something we want to do as well :)
import ipywidgets as ipw
from IPython import display
import numpy as np
import PIL
from io import BytesIO
import time
# image size
h,w = 200,300
# Make an Image Widget and display it
wIm = ipw.Image()
display.display(wIm)
# Make an RGBA array for the image
g3 = np.zeros((h,w,4), dtype=np.uint8)
g3[:,:,3] = 255 # opacity
g3[:,:,0:3] = 0 # color black
p = np.array([h//2,w//2], dtype=int)
for i in range(1000):
# Draw a coloured spiral
r = i/10
theta=i/20
p2 = p + r * np.array([ np.cos(theta), np.sin(theta) ])
(y,x) = p2.astype(int)
rgb = np.array([100+r, 100*(1+np.sin(theta)), 100*(1+np.cos(theta))], dtype=np.uint8)
g3[y:y+8, x:x+2, 0:3] = rgb
# convert numpy to PIL to png-format bytes
pilIm = PIL.Image.fromarray(g3, mode="RGBA")
with BytesIO() as fOut:
pilIm.save(fOut, format="png")
byPng = fOut.getvalue()
# set the png bytes as the image value;
# this updates the image in the browser.
wIm.value=byPng
time.sleep(1/60)

Python ipyleaflet export map as PNG or JPG or SVG

I have tried to export a visualisation of data with ipyleaflet as PNG or any other file format but i could not find a method that is working. For example in folium there is map.save(path). Is there a library or method in ipyleaflet that i have missed in my research which helps me to accomplish my goal?
here is some example code to generate a map
from ipyleaflet import *
center = [34.6252978589571, -77.34580993652344]
zoom = 10
m = Map(default_tiles=TileLayer(opacity=1.0), center=center, zoom=zoom)
m
I'd like to export this map as an image file without taking a screenshot manually.
I found two sources that allow to export javascript leaflet maps:
https://github.com/aratcliffe/Leaflet.print and https://github.com/mapbox/leaflet-image
Unfortunately i was not able to make use of them in python.
My colleague and I found a decent work around for ipyleaflet (python) image export. Here is how it works. The folium library is required for an export. The GeoJson data in this example is already prepared with style properties:
import folium
map = folium.Map([51., 12.], zoom_start=6,control_scale=True)
folium.GeoJson(data).add_to(map)
map.save('map.html')
This is how the result looks:
The html file can be further processed in python (windows) with subprocess calls to make a PDF or PNG out of it. I hope this helps as the ipyleaflet doc for python is almost non existant.
For generating html, you can use ipywidgets
from ipywidgets.embed import embed_minimal_html
embed_minimal_html('map.html', views=[m])
If you want to make a PNG, you can use ipywebrtc, more specifically:
https://ipywebrtc.readthedocs.io/en/latest/ImageRecorder.html
https://ipywebrtc.readthedocs.io/en/latest/WidgetStream.html
Or in code:
from ipywebrtc import WidgetStream, ImageRecorder
widget_stream = WidgetStream(widget=m, max_fps=1)
image_recorder = ImageRecorder(stream=widget_stream)
display(image_recorder)
Saving the PNG:
with open('map.png', 'wb') as f:
f.write(image_recorder.image.value)
Or converting to pillow image for preprocessing:
import PIL.Image
import io
im = PIL.Image.open(io.BytesIO(image_recorder.image.value))
ipyleaflet supports saving as html. Export of svg and png does not seem to be supported.
https://ipyleaflet.readthedocs.io/en/latest/map_and_basemaps/map.html#save-to-html
m.save('output.html')
I created an issue ticket for ipyleaflet:
https://github.com/jupyter-widgets/ipyleaflet/issues/1083

Using python to save a JPG image that was edited in the script

Referring to the answer to this question, I tried to save my own JPG image files, after some basic image processing. I've only applied a rotation and a shear. This is my code:
import numpy as np
import sys
from skimage import data, io, filter, color, exposure
import skimage.transform as tf
from skimage.transform import rotate, setup, AffineTransform
from PIL import Image
mypath = PATH_TO_FILENAME
readfile = FILENAME
img = color.rgb2gray(io.imread(mypath + readfile))
myimg = rotate(img, angle=10, order=2)
afine_tf = tf.AffineTransform(shear=0.1)
editedimg = tf.warp(myimg, afine_tf)
# IF I UNCOMMENT THE TWO LINES BELOW, I CAN SEE THE EDITED IMAGE AS EXPECTED
#io.imshow(editedimg)
#io.show()
saveimg= np.array(editedimg)
result = Image.fromarray((saveimg).astype(np.uint8))
newfile = "edited_" + readfile
result.save(path+newfile)
I know that the image processing was fine because if I display it before saving, it's just the original image with a bit of rotation and shearing, as expected. But I'm doing something wrong while saving it. I tried without the astype(np.uint8)) part but got an error. Then I removed some of the code from the link mentioned above because I guessed it was particularly for Fourier Transforms, since when I included some of their code, then I got an image that was all gray but with white lines in the direction of the shear I'd applied. But now the image that gets saved is just 2KB of nothing but blackness.
And when I tried something as simple as:
result = Image.fromarray(editedimg)
result.save(path+newfile)
then I got this error:
raise IOError("cannot write mode %s as JPEG" % im.mode)
IOError: cannot write mode F as JPEG
I don't really need to use PIL, if there's another way to simply save my image, I'm fine with that.
Look into the PIL fork, Pillow, is is not as outdated and what you should probably be using for this.
Also depending on your operating system you may need a few other libraries to compile PIL with JPEG support properly, see here
This may also help Says you need to convert your image to RGB mode before saving.
Image.open('old.jpeg').convert('RGB').save('new.jpeg')

Embed .SVG files into PDF using reportlab

I have written a script in python that produces matplotlib graphs and puts them into a pdf report using reportlab.
I am having difficulty embedding SVG image files into my PDF file. I've had no trouble using PNG images but I want to use SVG format as this produces better quality images in the PDF report.
This is the error message I am getting:
IOError: cannot identify image file
Does anyone have suggestions or have you overcome this issue before?
Yesterday I succeeded in using svglib to add a SVG Image as a reportlab Flowable.
so this drawing is an instance of reportlab Drawing, see here:
from reportlab.graphics.shapes import Drawing
a reportlab Drawing inherits Flowable:
from reportlab.platypus import Flowable
Here is a minimal example that also shows how you can scale it correctly (you must only specify path and factor):
from svglib.svglib import svg2rlg
drawing = svg2rlg(path)
sx = sy = factor
drawing.width, drawing.height = drawing.minWidth() * sx, drawing.height * sy
drawing.scale(sx, sy)
#if you want to see the box around the image
drawing._showBoundary = True
As mentioned by skidzo, you can totally do this with the svglib package, which you can find here: https://pypi.python.org/pypi/svglib/
According to the website, Svglib is a pure-Python library for reading SVG files and converting them (to a reasonable degree) to other formats using the ReportLab Open Source toolkit.
You can use pip to install svglib.
Here is a complete example script:
# svg_demo.py
from reportlab.graphics import renderPDF, renderPM
from reportlab.platypus import SimpleDocTemplate
from svglib.svglib import svg2rlg
def svg_demo(image_path, output_path):
drawing = svg2rlg(image_path)
renderPDF.drawToFile(drawing, output_path)
if __name__ == '__main__':
svg_demo('/path/to/image.svg', 'svg_demo.pdf')
skidzo's answer is very helpful, but isn't a complete example of how to use an SVG file as a flowable in a reportlab PDF. Hopefully this is helpful for others trying to figure out the last few steps:
from io import BytesIO
import matplotlib.pyplot as plt
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import SimpleDocTemplate, Paragraph
from svglib.svglib import svg2rlg
def plot_data(data):
# Plot the data using matplotlib.
plt.plot(data)
# Save the figure to SVG format in memory.
svg_file = BytesIO()
plt.savefig(svg_file, format='SVG')
# Rewind the file for reading, and convert to a Drawing.
svg_file.seek(0)
drawing = svg2rlg(svg_file)
# Scale the Drawing.
scale = 0.75
drawing.scale(scale, scale)
drawing.width *= scale
drawing.height *= scale
return drawing
def main():
styles = getSampleStyleSheet()
pdf_path = 'sketch.pdf'
doc = SimpleDocTemplate(pdf_path)
data = [1, 3, 2]
story = [Paragraph('Lorem ipsum!', styles['Normal']),
plot_data(data),
Paragraph('Dolores sit amet.', styles['Normal'])]
doc.build(story)
main()
You need to make sure you are importing PIL (Python Imaging Library) in your code so that ReportLab can use it to handle image types like SVG. Otherwise it can only support a few basic image formats.
That said, I recall having some trouble, even when using PIL, with vector graphics. I don't know if I tried SVG but I remember having a lot of trouble with EPS.

Categories