Skip to content

Commit

Permalink
Merge 18cffb3 into be9cce8
Browse files Browse the repository at this point in the history
  • Loading branch information
emcconville committed Apr 27, 2015
2 parents be9cce8 + 18cffb3 commit 6c8c318
Show file tree
Hide file tree
Showing 10 changed files with 786 additions and 92 deletions.
22 changes: 19 additions & 3 deletions docs/changes.rst
Expand Up @@ -9,15 +9,31 @@ Wand Changelog
Version 0.4.1
-------------

- Added ``gravity`` options in :meth:`Image.crop() <wand.image.BaseImage.crop>` method.
[:issue:`222` by Eric McConville]
- Added :meth:`Image.auto_orient() <wand.image.Image.auto_orient>` that fixes orientation by checking EXIF tags
- Added :meth:`Image.transverse() <wand.image.Image.transverse>` transverse (MagickTransverseImage)
- Added :meth:`Image.transpose() <wand.image.Image.transpose>` transpose (MagickTransposeImage)
- Added :meth:`Image.frame() <wand.image.BaseImage.frame>` method.
- Added :meth:`Image.function() <wand.image.BaseImage.function>` method.
- Added :meth:`Image.fx() <wand.image.BaseImage.fx>` expression method.
- Added ``gravity`` options in :meth:`Image.crop() <wand.image.BaseImage.crop>` method.
[:issue:`222` by Eric McConville]
- Added :meth:`Image.matte() <wand.image.BaseImage.matte>` method for toggling image matte channel.
- Added :attr:`Image.matte_color <wand.image.BaseImage.matte_color>` property.
- Added :attr:`Image.virtual_pixel <wand.image.BaseImage.virtual_pixel>` property.
- Added :meth:`Image.distort() <wand.image.BaseImage.distort>` method.
- Additional query functions have been added to :mod:`wand.version` API. [:issue:`120`]

- Added :func:`configure_options() <wand.version.configure_options>` function.
- Added :func:`fonts() <wand.version.fonts>` function.
- Added :func:`formats() <wand.version.formats>` function.

- Additional IPython support. [:issue:`117`]

- Render RGB :class:`Color <wand.color.Color>` preview.
- Display each frame in image :class:`Sequence <wand.sequence.Sequence>`.

- Fixed Windows memory-deallocate errors on :mod:`wand.drawing` API. [:issue:`226` by Eric McConville]

.. _changelog-0.4.0:

Version 0.4.0
-------------
Expand Down
37 changes: 0 additions & 37 deletions docs/guide/transform.rst
Expand Up @@ -99,40 +99,3 @@ The image :file:`transform-flopped.jpg` generated by the above code looks like:

.. image:: ../_images/transform-flopped.jpg
:alt: transform-flopped.jpg

Chroma Key with FX Expressions
------------------------------

.. versionadded:: 0.4.1

The :meth:`Image.fx() <wand.image.BaseImage.fx>` method is a powerful
tool for evaluating an image's pixel data, and creating an image mask.
Green screen, or Chroma-key compositing, is a common post-production
task for manipulating image data, and can be done with FX expressions.

A transparent mask can be calculated by applying the following
expression::

alpha(red, green, blue) = K1 * blue - K2 * green + K3

Where ``K1``, ``K2`` & ``K3`` are user-defined constants.

.. image:: ../_images/chroma-key.png
:alt: chroma-key.png

This example will assume the value of ``1.0`` for each constant, as the *lime*
color will be keyed.::

from wand.image import Image

with Image(filename='chroma.png') as image:
expression = '{k1} * b - {k2} * g + {k3}'.format(k1=1.0,
k2=1.0,
k3=1.0)
with image.fx(expression) as mask:
image.composite_channel(channel='alpha',
operator='copy_opacity',
image=mask)

.. image:: ../_images/chroma-keyed.png
:alt: chroma-keyed.png
Binary file added tests/assets/orientationtest.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion tests/drawing_test.py
Expand Up @@ -42,7 +42,7 @@ def test_set_get_font(fx_wand, fx_asset):
assert fx_wand.font == str(fx_asset.join('League_Gothic.otf'))

def test_set_get_font_family(fx_wand):
assert fx_wand.font_family == ''
assert fx_wand.font_family is None
fx_wand.font_family = 'sans-serif'
assert fx_wand.font_family == 'sans-serif'

Expand Down
168 changes: 167 additions & 1 deletion tests/image_test.py
Expand Up @@ -13,7 +13,7 @@
from wand.image import ClosedImageError, Image
from wand.color import Color
from wand.compat import PY3, string_type, text, text_type
from wand.exceptions import MissingDelegateError
from wand.exceptions import OptionError, MissingDelegateError
from wand.font import Font


Expand Down Expand Up @@ -320,6 +320,21 @@ def test_set_units(fx_asset):
assert img.units == "pixelspercentimeter"


def test_get_virtual_pixel(fx_asset):
"""Gets image virtual pixel"""
with Image(filename=str(fx_asset.join('mona-lisa.jpg'))) as img:
assert img.virtual_pixel == "undefined"


def test_set_virtual_pixel(fx_asset):
"""Sets image virtual pixel"""
with Image(filename=str(fx_asset.join('mona-lisa.jpg'))) as img:
img.virtual_pixel = "tile"
assert img.virtual_pixel == "tile"
with raises(ValueError):
img.virtual_pixel = "nothing"


def test_get_colorspace(fx_asset):
"""Gets the image colorspace"""
with Image(filename=str(fx_asset.join('mona-lisa.jpg'))) as img:
Expand Down Expand Up @@ -689,6 +704,28 @@ def test_crop_gravity_error(fx_asset):
img.crop(width=1, height=1, gravity='nowhere')


@mark.slow
def test_distort(fx_asset):
"""Distort image."""
with Image(filename=str(fx_asset.join('mona-lisa.jpg'))) as img:
with Color('skyblue') as color:
img.matte_color = color
img.virtual_pixel = 'tile'
img.distort('perspective', (0, 0, 20, 60, 90, 0,
70, 63, 0, 90, 5, 83,
90, 90, 85, 88))
assert img[img.width - 1, 0] == color


def test_distort_error(fx_asset):
"""Distort image with user error"""
with Image(filename=str(fx_asset.join('mona-lisa.jpg'))) as img:
with raises(ValueError):
img.distort('mirror', (1,))
with raises(TypeError):
img.distort('perspective', 1)


