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

Handle bounds on openslide sources to handle sparse sources #1543

Merged
merged 1 commit into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Change Log

## 1.28.3
## 1.29.0

### Features
- Add redis as cache backend option ([#1469](../../pull/1469))
Expand All @@ -10,6 +10,7 @@
- Reject the promise from a canceled annotation ([#1535](../../pull/1535))
- Disallow certain names from being read by any but specific sources ([#1537](../../pull/1537))
- Add the ability to specify the dtype of a multi-source file ([#1542](../../pull/1542))
- Handle bounds on openslide sources to handle sparse sources ([#1543](../../pull/1543))

### Changes
- Log more when saving annotations ([#1525](../../pull/1525))
Expand Down
27 changes: 27 additions & 0 deletions sources/openslide/large_image_source_openslide/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,30 @@ def __init__(self, path, **kwargs): # noqa
'svslevel': bestlevel,
'scale': scale,
})
self._bounds = None
try:
bounds = {
'x': int(self._openslide.properties[openslide.PROPERTY_NAME_BOUNDS_X]),
'y': int(self._openslide.properties[openslide.PROPERTY_NAME_BOUNDS_Y]),
'width': int(self._openslide.properties[openslide.PROPERTY_NAME_BOUNDS_WIDTH]),
'height': int(self._openslide.properties[openslide.PROPERTY_NAME_BOUNDS_HEIGHT]),
}
if (
bounds['x'] >= 0 and bounds['width'] > 0 and
bounds['x'] + bounds['width'] <= self.sizeX and
bounds['y'] >= 0 and bounds['height'] > 0 and
bounds['y'] + bounds['height'] <= self.sizeY and
(bounds['width'] < self.sizeX or bounds['height'] < self.sizeY)
):
self._bounds = bounds
self.sizeX, self.sizeY = bounds['width'], bounds['height']
prevlevels = self.levels
self.levels = int(math.ceil(max(
math.log(float(self.sizeX) / self.tileWidth),
math.log(float(self.sizeY) / self.tileHeight)) / math.log(2))) + 1
self._svslevels = self._svslevels[prevlevels - self.levels:]
except Exception:
pass
self._populatedLevels = len({l['svslevel'] for l in self._svslevels})

def _getTileSize(self):
Expand Down Expand Up @@ -292,6 +316,9 @@ def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
scale = 2 ** (self.levels - 1 - z)
offsetx = x * self.tileWidth * scale
offsety = y * self.tileHeight * scale
if self._bounds is not None:
offsetx += self._bounds['x'] // svslevel['scale']
offsety += self._bounds['y'] // svslevel['scale']
# We ask to read an area that will cover the tile at the z level. The
# scale we computed in the __init__ process for this svs level tells
# how much larger a region we need to read.
Expand Down