Skip to content

Commit

Permalink
Merge pull request #61 from JuliaIO/fixed-renaming
Browse files Browse the repository at this point in the history
Transition to the new Images
  • Loading branch information
timholy committed Jan 29, 2017
2 parents b72cde9 + ed2a9eb commit 5ba08ce
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 338 deletions.
3 changes: 1 addition & 2 deletions .travis.yml
Expand Up @@ -3,15 +3,14 @@ os:
- linux
- osx
julia:
- 0.4
- 0.5
- nightly
notifications:
email: false
# uncomment the following lines to override the default test script
script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia -e 'Pkg.clone("Images");'
- julia -e 'Pkg.add("Images"); Pkg.checkout("Images", "fixed-renaming")'
- julia -e 'Pkg.clone(pwd()); Pkg.build("ImageMagick")'
- julia --check-bounds=yes -e 'Pkg.test("ImageMagick"; coverage=true)'
after_success:
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
@@ -0,0 +1,2 @@
version 0.2.0:
- most images are now returned as an Array rather than the old Images.Image type
6 changes: 2 additions & 4 deletions REQUIRE
@@ -1,8 +1,6 @@
julia 0.4
FixedPointNumbers 0.1.8 0.3.0
julia 0.5
FixedPointNumbers 0.3.0
ColorTypes 0.2.7
FileIO
Images 0.0.0- 0.6.0
Compat 0.8.4
@osx Homebrew
BinDeps
3 changes: 1 addition & 2 deletions appveyor.yml
@@ -1,7 +1,5 @@
environment:
matrix:
- JULIAVERSION: "julialang/bin/winnt/x86/0.4/julia-0.4-latest-win32.exe"
- JULIAVERSION: "julialang/bin/winnt/x64/0.4/julia-0.4-latest-win64.exe"
- JULIAVERSION: "julialang/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe"
- JULIAVERSION: "julialang/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe"
- JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe"
Expand All @@ -28,6 +26,7 @@ build_script:
# Need to convert from shallow to complete for Pkg.clone to work
- IF EXIST .git\shallow (git fetch --unshallow)
- C:\projects\julia\bin\julia-debug -e "versioninfo(); Pkg.init();"
- C:\projects\julia\bin\julia-debug -e "Pkg.add(\"Images\"); Pkg.checkout(\"Images\",\"fixed-renaming\");"
- C:\projects\julia\bin\julia-debug -e "Pkg.clone(pwd(), \"ImageMagick\");"
- C:\projects\julia\bin\julia-debug -e "Pkg.build(\"ImageMagick\")"

Expand Down
199 changes: 86 additions & 113 deletions src/ImageMagick.jl 100755 → 100644
@@ -1,14 +1,17 @@
__precompile__(true)
module ImageMagick

using Compat
import Compat.String

using FixedPointNumbers, ColorTypes, Images, ColorVectorSpace
using FixedPointNumbers, ColorTypes, ColorVectorSpace, ImageCore
using FileIO: DataFormat, @format_str, Stream, File, filename, stream

typealias Color1{T} Color{T,1}
typealias Color2{T,C<:Color1} TransparentColor{C,T,2}
typealias Color3{T} Color{T,3}
typealias Color4{T,C<:Color3} TransparentColor{C,T,4}

export readblob
export image2wand
export magickinfo

include("libmagickwand.jl")

Expand Down Expand Up @@ -55,17 +58,21 @@ load{T <: DataFormat}(imgstream::Stream{T}, args...; key_args...) = load_(stream
load(imgstream::IO, args...; key_args...) = load_(imgstream, args...; key_args...)
save{T <: DataFormat}(imgstream::Stream{T}, args...; key_args...) = save_(imgstream, args...; key_args...)

const ufixedtype = Dict(10=>N6f10, 12=>N4f12, 14=>N2f14, 16=>N0f16)

readblob(data::Vector{UInt8}) = load_(data)

function load_(file::Union{AbstractString,IO,Vector{UInt8}}; ImageType=Image, extraprop="", extrapropertynames=false)
function load_(file::Union{AbstractString,IO,Vector{UInt8}}; ImageType=Array, extraprop="", extrapropertynames=nothing)
if ImageType != Array
error("this function now returns an Array, do not use ImageType keyword.")
end
if extraprop != "" || extrapropertynames != nothing
error("keywords \"extraprop\" and \"extrapropertynames\" no longer work, use magickinfo instead")
end
wand = MagickWand()
readimage(wand, file)
resetiterator(wand)

if extrapropertynames
return(getimageproperties(wand, "*"))
end

# Determine what we need to know about the image format
sz = size(wand)
n = getnumberimages(wand)
Expand All @@ -84,22 +91,18 @@ function load_(file::Union{AbstractString,IO,Vector{UInt8}}; ImageType=Image, ex
# use an even # of fractional bits for depth>8 (see issue 242#issuecomment-68845157)
evendepth = ((depth+1)>>1)<<1
if depth <= 8
T = UFixed{UInt8,8} # otherwise use 8 fractional bits
T = Normed{UInt8,8} # otherwise use 8 fractional bits
elseif depth <= 16
T = UFixed{UInt16,evendepth}
T = Normed{UInt16,evendepth}
else
warn("some versions of ImageMagick give spurious low-order bits for 32-bit TIFFs")
T = UFixed{UInt32,evendepth}
T = Normed{UInt32,evendepth}
end

channelorder = cs
if havealpha
if channelorder == "sRGB" || channelorder == "RGB"
if is_little_endian
T, channelorder = BGRA{T}, "BGRA"
else
T, channelorder = ARGB{T}, "ARGB"
end
T, channelorder = RGBA{T}, "RGBA"
elseif channelorder == "Gray"
T, channelorder = GrayA{T}, "IA"
else
Expand All @@ -115,39 +118,22 @@ function load_(file::Union{AbstractString,IO,Vector{UInt8}}; ImageType=Image, ex
end
end
# Allocate the buffer and get the pixel data
buf = Array(T, sz...)
exportimagepixels!(buf, wand, cs, channelorder)
buf = Array{T}(sz)
exportimagepixels!(rawview(channelview(buf)), wand, cs, channelorder)

prop = Dict{Compat.UTF8String, Any}()
orient = getimageproperty(wand, "exif:Orientation", false)
if haskey(orientation_dict, orient)
prop["spatialorder"] = orientation_dict[orient]
else
warn("orientation $orient not yet supported")
prop["spatialorder"] = ["x", "y"]
end
n > 1 && (prop["timedim"] = ndims(buf))
prop["colorspace"] = cs

if extraprop != ""
for extra in [extraprop;]
prop[extra] = getimageproperty(wand,extra)
end
end

ImageType(buf, prop)
orientation_dict[orient](buf)
end



function save_(filename::AbstractString, img, permute_horizontal=true; mapi = mapinfo(img), quality = nothing)
function save_(filename::AbstractString, img, permute_horizontal=true; mapi = identity, quality = nothing)
wand = image2wand(img, mapi, quality, permute_horizontal)
writeimage(wand, filename)
end

# This differs from `save_` for files because this is primarily used
# by IJulia, and we want to restrict large images to make display faster.
function save_(s::Stream, img, permute_horizontal=true; mapi = Images.mapinfo_writemime(img), quality = nothing)
function save_(s::Stream, img, permute_horizontal=true; mapi = clamp01nan, quality = nothing)
wand = image2wand(img, mapi, quality, permute_horizontal)
blob = getblob(wand, formatstring(s))
write(stream(s), blob)
Expand All @@ -156,34 +142,31 @@ end
function image2wand(img, mapi, quality, permute_horizontal=true)
local imgw
try
imgw = map(mapi, img)
imgw = map(x->mapIM(mapi(x)), img)
catch
warn("Mapping to the storage type failed; perhaps your data had out-of-range values?\nTry `map(Images.Clamp01NaN(img), img)` to clamp values to a valid range.")
warn("Mapping to the storage type failed; perhaps your data had out-of-range values?\nTry `map(clamp01nan, img)` to clamp values to a valid range.")
rethrow()
end
permute_horizontal && (imgw = permutedims_horizontal(imgw))
have_color = colordim(imgw)!=0
if ndims(imgw) > 3+have_color
if ndims(imgw) > 3
error("At most 3 dimensions are supported")
end
wand = MagickWand()
if haskey(img, "colorspace")
cs = img["colorspace"]
else
cs = colorspace(imgw)
end
if in(cs, ("RGB", "RGBA", "ARGB", "BGRA", "ABGR"))
T = eltype(imgw)
channelorder = T<:Real ? "Gray" : ColorTypes.colorant_string(T)
if T <: Union{RGB,RGBA,ARGB,BGRA,ABGR}
cs = libversion > v"6.7.5" ? "sRGB" : "RGB"
else
cs = channelorder
end
channelorder = colorspace(imgw)
if channelorder == "Gray"
channelorder = "I"
elseif channelorder == "GrayA"
channelorder = "IA"
elseif channelorder == "AGray"
channelorder = "AI"
end
tmp = to_explicit(to_contiguous(data(imgw)))
tmp = to_explicit(to_contiguous(imgw))
constituteimage(tmp, wand, cs, channelorder)
if quality != nothing
setimagecompressionquality(wand, quality)
Expand All @@ -194,78 +177,68 @@ end

formatstring{S}(s::Stream{DataFormat{S}}) = string(S)

# ImageMagick mapinfo client. Converts to RGB and uses UFixed.
mapinfo(img::AbstractArray{Bool}) = MapNone{UFixed8}()
mapinfo{T<:UFixed}(img::AbstractArray{T}) = MapNone{T}()
mapinfo{T<:AbstractFloat}(img::AbstractArray{T}) = MapNone{UFixed8}()
let handled = Set()
for ACV in (Color, AbstractRGB)
for CV in subtypes(ACV)
(length(CV.parameters) == 1 && !(CV.abstract)) || continue
CVnew = CV<:AbstractGray ? Gray : RGB
@eval mapinfo{T<:UFixed}(img::AbstractArray{$CV{T}}) = MapNone{$CVnew{T}}()
@eval mapinfo{CV<:$CV}(img::AbstractArray{CV}) = MapNone{$CVnew{UFixed8}}()
CVnew = CV<:AbstractGray ? Gray : BGR
AC, CA = alphacolor(CV), coloralpha(CV)
if AC in handled
continue
end
push!(handled, AC)
ACnew, CAnew = alphacolor(CVnew), coloralpha(CVnew)
@eval begin
mapinfo{T<:UFixed}(img::AbstractArray{$AC{T}}) = MapNone{$ACnew{T}}()
mapinfo{P<:$AC}(img::AbstractArray{P}) = MapNone{$ACnew{UFixed8}}()
mapinfo{T<:UFixed}(img::AbstractArray{$CA{T}}) = MapNone{$CAnew{T}}()
mapinfo{P<:$CA}(img::AbstractArray{P}) = MapNone{$CAnew{UFixed8}}()
end
end
end
function magickinfo(file::Union{AbstractString,IO})
wand = MagickWand()
readimage(wand, file)
resetiterator(wand)
getimageproperties(wand, "*")
end
mapinfo(img::AbstractArray{RGB24}) = MapNone{RGB{UFixed8}}()
mapinfo(img::AbstractArray{ARGB32}) = MapNone{BGRA{UFixed8}}()


# Make the data contiguous in memory, this is necessary for
# imagemagick since it doesn't handle stride.
to_contiguous(A::Array) = A
to_contiguous(A::AbstractArray) = copy(A)
to_contiguous(A::SubArray) = copy(A)
function magickinfo(file::Union{AbstractString,IO}, properties::Union{Tuple,AbstractVector})
wand = MagickWand()
readimage(wand, file)
resetiterator(wand)

to_explicit(A::Image) = to_explicit(data(A))
to_explicit(A::AbstractArray) = to_explicit(copy(A))
props = Dict{String,Any}()
for p in properties
props[p] = getimageproperty(wand, p)
end
props
end
magickinfo(file::Union{AbstractString,IO}, properties...) = magickinfo(file, properties)

to_explicit{T<:UFixed}(A::Array{T}) = reinterpret(FixedPointNumbers.rawtype(T), A)

to_explicit{T<:UFixed}(A::Array{RGB{T}}) = reinterpret(FixedPointNumbers.rawtype(T), A, tuple(3, size(A)...))
to_explicit{T<:AbstractFloat}(A::Array{RGB{T}}) = to_explicit(map(ClampMinMax(RGB{UFixed8}, zero(RGB{T}), one(RGB{T})), A))
to_explicit{T<:UFixed}(A::Array{Gray{T}}) = reinterpret(FixedPointNumbers.rawtype(T), A, size(A))
to_explicit{T<:AbstractFloat}(A::Array{Gray{T}}) = to_explicit(map(ClampMinMax(Gray{UFixed8}, zero(Gray{T}), one(Gray{T})), A))
# ImageMagick element-mapping function. Converts to RGB/RGBA and uses
# N0f8 "inner" element type.
mapIM(c::Color1) = mapIM(convert(Gray, c))
mapIM{T}(c::Gray{T}) = convert(Gray{N0f8}, c)
mapIM{T<:Normed}(c::Gray{T}) = c

to_explicit{T<:UFixed}(A::Array{GrayA{T}}) = reinterpret(FixedPointNumbers.rawtype(T), A)
to_explicit{T<:AbstractFloat}(A::Array{GrayA{T}}) = to_explicit(map(ClampMinMax(GrayA{UFixed8}, zero(GrayA{T}), one(GrayA{T})), A))
mapIM(c::Color2) = mapIM(convert(GrayA, c))
mapIM{T}(c::GrayA{T}) = convert(GrayA{N0f8}, c)
mapIM{T<:Normed}(c::GrayA{T}) = c

to_explicit{T<:UFixed}(A::Array{BGRA{T}}) = reinterpret(FixedPointNumbers.rawtype(T), A, tuple(4, size(A)...))
to_explicit{T<:AbstractFloat}(A::Array{BGRA{T}}) = to_explicit(map(ClampMinMax(BGRA{UFixed8}, zero(BGRA{T}), one(BGRA{T})), A))
to_explicit{T<:UFixed}(A::Array{RGBA{T}}) = reinterpret(FixedPointNumbers.rawtype(T), A, tuple(4, size(A)...))
to_explicit{T<:AbstractFloat}(A::Array{RGBA{T}}) = to_explicit(map(ClampMinMax(RGBA{UFixed8}, zero(RGBA{T}), one(RGBA{T})), A))
mapIM(c::Color3) = mapIM(convert(RGB, c))
mapIM{T}(c::RGB{T}) = convert(RGB{N0f8}, c)
mapIM{T<:Normed}(c::RGB{T}) = c

mapIM(c::Color4) = mapIM(convert(RGBA, c))
mapIM{T}(c::RGBA{T}) = convert(RGBA{N0f8}, c)
mapIM{T<:Normed}(c::RGBA{T}) = c

mapIM(x::UInt8) = reinterpret(N0f8, x)
mapIM(x::Bool) = convert(N0f8, x)
mapIM(x::AbstractFloat) = convert(N0f8, x)
mapIM(x::Normed) = x

# Permute to a color, horizontal, vertical, ... storage order (with time always last)
function permutation_horizontal(img)
cd = colordim(img)
td = timedim(img)
p = spatialpermutation(["x", "y"], img)
if cd != 0
p[p .>= cd] += 1
insert!(p, 1, cd)
end
if td != 0
push!(p, td)
end
p
# Make the data contiguous in memory, this is necessary for
# imagemagick since it doesn't handle stride.
to_contiguous(A::Array) = A
to_contiguous(A::AbstractArray) = copy(A)
to_contiguous(A::SubArray) = copy(A)
to_contiguous(A::BitArray) = convert(Array{N0f8}, A)
to_contiguous(A::ColorView) = to_contiguous(channelview(A))

to_explicit{C<:Colorant}(A::Array{C}) = to_explicit(channelview(A))
to_explicit{T}(A::ChannelView{T}) = to_explicit(copy!(Array{T}(size(A)), A))
to_explicit{T<:Normed}(A::Array{T}) = rawview(A)
to_explicit{T<:AbstractFloat}(A::Array{T}) = to_explicit(convert(Array{N0f8}, A))

permutedims_horizontal(img::AbstractVector) = img
function permutedims_horizontal(img)
# Vertical-major is hard-coded here
p = [2;1;3:ndims(img)]
permutedims(img, p)
end

permutedims_horizontal(img) = permutedims(img, permutation_horizontal(img))

end # module

0 comments on commit 5ba08ce

Please sign in to comment.