• 使用Python对图像进行不同级别量化QP,使用RLE计算压缩比,并计算对应的PSNR


    写这篇博客源于 博友的提问
    在这里插入图片描述

    1.效果图

    原图 VS QP=2 VS QP=4 VS QP=8效果图如下:
    QP量化是指把原始图像按像素级别划分取值。如QP=2,则<128 取0,>128取128.
    QP=4,则<64取0,<128取64,<192取128,<256取192.
    QP=8,则<32取0,<64取32,<96取64,<128取96,<160取128,<192取160,<224取192,<256取224.
    在这里插入图片描述

    可以看出QP越大,压缩率越大,同时psnr越大,这表示跟原图相似性越好,越接近原图像。
    PSNR 最小值为 0,PSNR 越大,两张图像差异越小;PSNR 计算简单,物理意义清晰,但是这种基于 MSE 的评价指标并不能很好的按人眼的感受来衡量两张图像的相似度
    在这里插入图片描述
    原始灰度图像 VS 原始图像rle后再反解码回去效果图如下:
    在这里插入图片描述
    原始灰度图像 VS qp=2图像rle后再反解码回去效果图如下:
    在这里插入图片描述

    原始灰度图像 VS qp=4图像rle后再反解码回去效果图如下:
    在这里插入图片描述
    原始灰度图像 VS qp=8图像rle后再反解码回去效果图如下:
    在这里插入图片描述

    2. 原理

    • 量化(Quantization)旨在将图像像素点对应亮度的连续变化区间转换为单个特定值的过程,即将原始灰度图像的空间坐标幅度值离散化。量化等级越多,图像层次越丰富,灰度分辨率越高,图像的质量也越好;量化等级越少,图像层次欠丰富,灰度分辨率越低,会出现图像轮廓分层的现象,降低了图像的质量。
      QP量化是指把原始图像按像素级别划分取值。如QP=2,则<128 取0,>128取128.
      QP=4,则<64取0,<128取64,<192取128,<256取192.
      QP=8,则<32取0,<64取32,<96取64,<128取96,<160取128,<192取160,<224取192,<256取224.

    • 行程编码(RLE) 在图像压缩上,行程编码一般用于压缩二值化图像,因为它是基于重复的压缩算法,比如:
      二维图像降维后(压缩前):0 0 0 0 0 255 255 255 0 0 255
      行程编码压缩后:5 0 3 255 2 0 1 255
      (压缩格式为:数量+像素+数量+像素…)
      如果有大量的像素连续重复,那么压缩率会更高。

    • PSNR(峰值信噪比,Peak Signal-to-Noise Ratio),用于衡量两张图像之间差异,例如压缩图像与原始图像,评估压缩图像质量;复原图像与ground truth,评估复原算法性能等。
      在这里插入图片描述

      PSNR 最小值为 0,PSNR 越大,两张图像差异越小;
      PSNR 计算简单,物理意义清晰,但是这种基于 MSE 的评价指标并不能很好的按人眼的感受来衡量两张图像的相似度

    3. 源码

    # -*- coding: utf-8 -*-
    # By:Eastmount
    # 1. 对图像进行 QP=2,QP=4,QP=8的量化并展示
    # 2. 采用行程编码(RLE),计算压缩比;
    # 3. 进行解码(反量化),并计算不同量化级别的PSNR值。
    # python ask_img_qa_rle_psnr.py
    import cv2
    import math
    import matplotlib.pyplot as plt
    import numpy as np
    
    # 读取原始图像
    img = cv2.imread('ml.jpg')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    
    # 获取图像高度和宽度
    height = img.shape[0]
    width = img.shape[1]
    
    # 创建一幅图像
    new_img1 = np.zeros((height, width, 3), np.uint8)
    new_img2 = np.zeros((height, width, 3), np.uint8)
    new_img3 = np.zeros((height, width, 3), np.uint8)
    
    # 图像量化等级为2的量化处理
    for i in range(height):
        for j in range(width):
            for k in range(3):  # 对应BGR三分量
                if img[i, j][k] < 128:
                    gray = 0
                else:
                    gray = 128
                new_img1[i, j][k] = np.uint8(gray)
    
    # 图像量化等级为4的量化处理
    for i in range(height):
        for j in range(width):
            for k in range(3):  # 对应BGR三分量
                if img[i, j][k] < 64:
                    gray = 0
                elif img[i, j][k] < 128:
                    gray = 64
                elif img[i, j][k] < 192:
                    gray = 128
                else:
                    gray = 192
                new_img2[i, j][k] = np.uint8(gray)
    
    # 图像量化等级为8的量化处理
    for i in range(height):
        for j in range(width):
            for k in range(3):  # 对应BGR三分量
                if img[i, j][k] < 32:
                    gray = 0
                elif img[i, j][k] < 64:
                    gray = 32
                elif img[i, j][k] < 96:
                    gray = 64
                elif img[i, j][k] < 128:
                    gray = 96
                elif img[i, j][k] < 160:
                    gray = 128
                elif img[i, j][k] < 192:
                    gray = 160
                elif img[i, j][k] < 224:
                    gray = 192
                else:
                    gray = 224
                new_img3[i, j][k] = np.uint8(gray)
    
    # 用来正常显示中文标签
    plt.rcParams['font.sans-serif'] = ['SimHei']
    
    # 显示图像
    titles = ['(a) 原始图像', '(b) 量化-QP=2', '(c) 量化-QP=4', '(d) 量化-QP=8']
    images = [img, new_img1, new_img2, new_img3]
    for i in range(4):
        plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray'),
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    plt.show()
    
    
    # 2. RLE计算压缩比
    def rle(title, origin, img):
        grayorigin = cv2.cvtColor(origin, cv2.COLOR_RGB2GRAY)
        grayimg = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
        rows, cols = grayimg.shape
    
        img = grayimg.flatten()  # 把灰度化后的二维图像降维为一维列表
    
        data = []
        image3 = []
        count = 1
        # 行程压缩编码
        for i in range(len(img) - 1):
            if (count == 1):
                image3.append(img[i])
            if img[i] == img[i + 1]:
                count = count + 1
                if i == len(img) - 2:
                    image3.append(img[i])
                    data.append(count)
            else:
                data.append(count)
                count = 1
    
        if (img[len(img) - 1] != img[-1]):
            image3.append(img[len(img) - 1])
            data.append(1)
    
        # 压缩率
        ys_rate = len(image3) / len(img) * 100
        print(str(title) + ' 压缩率: %.2f%s' % (ys_rate, '%'))
    
        # 行程编码RLE解码
        rec_image = []
        for i in range(len(data)):
            for j in range(data[i]):
                rec_image.append(image3[i])
    
        rec_image = np.reshape(rec_image, (rows, cols))
        cv2.imshow('grayorigin VS ' + str(title) + 'res', np.hstack([grayorigin, rec_image]))  # 重新输出二值化图像
        cv2.waitKey(0)
        plt.subplot()
        plt.imshow(np.hstack([grayorigin, rec_image]), 'gray')
        plt.title('grayOrigin VS ' + str(title) + '')
        plt.xticks([]), plt.yticks([])
        plt.show()
    
        print(str(title) + ' psnr: ', psnr2(grayorigin, rec_image))
    
    
    # 3. 计算psnr
    # 计算灰度图的psnr
    def psnr1(img1, img2):
        # compute mse
        # mse = np.mean((img1-img2)**2)
        mse = np.mean((img1 / 1.0 - img2 / 1.0) ** 2)
        # compute psnr
        if mse < 1e-10:
            return 100
        psnr1 = 20 * math.log10(255 / math.sqrt(mse))
        return psnr1
    
    
    # 计算彩色图的psnr
    def psnr2(img1, img2):
        mse = np.mean((img1 / 255.0 - img2 / 255.0) ** 2)
        if mse < 1e-10:
            return 100
        psnr2 = 20 * math.log10(1 / math.sqrt(mse))
        return psnr2
    
    
    for i in range(0, 4):
        rle(titles[i], images[0], images[i])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157

    参考

  • 相关阅读:
    短视频矩阵系统源码====3年技术公司源头开发商交付
    C++基于Qt中QOpenGLWidget模块实现的画图板源码+可执行文件
    链表(二)——LinkedList(双向链表)的CURD操作的模拟实现
    uniapp快速入门系列(3)- CSS技巧与布局
    【leetcode】 盛最多水的容器
    长路漫漫、技术作伴
    HDFS将普通用户添加到超级用户组
    【Flink 实战系列】Flink pipeline.operator-chaining 参数使用和解析
    搞懂SpringBean生命周期与依赖注入:你还在为这些热门技术感到困惑吗?Comate插件来帮你解答!
    面试官:来说说vue3是怎么处理内置的v-for、v-model等指令?
  • 原文地址:https://blog.csdn.net/qq_40985985/article/details/127760506