Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

[Clojure] examples with opencv4/origami #13813

Merged
merged 1 commit into from
Jan 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contrib/clojure-package/examples/gan/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ pom.xml.asc
/.nrepl-port
.hgignore
.hg/
results
7 changes: 4 additions & 3 deletions contrib/clojure-package/examples/gan/project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
;; limitations under the License.
;;

(defproject gan "0.1.0-SNAPSHOT"
(defproject gan-origami "0.1.0-SNAPSHOT"
:description "GAN MNIST with MXNet"
:plugins [[lein-cljfmt "0.5.7"]]
:repositories [["vendredi" {:url "https://repository.hellonico.info/repository/hellonico/"}]]
:dependencies [[org.clojure/clojure "1.9.0"]
[org.apache.mxnet.contrib.clojure/clojure-mxnet "1.5.0-SNAPSHOT"]
[org.openpnp/opencv "3.4.2-1"]
[origami "4.0.0-3"]
]
:main gan.gan-mnist)
:main gan.gan-mnist)
56 changes: 14 additions & 42 deletions contrib/clojure-package/examples/gan/src/gan/viz.clj
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,9 @@
(ns gan.viz
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran the GAN example and everything looks great 💯 - I even think it's faster 🚗
Nice job.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! happy to found it was faster too. This is due to the fact that we do not process byte-per-byte anymore.

(:require [org.apache.clojure-mxnet.ndarray :as ndarray]
[org.apache.clojure-mxnet.shape :as mx-shape]
[org.apache.clojure-mxnet.io :as mx-io])
(:import (nu.pattern OpenCV)
(org.opencv.core Core CvType Mat Size)
(org.opencv.imgproc Imgproc)
(org.opencv.imgcodecs Imgcodecs)))

;;; Viz stuff
(OpenCV/loadShared)
[org.apache.clojure-mxnet.io :as mx-io]
[opencv4.utils :as cvu]
[opencv4.core :as cv :refer [CV_8UC1 new-matofbyte flip! imwrite new-size hconcat! vconcat! new-mat merge!]]))

(defn clip [x]
(->> x
Expand All @@ -37,29 +32,11 @@
(mapv #(.byteValue %))))

(defn get-img [raw-data channels height width flip]
(let [totals (* height width)
img (if (> channels 1)
;; rgb image
(let [[ra ga ba] (byte-array (partition totals raw-data))
rr (new Mat height width (CvType/CV_8U))
gg (new Mat height width (CvType/CV_8U))
bb (new Mat height width (CvType/CV_8U))
result (new Mat)]
(.put rr (int 0) (int 0) ra)
(.put gg (int 0) (int 0) ga)
(.put bb (int 0) (int 0) ba)
(Core/merge (java.util.ArrayList. [bb gg rr]) result)
result)
(let [img (if (> channels 1)
(throw (Exception. "Image with 3 channels (RGB) not supported"))
;; gray image
(let [result (new Mat height width (CvType/CV_8U))
_ (.put result (int 0) (int 0) (byte-array raw-data))]
result))]
(do
(if flip
(let [result (new Mat)
_ (Core/flip img result (int 0))]
result)
img))))
(cv/>> (new-mat height width CV_8UC1) (byte-array raw-data)))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat threading :)

(if flip (flip! img 0) img)))

(defn im-sav [{:keys [title output-path x flip]
:or {flip false} :as g-mod}]
Expand All @@ -73,15 +50,10 @@
line-arrs (into [] (partition (* col c totals) raw-data))
line-mats (mapv (fn [line]
(let [img-arr (into [] (partition (* c totals) line))
col-mats (new Mat)
src (mapv (fn [arr] (get-img (into [] arr) c h w flip)) img-arr)
_ (Core/hconcat (java.util.ArrayList. src) col-mats)]
col-mats))
line-arrs)
result (new Mat)
resized-img (new Mat)
_ (Core/vconcat (java.util.ArrayList. line-mats) result)]
(do
(Imgproc/resize result resized-img (new Size (* (.width result) 1.5) (* (.height result) 1.5)))
(Imgcodecs/imwrite (str output-path title ".jpg") resized-img)
(Thread/sleep 1000))))
src (mapv (fn [arr] (get-img (into [] arr) c h w flip)) img-arr)]
(hconcat! src)))
line-arrs)]
(-> line-mats
(vconcat!)
(cvu/resize-by 1.5)
(imwrite (str output-path title ".jpg")))))
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ pom.xml.asc
/.nrepl-port
.hgignore
.hg/
results
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ $ ./scripts/get_ssd_data.sh
$
$ lein run -- --help
$ lein run -- -m models/resnet50_ssd/resnet50_ssd_model -i images/dog.jpg -d images/
$
$ # or the available lein alias
$ lein run-detector
$
$ lein uberjar
$ java -jar target/objectdetector-0.1.0-SNAPSHOT-standalone.jar --help
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@

(defproject objectdetector "0.1.0-SNAPSHOT"
:description "Object detection using infer with MXNet"
:repositories [["vendredi" "https://repository.hellonico.info/repository/hellonico/"]]
:plugins [[lein-cljfmt "0.5.7"]]
:aliases {"run-detector" ["run" "--" "-m" "models/resnet50_ssd/resnet50_ssd_model" "-i" "images/dog.jpg" "-d" "images/"]}
:dependencies [[org.clojure/clojure "1.9.0"]
[org.clojure/tools.cli "0.4.1"]
[origami "4.0.0-3"]
[org.apache.mxnet.contrib.clojure/clojure-mxnet "1.5.0-SNAPSHOT"]]
:main ^:skip-aot infer.objectdetector-example
:profiles {:uberjar {:aot :all}})
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
;; Licensed to the Apache Software Foundation (ASF) under one or more
;; contributor license agreements. See the NOTICE file distributed with
;; this work for additional information regarding copyright ownership.
;; The ASF licenses this file to You under the Apache License, Version 2.0
;; (the "License"); you may not use this file except in compliance with
;; the License. You may obtain a copy of the License at
;;
;; https://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;;

(ns infer.draw
(:require
[opencv4.colors.rgb :as rgb]
[opencv4.core :refer [FONT_HERSHEY_PLAIN imread imwrite new-point put-text! rectangle]]))

(defn black-boxes! [img results]
(doseq [{confidence :confidence label :label top-left :top-left bottom-right :bottom-right} results]
(let [w (.width img)
h (.height img)
top-left-p (new-point (int (* w (first top-left))) (int (* h (second top-left))))
bottom-right-p (new-point (int (* w (first bottom-right))) (int (* h (second bottom-right))))]
(if (< 15 confidence)
(do
(rectangle img top-left-p bottom-right-p rgb/white 1)
(put-text! img
(str label "[" confidence "% ]")
top-left-p
FONT_HERSHEY_PLAIN
1.0
rgb/white 1)))))
img)

(defn draw-bounds [image results output-dir]
(let [out-file (str output-dir "/" (.getName (clojure.java.io/as-file image)))]
(-> image
(imread)
(black-boxes! results)
(imwrite out-file))))
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
[org.apache.clojure-mxnet.infer :as infer]
[org.apache.clojure-mxnet.layout :as layout]
[clojure.java.io :as io]
[infer.draw :as draw]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran the example and the drawing of the bounding boxes is fantastic!

Can you please merge this PR with master because the output of the predictions has now changed shape a bit
#13864

Object detection now returns a map
{:class car, :prob 0.99847263, :x-min 0.6097917, :y-min 0.1406818, :x-max 0.8906532, :y-max 0.29426125}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing out ! Re-Merged with master and fixed.

[clojure.string :refer [join]]
[clojure.tools.cli :refer [parse-opts]])
(:gen-class))
Expand All @@ -45,37 +46,49 @@
["-i" "--input-image IMAGE" "Input image"
:default "images/dog.jpg"
:validate [check-valid-file "Input file not found"]]
["-o" "--output-dir IMAGE_DIR" "Output directory. Defaults to results"
:default "results/"
:validate [check-valid-dir "Output directory not found"]]
["-d" "--input-dir IMAGE_DIR" "Input directory"
:default "images/"
:validate [check-valid-dir "Input directory not found"]]
["-h" "--help"]])

(defn print-predictions
"Print image detector predictions for the given input file"
[predictions width height]
(println (apply str (repeat 80 "=")))
(doseq [{:keys [class prob x-min y-min x-max y-max]} predictions]
(println (format
"Class: %s Prob=%.5f Coords=(%.3f, %.3f, %.3f, %.3f)"
class
prob
(* x-min width)
(* y-min height)
(* x-max width)
(* y-max height))))
(println (apply str (repeat 80 "="))))
(defn result->map [{:keys [class prob x-min y-min x-max y-max]}]
(hash-map
:label class
:confidence (int (* 100 prob))
:top-left [x-min y-min]
:bottom-right [x-max y-max]))

(defn print-results [results]
(doseq [_r results]
(println (format "Class: %s Confidence=%s Coords=(%s, %s)"
(_r :label)
(_r :confidence)
(_r :top-left)
(_r :bottom-right)))))

(defn process-results [images results output-dir]
(dotimes [i (count images)]
(let [image (nth images i) _results (map result->map (nth results i))]
(println "processing: " image)
(print-results _results)
(draw/draw-bounds image _results output-dir))))

(defn detect-single-image
"Detect objects in a single image and print top-5 predictions"
[detector input-image]
[detector input-image output-dir]
(let [image (infer/load-image-from-file input-image)
topk 5
[predictions] (infer/detect-objects detector image topk)]
predictions))
topk 5]
(process-results
[input-image]
(infer/detect-objects detector image topk)
output-dir)))

(defn detect-images-in-dir
"Detect objects in all jpg images in the directory"
[detector input-dir]
[detector input-dir output-dir]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make the default output-dir for the example results or image-results?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"results" it is.

(let [batch-size 20
image-file-batches (->> input-dir
io/file
Expand All @@ -84,15 +97,18 @@
(filter #(re-matches #".*\.jpg$" (.getPath %)))
(mapv #(.getPath %))
(partition-all batch-size))]
(apply concat (for [image-files image-file-batches]
(let [image-batch (infer/load-image-paths image-files)
topk 5]
(infer/detect-objects-batch detector image-batch topk))))))
(doall
(for [image-files image-file-batches]
(let [image-batch (infer/load-image-paths image-files) topk 5]
(process-results
image-files
(infer/detect-objects-batch detector image-batch topk)
output-dir))))))

(defn run-detector
"Runs an image detector based on options provided"
[options]
(let [{:keys [model-path-prefix input-image input-dir
(let [{:keys [model-path-prefix input-image input-dir output-dir
device device-id]} options
width 512 height 512
descriptors [{:name "data"
Expand All @@ -103,12 +119,11 @@
detector (infer/create-object-detector
factory
{:contexts [(context/default-context)]})]
(println "Output results to:" output-dir ":" (.mkdir (io/file output-dir)))
(println "Object detection on a single image")
(print-predictions (detect-single-image detector input-image) width height)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we also leave the printing of the predictions to the console in as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-added.

(println "\n")
(detect-single-image detector input-image output-dir)
(println "Object detection on images in a directory")
(doseq [predictions (detect-images-in-dir detector input-dir)]
(print-predictions predictions width height))))
(detect-images-in-dir detector input-dir output-dir)))

(defn -main
[& args]
Expand Down
6 changes: 3 additions & 3 deletions contrib/clojure-package/examples/neural-style/project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
(defproject neural-style "0.1.0-SNAPSHOT"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran the example - works great!

:description "Neural Style Transfer with MXNet"
:plugins [[lein-cljfmt "0.5.7"]]
:repositories [["vendredi" {:url "https://repository.hellonico.info/repository/hellonico/"}]]
:dependencies [[org.clojure/clojure "1.9.0"]
[org.apache.mxnet.contrib.clojure/clojure-mxnet "1.5.0-SNAPSHOT"]
[net.mikera/imagez "0.12.0"]
[thinktopic/think.image "0.4.16"]]
:main neural-style.core)
[origami "4.0.0-3"]]
:main neural-style.core)
Loading