360° → four orientations

Splitting one equirectangular frame into flat front / right / back / left views.

Streamcatcher already aims a virtual pinhole camera into a 360° equirectangular panorama for its interactive look-around viewport. This tool reuses exactly that reprojection, but instead of one steerable view it renders four fixed headings at once — yaw 0°, 90°, 180° and −90° — so a single spherical frame becomes four ordinary flat images. With a 90° field of view the four views tile the whole horizon edge-to-edge, with no overlap and no gap.

Live demo below. Everything runs in your browser with a JavaScript port of the same math (reprojection.py). It starts with a synthetic labelled panorama; load your own equirectangular (2:1) image to try a real one. Nothing is uploaded.

Try it

Source equirectangular frame. The coloured wedges mark where each view is aimed: the centre column is front (0°), and longitude increases to the right.

Frontyaw 0°

Rightyaw 90°

Backyaw 180°

Leftyaw −90°

How the mapping works

For each output pixel the virtual camera casts a ray, rotates it by the view's yaw (and pitch), converts it to a longitude/latitude on the sphere, and reads the matching pixel of the equirectangular source:

focal = (size/2) / tan(hfov/2)
ray   = rotate_yaw( (u−cx, −(v−cy), focal), yaw )
lon   = atan2(ray.x, ray.z)          # 0 = front, + = right
lat   = atan2(ray.y, hypot(ray.x, ray.z))
src_x = (lon / 2π + 0.5) · width
src_y = (0.5 − lat / π) · height

At the centre pixel this puts front at the middle column of the panorama, right three-quarters across, left a quarter across, and back at the wrap-around seam — which is exactly what the four views show above.

In Streamcatcher

The split ships as a one-shot capture on play: it opens the stream, grabs one raw frame, writes the four views, and exits — no window, so it also runs on a headless (server) OpenCV build. It is mutually exclusive with --snapshot.

# Write front.jpg / right.jpg / back.jpg / left.jpg into ./views/
streamcatcher play rtsp://cam.local/live -b opencv --orientations ./views

# No directory → a timestamped folder in the current directory
streamcatcher play rtsp://cam.local/live -b opencv --orientations

Output files, size, and field of view:

WhatValue
Files writtenfront.jpg, right.jpg, back.jpg, left.jpg
View size1024×1024 · env STREAMCATCHER_ORIENTATION_SIZE
Horizontal FOV90° · env STREAMCATCHER_ORIENTATION_HFOV_DEG
Headingsfront 0° · right 90° · back 180° · left −90°

The same operation is available programmatically on the headless session:

from streamcatcher.player.session import StreamSession

session = StreamSession("rtsp://cam.local/live")
session.open()
views = session.split_orientations()          # {"front","right","back","left"} → images
session.write_orientations(views, "views")    # front.jpg / right.jpg / back.jpg / left.jpg
session.close()

# Or split a frame you already have (cv2 injected — no hard OpenCV import):
from streamcatcher.player.orientations import split_equirect
views = split_equirect(frame, cv2, size=1024, hfov_deg=90.0)
The recorder treats the source as a full 360°×180° equirectangular panorama, so it works on any equirect stream regardless of the --projection used for live viewing. On a non-360 source the four views are still produced but won't be meaningful.

Part of Streamcatcher · design/demo page for the four-orientation split.