10

I'm currently working with image processing in python using the scikit-image library. I'm trying to make a binary image using sauvola thresholding with the following code:

from PIL import Image
import numpy
from skimage.color import rgb2gray
from skimage.filters import threshold_sauvola

im = Image.open("test.jpg")
pix = numpy.array(im)
img = rgb2gray(pix)

window_size = 25
thresh_sauvola = threshold_sauvola(img, window_size=window_size)
binary_sauvola = img > thresh_sauvola

Which gives the following result: enter image description here

the output is a numpy array with data type of this image is a bool

[[ True  True  True ...  True  True  True]
 [ True  True  True ...  True  True  True]
 [ True  True  True ...  True  True  True]
 ...
 [ True  True  True ...  True  True  True]
 [ True  True  True ...  True  True  True]
 [ True  True  True ...  True  True  True]]

The problem is that I need to convert this array back to a PIL image using the following line of code:

image = Image.fromarray(binary_sauvola)

which makes the image look like this:

enter image description here

I also tried to change the data type from bool to uint8 but then I'll get the following exception:

AttributeError: 'numpy.ndarray' object has no attribute 'mask'

So far I haven't found a solution to get a PIL image which looks like the result of the thresholding.

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
R.hagens
  • 325
  • 4
  • 19
  • 1
    "I also tried to change the data type from bool to uint8" please show the attempt. It clearly didn't use `view` or `astype`, so really not sure what you did. – Mad Physicist May 02 '18 at 12:31
  • I tried the following line to change the dtype to uint8 `image = Image.fromarray(binary_sauvola.astype('uint8'))` – R.hagens May 02 '18 at 12:37
  • Then show the stack trace. That error seems strange. Please edit the question. Don't put cow and errors into comments if you can avoid it. – Mad Physicist May 02 '18 at 13:43

2 Answers2

12

Update

This bug has now been solved in Pillow==6.2.0. The link to the issue on GitHub is here.

If you cannot update to the new version of Pillow, please see below.


PIL's Image.fromarray function has a bug with mode '1' images. This Gist demonstrates the bug, and shows a few workarounds. Here are the best two workarounds:

import numpy as np
from PIL import Image

# The standard work-around: first convert to greyscale 
def img_grey(data):
    return Image.fromarray(data * 255, mode='L').convert('1')

# Use .frombytes instead of .fromarray. 
# This is >2x faster than img_grey
def img_frombytes(data):
    size = data.shape[::-1]
    databytes = np.packbits(data, axis=1)
    return Image.frombytes(mode='1', size=size, data=databytes)

Also see Error Converting PIL B&W images to Numpy Arrays.

Community
  • 1
  • 1
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • img_grey doesn't give me the right result, but img_frombytes does. Thank you so much for your answer – R.hagens May 02 '18 at 12:51
  • 1
    @R.hagens My pleasure! It's interesting that `img_grey` misbehaves for you. I'm not sure why that happens, I did my research on this topic almost a year ago, and I forget the fine details. But I prefer `img_frombytes`, since I wrote it. :) – PM 2Ring May 02 '18 at 12:54
  • well if you're interested, img_grey gave me the following result https://imgur.com/a/VC3yqs4 – R.hagens May 02 '18 at 13:04
  • @R.hagens Thanks. It's essentially the same bug, but I'll have to think about why it still happens despite the greyscale conversion that `img_grey` does. – PM 2Ring May 02 '18 at 13:15
  • 1
    This issue has been solved in the latest pillow==6.2.0. The link to the issue on github: https://github.com/python-pillow/Pillow/issues/3109 – Taro Kiritani Oct 24 '19 at 07:27
2

This option might was not available 2018 but currently

from skimage.io._plugins.pil_plugin import ndarray_to_pil, pil_to_ndarray
ndarray_to_pil(some_binary_image).convert("1")

seems to do the trick.

Florian
  • 597
  • 3
  • 9