0

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?

martineau
  • 119,623
  • 25
  • 170
  • 301
  • Diamond boxes also consist of 1s and 0s as every other computer file. You shouldn't expect to literally see the ASCII digits `0` (ASCII 48) and `1` (ASCII 49) when you open a binary file in a text editor. Why don't you open them in a PBM viewer instead of notepad? – Selcuk Feb 24 '22 at 02:25
  • I can't tell whether you just want to get the job done in order to actually do something else, or whether you want to know specifically how to convert it with Python and PIL. If the former, just FYI, you can do it without any Python, just in the Terminal with **ImageMagick** using `magick input.png -compresss none output.pbm`. – Mark Setchell Feb 24 '22 at 08:23

2 Answers2

1

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

martineau
  • 119,623
  • 25
  • 170
  • 301
0

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')
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432