使用 jpeg-turbo 压缩图片

1. jpeg-turbo的编译安装

使用标准的cmake的编译安装方式即可。

下面以 windows 平台为例说明编译安装过程,其他平台过程类似。

1.1 先安装NASM

NASM是个汇编工具,后面编译 jpeg-turbo 需要用到,所以先安装

NASM的官网: https://www.nasm.us/index.php

可以从下载页 https://www.nasm.us/pub/nasm/releasebuilds/?C=M;O=D 中选择适合自己系统的版本进行安装

1.2 下载 jpeg-turbo 源码

jpeg-turbo的github仓库网址: https://github.com/libjpeg-turbo/libjpeg-turbo
可以从中下载最新的master版本。

也可以从 https://github.com/libjpeg-turbo/libjpeg-turbo/releases 中下载历史版本

这里下载2.1.0版本得到文件"libjpeg-turbo-2.1.0.zip"

解压到文件夹"D:\test\libjpeg-turbo-2.1.0"
新建文件夹"D:\test\libjpeg-turbo-2.1.0\build",作为后续的编译文件夹
新建文件夹"D:\test\jpeg-turbo_install",作为安装文件夹

1.3 cmake 配置编译工程

这里用 cmake-gui,本质就是cmake,只是方便查看设置的选项。

如下设置源码文件夹、编译文件夹、安装文件夹


image

1.4 打开工程编译安装

依次点击"Generate"和"Open Project",打开编译工程。

如下编译"INSTALL"工程


image

编译完成之后,就在指定安装路径下生成了如下文件,如此便完成了jpeg-turbo的编译安装


image

2. 使用

2.1 通过 cmake 进行 jpeg-turbo 库引用的关键语句

写在CMakeLists.txt的关键语句如下:

set(libjpeg-turbo_DIR "D:/test/jpeg-turbo_install/lib/cmake/libjpeg-turbo")
find_package(libjpeg-turbo REQUIRED)

add_executable(${PROJECT_NAME} main.cpp) 
target_link_libraries(${PROJECT_NAME} libjpeg-turbo::turbojpeg-static)

这里是使用了静态库,使用其他形式可以去更改target_link_libraries的后面部分

2.2 压缩语句

bool Compress(unsigned char *data_uncompressed, unsigned char* &out_compressed, unsigned long *out_size, int width, int height, int pixel_format)
{
    if (nullptr == data_uncompressed) {
        return false;
    }

    tjhandle compressor = tjInitCompress();
    if (nullptr == compressor) {
        return false;
    }

    //pixel_format : TJPF::TJPF_BGR or other
    const int JPEG_QUALITY = 75;
    int pitch = tjPixelSize[pixel_format] * width;
    int status = tjCompress2(compressor, data_uncompressed, width, pitch, height, pixel_format,
        &out_compressed, out_size, TJSAMP_444, JPEG_QUALITY, TJFLAG_FASTDCT);
    if (status != 0) {
        tjDestroy(compressor);
        return false;
    }

    tjDestroy(compressor);
    return true;
}

2.3 解压缩语句

bool Decompress(uint8_t* data_compressed, uint32_t size, uint8_t* &out_uncompressed, int pixel_format) 
{
    if (nullptr == data_compressed || nullptr == out_uncompressed) {
        return false;
    }

    int width = 0, height = 0, jpegsubsamp = 0;
    void* decompressor = tjInitDecompress();
    if (nullptr == decompressor) {
        return false;
    }

    if (tjDecompressHeader2(decompressor, data_compressed, size, &width, &height, &jpegsubsamp) != 0) {
        tjDestroy(decompressor);
        return false;
    }

    // pixel_format : TJPF::TJPF_BGR or other
    int pitch = tjPixelSize[pixel_format] * width;
    uint32_t len = pitch * height;
    int flags = TJFLAG_FASTDCT;
    int status = tjDecompress2(decompressor, data_compressed, len, out_uncompressed, width, pitch, 
        height, pixel_format, flags);
    if ( status != 0) {
        tjDestroy(decompressor);
        return false;
    }

    tjDestroy(decompressor);
    return true;
}

2.4 测试程序

int main()
{
    cv::Mat image = cv::imread("../lena.jpg");

    unsigned char *data_uncompressed = image.data;
    unsigned char* out_compressed = nullptr;
    unsigned long out_size = 0;
    int pixel_format = TJPF::TJPF_BGR;
    int width = image.cols;
    int height = image.rows;
    int channels = image.channels();
    bool status = Compress(data_uncompressed, out_compressed, &out_size, width, height, pixel_format);
    if (false == status) {
        std::cout << "Compress failed" << std::endl;
    }

    uint8_t* out_uncompressed = new uint8_t[width*height*channels];

    status = Decompress(out_compressed, out_size, out_uncompressed, pixel_format);
    if (false == status) {
        std::cout << "Decompress failed" << std::endl;
    }

    cv::Mat image_decompress(height, width, CV_8UC3, out_uncompressed);
    cv::imshow("out_uncompressed", image_decompress);
    cv::waitKey(0);

    delete[] out_compressed;
    delete[] out_uncompressed;

    return 0;
}

上面的程序压缩了lena.jpg图片,然后再解压。
可以调试查看是否压缩成功和是否解压成原图。

2.5 以上程序汇总

2.5.1 CMakeLists.txt

cmake_minimum_required(VERSION 3.6) 
project(demo_jpeg-turbo)

#---------------------------------------------------------------------------------------
# jpeg turbo
#---------------------------------------------------------------------------------------
set(libjpeg-turbo_DIR "D:/test/jpeg-turbo_install/lib/cmake/libjpeg-turbo")
find_package(libjpeg-turbo REQUIRED)

#---------------------------------------------------------------------------------------
# OpenCV
#---------------------------------------------------------------------------------------
find_package( OpenCV REQUIRED )

#---------------------------------------------------------------------------------------
# ${PROJECT_NAME}
#---------------------------------------------------------------------------------------
add_executable(${PROJECT_NAME} main.cpp) 
target_link_libraries(${PROJECT_NAME} libjpeg-turbo::turbojpeg-static)
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS} )


## set ${PROJECT_NAME} as startup project
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})

2.5.2 main.cpp


#include <iostream>
#include "opencv2/opencv.hpp"
#include <turbojpeg.h>


bool Compress(unsigned char *data_uncompressed, unsigned char* &out_compressed, unsigned long *out_size, int width, int height, int pixel_format)
{
    if (nullptr == data_uncompressed) {
        return false;
    }

    tjhandle compressor = tjInitCompress();
    if (nullptr == compressor) {
        return false;
    }

    //pixel_format : TJPF::TJPF_BGR or other
    const int JPEG_QUALITY = 75;
    int pitch = tjPixelSize[pixel_format] * width;
    int status = tjCompress2(compressor, data_uncompressed, width, pitch, height, pixel_format,
        &out_compressed, out_size, TJSAMP_444, JPEG_QUALITY, TJFLAG_FASTDCT);
    if (status != 0) {
        tjDestroy(compressor);
        return false;
    }

    tjDestroy(compressor);
    return true;
}

bool Decompress(uint8_t* data_compressed, uint32_t size, uint8_t* &out_uncompressed, int pixel_format) 
{
    if (nullptr == data_compressed || nullptr == out_uncompressed) {
        return false;
    }

    int width = 0, height = 0, jpegsubsamp = 0;
    void* decompressor = tjInitDecompress();
    if (nullptr == decompressor) {
        return false;
    }

    if (tjDecompressHeader2(decompressor, data_compressed, size, &width, &height, &jpegsubsamp) != 0) {
        tjDestroy(decompressor);
        return false;
    }

    // pixel_format : TJPF::TJPF_BGR or other
    int pitch = tjPixelSize[pixel_format] * width;
    uint32_t len = pitch * height;
    int flags = TJFLAG_FASTDCT;
    int status = tjDecompress2(decompressor, data_compressed, len, out_uncompressed, width, pitch, 
        height, pixel_format, flags);
    if ( status != 0) {
        tjDestroy(decompressor);
        return false;
    }

    tjDestroy(decompressor);
    return true;
}

int main()
{
    cv::Mat image = cv::imread("../lena.jpg");

    unsigned char *data_uncompressed = image.data;
    unsigned char* out_compressed = nullptr;
    unsigned long out_size = 0;
    int pixel_format = TJPF::TJPF_BGR;
    int width = image.cols;
    int height = image.rows;
    int channels = image.channels();
    bool status = Compress(data_uncompressed, out_compressed, &out_size, width, height, pixel_format);
    if (false == status) {
        std::cout << "Compress failed" << std::endl;
    }

    uint8_t* out_uncompressed = new uint8_t[width*height*channels];

    status = Decompress(out_compressed, out_size, out_uncompressed, pixel_format);
    if (false == status) {
        std::cout << "Decompress failed" << std::endl;
    }

    cv::Mat image_decompress(height, width, CV_8UC3, out_uncompressed);
    cv::imshow("out_uncompressed", image_decompress);
    cv::waitKey(0);

    delete[] out_compressed;
    delete[] out_uncompressed;

    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 223,924评论 6 521
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,776评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 170,954评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,576评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,589评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,105评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,480评论 3 427
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,457评论 0 278
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,998评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,026评论 3 343
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,174评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,812评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,502评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,979评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,101评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,674评论 3 380
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,204评论 2 363

推荐阅读更多精彩内容