From 7badcacebe8e0a1aafee2916beda142b956106be Mon Sep 17 00:00:00 2001 From: JohnnyChen Date: Sat, 14 Dec 2019 18:56:15 +0800 Subject: [PATCH 1/4] crop images for special rotation degrees --- Project.toml | 4 ++-- src/warp.jl | 29 +++++++++++++++++++++++++---- test/warp.jl | 13 +++++++++++-- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/Project.toml b/Project.toml index 883cf60..1380d79 100644 --- a/Project.toml +++ b/Project.toml @@ -17,14 +17,14 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] AxisAlgorithms = "1.0" +ColorTypes = "0.7.3, 0.8" ColorVectorSpace = "0.2, 0.3, 0.4, 0.5, 0.6, 0.7" Colors = "0.7, 0.8, 0.9" -ColorTypes = "0.7.3, 0.8" CoordinateTransformations = "0.5" FixedPointNumbers = "0.5, 0.6" +IdentityRanges = "0.3" ImageCore = "0.7, 0.8" Interpolations = "0.9, 0.10, 0.11, 0.12" -IdentityRanges = "0.3" OffsetArrays = "0.10, 0.11" StaticArrays = "0.10, 0.11, 0.12" julia = "1" diff --git a/src/warp.jl b/src/warp.jl index 3183aab..9124994 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -112,7 +112,7 @@ By default, rotated image `imgr` will not be cropped. Bilinear interpolation wil ```julia julia> img = testimage("cameraman") -# rotate with bilinear interpolation but without cropping +# rotate with bilinear interpolation but without cropping julia> imrotate(img, π/4) # rotate with bilinear interpolation and with cropping @@ -125,7 +125,28 @@ julia> imrotate(img, π/4, Constant()) See also [`warp`](@ref). """ function imrotate(img::AbstractArray{T}, θ::Real, args...) where T - θ = floor(mod(θ,2pi)*typemax(Int16))/typemax(Int16) # periodic discretezation - tform = recenter(RotMatrix{2}(θ), center(img)) - warp(img, tform, args...) + # 1. discretize periodic for numerical stability to make sure + # imrotate(img, θ+2pi) == imrotate(img, θ) + # 2. typemax(Int16) is 32767, we choose 32760 to make sure the + # discretization result of pi/2 is exactly pi/2 (or 90°) + max_num_angles = 32760 + θ = round(Int, 180*floor(mod(θ, 2pi)/pi*max_num_angles)/max_num_angles) + tform = recenter(RotMatrix{2}(θ/180*pi), center(img)) + if θ in (0, 90, 180, 270) + # do auto cropping for these special cases since image is not expected to expand + # if indices is explicitly provided, then we take user's choice + if isempty(args) || !isa(args[1], Tuple) + if θ in (90, 270) + c = Tuple(center(img)) + offset = (reverse(size(img)) .- 1)./2 + indices = map(c, offset) do x, δ + Int(x-δ):Int(x+δ) + end + else + indices = axes(img) + end + return warp(img, tform, indices, args...) + end + end + return warp(img, tform, args...) end diff --git a/test/warp.jl b/test/warp.jl index 162d46e..0daabbb 100644 --- a/test/warp.jl +++ b/test/warp.jl @@ -427,15 +427,24 @@ ref_img_pyramid_grid = Float64[ @test_nowarn imrotate(img, π/4, Linear()) @test_nowarn imrotate(img, π/4, axes(img)) @test_nowarn imrotate(img, π/4, axes(img), Constant()) - @test isequal(channelview(imrotate(img,π/4)), channelview(imrotate(img, π/4, Linear()))) # TODO: if we remove channelview the test will break for Float + @test isequal(channelview(imrotate(img, π/4)), channelview(imrotate(img, π/4, Linear()))) # NaN != NaN end + + # check special rotation degrees + img = rand(Gray{N0f8}, 100, 50) + @test size(imrotate(img, pi/2)) == (50, 100) + @test size(imrotate(img, pi)) == (100, 50) + @test size(imrotate(img, 3pi/2)) == (50, 100) + @test size(imrotate(img, 2pi)) == (100, 50) + rotated_img = imrotate(imrotate(imrotate(imrotate(img, pi/2), pi/2), pi/2), pi/2) + @test rotated_img == img end @testset "numerical" begin for T in test_types img = Gray{T}.(graybar) for θ in range(0,stop=2π,length = 100) - @test isequal(channelview(imrotate(img,θ)), channelview(imrotate(img,θ+2π))) # TODO: if we remove channelview the test will break for Float + @test isequal(channelview(imrotate(img,θ)), channelview(imrotate(img,θ+2π))) # NaN != NaN end end end From f208d6be904b11a57f9491a3b993f31be9dd78fa Mon Sep 17 00:00:00 2001 From: JohnnyChen Date: Mon, 16 Dec 2019 09:41:47 +0800 Subject: [PATCH 2/4] do not use warp for these special degress --- src/warp.jl | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/warp.jl b/src/warp.jl index 9124994..35d647f 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -124,7 +124,7 @@ julia> imrotate(img, π/4, Constant()) See also [`warp`](@ref). """ -function imrotate(img::AbstractArray{T}, θ::Real, args...) where T +function imrotate(img::AbstractMatrix{T}, θ::Real, args...) where T # 1. discretize periodic for numerical stability to make sure # imrotate(img, θ+2pi) == imrotate(img, θ) # 2. typemax(Int16) is 32767, we choose 32760 to make sure the @@ -132,21 +132,20 @@ function imrotate(img::AbstractArray{T}, θ::Real, args...) where T max_num_angles = 32760 θ = round(Int, 180*floor(mod(θ, 2pi)/pi*max_num_angles)/max_num_angles) tform = recenter(RotMatrix{2}(θ/180*pi), center(img)) - if θ in (0, 90, 180, 270) - # do auto cropping for these special cases since image is not expected to expand - # if indices is explicitly provided, then we take user's choice - if isempty(args) || !isa(args[1], Tuple) - if θ in (90, 270) - c = Tuple(center(img)) - offset = (reverse(size(img)) .- 1)./2 - indices = map(c, offset) do x, δ - Int(x-δ):Int(x+δ) - end - else - indices = axes(img) - end - return warp(img, tform, indices, args...) - end + if θ == 0 + return img + elseif θ == 90 + idx = StepRange.(axes(img)) + perm_img = PermutedDimsArray(img, (2, 1)) + return view(perm_img, idx[2], reverse(idx[1])) + elseif θ == 180 + idx = map(i->1:1:length(i), axes(img)) + return view(img, reverse(idx[1]), reverse(idx[2])) + elseif θ == 270 + idx = StepRange.(axes(img)) + perm_img = PermutedDimsArray(img, (2, 1)) + return view(perm_img, reverse(idx[2]), idx[1]) + else + return warp(img, tform, args...) end - return warp(img, tform, args...) end From dd3b7e2febc801bc46e42cd15b0239754468f8ae Mon Sep 17 00:00:00 2001 From: JohnnyChen Date: Mon, 16 Dec 2019 17:59:42 +0800 Subject: [PATCH 3/4] add one more test case --- test/warp.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/warp.jl b/test/warp.jl index 0daabbb..a4e0420 100644 --- a/test/warp.jl +++ b/test/warp.jl @@ -438,6 +438,8 @@ ref_img_pyramid_grid = Float64[ @test size(imrotate(img, 2pi)) == (100, 50) rotated_img = imrotate(imrotate(imrotate(imrotate(img, pi/2), pi/2), pi/2), pi/2) @test rotated_img == img + rotated_img = imrotate(imrotate(img, pi), pi) + @test rotated_img == img end @testset "numerical" begin From ca777ff8eb846e17d0fe47bf1a3c486a08b1a679 Mon Sep 17 00:00:00 2001 From: JohnnyChen Date: Mon, 16 Dec 2019 18:00:09 +0800 Subject: [PATCH 4/4] ImageTransformations v0.8.1 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 1380d79..2efa103 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ImageTransformations" uuid = "02fcd773-0e25-5acc-982a-7f6622650795" -version = "0.8.0" +version = "0.8.1" [deps] AxisAlgorithms = "13072b0f-2c55-5437-9ae7-d433b7a33950"