Image Conversion from PNG to PBM (solely 1s and 0s) using PIL

1k Views Asked by At

I am trying to convert PNG images to PBM files. These PNG files are black and white and I need the resulting PBM to be a P1 bitmap which only contains 1s and 0s. So far the closest I have come is the following code:

img = Image.open("test part stl00000.png")
img = img.convert('1')
img.save("new.pbm")

However, this does not result in the desired output. When I open the output file in VS code, I get a bunch of question marks in diamond boxes along with red null boxes (it will not allow me to attach images here). When I open the file in notepad, it is blank spaces and y's with double dots above them. Does anyone know why my output is not 1s and 0s with the number of rows and columns corresponding to the size of the PNG I am converting?

Edit: I saw the post saying that I shouldn't expect to see the ASCII digits of 1s and 0s but that when I open it in a PBM viewer I would see the image I started with. This is correct, but unfortunately, I need the ASCII digits for the project I am working on. Does anyone know how I can do the conversion such that I end up with straight 1s and 0s so that I am able to manipulate it?

2

There are 2 best solutions below

0
martineau On BEST ANSWER

PIL doesn't support ASCII format of PBM, but it's fairly easy to do yourself with its help because the PBM file format is so simple. The code below is based on my answer to the question How to convert a grayscale image into a list of pixel values?

Note that if all you want are the ASCII digits, that's what ends up in the data list which is written to the output file.

from pathlib import Path
from PIL import Image

ASCII_BITS = '0', '1'
imagepath = Path('peace_sign.png')

img = Image.open(imagepath).convert('1')  # Convert image to bitmap.
width, height = img.size

# Convert image data to a list of ASCII bits.
data = [ASCII_BITS[bool(val)] for val in img.getdata()]
# Convert that to 2D list (list of character lists)
data = [data[offset: offset+width] for offset in range(0, width*height, width)]

with open(f'{imagepath.stem}.pbm', 'w') as file:
    file.write('P1\n')
    file.write(f'# Conversion of {imagepath} to PBM format\n')
    file.write(f'{width} {height}\n')
    for row in data:
        file.write(' '.join(row) + '\n')

print('fini')

This test image:

test image

Produced a PBM format file with this content:

output

0
Mark Setchell On

Numpy can save your array quite simply for you via np.savetxt() as follows:

#!/usr/bin/env python3

import numpy as np
from PIL import Image

# Load image and convert to '1' mode
im = Image.open('image.png').convert('1')

# Make Numpy array from image
na = np.array(im)

# Save array
np.savetxt('image.pbm', na, fmt='%d')

If you want the full PBM header, replace the last line above with the following three lines:

# Use this to put NetPBM header on as well
# https://en.wikipedia.org/wiki/Netpbm#PBM_example

with open('image.pbm','w') as f:
    f.write(f'P1\n{im.width} {im.height}\n')
    np.savetxt(f, na, fmt='%d')