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

Initial support for FileIO #35

Merged
merged 9 commits into from
Oct 28, 2024
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ docs/site/
# It records a fixed state of all packages used by the project. As such, it should not be
# committed for packages, but should be committed for applications that require a static
# environment.
Manifest.toml
Manifest.toml

.vscode
4 changes: 3 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ version = "4.6.0"

[deps]
CxxWrap = "1f15a43c-97ca-5a2a-ae31-89f07a497df4"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
OpenCV_jll = "33b9d88c-85f9-5d73-bd91-4e2b95a9aa0b"

[compat]
FileIO = "1.16"
CxxWrap = "0.16"
julia = "1.10"
julia = "1.10"
3 changes: 2 additions & 1 deletion docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
OpenCV = "f878e3a2-a245-4720-8660-60795d644f2a"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
OpenCV = "f878e3a2-a245-4720-8660-60795d644f2a"
4 changes: 4 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ DocMeta.setdocmeta!(OpenCV, :DocTestSetup, :(using OpenCV); recursive=true)
makedocs(;
modules=[OpenCV],
sitename="OpenCV.jl",
pages = [
"OpenCV.jl" => "index.md"
"Getting started with Images" => "Getting started with Images.md"
]
)

deploydocs(;
Expand Down
106 changes: 106 additions & 0 deletions docs/src/Getting started with Images.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Reading and Writing of Images

OpenCV provides a wide range of functions for image processing, including reading and writing images.

OpenCV.jl provides 2 ways to read and write images
1. OpenCV native api
2. FileIO.jl api

OpenCV provides `imread` and `imwrite`, while FileIO privides `load` and `save` for similar functionalities


!!! note

FileIO api not suppport all image formats currently but it support major formats.
If FileIO api not works then use OpenCV native api

## Reading Images

```julia
using OpenCV

img_path = "/path/to/image"
img = OpenCV.imread(img_path)
```
with FileIO
```julia
using OpenCV, FileIO

img_path = "/path/to/image"
img = load(img_path)
```

in both cases `img` has same data. Both methods accept same reading flags[^imreadflags].

```julia
using OpenCV

img_path = "/path/to/image"
flag = OpenCV.IMREAD_UNCHANGED
img = OpenCV.imread(img_path, flag)
```
with FileIO
```julia
using OpenCV, FileIO

img_path = "/path/to/image"
flag = OpenCV.IMREAD_UNCHANGED
img = load(img_path, flag)
```

## Writing Images
```julia
using OpenCV

img = rand(UInt8, 1000, 1000) |> OpenCV.Mat
img_path = "/path/to/image"
OpenCV.imwrite(img_path, img)
```
with FileIO
```julia
using OpenCV, FileIO

img = rand(UInt8, 1000, 1000) |> OpenCV.Mat
img_path = "/path/to/image"
save(img_path, img)
```

Both methods accept same writing flags[^imwriteflags].

```julia
using OpenCV

img = rand(UInt8, 1000, 1000) |> OpenCV.Mat
img_path = "/path/to/image"
flag = Int32[OpenCV.IMWRITE_JPEG_QUALITY, 100]
OpenCV.imwrite(img_path, img, flag)
```
with FileIO
```julia
using OpenCV, FileIO

img = rand(UInt8, 1000, 1000) |> OpenCV.Mat
img_path = "/path/to/image"
flag = Int32[OpenCV.IMWRITE_JPEG_QUALITY, 100]
save(img_path, img, flag)
```

## Displaying Images

When working with images, it's obviously helpful to be able to look at them. If you use Julia through Pluto, VSCode, or IJulia, images should display automatically.

Preview from Pluto.jl

![pluto preview](assets/pluto.png)

`OpenCV.imshow` method display image on Qt window.

![qt preview](assets/qt.png)

`Plots.jl` To-do

`Makie.jl` To-do

## Notes
[^imreadflags]: https://docs.opencv.org/4.x/d8/d6a/group__imgcodecs__flags.html#ga61d9b0126a3e57d9277ac48327799c80
[^imwriteflags]: https://docs.opencv.org/4.x/d8/d6a/group__imgcodecs__flags.html#ga292d81be8d76901bff7988d18d2b42ac
Binary file added docs/src/assets/pluto.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/src/assets/qt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/OpenCV.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
module OpenCV

using OpenCV_jll
using FileIO
using FileIO: DataFormat, File, Stream, stream

include(joinpath(OpenCV_jll.artifact_dir, "OpenCV", "src", "OpenCV.jl"))

include("fileio.jl")
include("show.jl")

end
58 changes: 58 additions & 0 deletions src/fileio.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const _IMAGE_DATA_FORMATS = Union{
format"BMP",
format"JP2",
format"JPEG",
format"PNG",
format"TIFF",
}

function _get_format_extension(fmt)
FileIO.info(fmt)[2]

Check warning on line 10 in src/fileio.jl

View check run for this annotation

Codecov / codecov/patch

src/fileio.jl#L9-L10

Added lines #L9 - L10 were not covered by tests
end

## Load Images

function load(f::File{T}) where {T<:_IMAGE_DATA_FORMATS}
data = imread(f.filename)
return data
end

function load(f::File{T}, flags::Int) where {T<:_IMAGE_DATA_FORMATS}
data = imread(f.filename, flags)
return data

Check warning on line 22 in src/fileio.jl

View check run for this annotation

Codecov / codecov/patch

src/fileio.jl#L20-L22

Added lines #L20 - L22 were not covered by tests
end

function load(s::Stream{T}) where {T<:_IMAGE_DATA_FORMATS}
data = read(stream(s))
img = imdecode(reshape(data, 1, 1, :))
return img

Check warning on line 28 in src/fileio.jl

View check run for this annotation

Codecov / codecov/patch

src/fileio.jl#L25-L28

Added lines #L25 - L28 were not covered by tests
end

function load(s::Stream{T}, flags::Int) where {T<:_IMAGE_DATA_FORMATS}
data = read(stream(s))
img = imdecode(reshape(data, 1, 1, :), flags)
return img

Check warning on line 34 in src/fileio.jl

View check run for this annotation

Codecov / codecov/patch

src/fileio.jl#L31-L34

Added lines #L31 - L34 were not covered by tests
end

## Save Images

function save(f::File{T}, image::InputArray) where {T<:_IMAGE_DATA_FORMATS}
imwrite(f.filename, image)
end

function save(f::File{T}, image::InputArray, params::Array{Int32,1}) where {T<:_IMAGE_DATA_FORMATS}
imwrite(f.filename, image, params)

Check warning on line 44 in src/fileio.jl

View check run for this annotation

Codecov / codecov/patch

src/fileio.jl#L43-L44

Added lines #L43 - L44 were not covered by tests
end

function save(s::Stream{T}, image::InputArray) where {T<:_IMAGE_DATA_FORMATS}
ext = _get_format_extension(T)
enc_img = imencode(ext, image)[2]
Base.write(stream(s), enc_img)

Check warning on line 50 in src/fileio.jl

View check run for this annotation

Codecov / codecov/patch

src/fileio.jl#L47-L50

Added lines #L47 - L50 were not covered by tests
end

function save(s::Stream{T}, image::InputArray, params::Vector{Int32}) where {T<:_IMAGE_DATA_FORMATS}
ext = _get_format_extension(T)
enc_img = imencode(ext, image, params)[2]
Base.write(stream(s), enc_img)

Check warning on line 56 in src/fileio.jl

View check run for this annotation

Codecov / codecov/patch

src/fileio.jl#L53-L56

Added lines #L53 - L56 were not covered by tests
end

5 changes: 5 additions & 0 deletions src/show.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
_format_stream(format, io) = Stream{format}(io)

Check warning on line 1 in src/show.jl

View check run for this annotation

Codecov / codecov/patch

src/show.jl#L1

Added line #L1 was not covered by tests

function Base.show(io::IO, ::MIME"image/png", image::Mat{T}) where {T<:dtypes}
save(_format_stream(DataFormat{:PNG}, io), image)

Check warning on line 4 in src/show.jl

View check run for this annotation

Codecov / codecov/patch

src/show.jl#L3-L4

Added lines #L3 - L4 were not covered by tests
end
1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[deps]
Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
LazyArtifacts = "4af54fe1-eca0-43a8-85a7-787d91b784e3"
OpenCV = "f878e3a2-a245-4720-8660-60795d644f2a"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Artifacts
using LazyArtifacts
using OpenCV
using FileIO
using Test

if "OPENCV_TEST_DATA_PATH" in keys(ENV)
Expand All @@ -16,4 +17,5 @@ end
include("test_imgproc.jl")
include("test_objdetect.jl")
include("test_dnn.jl")
include("test_fileio.jl")
end
58 changes: 58 additions & 0 deletions test/test_fileio.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
tmpdir = mktempdir()

images_path = joinpath(opencv_extra_path, "testdata", "python", "images")
images = readdir(images_path, join=true)

bmp_images = filter(endswith(".bmp"), images)
# jp2_images = filter(endswith(".jp2"), images) # Not available
jpg_images = filter(endswith(".jpg"), images)
png_images = filter(endswith(".png"), images)
tiff_images = filter(endswith(".tiff"), images)

@testset "BMP" begin
for (idx, img_path) in enumerate(bmp_images)
img1 = load(img_path)
f = joinpath(tmpdir, "img_$idx.bmp")
save(f, img1)
img2 = load(f)
@test img1 == img2
end
end

# @testset "JP2" begin
# img1 = rand(UInt8, 3, 1024, 1024) |> OpenCV.Mat
# f = joinpath(tmpdir, "img.jp2")
# save(f, img1)
# img2 = load(f)
# @test OpenCV.PSNR(img1, img2) > 15
# end

@testset "JPG" begin
for (idx, img_path) in enumerate(jpg_images)
img1 = load(img_path)
f = joinpath(tmpdir, "img_$idx.jpg")
save(f, img1)
img2 = load(f)
@test OpenCV.PSNR(img1, img2) > 30
end
end

@testset "PNG" begin
for (idx, img_path) in enumerate(png_images)
img1 = load(img_path)
f = joinpath(tmpdir, "img_$idx.png")
save(f, img1)
img2 = load(f)
@test img1 == img2
end
end

@testset "TIFF" begin
for (idx, img_path) in enumerate(tiff_images)
img1 = load(img_path)
f = joinpath(tmpdir, "img_$idx.tiff")
save(f, img1)
img2 = load(f)
@test img1 == img2
end
end