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
Uh oh!
There was an error while loading. Please reload this page.
-
scripts to make movies as of version 5
Beta Was this translation helpful? Give feedback.
All reactions