Skip to content

Commit

Permalink
Merge pull request #2 from ParkerICI/tiledtiff
Browse files Browse the repository at this point in the history
Adds support for tiled tiff files.
  • Loading branch information
rkageyama authored Apr 13, 2020
2 parents 6f04b86 + 54505b8 commit 28a7d34
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 18 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,22 @@ Required. Either a tiff file or a directory containing multiple tiff files to be
Optional. A folder to write the output files to. Will create if it does not exist. Defaults to the parent folder of the tiff being split.

`-s` or `--subfolder`
Optional. If used, generates a folder for each tiff being split and puts the individual tiffs in the subfolder instead of alongside the parent tiff.
Optional. If used, generates a folder for each tiff being split and puts the individual tiffs in the subfolder instead of alongside the parent tiff. Tiffs will be named only for the channel and not contain the sample name.

### split-tiled

`$ java -jar image-utils-0.1.0-standalone.jar split-tiled [args]`

Splits a tiled tiff into individual tiffs.

`-i` or `--input`
Required. Either a tiff file or a directory containing multiple tiff files to be split.

`-o` or `--outpath`
Optional. A folder to write the output files to. Will create if it does not exist. Defaults to the parent folder of the tiff being split.

`-s` or `--subfolder`
Optional. If used, generates a folder for each tiff being split and puts the individual tiffs in the subfolder instead of alongside the parent tiff. Tiffs will be named only for the channel and not contain the sample name.

### help

Expand Down
6 changes: 4 additions & 2 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject image-utils "0.1.0"
(defproject image-utils "0.1.1"
:description "PICI Image Utilities"
:url "http://example.com/FIXME"
:license {:name "GPL-3.0 "
Expand All @@ -8,7 +8,9 @@
[me.raynes/fs "1.4.6"]
[net.imglib2/imglib2 "5.8.0"]
[net.imglib2/imglib2-ij "2.0.0-beta-45"]
[net.imagej/ij "1.52s"]]
[net.imagej/ij "1.52s"]
[ome/formats-gpl "6.1.1"]
[ome/bio-formats_plugins "6.1.1"]]
:main ^:skip-aot org.parkerici.image-utils.core
:target-path "target/%s"
:jvm-opts ["-Xmx16G"]
Expand Down
25 changes: 14 additions & 11 deletions src/org/parkerici/image_utils/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@
(:require [clojure.tools.cli :as cli]
[clojure.string :as str]
[me.raynes.fs :as fs]
[org.parkerici.image-utils.ij.core :as ij])
[org.parkerici.image-utils.ij.core :as ij]
[org.parkerici.image-utils.utils.error :as error])

(:gen-class))

(defn exit [status msg]
(println msg)
(System/exit status))

(defn error-msg [errors]
(str "The following errors occurred while parsing your command:\n\n"
(str/join \newline errors)))

(defmulti command
(fn [argument _options _summary] argument))
Expand All @@ -23,8 +18,16 @@
(if (contains? options :input)
(if (fs/exists? (:input options))
(ij/split-hyperstack (:input options) (:output options) (:subfolder options))
(exit 1 (error-msg ["Input path does not exist."])))
(exit 1 (error-msg ["Input is required."]))))
(error/exit 1 (error/error-msg ["Input path does not exist."])))
(error/exit 1 (error/error-msg ["Input is required."]))))

(defmethod command "split-tiled"
[_ options _]
(if (contains? options :input)
(if (fs/exists? (:input options))
(ij/split-tiled (:input options) (:output options) (:subfolder options))
(error/exit 1 (error/error-msg ["Input path does not exist."])))
(error/exit 1 (error/error-msg ["Input is required."]))))

(defn all-commands []
(sort (keys (dissoc (methods command) :default))))
Expand Down Expand Up @@ -67,13 +70,13 @@
(:help options) ; help => exit OK with usage summary
{:exit-message (usage summary) :ok? true}
errors ; errors => exit with description of errors
{:exit-message (error-msg errors)}
{:exit-message (error/error-msg errors)}
;; custom validation on arguments
:else ; failed custom validation => exit with usage summary
{:action (first arguments) :options options :summary summary})))

