Rendering a gltf/glb file in jupyter notebook using vtk and k3d - python-3.x

I have explored the available methods of how to render a gltf/glb file inline in a jupyter notebook to keep the viewer callback interactivity intact. I have eventually ended up using vtk and k3d to achieve this. The two hurdles I have are:
How to use the vtkGLTFReader() to get vtkPolyData objects from the
glb and render these in k3d? SOLUTION: See method posted in the comments.
How to display colors/textures embedded in the gltf/glb to show them in
k3d?

Here is the code to get vtkPolyData and pass it to k3d.
import k3d
import vtk
import ipywidgets as widgets
reader = vtk.vtkGLTFReader()
reader.SetFileName('sample_glb/GroundVehicle.glb')
reader.Update()
plot = k3d.plot()
mb = reader.GetOutput()
iterator = mb.NewIterator()
vtk_polyobjects = []
while not iterator.IsDoneWithTraversal():
item = iterator.GetCurrentDataObject()
vtk_polyobjects.append(item)
iterator.GoToNextItem()
for obj in vtk_polyobjects:
plot += k3d.vtk_poly_data(obj, color=0x222222)
plot.display()
debug_info = widgets.HTML()

Related

vtkOBJReader object has no attribute SetFileNameMTL (.mtl) for Object (.obj) files

The code below complains that vtk.vtkOBJReader() object has no method SetFileNameMTL().
In the documentation it appears to exist vtkOBJImporter.SetFileNameMTL (Maybe the python wrapper for this is missing?).
How can the material (.mtl) for the object be set when parsing the (.obj) in vtk and made visible in k3d?
import k3d
import vtk
import ipywidgets as widgets
reader = vtk.vtkOBJReader()
reader.SetFileName('sample_obj/Model.obj')
reader.SetFileNameMTL('sample_obj/Model.mtl') #Attribute not found
reader.Update()
plot = k3d.plot()
poly_data = reader.GetOutput()
plot += k3d.vtk_poly_data(poly_data)
plot.display()
You are using vtkOBJReader, not vtkOBJImporter. Those are two different classes. vtkOBJReader is the older class, I think, and only reads in the geometry file. To load the material info, you need to use vtkOBJImporter.

Updating bqplot image widget

I'm working on a project that uses ipywidgets and bqplot to display and interact with an image.
Using ipywidgets and open cv I can modify an image, save it and update the value of the widget. But I also need the on_click_element aspect of bqplot, so I use the widget from the last one. I still have problems figuring out how to do the same thing with the widget in bqplot.
I would like to avoid to redraw the hole thing, but if needed it would have to close and redraw only the widget image since this is part of a bigger set of widgets. For example I would like to binarize the image using an arbitrary treshold.
From here I got the information on how to use the bqplot image widget: https://github.com/bqplot/bqplot/blob/master/examples/Marks/Object%20Model/Image.ipynb
I use something very similar to this to create the widget that I display.
from IPython.display import display
import ipywidgets as widgets
import bqplot as bq
with open('chelsea.png', 'rb') as f:
raw_image = f.read()
ipyimage = widgets.Image(value=raw_image, format='png')
x_sc = bq.LinearScale()
y_sc = bq.LinearScale()
bq_image = bq.Image(image=ipyimage, scales={'x':x_sc, 'y':y_sc})
img_ani = bq.Figure(marks=[bq_image], animation_duration=400)
display(img_ani)
After this I can't update the figure without redrawing the hole thing.
Any ideas?
jupyter 5.7.8,
ipython 7.5.0,
ipywidgets 7.5.1,
bqplot 0.12.10
Update the bqplot image mark by assigning a new image....
with open("chelsea2.png", 'rb') as f:
raw_image2 = f.read()
# ipyimage.value = raw_image2 # This doesn't seems to sync with widget display. Would require a redisplay of bqplot figure
# create new ipywidgets image and assign it to bqplot image
ipyimage2 = widgets.Image(value=raw_image2, format='png')
bq_image.image = ipyimage2

How to display/save a Layered graph in altair by running code from Python console

After creating three different charts with altair graph API and then merging them as per altair documentation.
(underlay+base+overlay).save("layeredChart.html")
An html file is generated with name layeredChart.html
On opening the html file error comes:
JavaScript Error: Duplicate signal name: "selector002_tuple"
This usually means there's a typo in your chart specification. See the javascript console for the full traceback.
What can be the reason for error in html file generation with altair though works fine with jupyter notebook??
Code:
import altair as alt
#altair plot
alt.data_transformers.disable_max_rows()
#Selection tool
selection = alt.selection_single(fields = ['Country/Region'])
#Underlay
base = alt.Chart(de_long).mark_line(strokeWidth=4,opacity=0.7).encode(
x = alt.X('Day'),
y = alt.Y('De',scale=alt.Scale(type='log')),
color = alt.Color('Country/Region',legend=None)
).properties(
width=800,
height=650
).interactive()
print(alt.renderers.names())
#Chart
chart1 = base.encode(
color=alt.condition(selection,'Country/Region:N',alt.value('lightgray'))).add_selection(selection)
#Overlay
overlay = base.encode(
color = 'Country/Region',
opacity = alt.value(0.5),
tooltip = ['Country/Region:N','Name:N']
).transform_filter(selection)
finalChart = (base+chart1+overlay)
finalChart.save("final.html")
This error generally means that you've called add_selection() with the same selection on multiple layers, which is not supported by the Vega-Lite renderer.
Here is a Minimal Reproducible Example of this error:
import altair as alt
import pandas as pd
df = pd.DataFrame({'x': range(10)})
selection = alt.selection_single()
base = alt.Chart(df).encode(x='x').add_selection(selection)
base.mark_line() + base.mark_point()
Javascript Error: Duplicate signal name: "selector001_tuple"
This usually means there's a typo in your chart specification. See the javascript console for the full traceback.
The way to fix it is to add the selection to only one of the layers; for example:
base = alt.Chart(df).encode(x='x')
base.mark_line() + base.mark_point().add_selection(selection)

Storing matplotlib images in S3 with S3.Object().put() on boto3 1.5.36

Amongst other things I am drawing plots using matplotlib, which I would like to immediately store as S3 objects.
According to the answers provided in this and this other question and the fine manual, I need S3.Object.put() to move my data into AWS and the procedure should be along the lines of
from matplotlib import pyplot as plt
import numpy as np
import boto3
import io
# plot something
fig, ax = plt.subplots()
x = np.linspace(0, 3*np.pi, 500)
a = ax.plot(x, np.sin(x**2))
# get image data, cf. https://stackoverflow.com/a/45099838/1129682
buf = io.BytesIO()
fig.savefig(buf, format="png")
buf.seek(0)
image = buf.read()
# put the image into S3
s3 = boto3.resource('s3', aws_access_key_id=awskey, aws_secret_access_key=awssecret)
s3.Object(mybucket, mykey).put(ACL='public-read', Body=image)
However, I end up with a new S3 object with content-length zero.
The following gives me a new S3 object with content-length 6.
s3.Object(mybucket, mykey).put(ACL='public-read', Body="foobar")
When I put the next line, I end up with content in the S3 object, but its not a usable image:
s3.Object(mybucket, mykey).put(ACL='public-read', Body=str(image))
I can make it work by going through an actual file, like this:
with open("/tmp/iamstupid","wb") as fh:
fig.savefile(fh, format="png")
s3.Bucket(mybucket).upload_file("/tmp/iamstupid", mykey)
So it seems to work. I am just unable to use the interface correctly. What am I doing wrong? How can I achieve my goal using S3.Object.put()
I was able to resolve it. I found the answer in this question. Its a Python3 thing. From what I understand Python3 "usually" works with unicode. If you want single bytes you have to be explicit about it. The correct usage therefore is
s3.Object(mybucket, mykey).put(ACL='public-read', Body=bytes(image))
I find this a bit strange, since buf.read() is already supposed to return an object of type bytes but I did stop wondering, b/c it works now.

STEP file reading issue in Python

I am using Python3.4.2 and pythonOCC-0.16.0-win32-py34.exe to draw components. Every components are rendered properly with one defined color but that is not look like a real world component.
Above image is my Python implementation which generate 3D image from STEP file with one color.
Below image is rendered one of my windows software and there I have used Step file. I want to render component same as look like in below image so its look like a real world component.
Is there any way to get correct colored output in Python by read STEP file? I have searched a lot but didn't get a way to implement it. Please help me to go in forward direction.
from future import print_function
import sys
#from OCC.STEPCAFControl import STEPCAFControl_Reader
from OCC.STEPControl import STEPControl_Reader
from OCC.IFSelect import IFSelect_RetDone, IFSelect_ItemsByEntity
from OCC.Display.SimpleGui import init_display
from OCC.Display.WebGl import threejs_renderer
step_reader = STEPControl_Reader()
status = step_reader.ReadFile('./models/test.STEP')
if status == IFSelect_RetDone: # check status
failsonly = False
step_reader.PrintCheckLoad(failsonly, IFSelect_ItemsByEntity)
step_reader.PrintCheckTransfer(failsonly, IFSelect_ItemsByEntity)
ok = step_reader.TransferRoot(1)
_nbs = step_reader.NbShapes()
aResShape = step_reader.Shape(1)
else:
print("Error: can't read file.")
sys.exit(0)
#display, start_display, add_menu, add_function_to_menu = init_display()
#display.DisplayShape(aResShape, update=True)
#start_display()
my_renderer = threejs_renderer.ThreejsRenderer(background_color="#123345")
my_renderer.DisplayShape(aResShape)
The above code is used for read STEP file using Python.

Resources