Skip to content

Commit

Permalink
Fixed Image.sparse_color color structure
Browse files Browse the repository at this point in the history
  • Loading branch information
emcconville committed Oct 1, 2024
1 parent 32d6b96 commit 7fd5f2d
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 36 deletions.
1 change: 1 addition & 0 deletions docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Unreleased.
default value of ``colorspace_type`` from :const:`None` to ``"undefined"``. [:issue:`644`]
- Fixed :meth:`Image.liquid_rescale() <wand.image.BaseImage.liquid_rescale>` behavior
by switching default value of ``delta_x`` from ``0`` to ``1``. [:issue:`653`]
- Fixed :meth:`Image.sparse_color() <wand.image.BaseImage.sparse_color>`'s ``colors`` argument structure to allow multiple (x, y) points with the same color value.
- [TEST] Added Python 3.12 regression test. [:issue:`648` by Thijs Triemstra]


Expand Down
90 changes: 54 additions & 36 deletions wand/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -8410,22 +8410,22 @@ def solarize(self, threshold=0.0, channel=None):

@manipulative
@trap_exception
def sparse_color(self, method, colors, channel_mask=0x7):
def sparse_color(self, method='barycentric', colors=None,
channel_mask=0x7):
"""Interpolates color values between points on an image.
The ``colors`` argument should be a dict mapping
:class:`~wand.color.Color` keys to coordinate tuples.
The ``colors`` argument should be a list of tuples containing the
point's x, y coordinate & :class:`~wand.color.Color` value.
For example::
from wand.color import Color
from wand.image import Image
colors = {
Color('RED'): (10, 50),
Color('YELLOW'): (174, 32),
Color('ORANGE'): (74, 123)
}
colors = [
(10, 50, 'RED'),
(174, 32, 'YELLOW'),
(74, 123, 'ORANGE')
]
with Image(filename='input.png') as img:
img.sparse_color('bilinear', colors)
Expand All @@ -8444,51 +8444,69 @@ def sparse_color(self, method, colors, channel_mask=0x7):
from wand.image import Image, CHANNELS
with Image(filename='input.png') as img:
colors = {
img[50, 50]: (50, 50),
img[100, 50]: (100, 50),
img[50, 75]: (50, 75),
img[100, 100]: (100, 100)
}
colors = [
(50, 50, img[50, 50]),
(100, 50, img[100, 50]),
(50, 75, img[50, 75]),
(100, 100, img[100, 100])
]
# Only apply Voronoi to Red & Alpha channels
mask = CHANNELS['red'] | CHANNELS['alpha']
img.sparse_color('voronoi', colors, channel_mask=mask)
:param method: Interpolate method. See :const:`SPARSE_COLOR_METHODS`
:type method: :class:`str`
:param colors: A dictionary of :class:`~wand.color.Color` keys mapped
to an (x, y) coordinate tuple.
:type colors: :class:`abc.Mapping`
{ :class:`~wand.color.Color`: (int, int) }
:param colors: A list of (x, y, color) tuples.
:type colors: :class:`abc.Iterable`
[ int, int, :class:`~wand.color.Color` ]
:param channel_mask: Isolate specific color channels to apply
interpolation. Default to RGB channels.
:type channel_mask: :class:`numbers.Integral`
.. versionadded:: 0.5.3
"""
assertions.string_in_list(SPARSE_COLOR_METHODS,
'wand.image.SPARSE_COLOR_METHODS',
method=method)
if not isinstance(colors, abc.Mapping):
raise TypeError('Colors must be a dict, not' + repr(colors))
assertions.assert_unsigned_integer(channel_mask=channel_mask)
method_idx = SPARSE_COLOR_METHODS[method]
arguments = list()
for color, point in colors.items():
.. versionchanged:: 0.7.0
The ``colors`` argument switch from a :class:`dict` to
:class:`list` to allow two, or more, points of the same
:class:`~wand.color.Color`.
"""
def __part2arg__(x, y, color):
assertions.assert_real(x=x)
assertions.assert_real(y=y)
arg = [x, y]
if isinstance(color, str):
color = Color(color)
x, y = point
arguments.append(x)
arguments.append(y)
with color as c:
if channel_mask & CHANNELS['red']:
arguments.append(c.red)
arg.append(c.red)
if channel_mask & CHANNELS['green']:
arguments.append(c.green)
arg.append(c.green)
if channel_mask & CHANNELS['blue']:
arguments.append(c.blue)
arg.append(c.blue)
if channel_mask & CHANNELS['alpha']:
arguments.append(c.alpha)
arg.append(c.alpha)
return arg
assertions.string_in_list(SPARSE_COLOR_METHODS,
'wand.image.SPARSE_COLOR_METHODS',
method=method)
assertions.assert_unsigned_integer(channel_mask=channel_mask)
method_idx = SPARSE_COLOR_METHODS[method]
arguments = list()
if isinstance(colors, abc.Mapping):
for color, point in colors.items():
x, y = point
arguments.extend(__part2arg__(x, y, color))
elif isinstance(colors, abc.Iterable):
for part in colors:
if len(part) != 3:
raise ValueError('Expecting color part as (X, Y, color), '
'not ' + repr(part))
x, y, color = part
arguments.extend(__part2arg__(x, y, color))
else:
raise TypeError(
'Colors must be a list or dict, not' + repr(colors)
)
argc = len(arguments)
args = (ctypes.c_double * argc)(*arguments)
if MAGICK_VERSION_NUMBER < 0x700:
Expand Down

0 comments on commit 7fd5f2d

Please sign in to comment.