pages/_projects/renders.md
2023-02-15 10:56:35 +02:00

181 lines
5.0 KiB
Markdown

---
layout: page
title: Renders
description: Video rendering of LiDAR point clouds and 3D images.
img: /assets/img/republique-480.webp
importance: 3
category: thesis
---
I made lots of videos to illustrate my slides during my PhD! You can see
some of them below, and how I made them.
## Point clouds
To visualize and render point clouds videos I used [CloudCompare].
CloudCompare is a very versatile tool for visualising and manipulating
3D point clouds and comes with a large number of plugins. I have been
using it to create videos like the one below:
[CloudCompare]: https://www.cloudcompare.org/
![Video: 3D point cloud of
Rennes.](/assets/vid/1080_x264_crf35.mp4){.figure-img .img-fluid
.rounded .z-depth-1 loop=true}
On this video you can see a point cloud with shading and color. The
colors represent the LiDAR intensity, using a color map ranging from
purple for low intensities, to yellow for high intensities. The shading
allow a better perception of the 3D depth of the point cloud. I used
*Portion de Ciel Visible* (PCV) to compute this shading. Unfortunately,
CloudCompare allow only one color scalar field to be displayed at a
time. So I rendered the frames with PCV, then a second time with
intensities. I then produced composite images with the PCV as luminance
and the intensity as chroma with this script:
```bash
#!/bin/env bash
LUMA_FRAMES=$1
CHROMA_FRAMES=$2
OUT_DIR=$3
CRF=10
SIZE='1920x1080'
mkdir -p $OUT_DIR
# Compose luma and chroma frames
for frame in $(ls $LUMA_FRAMES/*.png); do
fname=$(basename $frame)
echo -en "\rProcessing $fname..."
if ! montage $LUMA_FRAMES/$fname $CHROMA_FRAMES/$fname -geometry +25+0 $OUT_DIR/$fname
then
echo 'Error somehow'
exit 2
fi
done
# Resize and crop frames
mogrify -path $OUT_DIR \
-alpha off \
-resize $SIZE^ \
-gravity Center \
-extent $SIZE \
$IN_DIR/*
# Encode video
ffmpeg -r 50 \
-i $OUT_DIR/frame_000%3d.png \
-c:v libx264 \
-crf $CRF \
-preset veryslow \
-pix_fmt yuv420p \
x264_crf${CRF}.mp4
```
## Voxels
I tried a lot of different software to visualise and render voxels, but
nothing really convinced me. I brought out the big guns and went back to
basics!
For the rendering of the voxels I used [Blender] with the Cycles
renderer.
[Blender]: https://www.blender.org/
![Video: 3D image of the same dataset.](/assets/vid/parlement_c20.mp4){.figure-img
.img-fluid .rounded .z-depth-1 loop=true}
Here you can see a voxelization of the previous point cloud. The colors
represent the LiDAR intensity, using the same color map ranging from
purple for low LiDAR intensities, to yellow for high intensities. The
colors are shaded with a virtual sun by using a physically based
ray-tracing renderer and a logarithmic exposure sensitivity. The colors
appear a little washed out, as they would be on a real camera under
similar lighting conditions.
Nothing really provide support to open voxels in Blender. So I write
this Python script to open voxel files from my [Idefix Python
package][idefix] directly in Blender. This script is a quick draft and
would need a good refactoring, but hey! It works!
```python
import bpy
from matplotlib import pyplot as plt
import numpy as np
data_in = 'voxels.npz'
data = np.load(data_in)
coords = data['coords']
vmin, vmax = np.quantile(data['intensity'], (0.01, 0.99))
colors = ((np.clip(data['intensity'], vmin, vmax)
- vmin)
/ (vmax - vmin)
* 255).astype(np.int)
def gen_cmap(mesh, name='viridis'):
cm = plt.get_cmap(name)
for i in range(256):
mat = bpy.data.materials.new(name)
mat.diffuse_color = cm(i)
#mat.specular_color = cm(i)
mesh.materials.append(mat)
def gen_voxels(coords, colors):
# make mesh
vertices_base = np.array(((0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 0, 0),
(0, 0, 1), (0, 1, 1), (1, 1, 1), (1, 0, 1)))
faces_base = np.array([(0,1,2,3), (7,6,5,4), (7,4,0,3),
(6,7,3,2), (5,6,2,1), (4,5,1,0)])
vxl_count = coords.shape[0]
vertices = (coords[None].repeat(8, axis=0).swapaxes(1, 0)
+ vertices_base).reshape(-1, 3)
faces = (faces_base[None].repeat(vxl_count, axis=0)
+ (np.arange(vxl_count)
* 8)[None].repeat(6, axis=0).T[...,None]).reshape(-1, 4)
colors = colors.repeat(6)
new_mesh = bpy.data.meshes.new('vxl_mesh')
new_mesh.from_pydata(vertices, [], faces.tolist())
new_mesh.update()
# make object from mesh
new_object = bpy.data.objects.new('vxl_object', new_mesh)
# make collection
new_collection = bpy.data.collections.new('vxl_scene')
bpy.context.scene.collection.children.link(new_collection)
# add object to scene collection
new_collection.objects.link(new_object)
# Set colors
gen_cmap(new_mesh)
new_object.data.polygons.foreach_set('material_index', colors)
gen_voxels(coords, colors)
```
![](/assets/img/republique.png){.img-fluid .rounded .z-depth-1}
[idefix]: https://github.com/fguiotte/idefix/