计算机图形学【1】基础概念,图片格式及C++实现

Chapter 1: 图片格式

PPM

PPM format: PPM格式是一种用于表示图像的文件格式,全称为“Portable Pixmap”,通常也简称为 PPM 图像。它是一种比较简单的图片格式,不需要压缩,也不受操作系统和软件平台的限制。

其文本形式一般为这样:

P3
200 100
255
0 223 45
1 223 45
2 223 45
3 223 45
4 223 45
5 223 45
6 223 45
7 223 45
9 223 45
... ...

格式

PPM文件的格式可以分为三个部分:头信息像素矩阵原始数据

  • 头信息部分是PPM文件的开头,以“P6”或“P3”开头表示彩色或灰度(颜色深度)图像,后面跟着一些描述图像的参数。具体格式如下:

    • P6(或P3) # 文件类型,P6表示二进制数据,P3表示ASCII码
      宽度 # 图像的宽度
      高度 # 图像的高度
      最大像素值 # 每个像素点颜色通道的最大值,通常是255
  • 像素矩阵部分是一个二维数组,存储了每个像素点的颜色信息。

    • 对于彩色图像,每个像素点需要存储红、绿、蓝三个通道的颜色值,因此每个像素点需要占据三个字节。

    • 对于黑白或灰度图像,每个像素点只需要存储一个通道的灰度值,因此每个像素点只需要占据一个字节。

    • 像素的存放顺序通常是从左到右,从上到下的顺序排列。

  • 原始数据部分包含了所有的像素信息,它表示的就是图像的真实内容。

  • 对于二进制文件,每个像素点的颜色信息以三个字节的形式存储(需要注意的是,在使用二进制格式存储PPM文件时,一个像素点的颜色信息可能会被分为多行存储,因此在读取数据时需要注意边界问题和字节对齐的问题);

  • 对于ASCII码文件,每个像素点的颜色信息以一个或多个数字组成的字符串形式存储,每个数值之间通常用空格、制表符或换行符隔开。

注:

颜色信息

其颜色信息通常是以 RGB 三原色模式来表达的,每个像素点包含一个红色通道、一个绿色通道和一个蓝色通道。像素矩阵是一个二维矩阵,存储了每个像素点的颜色值。

对于彩色图片,每个颜色通道都占据一个字节(8位),范围从0到255;

对于灰度图片,只有一个通道,一个像素点只占一个字节,范围同样从0到255。

最大像素值

当然,除了255之外也可以设置其他的最大值,例如 127、63、31 等其他的值 ( 2 n − 1 ) (2^n-1) 2n1。但是需要注意的是,选择不同的最大值将会影响到图像的质量和精度。如果最大值太小,将会导致图像的灰度级别较少,因此可能损失一部分细节信息;如果最大值太大,将会占用更多的空间,同时也可能会造成颜色通道之间的冗余数据。因此,在选择最大值时需要根据具体情况进行调整。

应用

PPM 格式的图像文件大小较大,因此在存储和传输方面相对不够便利,但是其简单、通用的特点使得在实际应用中,其常常作为中间格式和数据交换格式来使用,以便在不同的系统和软件之间进行图像数据的传输和转换。

C++代码实现

/*PPM example*/
int width = 200;
int heighth = 100;
cout << "P3" << "\n" << width << ' ' << heighth << "\n255\n";
for (int i = 0; i < width * heighth; i++)
{
    float intensity = (float)i /(float)(width * heighth);
    int red = (int)(intensity * 255);
    cout << red << ' ' << '0' << ' ' << '0' << endl;
}
cout << "done" << endl;

在这里插入图片描述

//或是可以反向
for (int i = width * heighth-1; i >= 0; i--)

在这里插入图片描述

输出

  1. 可以加入文件处理语句,再将文件后缀改为.ppm

其他样式:

//波纹状
for (int i = (width * heighth)/2; i >= 0; i--)
{
    float intensity = (float)i /(float)(width * heighth)/2;
    int bw = intensity*(width * heighth)/2;
    outfile << '0' << ' ' << bw << ' ' << '0' << endl;
}
for (int i = 0; i < (width * heighth) / 2; i++)
{
    float intensity = (float)i / (float)(width * heighth) / 2;
    int bw = intensity * (width * heighth) / 2;
    outfile << '0' << ' ' << bw << ' ' << '0' << endl;
}
//向外渐变
for (int i = width * heighth - 1; i >= 0; i--)
{
    float intensity = (float)i /(float)(width * heighth);
    int green = 255 - intensity * 512;
    if(green >= 0)
        outfile << '0' << ' ' << green << ' ' << '0' << endl;
    else
        outfile << '0' << ' ' << -green << ' ' << '0' << endl;
}

在这里插入图片描述
在这里插入图片描述

/*双色*/
for (int i = 0, j = width * heighth-1; i < width * heighth; i++, j--)
{
    float ir = (float)i / (float)(width * heighth);
    float ig = (float)j / (float)(width * heighth);
    int r = ir * 255;
    int g = ig * 255;
    int b = 100;
    outfile << r << ' ' << g << ' ' << b << endl;
}
/*三色*/
for (int j = ny - 1; j >= 0; j--)
    for (int i = 0; i < nx; i++)
    {
        float r = float(i) / float(nx);
        float g = float(j) / float(ny);
        float b = g;
        int ir = int(225.99 * r);
        int ig = int(225.99 * g);
        int ib = int(225.99 * b);
        /*pixel matrix(array called better)*/
        outfile << ir << " " << ig << " " << ib << "\n";
    }

在这里插入图片描述
在这里插入图片描述

//灰度(当RGB相同时)
for (int i = 0; i < width * heighth; i++)
{
    float intensity = (float)i / (float)(width * heighth);
    int rgb = intensity * 255;
    outfile << rgb << ' ' << rgb << ' ' << rgb << endl;
}

在这里插入图片描述


BMP

BMP 格式是一种 Microsoft Windows 系统下广泛使用的图片格式,它的格式定义相对 PPM 更为复杂,支持的特性也更加丰富,例如存储有关于图像颜色空间(如 RGB 或 CMYK)的额外元数据。而 XBM 格式不支持颜色深度或者颜色空间的信息。

格式

BMP文件由文件头(BITMAPFILEHEADER)、图像信息头(BITMAPINFOHEADER)以及图像数据三个部分组成(与ppm差不多):

  • 文件头包括两个部分,共14个字节,用于标识该文件的类型和属性,并且出现在文件的开头位置。具体结构如下:
typedef struct tagBITMAPFILEHEADER {
    WORD bfType;     // 2 bytes,指明该文件为BMP文件,值固定为0x4D42(即"BM")
    DWORD bfSize;    // 4 bytes,指明该文件所占空间大小,单位字节
    WORD bfReserved1;// 2 bytes,保留,必须设置为0
    WORD bfReserved2;// 2 bytes,保留,必须设置为0
    DWORD bfOffBits; // 4 bytes,指明从文件头开始到图像数据区域的偏移量,单位字节
} BITMAPFILEHEADER;
  • 信息头记录了与该BMP文件相关的关键性信息,包含40个字节,可以分为不同种类,主要有BITMAPCOREHEADER、BITMAPINFOHEADER、BITMAPV2INFOHEADER、BITMAPV3INFOHEADER、BITMAPV4HEADER和BITMAPV5HEADER等,其中最常用的是BITMAPINFOHEADER,其结构体如下:
typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;          // 4 bytes,指明该信息头的大小
    LONG biWidth;          // 4 bytes,指明图像的宽度,单位像素
    LONG biHeight;         // 4 bytes,指明图像的高度,单位像素
    WORD biPlanes;         // 2 bytes,用于指定目标设备的平面数,必须为1
    WORD biBitCount;       // 2 bytes,指明每个像素占用的位数(即颜色深度)
    DWORD biCompression;   // 4 bytes,指明图像数据的压缩方式
    DWORD biSizeImage;     // 4 bytes,指明图像数据的大小,单位字节
    LONG biXPelsPerMeter;  // 4 bytes,指明水平分辨率,单位像素/米
    LONG biYPelsPerMeter;  // 4 bytes,指明垂直分辨率,单位像素/米
    DWORD biClrUsed;       // 4 bytes,指明调色板中实际使用的颜色数
    DWORD biClrImportant;  // 4 bytes,指明被认为是重要颜色的索引数
} BITMAPINFOHEADER;
  • 颜色表
    BMP文件支持使用颜色表来记录图像色彩信息,存储在文件中的调色板表可以由BI_RGB标志指定的24位和32位格式省略

  • 位图数据区
    BMP文件的最后部分就是实际的位图数据,如同名字所示,该区域包含了图像中每个像素的颜色信息。在不压缩的BMP文件中,每一个像素都与调色板中的一个颜色对应,若采用24位或32位真彩色,则将RGB或RGBA等颜色直接编码到图像数据中。

BMP文件格式文件大小相对较大,通常适合于需要保留图像精度的场合,而因为历史和技术原因,其一些参数的取值也相对随意,因此在解析时需要注意读取方式和对错误值的处理。

C++实现

#include <iostream>
#include <fstream>

using namespace std;

