Skip to content

Commit

Permalink
Merge pull request #95 from bgilbert/performance
Browse files Browse the repository at this point in the history
Drop fallback code for missing extension module
  • Loading branch information
bgilbert authored Sep 14, 2020
2 parents 9e9f960 + 7afbb00 commit 436225a
Show file tree
Hide file tree
Showing 3 changed files with 9 additions and 69 deletions.
59 changes: 7 additions & 52 deletions openslide/lowlevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import platform
import sys

from . import _convert

if platform.system() == 'Windows':
_lib = cdll.LoadLibrary('libopenslide-0.dll')
elif platform.system() == 'Darwin':
Expand All @@ -57,58 +59,6 @@
else:
_lib = cdll.LoadLibrary('libopenslide.so.0')

try:
from . import _convert
def _load_image(buf, size):
'''buf must be a mutable buffer.'''
_convert.argb2rgba(buf)
return PIL.Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1)
except ImportError:
def _load_image(buf, size):
'''buf must be a buffer.'''

# Load entire buffer at once if possible
MAX_PIXELS_PER_LOAD = (1 << 29) - 1
# Otherwise, use chunks smaller than the maximum to reduce memory
# requirements
PIXELS_PER_LOAD = 1 << 26

def do_load(buf, size):
'''buf can be a string, but should be a ctypes buffer to avoid an
extra copy in the caller.'''
# First reorder the bytes in a pixel from native-endian aRGB to
# big-endian RGBa to work around limitations in RGBa loader
rawmode = (sys.byteorder == 'little') and 'BGRA' or 'ARGB'
buf = PIL.Image.frombuffer('RGBA', size, buf, 'raw', rawmode, 0, 1)
# Image.tobytes() is named tostring() in Pillow 1.x and PIL
buf = (getattr(buf, 'tobytes', None) or buf.tostring)()
# Now load the image as RGBA, undoing premultiplication
return PIL.Image.frombuffer('RGBA', size, buf, 'raw', 'RGBa', 0, 1)

# Fast path for small buffers
w, h = size
if w * h <= MAX_PIXELS_PER_LOAD:
return do_load(buf, size)

# Load in chunks to avoid OverflowError in PIL.Image.frombuffer()
# https://github.com/python-pillow/Pillow/issues/1475
if w > PIXELS_PER_LOAD:
# We could support this, but it seems like overkill
raise ValueError('Width %d is too large (maximum %d)' %
(w, PIXELS_PER_LOAD))
rows_per_load = PIXELS_PER_LOAD // w
img = PIL.Image.new('RGBA', (w, h))
for y in range(0, h, rows_per_load):
rows = min(h - y, rows_per_load)
if sys.version[0] == '2':
chunk = buffer(buf, 4 * y * w, 4 * rows * w)
else:
# PIL.Image.frombuffer() won't take a memoryview or
# bytearray, so we can't avoid copying
chunk = memoryview(buf)[y * w:(y + rows) * w].tobytes()
img.paste(do_load(chunk, (w, rows)), (0, y))
return img

class OpenSlideError(Exception):
"""An error produced by the OpenSlide library.
Expand Down Expand Up @@ -167,6 +117,11 @@ def from_param(cls, obj):
else:
raise TypeError('Incorrect type')

def _load_image(buf, size):
'''buf must be a mutable buffer.'''
_convert.argb2rgba(buf)
return PIL.Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1)

# check for errors opening an image file and wrap the resulting handle
def _check_open(result, _func, _args):
if result is None:
Expand Down
7 changes: 0 additions & 7 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@
from PIL import Image
import unittest

try:
import openslide._convert as _
have_optimizations = True
except ImportError:
have_optimizations = False


# PIL.Image cannot have zero width or height on Pillow 3.4.0 - 3.4.2
# https://github.com/python-pillow/Pillow/issues/2259
try:
Expand Down
12 changes: 2 additions & 10 deletions tests/test_openslide.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
import sys
import unittest

from . import (file_path, have_optimizations, image_dimensions_cannot_be_zero,
skip_if)
from . import file_path, image_dimensions_cannot_be_zero, skip_if

# Tests should be written to be compatible with Python 2.6 unittest.

Expand Down Expand Up @@ -127,8 +126,7 @@ def test_read_region_bad_size(self):
@skip_if(sys.maxsize < 1 << 32, '32-bit Python')
# Broken on PIL and on Pillow >= 3.4.0, < 6.2.0.
# https://github.com/python-pillow/Pillow/issues/3963
@skip_if(have_optimizations and
[int(i) for i in getattr(Image, '__version__', '0').split('.')] < [6,2,0],
@skip_if([int(i) for i in getattr(Image, '__version__', '0').split('.')] < [6,2,0],
'broken on PIL and Pillow < 6.2.0')
# Disabled to avoid OOM killer on small systems, since the stdlib
# doesn't provide a way to find out how much RAM we have
Expand All @@ -137,12 +135,6 @@ def _test_read_region_2GB(self):
self.osr.read_region((1000, 1000), 0, (32768, 16384)).size,
(32768, 16384))

@skip_if(have_optimizations, 'only relevant --without-performance')
@skip_if(sys.maxsize < 1 << 32, '32-bit Python')
def test_read_region_2GB_width(self):
self.assertRaises(ValueError,
lambda: self.osr.read_region((1000, 1000), 0, (1 << 29, 1)))

def test_thumbnail(self):
self.assertEqual(self.osr.get_thumbnail((100, 100)).size, (100, 83))

Expand Down

0 comments on commit 436225a

Please sign in to comment.