From 200ffe84b8eef59df58ec2d38d41fa428ad16e00 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 26 Apr 2016 05:25:16 -0500 Subject: [PATCH] Support ScaleMinMax for To<:Integer Integer-valued images are not generally recommended (deciding contrast is a mess), but it's worth having them work. Note this also improves overflow-safety (for From<:Signed). --- src/map.jl | 22 +++++++++++++++------- test/map.jl | 10 ++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/map.jl b/src/map.jl index dc1d9735..e9e4a354 100644 --- a/src/map.jl +++ b/src/map.jl @@ -235,6 +235,9 @@ immutable ScaleMinMax{To,From,S<:AbstractFloat} <: MapInfo{To} end ScaleMinMax{To,From}(::Type{To}, min::From, max::From, s::AbstractFloat) = ScaleMinMax{To,From,typeof(s)}(min, max, s) +ScaleMinMax{To,From}(::Type{To}, min::From, max::From, s) = ScaleMinMax(To, min, max, convert_float(To, Float32, s)) +convert_float{To<:AbstractFloat,T}(::Type{To}, ::Type{T}, s) = convert(To, s) +convert_float{To,T}(::Type{To}, ::Type{T}, s) = convert(T, s) ScaleMinMax{To<:Union{Fractional,Colorant},From}(::Type{To}, mn::From, mx::From) = ScaleMinMax(To, mn, mx, 1.0f0/(convert(Float32, mx)-convert(Float32, mn))) # ScaleMinMax constructors that take AbstractArray input @@ -249,20 +252,22 @@ similar{T,F,To,From,S}(mapi::ScaleMinMax{To,From,S}, ::Type{T}, ::Type{F}) = Sca # Implementation function immap{To<:Union{Real,AbstractGray},From<:Union{Real,AbstractGray}}(mapi::ScaleMinMax{To,From}, val::From) - g = gray(val) - t = ifelse(g < mapi.min, zero(From), ifelse(g > mapi.max, mapi.max-mapi.min, g-mapi.min)) - convert(To, mapi.s*t) + t = clamp(gray(val), gray(mapi.min), gray(mapi.max)) + f = mapi.s*t - mapi.s*mapi.min # better than mapi.s*(t-mapi.min) (overflow) + convertsafely(To, f) end function immap{To<:Union{Real,AbstractGray},From<:Union{Real,AbstractGray}}(mapi::ScaleMinMax{To,From}, val::Union{Real,Colorant}) immap(mapi, convert(From, val)) end function map1{To<:Union{RGB24,ARGB32},From<:Real}(mapi::ScaleMinMax{To,From}, val::From) - t = ifelse(val < mapi.min, zero(From), ifelse(val > mapi.max, mapi.max-mapi.min, val-mapi.min)) - convert(UFixed8, mapi.s*t) + t = clamp(val, mapi.min, mapi.max) + f = mapi.s*t - mapi.s*mapi.min + convert(UFixed8, f) end function map1{To<:Colorant,From<:Real}(mapi::ScaleMinMax{To,From}, val::From) - t = ifelse(val < mapi.min, zero(From), ifelse(val > mapi.max, mapi.max-mapi.min, val-mapi.min)) - convert(eltype(To), mapi.s*t) + t = clamp(val, mapi.min, mapi.max) + f = mapi.s*t - mapi.s*mapi.min + convertsafely(eltype(To), f) end function map1{To<:Union{RGB24,ARGB32},From<:Real}(mapi::ScaleMinMax{To,From}, val::Union{Real,Colorant}) map1(mapi, convert(From, val)) @@ -695,3 +700,6 @@ end ufixedsc{T<:UFixed}(::Type{T}, img::AbstractImageDirect) = immap(mapinfo(T, img), img) ufixed8sc(img::AbstractImageDirect) = ufixedsc(UFixed8, img) + +convertsafely{T}(::Type{T}, val) = convert(T, val) +convertsafely{T<:Integer}(::Type{T}, val::AbstractFloat) = round(T, val) diff --git a/test/map.jl b/test/map.jl index 6ff37a7a..be13b60c 100755 --- a/test/map.jl +++ b/test/map.jl @@ -174,6 +174,16 @@ facts("Map") do @fact map(Clamp01NaN(A), A) --> [0.0 0.5; 1.0 0.0] B = colorim(repeat(reshape(A, (1,2,2)), outer=[3,1,1])) @fact map(Clamp01NaN(B), B) --> [RGB(0.0,0,0) RGB(0.5,0.5,0.5); RGB(1.0,1,1) RGB(0.0,0,0)] + # Integer-valued images are not recommended, but let's at + # least make sure they work + smm = ScaleMinMax(UInt8, 0.0, 1.0, 255) + @fact map(smm, 0.0) --> exactly(0x00) + @fact map(smm, 1.0) --> exactly(0xff) + @fact map(smm, 0.1) --> exactly(round(UInt8, 0.1*255.0f0)) + smm = ScaleMinMax(Gray{U8}, typemin(Int8), typemax(Int8)) + @fact map(smm, 2) --> Gray{U8}(0.51) + smm = ScaleMinMax(RGB24, typemin(Int8), typemax(Int8)) + @fact map(smm, 2) --> RGB24(0x828282) end context("ScaleSigned") do