使用霍夫曼编码的图像压缩一

霍夫曼编码是基本的压缩方法之一,已被证明在图像和视频压缩标准中很有用。在图像上应用霍夫曼编码技术时,源符号可以是图像的像素强度,也可以是强度映射函数的输出。

霍夫曼编码技术的第一步是将输入图像简化为有序直方图,其中某个像素强度值出现的概率为
prob_pixel = numix/totalnum
其中numix是具有特定强度值的像素的出现次数,totalnum是输入图像中的像素总数。
让我们拍一张 8 X 8 的图片

使用霍夫曼编码的图像压缩一_第1张图片

像素强度值是:
 

使用霍夫曼编码的图像压缩一_第2张图片


该图像包含 46 个不同的像素强度值,因此我们将有 46 个独特的霍夫曼码字。

显然,并非所有像素强度值都可能出现在图像中,因此不会有非零出现概率。

从这里开始,输入图像中的像素强度值将作为叶节点进行寻址。

现在,构建哈夫曼树有 2 个基本步骤:

  1. 建立霍夫曼树:
    1. 将概率最低的两个叶节点组合成一个新节点。
    2. 用新节点替换两个叶子节点,并根据新的概率值对节点进行排序。
    3. 继续步骤 (a) 和 (b),直到我们得到概率值为 1.0 的单个节点。我们将此节点称为
  2. 从根回溯,为每个中间节点分配“0”或“1”,直到我们到达叶节点

在这个例子中,我们将'0'分配给左边的子节点,'1'分配给右边的子节点。

现在,让我们看看实现:
第 1 步:
将图像读入二维数组image )
如果图像是.bmp格式。

int i, j;
char filename[] = "Input_Image.bmp";
int data = 0, offset, bpp = 0, width, height;
long bmpsize = 0, bmpdataoff = 0;
int** image;
int temp = 0;
   
// Reading the BMP File
FILE* image_file;
image_file = fopen(filename, "rb");
if (image_file == NULL)
{
    printf("Error Opening File!!");
    exit(1);
}
else 
{
  
    // Set file position of the 
    // stream to the beginning
    // Contains file signature 
    // or ID "BM"
    offset = 0;
      
    // Set offset to 2, which 
    // contains size of BMP File
    offset = 2;
      
    fseek(image_file, offset, SEEK_SET);
      
    // Getting size of BMP File
    fread(&bmpsize, 4, 1, image_file);
      
    // Getting offset where the
    // pixel array starts
    // Since the information 
    // is at offset 10 from 
    // the start, as given 
    // in BMP Header
    offset = 10; 
      
    fseek(image_file, offset, SEEK_SET);
      
    // Bitmap data offset
    fread(&bmpdataoff, 4, 1, image_file);
      
    // Getting height and width of the image
    // Width is stored at offset 18 and height
    // at offset 22, each of 4 bytes
    fseek(image_file, 18, SEEK_SET);
      
    fread(&width, 4, 1, image_file);
      
    fread(&height, 4, 1, image_file);
      
    // Number of bits per pixel
    fseek(image_file, 2, SEEK_CUR);
      
    fread(&bpp, 2, 1, image_file);
      
    // Setting offset to start of pixel data
    fseek(image_file, bmpdataoff, SEEK_SET);
      
    // Creating Image array
    image = (int**)malloc(height * sizeof(int*));
    for (i = 0; i < height; i++)
    {
        image[i] = (int*)malloc(width * sizeof(int));
    }
      
    // int image[height][width] 
    // can also be done
    // Number of bytes in the 
    // Image pixel array
    int numbytes = (bmpsize - bmpdataoff) / 3; 
      
    // Reading the BMP File 
    // into Image Array
    for (i = 0; i < height; i++) 
    {
        for (j = 0; j < width; j++)
        {
            fread(&temp, 3, 1, image_file);
              
            // the Image is a 
            // 24-bit BMP Image
            temp = temp & 0x0000FF; 
            image[i][j] = temp;
        }
    }
}

创建图像中存在的像素强度值的直方图

// Creating the Histogram

int hist[256];
  
for (i = 0; i < 256; i++)
    hist[i] = 0;
  
for (i = 0; i < height; i++)
    for (j = 0; j < width; j++)
        hist[image[i][j]] += 1;

找出具有非零出现概率的像素强度值的数量
,因为像素强度值的范围从 0 到 255,并且并非所有像素强度值都可能出现在图像中(从直方图和图像矩阵中可以明显看出),因此不会有非零的发生概率。此步骤的另一个目的是,具有非零概率值的像素强度值的数量将为我们提供图像中叶节点的数量。

// Finding number of 
// non-zero occurrences
int nodes = 0;
for (i = 0; i < 256; i++) 
{
    if (hist[i] != 0)
        nodes += 1;
}

计算霍夫曼代码字的最大长度正如YSAbu-MostafaRJMcEliece在他们的论文“霍夫曼代码中的最大代码字长度”
中所示,如果,那么在最小概率为 p 的源的任何有效前缀代码中,最长的代码字length is at most K & If , there is existing a source 其最小概率为 p, 并且具有最长字长度为 K 的 Huffman 代码。如果 ,存在这样一个源,其每个最优代码的最长字长度为 K .这里,是斐波那契数列。

Gallager [1] 注意到每棵哈夫曼树都是高效的,但实际上很容易看到更多
一般来说,每棵最优树都是有效的

斐波那契数列是:0、1、1、2、3、5、8、13、21、34、55、89、144、……

在我们的示例中,最低概率 (p) 为 0.015625

因此,

 1/p = 64
For K = 9,
F(K+2) = F(11) = 55
F(K+3) = F(12) = 89

所以,

1/F(K+3) < p < 1/F(K+2)
Hence optimal length of code is K=9
// Calculating max length 
// of Huffman code word
i = 0;
while ((1 / p) < fib(i))
    i++;
int maxcodelen = i - 3;
// Function for getting Fibonacci
// numbers defined outside main
int fib(int n)
{
    if (n <= 1)
        return n;
    return fib(n - 1) + fib(n - 2);
}

第 2 步
定义一个结构,它将包含像素强度值pix )、它们对应的概率freq )、指向左 ( *left ) 和右 ( *right )子节点的指针以及霍夫曼码字的字符串数组(代码)。

这些结构定义在main()内部,以便使用最大代码长度(maxcodelen)来声明结构pixfreq的代码数组字段

// Defining Structures pixfreq
struct pixfreq 
{
    int pix;
    float freq;
    struct pixfreq *left, *right;
    char code[maxcodelen];
};

第 3 步
定义另一个结构,它将包含像素强度值pix )、它们对应的概率freq ) 和一个附加字段,该字段将用于存储新生成节点的位置arrloc )。

// Defining Structures huffcode
struct huffcode
 {
    int pix, arrloc;
    float freq;
};

第 4 步
声明一个结构数组。数组的每个元素对应于霍夫曼树中的一个节点。

// Declaring structs
struct pixfreq* pix_freq;
struct huffcode* huffcodes;

你可能感兴趣的:(算法,算法)