@mark.parametrize(('method'), [
('resize'),
('sample'),
Expand Down Expand Up @@ -970,6 +1007,22 @@ def test_set_background_color(fx_asset):
assert img.background_color == color


def test_set_get_matte_color(fx_asset):
with Image(filename='rose:') as img:
with Color('navy') as color:
img.matte_color = color
assert img.matte_color == color
with raises(TypeError):
img.matte_color = False


def test_set_matte(fx_asset):
with Image(filename='rose:') as img:
img.matte(True)
img.matte(False)
with raises(TypeError):
img.matte('true')

def test_transparentize(fx_asset):
with Image(filename=str(fx_asset.join('croptest.png'))) as im:
with Color('transparent') as transparent:
Expand Down Expand Up @@ -1306,6 +1359,94 @@ def test_flop(fx_asset):
assert flopped[-1, -1] == img[0, -1]


def test_frame(fx_asset):
with Image(filename=str(fx_asset.join('mona-lisa.jpg'))) as img:
img.frame(width=4, height=4)
assert img[0, 0] == img[-1, -1]
assert img[-1, 0] == img[0, -1]
with Color('green') as green:
with Image(filename=str(fx_asset.join('mona-lisa.jpg'))) as img:
img.frame(matte=green, width=2, height=2)
assert img[0, 0] == green
assert img[-1, -1] == green


def test_frame_error(fx_asset):
with Image(filename=str(fx_asset.join('mona-lisa.jpg'))) as img:
with raises(TypeError):
img.frame(width='one')
with raises(TypeError):
img.frame(height=3.5)
with raises(TypeError):
img.frame(matte='green')
with raises(TypeError):
img.frame(inner_bevel=None)
with raises(TypeError):
img.frame(outer_bevel='large')


def test_function(fx_asset):
with Image(filename=str(fx_asset.join('croptest.png'))) as img:
img.function(function='polynomial',
arguments=(4, -4, 1))
assert img[150, 150] == Color('white')
img.function(function='sinusoid',
arguments=(1,),
channel='red')
assert abs(img[150, 150].red - Color('#80FFFF').red) < 0.01


def test_function_error(fx_asset):
with Image(filename=str(fx_asset.join('croptest.png'))) as img:
with raises(ValueError):
img.function('bad function', 1)
with raises(TypeError):
img.function('sinusoid', 1)
with raises(ValueError):
img.function('sinusoid', (1,), channel='bad channel')


def test_fx(fx_asset):
with Image(width=2, height=2, background=Color('black')) as xc1:
# NavyBlue == #000080
with xc1.fx('0.5019', channel='blue') as xc2:
assert abs(xc2[0, 0].blue - Color('navy').blue) < 0.0001

with Image(width=2, height=1, background=Color('white')) as xc1:
with xc1.fx('0') as xc2:
assert xc2[0, 0].red == 0


def test_fx_error(fx_asset):
with Image() as empty_wand:
with raises(AttributeError):
with empty_wand.fx('8'):
pass
with Image(filename='rose:') as xc:
with raises(OptionError):
with xc.fx('/0'):
pass
with raises(TypeError):
with xc.fx(('p[0,0]',)):
pass
with raises(ValueError):
with xc.fx('p[0,0]', True):
pass

def test_transpose(fx_asset):
with Image(filename=str(fx_asset.join('beach.jpg'))) as img:
with img.clone() as transposed:
transposed.transpose()
assert transposed[501, 501] == Color('srgb(205,196,179)')


def test_transverse(fx_asset):
with Image(filename=str(fx_asset.join('beach.jpg'))) as img:
with img.clone() as transversed:
transversed.transverse()
assert transversed[500, 500] == Color('srgb(96,136,185)')


def test_get_orientation(fx_asset):
with Image(filename=str(fx_asset.join('sasha.jpg'))) as img:
assert img.orientation == 'undefined'
Expand All @@ -1320,6 +1461,31 @@ def test_set_orientation(fx_asset):
assert img.orientation == 'bottom_right'


def test_auto_orientation(fx_asset):
with Image(filename=str(fx_asset.join('beach.jpg'))) as img:
# if orientation is undefined nothing should be changed
before = img[100, 100]
img.auto_orient()
after = img[100, 100]
assert before == after
assert img.orientation == 'top_left'

with Image(filename=str(fx_asset.join('orientationtest.jpg'))) as original:
with original.clone() as img:
# now we should get a flipped image
assert img.orientation == 'bottom_left'
before = img[100, 100]
img.auto_orient()
after = img[100, 100]
assert before != after
assert img.orientation == 'top_left'

assert img[0, 0] == original[0, -1]
assert img[0, -1] == original[0, 0]
assert img[-1, 0] == original[-1, -1]
assert img[-1, -1] == original[-1, 0]


def test_histogram(fx_asset):
with Image(filename=str(fx_asset.join('trim-color-test.png'))) as a:
h = a.histogram
Expand Down
22 changes: 20 additions & 2 deletions tests/misc_test.py
@@ -1,11 +1,12 @@
import datetime
import numbers
import re
from py.test import mark

from wand.version import (MAGICK_VERSION, MAGICK_VERSION_INFO,
MAGICK_VERSION_NUMBER, MAGICK_RELEASE_DATE,
MAGICK_RELEASE_DATE_STRING, QUANTUM_DEPTH)

MAGICK_RELEASE_DATE_STRING, QUANTUM_DEPTH,
configure_options, fonts, formats)

def test_version():
"""Test version strings."""
Expand All @@ -24,3 +25,20 @@ def test_version():
def test_quantum_depth():
"""QUANTUM_DEPTH must be one of 8, 16, 32, or 64."""
assert QUANTUM_DEPTH in (8, 16, 32, 64)


def test_configure_options():
assert 'RELEASE_DATE' in configure_options('RELEASE_DATE')


def test_fonts():
font_list = fonts()
mark.skipif(not font_list, reason='Fonts not configured on system')
first_font = font_list[0]
first_font_part = first_font[1:-1]
assert first_font in fonts('*{0}*'.format(first_font_part))


def test_formats():
xc = 'XC'
assert formats(xc) == [xc]

0 comments on commit 6c8c318

Please sign in to comment.