Last active
October 27, 2024 20:19
-
-
Save celoyd/cd0a827b17eb7e75d2451bc677229d63 to your computer and use it in GitHub Desktop.
Simple but reasonably flexible
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
"""Simple slitscan generator | |
$ slitscan.py source slice destination | |
Where: | |
- source is a video or a directory of images; | |
- slice is either "c" for column or "r" for row | |
and an integer, e.g., r1 or c42; and | |
- destination is an image file. | |
For example: | |
$ slitscan.py example.mp4 r100 example.png | |
Or perhaps: | |
$ slitscan.py directoy_of_frames c0 example.tiff | |
To do: | |
- error usefully when slice is larger than source | |
- warn when a frame is wrong-size? | |
- warn if dtype seems larger than dest can handle? | |
- safer slice parsing; test negative indexes (ranges?!) | |
- combine the separate code paths, within reason | |
By Charlie Loyd. Last update 2024-08-30. | |
Distributed under the Blue Oak Model License 1.0.0: | |
https://blueoakcouncil.org/license/1.0.0 | |
""" | |
from imageio import v3 as iio | |
from tqdm import tqdm | |
from sys import argv | |
from pathlib import Path | |
import numpy as np | |
def make_dst(height, width, depth, length, dtype, axis): | |
if axis == "r": | |
return np.zeros((length, width, depth), dtype=dtype) | |
else: | |
return np.zeros((height, length, depth), dtype=dtype) | |
src_path = Path(argv[1]) | |
if not src_path.exists(): | |
raise FileNotFoundError(f"{src_path} does not exist") | |
axis, posn = argv[2][0], int(argv[2][1:]) | |
if axis not in ("c", "r"): | |
raise ValueError(f"Use c for column or r for row, e.g., r1 or c42") | |
dst_path = Path(argv[3]) | |
if dst_path.exists(): | |
raise FileExistsError(f"{dst_path} already exists") | |
dst = True # you don’t have to declare variables in python, they said | |
if src_path.is_dir(): | |
files = sorted(list(src_path.glob("*"))) | |
t = iio.imread(files[0]) | |
dst = make_dst( | |
height=t.shape[0], | |
width=t.shape[1], | |
depth=t.shape[2], | |
length=len(files), | |
dtype=t.dtype, | |
axis=axis, | |
) | |
for i, f in tqdm(enumerate(files), total=len(files)): | |
frame = iio.imread(f) | |
if axis == "r": | |
dst[i] = frame[posn] | |
else: | |
dst[:, i] = frame[:, posn] | |
else: | |
props = iio.improps(src_path) | |
if props.shape[0] == 0: | |
raise RuntimeError( | |
"The video i/o library can’t tell the video’s length.\n" | |
"Try using ffmpeg to drop its frames to images, something like:\n" | |
f'$ mkdir frames; ffmpeg -i \"{src_path}\" -f image2 "frames/f%04d.png"\n' | |
"and run this script again with the frames folder as the input." | |
) | |
dst = make_dst( | |
height=props.shape[1], | |
width=props.shape[2], | |
depth=props.shape[3], | |
length=props.shape[0], | |
dtype=props.dtype, | |
axis=axis, | |
) | |
for i, frame in tqdm(enumerate(iio.imiter(src_path)), total=props.shape[0]): | |
if axis == "r": | |
dst[i] = frame[posn] | |
else: | |
dst[:, i] = frame[:, posn] | |
iio.imwrite(dst_path, dst) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment