-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Use IndirectArrays for color dithering * Rewrite of ordered dithering, removing `TiledIteration` dependency * Performance enhancements
- Loading branch information
Showing
16 changed files
with
261 additions
and
239 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# All functions in this file end up calling `binarydither!` and don't return IndirectArrays. | ||
|
||
""" | ||
dither!([out,] img, alg::AbstractDither, args...; kwargs...) | ||
Dither image `img` using algorithm `alg`. | ||
# Output | ||
If `out` is specified, it will be changed in place. Otherwise `img` will be changed in place. | ||
""" | ||
dither! | ||
|
||
""" | ||
dither([T::Type,] img, alg::AbstractDither, args...; kwargs...) | ||
Dither image `img` using algorithm `alg`. | ||
# Output | ||
If no return type is specified, `dither` will default to the type of the input image. | ||
""" | ||
dither | ||
|
||
############## | ||
# Public API # | ||
############## | ||
|
||
# If `out` is specified, it will be changed in place... | ||
function dither!(out::GenericImage, img::GenericImage, alg::AbstractDither; kwargs...) | ||
if size(out) != size(img) | ||
throw( | ||
ArgumentError( | ||
"out and img should have the same shape, instead they are $(size(out)) and $(size(img))", | ||
), | ||
) | ||
end | ||
return _binarydither!(out, img, alg; kwargs...) | ||
end | ||
|
||
# ...otherwise `img` will be changed in place. | ||
function dither!(img::GenericImage, alg::AbstractDither; kwargs...) | ||
tmp = copy(img) | ||
return _binarydither!(img, tmp, alg; kwargs...) | ||
end | ||
|
||
# Otherwise the return type can be chosen... | ||
function dither(::Type{T}, img::GenericImage, alg::AbstractDither; kwargs...) where {T} | ||
out = similar(Array{T}, axes(img)) | ||
return _binarydither!(out, img, alg; kwargs...) | ||
end | ||
|
||
# ...and defaults to the type of the input image. | ||
function dither(img::GenericImage{T,N}, alg::AbstractDither; kwargs...) where {T<:Pixel,N} | ||
return dither(T, img, alg; kwargs...) | ||
end | ||
|
||
############################# | ||
# Low-level algorithm calls # | ||
############################# | ||
|
||
# Dispatch to binary dithering on grayscale images | ||
# when no color palette is provided | ||
function _binarydither!( | ||
out::GenericGrayImage, | ||
img::GenericGrayImage, | ||
alg::AbstractDither; | ||
to_linear=false, | ||
kwargs..., | ||
) where {T} | ||
to_linear && (img = srgb2linear.(img)) | ||
return binarydither!(alg, out, img; kwargs...) | ||
end | ||
|
||
# Dispatch to per-channel dithering on color images when no color palette is provided | ||
function _binarydither!( | ||
out::GenericImage{T,2}, | ||
img::GenericImage{T,2}, | ||
alg::AbstractDither; | ||
to_linear=false, | ||
kwargs..., | ||
) where {T<:Color{<:Real,3}} | ||
to_linear && (@warn "Skipping transformation `to_linear` when dithering color images.") | ||
|
||
cvout = channelview(out) | ||
cvimg = channelview(img) | ||
for c in axes(cvout, 1) | ||
# Note: the input `out` will be modified | ||
# since binarydither! modifies the view of the channelview of `out`. | ||
binarydither!(alg, view(cvout, c, :, :), view(cvimg, c, :, :)) | ||
end | ||
return out | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# Binary dithering and color dithering can be distinguished by the extra argument `arg`, | ||
# which is either | ||
# - a color scheme (array of colors) | ||
# - a ColorSchemes.jl symbol | ||
# - the number of colors specified for clustering | ||
# | ||
# All functions in this file end up calling `colordither` and return IndirectArrays. | ||
|
||
struct ColorNotImplementedError <: Exception | ||
algname::String | ||
ColorNotImplementedError(alg::AbstractDither) = new("$alg") | ||
end | ||
function Base.showerror(io::IO, e::ColorNotImplementedError) | ||
return print( | ||
io, e.algname, " algorithm currently doesn't support custom color palettes." | ||
) | ||
end | ||
colordither(alg, img, cs, metric) = throw(ColorNotImplementedError(alg)) | ||
|
||
############## | ||
# Public API # | ||
############## | ||
|
||
# If `out` is specified, it will be changed in place... | ||
function dither!(out::GenericImage, img::GenericImage, alg::AbstractDither, arg; kwargs...) | ||
if size(out) != size(img) | ||
throw( | ||
ArgumentError( | ||
"out and img should have the same shape, instead they are $(size(out)) and $(size(img))", | ||
), | ||
) | ||
end | ||
return out .= _colordither(eltype(out), img, alg, arg; kwargs...) | ||
end | ||
|
||
# ...otherwise `img` will be changed in place. | ||
function dither!(img::GenericImage, alg::AbstractDither, arg; kwargs...) | ||
return img .= _colordither(eltype(img), img, alg, arg; kwargs...) | ||
end | ||
|
||
# The return type can be chosen... | ||
function dither(::Type{T}, img::GenericImage, alg::AbstractDither, arg; kwargs...) where {T} | ||
return _colordither(T, img, alg, arg; kwargs...) | ||
end | ||
|
||
# ...and defaults to the type of the input image. | ||
function dither( | ||
img::GenericImage{T,N}, alg::AbstractDither, arg; kwargs... | ||
) where {T<:Pixel,N} | ||
return _colordither(T, img, alg, arg; kwargs...) | ||
end | ||
|
||
############################# | ||
# Low-level algorithm calls # | ||
############################# | ||
|
||
# Dispatch to dithering with custom color palettes on any image type | ||
# when color palette is provided | ||
function _colordither( | ||
::Type{T}, | ||
img::GenericImage, | ||
alg::AbstractDither, | ||
cs::AbstractVector{<:Pixel}; | ||
metric::DifferenceMetric=DE_2000(), | ||
to_linear=false, | ||
) where {T} | ||
to_linear && (@warn "Skipping transformation `to_linear` when dithering in color.") | ||
length(cs) >= 2 || | ||
throw(DomainError(steps, "Color scheme for dither needs >= 2 colors.")) | ||
|
||
index = colordither(alg, img, cs, metric) | ||
return IndirectArray(index, T.(cs)) | ||
end | ||
|
||
# A special case occurs when a grayscale output image is to be dithered in colors. | ||
# Since this is not possible, instead the return image will be of type of the color scheme. | ||
function _colordither( | ||
::Type{T}, | ||
img::GenericImage, | ||
alg::AbstractDither, | ||
cs::AbstractVector{<:Color{<:Any,3}}; | ||
metric::DifferenceMetric=DE_2000(), | ||
to_linear=false, | ||
) where {T<:NumberLike} | ||
return _colordither(eltype(cs), img, alg, cs; metric=metric, to_linear=to_linear) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
# These functions are only conditionally loaded with ColorSchemes.jl | ||
function _dither!(out, img, alg, cs::ColorSchemes.ColorScheme; kwargs...) | ||
return _dither!(out, img, alg, cs.colors) | ||
function _colordither(T, img, alg, cs::ColorSchemes.ColorScheme; kwargs...) | ||
return _colordither(T, img, alg, cs.colors) | ||
end | ||
|
||
function _dither!(out, img, alg, csname::Symbol; kwargs...) | ||
function _colordither(T, img, alg, csname::Symbol; kwargs...) | ||
cs = ColorSchemes.colorschemes[csname] | ||
return _dither!(out, img, alg, cs.colors; kwargs...) | ||
return _colordither(T, img, alg, cs.colors; kwargs...) | ||
end |
Oops, something went wrong.