【Python】读取显示pgm图像文件

文章目录

  • 零. 前言
  • 一. pgm基本概念
  • 二. pgm基本信息读取
  • 三. pgm图像渲染
  • 四. 代码优化

零. 前言

这学期要学多媒体信息隐藏对抗,发现其中的图像数据集文件都是pgm文件形式的。虽然是图像文件,但是却不能直接通过图像查看器来打开,上网一搜:”如何打开pgm文件?“多半是使用第三方软件photoshop之类的。

都是能写代码的人了,难道为了看几张图片还要下一个几G软件吗?

至此,我就开始考虑如何使用python读取pgm(Portable Gray Map)文件并显示出来。

一. pgm基本概念

如果使用记事本的方式打开,可以看到如下格式(以P2为例):

P2
width height
max_gray_value
pixel1 pixel2 pixel3 ... pixelN
...

例如下面的P5:

【Python】读取显示pgm图像文件_第1张图片

下面我们逐行解析一下:

  • 首行:Magic Number(魔数)是portable像素图片文件中的一个标识符,用于指示文件的类型和格式。以下是文件中可能出现的几种魔数及其含义:
    1. P1:表示这是一个ASCII格式的黑白二值图像。在这种格式下,像素的灰度值只能是0或1。
    2. P2:表示这是一个ASCII格式的灰度图像。在这种格式下,像素的灰度值可以是0到最大灰度值之间的任何整数。
    3. P3:表示这是一个ASCII格式的彩色图像。在这种格式下,每个像素包含三个分量(红、绿、蓝)的灰度值。
    4. P4:表示这是一个二进制格式的黑白二值图像。在这种格式下,像素的灰度值只能是0或1。
    5. P5:表示这是一个二进制格式的灰度图像。在这种格式下,像素的灰度值可以是0到最大灰度值之间的任何整数。
    6. P6:表示这是一个二进制格式的彩色图像。在这种格式下,每个像素包含三个分量(红、绿、蓝)的灰度值。

基于此,我们可以得到如下表格:

魔数 类型 编码方式 文件后缀
P1 单色图 ASSII PBM
P2 灰度图 ASSII PGM
P3 像素图 ASSII PPM
P4 单色图 二进制 PBM
P5 灰度图 二进制 PGM
P6 像素图 二进制 PPM
  • 第二行:widthheight 表示图像的宽度和高度。

  • 第三行:

    • 如果是P1、P4:不存在颜色分量的最大值,即没有表示。
    • 如果是P2、P5:max_gray_value 表示灰度值的最大值(通常是255)。
    • 如果是P3、P6:max_color_value 表示颜色分量的最大值(通常是255)。
  • 后续:pixel1, pixel2, … pixelN 是图像的像素值。

    ​ 像素值可以是二进制或ASCII格式,如上图所示,如果无法解析成ASCII形式的字符,则表示这个pgm文件是二进制表示的pixels。

二. pgm基本信息读取

针对不同编码方式表示的像素,我们具有不同的读取与解析方案read_binary_pgm以及read_ascii_pgm。

代码如下:

def read_binary_pgm(file_path):
    with open(file_path, 'rb') as f:
        # 读取头部信息
        magic_number = f.readline().decode().strip()
        width, height = map(int, f.readline().decode().strip().split())
        max_gray_value = int(f.readline().decode().strip())

        # 读取像素值
        pixels = list(f.read())

        return (width, height, max_gray_value, pixels)


def read_ascii_pgm(file_path):
    with open(file_path, 'r') as f:
        # 读取头部信息
        header = f.readline().strip()
        width, height = map(int, f.readline().strip().split())
        max_gray_value = int(f.readline().strip())

        # 读取像素值
        pixels = [int(line) for line in f.readlines() if line.strip()]

        return (width, height, max_gray_value, pixels)


file_path = '../pgmfiles/1.pgm' # 注意修改你的读取路径

# width, height, max_gray_value, pixels = read_ascii_pgm(file_path)
width, height, max_gray_value, pixels = read_binary_pgm(file_path)

print(f"Width: {width}, Height: {height}")
print(f"Max Gray Value: {max_gray_value}")
print(f"Number of Pixels: {len(pixels)}")

这个小demo可以获取到这个pgm的基本信息:

三. pgm图像渲染

但是如何通过这些数据来渲染成图像呢?我们可以先将像素值转换为NumPy数组,并通过matplotlib将其显示为图像。

首先在上述代码顶部导包,并且在尾部追加图像显示代码即可:

import numpy as np
import matplotlib.pyplot as plt

# ...上述read代码...

# 将像素列表转换为NumPy数组
pixels_array = np.array(pixels).reshape(height, width)

# 显示图像
plt.imshow(pixels_array, cmap='gray', vmin=0, vmax=max_gray_value)
plt.show()

运行后,图像渲染如下:
【Python】读取显示pgm图像文件_第2张图片

四. 代码优化

最后,优化一下代码,根据首行的魔数来兼容P2和P5两种情况,实现函数read_pgm。

import numpy as np
import matplotlib.pyplot as plt

file_type = None  # 全局的文件类型变量

def read_pgm(file_path):
    global file_type

    with open(file_path, 'rb') as f:
        magic_number = f.readline().decode().strip()
        file_type = magic_number  # 更新全局的文件类型变量

        width, height = map(int, f.readline().decode().strip().split())
        max_gray_value = int(f.readline().decode().strip())

        if magic_number == 'P5':
            # 二进制格式
            pixels = list(f.read())
        elif magic_number == 'P2':
            # ASCII格式
            pixels = [int(line) for line in f.readlines() if line.strip()]
        else:
            raise ValueError("Unsupported file type.")

        return (width, height, max_gray_value, pixels)


def render_image(pixels, width, height, max_gray_value):
    # 将像素列表转换为NumPy数组
    pixels_array = np.array(pixels).reshape(height, width)

    # 显示图像
    plt.imshow(pixels_array, cmap='gray', vmin=0, vmax=max_gray_value)
    plt.show()


file_path = '../pgmfiles/1.pgm'

width, height, max_gray_value, pixels = read_pgm(file_path)

print(f"Width: {width}, Height: {height}")
print(f"Max Gray Value: {max_gray_value}")
print(f"Number of Pixels: {len(pixels)}")

# 显示图像
render_image(pixels, width, height, max_gray_value)

# 根据全局的文件类型变量进行其他操作
if file_type == 'P5':
    print("This is a binary format PGM file.")
elif file_type == 'P2':
    print("This is an ASCII format PGM file.")

基于此,其实也可以继续优化兼容其他4种文件,比如xx.ppm,xxx.pbm 文件。

再完善一点可以封装成一个小型的ppm/pgm/pbm图像显示器.exe。

不过那个就不在笔者的考虑范围内了。

你可能感兴趣的:(Python,python,pgm,图像读取)