计算机图像处理实验二(C语言)

实验任务:Huffman编解码

已知:

  1. 原灰度图像Is:b8gray.bmp
    图片在实验一中已经给出。
    求:
  2. 代码
  3. 编码后的bpp值
  4. 原灰度图像的熵
    验证:
  5. 编码程序,输入为Is,输出为压缩文件C.dat
  6. 解码程序,输入为C.dat,输出为解码图像Id
  7. Is与Id是否相同
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAX_TREE_NODES 256

// Huffman树节点
typedef struct Node
{
    unsigned char symbol;
    int frequency;
    struct Node *left;
    struct Node *right;
} Node;

// 优先队列
typedef struct PriorityQueue
{
    int size;
    int capacity;
    Node **nodes;
} PriorityQueue;

// 创建Huffman树节点
Node *createNode(unsigned char symbol, int frequency)
{
    Node *node = (Node *)malloc(sizeof(Node));
    node->symbol = symbol;
    node->frequency = frequency;
    node->left = NULL;
    node->right = NULL;
    return node;
}

// 创建优先队列
PriorityQueue *createPriorityQueue(int capacity)
{
    PriorityQueue *queue = (PriorityQueue *)malloc(sizeof(PriorityQueue));
    queue->size = 0;
    queue->capacity = capacity;
    queue->nodes = (Node **)malloc(capacity * sizeof(Node *));
    return queue;
}

// 交换两个节点
void swapNodes(Node **a, Node **b)
{
    Node *temp = *a;
    *a = *b;
    *b = temp;
}

// 从优先队列中获取最小频率的节点
Node *extractMin(PriorityQueue *queue)
{
    int minIndex = 0;
    for (int i = 1; i < queue->size; i++)
    {
        if (queue->nodes[i]->frequency < queue->nodes[minIndex]->frequency)
        {
            minIndex = i;
        }
    }

    Node *minNode = queue->nodes[minIndex];
    queue->size--;
    swapNodes(&queue->nodes[minIndex], &queue->nodes[queue->size]);
    return minNode;
}

// 插入节点到优先队列
void insertNode(PriorityQueue *queue, Node *node)
{
    int i = queue->size;
    while (i > 0 && node->frequency < queue->nodes[i - 1]->frequency)
    {
        queue->nodes[i] = queue->nodes[i - 1];
        i--;
    }
    queue->nodes[i] = node;
    queue->size++;
}

// 构建Huffman树
Node *buildHuffmanTree(int *frequencies)
{
    PriorityQueue *queue = createPriorityQueue(MAX_TREE_NODES);

    for (int i = 0; i < MAX_TREE_NODES; i++)
    {
        if (frequencies[i] > 0)
        {
            Node *node = createNode(i, frequencies[i]);
            insertNode(queue, node);
        }
    }

    while (queue->size > 1)
    {
        Node *left = extractMin(queue);
        Node *right = extractMin(queue);

        Node *parent = createNode('$', left->frequency + right->frequency);
        parent->left = left;
        parent->right = right;

        insertNode(queue, parent);
    }

    Node *root = extractMin(queue);
    free(queue->nodes);
    free(queue);
    return root;
}

// 递归地构建Huffman编码
void buildHuffmanCodes(Node *node, unsigned long code, int length, unsigned long *codes, int *lengths)
{
    if (node->left == NULL && node->right == NULL)
    {
        codes[node->symbol] = code;
        lengths[node->symbol] = length;
    }
    else
    {
        buildHuffmanCodes(node->left, (code << 1) | 0, length + 1, codes, lengths);
        buildHuffmanCodes(node->right, (code << 1) | 1, length + 1, codes, lengths);
    }
}

// 将Huffman编码保存到文件
void saveHuffmanCodes(FILE *file, unsigned long *codes, int *lengths)
{
    for (int i = 0; i < MAX_TREE_NODES; i++)
    {
        if (lengths[i] > 0)
        {
            unsigned char symbol = (unsigned char)i;
            fwrite(&symbol, sizeof(unsigned char), 1, file);
            fwrite(&lengths[i], sizeof(int), 1, file);
            fwrite(&codes[i], sizeof(unsigned long), 1, file);
        }
    }
}
// 从文件中加载Huffman编码
void loadHuffmanCodes(FILE *file, unsigned long *codes, int *lengths)
{
    unsigned char symbol;
    int length;
    unsigned long code;
    while (fread(&symbol, sizeof(unsigned char), 1, file) == 1)
    {
        fread(&length, sizeof(int), 1, file);
        fread(&code, sizeof(unsigned long), 1, file);
        codes[symbol] = code;
        lengths[symbol] = length;
    }
}
// 获取文件大小
long getFileSize(FILE *file)
{
    fseek(file, 0, SEEK_END);
    long size = ftell(file);
    rewind(file);
    return size;
}

// 对图像进行Huffman编码
void encodeImage(FILE *inputFile, FILE *outputFile, unsigned long *codes, int *lengths, int *bpp)
{
    unsigned char buffer = 0;
    int bufferLength = 0;
    unsigned char pixel;
    long inputSize = getFileSize(inputFile);
    while (fread(&pixel, sizeof(unsigned char), 1, inputFile) == 1)
    {
        unsigned long code = codes[pixel];
        int length = lengths[pixel];

        for (int i = 0; i < length; i++)
        {
            buffer = (buffer << 1) | ((code >> (length - 1 - i)) & 1);
            bufferLength++;

            if (bufferLength == 8)
            {
                fwrite(&buffer, sizeof(unsigned char), 1, outputFile);
                buffer = 0;
                bufferLength = 0;
            }
        }
    }

    if (bufferLength > 0)
    {
        buffer = buffer << (8 - bufferLength);
        fwrite(&buffer, sizeof(unsigned char), 1, outputFile);
    }

    // 计算压缩后的bpp值
    long outputSize = getFileSize(outputFile);
    *bpp = (int)(outputSize * 8 / (float)inputSize);
}
// 对Huffman编码进行解码
void decodeImage(FILE *inputFile, FILE *outputFile, Node *root, long inputSize)
{
    Node *currentNode = root;
    unsigned char byte;
    unsigned char bit;
    long bytesRead = 0;
    while (fread(&byte, sizeof(unsigned char), 1, inputFile) == 1 && bytesRead < inputSize)
    {
        for (int i = 7; i >= 0; i--)
        {
            bit = (byte >> i) & 1;

            if (bit == 0)
            {
                currentNode = currentNode->left;
            }
            else
            {
                currentNode = currentNode->right;
            }

            if (currentNode->left == NULL && currentNode->right == NULL)
            {
                fwrite(&(currentNode->symbol), sizeof(unsigned char), 1, outputFile);
                currentNode = root;
            }

            bytesRead++;

            if (bytesRead >= inputSize)
            {
                break;
            }
        }
    }
}
double computeEntropy(int *frequencies, int totalPixels)
{
    double entropy = 0.0;

    for (int i = 0; i < MAX_TREE_NODES; i++)
    {
        if (frequencies[i] > 0)
        {
            double probability = (double)frequencies[i] / totalPixels;
            entropy -= probability * log2(probability);
        }
    }

    return entropy;
}
int main()
{
    // 读取图像文件和创建输出文件
    FILE *input = fopen("b8gray.bmp", "rb");
    FILE *output = fopen("C.dat", "wb");
    // 计算图像中每个像素的频率
    int frequencies[MAX_TREE_NODES] = {0};
    unsigned char pixel;
    int totalPixels = 0;
    while (fread(&pixel, sizeof(unsigned char), 1, input) == 1)
    {
        frequencies[pixel]++;
        totalPixels++;
    }
    rewind(input);
    // 计算灰度图像的熵
    double entropy = computeEntropy(frequencies, totalPixels);
    printf("原灰度图像的熵:%f\n", entropy);

    // 构建Huffman树
    Node *root = buildHuffmanTree(frequencies);

    // 构建Huffman编码
    unsigned long codes[MAX_TREE_NODES] = {0};
    int lengths[MAX_TREE_NODES] = {0};
    buildHuffmanCodes(root, 0, 0, codes, lengths);

    // 将Huffman编码保存到文件
    saveHuffmanCodes(output, codes, lengths);

    // 对图像进行编码
    int bpp;
    encodeImage(input, output, codes, lengths, &bpp);
    printf("压缩后的bpp值:%d\n", bpp);

    fclose(input);
    fclose(output);

    // 解码
    FILE *compressed = fopen("C.dat", "rb");
    FILE *decoded = fopen("Id.bmp", "wb");

    // 加载Huffman编码
    unsigned long loadedCodes[MAX_TREE_NODES] = {0};
    int loadedLengths[MAX_TREE_NODES] = {0};
    loadHuffmanCodes(compressed, loadedCodes, loadedLengths);

    // 重新构建Huffman树
    Node *decodedRoot = buildHuffmanTree(loadedLengths);

    // 解码图像
    long inputSize = getFileSize(compressed);
    decodeImage(compressed, decoded, decodedRoot, inputSize);

    fclose(compressed);
    fclose(decoded);

    return 0;
}

上述代码运行后,对b8gray.bmp文件进行编码再解码后,生成的Id.bmp大小与源文件不同,(可能代码有误,之后解决问题后再更改文章)

主要步骤:

  1. 定义了Huffman树节点结构体(Node)和优先队列结构体(PriorityQueue),用于构建Huffman树和管理节点。

  2. 创建Huffman树节点的函数(createNode)用于创建一个新的节点。

  3. 创建优先队列的函数(createPriorityQueue)用于创建一个空的优先队列。

  4. 交换节点的函数(swapNodes)用于交换两个节点的位置。

  5. 从优先队列中获取最小频率的节点的函数(extractMin)用于从优先队列中取出频率最小的节点。

  6. 插入节点到优先队列的函数(insertNode)用于将一个节点插入到优先队列中的适当位置。

  7. 构建Huffman树的函数(buildHuffmanTree)根据给定的字符频率数组构建Huffman树。

  8. 递归地构建Huffman编码的函数(buildHuffmanCodes)根据Huffman树节点,计算每个字符的Huffman编码和编码长度。

  9. 将Huffman编码保存到文件的函数(saveHuffmanCodes)将每个字符的编码和长度保存到输出文件中。

  10. 从文件中加载Huffman编码的函数(loadHuffmanCodes)从输入文件中加载每个字符的编码和长度。

  11. 获取文件大小的函数(getFileSize)用于计算给定文件的大小。

  12. 对图像进行Huffman编码的函数(encodeImage)将输入图像文件中的像素值根据Huffman编码转换为压缩后的二进制数据,并保存到输出文件中。同时计算压缩后的bpp值。

  13. 对Huffman编码进行解码的函数(decodeImage)将压缩后的二进制数据根据Huffman编码进行解码,并将解码后的像素值写入到输出文件中。

  14. 主函数中执行以下操作:

  • 打开输入图像文件和输出文件。
  • 计算图像中每个像素的频率。
  • 构建Huffman树。
  • 构建Huffman编码。
  • 将Huffman编码保存到文件。
  • 对图像进行编码并计算压缩后的bpp值。
  • 关闭输入和输出文件。
  • 打开压缩后的文件和解码后的输出文件。
  • 加载Huffman编码。
  • 重新构建Huffman树。
  • 解码图像。
  • 关闭压缩文件和解码文件。

解决遗留问题:上述代码的压缩和解压缩过程是基于像素值进行的,并使用Huffman编码来表示每个像素值。因此,如果输入图像(C.dat)是非像素图像,则无法正确执行解码过程。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值