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

specialize imrotate for 0, 90, 180, 270 degrees #79

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"
Expand Down
30 changes: 25 additions & 5 deletions src/warp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -124,8 +124,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...)
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
# discretization result of pi/2 is exactly pi/2 (or 90°)
max_num_angles = 32760
Comment on lines +130 to +132

Choose a reason for hiding this comment

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

Although the same goes for the current code, why don't you use 32768?

Copy link
Member Author

@johnnychen94 johnnychen94 Dec 20, 2019

Choose a reason for hiding this comment

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

Well... It's like I randomly chose the first number I found that meets the requirement, and I feel this choice doesn't matter for image processing tasks.

θ = round(Int, 180*floor(mod(θ, 2pi)/pi*max_num_angles)/max_num_angles)
tform = recenter(RotMatrix{2}(θ/180*pi), center(img))
Comment on lines +133 to +134

Choose a reason for hiding this comment

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

Don't use the θ! 😱

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])
Comment on lines +137 to +147
Copy link
Contributor

Choose a reason for hiding this comment

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

Choose a reason for hiding this comment

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

I don't think @johnnychen94 want to reallocate the memory and relocate the elements.:smile:

Copy link
Member Author

Choose a reason for hiding this comment

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

True, and there won't be type stability issues if we collect them and return an Array; but that would be slower than views.

I don't have a good fix in mind for this at the moment.

else
return warp(img, tform, args...)
end
end
15 changes: 13 additions & 2 deletions test/warp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -427,15 +427,26 @@ 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
Copy link
Member

Choose a reason for hiding this comment

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

The meaning of this comment is unclear. Do you mean that you wanted to write == but instead have to use isequal?

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
rotated_img = imrotate(imrotate(img, pi), pi)
@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
Expand Down