图片展示需要BGR模式的三维向量,图片的编码是把BGR图片编码成文件能存储的格式,解码则反之。目前常见的编码为jpg、png、gif等。新兴的如webp、heic。
BMP
从简单入手,BMP是最简单的编码方式,甚至数十行代码就能完成编码和解码简单的程序。
bmp由文件头和位图信息头组成
import struct import numpy as np BITMAP_FILE_HEADER_FMT = '<2sI4xI' BITMAP_FILE_HEADER_SIZE = struct.calcsize(BITMAP_FILE_HEADER_FMT) BITMAP_INFO_FMT = '0 else range(0, header.bi_height) for y in y_axis: for x in range(0, header.bi_width): plex = np.array(struct.unpack_from('<' + str(channel) + 'B', self.__data, offset), np.int8) img[y][x] = plex offset += channel return img class BmpEncoder: def __init__(self, img): self.__img = img def write_data(self): image_height, image_width, channel = self.__img.shape # 只支持RGB或者RGBA图片 if channel != 3 and channel != 4: return False header = BmpHeader() header.bf_type = b'BM' header.bi_bit_count = channel * 8 header.bi_width = image_width header.bi_height = image_height header.bi_size = BITMAP_INFO_SIZE header.bf_off_bits = header.bi_size + BITMAP_FILE_HEADER_SIZE header.bf_size = header.bf_off_bits + image_height * image_width * channel buffer = bytearray(header.bf_size) # bmp信息头 struct.pack_into(BITMAP_FILE_HEADER_FMT, buffer, 0, header.bf_type, header.bf_size, header.bf_off_bits) # 位图信息头 struct.pack_into(BITMAP_INFO_FMT, buffer, BITMAP_FILE_HEADER_SIZE, header.bi_size, header.bi_width, header.bi_height, header.bi_planes, header.bi_bit_count, header.bi_compression, header.bi_size_image, header.bi_x_pels_per_meter, header.bi_y_pels_per_meter, header.bi_clr_used, header.bi_clr_important) # 位图,一般都是纵坐标倒序模式 offset = header.bf_off_bits for y in range(header.bi_height - 1, -1, -1): for x in range(header.bi_width): struct.pack_into('<' + str(channel) + 'B', buffer, offset, *self.__img[y][x]) offset += channel return buffer
bmp图片的纵坐标是反过来的,如下图所示:
JPEGJPEG是一种编码压缩方法,真正描述图片如何存储的是JFIF(JPEG File Interchange Format),但是普通交流中往往使用“JPEG文件”这种叫法。由于精力有限,只尝试了JPEG解码的步骤。
背景知识 DCT离散余弦变换(discrete cosine transform),把信号从空域转换成频域,且具有较好的能量聚集。变换公式如下:
DCT:,其中。
IDCT:,其中。
可以阅读matlab的帮助文档离散余弦变换- MATLAB & Simulink- MathWorks 中国,或者一篇博客离散余弦变换(DCT)的来龙去脉_独孤呆博的博客-CSDN博客_二维离散余弦变换。
哈夫曼编码根据符号出现概率,使用较短的编码更频繁出现的符号。更详细的可以阅读详细图解哈夫曼Huffman编码树_无鞋童鞋的博客-CSDN博客_huffman编码树
色差信号使用亮度和蓝色、红色的浓度偏移量描述图像信号的色彩空间,和RGB转换公式可阅读https://en.wikipedia.org/wiki/YCbCr。使用YCbCr是因为,人眼对于亮度对比的感知能力比色彩的感知能力要强,把亮度分量分离出来后,可以有针对性地使用不同的量化表、采样因子来达到不同的压缩率,且人眼感知不强。
读取JPEG文件HeaderJPEG文件在制定规范时,定义文件是由marker和segment组成。marker都是以0xff开头,以非0x00结束。对应常用marker如下:
| marker | value | description |
|---|---|---|
| SOI | 0xFFD8 | 图像开始(Start Of Scan) |
| APP0 | 0xFFE0 | 存储图像参数 |
| APP1 | 0xFFE1 | EXIF |
| APP2 | 0xFFE2 | |
| APP12 | 0xFFEC | 图片质量等信息 |
| APP13 | 0xFFED | phptoshop存储的信息Photoshop Tags |
| SOF0 | 0xFFC0 | Start Of frame,SOF0是baseline DCT |
| SOF2 | 0xFFC2 | Start Of frame,SOF2是progressive DCT |
| DHT | 0xFFC4 | Define Huffman Table,定义哈夫曼编码表,可以有多个,具体重建哈夫曼树方法见下 |
| DQT | 0xFFDB | Define Quantization Table,定义量化表,可以有多个。量化表能影响图片的压缩质量 |
| DRI | 0xFFDD | Define Restart Interval,重置DC信号的间隔(每解码指定次MCU就重置DC信号) |
| SOS | 0xFFDA | Start Of Scan |
| image data | 如果有0xFF的数据,会使用0xFF00表示,解码的时候需要注意 | |
| EOI | 0xFFD9 | End Of Image |
更多marker可以参考exiftool的文档JPEG Tags
APP0| field | size(bytes) | description |
|---|---|---|
| 长度 | 2 | 包括这个字段为首的整个segment长度 |
| 标识符 | 5 | 图片编码方式,“JFIF "或者”JFXX “等,下面的字段均以JFIF为示例 |
JFIF
| JFIF版本 | 2 | 第一个字节为主版本,第二个字节为次要版本(01 02表示1.02) |
| 密度单位 | 1 | 下列像素密度字段的单位 |
| x方向密度 | 2 | 水平像素密度。不得为零。 |
| y方向密度 | 2 | 垂直像素密度。不得为零。 |
| 缩略图宽度 | 1 | 嵌入的RGB缩略图的水平像素数。可以为零。 |
| 缩略图高度 | 1 | 嵌入的RGB缩略图的垂直像素数。可以为零。 |
| 缩略图数据 | 3n | 未压缩的24位RGB(每个颜色通道8位)光栅缩略图数据,顺序为R0、G0、B0、...Rn、Gn、Bn;其中n = Xthumbnail × Ythumbnail。 |
| field | size(bytes) | description |
|---|---|---|
| 长度 | 2 | 包括这个字段为首的整个segment长度 |
| 标识符 | "Ducky"等 |
Ducky
| field | size(bytes) | description |
|---|---|---|
| tag | 2 | 0x0001:压缩质量,uint32 0x0002:评论,string 0x0003:版权,string |
| 长度 | 2 | 接下来的内容长度 |
| 内容 |
| field | size(bytes) | description |
|---|---|---|
| 长度 | 2 | 包括这个字段为首的整个segment长度 |
| 精度 | 1 | 每像素数据位数,一般为8bit |
| 高度 | 2 | 图像高度 |
| 宽度 | 2 | 图像宽度 |
| 颜色分量数 | 1 | 01:灰度图 03:YCbCr图,一般为这个 04:CMYK |
| 颜色分量信息 | 三色分量数*3 | 3字节分别为:
|



