-
Notifications
You must be signed in to change notification settings - Fork 6.8k
[Clojure] examples with opencv4/origami #13813
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,4 @@ pom.xml.asc | |
/.nrepl-port | ||
.hgignore | ||
.hg/ | ||
results |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,14 +18,9 @@ | |
(ns gan.viz | ||
(: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 | ||
|
@@ -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)))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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}] | ||
|
@@ -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 |
---|---|---|
|
@@ -10,3 +10,4 @@ pom.xml.asc | |
/.nrepl-port | ||
.hgignore | ||
.hg/ | ||
results |
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 |
---|---|---|
|
@@ -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] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 Object detection now returns a map There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)) | ||
|
@@ -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] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we make the default output-dir for the example There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
@@ -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" | ||
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,8 +18,8 @@ | |
(defproject neural-style "0.1.0-SNAPSHOT" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.