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

Blue noise sampling #3531

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open

Blue noise sampling #3531

wants to merge 7 commits into from

Conversation

rougier
Copy link
Contributor

@rougier rougier commented Nov 2, 2018

Description

This PR implement blue noise sampling following proposal #2380. I'm not quite sure how to add the associated example in doc/examples.

Checklist

[It's fine to submit PRs which are a work in progress! But before they are merged, all PRs should provide:]

[For detailed information on these and other aspects see scikit-image contribution guidelines]

References

[If this is a bug-fix or enhancement, it closes issue # ]
[If this is a new feature, it implements the following paper: ]

For reviewers

(Don't remove the checklist below.)

  • Check that the PR title is short, concise, and will make sense 1 year
    later.
  • Check that new functions are imported in corresponding __init__.py.
  • Check that new features, API changes, and deprecations are mentioned in
    doc/release/release_dev.rst.
  • Consider backporting the PR with @meeseeksdev backport to v0.14.x

@pep8speaks
Copy link

pep8speaks commented Nov 2, 2018

Hello @rougier! Thanks for updating the PR.

Line 17:1: E302 expected 2 blank lines, found 1
Line 25:1: E305 expected 2 blank lines after class or function definition, found 1
Line 28:34: E231 missing whitespace after ','
Line 29:14: E231 missing whitespace after ','
Line 29:22: E231 missing whitespace after ','
Line 34:10: E226 missing whitespace around arithmetic operator
Line 34:31: E226 missing whitespace around arithmetic operator
Line 34:36: E226 missing whitespace around arithmetic operator
Line 35:10: E226 missing whitespace around arithmetic operator
Line 35:31: E226 missing whitespace around arithmetic operator
Line 35:36: E226 missing whitespace around arithmetic operator
Line 37:1: W293 blank line contains whitespace
Line 40:14: E231 missing whitespace after ','
Line 40:22: E231 missing whitespace after ','
Line 41:1: W293 blank line contains whitespace

Line 8:1: E302 expected 2 blank lines, found 1
Line 16:48: W291 trailing whitespace
Line 44:25: E226 missing whitespace around arithmetic operator
Line 62:1: W293 blank line contains whitespace
Line 80:34: E231 missing whitespace after ','
Line 80:36: E226 missing whitespace around arithmetic operator
Line 83:80: E501 line too long (85 > 79 characters)
Line 90:1: W293 blank line contains whitespace

Comment last updated on November 04, 2018 at 17:41 Hours UTC

@stefanv
Copy link
Member

stefanv commented Nov 3, 2018

Thanks, @rougier!

It looks like this should also be added to random_noise.

To add a gallery example, make the directory doc/examples/utils and add plot_noise.py in there. For an example of how those files are formatted, take a look at any of the other examples.

Two-dimensional domain (width x height) over which to sample noise
radius : float
Minimum distance between samples
k : int
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
k : int
k : int, optional

Copy link
Member

@sciunto sciunto 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 submitting! I made several comments, most of them on the style, and few suggestions on the code itself.

Minimum distance between samples
k : int
Limit of samples to choose before rejection (typically k = 30)
seed : int
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
seed : int
seed : int, optional


This function implements the method introduced in "Fast Poisson Disk
Sampling in Arbitrary Dimensions, Robert Bridson, Siggraph, 2007" for
generating (fast) blue noise.
Copy link
Member

Choose a reason for hiding this comment

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

When we want to add a reference, we add a specific section for that. Take a look here for instance: https://github.com/scikit-image/scikit-image/blob/master/skimage/filters/thresholding.py#L318 Note also how to put a DOI.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, done.


See also:
---------
https://github.com/scikit-image/scikit-image/issues/2380
Copy link
Member

Choose a reason for hiding this comment

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

The See also section is generally not used for that. Here, I would not refer to the issue...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed it.


def blue_noise(shape, radius=0.01, k=30, seed=None):
"""
Function to add random noise of various types to a floating-point image.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Function to add random noise of various types to a floating-point image.
Add random noise of various types to a floating-point image.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed it (I wrongly copied it from the random_noise function)


"""

def squared_distance(p0, p1):
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would be a bit overkill, no?

def in_limits(p):
return 0 <= p[0] < width and 0 <= p[1] < height

def neighborhood(shape, index, n=2):
Copy link
Member

Choose a reason for hiding this comment

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

Can you put comment to explain what the function is doing please?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added docstring for all of them.

I.remove([row, col])
return I

def in_neighborhood(p):
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def in_neighborhood(p):
def in_neighborhood(p, squared_radius):

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right. I've changed it but in the process some other functions are using implicit global variables. Would you prefer I tried to remove every inside function? I might be able to remove most of them (but not all).

Copy link
Member

Choose a reason for hiding this comment

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

I think some of them are not necessary, especially the short ones, So, +1!

del points[i]
Q = random_point_around(p, k)
for q in Q:
if in_limits(q) and not in_neighborhood(q):
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if in_limits(q) and not in_neighborhood(q):
if in_limits(q) and not in_neighborhood(q, squared_radius):

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

i = np.random.randint(len(points))
p = points[i]
del points[i]
Q = random_point_around(p, k)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Q = random_point_around(p, k)
Q = random_point_around(p, radius, k)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

If provided, this will set the random seed before generating noise,
for valid pseudo-random comparisons.

Notes:
Copy link
Member

Choose a reason for hiding this comment

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

Dont use : signs in headers please :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure I get this one...

Copy link
Member

@sciunto sciunto Nov 3, 2018

Choose a reason for hiding this comment

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

Sorry, I wanted to say title.

All titles must look like

title
-----

not

title:
------

:)

@rougier
Copy link
Contributor Author

rougier commented Nov 3, 2018

I've started to modify the source according to review but I think my code is too complicated and could probably be simplified. I'll try to look into that.

@sciunto
Copy link
Member

sciunto commented Nov 3, 2018

Sounds great! Thanks @rougier

----------

.. [1] Fast Poisson Disk Sampling in Arbitrary Dimensions, Robert Bridson,
Siggraph, 2007. :DOI: 10.1145/1278780.1278807
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Siggraph, 2007. :DOI: 10.1145/1278780.1278807
Siggraph, 2007. :DOI:`10.1145/1278780.1278807`

@rougier
Copy link
Contributor Author

rougier commented Nov 3, 2018

I've found the implementation by @emulbreh (see https://github.com/emulbreh/bridson, MIT License) to be more clear and probably faster. I've slightly adapted it.

queue.pop()
for _ in range(k):

# WARNING: p is not uniform sampled but we can live with it here
Copy link
Member

Choose a reason for hiding this comment

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

There is still this line I don't understand, and you didn't reply to me about my suggestion

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I did not see it. I need to pick k points around p in a torus (r,2r) and I used uniform polar coordinates introducing a bias when converted into Cartesian coordinates. I'll fix it and remove the comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And the implementation by @emulbreh is right actually.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure about the suggestion you're referring to

Copy link
Member

Choose a reason for hiding this comment

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

LGTM.

@sciunto
Copy link
Member

sciunto commented Nov 5, 2018

The main task that remains is to right few unittests. This is necessary to include a new feature in scikit-image. Are you familiar with this?

@rougier
Copy link
Contributor Author

rougier commented Nov 5, 2018

Yes. I will write some tests but I need to find relevant ones. Do I need to check both for potentially wrong arguments (this would require additional code for checking correctness of arguments such as for example negative sizes) and also expected statistical properties (approximate number of samples, mean distance between samples, etc.)?

@sciunto
Copy link
Member

sciunto commented Nov 8, 2018

We usually do not perform extensive checks of parameters (you can have a look at other functions). IMO, it is not necessary to add more checks. I suggest to start by covering the existing code. Eventually, if other reviewers ask, we can add more.

@emmanuelle
Copy link
Member

Hi sorry to jump in late. This code is very nice and I love the stippling application, but does it fit in the scope of scikit-image? I can't think of an application in biology, physics, astronomy etc. where one could use blue noise. If I'm wrong (please prove me wrong :-), the gallery example should try to show such a use case. Something like determining markers for a segmentation?

@soupault
Copy link
Member

A followup question: may it be of interest for SciPy integration?

@rougier
Copy link
Contributor Author

rougier commented Nov 10, 2018

@emmanuelle No opinion on that. I'm fine if you prefer not to merge the PR.

@emmanuelle
Copy link
Member

@rougier let's wait for other opinions; in #2380 both @jni and @JDWarner said that they saw places in the package where this could be used.

But perhaps we need to be more explicit about the scope of scikit-image, for example in the contributers documentation (and I'm sure different devs have a slightly different view of the scope of the package: is it mostly for scientific applications? Also for some computer vision applications? Graphics applications? This makes it all the more important to talk and agree about the scope of scikit-image).

@emmanuelle
Copy link
Member

emmanuelle commented Nov 24, 2018

@jni your thoughts here would be welcome

@soupault soupault requested a review from jni August 4, 2019 10:57
Base automatically changed from master to main February 18, 2021 18:23
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

7 participants