Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImageOps.autocontrast: add mask parameter #4843

Merged
merged 13 commits into from Aug 22, 2020
Merged

Conversation

navneeth
Copy link
Contributor

@navneeth navneeth commented Aug 6, 2020

This is a draft of an enhancement to autocontrast that allows the specification of a mask to constrain the contrast stretching.
autocontrast(image, mask=a_mask)

Perhaps it Fixes # #3331.

Changes proposed in this pull request:

  • adding an optional mask argument. if none is specified, behaviour defaults to how it was before.
  • added tests in Tests/test_imageops.py
  • also added tests for cutoff argument, where the tuple case was not being checked.

This is my first contribution to pillow. So do let me know if this suffices.

Copy link
Member

@hugovk hugovk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

Here's some suggestions.

To fix the lint, you can do this:

pip install -U black
black .

pip install -U isort==4.3.21
isort -y

Or manually apply the diff from https://github.com/python-pillow/Pillow/pull/4843/checks#step:8:57.

And we should probably add release notes for this. But we can follow up with that later :)

src/PIL/ImageOps.py Outdated Show resolved Hide resolved
src/PIL/ImageOps.py Outdated Show resolved Hide resolved
Tests/test_imageops.py Show resolved Hide resolved
x1, y1 = 3*img.size[0]//4, 3*img.size[1]//4
draw.rectangle((x0, y0, x1, y1), fill=255)

assert ImageOps.autocontrast(img, mask=rect_mask) != ImageOps.autocontrast(img)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test asserts that an autocontrasted image with a mask is different an autocontrasted image without a mask. Okay.

But for all we know the masked image could be a blank black image. Could you add something to actually test for correctness of the masked output?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. That will require some deeper thought. What you are suggesting is a functionality
test for the autocontrast method.
I noticed that the baseline autocontrast did not have such a functional test in place.

  1. So I guess the test would apply to both ?
  2. I would likely have to add a custom test image for such a test. Any guidelines on this ?

How about we close this PR and follow up with the functional test in another PR ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could test an arbitrary pixel is something expected. For example:

diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py
index 3d5223d9..c5ad670a 100644
--- a/Tests/test_imageops.py
+++ b/Tests/test_imageops.py
@@ -342,3 +342,5 @@ def test_auto_contrast_mask_real_input():
         result_nomask = ImageOps.autocontrast(img)

         assert result_nomask != result
+        assert result.getpixel((20, 30)) == (239, 239, 242)
+        assert result_nomask.getpixel((20, 30)) == (132, 118, 96)

No need to close and create a new PR, we can update this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just added code to test for explicit pixel value in the test. Rather than look for a pixel value it compares the stats (median) of the autocontrast (with mask) and autocontrast (without mask). This logic draws from the theory of autocontrast and should scale well. PR 01aeaa4

result = ImageOps.autocontrast(img, mask=rect_mask)
result_nomask = ImageOps.autocontrast(img)

assert result_nomask != result
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here about testing for correctness.

navneeth and others added 3 commits August 7, 2020 14:15
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
@navneeth
Copy link
Contributor Author

navneeth commented Aug 7, 2020

Thank you for the thorough and sensible review. I genuinely appreciate what you do.
cheers.
Just submitted an updated PR with the changes.

Tests/test_imageops.py Outdated Show resolved Hide resolved
x1, y1 = 3*img.size[0]//4, 3*img.size[1]//4
draw.rectangle((x0, y0, x1, y1), fill=255)

assert ImageOps.autocontrast(img, mask=rect_mask) != ImageOps.autocontrast(img)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could test an arbitrary pixel is something expected. For example:

diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py
index 3d5223d9..c5ad670a 100644
--- a/Tests/test_imageops.py
+++ b/Tests/test_imageops.py
@@ -342,3 +342,5 @@ def test_auto_contrast_mask_real_input():
         result_nomask = ImageOps.autocontrast(img)

         assert result_nomask != result
+        assert result.getpixel((20, 30)) == (239, 239, 242)
+        assert result_nomask.getpixel((20, 30)) == (132, 118, 96)

No need to close and create a new PR, we can update this one.

@navneeth
Copy link
Contributor Author

navneeth commented Aug 12, 2020

Updated the PR with code to test for explicit pixel value in the test. Rather than look for a pixel value it compares the stats (median) of the autocontrast (with mask) and autocontrast (without mask). This logic draws from the theory of autocontrast and should scale well. Please see PR 2d3a841

@hugovk
Copy link
Member

hugovk commented Aug 19, 2020

I've updated this PR with release notes:

@hugovk
Copy link
Member

hugovk commented Aug 22, 2020

Thank you!

@hugovk hugovk merged commit 9c6d0bd into python-pillow:master Aug 22, 2020
@radarhere radarhere changed the title ENH: Autocontrast method enhancement - mask specification for computation Autocontrast method enhancement - mask specification for computation Aug 30, 2020
@radarhere radarhere changed the title Autocontrast method enhancement - mask specification for computation ImageOps.autocontrast: add mask parameter Aug 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants