JPG学习笔记1(附完整代码)
#topics h2 { background: rgba(43, 102, 149, 1); border-radius: 6px; box-shadow: 0 0 1px rgba(95, 90, 75, 1), 1px 1px 6px 1px rgba(10, 10, 0, 0.5); color: rgba(255, 255, 255, 1); font-family: "微软雅黑", "宋体", "黑体", Arial; font-size: 15px; font-weight: bold; height: 24px; line-height: 23px; margin: 12px 0 !important; padding: 5px 0 5px 10px; text-shadow: 2px 2px 3px rgba(34, 34, 34, 1) }
#topics h1 span { font-weight: bold; line-height: 1.5; font-family: "Helvetica Neue", Helvetica, Verdana, Arial, sans-serif; text-decoration: underline; color: rgba(201, 27, 67, 1); text-shadow: 2px 2px 3px rgba(34, 34, 34, 1) }
在学习图象处理的过程中,JPEG是我的第一个拦路虎。一直很想手写一下JPG的压缩和解压的过程,我在网上找到了一些代码或者文章,很多都是没有注释或者是解释不够清楚的。所以特地写这篇文章记录自己从无到有写一个JPEG_Encoder的过程,也能帮助其他学习图形或者音视频的童鞋。对于不想看文章的同学,这边直接上代码 https://github.com/Cheemion/JPEG_COMPRESS。 以下是JPEG的压缩流程。采样->>离散傅里叶变化->>量化->>哈夫曼压缩->>写入jpg文件. 在进行这些流程之前,必须从BMP文件中读取待压缩的图片文件。

图片引用自"Compressed Image File Formats JPEG, PNG, GIF, XBM, BMP - John Miano"[4]
1.BMP文件Format
[1]BMP文件
一个文件头BITMAPFILEHEADER,里面包含了文件的各种信息。
一个图片头BITMAPINFOHEADER,里面包含了图片信息。
一个RGBQUAD array,里面包含了像素的对应关系,比如1 代表 RGB(1,1,1)。因为我们用的都是24位图片,所以我们不会不考虑这一项。
一个Color-index,就是我们的图片的像素了。我们只考虑24位图像

BMP文件的字节是大端存储的
BMP图片的每一行像素所占的字节数必须是4字节的的整数倍
BMP数据的第一行像素存储的是图片的最后一行的数据(相当于图片在BMP中是倒置的)
2.BITMAPFILEHEADER(头文件格式)
[2]头文件包含如下的字段
1 typedef struct tagBITMAPFILEHEADER {
2 WORD bfType; //文件类型 规定为'BM'
3 DWORD bfSize; //文件大小
4 WORD bfReserved1; //保留
5 WORD bfReserved2; //保留
6 DWORD bfOffBits; //数据起始地址距离首地址的位置
7 } BITMAPFILEHEADER, *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
3.BITMAPINFOHEADER文件格式
[3]bitMapInfoHeader根据版本的不同包含了不同的结构,我们主要用到了BitMapInfoHeader和BitMapCoreHeader
以下是BitMapInfoHeader
1 typedef struct tagBITMAPINFOHEADER {
2 DWORD biSize; // infoHeader这个头文件的大小
3 LONG biWidth; // 图片宽多少像素
4 LONG biHeight; //高多少
5 WORD biPlanes; //默认1
6 WORD biBitCount; //一个像素点有多少位
7 DWORD biCompression; //是否压缩过
8 DWORD biSizeImage; //图片大小
9 LONG biXPelsPerMeter; //never used
10 LONG biYPelsPerMeter; //never used
11 DWORD biClrUsed; //never used
12 DWORD biClrImportant; //never used
13 } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
以下是BitMapCoreHeader, coreHeader就简单多了,就是少了一些字段,其他一毛一样。
typedef struct tagBITMAPCOREHEADER {
DWORD bcSize;
WORD bcWidth;
WORD bcHeight;
WORD bcPlanes;
WORD bcBitCount;
} BITMAPCOREHEADER, *LPBITMAPCOREHEADER, *PBITMAPCOREHEADER;
4.读取图片数据
定义常用结构
using byte = unsigned char;
using uint = unsigned int;
struct RGB {
byte blue;
byte green;
byte red;
};
定义文件结构,和一个BMPReader(用来读取bmp文件)
1 #pragma once
4 class BMPReader {
5 public:
6 BMPReader() = default;
7 ~BMPReader() {
8 if (data) {
9 delete[] data;
data = nullptr;
10 }
11 }
12 bool open(std::string& path);
13 public:
14 uint height = 0;
15 uint width = 0;
16 uint paddingBytes = 0;
17 RGB* data = nullptr; //实际的数据
18 };
19
20 //2个字节对齐,
21 #pragma pack(2)
22 typedef struct {
23 unsigned short bfType = 0x424d;
24 unsigned int bfSize = 0;
25 unsigned short bfReserved1 = 0;
26 unsigned short bfReserved2 = 0;
27 unsigned int bfOffBits = 0;
28 } BitMapFileHeader;
29
30 typedef struct {
31 unsigned int biSize;
32 int biWidth;
33 int biHeight;
34 unsigned short biPlanes;
35 unsigned short biBitCount;
36 unsigned int biCompression;
37 unsigned int biSizeImage;
38 int biXPelsPerMeter;
39 int biYPelsPerMeter;
40 unsigned int biClrUsed;
41 unsigned int biClrImportant;
42 } BitMapInfoHeader;
43
44 typedef struct {
45 unsigned int bcSize;
46 unsigned short bcWidth;
47 unsigned short bcHeight;
48 unsigned short bcPlanes = 1;
49 unsigned short bcBitCount = 24;
50 } BitMapCoreHeader;
51 #pragma pack()
读取图片数据到BMPReader的data字段
bool BMPReader::open(std::string& path) {
FILE* file = nullptr;
//判断是否打开图片成功
if ((file = fopen(path.c_str(), "rb")) == nullptr) {
printf("error occured when opening file:%s", path.c_str());
return false;
}
BitMapFileHeader fileHeader;
//读取文件头
if (fread(&fileHeader, sizeof(fileHeader), 1, file) != 1) {
printf("Error - error occured when reading BITMAPFILEHEADER");
return false;
}
//判断是不是BMP文件,通过判断'BM'
if(fileHeader.bfType != 0x4D42) {
printf("Error - this is not a BMP file that you're reading");
return false;
}
//读取infoHeader的大小,通过size来判断是哪个版本的BitMapInfoHeader
uint infoHeaderSize;
fread(&infoHeaderSize, sizeof(infoHeaderSize), 1, file);
fseek(file, -sizeof(infoHeaderSize), SEEK_CUR);
//2中infoHeader, 通过size来判断
//如果是BitMapCoreHeader的话
if (infoHeaderSize == sizeof(BitMapCoreHeader)) {
BitMapCoreHeader bitMapCoreHeader;
if (fread(&bitMapCoreHeader, sizeof(bitMapCoreHeader), 1, file) != 1) {
printf("Error - mal-structure BITMAPCOREHEADER");
return false;
}
this->width = bitMapCoreHeader.bcWidth;
this->height = bitMapCoreHeader.bcHeight;
if (bitMapCoreHeader.bcBitCount != 24) {
printf("Error - the picture format is not consistent with our programm");
return false;
}
} else {
BitMapInfoHeader bitMapInfoHeader;
if (fread(&bitMapInfoHeader, sizeof(bitMapInfoHeader), 1, file) != 1) {
printf("Error - mal-structure BITMAPINFOHEADER");
return false;
}
this->width = bitMapInfoHeader.biWidth;
this->height = bitMapInfoHeader.biHeight;
if (bitMapInfoHeader.biBitCount != 24) {
printf("Error - the picture format is not consistent with our programm");
return false;
}
}
// 必须是4byte的整数倍,算出需要padding多少字节,也就是需要填补多少字节// if width = 1, 1 * 3个像素 * 8位 = 24位, 差一个字节, paddingSize = 1
// if width = 2, 2 * 3个 * 8位 = 48位 paddingSize = 2
// if width = 3, paddingSize = 3
// if width = 4, paddingSize = 0
this->paddingBytes = width % 4;
//为像素数据 创建内存空间
data = new (std::nothrow) RGB[this->width * this->height];
if (!data) {
printf("Error - error when allocating memroy for RGB");
return false;
}
//跳到data数据的位置
fseek(file, fileHeader.bfOffBits, SEEK_SET);
for (uint i = 0; i < height; i++) {
//read data一行,一行的读取放入我们的data
if (width != fread(data + (height - 1 - i) * width , sizeof(RGB), width, file)) {
printf("Error - something wrong when reading data from BMP file");
delete data;
return false;
}
//因为可能有paddingSize,所以这边跳过PaddingSize
fseek(file, paddingBytes, SEEK_CUR);
}
fclose(file);
return true;
}
以上全部的代码在https://github.com/Cheemion/JPEG_COMPRESS/tree/main/Day1
完结
Thanks for reading, happy lunar new year.
参考资料
[1]https://docs.microsoft.com/en-us/windows/win32/gdi/bitmap-storage
[2]https://docs.microsoft.com/en-us/previous-versions//dd183376(v=vs.85)
[3]https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapcoreheader
JPG学习笔记1(附完整代码)的更多相关文章
- JPG学习笔记3(附完整代码)
#topics h2 { background: rgba(43, 102, 149, 1); border-radius: 6px; box-shadow: 0 0 1px rgba(95, 90, ...
- Android 监听双卡信号强度(附完整代码)
Android 监听双卡信号强度 监听单卡信号强度 监听单卡的信号强度非常简单直接用TelephonyManager.listen()去监听sim卡的信号强度. TelephonyManager = ...
- JPG学习笔记4(附完整代码)
#topics h2 { background: rgba(43, 102, 149, 1); border-radius: 6px; box-shadow: 0 0 1px rgba(95, 90, ...
- JPG学习笔记2(附完整代码)
#topics h2 { background: rgba(43, 102, 149, 1); border-radius: 6px; box-shadow: 0 0 1px rgba(95, 90, ...
- WebGL three.js学习笔记 创建three.js代码的基本框架
WebGL学习----Three.js学习笔记(1) webgl介绍 WebGL是一种3D绘图协议,它把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的 ...
- 学习笔记:python3,代码。小例子习作(2017)
http://www.cnblogs.com/qq21270/p/7634025.html 学习笔记:python3,一些基本语句(一些基础语法的代码,被挪到这里了) 日期和时间操作 http://b ...
- 学习笔记:python3,代码。小例子习作
http://www.cnblogs.com/qq21270/p/7634025.html 学习笔记:python3,一些基本语句(一些基础语法的代码,被挪到这里了) 日期和时间操作 http://b ...
- 雨痕 的《Python学习笔记》--附脑图(转)
原文:http://www.pythoner.com/148.html 近日,在某微博上看到有人推荐了 雨痕 的<Python学习笔记>,从github上下载下来看了下,确实很不错. 注意 ...
- Linux Shell输出颜色字符学习笔记(附Python脚本实现自动化定制生成)
齿轮发出咔嚓一声,向前进了一格.而一旦向前迈进,齿轮就不能倒退了.这就是世界的规则. 0x01背景 造了个轮子:御剑师傅的ipintervalmerge的Python版本.觉得打印的提示信息如果是普通 ...
- 基于C#的内网穿透学习笔记(附源码)
如何让两台处在不同内网的主机直接互连?你需要内网穿透! 上图是一个非完整版内外网通讯图由内网端先发起,内网设备192.168.1.2:6677发送数据到外网时候必须经过nat会转换成 ...
随机推荐
- 处理Promise.reject()
一般处理Promise.reject()都是catch住错误,然后进行错误处理,一般都是再次发起请求或者直接打印. 直接打印的情况用console.error()就可以了,而再次发起请求呢? 最好是先 ...
- 你这样用过DO循环吗?
DATA: BEGIN OF text, word1(4) TYPE c VALUE 'This', word2(4) TYPE c VALUE 'is', ...
- AOP面向切面编程(使用注解和使用配置文件)
Aop(面向切面编程) 使用注解的方式: 加入相应的jar包: com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspe ...
- (10)-Python3之--引入
1.什么是模块 .py文件就是模块 模块名有命名要求: 1.不要以数字.下划线开头.特殊符号.也不要以中文开头. 2.通常来说,都是以字母开头. 3.不要以关键字来命名.内置函数.内置模块.不要以第三 ...
- DDD的实体、值对象、聚合根的基类和接口:设计与实现
1 前置阅读 在阅读本文章之前,你可以先阅读: 什么是DDD 2 实现值对象 值对象有两个主要特征:它们没有任何标识.它们是不可变的. 我们举个例子:小明是"浙江宁波"人,小红也是 ...
- 【vulnhub】靶机-【DC系列】DC9(附靶机)
出品|MS08067实验室(www.ms08067.com) 本文作者:大方子(Ms08067实验室核心成员) 主机信息 Kali:192.168.56.113 DC9:192.168.56.112 ...
- monitor a local unix domain socket like tcpdump
Can I monitor a local unix domain socket like tcpdump? - Super User https://superuser.com/questions/ ...
- Buffer Data RDMA 零拷贝 直接内存访问
waylau/netty-4-user-guide: Chinese translation of Netty 4.x User Guide. 中文翻译<Netty 4.x 用户指南> h ...
- 那些我们不知道的 Python 免费学习资料
作者:小R编辑:AI 兔兔 Python 语言因为其易学,以及强大的功能,是很多刚开始学习编程的入门语言的选择之一. Python 语言被列入中小学教材后引起了越来越多人的关注. 希望孩子学习编程的家 ...
- ByteDance 2019 春招题目
牛客网字节跳动笔试真题:https://www.nowcoder.com/test/16516564/summary 分了 2 次做,磕磕碰碰才写完,弱鸡悲鸣. 1. 聪明的编辑 题目:Link . ...