Replies: 1 comment
-
inside # make_video.py
#! /usr/env/python
import os
import sys
import subprocess
path_tmp = "../videos/tmp34"
path_out = "../videos"
resolution = 40 # 250 # must be divisible by 2 for ffmpeg
scale_factor = 10 # 1, must be integer
frame_start = 0 # 0
frame_stop = 100 # 100
frame_skip = 24 # 0
frames_per_second = 5 # 5
pool_size = 4 # 4
subprocess.run(
f"python make_video_frames.py"
f" --out {path_tmp}"
f" --resolution {resolution}"
f" --start {frame_start}"
f" --stop {frame_stop} "
f" --skip {frame_skip}"
f" --pool_size {pool_size}",
shell=True,
stderr=sys.stderr,
stdout=sys.stdout,
check=True,
)
subprocess.run(
# for obscura projection add (vertical flip) if needed
# -vf "traspose=clock,transpose=clok"
# for upscaling add_box
# -vf scale={scale_factor*resolution}x{scale_factor*resolution}:flags=neighbor
# for input with skipped frames
# -pattern_type glob -i "{path_tmp}/color_%*.png"
f"ffmpeg"
f" -r {frames_per_second}"
f' -i "{path_tmp}/color_%03d.png"'
f" -vcodec libx264"
f" -pix_fmt yuv420p"
f' "{path_out}/out.mp4"',
shell=True,
stderr=sys.stderr,
stdout=sys.stdout,
check=True,
) # make_video_frames.py
import sys
from typing import Callable
import os
import argparse
import functools
from multiprocessing import Pool
import time
import math
from enum import IntEnum
sys.path.append("../src")
from nerte.values.coordinates import Coordinates3D
from nerte.values.linalg import AbstractVector
from nerte.values.face import Face
from nerte.values.interval import Interval
from nerte.values.domains import CartesianProduct2D
from nerte.values.manifolds.swirl.cartesian_swirl import CartesianSwirl
from nerte.values.transitions.cartesian_cartesian_swirl import (
CartesianToCartesianSwirlTransition,
)
from nerte.values.submanifolds import Plane
from nerte.values.submanifolds.pushforward_submanifold_2d_in_3d import (
PushforwardSubmanifold2DIn3D,
)
from nerte.world.object import Object
from nerte.world.camera import Camera
from nerte.world.scene import Scene
from nerte.geometry import Geometry
from nerte.geometry.runge_kutta_geometry import RungeKuttaGeometry
from nerte.render.projection import ProjectionMode
from nerte.render.image_filter_renderer import ImageFilterRenderer
from nerte.render.image_color_renderer import ImageColorRenderer
from nerte.render.ray_depth_filter import RayDepthFilter
from nerte.render.meta_info_filter import MetaInfoFilter
from nerte.util.random_color_generator import RandomColorGenerator
# pseudo-random color generator
COLOR = RandomColorGenerator()
class Axis(IntEnum):
"""Representation of an axis."""
X = 0
Y = 1
Z = 2
class Distance(IntEnum):
"""Representation of the negative or positive domain of an axis."""
NEAR = -1
FAR = +1
class Side(IntEnum):
"""Representation of one half of a square (trinagle)."""
THIS = -1
THAT = +1
def make_camera(swirl: float, canvas_dimension: int) -> Camera:
"""Creates a camera with preset values."""
camera_origin = AbstractVector((0, 2, 3)) # center of plane
crop_factor = 0.45 # smaller -> zoom in
theta = -math.pi / 4 # pitch
camera_x = AbstractVector((1, 0, 0))
camera_y = AbstractVector((0.0, math.cos(theta), math.sin(theta)))
camera_z = AbstractVector((0.0, -math.sin(theta), math.cos(theta)))
camera_focus = camera_origin - camera_z * 0.5
interval_x = Interval(-crop_factor, +crop_factor)
interval_y = Interval(
# -crop_factor,
-crop_factor * 0.4, # cut away empty space
+ crop_factor,
)
domain = CartesianProduct2D(interval_x, interval_y)
cartesian_plane = Plane(
direction0=camera_x,
# note: vertical direction flipped due to obscura projection
direction1=-camera_y,
offset=camera_origin,
)
cartesian_to_cartesian_swirl = CartesianToCartesianSwirlTransition(
swirl=swirl,
)
swirl_location = cartesian_to_cartesian_swirl.transform_coords(camera_focus)
swirl_plane = PushforwardSubmanifold2DIn3D(
cartesian_plane, cartesian_to_cartesian_swirl
)
camera = Camera(
location=swirl_location,
detector_domain=domain,
detector_manifold=swirl_plane,
canvas_dimensions=(
canvas_dimension,
# canvas_dimension,
int(
canvas_dimension * 0.7
), # adjust aspect ratio: due to: cut away empty space
),
)
return camera
def make_box_face(
size: float, fix: Axis, distance: Distance, side: Side
) -> Object:
"""
Creates a section of a cube (triangle) where each section gets assigned
a random color.
"""
# intermediate matrix for coordinate coefficients
coords = [[0.0 for _ in range(3)] for _ in range(3)]
# create the coefficients based on the parameters
for coord in coords:
coord[fix.value] = 1.0 * distance.value
axis_u, axis_v = (axis for axis in (0, 1, 2) if axis != fix.value)
coords[0][axis_u] = -size
coords[0][axis_v] = -size
coords[1][axis_u] = -size * side.value
coords[1][axis_v] = +size * side.value
coords[2][axis_u] = +size
coords[2][axis_v] = +size
# represent the coefficients as proper coordinates
point0 = Coordinates3D(coords[0]) # type: ignore[arg-type]
point1 = Coordinates3D(coords[1]) # type: ignore[arg-type]
point2 = Coordinates3D(coords[2]) # type: ignore[arg-type]
# create the triangle as an object
tri = Face(point0, point1, point2)
obj = Object(color=next(COLOR)) # pseudo-random color
obj.add_face(tri)
return obj
def add_box(scene: Scene, size: float) -> None:
"""Adds a box at the center of the scene."""
for axis in Axis:
for distance in Distance:
for side in Side:
obj = make_box_face(size, axis, distance, side)
scene.add_object(obj)
def make_scene(swirl: float, canvas_dimension: int) -> Scene:
"""
Creates a scene with a camera pointing towards an object.
"""
global COLOR
camera = make_camera(swirl, canvas_dimension=canvas_dimension)
scene = Scene(camera=camera)
# reset color generator
COLOR = RandomColorGenerator(seed=0)
add_box(scene, size=1.0)
return scene
def timed(func: Callable[[], None]) -> float:
start = time.perf_counter()
func()
stop = time.perf_counter()
return stop - start
def render_with_filters(
scene: Scene, geo: Geometry, path: str, i: int, params: dict
):
filter_and_file_prefixes = (
(RayDepthFilter(), "ray_depth_"),
(
MetaInfoFilter(
meta_data_key="steps",
min_value=0,
max_value=params(i)["max_steps"],
),
"meta_info_steps_",
),
)
renderer = ImageFilterRenderer(
projection_mode=ProjectionMode.OBSCURA,
filtr=filter_and_file_prefixes[0][0],
auto_apply_filter=False,
)
render_time = timed(lambda: renderer.render(scene=scene, geometry=geo))
print(f"frame (filtered) {i} took {render_time:0.3f}s to render")
for filtr, file_prefix in filter_and_file_prefixes:
renderer.change_filter(filtr)
renderer.apply_filter()
img = renderer.last_image()
img.save(f"{path}/{file_prefix}{i:03}.png")
def render_with_colors(
scene: Scene, geo: Geometry, path: str, i: int, params: dict
):
renderer = ImageColorRenderer(
projection_mode=ProjectionMode.OBSCURA,
)
render_time = timed(lambda: renderer.render(scene=scene, geometry=geo))
print(f"frame (colored) {i} took {render_time:0.3f}s to render")
img = renderer.last_image()
img.save(f"{path}/color_{i:03}.png")
def render_frame(resolution: int, path: str, i: int):
print(f"rendering frame {i:03} ...")
params = lambda i: {
"max_ray_depth": 8.0,
"step_size": 0.1,
"max_steps": 50,
}
swirl = lambda i: 1.0 * ((i - 50) / 50)
scene = make_scene(
swirl=swirl(i),
canvas_dimension=resolution,
)
geo = RungeKuttaGeometry(
CartesianSwirl(swirl=swirl(i)),
**params(i),
)
render_with_filters(scene, geo, path, i, params)
render_with_colors(scene, geo, path, i, params)
def main(path, resolution, frame_start, frame_stop, frame_step, pool_size):
os.makedirs(path, exist_ok=True)
task = functools.partial(render_frame, resolution, path)
with Pool(pool_size) as pool:
pool.map(task, range(frame_start, frame_stop + 1, frame_step))
def positive_int(obj: object) -> int:
i = int(obj)
if i < 0:
raise ValueError(f"{i} is not positive.")
return i
def parse():
parser = argparse.ArgumentParser(
description="Render frames of movie with nerte."
)
parser.add_argument(
"-o",
"--out",
type=str,
help="output directory",
required=True,
)
parser.add_argument(
"--resolution",
type=int,
help="frame resolution in pixels",
required=True,
)
parser.add_argument(
"--start", type=positive_int, help="first frame", default=0
)
parser.add_argument(
"--stop",
type=positive_int,
help="last frame",
required=True,
)
parser.add_argument(
"--skip",
type=positive_int,
help="frames are skipped each",
default=0,
)
parser.add_argument(
"--pool_size",
type=positive_int,
help="size of multiprocessing pool",
default=4,
)
args = parser.parse_args()
path = args.out # "..videos/tmp"
resolution = args.resolution # 100
frame_start = args.start # 0
frame_stop = args.stop # 100
frame_step = args.skip + 1 # 1
pool_size = args.pool_size # 4
os.makedirs(path, exist_ok=True)
main(path, resolution, frame_start, frame_stop, frame_step, pool_size)
if __name__ == "__main__":
parse() |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
scripts to make movies as of version 5
Beta Was this translation helpful? Give feedback.
All reactions