(defn -main [& args]
(let [{:keys [action options summary exit-message ok?]} (validate-args args)]
(if exit-message
(exit (if ok? 0 1) exit-message)
(error/exit (if ok? 0 1) exit-message)
(command action options summary))))
44 changes: 40 additions & 4 deletions src/org/parkerici/image_utils/ij/core.clj
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(ns org.parkerici.image-utils.ij.core
(:require [org.parkerici.image-utils.ij.hyperstack :as hyperstack]
[org.parkerici.image-utils.ij.tiled :as tiled]
[org.parkerici.image-utils.ij.io :as ij-io]
[org.parkerici.image-utils.utils.path :as path]
[me.raynes.fs :as fs]))
Expand All @@ -22,12 +23,18 @@
fdir))

(defn output-hyperstack-slice
[source-img slice dimension img-base-name img-extension fdir]
(let [fname (str img-base-name "_" dimension img-extension)
[source-img slice dimension img-base-name img-extension fdir output-to-subfolder]
(let [fname (if output-to-subfolder (str dimension img-extension) (str img-base-name "_" dimension img-extension))
fpath (path/join fdir fname)
slice-img (hyperstack/slice->img source-img slice)]
(ij-io/write-tiff slice-img fpath)))

(defn output-tiled-slice
[slice dimension img-base-name img-extension fdir output-to-subfolder]
(let [fname (if output-to-subfolder (str dimension img-extension) (str img-base-name "_" dimension img-extension))
fpath (path/join fdir fname)]
(tiled/save-imp-as-tiff slice fpath)))

(defn split-hyperstack-file
[fpath outpath output-to-subfolder]
(println (str "\nReading image at " fpath))
Expand All @@ -40,9 +47,27 @@
(print-image-info img)
(println "Writing slices to files. Warning: this may take a while.")
(doall (pmap
#(output-hyperstack-slice img (get slices %) % img-base-name img-extension fdir)
#(output-hyperstack-slice img (get slices %) % img-base-name img-extension fdir output-to-subfolder)
(keys slices)))))

(defn split-tiled-file
[fpath outpath output-to-subfolder]
(println (str "\nReading image at " fpath))
(let [img-base-name (fs/base-name fpath true)
img-extension (fs/extension fpath)
img (tiled/open-tiled-tiff fpath)
slices (tiled/split-channels img)
channel-names (tiled/get-channels img)
slices-map (zipmap channel-names slices)
fdir (get-create-fdir output-to-subfolder (or outpath (.getPath (fs/parent fpath))) img-base-name)]
(println (str "Writing outputs to " fdir "\n"))
;(print-image-info img)
(println "Writing slices to files. Warning: this may take a while.")
(doall (pmap
#(output-tiled-slice (get slices-map %) % img-base-name img-extension fdir output-to-subfolder)
(keys slices-map)
))))

(defn tiffs-in-dir
[fpath]
(remove nil? (flatten (into [] (for [ext-pattern ["*.tif" "*.TIF" "*.tiff" "*.TIFF"]]
Expand All @@ -53,8 +78,19 @@
(doall (for [tiff (tiffs-in-dir fpath)]
(split-hyperstack-file (.getPath tiff) outpath output-to-subfolder))))

(defn split-tiled-dir
[fpath outpath output-to-subfolder]
(doall (for [tiff (tiffs-in-dir fpath)]
(split-tiled-file (.getPath tiff) outpath output-to-subfolder))))

(defn split-hyperstack
[fpath outpath output-to-subfolder]
(if (fs/file? fpath)
(split-hyperstack-file fpath outpath output-to-subfolder)
(split-hyperstack-dir fpath outpath output-to-subfolder)))
(split-hyperstack-dir fpath outpath output-to-subfolder)))

(defn split-tiled
[fpath outpath output-to-subfolder]
(if (fs/file? fpath)
(split-tiled-file fpath outpath output-to-subfolder)
(split-tiled-dir fpath outpath output-to-subfolder)))
96 changes: 96 additions & 0 deletions src/org/parkerici/image_utils/ij/tiled.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
(ns org.parkerici.image-utils.ij.tiled
(:require [clojure.xml]
[org.parkerici.image-utils.utils.error :as error])
(:import [loci.plugins BF]
[java.io ByteArrayInputStream]
[ij IJ ImagePlus ImageStack]
[loci.formats.tiff TiffParser]))

(defn parse-xml [s]
(clojure.xml/parse (ByteArrayInputStream. (.getBytes s "utf-16"))))

(defn get-tiff-comment [filename]
(.getComment (TiffParser. filename)))

(defn read-tiff-metadata [filename]
(-> filename get-tiff-comment parse-xml))

(defn get-num-channels
"Return the number of channels in an ImagePlus."
[^ImagePlus imp]
(.getNChannels imp))

(defn get-num-frames
"Return the number of frames in an ImagePlus."
[^ImagePlus imp]
(.getNFrames imp))

(defn get-num-slices
"Return the number of slices in an ImagePlus."
[^ImagePlus imp]
(.getNSlices imp))

(defn get-width
"Return the width of an image."
[imp]
(.getWidth ^ImagePlus imp))

(defn get-height
"Return the height of an image."
[imp]
(.getHeight ^ImagePlus imp))

(defn get-channels
[imp]
(let [info (.getInfoProperty imp)
split-info (clojure.string/split-lines info)
name-strings (filterv #(clojure.string/starts-with? % "Name #") split-info)]
(if (> (count name-strings) 0)
(let [channels (map (fn [x] (last (clojure.string/split x #" = "))) name-strings)
channels-underscore (map (fn [x] (clojure.string/replace x #" " "_")) channels)]
channels-underscore)
(error/exit 1 (error/error-msg ["No field Name found in image Info"]))
)))

(defn split-channels
"Split channels."
[^ImagePlus imp]
(doall
(for [chan (range (get-num-channels imp))]
(let [new-stack (ij.ImageStack. (get-width imp) (get-height imp))]
(dotimes [t (get-num-frames imp)]
(dotimes [z (get-num-slices imp)]
(let [n (.getStackIndex imp (inc chan) (inc z) (inc t))]
(.addSlice new-stack (.getProcessor (.getImageStack imp) n)))))
(ImagePlus. (str "C" chan "-" (.getTitle imp))
new-stack)))))

(defn save-imp-as-tiff
"Save an image as a tiff."
[imp filename]
(IJ/saveAsTiff ^ImagePlus imp ^string filename)
imp)

(defn open-tiled-tiff
[filename]
(first (BF/openImagePlus filename))
)

(comment
(def filename "/Users/rkageyama/temp/seg/expandedtest/raw/840-100100-002_panel21_Baseline_10.tiff")
(def metadata (read-tiff-metadata filename))
(def myimg (BF/openImagePlus filename))
(def imp (first myimg))
(def channels (get-channels imp))
(def split (split-channels imp))
(range 3)
(for [n (range (count split))]
(let [channel (nth channels n)
filepath (format "%s.tiff" channel)
slice (nth split n)
]
(save-imp-as-tiff slice filepath))
)
(def impo (ImporterOptions.setquiet))
(map println (hash-map "a" "b"))
(ImporterOptions/KEY_QUIET) )
11 changes: 11 additions & 0 deletions src/org/parkerici/image_utils/utils/error.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(ns org.parkerici.image-utils.utils.error
(:require [clojure.string :as str]))


(defn exit [status msg]
(println msg)
(System/exit status))

(defn error-msg [errors]
(str "The following errors occurred while parsing your command:\n\n"
(str/join \newline errors)))

0 comments on commit 28a7d34

Please sign in to comment.