Commit e33aae5e authored by nik gaffney's avatar nik gaffney
Browse files

"...because the geometry of the abyss demands it"

parent 3ca50a00
* time, space, stacking, slicing, scanning, etc
"Slitscan imaging techniques are used to create static images of time-based phenomena. In traditional film photography, slit scan images are created by exposing film as it slides past a slit-shaped aperture. In the digital realm, thin slices are extracted from a sequence of video frames, and concatenated into a new image."
[[][An Informal Catalogue of Slit-Scan Video Artworks and Research]] Compiled by Golan Levin.
#+ATTR_HTML: :style margin-left: auto; margin-right: auto;
#+name: unfolding
#+caption: Egbert Mittelstaedt. Unfolding (2002)
This particular slitscan provides some slightly configurable scaffolding around ffmpeg (based on a blogpost from [[][oioiiooixiii]]) and is intended for non-realtime rendering at various resolutions. it's neither first, nor last. it's not especially interesting, educational or efficient. just another cloudform in the endless space of possible implementation...
* installation, configuration, operation, etc
install (mac)...
#+BEGIN_SRC shell
brew install ffmpeg drracket
git clone
install (debian/ubuntu)...
#+BEGIN_SRC shell
sudo apt install ffmpeg racket
git clone
simple transform...
#+BEGIN_SRC shell
racket slitscan.rkt -v -i <input.mp4>
transform at 1080p and delete working files, with minimal command line noise...
#+BEGIN_SRC shell
racket slitscan.rkt --width 1920 --height 1080 --loglevel quiet --cleanup -i <input.mp4>
#+BEGIN_SRC shell
slitscan [ <option> ... ]
where <option> is one of
-v, --verbose : various verbose messages
-l <level>, --loglevel <level> : loglevel for ffmpeg e.g. quiet, error, warning, info, debug
-c, --cleanup : Clean up temporary/working files
--horizontal : output a horizontal video only (default: both)
--vertical : output a vertical video only (default: both)
--width <pixels> : width of output output video
--height <pixels> : height of output output video
-i <video>, --input <video> : input file
-f <folder>, --folder <folder> : input folder
--help, -h : Show this help
* further reading
- The Fourth Dimension and How to Get There by Rudy Rucker
- Muybridge's Complete Human and Animal Locomotion, Vol. I
- Baroness Elsa, by Irene Gammel
#! /usr/bin/env racket
#lang racket
;;; Temporal slice-stacking with FFmpeg (aka slitscan, etc+)
;;; Copyright (C) 2019 FoAM
;;; This program is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation, either version 3 of the License, or
;;; (at your option) any later version.
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; GNU General Public License for more details.
;;; You should have received a copy of the GNU General Public License
;;; along with this program. If not, see <>.
;;; Authors
;;; nik gaffney <>
;;; Requirements
;;; Racket, ffmpeg and zsh or bash (probably)
;;; Commentary
;;; Mostly scaffolding around ffmpeg, based on a shell script from oioiiooixiii
;;; Further details, instructions and digression can be found at
;;; Revision history
;;; - 2017-10-01 - shell script from oioiiooixiii
;;; - 2019-08-08 - cmprzs, convert, reconstruct
;;; - 2019-08-10 - racket translation, info
;; cli options
(define verbose? (make-parameter #f))
(define cleanup? (make-parameter #f))
(define filename (make-parameter "untitled"))
(define input-folder (make-parameter ""))
(define loglevel (make-parameter "")) ;; leave unset by default
;; output options horiz/vert/both
(define hout-only? (make-parameter #f))
(define vout-only? (make-parameter #f))
(define output-width (make-parameter 640))
(define output-height (make-parameter 360))
;; parse command line
(define getopt
#:program "slitscan"
(("-v" "--verbose") "various verbose messages" (verbose? #t))
(("-l" "--loglevel") level "loglevel for ffmpeg e.g. quiet, error, warning, info, debug" (loglevel level))
(("-c" "--cleanup") "Clean up temporary/working files" (cleanup? #t))
(("--horizontal") "output a horizontal video only (default: both)" (hout-only? #t))
(("--vertical") "output a vertical video only (default: both)" (vout-only? #t))
(("--width") pixels "width of transform video" (output-width pixels))
(("--height") pixels "height of tansform video" (output-height pixels))
(("-i" "--input") video "input file" (filename video))
(("-f" "--folder") folder "input folder" (input-folder folder))
#:args () ; rest of the args?
(printf "slitscanning: ~a\n" (filename))))
;; emulation of $(...) shell syntax
(define-syntax zout
(syntax-rules ()
((_ cmd) (string-trim (with-output-to-string (lambda () (system cmd)))))))
;; echoing verbosity
(define-syntax vecho
(syntax-rules ()
((_ str ...) (when (verbose?) (printf str ...)))))
(cond ((and (verbose?) (string=? "" (loglevel))) (loglevel "info"))
((and (not (verbose?)) (string=? "" (loglevel))) (loglevel "error")))
;; variables/various
(define folder (if (not (string=? "" (input-folder)))
(zout "mktemp -d")))
(vecho "using folder: ~a\n" folder)
;(define fffeature "ffprobe -v quiet -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries stream=")
(define fffeature "ffprobe -v error -of default=noprint_wrappers=1:nokey=1 -show_entries format=")
;; assume most/all codecs/formats/streams have a duration
(define duration (let ((f (string->number
(zout (format
"~aduration \"~a\""
fffeature (filename))))))
(if f f 0)))
(vecho "duration: ~a seconds\n" duration)
;; determine fps from input file or default to 24
(define fps (let ((f (string->number
(zout (format
"~ar_frame_rate \"~a\""
fffeature (filename))))))
(if f f 24)))
(vecho "fps: ~a\n" fps)
;; number of frames. estimate for codecs/formats/streams without nb_frames
(define frames (let ((f (string->number
(zout (format
"~anb_frames \"~a\""
fffeature (filename))))))
(if f f (ceiling (* duration fps)))))
(vecho "frames: ~a\n" frames)
;; output dimensions. change if/as required via opts...
(define w (output-width))
(define h (output-height))
(vecho "transform dimensions: ~ax~a\n" w h)
;; FFmpeg Filterchains
(define stemStart "select=gte(n\\,")
(define stemEnd "),format=yuv444p,split[horz][vert]")
(define horz (format "[horz]crop=in_w:1:0:n,tile=1x~a[horz]" h))
(define vert (format "[vert]crop=1:in_h:n:0,tile=~aX1[vert]" w))
(define merge "[0:v]null[horz];[1:v]null[vert]")
(define scale (format "scale=~a:~a" w h))
;; check resume?
;; rezise video
(define resized-video (string-append folder "/resized.mkv"))
(define (resize)
(define ffmpeg-r1
(format "ffmpeg -loglevel ~a -i \"~a\" -vf ~a -crf 10 \"~a\" 2>&1 | grep 'frame=' | tr \\n \\r; echo"
(loglevel) (filename) scale resized-video))
(vecho "resizing video: ~a\n" resized-video)
(system ffmpeg-r1))
;; slice/crop/loop
(define (slice)
(for ((i (range frames)))
(vecho "Frame: ~a of ~a\r" i frames)
(define ffmpeg-s1
"ffmpeg -loglevel ~a -i \"~a\" \
-filter_complex \"~a~a~a;~a;~a\" \
-map '[horz]' \
-vframes 1 ~a/horz_frame~a.png \
-map '[vert]' \
-vframes 1 ~a/vert_frame~a.png"
(loglevel) resized-video
stemStart i stemEnd horz vert
folder i
folder i))
(system ffmpeg-s1))
(vecho "\n"))
;; Join images (optional sharpening, upscale, etc. via 'merge' variable)
(define (assemble)
(vecho "Creating output videos\n")
(define ffmpeg-a0 ;; horizontal & vertical videos (from .sh)
"ffmpeg -loglevel ~a \
-r ~a -i ~a/horz_frame%d.png \
-r ~a -i ~a/vert_frame%d.png \
-filter_complex \"~a\" \
-map '[horz]' \
-r ~a \
-crf 10 \
\"~a_horizontal-smear.mkv\" \
-map '[vert]' \
-r ~a \
-crf 10 \
fps folder
fps folder
fps (filename)
fps (filename)))
(define ffmpeg-a1 ;; horizontal frame stack
"ffmpeg -loglevel ~a -r ~a -i ~a/horz_frame%d.png \"~a_horizontal-smear.mkv\""
(loglevel) fps folder (filename)))
(define ffmpeg-a2 ;; vertical frame stack
"ffmpeg -loglevel ~a -r ~a -i ~a/vert_frame%d.png \"~a_vertical-smear.mkv\""
(loglevel) fps folder (filename)))
;; assemble
(cond ((hout-only?) (system ffmpeg-a1))
((vout-only?) (system ffmpeg-a2))
(else (system ffmpeg-a1)
(system ffmpeg-a2))))
;; spatial extents (frames -> image) summary
(define (framecat start offset)
(define montage-h1
(format "montage ~a/horz_frame~a.png ~a/horz_frame~a.png ~a/horz_frame~a.png ~a/horz_frame~a.png ~a/horz_frame~a.png -tile x1 -mode concatenate ~a-slitscan-s~a.o~a.jpg"
folder start
folder (+ start offset)
folder (+ start (* offset 2))
folder (+ start (* offset 3))
folder (+ start (* offset 4))
(filename) start offset))
(system montage-h1))
(define (image) (framecat 1 (/ frames 5)))
;; delete working files (or not)
(define (cleanup)
(if (cleanup?)
(begin (system (format "rm -rf ~a" folder))
(printf "Removed temp files at ~a\n" folder))
(vecho "leaving working files at ~a\n" folder)))
;; output in 3 (or 4 (or 5)) steps
(when (resize) (printf "resized\n"))
(when (slice) (printf "slicwd\n"))
(when (assemble) (printf "assembled\n"))
(when (cleanup) (printf "done\n"))
;;; FIN
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment