I have a .pfm which is a (.PF/.pf) file. I am trying to visualize it but I am unable to do so. Normally the .pfm files contain the header of the format.
But my file has this header.I am unable to visualize it as the image can anyone help me out. Any help is appreciated
Typ=Pic98::TPlane
Lines=750
Columns=1125
FirstLine=0
FirstColumn=0
import re
import numpy as np
file = open("PF file.PF", 'rb')
header = file.readline().rstrip().decode('utf-8')
if header == 'PF':
raise Exception('Only ONE channel image is supported.')
elif header == 'Typ=Pic98::TPlane<float>':
color = False
else:
raise Exception('Not a PFM file.')
dim_match = re.match(r'(^(\w+).(\d+)$)\n(^(\w+).(\d+)\s$)',
file.readline().decode('ascii'))
if dim_match:
width, height = map(int, dim_match.groups())
else:
raise Exception('Malformed PFM header.')
if header == 'Typ=Pic98::TPlane<float>':
scale =1
endian = '>'
else:
scale = -scale
endian = '<'
npImage = np.reshape(npImage, width,height)
npImage = np.flipud(npImage)
if ret_PIL:
img = Image.fromarray(npImage, 'F')
return img
return npImage
file.close()
Updated Answer
I have re-written my answer below in a slightly different, hopefully clearer style.
#!/usr/bin/env python3
import re
import cv2
import numpy as np
from PIL import Image
def readPF(filename):
"""Read named PF file into Numpy array"""
# Slurp entire file into memory as binary 'bytes'
with open(filename, 'rb') as f:
data = f.read()
# Check correct header, return None if incorrect
if not re.match(b'Typ=Pic98::TPlane<float>', data):
return None
# Get Lines and Columns, both must be present, else return None
L = re.search(b'Lines=(\d+)', data)
C = re.search(b'Columns=(\d+)', data)
if not (L and C):
return None
height = int(L.groups()[0])
width = int(C.groups()[0])
print(f'DEBUG: Height={height}, width={width}')
# Take the data from the END of the file in case other header lines added at start
na = np.frombuffer(data[-4*height*width:], dtype=np.dtype('<f4')).reshape((height,width))
# Some debug stuff
min, max, mean = na.min(), na.max(), na.mean()
print(f'DEBUG: min={min}, max={max}, mean={mean}')
return na
################################################################################
# Main
################################################################################
na = readPF('PF file.PF')
################################################################################
# Use either of the following to save the image:
################################################################################
# Save with OpenCV as scaled PNG
u16 = (65535*(na - np.min(na))/np.ptp(na)).astype(np.uint16)
cv2.imwrite('OpenCV.png', u16)
# Convert to PIL Image and save as TIFF
pi = Image.fromarray(na, mode='F')
pi.save('PIL.tif')
Original Answer
Not too sure what I image I should be expecting, but here is a rough idea:
#!/usr/bin/env python3
import re
import cv2
import numpy as np
from PIL import Image
file = open("PF file.PF", 'rb')
header = file.readline().rstrip().decode('utf-8')
if header == 'PF':
raise Exception('Only ONE channel image is supported.')
elif header == 'Typ=Pic98::TPlane<float>':
color = False
else:
raise Exception('Not a PFM file.')
while True:
line = file.readline().decode('ascii')
match = re.match('(\w+)=(\d+)', line)
n, v = match.groups()
if n == 'Lines':
height = int(v)
print(f'Height: {height}')
if n == 'Columns':
width = int(v)
print(f'Width: {width}')
break
# Seek backwards from the end of the file in case any clown has added something to the header
file.seek(-height*width*4,2)
# Read remainder of file into Numpy array of floats and reshape
na = np.fromfile(file, dtype=np.float32).reshape((height,width))
# Some debug stuff
min, max, mean = na.min(), na.max(), na.mean()
print(f'DEBUG: min={min}, max={max}, mean={mean}')
################################################################################
# Use either of the following to save the image:
################################################################################
# Save with OpenCV as scaled PNG
u16 = (65535*(na - np.min(na))/np.ptp(na)).astype(np.uint16)
cv2.imwrite('OpenCV.png', u16)
# Convert to PIL Image and save as TIFF
pi = Image.fromarray(na, mode='F')
pi.save('PIL.tif')
The output is as follows:
Height: 750
Width: 1125
DEBUG: min=0.0, max=127881704.0, mean=1618343.625
Another possibility is to use ImageMagick to make it into a PNG, I get the following, and ImageMagick defaults to little-endian, so if this is correct, your image is little endian.
magick -define quantum:format=floating-point -depth 32 -size 1125x750+80 gray:"PF file.pf" -auto-level image.png
Keywords: Python, ImageMagick, image processing, float, float32, Numpy, PFM