I have a python script which opens an image file (.png or .ppm) using OpenCV, then loads all the RGB values into a multidimensional Python array (or list), performs some pixel by pixel calculations solely on the Python array (OpenCV is not used at all for this stage), then uses the newly created array (containing new RGB values) to write a new image file (.png here) using OpenCV again. Numpy is not used at all in this script. The program works fine.
The question is how to do this without using any external libraries, regardless whether they are for image processing or not (e.g. OpenCV, Numpy, Scipy, Pillow etc.). To summarize, I need to use bare bones Python's internal modules to: 1. open image and read the RGB values and 2. write a new image from pre-calculated RGB values. I will use Pypy instead of CPython for this purpose, to speed things up. Note: I use Windows 10, if that matters.
You can write a PPM file like this without OpenCV or numpy or anything that hates you :-
import array
width,height = 800,600
PPMheader = 'P6\n' +str(width) + ' ' +str(height) + '\n255\n'
# Create and fill a red PPM image
image = array.array('B', [255, 0, 0] * width * height)
# Save as PPM image
with open('result.ppm', 'wb') as f:
f.write(bytearray(PPMheader, 'ascii'))
image.tofile(f)

I wrote a really simplistic PPM reader too - it only reads 24-bit P6 PPM files matching the ones written above. I guess it could be fleshed out to handle comments, 16-bit data and greyscale data, but it's a start:
#!/usr/local/bin/python3
import array
def ppmread(filename):
with open(filename, 'rb') as f:
# Read first line - expecting "P6"
line = f.readline().decode('latin-1')
if not line.startswith('P6'):
print("ERROR: Expected PPM file to start with P6")
return False
# Read second line - expecting "width height"
line = f.readline().decode('latin-1')
dims = line.split()
width,height=int(dims[0]),int(dims[1])
# Read third line - expecting "255"
line = f.readline().decode('latin-1')
if not line.startswith('255'):
print("ERROR: Expected 8-bit PPM with MAXVAL=255")
return False
image= f.read(width*height*3)
ppmread('test.ppm')
Convert PPM to PNG or JPEG with ImageMagick in Terminal:
convert result.ppm -auto-level image.png
Or:
convert result.ppm -resize 1024x768 image.jpg
Likewise, convert JPEG to PPM with ImageMagick in Terminal:
convert image.jpg -depth 8 image.ppm
Working with bare-bones .ppm files is trivial: you have three lines of text (P6, "width height", 255), and then you have the 3*width*height bytes of RGB. As long as you don't need more complicated variants of the .ppm format, you can write a loader and a saver in 5 lines of code each.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With