Merge pull request #4230 from dwastberg/new_chops

Added three new channel operations
This commit is contained in:
Hugo van Kemenade 2020-03-31 10:24:44 +03:00 committed by GitHub
commit c6115c1ceb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 153 additions and 8 deletions

View File

@ -38,6 +38,10 @@ def test_sanity():
ImageChops.blend(im, im, 0.5)
ImageChops.composite(im, im, im)
ImageChops.soft_light(im, im)
ImageChops.hard_light(im, im)
ImageChops.overlay(im, im)
ImageChops.offset(im, 10)
ImageChops.offset(im, 10, 20)
@ -209,8 +213,8 @@ def test_lighter_image():
# Act
new = ImageChops.lighter(im1, im2)
# Assert
assert_image_equal(new, im1)
# Assert
assert_image_equal(new, im1)
def test_lighter_pixel():
@ -275,13 +279,13 @@ def test_offset():
# Act
new = ImageChops.offset(im, xoffset, yoffset)
# Assert
assert new.getbbox() == (0, 45, 100, 96)
assert new.getpixel((50, 50)) == BLACK
assert new.getpixel((50 + xoffset, 50 + yoffset)) == DARK_GREEN
# Assert
assert new.getbbox() == (0, 45, 100, 96)
assert new.getpixel((50, 50)) == BLACK
assert new.getpixel((50 + xoffset, 50 + yoffset)) == DARK_GREEN
# Test no yoffset
assert ImageChops.offset(im, xoffset) == ImageChops.offset(im, xoffset, xoffset)
# Test no yoffset
assert ImageChops.offset(im, xoffset) == ImageChops.offset(im, xoffset, xoffset)
def test_screen():
@ -362,6 +366,45 @@ def test_subtract_modulo_no_clip():
assert new.getpixel((50, 50)) == (241, 167, 127)
def test_soft_light():
# Arrange
im1 = Image.open("Tests/images/hopper.png")
im2 = Image.open("Tests/images/hopper-XYZ.png")
# Act
new = ImageChops.soft_light(im1, im2)
# Assert
assert new.getpixel((64, 64)) == (163, 54, 32)
assert new.getpixel((15, 100)) == (1, 1, 3)
def test_hard_light():
# Arrange
im1 = Image.open("Tests/images/hopper.png")
im2 = Image.open("Tests/images/hopper-XYZ.png")
# Act
new = ImageChops.hard_light(im1, im2)
# Assert
assert new.getpixel((64, 64)) == (144, 50, 27)
assert new.getpixel((15, 100)) == (1, 1, 2)
def test_overlay():
# Arrange
im1 = Image.open("Tests/images/hopper.png")
im2 = Image.open("Tests/images/hopper-XYZ.png")
# Act
new = ImageChops.overlay(im1, im2)
# Assert
assert new.getpixel((64, 64)) == (159, 50, 27)
assert new.getpixel((15, 100)) == (1, 1, 2)
def test_logical():
def table(op, a, b):
out = []

View File

@ -36,6 +36,9 @@ operations in this module).
.. autofunction:: PIL.ImageChops.logical_or
.. autofunction:: PIL.ImageChops.logical_xor
.. autofunction:: PIL.ImageChops.multiply
.. autofunction:: PIL.ImageChops.soft_light
.. autofunction:: PIL.ImageChops.hard_light
.. autofunction:: PIL.ImageChops.overlay
.. py:method:: PIL.ImageChops.offset(image, xoffset, yoffset=None)
Returns a copy of the image where data has been offset by the given

View File

@ -139,6 +139,42 @@ def screen(image1, image2):
return image1._new(image1.im.chop_screen(image2.im))
def soft_light(image1, image2):
"""
Superimposes two images on top of each other using the Soft Light algorithm
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_soft_light(image2.im))
def hard_light(image1, image2):
"""
Superimposes two images on top of each other using the Hard Light algorithm
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_hard_light(image2.im))
def overlay(image1, image2):
"""
Superimposes two images on top of each other using the Overlay algorithm
:rtype: :py:class:`~PIL.Image.Image`
"""
image1.load()
image2.load()
return image1._new(image1.im.chop_overlay(image2.im))
def add(image1, image2, scale=1.0, offset=0):
"""
Adds two images, dividing the result by scale and adding the

View File

@ -2406,6 +2406,38 @@ _chop_subtract_modulo(ImagingObject* self, PyObject* args)
return PyImagingNew(ImagingChopSubtractModulo(self->image, imagep->image));
}
static PyObject*
_chop_soft_light(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
return NULL;
return PyImagingNew(ImagingChopSoftLight(self->image, imagep->image));
}
static PyObject*
_chop_hard_light(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
return NULL;
return PyImagingNew(ImagingChopHardLight(self->image, imagep->image));
}
static PyObject*
_chop_overlay(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
return NULL;
return PyImagingNew(ImagingOverlay(self->image, imagep->image));
}
#endif
@ -3325,6 +3357,10 @@ static struct PyMethodDef methods[] = {
{"chop_and", (PyCFunction)_chop_and, 1},
{"chop_or", (PyCFunction)_chop_or, 1},
{"chop_xor", (PyCFunction)_chop_xor, 1},
{"chop_soft_light", (PyCFunction)_chop_soft_light, 1},
{"chop_hard_light", (PyCFunction)_chop_hard_light, 1},
{"chop_overlay", (PyCFunction)_chop_overlay, 1},
#endif
#ifdef WITH_UNSHARPMASK

View File

@ -146,3 +146,27 @@ ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2)
{
CHOP2(in1[x] - in2[x], NULL);
}
Imaging
ImagingChopSoftLight(Imaging imIn1, Imaging imIn2)
{
CHOP2( (((255-in1[x]) * (in1[x]*in2[x]) ) / 65536) +
(in1[x] * ( 255 - ( (255 - in1[x]) * (255 - in2[x] ) / 255) )) / 255
, NULL );
}
Imaging
ImagingChopHardLight(Imaging imIn1, Imaging imIn2)
{
CHOP2( (in2[x]<128) ? ( (in1[x]*in2[x])/127)
: 255 - ( ((255-in2[x]) * (255-in1[x])) / 127)
, NULL);
}
Imaging
ImagingOverlay(Imaging imIn1, Imaging imIn2)
{
CHOP2( (in1[x]<128) ? ( (in1[x]*in2[x])/127)
: 255 - ( ((255-in1[x]) * (255-in2[x])) / 127)
, NULL);
}

View File

@ -339,6 +339,9 @@ extern Imaging ImagingChopSubtract(
Imaging imIn1, Imaging imIn2, float scale, int offset);
extern Imaging ImagingChopAddModulo(Imaging imIn1, Imaging imIn2);
extern Imaging ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2);
extern Imaging ImagingChopSoftLight(Imaging imIn1, Imaging imIn2);
extern Imaging ImagingChopHardLight(Imaging imIn1, Imaging imIn2);
extern Imaging ImagingOverlay(Imaging imIn1, Imaging imIn2);
/* "1" images only */
extern Imaging ImagingChopAnd(Imaging imIn1, Imaging imIn2);