-
Notifications
You must be signed in to change notification settings - Fork 300
Perceptual image hashing #2206
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
Perceptual image hashing #2206
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,7 +35,6 @@ | |
| from iris._deprecation import IrisDeprecation | ||
| import iris.plot as iplt | ||
| import iris.quickplot as qplt | ||
| from iris.tests import _DEFAULT_IMAGE_TOLERANCE | ||
|
|
||
|
|
||
| EXAMPLE_DIRECTORY = os.path.join(os.path.dirname(os.path.dirname(__file__)), | ||
|
|
@@ -60,7 +59,7 @@ def add_examples_to_path(): | |
|
|
||
|
|
||
| @contextlib.contextmanager | ||
| def show_replaced_by_check_graphic(test_case, tol=_DEFAULT_IMAGE_TOLERANCE): | ||
| def show_replaced_by_check_graphic(test_case): | ||
| """ | ||
| Creates a context manager which can be used to replace the functionality | ||
| of matplotlib.pyplot.show with a function which calls the check_graphic | ||
|
|
@@ -69,7 +68,7 @@ def show_replaced_by_check_graphic(test_case, tol=_DEFAULT_IMAGE_TOLERANCE): | |
| """ | ||
| def replacement_show(): | ||
| # form a closure on test_case and tolerance | ||
| test_case.check_graphic(tol=tol) | ||
| test_case.check_graphic() | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the hope of refraining from a culture of tweaking graphical testing tolerances, I've closed the door to passing a |
||
|
|
||
| orig_show = plt.show | ||
| plt.show = iplt.show = qplt.show = replacement_show | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,7 +33,7 @@ def test_atlantic_profiles(self): | |
| with fail_any_deprecation_warnings(): | ||
| with add_examples_to_path(): | ||
| import atlantic_profiles | ||
| with show_replaced_by_check_graphic(self, tol=14.0): | ||
| with show_replaced_by_check_graphic(self): | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This covers up an issue in the use of |
||
| atlantic_profiles.main() | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,8 @@ | |
|
|
||
| import collections | ||
| import datetime | ||
| from functools import wraps | ||
| import threading | ||
|
|
||
| import cartopy.crs as ccrs | ||
| import cartopy.mpl.geoaxes | ||
|
|
@@ -52,9 +54,28 @@ | |
| # Cynthia Brewer citation text. | ||
| BREWER_CITE = 'Colours based on ColorBrewer.org' | ||
|
|
||
|
|
||
| PlotDefn = collections.namedtuple('PlotDefn', ('coords', 'transpose')) | ||
|
|
||
| # Threading reentrant lock to ensure thread-safe plotting. | ||
| _lock = threading.RLock() | ||
|
|
||
|
|
||
| def _locker(func): | ||
| """ | ||
| Decorator that ensures a thread-safe atomic operation is | ||
| performed by the decorated function. | ||
|
|
||
| Uses a shared threading reentrant lock to provide thread-safe | ||
| plotting by public API functions. | ||
|
|
||
| """ | ||
| @wraps(func) | ||
| def decorated_func(*args, **kwargs): | ||
| with _lock: | ||
| result = func(*args, **kwargs) | ||
| return result | ||
| return decorated_func | ||
|
|
||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A simple decorator to facilitate the use of a re-entrant lock to provide thread-safe plotting. A re-entrant lock is required here as |
||
|
|
||
| def _get_plot_defn_custom_coords_picked(cube, coords, mode, ndims=2): | ||
| def names(coords): | ||
|
|
@@ -665,6 +686,7 @@ def _map_common(draw_method_name, arg_func, mode, cube, plot_defn, | |
| return plotfn(*new_args, **kwargs) | ||
|
|
||
|
|
||
| @_locker | ||
| def contour(cube, *args, **kwargs): | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the lock for all public api plotting functions ...
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder whether this will have any unexpected behavioural impacts in normal usage of
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm inclined to agree... it feels like this should be done when testing. We are mostly wrapping matplotlib calls, which I guess aren't thread safe either, so it doesn't seem consistent to lock our wrappers.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dkillick @ajdawson I see your point, and I was in two minds whether to lock at this level. To be honest, I expected that discussion to have happen during the review 😉 ... Regardless of testing, iris plotting is not thread safe for users thanks to mpl, hence why I locked the wrappers at the plotting level. The problem lies deep within iris plotting regarding calls to iris.plot._replace_axes_with_cartopy_axes Locking at the graphics test level completely makes sense as it totally ensures that graphics tests are atomic (as I've already implemented), so at a minimum we definitely require to keep that level of locking. The plotting level of locking may help the user in a threaded context, but I'm less convinced of this ... So we may be able to compromise and drop the plotting level of locking ... I'll take a look and see how it behaves. But I'd be more than happy to do that. |
||
| """ | ||
| Draws contour lines based on the given Cube. | ||
|
|
@@ -689,6 +711,7 @@ def contour(cube, *args, **kwargs): | |
| return result | ||
|
|
||
|
|
||
| @_locker | ||
| def contourf(cube, *args, **kwargs): | ||
| """ | ||
| Draws filled contours based on the given Cube. | ||
|
|
@@ -815,6 +838,7 @@ def _fill_orography(cube, coords, mode, vert_plot, horiz_plot, style_args): | |
| return result | ||
|
|
||
|
|
||
| @_locker | ||
| def orography_at_bounds(cube, facecolor='#888888', coords=None, axes=None): | ||
| """Plots orography defined at cell boundaries from the given Cube.""" | ||
|
|
||
|
|
@@ -845,6 +869,7 @@ def horiz_plot(v_coord, orography, style_args): | |
| horiz_plot, style_args) | ||
|
|
||
|
|
||
| @_locker | ||
| def orography_at_points(cube, facecolor='#888888', coords=None, axes=None): | ||
| """Plots orography defined at sample points from the given Cube.""" | ||
|
|
||
|
|
@@ -866,6 +891,7 @@ def horiz_plot(v_coord, orography, style_args): | |
| horiz_plot, style_args) | ||
|
|
||
|
|
||
| @_locker | ||
| def outline(cube, coords=None, color='k', linewidth=None, axes=None): | ||
| """ | ||
| Draws cell outlines based on the given Cube. | ||
|
|
@@ -903,6 +929,7 @@ def outline(cube, coords=None, color='k', linewidth=None, axes=None): | |
| return result | ||
|
|
||
|
|
||
| @_locker | ||
| def pcolor(cube, *args, **kwargs): | ||
| """ | ||
| Draws a pseudocolor plot based on the given Cube. | ||
|
|
@@ -929,6 +956,7 @@ def pcolor(cube, *args, **kwargs): | |
| return result | ||
|
|
||
|
|
||
| @_locker | ||
| def pcolormesh(cube, *args, **kwargs): | ||
| """ | ||
| Draws a pseudocolor plot based on the given Cube. | ||
|
|
@@ -953,6 +981,7 @@ def pcolormesh(cube, *args, **kwargs): | |
| return result | ||
|
|
||
|
|
||
| @_locker | ||
| def points(cube, *args, **kwargs): | ||
| """ | ||
| Draws sample point positions based on the given Cube. | ||
|
|
@@ -980,6 +1009,7 @@ def _scatter_args(u, v, data, *args, **kwargs): | |
| *args, **kwargs) | ||
|
|
||
|
|
||
| @_locker | ||
| def plot(*args, **kwargs): | ||
| """ | ||
| Draws a line plot based on the given cube(s) or coordinate(s). | ||
|
|
@@ -1024,6 +1054,7 @@ def plot(*args, **kwargs): | |
| return _draw_1d_from_points('plot', _plot_args, *args, **kwargs) | ||
|
|
||
|
|
||
| @_locker | ||
| def scatter(x, y, *args, **kwargs): | ||
| """ | ||
| Draws a scatter plot based on the given cube(s) or coordinate(s). | ||
|
|
@@ -1059,6 +1090,7 @@ def scatter(x, y, *args, **kwargs): | |
| show = plt.show | ||
|
|
||
|
|
||
| @_locker | ||
| def symbols(x, y, symbols, size, axes=None, units='inches'): | ||
| """ | ||
| Draws fixed-size symbols. | ||
|
|
@@ -1122,6 +1154,7 @@ def symbols(x, y, symbols, size, axes=None, units='inches'): | |
| axes.autoscale_view() | ||
|
|
||
|
|
||
| @_locker | ||
| def citation(text, figure=None, axes=None): | ||
| """ | ||
| Add a text citation to a plot. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW you don't need to install pip - it's already available on Travis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As part of the conda environment (and installing to it), though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pip is installed by default when you create a new conda environment (e.g. https://travis-ci.org/SciTools/iris/jobs/170017955#L253) so it doesn't need to be done separately. The result of this line is a no-op (https://travis-ci.org/SciTools/iris/jobs/170017955#L464).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup