范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

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压缩
  上文的图例是图像文件实际保存的数据,也就是图像压缩后的数据。本文以JPEG格式为例讲解图像压缩的过程。JPEG的文件格式一般有两种文件扩展名:.jpg和.jpeg,这两种扩展名的实质是相同的,我们可以把.jpg的文件改名为.jpeg,而对文件本身不会有任何影响。严格来讲,JPEG的文件扩展名应该为.jpeg,由于DOS时代的8.3文件名命名原则,就使用了.jpg的扩展名。
  下文以小狗图像为例,详述图片压缩具体过程,图像分辨率是320x264。首先看下图:
  通常我们看到的彩色图像是三通道或四通道图像。三通道图像是指有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)
  结果展示
  2. 降采样
  由于人眼对色度不敏感,直接将U、V分量进行色度采样,JPEG压缩算法采用YUV 4:2:0的色度抽样方法。4:2:0表示对于每行扫描的像素,只有一种色度分量以2:1的抽样率存储,也就是说每隔一行/列取值,偶数行取U值,奇数行取V值,UV通道宽度和高度分别降低为原来的1/2。
  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
  结果展示
  3. 离散余弦变换(DCT)
  人类视觉对高频信息不敏感,利用离散余弦变换可分析出图像中高低频信息含量,进而压缩数据。
  JPEG中将图像分为8*8的像素块,对每个像素块利用离散余弦变换进行频域编码,生成一个新的8*8的数字矩阵。对于不能被8整除的图像大小,需对图像填充使其可被8整除,通常使用0填充。由于离散余弦变换需要定义域对称,所以先将矩阵中的数值左移128,使值域范围在[-128, 127]。
  二维离散余弦变换公式为:
  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)
  结果展示
  4. 量化
  每个8*8的像素块经离散余弦变换后生成一个8*8的浮点数矩阵,量化的过程则是去除矩阵中的高频信息,保留低频信息。JPEG算法提供了两张标准化系数矩阵,分别处理亮度数据和色差数据,表示 50% 的图像质量。
  量化的过程:使用DCT变换后的浮点矩阵除以量化表中数值,然后取整。量化表是控制JPEG压缩比的关键,可以根据输出图片的质量来自定义量化表,通常自定义量化表与标准量化表呈比例关系,表中数字越大则质量越低,压缩率越高。
  python 实现 def quantization(blocks, Q):     img_quan = []     for block in blocks:         img_quan.append(np.round(np.pide(block, Q)))     return img_quan img_quan = quantization(img_dct, Qy)
  结果展示
  5. ZIGZAG排序
  排序规则如图:
  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表如下:
  以第一个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格式的图像文件。
  智驱力-科技驱动生产力

