说明
1,输入文件格式为pgm P5格式
2,输出格式为 bmp 格式
格式图解
PGM格式 P5
BMP格式 24位真彩色
步骤
1,读取 pgm 文件,得到图片width,height,像素数据 = [G1,G2,…]
2,因为像素数据范围可能是 0–65535,或者其它,取决于文件描述。要重新映射成 0–255 以写入bmp格式的文件
3,pgm是灰度图像,一个像素只有一个值描述:G;输出采用的bmp是24位真彩色,有3个byte描述一个像素,所以令 R = G = B = G
代码
# -*- coding: UTF-8 -*-
# python2
import math
class PGM(object):
def __init__(self):
self.magic_number = ''
self.width = -1
self.height = -1
self.deep = -1
self.bytes_for_pix = -1
self.data = []
def __str__(self):
rtn_str = 'magic_number: ' + self.magic_number + '\n'
rtn_str += 'width: ' + str(self.width) + '\n'
rtn_str += 'height: ' + str(self.height) + '\n'
rtn_str += 'deep: ' + str(self.deep) + '\n'
rtn_str += 'bytes_for_pix: ' + str(self.bytes_for_pix) + '\n'
rtn_str += 'data length: ' + str(len(self.data))
return rtn_str
def read_pgm(pgm_file):
pgm = PGM()
with open(pgm_file, 'rb') as fi:
first_line = fi.readline() # 读取第一行,是PGM图像的 描述信息, 都是 ASCII 字符
result = first_line.replace('\n', '').split(' ') # e.g.['P5', '1280', '960', '4095']
pgm.magic_number = result[0] # 魔术字,P2或者P5,本例子只读 P5 格式
pgm.width = int(result[1])
pgm.height = int(result[2])
pgm.deep = int(result[3]) # 本PGM文件描述灰度的数据范围 [0,pgm.deep]
pgm.bytes_for_pix = 1 if pgm.deep <= 256 else 2 # 每个像素用1个或者2个字节描述灰度
pgm.data = []
while True:
byte = fi.read(pgm.bytes_for_pix) # 几个字节描述一个像素就读一次读几个字节
if byte == '':
break
val = int(byte.encode('hex'), 16) # 此像素的灰度值
pgm.data.append(val)
return pgm
def write_bmp(pgm):
def get_size_data(value):
base_str = '%08x' % value # 数值转为16进制的 AABBCCDD 字符串
width_str_list = []
for i in range(0, len(base_str), 2):
width_str_list.append(base_str[i:i+2]) # 转为 [AA, BB, CC, DD]
width_str_list = width_str_list[::-1] # 转为小端模式 [DD, CC, BB, AA]
return ' '.join(width_str_list) # 转为字符串 'DD CC BB AA'
width_str = get_size_data(pgm.width)
height_str = get_size_data(pgm.height)
# bmp文件数据头,54个字节,24位真彩色,各种描述信息不管,只修改 width 和 height 数据,小端模式。
bmp_head = '42 4d 36 80 01 00 00 00 00 00 36 00 00 00 28 00 00 00 ' + width_str + ' ' + height_str + ' 01 00 18 00 00 00 00 00 00 80 01 00 74 12 00 00 74 12 00 00 00 00 00 00 00 00 00 00'
bmp_head = bmp_head.split(' ')
bmp_head = [int(each, 16) for each in bmp_head]
# 原数据灰度范围是 0 -- pgm.deep, bmp是 0--256,所以要缩放
factor = int(math.ceil(pgm.deep/256.)) # 向上取整,务使缩放后的数据范围在0--255
bmp_data = [each/factor for each in pgm.data]
# 跟原图比是上下倒的,反一下
bmp_data = bmp_data[::-1]
# 跟原图比是左右倒的,对每行再反一下
out_data = []
for pos in range(0, len(bmp_data), pgm.width):
out_data += bmp_data[pos: pos + pgm.width][::-1]
with open('output.bmp', 'wb') as fo:
for byte in bmp_head:
# bmp 头数据
fo.write(('%02x' % byte).decode('hex'))
import sys
old = 0
for i, byte in enumerate(out_data):
prss = i/(len(out_data) * 1.0) * 100
if prss > old + 10:
old += 10
sys.stdout.write('. ')
# 因为bmp有 R G B,pgm只有一个灰度,取R=G=B=灰度值
fo.write(('%02x' % byte).decode('hex'))
fo.write(('%02x' % byte).decode('hex'))
fo.write(('%02x' % byte).decode('hex'))
if __name__ == '__main__':
pgm = read_pgm('img.pgm')
write_bmp(pgm)
try:
input('done')
except Exception as e:
pass