openslide / openslide-python

Python bindings to OpenSlide
https://openslide.org/
GNU Lesser General Public License v2.1
371 stars 180 forks source link

Reading and resizing Openslide to PIL image creates black boxes ## Details #97

Closed kara-insitro closed 4 years ago

kara-insitro commented 4 years ago

Context

Issue type (bug report or feature request): Reading and resizing Openslide to PIL image creates black boxes

Details

My issue is very similar to that which someone else posted here: https://stackoverflow.com/questions/63190495/reading-and-resizing-svs-slide-results-in-a-damaged-image When I use scale factors >= 32, the image comes out fine. However, if use anything less, I get these weird black boxes. 1_raw (1)

The code to reproduce it will be from the link above. I think for this person, the scaling factor was good for 64, but didn't work for 32 or lower. Slide he / she used was here: https://portal.gdc.cancer.gov/files/5b54f176-3727-4a02-aa7a-921eebe5aeea.

def slide_to_scaled_pil_image(slide, SCALE_FACTOR=32): """ Convert a WSI training slide to a scaled-down PIL image. Args: slide: An OpenSlide object. Returns: Tuple consisting of scaled-down PIL image, original width, original height, new width, and new height. """

large_w, large_h = slide.dimensions new_w = math.floor(large_w / SCALE_FACTOR) new_h = math.floor(large_h / SCALE_FACTOR) level = slide.get_best_level_for_downsample(SCALE_FACTOR) whole_slide_image = slide.read_region((0, 0), level, slide.level_dimensions[level]) whole_slide_image = whole_slide_image.convert("RGB") img = whole_slide_image.resize((new_w, new_h), PIL.Image.BILINEAR) return img, large_w, large_h, new_w, new_h

img = openslide.OpenSlide(PATH_TO_SLIDE) ds_img, large_w, large_h, new_w, new_h = slide_to_scaled_pil_image(img, SCALE_FACTOR=SCALE_FACTOR)

jaharkes commented 4 years ago

I assume you are hitting an overflow somewhere, that particular slide has 4 levels internally.

So it works when you are scaling starting from level 3 where you read out a 2490x1670 sized region (16MB) and scaling it down to where you want to be. But because of the 3 extra pixels on the height of level 0, the optimal level for a 32 scale factor ends up to be level 2. So then you are using read_region over a 4980x3340 image (65MB). I assume either Cairo (used to stitch the slide's internal tiles inside read_region) or PIL isn't handling the single large region very well.

I assume that if you ignore the recommendation from best_level_for_downsample and just force level = 3, it will 'upsample' it to 53443/32. = 1670.09375 which rounds down to the level 3 dimensions that were read and resize becomes a noop.

jaharkes commented 4 years ago

I ran your code locally on an Ubuntu-18.04 system with openslide and openslide-python installed from git and it is definitely scaling from a region grabbed from level 2.

However I am not seeing any non-rendered areas, the final image looks as you would expect.

This does not look like an openslide or openslide-python bug, so I'm closing this. Feel free to reopen if you have more information.

jaharkes commented 4 years ago

Just noticed this older closed issue in the openslide repository where similar odd tiling artifacts were caused by a bug in the pixman library.

https://github.com/openslide/openslide/issues/278#issuecomment-524377558

CasiaFan commented 1 year ago

I met the same problem. Upgrading pixman to 0.40.0 can solve it.