JPEG图像压缩详解和代码实现

这篇具有很好参考价值的文章主要介绍了JPEG图像压缩详解和代码实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、图像存储

为了有效的传输和存储图像,需要对图像数据进行压缩。依据图像的保真度,图像压缩可分为无损压缩和有损压缩。

1. 无损压缩

无损压缩的基本原理是相同的颜色信息只需保存一次。无损压缩保证解压以后的数据和原始数据完全一致,压缩时去掉或减少数据中的冗余,解压时再重新插到数据中,是一个可逆过程。无损压缩算法一般可以把普通文件的数据压缩到原来的1/2-1/4。

2. 有损压缩

有损压缩方式在解压后图像像素值会发生改变,解压以后的数据和原始数据不完全一致,是不可逆压缩方式。在保存图像时保留了较多的亮度信息,将冗余信息合并,合并的比例不同,压缩的比例也就不同。由于信息量减少了,所以压缩比可以很高,图像质量也会下降。

二、图像格式

常见有损的图像格式有:JPEG、WebP,常见无损的图像格式有:PNG、BMP、GIF。

通常以文件的后缀名来区分图片的格式,但有时并不准确。实际的图片格式可通过查看图片数据来确定(查看方式:Notepad++打开图片,选择“插件”->“插件管理”,安装“HEX-Editor”,安装后再次选择“插件”->“HEX-Editor”->“View in HEX”)。

以JPEG和PNG图像格式为例。JPEG格式以0xFF D8开头,以0xFF D9结尾。PNG格式以0x89 50 4E 47 0D 0A 1A 0A开头,其中50 4E 47是英文字符串“PNG”的ASCII码,以00 00 00 00 49 45 4E 44 AE 42 60 82结尾,标志着PNG数据流结束。

jpeg压缩,python,图像处理,Powered by 金山文档

三、JPEG压缩

上文的图例是图像文件实际保存的数据,也就是图像压缩后的数据。本文以JPEG格式为例讲解图像压缩的过程。JPEG的文件格式一般有两种文件扩展名:.jpg和.jpeg,这两种扩展名的实质是相同的,我们可以把.jpg的文件改名为.jpeg,而对文件本身不会有任何影响。严格来讲,JPEG的文件扩展名应该为.jpeg,由于DOS时代的8.3文件名命名原则,就使用了.jpg的扩展名。

下文以小狗图像为例,详述图片压缩具体过程,图像分辨率是320x264。首先看下图:

jpeg压缩,python,图像处理,Powered by 金山文档

通常我们看到的彩色图像是三通道或四通道图像。三通道图像是指有RGB三个通道,R:红色,G:绿色,B:蓝色。四通道图像是在三通道的基础上加了Alpha通道,Alpha通道用来衡量一个像素的透明度。当Alpha为0时,该像素完全透明;当Alpha为255时,该像素完全不透明。四通道图像只有PNG格式支持。

图中小狗是三通道图像,有320x264个像素点,每个像素点由三个值表示,如上图右侧小狗眼睛部分,黑色区域每个通道的像素值较小如(3,2,11),白点部分像素值较高如(114,116,117)。图中共84480个像素,每个像素用24位表示,若直接存储需要占用84480*24/8/1024=247.5KB,为了有效地传输和存储图像,有必要对图像做压缩。JPEG压缩步骤如下。

1. 色彩空间转换

JPEG采用YUV颜色空间,“Y”表示明亮度,也就是灰度值;“U”和“V”表示色度,用于描述图像色彩和饱和度。因为人眼对亮度比较敏感,而对于色度不那么敏感,可以在UV维度大量缩减信息,所以先将RGB的数据转换到YUV色彩空间。转换公式:

Y = 0.299R + 0.587G + 0.114B

U = 0.5R - 0.4187G - 0.0813G + 128

V = -0.1687R - 0.3313G + 0.5B + 128

python 实现

import cv2
import numpy as np
# opencv 读取的图片是BGR顺序
image = cv2.imread('data/dog.jpg')
h, w, c = image.shape
# 色彩空间转换 BGR -> YUV
image_yuv = np.zeros_like(image, dtype=np.uint8)
for line in range(h):
    for row in range(w):
        B = image[line, row, 0]
        G = image[line, row, 1]
        R = image[line, row, 2]
        Y = np.round(0.299*R + 0.587*G + 0.114*B)
        U = np.round(0.5*R - 0.4187*G - 0.0813*G + 128)
        V = np.round(-0.1687*R - 0.3313*G + 0.5*B + 128)
        image_yuv[line, row, :] = (Y, U, V)
