这篇博文解决如下问题
问题:
自编实现JPEG压缩标准,将一幅未压缩图像通过JPEG压缩变成压缩数据流。
jpeg压缩的步骤如下:
Y分量、Cb分量、Cr分量采样比例如下图所示:
4:4:4就是全采样,对Cb分量、Cr分量的所有像素编码,如果4:2:0比例的话,就是Y分量全采样,Cb、Cr分量每四个像素采一个像素,因为人眼对亮度信息会更敏感一些,因此Y分量一般都是全采样,而Cb、Cr分量颜色信息会丢弃一些像素。这样做不仅提高了图片的压缩比,而且还不影响图片的可视化质量。
采样之后,就是分别对采样后的Y分量、Cb分量、Cr分量按照8x8分块进行DCT变换,DCT变换的作用主要是提取各分量的高频信息和低频信息,然后舍弃部分高频信息。
之后对DCT矩阵量化,量化矩阵一般是固定的,但是有两套方案,分别是针对亮度的亮度量化表和针对色度的色度量化表。如下图所示:
接下来就是对DC系数和AC系数编码。DC系数就是DCT矩阵中(0,0)位置的值,除此之外,其它的所有系数多是AC系数,对AC系数编码时,63个DCT系数的编码顺序有要求,因为DCT矩阵不管是从上到下还是从左到右,DCT系数都在变小,为了能得到更好的压缩比,jpeg采用Z字型编码顺序。如下图所示:
另外对于DC系数和AC系数有不同的编码方案。
DC系数编码:
相邻8x8分块间的DC系数是相差不会很大,因此只需要记录相邻DC系数的差值即可。下面举个例子。
对于这些DC系数,按照如下步骤进行对DC系数编码。
1)将DC系数转换为二进制数表示,如果系数为负数,用反码表示。
2)计算DC系数的二进制码长n
3)查表,找到码长对应的哈夫曼编码码字
4)编码=DC系数的哈夫曼编码码字+DC系数二进制编码
例如上述DC系数的编码如下图所示:
AC系数编码:
按照ZIG扫描顺序,如果AC系数是0,先记录连续0的个数,但是连续0的个数不能超过16个,每16个连续0应记录一个编码,但是这一切的前提是AC系数为0的后面还有不为0的AC系数。如果AC系数非0,则记录三个数字(a,b,c),a代表非0AC系数之前有几个连续0,b代表AC系数,c代表当前AC系数后面的所有AC系数是否全为0,如果是,c=1;否则,c=0。ZIG扫描完成之后,会得到如下图所示的这样一张表:
其中,AC系数编码步骤为:
1)将AC系数转换游程编码用三个量(a,b,c)表示
2)将系数b转换为二进制表示,如果AC系数为负数,用反码表示
3)计算系数b二进制表示的码长n
4)查表,找到游程a、二进制码长n对应的哈夫曼编码码字,即a/n所对应的码字。
5)编码=AC系数的哈夫曼编码码字+AC系数二进制编码
6)在一个块结束后加一个EOB块结束标志(0/0)
以上就是JPEG编码原理。下面用JPEG对下面这张图进行编码。
部分编码如下图所示:
python代码
import base64
import matplotlib.pyplot as plt # plt 用于显示图片
import numpy as np
import cv2
#Y分量压缩
def compress_Y(img_data, quality_scale=50):
#获取图像数据流宽高
h, w = img_data.shape
#标准亮度量化表
Qy = np.array([[16, 11, 10, 16, 24, 40, 51, 61],
[12, 12, 14, 19, 26, 58, 60, 55],
[14, 13, 16, 24, 40, 57, 69, 56],
[14, 17, 22, 29, 51, 87, 80, 62],
[18, 22, 37, 56, 68, 109, 103, 77],
[24, 35, 55, 64, 81, 104, 113, 92],
[49, 64, 78, 87, 103, 121, 120, 101],
[72, 92, 95, 98, 112, 100, 103, 99]], dtype=np.uint8)
#根据压缩质量重新计算量化表
if quality_scale <= 0:
quality_scale = 1
elif quality_scale >= 100:
quality_scale = 99
for i in range(64):
tmp = int((Qy[int(i / 8)][i % 8] * quality_scale + 50) / 100)
if tmp <= 0:
tmp = 1
elif tmp > 255:
tmp = 255
Qy[int(i / 8)][i % 8] = tmp
#Z字型
ZigZag = [
0, 1, 5, 6, 14, 15, 27, 28,
2, 4, 7, 13, 16, 26, 29, 42,
3, 8, 12, 17, 25, 30, 41, 43,
9, 11, 18, 24, 31, 40, 44, 53,
10, 19, 23, 32, 39, 45, 52, 54,
20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63]
#DC系数哈夫曼编码表
standard_dc_nrcodes = [0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
standard_dc_values = [4, 5, 3, 2, 6, 1, 0, 7, 8, 9, 10, 11]
pos_in_table = 0;
code_value = 0;
dc_huffman_table = [0] * 16
for i in range(1, 9):
for j in range(1, standard_dc_nrcodes[i - 1] + 1):
dc_huffman_table[standard_dc_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
pos_in_table += 1
code_value += 1
code_value <<= 1
#AC系数哈夫曼编码表
standard_ac_nrcodes = [0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d]
standard_ac_values = [0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa]
pos_in_table = 0;
code_value = 0;
ac_huffman_table = [0] * 256
for i in range(1, 17):
for j in range(1, standard_ac_nrcodes[i - 1] + 1):
ac_huffman_table[standard_ac_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
pos_in_table += 1
code_value += 1
code_value <<= 1
# 转成float类型
img_data = img_data.astype(np.float64)
#result存储最后的哈夫曼编码
result = ''
#DC系数初始值为0
prev = 0
#分成8*8的块
#遍历所有8*8分块
for i in range(h // 8):
for j in range(w // 8):
block = img_data[i * 8:(i + 1) * 8, j * 8:(j + 1) * 8] - 128
block = cv2.dct(block)
# 数据量化
block[:] = np.round(block / Qy)
# 把量化后的二维矩阵转成一维数组
arr = [0] * 64
notnull_num = 0
for k in range(64):
tmp = int(block[int(k / 8)][k % 8])
arr[ZigZag[k]] = tmp;
# 统计arr数组中有多少个非0元素
if tmp != 0:
notnull_num += 1
#RLE编码
#标识连续0的个数
time = 0
# 处理DC
if arr[0] != 0:
notnull_num -= 1
data = arr[0] - prev#计算相邻DC系数的差值
data2 = bin(np.abs(data))[2:]#将DC系数的差值转换成为二进制编码
# print(bin(np.abs(data)));
data1 = len(data2)#计算DC系数差值二进制码长
#判断DC系数差值是否小于0,小于0的DC系数差值用反码表示
if data < 0:
#将小于0的DC系数二进制编码转换成反码
data2 = bin(np.abs(data) ^ (2 ** data1 - 1))[2:].rjust(data1, '0')
#如果DC系数差值为0,不需要任何表示
if data == 0:
data2 = ''
data1 = 0
#从DC系数编码表中找到与DC系数差值二进制码长对应的哈夫曼编码码字
#编码=DC系数的哈夫曼编码码字 + DC系数二进制编码
result += dc_huffman_table[data1]
result += data2
#更新前一个DC系数
prev = arr[0]
#遍历所有AC系数,1~63
for k in range(1, 64):
#有可能AC系数全为0,所以要先进行判断
if notnull_num == 0:
#如果AC系数全是0,添加EOB
result += '1010'
break
#如果当前AC系数是0,统计连续0的个数,最大连续0的个数为16
if arr[k] == 0 and time < 15:
time += 1
else:
#BIT编码
#处理括号中第二个数
data = arr[k]#获得系数b
data2 = bin(np.abs(data))[2:]#系数b转换成二进制编码
data1 = len(data2)#计算系数b二进制码长
#如果系数b是负数,用反码表示
if data < 0:
#将系数b的二进制编码转换成反码
data2 = bin(np.abs(data) ^ (2 ** data1 - 1))[2:].rjust(data1, '0')
#如果系数b=0,不需要任何表示
if data == 0:
data1 = 0
data2 = ''
# 哈夫曼编码,序列化
#从AC系数编码表中找到游程为a、二进制码长为n对应的哈夫曼编码码字
#编码=AC系数的哈夫曼编码码字+AC系数二进制编码
result += ac_huffman_table[time * 16 + data1]
result += data2
time = 0#复位time=0
#判断是否要添加EOB
if int(arr[k]) != 0:
notnull_num -= 1
#编码结束后,判断哈夫曼编码长度是否是8的整数倍,
#如果不是,补足为8的整数倍,以便编码成16进制数据
if len(result) % 8 != 0:
result = result.ljust(len(result) + 8 - len(result) % 8, '0')
#将哈夫曼编码转换成为16进制编码
res_data = ''
for i in range(0, len(result), 8):
temp = int(result[i:i + 8], 2)
res_data += hex(temp)[2:].rjust(2, '0').upper()
if temp == 255:
res_data += '00'
#更新result的值为哈夫曼编码对应的16进制编码
result = res_data
#定义jpeg输出流
res = ''
#添加jpeg文件头
#SOI(文件头),共89个字节
res += 'FFD8'
#APP0图像识别信息
res += 'FFE000104A46494600010100000100010000'
#DQT定义量化表
res += 'FFDB004300'
#将64字节的量化表添加到jpeg输出流
for i in range(64):
res += hex(Qy[int(i / 8)][i % 8])[2:].rjust(2, '0')
#SOF0图像基本信息,13个字节
res += 'FFC0000B08'
#将图像的长度信息和宽度信息添加到jpeg输出流
res += hex(h)[2:].rjust(4, '0')
res += hex(w)[2:].rjust(4, '0')
#res+='01012200'
#采样系数好像都是1
res += '01011100'
#DHT定义huffman表,33个字节+183
res += 'FFC4001F00'
#将DC系数编码表添加到jpeg输出流
for i in standard_dc_nrcodes:
res += hex(i)[2:].rjust(2, '0')
for i in standard_dc_values:
res += hex(i)[2:].rjust(2, '0')
res += 'FFC400B510'
# 将AC系数编码表添加到jpeg输出流
for i in standard_ac_nrcodes:
res += hex(i)[2:].rjust(2, '0')
for i in standard_ac_values:
res += hex(i)[2:].rjust(2, '0')
#SOS扫描行开始,10个字节
res += 'FFDA0008010100003F00'
#压缩的图像数据(一个个扫描行),数据存放顺序是从左到右、从上到下
res += result
#EOI文件尾0
res += 'FFD9'
# print(res);
return res
#Cb,Cr分量压缩
def compress_Cb_Cr(img_data, quality_scale=50):
#获取图像数据流宽高
h, w = img_data.shape
#标准亮度量化表
Qy = np.array([[17,18,24,47,99,99,99,99],
[18,21,26,66,99,99,99,99],
[24,26,56,99,99,99,99,99],
[47,66,99,99,99,99,99,99],
[99,99,99,99,99,99,99,99],
[99,99,99,99,99,99,99,99],
[99,99,99,99,99,99,99,99],
[99,99,99,99,99,99,99,99]], dtype=np.uint8)
#根据压缩质量重新计算量化表
if quality_scale <= 0:
quality_scale = 1
elif quality_scale >= 100:
quality_scale = 99
for i in range(64):
tmp = int((Qy[int(i / 8)][i % 8] * quality_scale + 50) / 100)
if tmp <= 0:
tmp = 1
elif tmp > 255:
tmp = 255
Qy[int(i / 8)][i % 8] = tmp
#Z字型
ZigZag = [
0, 1, 5, 6, 14, 15, 27, 28,
2, 4, 7, 13, 16, 26, 29, 42,
3, 8, 12, 17, 25, 30, 41, 43,
9, 11, 18, 24, 31, 40, 44, 53,
10, 19, 23, 32, 39, 45, 52, 54,
20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61,
35, 36, 48, 49, 57, 58, 62, 63]
#DC系数哈夫曼编码表
standard_dc_nrcodes = [0, 0, 7, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
standard_dc_values = [4, 5, 3, 2, 6, 1, 0, 7, 8, 9, 10, 11]
pos_in_table = 0;
code_value = 0;
dc_huffman_table = [0] * 16
for i in range(1, 9):
for j in range(1, standard_dc_nrcodes[i - 1] + 1):
dc_huffman_table[standard_dc_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
pos_in_table += 1
code_value += 1
code_value <<= 1
#AC系数哈夫曼编码表
standard_ac_nrcodes = [0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d]
standard_ac_values = [0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa]
pos_in_table = 0;
code_value = 0;
ac_huffman_table = [0] * 256
for i in range(1, 17):
for j in range(1, standard_ac_nrcodes[i - 1] + 1):
ac_huffman_table[standard_ac_values[pos_in_table]] = bin(code_value)[2:].rjust(i, '0')
pos_in_table += 1
code_value += 1
code_value <<= 1
# 转成float类型
img_data = img_data.astype(np.float64)
#result存储最后的哈夫曼编码
result = ''
#DC系数初始值为0
prev = 0
#分成8*8的块
#遍历所有8*8分块
for i in range(h // 8):
for j in range(w // 8):
block = img_data[i * 8:(i + 1) * 8, j * 8:(j + 1) * 8] - 128
block = cv2.dct(block)
# 数据量化
block[:] = np.round(block / Qy)
# 把量化后的二维矩阵转成一维数组
arr = [0] * 64
notnull_num = 0
for k in range(64):
tmp = int(block[int(k / 8)][k % 8])
arr[ZigZag[k]] = tmp;
# 统计arr数组中有多少个非0元素
if tmp != 0:
notnull_num += 1
#RLE编码
#标识连续0的个数
time = 0
# 处理DC
if arr[0] != 0:
notnull_num -= 1
data = arr[0] - prev#计算相邻DC系数的差值
data2 = bin(np.abs(data))[2:]#将DC系数的差值转换成为二进制编码
# print(bin(np.abs(data)));
data1 = len(data2)#计算DC系数差值二进制码长
#判断DC系数差值是否小于0,小于0的DC系数差值用反码表示
if data < 0:
#将小于0的DC系数二进制编码转换成反码
data2 = bin(np.abs(data) ^ (2 ** data1 - 1))[2:].rjust(data1, '0')
#如果DC系数差值为0,不需要任何表示
if data == 0:
data2 = ''
data1 = 0
#从DC系数编码表中找到与DC系数差值二进制码长对应的哈夫曼编码码字
#编码=DC系数的哈夫曼编码码字 + DC系数二进制编码
result += dc_huffman_table[data1]
result += data2
#更新前一个DC系数
prev = arr[0]
#遍历所有AC系数,1~63
for k in range(1, 64):
#有可能AC系数全为0,所以要先进行判断
if notnull_num == 0:
#如果AC系数全是0,添加EOB
result += '1010'
break
#如果当前AC系数是0,统计连续0的个数,最大连续0的个数为16
if arr[k] == 0 and time < 15:
time += 1
else:
#BIT编码
#处理括号中第二个数
data = arr[k]#获得系数b
data2 = bin(np.abs(data))[2:]#系数b转换成二进制编码
data1 = len(data2)#计算系数b二进制码长
#如果系数b是负数,用反码表示
if data < 0:
#将系数b的二进制编码转换成反码
data2 = bin(np.abs(data) ^ (2 ** data1 - 1))[2:].rjust(data1, '0')
#如果系数b=0,不需要任何表示
if data == 0:
data1 = 0
data2 = ''
# 哈夫曼编码,序列化
#从AC系数编码表中找到游程为a、二进制码长为n对应的哈夫曼编码码字
#编码=AC系数的哈夫曼编码码字+AC系数二进制编码
result += ac_huffman_table[time * 16 + data1]
result += data2
time = 0#复位time=0
#判断是否要添加EOB
if int(arr[k]) != 0:
notnull_num -= 1
#编码结束后,判断哈夫曼编码长度是否是8的整数倍,
#如果不是,补足为8的整数倍,以便编码成16进制数据
if len(result) % 8 != 0:
result = result.ljust(len(result) + 8 - len(result) % 8, '0')
#将哈夫曼编码转换成为16进制编码
res_data = ''
for i in range(0, len(result), 8):
temp = int(result[i:i + 8], 2)
res_data += hex(temp)[2:].rjust(2, '0').upper()
if temp == 255:
res_data += '00'
#更新result的值为哈夫曼编码对应的16进制编码
result = res_data
#定义jpeg输出流
res = ''
#添加jpeg文件头
#SOI(文件头),共89个字节
res += 'FFD8'
#APP0图像识别信息
res += 'FFE000104A46494600010100000100010000'
#DQT定义量化表
res += 'FFDB004300'
#将64字节的量化表添加到jpeg输出流
for i in range(64):
res += hex(Qy[int(i / 8)][i % 8])[2:].rjust(2, '0')
#SOF0图像基本信息,13个字节
res += 'FFC0000B08'
#将图像的长度信息和宽度信息添加到jpeg输出流
res += hex(h)[2:].rjust(4, '0')
res += hex(w)[2:].rjust(4, '0')
#res+='01012200'
#采样系数好像都是1
res += '01011100'
#DHT定义huffman表,33个字节+183
res += 'FFC4001F00'
#将DC系数编码表添加到jpeg输出流
for i in standard_dc_nrcodes:
res += hex(i)[2:].rjust(2, '0')
for i in standard_dc_values:
res += hex(i)[2:].rjust(2, '0')
res += 'FFC400B510'
# 将AC系数编码表添加到jpeg输出流
for i in standard_ac_nrcodes:
res += hex(i)[2:].rjust(2, '0')
for i in standard_ac_values:
res += hex(i)[2:].rjust(2, '0')
#SOS扫描行开始,10个字节
res += 'FFDA0008010100003F00'
#压缩的图像数据(一个个扫描行),数据存放顺序是从左到右、从上到下
res += result
#EOI文件尾0
res += 'FFD9'
# print(res);
return res
def main():
# 原始图像路径,灰度图像
img_path = './apple1.bmp'
# 读取原始图像,cv2.imread()默认是用color模式读取的,保持原样读取要加上第二个参数-1,即CV_LOAD_IMAGE_GRAYSCALE
# 得到图像原数据流
img_data = cv2.imread(img_path, -1)
row1,col1,channel1=img_data.shape
# print(img_data.shape)
temp=row1//16
row=temp*16
temp=col1//16
col=temp*16
channel=channel1
new_img_data=np.zeros((row,col,channel),dtype=int)
for k in range(0,3):
for i in range(row):
for j in range(col):
new_img_data[i,j,k]=img_data[i,j,k]
# print(f"img_data:{img_data}");
# print(f"new_img_data:{new_img_data}");
# print(new_img_data.shape)
[row,col,channel]=new_img_data.shape
cv2.imwrite('./new_apple1.bmp',new_img_data)
new_img=cv2.imread('./new_apple1.bmp',-1)
plt.imshow(new_img)
YCbCr_img = cv2.cvtColor(new_img, cv2.COLOR_RGB2YCrCb)
Y_weight=YCbCr_img[:,:,0]
Cb_weight=YCbCr_img[:,:,1]
Cr_weight = YCbCr_img[:, :, 2]
Cb_img=np.zeros((row//2,col//2),dtype=int)
Cr_img = np.zeros((row // 2, col // 2), dtype=int)
i1=0
j1=0
for i in range(0,row,2):
for j in range(0,col,2):
Cb_img[i1,j1]=Cb_weight[i,j]
Cr_img[i1, j1] = Cr_weight[i, j]
# print(f"({i1},{j1})",end='')
j1+=1
i1+=1
j1=0
# print(f"Cb_img.shape:{Cb_img.shape}")
# print(f"Cr_img.shape:{Cr_img.shape}")
# cv2.imwrite('./jpeg_compress.jpg', img_data)
# img0 = cv2.imread('./jpeg_compress.jpg', -1)
# 得到压缩后图像数据
Y_compress = compress_Y(Y_weight, 50)
Cb_compress=compress_Cb_Cr(Cb_img,50)
Cr_compress = compress_Cb_Cr(Cr_img, 50)
print(f"Y_compress:{Y_compress}")
print(f"Cb_compress:{Cb_compress}")
print(f"Cr_compress:{Cr_compress}")
if __name__ == '__main__':
main()
这篇博文只实现了JPEG编码,并没有实现解码,如果要实现解码,可以参考博客:
JPEG原理详解(附python实现)_jpeg压缩python实现-CSDN博客
jpeg 彩色图像压缩(python实现)_图像压缩 色彩-CSDN博客
另外,如果要了解JPEG压缩原理,可以参考博客: