Created
April 22, 2020 15:39
-
-
Save devstopfix/5d3904097190f527410e5539a687fa65 to your computer and use it in GitHub Desktop.
Partition image files into folders based on timestamp.
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 plk | |
(ns chunk.core | |
(:require [goog.date.UtcDateTime] | |
[planck.core :refer [*in* line-seq]] | |
[planck.io :as io])) | |
; | |
; Partition image files into folders based on timestamp. | |
; | |
; This script takes a list of images produced by a webcamera with motion detection | |
; trigger and copies them into folders of images that were taken in sucession. These | |
; folders can then be converted into animations or movies. | |
; | |
; The only parameter to this script is the number of seconds between images for them | |
; to be considered part of the same movie. The image filename must start with timestamp. | |
; | |
; The list of files to be processed should be piped to the script. Inspect the output of: | |
; | |
; ls /tmp/webcam/*jpg | ./chunk_files.cljs 60 | |
; | |
; Once you are satisfied, you can execute in BASH (4.0 or later on OS X) with: | |
; | |
; source <(ls /tmp/webcam/*jpg | ./chunk_files.cljs 60) | |
; | |
; If single images are detected, they are placed in a folder called photos. | |
; | |
; Download this script, set it to executable, and run with https://planck-repl.org/ | |
; | |
(def orphans-folder "photos/") | |
(def sort-by-path (partial sort-by :path)) | |
; https://stackoverflow.com/a/23221442/3366 | |
(defn partition-between [pred? coll] | |
"Partition a seq into a list of lists, partitioning | |
when the pred returns true for adjacent items." | |
(let [switch (reductions not= true (map pred? coll (rest coll)))] | |
(map (partial map first) (partition-by second (map list coll switch))))) | |
(defn parse-timestamp [f] | |
"Extract timestamp from filename in format yyyymmddhhnnss...jpg" | |
(let [[y mm d h mn s] (->> f | |
(re-matches #"(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2}).*") | |
(rest) | |
(map js/parseInt))] | |
(goog.date.UtcDateTime. y mm d h mn s))) | |
(defn get-time [f] (-> f io/file-name (parse-timestamp) .getTime)) | |
(defn nigh-pred [interval] | |
"Return a predicate that returns true if two dates | |
are outside interval seconds of each other" | |
(let [i-ms (* interval 1000)] | |
(fn [d1 d2] | |
(let [t1 (get-time d1) | |
t2 (get-time d2)] | |
(> (- t2 t1) i-ms))))) | |
(defn chunk-files [interval files] | |
(let [nigh? (nigh-pred interval)] | |
(->> files | |
(map io/file) | |
(filter io/regular-file?) | |
(sort-by-path) | |
(partition-between nigh?)))) | |
(defn sstr [& xs] (clojure.string/join " " (cons "" xs))) | |
(def mkdir (partial sstr "mkdir" "-p")) | |
(defn q [s] (str "\"" s "\"")) | |
(defn path [f] "Path of file without its name" | |
(->> f io/path-elements butlast (apply io/file) (io/file ""))) | |
(defn replace-ext [f] | |
(-> (:path f) | |
(clojure.string/replace #"\.\w+$" ".frames") | |
(clojure.string/replace #" " "_"))) | |
(defn output [lfs] | |
(flatten | |
(for [fs lfs] | |
(if (second fs) | |
(let [target (replace-ext (first fs))] | |
(cons | |
(mkdir target) | |
(for [f fs] (sstr "cp" (q f) target)))) | |
(let [f (first fs) | |
target (io/file (path f) orphans-folder)] | |
[(mkdir target) (sstr "cp" (q f) target)]))))) | |
(defn -main [arg] | |
(let [interval (js/parseInt arg)] | |
(->> | |
(chunk-files interval (line-seq *in*)) | |
(output) | |
(distinct) | |
(map println) | |
(doall)))) | |
(set! *main-cli-fn* -main) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment