Cedric
Cedric

Reputation: 285

Is there a way to convert an image to black and white pixel by pixel?

When using image = image.convert("1") on a very light grey, it'll add little black pixels to "average" it out. I'm looking for something that just looks at every individual pixel and determines whether that pixel is closer to black or to white.

Upvotes: 0

Views: 2483

Answers (1)

HansHirse
HansHirse

Reputation: 18905

Please pay attention to the documentation on PIL.Image.convert:

The default method of converting a greyscale (“L”) or “RGB” image into a bilevel (mode “1”) image uses Floyd-Steinberg dither to approximate the original image luminosity levels. If dither is NONE, all values larger than 128 are set to 255 (white), all other values to 0 (black). To use other thresholds, use the point() method.

So, you actually want no dithering and must set this option explicitly when converting:

from matplotlib import pyplot as plt
import numpy as np
from PIL import Image

# Grayscale image as NumPy array with values from [0 ... 255]
image = np.reshape(np.tile(np.arange(256, dtype=np.uint8), 256), (256, 256))

# PIL grayscale image with values from [0 ... 255]
image_pil = Image.fromarray(image, mode='L')

# PIL grayscale image converted to mode '1' without dithering
image_pil_conv = image_pil.convert('1', dither=Image.NONE)

# Threshold PIL grayscale image using point with threshold 128 (for comparison)
threshold = 128
image_pil_thr = image_pil.point(lambda p: p > threshold and 255)

# Result visualization
plt.figure(1, figsize=(9, 8))
plt.subplot(2, 2, 1), plt.imshow(image, cmap=plt.gray()), plt.ylabel('NumPy array')
plt.subplot(2, 2, 2), plt.imshow(image_pil, cmap=plt.gray()), plt.ylabel('PIL image')
plt.subplot(2, 2, 3), plt.imshow(image_pil_conv, cmap=plt.gray()), plt.ylabel('PIL image converted, no dithering')
plt.subplot(2, 2, 4), plt.imshow(image_pil_thr, cmap=plt.gray()), plt.ylabel('PIL image thresholded')
plt.tight_layout()
plt.show()

Output

The documentation is also imprecise: Actually, all values greater than OR EQUAL 128 are set to white, both for convert as well as for point – which makes sense, since [0 ... 127] are 128 values, and [128 ... 255] are 128 values.

Hope that helps!

Upvotes: 1

Related Questions