├── Makefile ├── README.md ├── compile.sh ├── img ├── architecture.png ├── big_test.jpg ├── big_test_out.png ├── compare.png ├── make_output.png ├── run_output.png └── test2.png ├── include ├── encodeFile.h ├── hfmCodeBitSet.h ├── huffmanCompression.h ├── image.h ├── imageIOException.h └── imageIOHelper.h └── src ├── encodeFile.cpp ├── hfmCodeBitSet.cpp ├── huffmanCompression.cpp ├── image.cpp ├── imageIOException.cpp ├── imageIOHelper.cpp └── main.cpp /Makefile: -------------------------------------------------------------------------------- 1 | G++ = g++ -std=c++11 2 | 3 | bin/main: build/huffmanCompression.o build/main.o \ 4 | build/imageIOHelper.o build/imageIOException.o \ 5 | build/encodeFile.o build/image.o build/hfmCodeBitSet.o 6 | $(G++) -o bin/main build/* -g 7 | 8 | build/main.o: src/main.cpp 9 | $(G++) -c -o build/main.o src/main.cpp -I ./include 10 | 11 | build/imageIOHelper.o: src/imageIOHelper.cpp include/imageIOHelper.h 12 | $(G++) -c -o build/imageIOHelper.o src/imageIOHelper.cpp -g -I ./include 13 | 14 | build/imageIOException.o: src/imageIOException.cpp include/imageIOException.h 15 | $(G++) -c -o build/imageIOException.o src/imageIOException.cpp -g -I ./include 16 | 17 | build/encodeFile.o: src/encodeFile.cpp include/encodeFile.h 18 | $(G++) -c -o build/encodeFile.o src/encodeFile.cpp -g -I ./include 19 | 20 | build/image.o: src/image.cpp include/image.h 21 | $(G++) -c -o build/image.o src/image.cpp -g -I ./include 22 | 23 | build/huffmanCompression.o: src/huffmanCompression.cpp include/huffmanCompression.h 24 | $(G++) -c -o build/huffmanCompression.o src/huffmanCompression.cpp -g -I ./include 25 | 26 | build/hfmCodeBitSet.o: src/hfmCodeBitSet.cpp include/hfmCodeBitSet.h 27 | $(G++) -c -o build/hfmCodeBitSet.o src/hfmCodeBitSet.cpp -g -I ./include 28 | 29 | clean: 30 | rm build/* bin/* 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HuffmanImageCompression 2 | 3 | ## 项目说明 4 | 5 | ### 项目github 6 | 7 | [HuffmanImageCompression](https://github.com/xujj25/HuffmanImageCompression) 8 | 9 | ### 项目语言 10 | 11 | C++(std=C++11) 12 | 13 | ### 编写与测试操作系统平台 14 | 15 | ubuntu 16.04 16 | 17 | ### 文件要求 18 | 19 | - 目前项目仅可以对BMP图像文件进行压缩。 20 | - 压缩后的文件的扩展名为`.hfmc` ,压缩后文件结构为: 21 | - 原图像文件头(54字节) 22 | - 编码后图像数据总位数dataBitCount(4字节,即一个32为无符号整数) 23 | - 原图像文件中不同的数据单元的数目weightMapValCount(4字节,即一个32为无符号整数) 24 | - 权重映射表(表中每一项包含1个字节的原数据和4个字节即32位无符号整数表示的权重值,一共5个字节,表的总大小为weightMapValCount * 5) 25 | - 编码数据,实际的编码数据位数为dataBitCount,最后一个字节的数据如果是不满8位,余下的位用0作为padding 26 | 27 | ### 算法流程 28 | 29 | - 压缩 30 | - 读取图像文件,每个像素包含RGB三个色彩通道,每个通道占1个字节,这是编码的单元。 31 | - 对读取到的每个像素的色彩通道数据进行权重统计。 32 | - 根据权重统计构建Huffman编码树。 33 | - 从Huffman编码树的根节点开始向根节点进行DFS,产生编码表。 34 | - 使用编码表对原来的每个通道的色彩通道数据进行编码。 35 | - 将编码输出到自定义的编码文件中。 36 | - 解压缩 37 | - 读取自定义的编码文件,根据对编码文件结构的定义将各个部分分割出来。 38 | - 构造权重映射表,根据权重映射表重建Huffman编码树。 39 | - 读取实际编码数据,根据每个位的数据所指定的方向,从根节点开始向下游历编码树,每次到达叶子节点的时候输出叶子节点的对应数据,再重新回到根节点开始下一次游历,直到读取完编码数据。 40 | - 把得到的解码数据重新构建成图像格式,写入到图像文件中。 41 | 42 | ### 项目结构 43 | 44 | ![architecture](img/architecture.png) 45 | 46 | 在include目录包含了项目所需的各个类的定义,从上到下分别为: 47 | 48 | - 编码文件辅助类(用于储存编码文件的相关信息) 49 | - Huffman编码数据单元辅助类(考虑到数据量大的时候,使用字符串来保存码字会由较大的内存开销,所以创建了这个类,采用了与最后存储到文件中一样的位数据集保存形式来保存编码数据单元,也就是在Huffman编码树上的路径) 50 | - Huffman编码操作类(算法核心) 51 | - 图像文件辅助类(用于储存图片文件的相关信息) 52 | - 图像IO异常类(对C++的运行时错误类的扩展,用于报出实际文件IO中详细的错误) 53 | - 图像IO辅助类(用于进行图像和压缩后文件的读写) 54 | 55 | ### 关键代码 56 | 57 | - 压缩: 58 | 59 | ```c++ 60 | void HuffmanCompression::getEncodedData(const unsigned char *rawDataPtr, uint32_t rawDataSize, unordered_map &dstWeightMap, unsigned char *&outputDataPtr, uint32_t &outputDataBitSize) { 61 | calcWeight(rawDataPtr, rawDataSize, dstWeightMap); // 计算出权值映射表 62 | generateEncodedNodeQueue(dstWeightMap); // 根据权值映射表构建编码树节点队列(使用priority_queue这样一个以堆实现的“队列”,可以保证每次进出队操作后队列的节点保持权值从小到大排列) 63 | buildTree(); // 建立编码树 64 | unordered_map resCodeMap; 65 | getCodeMap(resCodeMap); // 根据编码树产生权值表 66 | outputDataBitSize = calcEncodedOutputSize(dstWeightMap, resCodeMap); // 计算编码数据位数 67 | generateEncodedOutput(rawDataPtr, rawDataSize, resCodeMap, outputDataPtr, outputDataBitSize); // 对原数据进行编码 68 | } 69 | ``` 70 | 71 | - 解压: 72 | 73 | ```c++ 74 | void HuffmanCompression::getDecodedData(const unsigned char *rawDataPtr, uint32_t rawDataBitSize, const vector> &srcWeightMapArr, unsigned char *&outputDataPtr, uint32_t &outputDataSize) { 75 | generateDecodedNodeQueue(srcWeightMapArr); // 根据读取文件之后构建的权值映射表构建节点队列 76 | buildTree(); // 建立编码树 77 | outputDataSize = calcDecodedOutputSize(srcWeightMapArr); // 计算输出数据大小 78 | generateDecodedOutput(rawDataPtr, rawDataBitSize, outputDataPtr, outputDataSize); // 对编码数据进行解码 79 | } 80 | ``` 81 | - 建立编码树: 82 | ```c++ 83 | // 因为节点队列能够根据进出队列的节点的权值进行顺序调整,所以每次只需要从队首取出2个节点构造一棵树,将树根再次加进节点队列,直到队列只剩下一个节点,这个节点就是编码树的根节点 84 | void HuffmanCompression::buildTree() { 85 | while (nodeQueue.size() > 1) { 86 | TreeNode* rightNode = nodeQueue.top(); 87 | nodeQueue.pop(); 88 | TreeNode* leftNode = nodeQueue.top(); 89 | nodeQueue.pop(); 90 | auto parentNode = new TreeNode(0, rightNode -> weight + leftNode -> weight); 91 | parentNode -> left = leftNode; 92 | parentNode -> right = rightNode; 93 | nodeQueue.push(parentNode); 94 | } 95 | treeRoot = nodeQueue.top(); 96 | } 97 | ``` 98 | - 编码表的生成: 99 | ```c++ 100 | // 深度优先搜索,过程中使用path来记录路径,到达每个叶子节点的path就是叶子节点对应数据的编码 101 | void HuffmanCompression::dfs(TreeNode* node, hfmCodeBitSet& path, 102 | unordered_map &resCodeMap) { 103 | if (node -> left == nullptr && node -> right == nullptr) { 104 | resCodeMap[node -> val] = path; 105 | return; 106 | } 107 | path.append(0); 108 | dfs(node -> left, path, resCodeMap); 109 | path.pop_back(); 110 | path.append(1); 111 | dfs(node -> right, path, resCodeMap); 112 | path.pop_back(); 113 | } 114 | 115 | // 生成编码表:调用dfs来生成编码表 116 | void HuffmanCompression::getCodeMap(unordered_map &resCodeMap) { 117 | if (treeRoot == nullptr) 118 | return; 119 | hfmCodeBitSet path; 120 | dfs(treeRoot, path, resCodeMap); 121 | } 122 | ``` 123 | 124 | ## 运行方式: 125 | 126 | 终端输入: 127 | 128 | ```bash 129 | bash compile.sh 130 | ``` 131 | 132 | 即可编译 133 | 134 | 输入 135 | ``` 136 | ./bin/main 137 | ``` 138 | 139 | 即可运行 140 | 141 | 根据提示可以完成图像压缩和解压 142 | 143 | ### 示例 144 | 145 | 146 | ![make](img/make_output.png) 147 | 148 | 149 | ![run](img/run_output.png) 150 | 151 | ## 测试结果 152 | 153 | ### 原始图像和压缩后恢复图像的视觉对比 154 | 155 | ![compare](img/compare.png) 156 | - 可以看到,原始图像(test.bmp)和压缩后恢复图像(out.bmp)的视觉对比上没有区别。 157 | 158 | ### 评价和对比压缩率和 SNR 失真度量 159 | 160 | - 压缩率对比 161 | 162 | - 对色彩较为丰富的图像,权值映射表较大,压缩效果较差。测试图片: 163 | 164 | ![big_test](img/big_test.jpg) 165 | 166 | 输出: 167 | 168 | ![big_test_out](img/big_test_out.png) 169 | 170 | 可以看到压缩前后文件大小比值为93.81% 。 171 | 172 | - 对色彩较为简单的图像,权值映射表较小,压缩效果较好。测试图片: 173 | 174 | ![test2](img/test2.png) 175 | 176 | 输出: 177 | 178 | ![run_output](img/run_output.png) 179 | 180 | 可以看到压缩前后文件大小比值为84.55% 。 181 | 182 | - 总的来说压缩前后文件大小比值范围在80~95% 。 183 | 184 | - 信噪比和失真度量 185 | 186 | - 本项目采用无损Huffman编码压缩图像,可以保证压缩前后图像完全一致,图像不会失真。 187 | 188 | 189 | ## 总结 190 | 191 | 本项目没有选用matlab或者python这些编码效率较高的语言来完成,而是选用了C++,是想实际体会下如何写出一个真正好用、效率较高的压缩程序。确实过程中还有很多很多基本的压缩算法之外的问题需要思考,包括文件IO、结果保存、时间复杂度和内存占用量,还有很重要的异常处理部分。并且由于程序中涉及动态内存使用的地方很多,稍有不慎就会造成内存问题,包括很难察觉得到的内存泄漏问题,所以编写过程中借助了Valgrind这一内存分析工具对整个程序的内存使用情况进行了排查,消除了绝大部分可能存在的内存使用问题。不过在项目完成之后回顾了有关C++11的一些知识,发现其实要规避内存问题的话,可以选用智能指针,这需要在后面的学习中进一步进行实践了。 -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | mkdir bin build 2 | make 3 | 4 | -------------------------------------------------------------------------------- /img/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xujj25/HuffmanImageCompression/f4f8dc73f0c641cb281a8b461a647d98f2dea354/img/architecture.png -------------------------------------------------------------------------------- /img/big_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xujj25/HuffmanImageCompression/f4f8dc73f0c641cb281a8b461a647d98f2dea354/img/big_test.jpg -------------------------------------------------------------------------------- /img/big_test_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xujj25/HuffmanImageCompression/f4f8dc73f0c641cb281a8b461a647d98f2dea354/img/big_test_out.png -------------------------------------------------------------------------------- /img/compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xujj25/HuffmanImageCompression/f4f8dc73f0c641cb281a8b461a647d98f2dea354/img/compare.png -------------------------------------------------------------------------------- /img/make_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xujj25/HuffmanImageCompression/f4f8dc73f0c641cb281a8b461a647d98f2dea354/img/make_output.png -------------------------------------------------------------------------------- /img/run_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xujj25/HuffmanImageCompression/f4f8dc73f0c641cb281a8b461a647d98f2dea354/img/run_output.png -------------------------------------------------------------------------------- /img/test2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xujj25/HuffmanImageCompression/f4f8dc73f0c641cb281a8b461a647d98f2dea354/img/test2.png -------------------------------------------------------------------------------- /include/encodeFile.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/31/17. 3 | // 4 | 5 | #ifndef HUFFMANIMAGECOMPRESSION_ENCODEFILE_H 6 | #define HUFFMANIMAGECOMPRESSION_ENCODEFILE_H 7 | 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | class EncodeFile { 13 | unsigned char *imageInfo; 14 | unsigned char *weightMapData; 15 | unsigned char *data; 16 | uint32_t dataBitSize; 17 | uint32_t weightMapValCount; 18 | 19 | vector> weightMapArr; 20 | 21 | void generateWeightMapArr(); 22 | public: 23 | EncodeFile(unsigned char* , unsigned char*, unsigned char*, uint32_t, uint32_t); 24 | ~EncodeFile(); 25 | vector>& getWeightMapArr(); 26 | uint32_t getDataBitSize(); 27 | unsigned char* getData(); 28 | unsigned char* getImageInfo(); 29 | 30 | static const uint32_t WeightMapElementSize; 31 | 32 | }; 33 | 34 | 35 | #endif //HUFFMANIMAGECOMPRESSION_ENCODEFILE_H 36 | -------------------------------------------------------------------------------- /include/hfmCodeBitSet.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/31/17. 3 | // 4 | 5 | #ifndef HUFFMANIMAGECOMPRESSION_HFMCODEBITSET_H 6 | #define HUFFMANIMAGECOMPRESSION_HFMCODEBITSET_H 7 | 8 | #include 9 | using namespace std; 10 | 11 | // Huffman encode value element 12 | class hfmCodeBitSet { 13 | private: 14 | uint32_t len; 15 | unsigned char *data; 16 | 17 | void initFromOther(const hfmCodeBitSet& other); 18 | 19 | public: 20 | hfmCodeBitSet(); 21 | 22 | hfmCodeBitSet(const hfmCodeBitSet&); 23 | 24 | ~hfmCodeBitSet(); 25 | 26 | uint32_t length() const ; 27 | 28 | bool append(unsigned char); 29 | 30 | bool pop_back(); 31 | 32 | bool operator[] (uint32_t) const; 33 | 34 | hfmCodeBitSet& operator=(const hfmCodeBitSet&); 35 | 36 | unsigned char* getData() const; 37 | 38 | static const uint32_t DataBlockLength; 39 | }; 40 | 41 | #endif //HFMCODEBITSET_H 42 | -------------------------------------------------------------------------------- /include/huffmanCompression.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/31/17. 3 | // 4 | 5 | 6 | #ifndef HUFFMANIMAGECOMPRESSION_HUFFMANCOMPRESSION_H 7 | #define HUFFMANIMAGECOMPRESSION_HUFFMANCOMPRESSION_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "hfmCodeBitSet.h" 13 | #include 14 | using namespace std; 15 | 16 | 17 | class HuffmanCompression { 18 | private: 19 | struct TreeNode { 20 | unsigned char val; 21 | uint32_t weight; 22 | TreeNode *left; 23 | TreeNode *right; 24 | TreeNode(unsigned char _val, uint32_t _weight) : val(_val), weight(_weight), left(nullptr), right(nullptr) {} 25 | }; 26 | 27 | struct nodePtrCmp { 28 | bool operator() (TreeNode* a, TreeNode* b) { 29 | return (a -> weight) >= (b -> weight); 30 | } 31 | }; 32 | 33 | TreeNode *treeRoot; 34 | 35 | priority_queue, nodePtrCmp> nodeQueue; 36 | 37 | void calcWeight(const unsigned char*, uint32_t ,unordered_map&); 38 | 39 | void generateEncodedNodeQueue(const unordered_map&); 40 | 41 | void generateDecodedNodeQueue(const vector>&); 42 | 43 | void buildTree(); 44 | 45 | void dfs(TreeNode*, hfmCodeBitSet&, unordered_map&); 46 | void getCodeMap(unordered_map&); 47 | 48 | uint32_t calcEncodedOutputSize(const unordered_map&, 49 | const unordered_map&); 50 | void generateEncodedOutput(const unsigned char *, uint32_t , 51 | const unordered_map &, 52 | unsigned char *&, uint32_t); 53 | uint32_t calcDecodedOutputSize(const vector>& ); 54 | void generateDecodedOutput(const unsigned char *, uint32_t , 55 | unsigned char *&, uint32_t); 56 | 57 | public: 58 | HuffmanCompression(); 59 | ~HuffmanCompression(); 60 | void getEncodedData(const unsigned char* , uint32_t , 61 | unordered_map& , unsigned char* &, uint32_t &); 62 | void getDecodedData(const unsigned char* , uint32_t , 63 | const vector>& , unsigned char* &, uint32_t &); 64 | }; 65 | 66 | #endif -------------------------------------------------------------------------------- /include/image.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/11/17. 3 | // 4 | 5 | #ifndef HUFFMANIMAGECOMPRESSION_IMAGE_H 6 | #define HUFFMANIMAGECOMPRESSION_IMAGE_H 7 | 8 | class Image { 9 | unsigned char* imageInfo; 10 | unsigned char* data; 11 | uint32_t width; 12 | uint32_t height; 13 | uint32_t rawDataSize; 14 | uint32_t dataSize; 15 | public: 16 | Image(unsigned char* , unsigned char* , uint32_t , uint32_t , uint32_t, uint32_t); 17 | ~Image(); 18 | 19 | uint32_t getRawDataSize(); 20 | uint32_t getHeight(); 21 | uint32_t getWidth(); 22 | uint32_t getDataSize(); 23 | unsigned char* getData(); 24 | unsigned char* getImageInfo(); 25 | }; 26 | 27 | 28 | #endif //HUFFMANIMAGECOMPRESSION_IMAGE_H 29 | -------------------------------------------------------------------------------- /include/imageIOException.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/10/17. 3 | // 4 | 5 | #ifndef HUFFMANIMAGECOMPRESSION_IMAGEIOEXCEPTION_H 6 | #define HUFFMANIMAGECOMPRESSION_IMAGEIOEXCEPTION_H 7 | 8 | #include 9 | #include 10 | using namespace std; 11 | 12 | class ImageIOException: public runtime_error { 13 | public: 14 | explicit ImageIOException(const string &__arg); 15 | }; 16 | 17 | #endif //HUFFMANIMAGECOMPRESSION_IMAGEIOEXCEPTION_H 18 | -------------------------------------------------------------------------------- /include/imageIOHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/10/17. 3 | // 4 | 5 | #ifndef HUFFMANIMAGECOMPRESSION_IMAGEIOHELPER_H 6 | #define HUFFMANIMAGECOMPRESSION_IMAGEIOHELPER_H 7 | 8 | #include 9 | #include "image.h" 10 | #include "encodeFile.h" 11 | #include 12 | 13 | using namespace std; 14 | 15 | class ImageIOHelper { 16 | public: 17 | static const uint32_t BitmapInfoLength; 18 | 19 | static const uint32_t BitmapWidthOffset; 20 | static const uint32_t BitmapHeightOffset; 21 | static const uint32_t BitmapSizeOffset; 22 | 23 | static const uint32_t BitmapPixelBitSizeOffset; 24 | static const uint32_t BitmapTrueColorBitSize; 25 | static const uint32_t BitmapTrueColorChannelCount; 26 | 27 | static const uint32_t EncodeFileInfoLength; 28 | 29 | static Image* readImage(const string&); 30 | static EncodeFile* readEncodeFile(const string&); 31 | 32 | static void writeEncodeFile(const string& , 33 | const unsigned char* , 34 | const unordered_map , 35 | const unsigned char* , 36 | uint32_t, uint32_t); 37 | static void writeImage(const string &, 38 | unsigned char* , 39 | const unsigned char*); 40 | }; 41 | 42 | #endif //HUFFMANIMAGECOMPRESSION_IMAGEIOHELPER_H 43 | -------------------------------------------------------------------------------- /src/encodeFile.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/31/17. 3 | // 4 | 5 | #include "encodeFile.h" 6 | 7 | const uint32_t EncodeFile::WeightMapElementSize = 5; // every element contains 1 + 4 = 5 bytes(1 for val, 4 for weight) 8 | 9 | EncodeFile::EncodeFile(unsigned char *imageInfo, 10 | unsigned char *weightMapData, 11 | unsigned char *data, 12 | uint32_t dataBitSize, uint32_t weightMapValCount) { 13 | this -> imageInfo = imageInfo; 14 | this -> weightMapData = weightMapData; 15 | this -> data = data; 16 | this -> dataBitSize = dataBitSize; 17 | this -> weightMapValCount = weightMapValCount; 18 | this -> weightMapArr = vector>(weightMapValCount); 19 | generateWeightMapArr(); 20 | } 21 | 22 | EncodeFile::~EncodeFile() { 23 | delete [] imageInfo; 24 | delete [] weightMapData; 25 | delete [] data; 26 | } 27 | 28 | void EncodeFile::generateWeightMapArr() { 29 | uint32_t valIndex; 30 | for (valIndex = 0; valIndex < weightMapValCount; valIndex++) { 31 | weightMapArr[valIndex].first = weightMapData[valIndex * WeightMapElementSize]; // set value 32 | weightMapArr[valIndex].second = 33 | *reinterpret_cast(weightMapData + (valIndex * WeightMapElementSize + 1)); // set weight 34 | } 35 | } 36 | 37 | vector>& EncodeFile::getWeightMapArr() { 38 | return weightMapArr; 39 | } 40 | 41 | uint32_t EncodeFile::getDataBitSize() { 42 | return dataBitSize; 43 | } 44 | 45 | unsigned char *EncodeFile::getData() { 46 | return data; 47 | } 48 | 49 | unsigned char *EncodeFile::getImageInfo() { 50 | return imageInfo; 51 | } 52 | -------------------------------------------------------------------------------- /src/hfmCodeBitSet.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/31/17. 3 | // 4 | 5 | 6 | #include 7 | #include 8 | #include "hfmCodeBitSet.h" 9 | using namespace std; 10 | 11 | const uint32_t hfmCodeBitSet::DataBlockLength = 32; // an color channel value won't be larger than 256 12 | 13 | hfmCodeBitSet::hfmCodeBitSet() { 14 | len = 0; 15 | data = new unsigned char[DataBlockLength]; 16 | memset(data, 0, DataBlockLength); 17 | } 18 | 19 | hfmCodeBitSet::hfmCodeBitSet(const hfmCodeBitSet &other) { 20 | data = new unsigned char[DataBlockLength]; 21 | initFromOther(other); 22 | } 23 | 24 | uint32_t hfmCodeBitSet::length() const { 25 | return len; 26 | } 27 | 28 | bool hfmCodeBitSet::append(unsigned char flag) { 29 | if (len >= DataBlockLength * 8) { 30 | throw runtime_error("hfmCodeBitSet::append error: hfmCodeBitSet overflow"); 31 | } 32 | uint32_t byteOff = len / 8; 33 | uint32_t bitOff = len % 8; 34 | if (flag & 1) 35 | data[byteOff] |= (1 << bitOff); 36 | else 37 | data[byteOff] &= (~(1 << bitOff)); 38 | len++; 39 | return true; 40 | } 41 | 42 | bool hfmCodeBitSet::pop_back() { 43 | if (len <= 0) { 44 | throw runtime_error("hfmCodeBitSet::pop_back error: hfmCodeBitSet underflow"); 45 | } 46 | len--; 47 | return true; 48 | } 49 | 50 | hfmCodeBitSet::~hfmCodeBitSet() { 51 | delete[] data; 52 | } 53 | 54 | bool hfmCodeBitSet::operator[](uint32_t pos) const { 55 | if (pos >= len) { 56 | throw runtime_error("hfmCodeBitSet::[] error: access overflow"); 57 | } 58 | if (pos < 0) { 59 | throw runtime_error("hfmCodeBitSet::[] error: access underflow"); 60 | } 61 | uint32_t byteOff = pos / 8; 62 | uint32_t bitOff = pos % 8; 63 | bool res = static_cast((data[byteOff] >> bitOff) & 1); 64 | return res; 65 | } 66 | 67 | unsigned char *hfmCodeBitSet::getData() const { 68 | return data; 69 | } 70 | 71 | hfmCodeBitSet& hfmCodeBitSet::operator=(const hfmCodeBitSet &other) { 72 | initFromOther(other); 73 | return *this; 74 | } 75 | 76 | void hfmCodeBitSet::initFromOther(const hfmCodeBitSet &other) { 77 | len = other.length(); 78 | memcpy(data, other.getData(), DataBlockLength); 79 | } 80 | -------------------------------------------------------------------------------- /src/huffmanCompression.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/31/17. 3 | // 4 | 5 | 6 | #include 7 | #include 8 | #include "huffmanCompression.h" 9 | using namespace std; 10 | 11 | HuffmanCompression::HuffmanCompression() { 12 | treeRoot = nullptr; 13 | } 14 | 15 | HuffmanCompression::~HuffmanCompression() { 16 | if (treeRoot == nullptr) 17 | return; 18 | queue q; 19 | q.push(treeRoot); 20 | while (!q.empty()) { 21 | TreeNode* node = q.front(); 22 | q.pop(); 23 | if (node -> left != nullptr) 24 | q.push(node -> left); 25 | if (node -> right != nullptr) 26 | q.push(node -> right); 27 | delete(node); 28 | } 29 | treeRoot = nullptr; 30 | } 31 | 32 | void HuffmanCompression::calcWeight(const unsigned char* dataPtr, uint32_t dataSize, 33 | unordered_map& weightMap) { 34 | for (uint32_t i = 0; i < dataSize; i++) { 35 | if (weightMap.find(dataPtr[i]) == weightMap.end()) { 36 | weightMap[dataPtr[i]] = 1; 37 | } else { 38 | weightMap[dataPtr[i]]++; 39 | } 40 | } 41 | } 42 | 43 | // 因为节点队列能够根据进出队列的节点的权值进行顺序调整, 44 | // 所以每次只需要从队首取出2个节点构造一棵树,将树根再次加进节点队列, 45 | // 直到队列只剩下一个节点,这个节点就是编码树的根节点 46 | void HuffmanCompression::buildTree() { 47 | while (nodeQueue.size() > 1) { 48 | TreeNode* rightNode = nodeQueue.top(); 49 | nodeQueue.pop(); 50 | TreeNode* leftNode = nodeQueue.top(); 51 | nodeQueue.pop(); 52 | auto parentNode = new TreeNode(0, rightNode -> weight + leftNode -> weight); 53 | parentNode -> left = leftNode; 54 | parentNode -> right = rightNode; 55 | nodeQueue.push(parentNode); 56 | } 57 | treeRoot = nodeQueue.top(); 58 | } 59 | 60 | // 深度优先搜索,过程中使用path来记录路径, 61 | // 到达每个叶子节点的path就是叶子节点对应数据的编码 62 | void HuffmanCompression::dfs(TreeNode* node, hfmCodeBitSet& path, 63 | unordered_map &resCodeMap) { 64 | if (node -> left == nullptr && node -> right == nullptr) { 65 | resCodeMap[node -> val] = path; 66 | return; 67 | } 68 | path.append(0); 69 | dfs(node -> left, path, resCodeMap); 70 | path.pop_back(); 71 | path.append(1); 72 | dfs(node -> right, path, resCodeMap); 73 | path.pop_back(); 74 | } 75 | 76 | // 生成编码表:调用dfs来生成编码表 77 | void HuffmanCompression::getCodeMap(unordered_map &resCodeMap) { 78 | if (treeRoot == nullptr) 79 | return; 80 | hfmCodeBitSet path; 81 | dfs(treeRoot, path, resCodeMap); 82 | } 83 | 84 | void HuffmanCompression::generateEncodedOutput(const unsigned char *rawDataPtr, uint32_t rawDataSize, 85 | const unordered_map &resCodeMap, 86 | unsigned char* &outputDataPtr, uint32_t outputDataBitSize) { 87 | outputDataPtr = new unsigned char[outputDataBitSize / 8 + 1]; 88 | memset(outputDataPtr, 0, outputDataBitSize / 8 + 1); 89 | uint32_t outputBitOffset = 0; 90 | uint32_t codeBitLength = 0; 91 | uint32_t byteOff, bitOffInByte; 92 | uint32_t i, j; 93 | 94 | // generate encode output data by looking up the weight map 95 | for (i = 0; i < rawDataSize; i++) { 96 | const hfmCodeBitSet& code = resCodeMap.at(rawDataPtr[i]); 97 | codeBitLength = code.length(); 98 | for (j = 0; j < codeBitLength; j++) { 99 | byteOff = (j + outputBitOffset) / 8; 100 | bitOffInByte = (j + outputBitOffset) % 8; 101 | outputDataPtr[byteOff] |= (code[j] << bitOffInByte); 102 | } 103 | outputBitOffset += codeBitLength; 104 | } 105 | if (outputBitOffset != outputDataBitSize) { 106 | throw runtime_error("HuffmanCompression::generateEncodedOutput error: bitCount not match"); 107 | } 108 | } 109 | 110 | void HuffmanCompression::generateDecodedOutput(const unsigned char *rawDataPtr, uint32_t rawDataBitSize, 111 | unsigned char *&outputDataPtr, uint32_t outputDataSize) { 112 | outputDataPtr = new unsigned char[outputDataSize + 1]; 113 | memset(outputDataPtr, 0, outputDataSize + 1); 114 | uint32_t bitOffInByte = 0; 115 | uint32_t byteOff = 0; 116 | uint32_t rawDataBitOff; 117 | uint32_t outputDataOff = 0; 118 | TreeNode *curNode = treeRoot; 119 | 120 | // generate decode output data by DFS the Huffman Tree 121 | for (rawDataBitOff = 0; rawDataBitOff < rawDataBitSize; rawDataBitOff++) { 122 | if (curNode -> left == nullptr && curNode -> right == nullptr) { 123 | outputDataPtr[outputDataOff++] = curNode -> val; 124 | curNode = treeRoot; 125 | } 126 | byteOff = rawDataBitOff / 8; 127 | bitOffInByte = rawDataBitOff % 8; 128 | if ((rawDataPtr[byteOff] >> bitOffInByte) & 1) { 129 | if (curNode == nullptr || curNode -> right == nullptr) { 130 | throw runtime_error("HuffmanCompression::generateDecodedOutput error: bit not match"); 131 | } else { 132 | curNode = curNode -> right; 133 | } 134 | } else { 135 | if (curNode == nullptr || curNode -> left == nullptr) { 136 | throw runtime_error("HuffmanCompression::generateDecodedOutput error: bit not match"); 137 | } else { 138 | curNode = curNode -> left; 139 | } 140 | } 141 | } 142 | if (curNode -> left == nullptr && curNode -> right == nullptr) { 143 | outputDataPtr[outputDataOff++] = curNode -> val; 144 | curNode = treeRoot; 145 | } 146 | if (outputDataOff != outputDataSize) { 147 | throw runtime_error("HuffmanCompression::generateDecodedOutput error: outputData overflow"); 148 | } 149 | } 150 | 151 | void HuffmanCompression::getEncodedData(const unsigned char *rawDataPtr, uint32_t rawDataSize, 152 | unordered_map &dstWeightMap, 153 | unsigned char *&outputDataPtr, uint32_t &outputDataBitSize) { 154 | calcWeight(rawDataPtr, rawDataSize, dstWeightMap); // 计算出权值映射表 155 | generateEncodedNodeQueue(dstWeightMap); // 根据权值映射表构建编码树节点队列 156 | // (使用priority_queue这样一个以堆实现的“队列”,可以保证每次进出队操作后队列的节点保持权值从小到大排列) 157 | 158 | buildTree(); // 建立编码树 159 | unordered_map resCodeMap; 160 | getCodeMap(resCodeMap); // 根据编码树产生权值表 161 | outputDataBitSize = calcEncodedOutputSize(dstWeightMap, resCodeMap); // 计算编码数据位数 162 | generateEncodedOutput(rawDataPtr, rawDataSize, resCodeMap, outputDataPtr, outputDataBitSize); // 对原数据进行编码 163 | } 164 | 165 | void HuffmanCompression::getDecodedData(const unsigned char *rawDataPtr, uint32_t rawDataBitSize, 166 | const vector> &srcWeightMapArr, 167 | unsigned char *&outputDataPtr, uint32_t &outputDataSize) { 168 | generateDecodedNodeQueue(srcWeightMapArr); // 根据读取文件之后构建的权值映射表构建节点队列 169 | buildTree(); // 建立编码树 170 | outputDataSize = calcDecodedOutputSize(srcWeightMapArr); // 计算输出数据大小 171 | generateDecodedOutput(rawDataPtr, rawDataBitSize, outputDataPtr, outputDataSize); // 对编码数据进行解码 172 | } 173 | 174 | uint32_t HuffmanCompression::calcEncodedOutputSize(const unordered_map &weightMap, 175 | const unordered_map &resCodeMap) { 176 | uint32_t bitsCount = 0; 177 | unsigned char val; 178 | uint32_t weight, codeLength; 179 | 180 | for (const auto &ele : weightMap) { 181 | val = ele.first; 182 | weight = ele.second; 183 | codeLength = static_cast(resCodeMap.at(val).length()); 184 | bitsCount += weight * codeLength; 185 | } 186 | return bitsCount; 187 | } 188 | 189 | uint32_t HuffmanCompression::calcDecodedOutputSize(const vector> &weightMap) { 190 | uint32_t res = 0; 191 | for (const auto &ele : weightMap) { 192 | res += ele.second; 193 | } 194 | return res; 195 | } 196 | 197 | void HuffmanCompression::generateEncodedNodeQueue(const unordered_map &weightMap) { 198 | for (const auto &ele : weightMap) { 199 | nodeQueue.push(new TreeNode(ele.first, ele.second)); 200 | } 201 | } 202 | 203 | void HuffmanCompression::generateDecodedNodeQueue(const vector> &weightMapArr) { 204 | for (const auto &ele : weightMapArr) { 205 | nodeQueue.push(new TreeNode(ele.first, ele.second)); 206 | } 207 | } 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /src/image.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/11/17. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "image.h" 9 | 10 | 11 | 12 | Image::Image(unsigned char *data, unsigned char *info, 13 | uint32_t width, uint32_t height, uint32_t rawSize, uint32_t dataSize) { 14 | this -> data = data; 15 | this -> imageInfo = info; 16 | this -> width = width; 17 | this -> height = height; 18 | this -> rawDataSize = rawSize; 19 | this -> dataSize = dataSize; 20 | } 21 | 22 | Image::~Image() { 23 | delete [] data; 24 | delete [] imageInfo; 25 | } 26 | 27 | unsigned int Image::getRawDataSize() { 28 | return rawDataSize; 29 | } 30 | 31 | uint32_t Image::getHeight() { 32 | return height; 33 | } 34 | 35 | uint32_t Image::getWidth() { 36 | return width; 37 | } 38 | 39 | uint32_t Image::getDataSize() { 40 | return dataSize; 41 | } 42 | 43 | unsigned char *Image::getData() { 44 | return data; 45 | } 46 | 47 | unsigned char *Image::getImageInfo() { 48 | return imageInfo; 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/imageIOException.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/10/17. 3 | // 4 | 5 | #include "imageIOException.h" 6 | 7 | 8 | ImageIOException::ImageIOException(const string &__arg) : 9 | runtime_error("ImageIOException: " + __arg) {} 10 | -------------------------------------------------------------------------------- /src/imageIOHelper.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/10/17. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "imageIOHelper.h" 9 | #include "imageIOException.h" 10 | using namespace std; 11 | 12 | const uint32_t ImageIOHelper::BitmapInfoLength = 54; 13 | 14 | const uint32_t ImageIOHelper::BitmapWidthOffset = 18; 15 | const uint32_t ImageIOHelper::BitmapHeightOffset= 22; 16 | const uint32_t ImageIOHelper::BitmapSizeOffset = 34; 17 | 18 | const uint32_t ImageIOHelper::BitmapPixelBitSizeOffset = 28; 19 | const uint32_t ImageIOHelper::BitmapTrueColorBitSize = 24; 20 | const uint32_t ImageIOHelper::BitmapTrueColorChannelCount = 3; 21 | 22 | const uint32_t ImageIOHelper::EncodeFileInfoLength = 8; 23 | 24 | Image* ImageIOHelper::readImage(const string& filename) { 25 | FILE* fp = fopen(filename.c_str(), "rb"); // open the image file in mode "read binary" 26 | if (fp == nullptr) 27 | throw ImageIOException("Failed to open file"); 28 | 29 | auto imageInfoPtr = new unsigned char[BitmapInfoLength]; 30 | fread(imageInfoPtr, sizeof(unsigned char), BitmapInfoLength, fp); // read the header 31 | 32 | // get bit size of the image 33 | uint32_t bitSize = *reinterpret_cast(&imageInfoPtr[BitmapPixelBitSizeOffset]); 34 | if (bitSize != BitmapTrueColorBitSize) { 35 | fclose(fp); 36 | throw ImageIOException("Input file is not a true color bitmap"); 37 | } 38 | 39 | // get image height and width 40 | uint32_t width = *reinterpret_cast(&imageInfoPtr[BitmapWidthOffset]); 41 | uint32_t height = *reinterpret_cast(&imageInfoPtr[BitmapHeightOffset]); 42 | uint32_t rawSize = *reinterpret_cast(&imageInfoPtr[BitmapSizeOffset]); 43 | uint32_t dataSize = 3 * width * height; // 3 Bytes per pixel 44 | 45 | unsigned char* rawDataPtr = new unsigned char[rawSize + 1]; 46 | unsigned char* dataPtr = new unsigned char[dataSize + 1]; 47 | memset(rawDataPtr, '\0', rawSize + 1); 48 | memset(dataPtr, '\0', dataSize + 1); 49 | 50 | fread(rawDataPtr, sizeof(unsigned char), rawSize, fp); // read the rest of the data 51 | fclose(fp); 52 | uint32_t row, col, pixelIndex, byteIndex; 53 | const uint32_t nullBytesCountInRow = rawSize / height - width * BitmapTrueColorChannelCount; 54 | uint32_t pixelOffset = 0; 55 | for (row = 0; row < height; row++) { 56 | for (col = 0; col < width; col++) { 57 | pixelIndex = col + row * width; 58 | byteIndex = pixelIndex * BitmapTrueColorChannelCount; 59 | memcpy(&dataPtr[byteIndex], &rawDataPtr[pixelOffset], BitmapTrueColorChannelCount); 60 | pixelOffset += BitmapTrueColorChannelCount; 61 | } 62 | // increase the pixelOffset for the null bytes in every row in the bmp matrix 63 | pixelOffset += nullBytesCountInRow; 64 | } 65 | 66 | Image* imagePtr = new Image(dataPtr, imageInfoPtr, width, height, rawSize, dataSize); 67 | 68 | delete [] rawDataPtr; 69 | return imagePtr; 70 | } 71 | 72 | EncodeFile *ImageIOHelper::readEncodeFile(const string& filename) { 73 | FILE* fp = fopen(filename.c_str(), "rb"); // open the image file in mode "read binary" 74 | if (fp == nullptr) 75 | throw ImageIOException("Failed to open file"); 76 | 77 | // read imageInfo 78 | auto imageInfoPtr = new unsigned char[BitmapInfoLength]; 79 | fread(imageInfoPtr, sizeof(unsigned char), BitmapInfoLength, fp); // read the header 80 | 81 | // read dataBitSize and weightMapValCount 82 | auto encodeFileInfoPtr = new unsigned char[EncodeFileInfoLength]; 83 | fread(encodeFileInfoPtr, sizeof(unsigned char), EncodeFileInfoLength, fp); 84 | uint32_t dataBitSize = *reinterpret_cast(encodeFileInfoPtr); 85 | uint32_t weightMapValCount = *reinterpret_cast(encodeFileInfoPtr + EncodeFileInfoLength / 2); 86 | delete [] encodeFileInfoPtr; 87 | 88 | // read weightMapData 89 | const size_t weightMapDataSize = weightMapValCount * EncodeFile::WeightMapElementSize; 90 | auto weightMapDataPtr = 91 | new unsigned char[weightMapDataSize + 1]; 92 | fread(weightMapDataPtr, sizeof(unsigned char), weightMapDataSize, fp); 93 | 94 | // read encode data 95 | uint32_t dataSize = dataBitSize / 8 + (dataBitSize % 8 == 0 ? 0 : 1); 96 | 97 | auto dataPtr = new unsigned char[dataSize + 1]; 98 | memset(dataPtr, '\0', dataSize + 1); 99 | fread(dataPtr, sizeof(unsigned char), dataSize, fp); 100 | fclose(fp); 101 | 102 | EncodeFile* encodeFilePtr = 103 | new EncodeFile(imageInfoPtr, weightMapDataPtr, dataPtr, dataBitSize, weightMapValCount); 104 | 105 | return encodeFilePtr; 106 | } 107 | 108 | void ImageIOHelper::writeEncodeFile(const string& filename, 109 | const unsigned char* imageInfo, 110 | const unordered_map weightMap, 111 | const unsigned char* encodeData, 112 | uint32_t encodeDataSize, 113 | uint32_t encodeDataBitSize) { 114 | FILE* fp = fopen(filename.c_str(), "wb"); 115 | 116 | if (fp == nullptr) 117 | throw ImageIOException("Failed to open file"); 118 | 119 | // write imageInfo 120 | fwrite(imageInfo, sizeof(unsigned char), BitmapInfoLength, fp); 121 | 122 | // write encodeDataInfo: encodeDataBitSize and weightMapValCount 123 | uint32_t weightMapValCount = static_cast(weightMap.size()); 124 | auto encodeFileInfoPtr = new unsigned char[EncodeFileInfoLength]; 125 | 126 | int byteIndex; 127 | for (byteIndex = 0; byteIndex < 4; byteIndex++) { 128 | encodeFileInfoPtr[byteIndex] = 129 | static_cast(0xff & (encodeDataBitSize >> (byteIndex * 8))); 130 | encodeFileInfoPtr[byteIndex + 4] = 131 | static_cast(0xff & (weightMapValCount >> (byteIndex * 8))); 132 | } 133 | fwrite(encodeFileInfoPtr, sizeof(unsigned char), EncodeFileInfoLength, fp); 134 | 135 | // write weightMap 136 | const size_t weightMapArrSize = weightMapValCount * EncodeFile::WeightMapElementSize; 137 | unsigned char *weightMapArr = new unsigned char[weightMapArrSize]; 138 | int elementIndex = 0; 139 | for (auto& element: weightMap) { 140 | weightMapArr[elementIndex * EncodeFile::WeightMapElementSize] = element.first; 141 | for (byteIndex = 0; byteIndex < 4; byteIndex++) { 142 | weightMapArr[elementIndex * EncodeFile::WeightMapElementSize + byteIndex + 1] = 143 | static_cast(0xff & (element.second >> (byteIndex * 8))); 144 | } 145 | elementIndex++; 146 | } 147 | fwrite(weightMapArr, sizeof(unsigned char), weightMapArrSize, fp); 148 | 149 | // write encode data 150 | fwrite(encodeData, sizeof(unsigned char), encodeDataSize, fp); 151 | 152 | delete [] encodeFileInfoPtr; 153 | delete [] weightMapArr; 154 | fclose(fp); 155 | } 156 | 157 | void ImageIOHelper::writeImage(const string &filename, 158 | unsigned char* imageInfoPtr, 159 | const unsigned char* decodedDataPtr) { 160 | 161 | FILE* fp = fopen(filename.c_str(), "wb"); 162 | 163 | if (fp == nullptr) 164 | throw ImageIOException("Failed to open file"); 165 | 166 | // write imageInfo 167 | fwrite(imageInfoPtr, sizeof(unsigned char), BitmapInfoLength, fp); 168 | 169 | // get image height and width 170 | uint32_t width = *reinterpret_cast(&imageInfoPtr[BitmapWidthOffset]); 171 | uint32_t height = *reinterpret_cast(&imageInfoPtr[BitmapHeightOffset]); 172 | uint32_t rawSize = *reinterpret_cast(&imageInfoPtr[BitmapSizeOffset]); 173 | 174 | const uint32_t nullBytesCountInRow = rawSize / height - width * BitmapTrueColorChannelCount; 175 | unsigned char *rawDataPtr = new unsigned char[rawSize]; 176 | memset(rawDataPtr, '\0', rawSize); 177 | 178 | uint32_t row, col, pixelOffset = 0; 179 | uint32_t pixelIndex, byteIndex; 180 | for (row = 0; row < height; row++) { 181 | for (col = 0; col < width; col++) { 182 | pixelIndex = col + row * width; 183 | byteIndex = pixelIndex * BitmapTrueColorChannelCount; 184 | // get 3 color channel value in every pixel 185 | memcpy(&rawDataPtr[pixelOffset], &decodedDataPtr[byteIndex], BitmapTrueColorChannelCount); 186 | pixelOffset += BitmapTrueColorChannelCount; 187 | } 188 | // increase the pixelOffset for the null bytes in every row in the bmp matrix 189 | pixelOffset += nullBytesCountInRow; 190 | } 191 | 192 | fwrite(rawDataPtr, sizeof(unsigned char), rawSize, fp); 193 | 194 | delete [] rawDataPtr; 195 | fclose(fp); 196 | } 197 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xujijun on 12/31/17. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "huffmanCompression.h" 10 | #include "imageIOHelper.h" 11 | using namespace std; 12 | 13 | void encodeImage() { 14 | string imageFilename, encodedFilename; 15 | cout << "Please input image filename(BMP format):" << endl; 16 | cin >> imageFilename; 17 | if (imageFilename.length() < 5 || imageFilename.substr(imageFilename.length() - 4) != ".bmp") { 18 | throw ImageIOException("image format wrong!"); 19 | } 20 | 21 | cout << "Please input encoded filename(with extension name \".hfmc\"):" << endl; 22 | cin >> encodedFilename; 23 | if (encodedFilename.length() < 6 || encodedFilename.substr(encodedFilename.length() - 5) != ".hfmc") { 24 | throw ImageIOException("encoded filename format wrong!"); 25 | } 26 | 27 | Image* imagePtr = ImageIOHelper::readImage(imageFilename); 28 | 29 | cout << "Width = " << imagePtr -> getWidth() << endl; 30 | cout << "Height = " << imagePtr -> getHeight() << endl; 31 | cout << "Pure dataSize = " << imagePtr -> getDataSize() << " Bytes" << endl; 32 | cout << "RawDataSize = " << imagePtr -> getRawDataSize() << " Bytes" << endl; 33 | 34 | HuffmanCompression encoder; 35 | unordered_map weightMap; 36 | unsigned char *encodedDataPtr = nullptr; 37 | uint32_t encodedDataBitSize; 38 | encoder.getEncodedData(imagePtr -> getData(), imagePtr -> getDataSize(), 39 | weightMap, encodedDataPtr, encodedDataBitSize); 40 | 41 | cout << "Encoded data bit size: " << encodedDataBitSize << endl; 42 | uint32_t encodedDataSize = encodedDataBitSize / 8 + (encodedDataBitSize % 8 == 0 ? 0 : 1); 43 | cout << "EncodedDataSize: " << encodedDataSize << " Bytes" << endl; 44 | 45 | printf("Compression ratio = %.2f%%\n", 46 | static_cast(encodedDataSize) / 47 | static_cast(imagePtr -> getRawDataSize()) * 100); 48 | 49 | ImageIOHelper::writeEncodeFile(encodedFilename, imagePtr -> getImageInfo(), weightMap, 50 | encodedDataPtr, encodedDataSize, encodedDataBitSize); 51 | 52 | delete [] encodedDataPtr; 53 | delete imagePtr; 54 | } 55 | 56 | void decodeImage() { 57 | string imageFilename, encodedFilename; 58 | 59 | cout << "Please input encoded filename(with extension name \".hfmc\"):" << endl; 60 | cin >> encodedFilename; 61 | if (encodedFilename.length() < 6 || encodedFilename.substr(encodedFilename.length() - 5) != ".hfmc") { 62 | throw ImageIOException("encoded filename format wrong!"); 63 | } 64 | 65 | cout << "Please input image filename(BMP format):" << endl; 66 | cin >> imageFilename; 67 | if (imageFilename.length() < 5 || imageFilename.substr(imageFilename.length() - 4) != ".bmp") { 68 | throw ImageIOException("image format wrong!"); 69 | } 70 | 71 | EncodeFile* encodeFilePtr = ImageIOHelper::readEncodeFile(encodedFilename); 72 | HuffmanCompression decoder; 73 | 74 | unsigned char *decodedDataPtr = nullptr; 75 | uint32_t decodedDataSize; 76 | 77 | decoder.getDecodedData(encodeFilePtr -> getData(), 78 | encodeFilePtr -> getDataBitSize(), 79 | encodeFilePtr -> getWeightMapArr(), 80 | decodedDataPtr, decodedDataSize); 81 | 82 | cout << "Decoded data size: " << decodedDataSize << " Bytes" << endl; 83 | 84 | ImageIOHelper::writeImage(imageFilename, 85 | encodeFilePtr -> getImageInfo(), 86 | decodedDataPtr); 87 | 88 | delete [] decodedDataPtr; 89 | delete encodeFilePtr; 90 | } 91 | 92 | int main() { 93 | string command; 94 | const string EncodeCmd = "e"; 95 | const string DecodeCmd = "d"; 96 | cout << "Input your choice:(\"e\"for encode, \"d\"for decode): "; 97 | cin >> command; 98 | if (command == EncodeCmd) { 99 | encodeImage(); 100 | } else if (command == DecodeCmd) { 101 | decodeImage(); 102 | } else { 103 | cout << endl; 104 | cout << "Wrong Command!" << endl; 105 | } 106 | 107 | return 0; 108 | } 109 | --------------------------------------------------------------------------------