江西魅丝蔻化妆用品有限公司董事长江期胜激情创新真诚实干来源经济日报江西魅丝蔻化妆用品有限公司直播间外,公司董事长江期胜像往常一样,关注着电脑屏幕上的流量数据。每天近20万支化妆刷销售至65个国家和地区。我们要用专业与专注的力量,将小小香港老戏骨林俊贤中国人不该过圣诞节这是国耻!引发网友争论这是香港老戏骨资深演员林俊贤在个人社交账号上发布的一条最新帖子。帖子内容本身非常简单,一共只有7个字勿再过国耻日了!同时,他还附上了一张著名的巴西基督山雕像的照片耶稣的徒孙们是八国睿心医疗CEO郑凌霄释放创新力量,满足企业高速高质增长连线创始人本期访谈人物睿心医疗创始人CEO郑凌霄如果说遇到什么困难,那遇到的最大困难在于我自身的成长速度,这是一个炼心的过程,我要拼命提升自己,需要足够智慧足够努力足够坚韧,才能满足睿心持续阿曼议员出价百万求购,只为得到梅西决赛领奖所穿的金边黑袍在梅西世界杯决赛站在领奖台上的时候,卡塔尔国王塔米姆在全世界的瞩目之下为梅西披上了中东传统服饰bisht。而作为国际足联的主席因凡蒂诺也没有对此举动做出阻止,所以并非突发情况应该是重罚!连续两人挑战篮协,超级大罚单捍卫篮协底线,力挺裁判!昨日,中国篮协开出两个大罚单,一个是齐麟顶撞裁判,险酿冲突,被停牌2场,罚款15万。一个是马尚布鲁克斯,佩戴竞品产品,被罚款50万。中国篮协用处罚捍卫篮协底线。中国篮协对于球员处罚霍华德又受伤!再见NBA!别了CBA什么?总经理正在热身?不好意思,一般小场面还轮不到我们朱总出面。T1联赛的圣诞大战,云豹队与猎鹰队展开较量,苏翊杰以球员身份回归,并且首发登场,全场11投6中(三分3中1)独揽拿到周中战曼城,利兹联主帅打趣我批准哈兰德在比赛中腿筋轻微受伤直播吧12月24日讯北京时间12月29日凌晨4点,英超第17轮,利兹联主场对阵曼城。赛前,利兹联主帅杰克马什透露他和曼城前锋哈兰德互相发了短信。杰克马什说在赛程的具体时间公布后,哈周定洋昨晚真的太惊喜太棒了,如果没有球迷足球什么都不是直播吧12月24日讯中超第32轮,成都凤凰山体育公园专业足球场迎来中超首秀,最终成都蓉城10绝杀武汉三镇,本场比赛现场约3万球迷现场观战,赛后成都蓉城球员周定洋在个人社媒发文感谢球广东大胜北控!阿联徐杰单节击溃,凯皇神准,赵睿回暖,大汉太香北京时间12月24号广东和北控的比赛,这场比赛广东首节就领先了24分,此后他们将比分越拉越开,最终10990大胜北控。而今天广东首节之所以打得碾压北控,防守端是全队的功劳,不过进攻神仙打架!又一15岁天才少女击败瓦利耶娃夺冠,观赏度远超国际赛北京时间12月24日,花样滑冰全俄锦标赛落下帷幕,最终年仅15岁的七妹索菲亚阿卡蒂耶娃力压卡米拉瓦利耶娃拿下冠军。短节目表现完美的索菲亚阿卡蒂耶娃在自由滑发挥稳定拿下164。15分哈利伯顿单场10三分为队史新高,TJ沃伦和乔治并列第二今天NBA常规赛步行者客场迎战热火的比赛已经结束。全场战罢,步行者以111108战胜热火。本场比赛,步行者球员泰雷斯哈利伯顿出战37分钟,20投14中,其中三分球16投10中,罚球
剖宫产时结扎有危害吗?剖宫产时结扎有危害吗?剖宫产时结扎一般不会对女性产生危害。如果出现盆狭窄头盆不称或胎儿宫内缺氧等情况时需要进行剖宫产,并且产妇以后不想再生育,可以在剖宫产手术的同时做输卵管结扎术。火箭输鹈鹕,奥迪沃尔将重新评估,全员斥防守,沃尔自责,怎么看?今天火箭惨败给了鹈鹕,而赛后塞拉斯提到了沃尔和奥迪,他表示球队会重新评估他们两个出战背靠背的全部比赛。毕竟对于他们两个来说,遭遇过大伤之后,全队都需要注意他们的隐患。如果他们两个伤如果让勇士队的格林来CBA打球,是什么水平?首先我们来看看他是一个什么样的球员,再来说可能性吧。德雷蒙德格林(DraymondGreen),1990年3月4日出生于美国密歇根州塞基诺(Saginaw,Michigan),美国我想知道CBA篮球各个篮球队靠什么挣钱?打赢了或者总冠军都是谁给球队钱?前几年的CBA球队大多都是处于亏损状态,但是最近几年随着CBA的品牌价值提升,CBA大多数球队都是扭亏为盈。比如吉林队这样小本经营的球队,也能在一个赛季下来有剩余,我来说说CBA俱年底找工作太难了,怎么办呀?网购是把中间商都打掉然后平台自己来做房东和中间商,造成大量的失业,年轻人大多都在送餐送快递,普通人很难再创业。财富更加集中到少数人手里,而网购的商家都在拼倾销,都没有利润,便宜买的现在我创业负债累累,有没有和我一样因创业负债的?78年的,今年44了,打小家里就穷。因家里兄弟多,我1993年就下学打工了,2005年来到苏州,先是在一家广告公司上班,2008年自己出来和两个兄弟一起创业,2013年被那两个兄弟中山佳能工厂离职后想再进去该怎么办?离职后,又想进去,那肯定是发现原来佳能这份工作还不错。我曾经也遇到过同样的问题,在还没有毕业的时候,在一家德国外企实习,实习满三个月后,由于听信学校的虚假宣传,就不屑实习的这家公司孕妇乳房鼓起怎么办?孕妇乳房鼓起怎么办孕妇乳房鼓起,可能是指乳房肿胀,乳头勃起,通常情况下是正常生理现象。可选择有支撑力面料柔软的棉质内衣,避免穿着有钢圈或偏小的文胸,以防压迫乳腺组织。极少可能与乳腺什么奶粉比较接近母乳?有宝妈知道吗?什么奶粉都比不上母乳我们家孩子喝的能立多奶粉就不错,除了基础营养元素,还富含结构脂OPODHA益生元益生菌乳铁蛋白叶黄素核苷酸等各种营养元素,高度拟合宝宝理想食物营养元素,为宝宝成如果新生婴儿一开始就用尿不湿,一月大概多少钱?我有两个宝宝,都是从出生就用纸尿裤。说真的,现在可能还有地方不用纸尿裤。但是我个人认为用尿布真的不省钱,而且会让宝妈身心俱疲。下面我就介绍一个月用纸尿裤大概多少钱。刚开始,新生儿,如何看待腾讯的手游剑灵?谢谢邀请。大家好,我是X博士。中国网游史上最受期待的网游是谁?在X博士看来,不是魔兽也不是DNF,而是油腻的师姐剑灵。这款游戏当年测试时一枚激活码炒到了三千块,公测后创下的记录更是