JPEG编码输出流

这篇博文解决如下问题

问题:

自编实现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压缩原理,可以参考博客:

程序员看的JPEG图像压缩介绍(多图慎入)_jpeg压缩-CSDN博客 

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值