Skip to content

Commit

Permalink
Merge pull request #1736 from girder/alpha-compositing
Browse files Browse the repository at this point in the history
Adjust multisource multi layer compositing.
  • Loading branch information
manthey authored Dec 5, 2024
2 parents f8293ff + 14c8691 commit 10b0578
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## 1.30.5

### Improvements

- When using the multisource to composite multiple images with alpha channels, use nearest neighbor for upper tiles ([#1736](../../pull/1736))

### Changes

- Adjust how compositing is done on styled images by adjusting the expected full alpha value ([#1735](../../pull/1735))
Expand Down
16 changes: 12 additions & 4 deletions sources/multi/large_image_source_multi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,8 @@ def _mergeTiles(self, base, tile, x, y):
base[y:y + tile.shape[0], x:x + tile.shape[1], :] = tile
return base

def _getTransformedTile(self, ts, transform, corners, scale, frame, crop=None):
def _getTransformedTile(self, ts, transform, corners, scale, frame,
crop=None, firstMerge=False):
"""
Determine where the target tile's corners are located on the source.
Fetch that so that we have at least sqrt(2) more resolution, then use
Expand All @@ -1054,6 +1055,9 @@ def _getTransformedTile(self, ts, transform, corners, scale, frame, crop=None):
:param crop: an optional dictionary to crop the source image in full
resolution, untransformed coordinates. This may contain left, top,
right, and bottom values in pixels.
:param firstMerge: if False and using an alpha channel, transform
with nearest neighbor rather than a higher order function to
avoid transparency effects.
:returns: a numpy array tile or None, x, y coordinates within the
target tile for the placement of the numpy tile array.
"""
Expand Down Expand Up @@ -1145,9 +1149,12 @@ def _getTransformedTile(self, ts, transform, corners, scale, frame, crop=None):
destShape = [min(destShape[0], outh - y), min(destShape[1], outw - x)]
if destShape[0] <= 0 or destShape[1] <= 0:
return None, None, None
# Add an alpha band if needed
# Add an alpha band if needed. This has to be done before the
# transform if it isn't the first tile, since the unused transformed
# areas need to have a zero alpha value
if srcImage.shape[2] in {1, 3}:
_, srcImage = _makeSameChannelDepth(np.zeros((1, 1, srcImage.shape[2] + 1)), srcImage)
useNearest = srcImage.shape[2] in {2, 4} and not firstMerge
# skimage.transform.warp is faster and has less artifacts than
# scipy.ndimage.affine_transform. It is faster than using cupy's
# version of scipy's affine_transform when the source and destination
Expand All @@ -1157,7 +1164,7 @@ def _getTransformedTile(self, ts, transform, corners, scale, frame, crop=None):
# provide any speed improvement
srcImage.astype(float),
skimage.transform.AffineTransform(np.linalg.inv(transform)),
order=3,
order=0 if useNearest else 3,
output_shape=(destShape[0], destShape[1], srcImage.shape[2]),
).astype(srcImage.dtype)
return destImage, x, y
Expand Down Expand Up @@ -1234,7 +1241,8 @@ def _addSourceToTile(self, tile, sourceEntry, corners, scale):
else:
sourceTile, x, y = self._getTransformedTile(
ts, transform, corners, scale, sourceEntry.get('frame', 0),
source.get('position', {}).get('crop'))
source.get('position', {}).get('crop'),
firstMerge=tile is None)
if sourceTile is not None and all(dim > 0 for dim in sourceTile.shape):
targetDtype = np.dtype(self._info.get('dtype', ts.dtype))
changeDtype = sourceTile.dtype != targetDtype
Expand Down

0 comments on commit 10b0578

Please sign in to comment.