Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image Processing Library Prototype #1450

Merged
merged 41 commits into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4d78828
doc: add section on enso testing
4e6 Feb 2, 2021
ecbcd65
add: image lib
4e6 Feb 3, 2021
088e534
impl: linear transformation
4e6 Feb 4, 2021
f1d7a8b
impl: vectorized color ops
4e6 Feb 4, 2021
d35d106
impl: vectorized matrix ops
4e6 Feb 6, 2021
d530bcb
feat: add read write flags
4e6 Feb 11, 2021
eb90db3
feat: visualization
4e6 Feb 14, 2021
31a9868
feat: histogram
4e6 Feb 16, 2021
bdde6ea
feat: update Codecs tests
4e6 Feb 24, 2021
31c8371
fix: after rebase
4e6 Mar 15, 2021
0d9746c
feat: bump opencv
4e6 Mar 15, 2021
057be01
test: matrix
4e6 Mar 16, 2021
48baf6d
refactor: remove color
4e6 Mar 17, 2021
390efe3
refactor: move image to standard distrib
4e6 Mar 17, 2021
716b802
doc: std-bits
4e6 Mar 17, 2021
9446132
feat: update histogram
4e6 Mar 17, 2021
e4dce50
feat: image
4e6 Mar 18, 2021
87c29ea
misc: cleanup
4e6 Mar 18, 2021
532fd52
docs: std-lib
4e6 Mar 18, 2021
55246c9
docs: histogram
4e6 Mar 18, 2021
7658945
legel-review
4e6 Mar 18, 2021
d8c5e9d
legel-review: license
4e6 Mar 18, 2021
c513480
legel-review: update licenses
4e6 Mar 18, 2021
5ee1bf9
misc: review comments
4e6 Mar 19, 2021
3c9c08f
refactor: rename eye to identity
4e6 Mar 19, 2021
4ae70e9
feat: convert to RGB
4e6 Mar 19, 2021
d0af36c
feat: test pending without internet
4e6 Mar 19, 2021
32eece6
feat: operations with matrices
4e6 Mar 21, 2021
8ee205b
feat: matrix normalization
4e6 Mar 21, 2021
4dea6b1
feat: handle matrix errors
4e6 Mar 21, 2021
b0cff5e
misc: fixes
4e6 Mar 21, 2021
a1478de
refactor: codecs
4e6 Mar 23, 2021
3be286e
refactor: histogram
4e6 Mar 23, 2021
606d5cc
test: error message
4e6 Mar 23, 2021
9805e2c
doc: arguments
4e6 Mar 23, 2021
92948ee
test: check result
4e6 Mar 23, 2021
e905f95
misc: cleanup
4e6 Mar 23, 2021
d9d8226
refactor: codecs write
4e6 Mar 23, 2021
d9849e5
fix: write
4e6 Mar 23, 2021
9595f24
feat: unstable
4e6 Mar 23, 2021
f4149f5
Merge branch 'main' into wip/db/image-lib
4e6 Mar 23, 2021
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 build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,7 @@ lazy val `std-bits` = project
libraryDependencies ++= Seq(
"com.ibm.icu" % "icu4j" % icuVersion,
"com.univocity" % "univocity-parsers" % "2.9.0",
"org.openpnp" % "opencv" % "4.5.1-0",
"org.xerial" % "sqlite-jdbc" % "3.34.0",
"org.postgresql" % "postgresql" % "42.2.19"
),
Expand Down
5 changes: 5 additions & 0 deletions distribution/std-lib/Standard/THIRD-PARTY/NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ The license information can be found along with the copyright notices.
Copyright notices related to this dependency can be found in the directory `org.checkerframework.checker-qual-3.5.0`.


'opencv', licensed under the BSD License, is distributed with the Standard.
The license file can be found at `licenses/BSD-3-Clause`.
Copyright notices related to this dependency can be found in the directory `org.openpnp.opencv-4.5.1-0`.


'postgresql', licensed under the BSD-2-Clause, is distributed with the Standard.
The license information can be found along with the copyright notices.
Copyright notices related to this dependency can be found in the directory `org.postgresql.postgresql-42.2.19`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND

1. Redistributions of source code must retain the above copyright notice, this

Copyright (c) 2015, Advanced Micro Devices, Inc.

2. Redistributions in binary form must reproduce the above copyright notice,

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
35 changes: 33 additions & 2 deletions distribution/std-lib/Standard/src/Base/Data/Vector.enso
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,22 @@ type Vector
arr.set_at i (that.at i-this_len)
Vector arr

## Add element to the beginning of this vector.

4e6 marked this conversation as resolved.
Show resolved Hide resolved
> Example
Add one element in front:
[2, 3].prepend 1 == [1, 2, 3]
prepend : Any -> Vector
prepend x = [x] + this

## Add element to the end of this vector.

4e6 marked this conversation as resolved.
Show resolved Hide resolved
> Example
Add one element to the end:
[1, 2].append 3 == [1, 2, 3]
append : Any -> Vector
append x = this + [x]

## When `this` is a vector of text values, concatenates all the values by
interspersing them with `separator`.

Expand Down Expand Up @@ -373,15 +389,30 @@ type Vector

> Example
To pairwise-sum two vectors:
zip [1, 2, 3] [4, 5, 6] (+) == [5, 7, 9]
[1, 2, 3].zip [4, 5, 6] (+) == [5, 7, 9]
When the `function` is not provided, it defaults to creating a pair
of both elements:
zip [1, 2, 3] [4, 5, 6] == [[1, 4], [2, 5], [3, 6]]
[1, 2, 3].zip [4, 5, 6] == [[1, 4], [2, 5], [3, 6]]
zip : Vector -> (Any -> Any -> Any) -> Vector
zip that function=[_,_] =
len = Math.min this.length that.length
here.new len i-> function (this.at i) (that.at i)

## Extend `this` vector to the length of `n` appending elements `elem` to
the end. If the new length `n` is less than existing length, `this`
vector is returned.

> Example
Extending vector to the length of 5 returns `[1, 2, 3, 0, 0]`
[1, 2, 3].pad 5 0
> Example
Extending vector to the length of 5 returns `[1, 2, 3, 4, 5]`
[1, 2, 3, 4, 5].pad 5 0
pad : Integer -> Any -> Vector
pad n elem =
if this.length >= n then this else
this + (here.fill n-this.length elem)

## Vector to JSON conversion.
to_json : Json.Array
to_json = Json.Array (this.map .to_json)
Expand Down
5 changes: 5 additions & 0 deletions distribution/std-lib/Standard/src/Image.enso
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from Standard.Base import all

import Standard.Image.Codecs

from Standard.Image.Codecs export read, write
93 changes: 93 additions & 0 deletions distribution/std-lib/Standard/src/Image/Codecs.enso
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
from Standard.Base import all
import Standard.Base.System.File
4e6 marked this conversation as resolved.
Show resolved Hide resolved
import Standard.Image.Data.Image
import Standard.Image.Codecs.Internal
4e6 marked this conversation as resolved.
Show resolved Hide resolved

polyglot java import org.enso.image.Codecs as Java_Codecs

type Read_Flag

## Read image with alpha channel, otherwise it gets cropped.
4e6 marked this conversation as resolved.
Show resolved Hide resolved
type Read_Alpha_Channel

## Always convert image to the single channel grayscale image.
4e6 marked this conversation as resolved.
Show resolved Hide resolved
type Read_Grayscale

## Use the gdal driver for loading the image.
4e6 marked this conversation as resolved.
Show resolved Hide resolved
type Read_Gdal

type Write_Flag

## For JPEG, it can be a quality from 0 to 100 (the higher is the better).
Default value is 95.
4e6 marked this conversation as resolved.
Show resolved Hide resolved
type Write_Jpeg_Quality value

## Enable JPEG features. Disabled by default.
4e6 marked this conversation as resolved.
Show resolved Hide resolved
type Write_Jpeg_Progressive

## Enable JPEG features. Disabled by default.
4e6 marked this conversation as resolved.
Show resolved Hide resolved
type Write_Jpeg_Optimize

## Separate luma quality level, 0 - 100, default is 0 - don't use.
type Write_Jpeg_Luma_Quality value

## Separate chroma quality level, 0 - 100, default is 0 - don't use.
type Write_Jpeg_Chroma_Quality value

## For PNG, it can be the compression level from 0 to 9. A higher value
means a smaller size and longer compression time. Default value is 3.
type Write_Png_Compression value
4e6 marked this conversation as resolved.
Show resolved Hide resolved

## For WEBP, it can be a quality from 1 to 100 (the higher is the better).
By default (without any parameter) and for quality above 100 the lossless
compression is used.
4e6 marked this conversation as resolved.
Show resolved Hide resolved
type Write_Webp_Quality value

## Read the image from a file.
4e6 marked this conversation as resolved.
Show resolved Hide resolved

The functon reads images in BGR format, or BGRA if the `Read_Alpha_Channel` flag is specified.
4e6 marked this conversation as resolved.
Show resolved Hide resolved

> Example
Read the image.
Codecs.read "image.png"
Read the image with alpha channel.
Codecs.read "image.png" Codecs.Read_Alpha_Channel
Read the image and convert it to grayscale.
Codecs.read "image.png" Codecs.Read_Grayscale
4e6 marked this conversation as resolved.
Show resolved Hide resolved
read : (Text | File) -> (Read_Flag | Vector) -> Image ! File.Io_Error
read location flags=[] =
path = case location of
File.File _ -> location.path
_ -> location
read_flags = case flags of
Vector.Vector _ ->
if flags.is_empty then Java_Codecs.READ_FLAG_EMPTY else
flags.map Internal.read_flag_to_integer . reduce (_.bit_or _)
_ -> Internal.read_flag_to_integer flags
Panic.recover (Image.Image (Java_Codecs.read path read_flags)) . catch e->
case e of
Polyglot_Error _ -> Error.throw (File.Io_Error ('Failed to read ' + path))
4e6 marked this conversation as resolved.
Show resolved Hide resolved
err -> Panic.throw err

## Write the image to a file.

> Example
Write the image with applying png compression.
Codecs.write image
Write the image with applying png compression.
Codecs.write image (Codecs.Write_Png_Compression 9)
Write the image with applying several flags.
Codecs.write image [Codecs.Write_Jpeg_Quality 40, Codecs.Write_Jpeg_Progressive]
4e6 marked this conversation as resolved.
Show resolved Hide resolved
write : (Text | File) -> Image -> (Write_Flag | Vector) -> Nothing ! File.Io_Error
write location image flags=[] =
4e6 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

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

What we talked about for this function.

path = case location of
File.File _ -> location.path
_ -> location
write_flags = case flags of
Vector.Vector _ -> flags
_ -> [flags]
int_flags = Internal.write_flags_to_mat write_flags
Panic.recover (Java_Codecs.write path image.opencv_mat int_flags) . catch e->
case e of
Polyglot_Error _ -> Panic.throw (File.Io_Error ('Faled to write ' + path))
err -> Panic.throw err
4e6 marked this conversation as resolved.
Show resolved Hide resolved
31 changes: 31 additions & 0 deletions distribution/std-lib/Standard/src/Image/Codecs/Internal.enso
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from Standard.Base import all
import Standard.Image.Codecs
4e6 marked this conversation as resolved.
Show resolved Hide resolved

polyglot java import org.opencv.core.MatOfInt
polyglot java import org.opencv.imgcodecs.Imgcodecs

## PRIVATE
4e6 marked this conversation as resolved.
Show resolved Hide resolved

Convert read flag to OpenCV.
4e6 marked this conversation as resolved.
Show resolved Hide resolved
read_flag_to_integer flag = case flag of
Codecs.Read_Alpha_Channel -> Imgcodecs.IMREAD_UNCHANGED
Codecs.Read_Grayscale -> Imgcodecs.IMREAD_GRAYSCALE
Codecs.Read_Gdal -> Imgcodecs.IMREAD_LOAD_GDAL

## PRIVATE

Convert write flag to OpenCV.
4e6 marked this conversation as resolved.
Show resolved Hide resolved
write_flag_to_vector flag = case flag of
Codecs.Write_Jpeg_Quality value -> [Imgcodecs.IMWRITE_JPEG_QUALITY, value]
Codecs.Write_Jpeg_Progressive -> [Imgcodecs.IMWRITE_JPEG_PROGRESSIVE, 1]
Codecs.Write_Jpeg_Optimize -> [Imgcodecs.IMWRITE_JPEG_OPTIMIZE, 1]
Codecs.Write_Jpeg_Luma_Quality value -> [Imgcodecs.IMWRITE_JPEG_LUMA_QUALITY, value]
Codecs.Write_Jpeg_Chroma_Quality value -> [Imgcodecs.IMWRITE_JPEG_CHROMA_QUALITY, value]
Codecs.Write_Png_Compression value -> [Imgcodecs.IMWRITE_PNG_COMPRESSION, value]
Codecs.Write_Webp_Quality value -> [Imgcodecs.IMWRITE_WEBP_QUALITY, value]

## PRIVATE

Create an OpenCV matrix holding the flag values.
write_flags_to_mat flags =
MatOfInt.new (flags.flat_map here.write_flag_to_vector . to_array)
18 changes: 18 additions & 0 deletions distribution/std-lib/Standard/src/Image/Data/Histogram.enso
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from Standard.Base import all

polyglot java import org.enso.image.data.Histogram as Java_Histogram

type Histogram

type Histogram channel data

## Convert histogram data to Json.
to_json : Json
to_json =
bins = Json.from_pairs [["bins", this.data]]
Json.from_pairs [["data", bins]]

## Create a histogram for a specified channel from the image.
calculate image channel =
4e6 marked this conversation as resolved.
Show resolved Hide resolved
4e6 marked this conversation as resolved.
Show resolved Hide resolved
hist = Java_Histogram.calculate image.opencv_mat channel
Histogram channel (Vector.Vector hist.get_data)
111 changes: 111 additions & 0 deletions distribution/std-lib/Standard/src/Image/Data/Image.enso
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from Standard.Base import all
import Standard.Image.Data.Histogram
import Standard.Image.Data.Image.Internal
import Standard.Image.Data.Matrix

polyglot java import org.enso.image.data.Image as Java_Image

type Image
4e6 marked this conversation as resolved.
Show resolved Hide resolved

## The image data type.

The image is represented with a matrix of rows x columns. Each
pixel is represented with a vector of 1 to 4 values (channels).
Pixel values are normalized in a range [0.0 .. 1.0].
type Image opencv_mat

## Return the number of image rows.
rows : Integer
rows = this.opencv_mat.rows

## Return the number of image columns.
columns : Integer
columns = this.opencv_mat.cols

## Return the number of image channels.
channels : Integer
channels = this.opencv_mat.channels

## Get the pixel value indexed by row and column.
get : Integer -> Integer -> Vector ! Matrix.Index_Out_Of_Bounds_Error
get row column =
if (row < 0) || (row >= this.rows) then Error.throw (Matrix.Index_Out_Of_Bounds_Error row) else
if (column < 0) || (column >= this.columns) then Error.throw (Matrix.Index_Out_Of_Bounds_Error column) else
arr = Java_Image.get this.opencv_mat row column
Vector.Vector arr

## Calculates the per-element sum of an image and a scalar.

> Example
4e6 marked this conversation as resolved.
Show resolved Hide resolved
Add the constant to an image. Operation will add 0.1 to each channel of the image.
image + 0.1
Add 0.1 to the first channel of the image.
image + [0.1]
image + [0.1, 0, 0]
+ : (Number | Vector) -> Image
+ value = Internal.core_op this.opencv_mat value (Java_Image.add _ _ _)

## Calculates the per-element difference between an image and a scalar.

> Example
Subtract 0.5 from each channel of the image.
image - 0.5
Subtract 0.5 from second channel of the image.
image - [0, 0.5] == image - [0, 0.5, 0, 0]
- : (Number | Vector) -> Image
- value = Internal.core_op this.opencv_mat value (Java_Image.subtract _ _ _)

## Calculates the per-element product of an image and a scalar.

> Example
Multiply each channel of the image by 2.
image * 2
Multiply first channel of the image by 2, and second channel by 4.
image * [2, 4, 1, 1]
Multiply first channel of the image by 2, and zero the rest of the channels.
image * [2]
* : (Number | Vector) -> Image
* value = Internal.core_op this.opencv_mat value (Java_Image.multiply _ _ _)

## Performs per-element division of an image and a scalar.

> Example
Divide each channel of the image by 2.
image / 2
Divide first channel of the image by 2, and second channel by 4.
image / [2, 4, 1, 1]
Divide first channel of the image by 2, and zero the rest of the channels.
image / [2]
/ : (Number | Vector) -> Image
/ value = Internal.core_op this.opencv_mat value (Java_Image.divide _ _ _)

## Check equality of two images.
4e6 marked this conversation as resolved.
Show resolved Hide resolved
== : Image -> Boolean
== that = Java_Image.is_equals this.opencv_mat that.opencv_mat

## Convert the image to a vector.
to_vector : Vector
to_vector =
arr = Java_Image.to_vector this.opencv_mat
Vector.Vector arr

## Convert the image to Json.
to_json : Json
to_json =
base64 = Java_Image.to_base64 this.opencv_mat
Json.from_pairs [["mediaType", "image/png"], ["base64", base64]]

## Create a histogram for the specified channel.
histogram : Integer -> Histogram
histogram channel = Histogram.calculate this channel

## Create an image from the array of values.

The function expects the array of normalized values in a range of [0.0 .. 1.1].

> Example
Create an image from the vector.
Image.from_vector [0, 0, 0, 0, 0, 0] rows=2 channels=1
from_vector : Vector -> Integer -> Integer -> Image
from_vector data rows=1 channels=1 =
Image (Java_Image.from_vector data.to_array rows channels)
18 changes: 18 additions & 0 deletions distribution/std-lib/Standard/src/Image/Data/Image/Internal.enso
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from Standard.Base import all
import Standard.Image.Data.Image
import Standard.Image.Data.Matrix
4e6 marked this conversation as resolved.
Show resolved Hide resolved

polyglot java import org.enso.image.data.Image as Java_Image
polyglot java import org.opencv.core.Mat
polyglot java import org.opencv.core.Scalar

## PRIVATE
core_op mat value function =
4e6 marked this conversation as resolved.
Show resolved Hide resolved
result = Mat.new
scalar = case value of
Vector.Vector arr ->
Scalar.new arr
_ ->
Scalar.all value
function mat scalar result
Image.Image result
Loading