福州大学《嵌入式系统综合设计》 实验十二:图像压缩标准JPEG编解码

一、实验目的

掌握基于算能平台的JPEG压缩编码方法以及开发环境,包括开发主机环境搭建,硬件嵌入式开发板的连接,云平台的配置,编码程序的编译、运行等。

二、实验内容

搭建实验开发环境,并编写静止图像jpeg格式编解码程序,输入端读取原始图像数据,选择编解码模式,输出压缩编码结果。在目标开发机运行测试,验证开发环境。如果是基于云平台虚拟环境,则需要将编译好的程序代码上传云平台进行测试运行。

三、开发环境

开发主机:Ubuntu 20.04.6 LTS

硬件:算能SE5

四、实验器材

开发主机 + 云平台(或SE5硬件)

五、实验过程与结论

实验原理概述

本小节重点讨论JPEG编解码实验。JPEG是当前流行的静态图像压缩格式,从创造至今已经过去几十年,但依然是当今互联网时代的主要图像压缩标准。原始未压缩的图片,经过压缩算法运行后,生成jpeg格式的压缩图片。其占用的对于原始图像数据空间更小,更有利于图像文件的存储和传输。PEG编码包括预测编码、变换编码、量化、熵编码等过程,在之前的理论课程中已经充分学习并熟练掌握其基本原理。本实验的JPEG编解码实现代码通过直接调用OpenCV库实现。

算能的OpenCV库提供了imencode和imdecode分别进行编码和解码。和OpenCV的其他函数使用方法一样,imencode和imdecode同样针对Mat数据进行处理。在imencode函数中读取Mat数据格式图片进行编码,在imdecode将解码后的数据通过Mat数据格式返回。

编解码实验过程

JPEG编码:

本实验可以封装FnEncode函数用于进行编码,函数关键代码如下:

void FnEncode(const char *filenpath, int output_en)
{
    Mat save = imread(filenpath, IMREAD_UNCHANGED);
    vector<uint8_t> encoded;
    imencode(".jpg", save, encoded);                  //编码

    if (output_en)
    {
        char *str = "encodeImage.jpg";
        int bufLen = encoded.size();
        if (bufLen)
        {
            uint8_t *pYuvBuf = encoded.data();
            FILE *fclr = fopen(str, "wb");
            fwrite(pYuvBuf, 1, bufLen, fclr);        //编码后的数据写入文件
            fclose(fclr);
        }
    }
}

JPEG解码:

本实验可以封装FnDecode函数用于进行编码,函数关键代码如下:

void FnDecode(const char *filenpath, int output_en)
{
    ifstream in(filenpath, ios::binary);
    string s((istreambuf_iterator<char>(in)), (istreambuf_iterator<char>()));
    in.close();
    vector<char> pic(s.c_str(), s.c_str() + s.length());
    Mat image;
    imdecode(pic, IMREAD_UNCHANGED, &image);        //解码

    if (output_en)
    {
        char *str = "decodeImage.bmp";
        imwrite(str, image);                        //解码后写入文件
    }
}

 主函数:

最后我们通过封装主函数,分别对输入的图片进行编码或解码:

int main(int argc, char *argv[])
{
   ..... 
   switch (codec_type)
    {
    case 1:
        /* code */
        FnEncode(input_file.data(), outputEnable);
        cout << "encode finish ." << endl;
        break;
    case 2:
        FnDecode(input_file.data(), outputEnable);
        cout << "decode finish ." << endl;
        break;
    default:
        cout << "please input correct codec type number." << endl
             << "   "
             << " [codec-type] - the codec type you want to use . 1 -> encode ,2 -> decode" << endl;
        break;
    }
}

如上所示,我们可以在主函数中根据用户输入不同的codec_type即编码或者解码,分别对输入的图片进行编码和解码。outputEnable是由用户指定是否输出到文件。

以下是完整版的源代码(jpeg_encdec.cpp):

#include <iostream>
#include <fstream>
#include <vector>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