#pragma pack(push, 1) // 设置按照字节对齐
struct BMPHeader {
    char header_field1[2] {'B', 'M'}; // BMP 文件头标识
    uint32_t file_size; // 文件大小,4 个字节
    uint16_t reserved1 {0}; // 保留字段,2 个字节
    uint16_t reserved2 {0}; // 保留字段,2 个字节
    uint32_t pixel_data_offset; // 像素数据起始位置,从文件头开始计算的偏移量,4 个字节
    uint32_t dib_header_size {40}; // DIB 头大小,4 个字节
    int32_t width; // 图像宽度,4 个字节
    int32_t height; // 图像高度,4 个字节
    uint16_t planes {1}; // 位平面数,设定为 1,2 个字节
    uint16_t bits_per_pixel {24}; // 每个像素的位数,3 byte,24 位
    uint32_t compression {0}; // 压缩类型,BI_RGB 表示无压缩,4 个字节
    uint32_t size_of_bitmap {0}; // 图像大小,以字节为单位,BI_RGB 模式下可以设置为 0,4 个字节
    int32_t horizontal_resolution {2835}; // 水平分辨率,像素/米,很随意
    int32_t vertical_resolution {2835}; // 垂直分辨率,像素/米,很随意
    uint32_t colors_used {0}; // 颜色表中实际使用的颜色数,BI_RGB 模式下可以设置为 0,4 个字节
    uint32_t important_colors {0}; // 指定重要的颜色数,BI_RGB 模式下可以设置为 0,4 个字节
};
#pragma pack(pop)

int main()
{
    int nx = 200;
    int ny = 100;

    const int row_size = ((nx * 3 + 3) / 4) * 4; // BMP 中每行像素数据必须是 4 的倍数,对于 24 位像素数据,每个像素占用 3 byte
    const int pixel_data_size = row_size * ny; // 计算像素数据总大小
    const int file_size = sizeof(BMPHeader) + pixel_data_size; // 计算文件总大小
    
    BMPHeader bmp_header;
    bmp_header.file_size = file_size;
    bmp_header.pixel_data_offset = sizeof(BMPHeader); // BMP 文件头和 DIB 头共计 54 bytes
    
    bmp_header.width = nx;
    bmp_header.height = ny;
    
    ofstream outfile("mytest.bmp", ios_base::out | ios_base::binary);
    outfile.write(reinterpret_cast<const char*>(&bmp_header), sizeof(BMPHeader));
    
    std::cout << "P3\n" << nx << " " << ny << "\n255\n";
    for (int j = ny - 1; j >= 0; j--)
    {
        for (int i = 0; i < nx; i++)
        {
            float r = float(i) / float(nx);
            float g = float(j) / float(ny);
            float b = 0.2;
            int ir = int(255.99 * r);
            int ig = int(255.99 * g);
            int ib = int(255.99 * b);
    
            outfile.put(static_cast<char>(ib));
            outfile.put(static_cast<char>(ig));
            outfile.put(static_cast<char>(ir));
            std::cout << ir << " " << ig << " " << ib << "\n";
        }
        for (int k = 0; k < row_size - nx * 3; k++) {
            outfile.put(static_cast<char>(0)); // 填充到 4 的倍数
        }
    }
}

输出

在这里插入图片描述


JPEG/JPG

JPEG/JPG是一种有损的压缩图像格式,采用了离散余弦变换(DCT)和量化的技术来压缩图像数据。

格式

  • 文件头
    JPG文件的文件头共2个字节,用于标识该文件的类型和属性。其中第一个字节的值为0xFF,表示这是一个JPG文件,第二个字节决定了接下来的数据类型。

  • 图像信息
    JPG的图像信息包括两类数据:应用数据段(APPn,n=0f)和标记数据段(Mk,k=0f)。

  • 应用数据段:包含了与该图像相关的应用程序的信息,例如照片拍摄时间、设备制造商等。
    标记数据段:包含了该图像的压缩信息、分量数、每个分量的采样精度以及哈夫曼表(Huffman Table)等信息。其中,哈夫曼表记录了JPG压缩算法中使用的编码表,用于将图像的像素值编码为二进制数据。

  • 量化表
    JPG使用量化表对图像进行有损压缩,用于降低图像数据冗余和噪声的影响,从而减小图像文件的大小。

  • 帧数据
    JPG采用基于YCbCr颜色空间的色彩模型,将图像分解为亮度(Y)和色度(Cb和Cr)三个分量,并将每个分量进行离散余弦变换和量化处理。帧数据段包括了压缩后的亮度和色度数据以及采样率等信息。

  • 扫描数据
    扫描数据段是JPG文件中最重要的部分,它包含了压缩后的图像数据。在该数据段中,采用了游程编码(Run-length Coding)和哈夫曼编码(Huffman Coding)等算法对图像数据进行压缩和编码,在遍历过程中根据哈夫曼表解码并还原出图像数据。

JPG是一种高效的图像压缩格式,具有良好的压缩比和图像质量。但它也是一种有损压缩格式,会丢失一部分图像信息,因此适合于在保证图像质量的前提下尽可能地减小文件大小

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值