You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by cm...@apache.org on 2018/11/11 13:43:50 UTC
[incubator-mxnet] branch master updated: Port of scala Image API to
clojure (#13107)
This is an automated email from the ASF dual-hosted git repository.
cmeier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git
The following commit(s) were added to refs/heads/master by this push:
new b441628 Port of scala Image API to clojure (#13107)
b441628 is described below
commit b4416288ff6c7bab3293e73fe7d526a716630677
Author: Kedar Bellare <ke...@gmail.com>
AuthorDate: Sun Nov 11 05:43:22 2018 -0800
Port of scala Image API to clojure (#13107)
* Port of scala Image API to clojure
* Minor style changes
* Add specs and other minor fixes
* Fix unit tests (:facepalm:)
---
.../src/org/apache/clojure_mxnet/image.clj | 139 +++++++++++++++++++++
.../test/org/apache/clojure_mxnet/image_test.clj | 79 ++++++++++++
2 files changed, 218 insertions(+)
diff --git a/contrib/clojure-package/src/org/apache/clojure_mxnet/image.clj b/contrib/clojure-package/src/org/apache/clojure_mxnet/image.clj
new file mode 100644
index 0000000..6e726eb
--- /dev/null
+++ b/contrib/clojure-package/src/org/apache/clojure_mxnet/image.clj
@@ -0,0 +1,139 @@
+;;
+;; 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
+;;
+;; http://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 org.apache.clojure-mxnet.image
+ (:require [t6.from-scala.core :refer [$ $$] :as $]
+ [org.apache.clojure-mxnet.dtype :as dtype]
+ [org.apache.clojure-mxnet.ndarray :as ndarray]
+ [org.apache.clojure-mxnet.util :as util]
+ [clojure.spec.alpha :as s])
+ (:import (org.apache.mxnet Image NDArray)
+ (java.io InputStream)))
+
+;; Flags for conversion of images
+(def GRAYSCALE 0)
+(def COLOR 1)
+
+(s/def ::input-stream #(instance? InputStream %))
+(s/def ::color-flag #{GRAYSCALE COLOR})
+(s/def ::to-rgb boolean?)
+(s/def ::ndarray #(instance? NDArray %))
+(s/def ::output (s/or :empty nil? :ndarray ::ndarray))
+(s/def ::decode-image-opts
+ (s/keys :opt-un [::color-flag ::to-rgb ::output]))
+
+(defn decode-image
+ "Decodes an image from an input stream"
+ ([input-stream {:keys [color-flag to-rgb output]
+ :or {color-flag COLOR to-rgb true output nil}
+ :as opts}]
+ (util/validate! ::input-stream input-stream "Invalid input stream")
+ (util/validate! ::decode-image-opts opts "Invalid options for decoding")
+ (Image/imDecode input-stream color-flag to-rgb ($/option output)))
+ ([input-stream]
+ (decode-image input-stream {})))
+
+(s/def ::filename string?)
+(s/def ::optional-color-flag
+ (s/or :none nil? :some ::color-flag))
+(s/def ::optional-to-rgb
+ (s/or :none nil? :some ::to-rgb))
+
+(defn read-image
+ "Reads an image file and returns an ndarray"
+ ([filename {:keys [color-flag to-rgb output]
+ :or {color-flag nil to-rgb nil output nil}
+ :as opts}]
+ (util/validate! ::filename filename "Invalid filename")
+ (util/validate! ::optional-color-flag color-flag "Invalid color flag")
+ (util/validate! ::optional-to-rgb to-rgb "Invalid conversion flag")
+ (util/validate! ::output output "Invalid output")
+ (Image/imRead
+ filename
+ ($/option color-flag)
+ ($/option to-rgb)
+ ($/option output)))
+ ([filename]
+ (read-image filename {})))
+
+(s/def ::int int?)
+(s/def ::optional-int (s/or :none nil? :some int?))
+
+(defn resize-image
+ "Resizes the image array to (width, height)"
+ ([input w h {:keys [interpolation output]
+ :or {interpolation nil output nil}
+ :as opts}]
+ (util/validate! ::ndarray input "Invalid input array")
+ (util/validate! ::int w "Invalid width")
+ (util/validate! ::int h "Invalid height")
+ (util/validate! ::optional-int interpolation "Invalid interpolation")
+ (util/validate! ::output output "Invalid output")
+ (Image/imResize input w h ($/option interpolation) ($/option output)))
+ ([input w h]
+ (resize-image input w h {})))
+
+(defn apply-border
+ "Pad image border"
+ ([input top bottom left right
+ {:keys [fill-type value values output]
+ :or {fill-type nil value nil values nil output nil}
+ :as opts}]
+ (util/validate! ::ndarray input "Invalid input array")
+ (util/validate! ::int top "Invalid top margin")
+ (util/validate! ::int bottom "Invalid bottom margin")
+ (util/validate! ::int left "Invalid left margin")
+ (util/validate! ::int right "Invalid right margin")
+ (util/validate! ::optional-int fill-type "Invalid fill type")
+ (util/validate! ::output output "Invalid output")
+ (Image/copyMakeBorder input top bottom left right
+ ($/option fill-type)
+ ($/option value)
+ ($/option values)
+ ($/option output)))
+ ([input top bottom left right]
+ (apply-border input top bottom left right {})))
+
+(defn fixed-crop
+ "Return a fixed crop of the image"
+ [input x0 y0 w h]
+ (util/validate! ::ndarray input "Invalid input array")
+ (util/validate! ::int x0 "Invalid starting x coordinate")
+ (util/validate! ::int y0 "Invalid starting y coordinate")
+ (util/validate! ::int w "Invalid width")
+ (util/validate! ::int h "Invalid height")
+ (Image/fixedCrop input x0 y0 w h))
+
+(defn rgb-array?
+ "Returns whether the ndarray is in the RGB format"
+ [input]
+ (util/validate! ::ndarray input "Invalid input array")
+ (let [shape (ndarray/shape-vec input)]
+ (and
+ (= 3 (count shape))
+ (= 3 (shape 2)))))
+
+(s/def ::all-bytes #(= dtype/UINT8 (ndarray/dtype %)))
+(s/def ::rgb-array rgb-array?)
+(s/def ::to-image-ndarray
+ (s/and ::ndarray ::all-bytes ::rgb-array))
+
+(defn to-image
+ "Convert a NDArray image in RGB format to a real image"
+ [input]
+ (util/validate! ::to-image-ndarray input "Invalid input array")
+ (Image/toImage input))
diff --git a/contrib/clojure-package/test/org/apache/clojure_mxnet/image_test.clj b/contrib/clojure-package/test/org/apache/clojure_mxnet/image_test.clj
new file mode 100644
index 0000000..38ab11c
--- /dev/null
+++ b/contrib/clojure-package/test/org/apache/clojure_mxnet/image_test.clj
@@ -0,0 +1,79 @@
+;;
+;; 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
+;;
+;; http://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 org.apache.clojure-mxnet.image-test
+ (:require [org.apache.clojure-mxnet.image :as image]
+ [org.apache.clojure-mxnet.ndarray :as ndarray]
+ [clojure.java.io :as io]
+ [clojure.test :refer :all])
+ (:import (javax.imageio ImageIO)))
+
+(def tmp-dir (System/getProperty "java.io.tmpdir"))
+(def image-path (.getAbsolutePath (io/file tmp-dir "Pug-Cookie.jpg")))
+
+(defn download-image []
+ (with-open [in (io/input-stream "https://s3.amazonaws.com/model-server/inputs/Pug-Cookie.jpg")
+ out (io/output-stream (io/file image-path))]
+ (io/copy in out)))
+
+(defn delete-image []
+ (io/delete-file image-path))
+
+(defn with-downloaded-image [f]
+ (download-image)
+ (f)
+ (delete-image))
+
+(use-fixtures :once with-downloaded-image)
+
+(deftest test-decode-image
+ (let [img-arr (image/decode-image
+ (io/input-stream image-path))
+ img-arr-2 (image/decode-image
+ (io/input-stream image-path)
+ {:color-flag image/GRAYSCALE})]
+ (is (= [576 1024 3] (ndarray/shape-vec img-arr)))
+ (is (= [576 1024 1] (ndarray/shape-vec img-arr-2)))))
+
+(deftest test-read-image
+ (let [img-arr (image/read-image image-path)
+ img-arr-2 (image/read-image
+ image-path
+ {:color-flag image/GRAYSCALE})]
+ (is (= [576 1024 3] (ndarray/shape-vec img-arr)))
+ (is (= [576 1024 1] (ndarray/shape-vec img-arr-2)))))
+
+(deftest test-resize-image
+ (let [img-arr (image/read-image image-path)
+ resized-arr (image/resize-image img-arr 224 224)]
+ (is (= [224 224 3] (ndarray/shape-vec resized-arr)))))
+
+(deftest test-crop-image
+ (let [img-arr (image/read-image image-path)
+ cropped-arr (image/fixed-crop img-arr 0 0 224 224)]
+ (is (= [224 224 3] (ndarray/shape-vec cropped-arr)))))
+
+(deftest test-apply-border
+ (let [img-arr (image/read-image image-path)
+ padded-arr (image/apply-border img-arr 1 1 1 1)]
+ (is (= [578 1026 3] (ndarray/shape-vec padded-arr)))))
+
+(deftest test-to-image
+ (let [img-arr (image/read-image image-path)
+ resized-arr (image/resize-image img-arr 224 224)
+ new-img (image/to-image resized-arr)]
+ (is (= true (ImageIO/write new-img "png" (io/file tmp-dir "out.png"))))))