// JPEG编码函数
void EncodeJPEG(const char *inputFilePath, bool isOut) {
    // 读取图像
    Mat image = imread(inputFilePath, IMREAD_UNCHANGED);

    // 进行JPEG编码
    vector<uint8_t> encoded;
    imencode(".jpg", image, encoded);

    if (isOut) {
        // 输出文件名
        const char *outputFile = "encodedImage.jpg";
        int bufLen = encoded.size();
        if (bufLen) {
            // 获取编码后的数据并写入文件
            uint8_t *pYuvBuf = encoded.data();
            FILE *fclr = fopen(outputFile, "wb");
            fwrite(pYuvBuf, 1, bufLen, fclr);
            fclose(fclr);
        }
    }
}

// JPEG解码函数
void DecodeJPEG(const char *inputFilePath, bool isOut) {
    // 读取二进制文件
    ifstream in(inputFilePath, ios::binary);
    string s((istreambuf_iterator<char>(in)), (istreambuf_iterator<char>()));
    in.close();

    // 将字符串数据转换为向量
    vector<char> pic(s.c_str(), s.c_str() + s.length());
    Mat image;
    
    // 进行JPEG解码
    imdecode(pic, IMREAD_UNCHANGED, &image);

    if (isOut) {
        // 输出文件名
        const char *outputFile = "decodedImage.bmp";
        
        // 将解码后的图像写入文件
        imwrite(outputFile, image);
    }
}

int main(int argc, char *argv[]) {
    if (argc != 4) {
        cerr << "使用方式:./test_ocv_jpumulti <inputfile> <test type: 1-编码, 2-解码> <isOut: 0-不生成输出文件, 1-生成输出文件>" << endl;
        return 1;
    }

    const char *inputFile = argv[1];
    int testType = stoi(argv[2]);
    bool isOut = stoi(argv[3]);

    switch (testType) {
        case 1:
            EncodeJPEG(inputFile, isOut);
            cout << "JPEG编码完成。" << endl;
            break;
        case 2:
            DecodeJPEG(inputFile, isOut);
            cout << "JPEG解码完成。" << endl;
            break;
        default:
            cerr << "错误:请提供正确的测试类型,1-编码,2-解码。" << endl;
            return 1;
    }

    return 0;
}
执行与测试

本地编译后上传到云平台或者本地SE5盒子中的过程与前面的方法相同,这里不再赘述。上传完成后,在终端中输入指令:

root@d11ae417e206:/tmp/test# ./jpeg_encdec lena.bmp 1 1

 各个指令的解释如下表所示:

指令:./jpeg_encdec  <inputfile>  <test type>  <isOut>

参数说明:

  • inputfile: 输入图像文件
  • test type: 选择测试功能, 1: 编码, 2: 解码,
  • IsOut: 是否输出到文件, 0: 不生成输出文件, 1: 生成输出文件

 编码输出结果如下:

root@211bf635cafb:/tmp/test# ./jpeg_encdec lena.bmp 1 1

输入文件的路径:lena.bmp
编解码类型:11-编码2-解码
Open /dev/jpu successfully, device index = e, jpu fd = 6,vpp fd = 7

编码完成
root@211bf635cafb:/tmp/test# ./
jpeg_encdec EncodeImage.jpg 2 1

输入文件的路径:EncodeImage.jpg
编解码类型:21-编码2-解码
Open /dev/jpu successfully, device index = e, jpu fd= 4, vpp fd = 5

解码完成

结果分析:根据指令我们选择编解码模式1,也就是编码,输入为图片:lena.bmp,选择输出到图片(isOut=1)。结果显示,65KB的图片压缩成了15KB;另外,我们再运行一次程序,选择编解码模式2,也就是解码,输入刚才编码输出的图片EnCodeImage.jpg,进行解码,同样也选择解码文件输出输出到图片(isOut=1)。结果显示,15KB的压缩图片还原成了65KB,并得到了指定的原来的格式的图片DecodeImage.bmp。

信息如下图所示。

猜你喜欢

转载自blog.csdn.net/m0_52537869/article/details/134703276
今日推荐