# 保存图像
cv2.imwrite('Y.png', image_yuv[:,:, 0])
cv2.imwrite('U.png', image_yuv[:,:, 1])
cv2.imwrite('V.png', image_yuv[:,:, 2])     
cv2.imwrite('YUV.png', image_yuv) 

结果展示

jpeg压缩,python,图像处理,Powered by 金山文档

2. 降采样

由于人眼对色度不敏感,直接将U、V分量进行色度采样,JPEG压缩算法采用YUV 4:2:0的色度抽样方法。4:2:0表示对于每行扫描的像素,只有一种色度分量以2:1的抽样率存储,也就是说每隔一行/列取值,偶数行取U值,奇数行取V值,UV通道宽度和高度分别降低为原来的1/2。

jpeg压缩,python,图像处理,Powered by 金山文档

python 实现

# 色彩空间转换 BGR -> YUV 4:2:0
def RGB2YUV420(image):
    h, w, c = image.shape
    image_y = np.zeros((h, w), dtype=np.uint8)
    image_u = np.zeros(((h-1)//2+1, (w-1)//2+1), dtype=np.uint8)
    image_v = np.zeros(((h-1)//2+1, (w-1)//2+1), dtype=np.uint8)
    for line in range(h):
        for row in range(w):
            B = image[line, row, 0]
            G = image[line, row, 1]
            R = image[line, row, 2]
            Y = np.round(0.299*R + 0.587*G + 0.114*B)
            image_y[line, row] = Y
            if line % 2 == 0 and row % 2 == 0:
                U = np.round(0.5*R - 0.4187*G - 0.0813*G + 128)
                image_u[line//2, row//2] = U 
            if line % 2 == 1 or line == h-1:
                V = np.round(-0.1687*R - 0.3313*G + 0.5*B + 128)
                image_v[line//2, row//2] = V
    return image_y, image_u, image_v

结果展示

jpeg压缩,python,图像处理,Powered by 金山文档

3. 离散余弦变换(DCT)

人类视觉对高频信息不敏感,利用离散余弦变换可分析出图像中高低频信息含量,进而压缩数据。

JPEG中将图像分为8*8的像素块,对每个像素块利用离散余弦变换进行频域编码,生成一个新的8*8的数字矩阵。对于不能被8整除的图像大小,需对图像填充使其可被8整除,通常使用0填充。由于离散余弦变换需要定义域对称,所以先将矩阵中的数值左移128,使值域范围在[-128, 127]。

二维离散余弦变换公式为:

jpeg压缩,python,图像处理,Powered by 金山文档
jpeg压缩,python,图像处理,Powered by 金山文档

python 实现

import math
def alpha(u):
    if u==0:
        return 1/np.sqrt(8)
    else:
        return 1/2

def block_fill(block):
    block_size = 8
    dst = np.zeros((block_size, block_size), dtype=np.uint8)
    h, w = block.shape
    dst[:h, :w] = block   
      return dst

def DCT_block(img):
    block_size = 8
    img = block_fill(img)
    img_fp32 = img.astype(np.float32)
    img_fp32 -= 128
    img_dct = np.zeros((block_size, block_size), dtype=np.float32)
    for line in range(block_size):
        for row in range(block_size):
            n = 0
            for x in range(block_size):
                for y in range(block_size):
                    n += img_fp32[x,y]*math.cos(line*np.pi*(2*x+1)/16)*math.cos(row*np.pi*(2*y+1)/16)
            img_dct[line, row] = alpha(line)*alpha(row)*n
    return np.ceil(img_dct)
    
def DCT(image):
    block_size = 8
    h, w = image.shape
    dlist = []
    for i in range((h + block_size - 1) // block_size):
        for j in range((w + block_size - 1) // block_size):
            img_block = image[i*block_size:(i+1)*block_size, j*block_size:(j+1)*block_size]
            # 处理一个像素块
            img_dct = DCT_block(img_block)
            dlist.append(img_dct)
      return dlist

img_dct = DCT(image_y)

结果展示

jpeg压缩,python,图像处理,Powered by 金山文档

4. 量化

每个8*8的像素块经离散余弦变换后生成一个8*8的浮点数矩阵,量化的过程则是去除矩阵中的高频信息,保留低频信息。JPEG算法提供了两张标准化系数矩阵,分别处理亮度数据和色差数据,表示 50% 的图像质量。

jpeg压缩,python,图像处理,Powered by 金山文档

量化的过程:使用DCT变换后的浮点矩阵除以量化表中数值,然后取整。量化表是控制JPEG压缩比的关键,可以根据输出图片的质量来自定义量化表,通常自定义量化表与标准量化表呈比例关系,表中数字越大则质量越低,压缩率越高。

python 实现

def quantization(blocks, Q):
    img_quan = []
    for block in blocks:
        img_quan.append(np.round(np.divide(block, Q)))
    return img_quan
img_quan = quantization(img_dct, Qy)

结果展示

jpeg压缩,python,图像处理,Powered by 金山文档

5. ZIGZAG排序

排序规则如图:

jpeg压缩,python,图像处理,Powered by 金山文档

python 实现

def zigzag(blocks):
    block_list = []
    for block in blocks:
        zlist = []
        w, h = block.shape
        if w != h:
            return None
        max_sum = w + h - 2
        for _s in range(max_sum + 1):
            if _s % 2 == 0:
                for i in range(_s, -1, -1):
                    j = _s - i
                    if i >= w or j >= h:
                        continue
                    zlist.append(block[i,j])
            else:
                for j in range(_s, -1, -1):
                    i = _s - j
                    if i >= w or j >= h:
                        continue
                    zlist.append(block[i,j])
        block_list.append(zlist)
    return block_list
zglist = zigzag(img_quan)

结果展示

[39.0, 4.0, -4.0, 0.0, -0.0, 2.0, -2.0, -1.0, -1.0, -1.0, 0.0, -0.0, -0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.0, -0.0, 0.0, 0.0, -0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.0, -0.0, 0.0, -0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

6. 差分脉冲编码调制(DPCM)对直流系数(DC)编码

对像素矩阵做DCT变换,相当于将矩阵的能量压缩到第一个元素中,左上角第一个元素被称为直流(DC)系数,其余的元素被称为交流(AC)系数。JPEG将量化后的频域矩阵中的DC系数和AC系数分开编码。使用DPCM技术,对相邻图像块量化DC系数的差值进行编码;使用行程长度编码(RLE)对AC系数编码。需要注意的一点是,对AC系数的的RLE编码是在8x8的块内部进行的,而对DC系数的DPCM编码是在整个图像上若干个8x8的块之间进行的。

差值编码原理:样值与前一个(相邻)样值的差值,则这些差值大多数是很小的或为零,可以用短码来表示;而对于出现几率较差的差值,用长码表示,这样可以使总体码数下降;采用对相邻样值差值进行变字节长编码的方式称为差值编码,又称为差分脉码调制(DPCM)。

8x8的图像块经过DCT变换后,得到的直流系数特点:

  • 系数值较大;

  • 相邻图像块的系数值变换不大。

python 实现

def DPCM(zglist):
    res_dpcm = []
    for i in range(len(zglist)):
        if i == 0:
            res_dpcm.append(zglist[i][0])
            continue
        res_dpcm.append(zglist[i][0]-zglist[i-1][0])
    return res_dpcm
res_dpcm = DPCM(zglist)

结果展示

[50.0, -2.0, -13.0, -7.0, -3.0, 0.0, -1.0, 0.0, -1.0, -2.0, -0.0, -1.0, 0.0, -1.0, -0.0, -1.0, 0.0, -0.0, -0.0, -0.0, -0.0, -0.0, 0.0, -0.0, -0.0, ..., -0.0, 0.0, -0.0, 0.0, -0.0]

7. DC系数中间格式

JPEG中为了更进一步节约空间,不直接保存数据的具体数值,而是将数据按照位数分为16组,保存在表里面。这也就是所谓的变长整数编码VLI。编码VLI表如下:

jpeg压缩,python,图像处理,Powered by 金山文档

以第一个block和第二个block为例,DPCM结果是50,通过查找VLI编码表该值位于VLI表格的第6组,因此可以写成(6)(50)的形式,即为DC系数的中间格式。

8. 行程长度编码(RLC)对交流系数(AC)编码

具有相同颜色并且是连续的像素数目称为行程长度。RLC编码简单直观,编码/解码速度快。例如,字符串AAABCDDDDDDDDBBBBB 利用RLE原理可以压缩为3ABC8D5B。在JPEG编码中,使用的数据对是(两个非零AC系数之间连续0的个数,下一个非零AC系数的值)。注意,如果AC系数之间连续0的个数超过16,则用一个扩展字节(15,0)来表示16连续的0。

python 实现

def rlc(zglist):
    res_ac = []
    for i in range(len(zglist)):
        ac = []
        zg = zglist[i]
        zero_num = 0
        for k in range(1, len(zg)):
            if zg[k] != 0:
                ac.append((zero_num, zg[k]))
                zero_num = 0
            else:
                zero_num += 1
        if zero_num:
            ac.append((0, 0))
        res_ac.append(ac)
    return res_ac
res_ac = rlc(zglist)

结果展示

zigzag结果:[50.0, -2.0, -13.0, -7.0, -3.0, 0.0, -1.0, 0.0, -1.0, -2.0, -0.0, -1.0, 0.0, -1.0, -0.0, -1.0, 0.0, -0.0, -0.0, -0.0, -0.0, -0.0, 0.0, -0.0, -0.0, ..., -0.0, 0.0, -0.0, 0.0, -0.0]

RLC编码结果:[(0, -2.0), (0, -13.0), (0, -7.0), (0, -3.0), (1, -1.0), (1, -1.0), (0, -2.0), (1, -1.0), (1, -1.0), (1, -1.0), (0, 0)]

9. AC系数中间格式

RLC编码结果:[(0, -2.0), (0, -13.0), (0, -7.0), (0, -3.0), (1, -1.0), (1, -1.0), (0, -2.0), (1, -1.0), (1, -1.0), (1, -1.0), (0, 0)]

对每组数据第二个数进行VLI编码,(0, -2.0)第二个数是-2.0,查找VLI编码表是第2组,所以可将其写(0, 2), -2.0。同理,AC系数中间格式可写成以下形式:

(0, 2), -2.0, (0, 4), -13.0, (0, 3), -7.0, (0, 2), -3.0, (1, 1), -1.0, (1, 1), -1.0, (0, 2), -2.0, (1, 1), -1.0, (1, 1), -1.0, (1, 1), -1.0, (0, 0)

10. 熵编码

JPEG基本系统规定采用Huffman编码。Huffman编码时DC系数与AC系数分别采用不同的Huffman编码表,对于亮度和色度也采用不同的Huffman编码表。因此,需要4张Huffman编码表才能完成熵编码的工作。具体的Huffman编码采用查表的方式来高效地完成。

上文中8x8像素块的中间格式:

  • DC: (6)(50),数字6查DC亮度Huffman编码表是1110,数字50查VLI编码表是110010。

  • AC: (0, 2), -2.0, (0, 4), -13.0, (0, 3), -7.0, (0, 2), -3.0, (1, 1), -1.0, (1, 1), -1.0, (0, 2), -2.0, (1, 1), -1.0, (1, 1), -1.0, (1, 1), -1.0, (0, 0),(0,2)查AC亮度Huffman编码表是01,-2.0查VLI编码表是01。

因此,这个8x8的亮度像素块信息压缩后的数据流为1110110010,0101,10110010,100000,0100,11000,11000,0101,11000,11000,11000,1010。总共65比特,压缩比为(64*8-65)/(64*8)*100%=87.3%

以上是JPEG压缩的整个过程,最终将所有编码结果整合并按JPEG规范格式存储,即可得到jpg格式的图像文件。

jpeg压缩,python,图像处理,Powered by 金山文档

智驱力-科技驱动生产力文章来源地址https://www.toymoban.com/news/detail-782770.html

到了这里,关于JPEG图像压缩详解和代码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA实现JPEG-LS图像压缩,有损无损可配置,提供工程源码和技术支持

    JPEG-LS(简称JLS)是一种无损/有损的图像压缩算法,其无损模式的压缩率相当优异,优于 Lossless-JPEG、Lossless-JPEG2000、Lossless-JPEG-XR、FELICES 等。 本设计使用system verilog语言设计了一个JPEG-LS图像压缩加速器,输入数据为8位的灰度图,输出数据为JPEG-LS图像压缩后的16位数据,集成

    2024年02月03日
    浏览(35)
  • Baumer工业相机堡盟工业相机如何通过BGAPI SDK使用JPEG图像压缩功能(C++)

    Baumer工业相机堡盟相机是一种高性能、高质量的工业相机,可用于各种应用场景,如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能,可以实时传输高分辨率图像。此外,该相机还具有快速数据传输、低功耗、易于集成以及高度可扩

    2024年02月08日
    浏览(31)
  • C++实现JPEG格式图片解析(附代码)

    在网上看了好多解析JPEG图片的文章,多多少少都有问题,下面是我参考过的文章链接: JPEG格式中信息是以段(数据结构)来存储的。 段的格式如下 名称 字节数 数据 说明 段标识 1 FF 每个新段的开始标识 段类型 1 类型编码(称作“标记码”) 段长度 2 包括段内容和段长度

    2024年02月10日
    浏览(31)
  • 【视频编解码】M-JPEG压缩、H.264压缩 对比

    参考这篇文章:https://blog.csdn.net/qq_41248872/article/details/83590337 写的比较好,这里就不赘述了。 我们在视频传输的时候,需要压缩,常见的压缩包括: jpeg 压缩 h264 压缩 当然使用最多的还是 264, 毕竟他的压缩比高嘛。对于一些实时的场景比较有优势,但是这玩意还是得分不同的

    2024年02月22日
    浏览(40)
  • 用Python编写GUI程序实现WebP文件批量转换为JPEG格式

    在Python编程中,经常会遇到需要处理图片格式的情况。最近,我遇到了一个有趣的问题:如何通过编写一个GUI程序来实现将WebP格式的图片批量转换为JPEG格式?在这篇博客中,我将分享我使用Python、wxPython模块和Pillow库实现这一功能的经验。 C:pythoncodenewwebp2jpeg.py 准备工作

    2024年04月11日
    浏览(26)
  • FPGA实现jpeg图片解码RGB 纯verilog代码编写 提供基于zynq得工程源码和技术支持

    jpg是一种压缩的图片格式,之所以压缩是为了减小图片所占空间,jpg压缩原理这里不罗嗦,可以自行百度或者b站,大佬讲的比我好,jpg解压缩就是逆向过程,用opencv啥的解压缩就是一句话的事儿,但对于fpga硬件来说就是大型工程了。 本设计使用zynq7100位平台,将jpg图片的c语

    2024年02月07日
    浏览(45)
  • 【K210+ESP8266图传上位机开发】TCP server + JPEG图像解析上位机开发

    本文章主要记录基于 【K210-ESP8266】 图传和显示的过程,上位机开发过程,系统架构和下位机开发请参考文章: 【K210-ESP8266】开发板上传图像数据到服务器并实时显示 💖 作者简介:大家好,我是喜欢记录零碎知识点的小菜鸟。😎 📝 个人主页:欢迎访问我的 Ethernet_Comm 博客

    2024年02月09日
    浏览(52)
  • 已解决java.lang.NoClassDefFoundError: com/sun/image/codec/jpeg/ImageFormatException图像格式异常的正确解决方法,亲测有效!!

    已解决java.lang.NoClassDefFoundError: com/sun/image/codec/jpeg/ImageFormatException图像格式异常的正确解决方法,亲测有效!!! 目录 问题分析 报错原因 解决思路 解决方法 确认类路径设置 替换为新的API 添加缺失的Jar文件 确保编译和运行环境一致 总结  博主v:XiaoMing_Java 在Java开发中,

    2024年04月26日
    浏览(31)
  • 数字图像处理常用算法的原理和代码实现详解

      本专栏详细地分析了常用图像处理算法的数学原理、实现步骤。配有matlab或C++实现代码,并对代码进行了详细的注释。最后,对算法的效果进行了测试。相信通过这个专栏,你可以对这些算法的原理及实现有深入的理解!   如有疑问,欢迎在评论区留言讨论! Canny边缘

    2024年01月16日
    浏览(35)
  • 图片:前端展示图像(img 、picture、svg、canvas )及常用图片格式(PNG、JPG、JPEG、WebP、GIF、SVG、AVIF等)

    一、浏览器网页展示图片方法 1.1、HTML img 标签 img 标签常用属性 序号 属性 描述 1 src 用于指定图片的 URL 或路径。 2 alt 用于当图片无法展示时显示的替代文本,seo优化时要注意添加这个属性。 3 width/height 用于指定图片展示的宽度和高度。如果只指定其中一个值,那么另一个

    2024年02月11日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包