Skip to content

Commit

Permalink
Add a module for style functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
manthey committed Sep 12, 2022
1 parent f0a18f9 commit e852eee
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 42 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## 1.16.3

### Features
- Style functions ([960](../../pull/960))

### Improvements
- Reduce rest calls to get settings ([953](../../pull/953))
- Add an endpoint to delete all annotations in a folder ([954](../../pull/954))
Expand Down
2 changes: 1 addition & 1 deletion docs/tilesource_options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ A band definition is an object which can contain the following keys:

The function parameter can be a single function or a list of functions. Items in a list of functions can, themselves, be lists of functions. A single function can be an object or a string. If a string, this is shorthand for ``{"name": <function>}``. The function object contains (all but ``name`` are optional):

- ``name``: The name of a Python module and function that is installed in the same environment as large_image. For instance, ``large_image.tilesource.utilities.maskPixelValues`` will use the function ``maskPixelValues`` in the ``large_image.tilesource.utilities`` module. The function must be a Python function that takes a numpy array as the first parameter (the image) and has named parameters or kwargs for any passed parameters and possibly the style context.
- ``name``: The name of a Python module and function that is installed in the same environment as large_image. For instance, ``large_image.tilesource.stylefuncs.maskPixelValues`` will use the function ``maskPixelValues`` in the ``large_image.tilesource.stylefuncs`` module. The function must be a Python function that takes a numpy array as the first parameter (the image) and has named parameters or kwargs for any passed parameters and possibly the style context.

- ``parameters``: A dictionary of parameters to pass to the function.

Expand Down
39 changes: 39 additions & 0 deletions large_image/tilesource/stylefuncs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This module contains functions for use in styles

import numpy


def maskPixelValues(image, context, values=None, negative=None, positive=None):
"""
This is a style utility function that returns a black-and-white 8-bit image
where the image is white if the pixel of the source image is in a list of
values and black otherwise. The values is a list where each entry can
either be a tuple the same length as the band dimension of the output image
or a single value which is handled as 0xBBGGRR.
:param image: a numpy array of Y, X, Bands.
:param context: the style context. context.image is the source image
:param values: an array of values, each of which is either an array of the
same number of bands as the source image or a single value of the form
0xBBGGRR assuming uint8 data.
:param negative: None to use [0, 0, 0, 255], or an RGBA uint8 value for
pixels not in the value list.
:param positive: None to use [255, 255, 255, 0], or an RGBA uint8 value for
pixels in the value list.
:returns: an RGBA numpy image which is exactly black or transparent white.
"""
src = context.image
mask = numpy.full(src.shape[:2], False)
for val in values:
if not isinstance(val, (list, tuple)):
if src.shape[-1] == 1:
val = [val]
else:
val = [val % 256, val // 256 % 256, val // 65536 % 256]
val = (list(val) + [255] * src.shape[2])[:src.shape[2]]
match = numpy.array(val)
mask = mask | (src == match).all(axis=-1)
image[mask != True] = negative or [0, 0, 0, 255] # noqa E712
image[mask] = positive or [255, 255, 255, 0]
image = image.astype(numpy.uint8)
return image
36 changes: 0 additions & 36 deletions large_image/tilesource/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -935,42 +935,6 @@ def histogramThreshold(histogram, threshold, fromMax=False):
return edges[-1]


def maskPixelValues(image, context, values=None, negative=None, positive=None):
"""
This is a style utility function that returns a black-and-white 8-bit image
where the image is white if the pixel of the source image is in a list of
values and black otherwise. The values is a list where each entry can
either be a tuple the same length as the band dimension of the output image
or a single value which is handled as 0xBBGGRR.
:param image: a numpy array of Y, X, Bands.
:param context: the style context. context.image is the source image
:param values: an array of values, each of which is either an array of the
same number of bands as the source image or a single value of the form
0xBBGGRR assuming uint8 data.
:param negative: None to use [0, 0, 0, 255], or an RGBA uint8 value for
pixels not in the value list.
:param positive: None to use [255, 255, 255, 0], or an RGBA uint8 value for
pixels in the value list.
:returns: an RGBA numpy image which is exactly black or transparent white.
"""
src = context.image
mask = numpy.full(src.shape[:2], False)
for val in values:
if not isinstance(val, (list, tuple)):
if src.shape[-1] == 1:
val = [val]
else:
val = [val % 256, val // 256 % 256, val // 65536 % 256]
val = (list(val) + [255] * src.shape[2])[:src.shape[2]]
match = numpy.array(val)
mask = mask | (src == match).all(axis=-1)
image[mask != True] = negative or [0, 0, 0, 255] # noqa E712
image[mask] = positive or [255, 255, 255, 0]
image = image.astype(numpy.uint8)
return image


def addPILFormatsToOutputOptions():
"""
Check PIL for available formats that be saved and add them to the lists of
Expand Down
10 changes: 5 additions & 5 deletions test/test_source_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ def testStyleFunctions():
format=large_image.constants.TILE_FORMAT_NUMPY)
sourceFunc2 = large_image.open(imagePath, style={
'function': {
'name': 'large_image.tilesource.utilities.maskPixelValues',
'name': 'large_image.tilesource.stylefuncs.maskPixelValues',
'context': True,
'parameters': {'values': [164, 165]}},
'bands': []})
Expand All @@ -584,7 +584,7 @@ def testStyleFunctions():
assert numpy.any(region2 != region1)
sourceFunc3 = large_image.open(imagePath, style={
'function': {
'name': 'large_image.tilesource.utilities.maskPixelValues',
'name': 'large_image.tilesource.stylefuncs.maskPixelValues',
'context': True,
'parameters': {'values': [[63, 63, 63]]}},
'bands': []})
Expand All @@ -594,7 +594,7 @@ def testStyleFunctions():
assert numpy.any(region3 != region2)
sourceFunc4 = large_image.open(imagePath, style={
'function': [{
'name': 'large_image.tilesource.utilities.maskPixelValues',
'name': 'large_image.tilesource.stylefuncs.maskPixelValues',
'context': 'context',
'parameters': {'values': [164, 165]}}],
'bands': []})
Expand All @@ -608,7 +608,7 @@ def testStyleFunctionsWarnings():
imagePath = datastore.fetch('extraoverview.tiff')
source = large_image.open(imagePath, style={
'function': {
'name': 'large_image.tilesource.utilities.maskPixelValues',
'name': 'large_image.tilesource.stylefuncs.maskPixelValues',
'context': True,
'parameters': {'values': ['bad value']}},
'bands': []})
Expand All @@ -630,7 +630,7 @@ def testStyleFunctionsWarnings():

source = large_image.open(imagePath, style={
'function': {
'name': 'large_image.tilesource.utilities.noSuchFunction',
'name': 'large_image.tilesource.stylefuncs.noSuchFunction',
'context': True,
'parameters': {'values': [100]}},
'bands': []})
Expand Down

0 comments on commit e852eee

Please sign in to comment.