很多年之前在https://blog.csdn.net/fengbingchun/article/details/10171583 中简单介绍过libjpeg-turbo的安装,因为libjpeg-turbo一直在维护更新,较之前有了些变化,这里再次整理下,并增加更多的测试代码。
libjpeg-turbo的主页为https://libjpeg-turbo.org/ ,GitHub地址为https://github.com/libjpeg-turbo/libjpeg-turbo ,最新分布版本为2.0.2。
libjpeg-turbo是一个JPEG图像编解码器,它使用SIMD指令(MMX, SSE2, AVX2, NEON, AltiVec)来加速x86, x86-64, ARM和PowerPC系统上baseline JPEG压缩和解压缩,在x86和x86-64系统上还支持渐进式(progressive) JPEG压缩。它的速度一般是libjpeg的2至6倍。在许多情况下,libjpeg-turbo的性能可以与专有的高速JPEG编解码器相媲美。
libjpeg-turbo既实现了传统的libjpeg API功能也有一些更直接但less powerful的TurboJPEG API。libjpeg-turbo还具有颜色空间扩展的特性,允许它压缩/解压缩到32位和大端(big-endian) 像素缓冲区(RGBX, XBGR等)。libjpeg-turbo也提供了功能齐全的Java接口。
因为libjpeg-turbo中有很多汇编文件,因此需要编译器支持汇编编译:
1. 在windows下通过vs编译:
可以从https://www.nasm.us/ 中下载最新的NASM稳定版2.14.02,即nasm-2.14.02-installer-x64.exe,这里下载安装的是64位的,也有对应32位的,安装到D:\ProgramFiles\NASM目录下,并将D:\ProgramFiles\NASM添加到系统环境变量中。将此目录下的nasm.exe和ndisasm.exe拷贝到C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin中,即对应的vs版本是vs2013。
从https://www.codeproject.com/Articles/410776/Integrating-a-compiler-assembler-in-VS-Using-NASM 点击下载” Download Property,Target,XML file - 3.8 KB”,即Target_Files_Collection.zip,解压缩,然后将解压后产生的3个文件nasm.props, nasm.targets, nasm.xml拷贝到C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\BuildCustomizations目录下,此目录下也有对应的masm的三个文件,masm.props, masm.targets, masm.xml。此3个配置文件用于控制自定义生成规则。
配置好后,打开任意一个工程:点击右键 --> 生成依赖项(B) --> 生成自定义(B),可找到nasm(.targets),勾选nasm(.targets)。再右键 --> 属性后,会发现多了一项Netwide Assmbler,如下图所示:
新建一个空工程libjpeg-turbo,用于生成静态库,但是将汇编文件加入到该工程中一直有问题,因此先将通过CMake生成工程turbojpeg-static编译生成的静态库用于测试。
2. 在Linux下通过GCC编译:
首先在ubuntu上安装nasm,有两种方式,一种是从https://www.nasm.us/pub/nasm/releasebuilds/2.14.02/ 下载nasm-2.14.02.zip通过源码安装,一种是直接通过执行”sudo apt-get install nasm”来安装。
build.sh内容如下:
#! /bin/bash
real_path=$(realpath $0)
dir_name=`dirname "${real_path}"`
echo "real_path: ${real_path}, dir_name: ${dir_name}"
data_dir="test_data"
if [ -d ${dir_name}/${data_dir} ]; then
rm -rf ${dir_name}/${data_dir}
fi
ln -s ${dir_name}/./../../${data_dir} ${dir_name}
new_dir_name=${dir_name}/build
mkdir -p ${new_dir_name}
cd ${new_dir_name}
echo "pos: ${new_dir_name}"
if [ "$(ls -A ${new_dir_name})" ]; then
echo "directory is not empty: ${new_dir_name}"
rm -r *
else
echo "directory is empty: ${new_dir_name}"
fi
# build libjpeg-turbo
libjpeg_turbo_path=${dir_name}/../../src/libjpeg-turbo
cd ${libjpeg_turbo_path}
mkdir build
cd build
cmake ..
make
ln -s ${libjpeg_turbo_path}/build/libturbojpeg.a ${new_dir_name}
cd -
cd ${new_dir_name}
cmake ..
make
cd -
CMakeList.txt内容如下:
PROJECT(Libjpeg-turbo_Test)
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
# support C++11
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# support C++14, when gcc version > 5.1, use -std=c++14 instead of c++1y
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O2")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O2")
MESSAGE(STATUS "project source dir: ${PROJECT_SOURCE_DIR}")
SET(PATH_TEST_FILES ${PROJECT_SOURCE_DIR}/./../../demo/Libjpeg-turbo_Test)
SET(PATH_SRC_LIBJPEG_TURBO_FILES ${PROJECT_SOURCE_DIR}/./../../src/libjpeg-turbo)
MESSAGE(STATUS "path libjpeg-turbo src files: ${PATH_SRC_LIBJPEG_TURBO_FILES}")
SET(PATH_OPENCV /opt/opencv3.4.2)
IF(EXISTS ${PATH_OPENCV})
MESSAGE(STATUS "Found OpenCV: ${PATH_OPENCV}")
ELSE()
MESSAGE(FATAL_ERROR "Can not find OpenCV in ${PATH_OPENCV}")
ENDIF()
# head file search path
INCLUDE_DIRECTORIES(
${PATH_TEST_FILES}
${PATH_SRC_LIBJPEG_TURBO_FILES}
${PATH_SRC_LIBJPEG_TURBO_FILES}/build
${PATH_OPENCV}/include
)
# find opencv library
FIND_LIBRARY(opencv_core NAMES opencv_core PATHS ${PATH_OPENCV}/lib NO_DEFAULT_PATH)
FIND_LIBRARY(opencv_imgproc NAMES opencv_imgproc PATHS ${PATH_OPENCV}/lib NO_DEFAULT_PATH)
FIND_LIBRARY(opencv_highgui NAMES opencv_highgui PATHS ${PATH_OPENCV}/lib NO_DEFAULT_PATH)
FIND_LIBRARY(opencv_imgcodecs NAMES opencv_imgcodecs PATHS ${PATH_OPENCV}/lib NO_DEFAULT_PATH)
FIND_LIBRARY(opencv_video NAMES opencv_video PATHS ${PATH_OPENCV}/lib NO_DEFAULT_PATH)
FIND_LIBRARY(opencv_videoio NAMES opencv_videoio PATHS ${PATH_OPENCV}/lib NO_DEFAULT_PATH)
FIND_LIBRARY(opencv_objdetect NAMES opencv_objdetect PATHS ${PATH_OPENCV}/lib NO_DEFAULT_PATH)
FIND_LIBRARY(opencv_ml NAMES opencv_ml PATHS ${PATH_OPENCV}/lib NO_DEFAULT_PATH)
MESSAGE(STATUS "opencv libraries: ${opencv_core} ${opencv_imgproc} ${opencv_highgui} ${opencv_imgcodecs} ${opencv_video}" ${opencv_videoio} ${opencv_objdetect} ${opencv_ml})
# recursive query match files :*.cpp
FILE(GLOB_RECURSE TEST_CPP_LIST ${PATH_TEST_FILES}/*.cpp)
# find library
FIND_LIBRARY(libturbojpeg NAMES turbojpeg PATHS ${PROJECT_SOURCE_DIR}/build NO_DEFAULT_PATH)
MESSAGE(STATUS "image libraries: ${libturbojpeg}")
# build executable program
ADD_EXECUTABLE(Libjpeg-turbo_Test ${TEST_CPP_LIST})
# add dependent library: static and dynamic
TARGET_LINK_LIBRARIES(Libjpeg-turbo_Test ${libturbojpeg} ${opencv_ml} ${opencv_core} ${opencv_imgproc} ${opencv_highgui} ${opencv_imgcodecs} ${opencv_video} ${opencv_videoio} ${opencv_objdetect} pthread)
测试代码如下:
#include <string>
#include <memory>
#include "funset.hpp"
#include <opencv2/opencv.hpp>
int parse_jpeg_file(const char* name)
{
FILE* infile = nullptr;
if ((infile = fopen(name, "rb")) == nullptr) {
fprintf(stderr, "can't open %s\n", name);
return -1;
}
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress(&cinfo);
/* Step 2: specify data source (eg, a file) */
jpeg_stdio_src(&cinfo, infile);
/* Step 3: read file parameters with jpeg_read_header() */
jpeg_read_header(&cinfo, TRUE);
fprintf(stdout, "image_width = %d\n", cinfo.image_width);
fprintf(stdout, "image_height = %d\n", cinfo.image_height);
fprintf(stdout, "num_components = %d\n", cinfo.num_components);
/* Step 4: set parameters for decompression */
cinfo.scale_num = 2;
cinfo.scale_denom = 4;
/* Step 5: Start decompressor */
jpeg_start_decompress(&cinfo);
fprintf(stdout, "output_width = %d\n", cinfo.output_width);
fprintf(stdout, "output_height = %d\n", cinfo.output_height);
fprintf(stdout, "output_components = %d\n", cinfo.output_components);
/* JSAMPLEs per row in output buffer */
int row_stride = cinfo.output_width * cinfo.output_components;
/* Make a one-row-high sample array that will go away when done with image */
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
/* Step 6: while (scan lines remain to be read) */
while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, buffer, 1);
}
/* Step 7: Finish decompression */
jpeg_finish_decompress(&cinfo);
/* Step 8: Release JPEG decompression object */
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return 0;
}
int write_jpeg_file(const unsigned char* data, int width, int height, int channels, J_COLOR_SPACE color_space, int quality, const char* name)
{
/* Step 1: allocate and initialize JPEG compression object */
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG compression object. */
jpeg_create_compress(&cinfo);
/* Step 2: specify data destination (eg, a file) */
FILE* outfile = nullptr;
if ((outfile = fopen(name, "wb")) == nullptr) {
fprintf(stderr, "can't open file: %s\n", name);
return -1;
}
jpeg_stdio_dest(&cinfo, outfile);
/* Step 3: set parameters for compression */
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = channels;
cinfo.in_color_space = color_space;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE);
/* Step 4: Start compressor */
jpeg_start_compress(&cinfo, TRUE);
/* Step 5: while (scan lines remain to be written) */
int row_stride = width * channels;
int line = 0;
JSAMPROW row_pointer[1];
while (line < cinfo.image_height) {
row_pointer[0] = (JSAMPROW)&data[line * row_stride];
jpeg_write_scanlines(&cinfo, row_pointer, 1);
++line;
}
/* Step 6: Finish compression */
jpeg_finish_compress(&cinfo);
fclose(outfile);
/* Step 7: release JPEG compression object */
jpeg_destroy_compress(&cinfo);
return 0;
}
int get_jpeg_compress_data(const unsigned char* data, int width, int height, int channels, J_COLOR_SPACE color_space, int quality, unsigned char** out_buffer, unsigned long out_buffer_size, unsigned long& free_in_buffer)
{
/* Step 1: allocate and initialize JPEG compression object */
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG compression object. */
jpeg_create_compress(&cinfo);
/* Step 2: specify data destination (eg, a file) */
jpeg_mem_dest(&cinfo, out_buffer, &out_buffer_size);
/* Step 3: set parameters for compression */
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = channels;
cinfo.in_color_space = color_space;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE);
/* Step 4: Start compressor */
jpeg_start_compress(&cinfo, TRUE);
/* Step 5: while (scan lines remain to be written) */
int row_stride = width * channels;
int line = 0;
JSAMPROW row_pointer[1];
while (line < cinfo.image_height) {
row_pointer[0] = (JSAMPROW)&data[line * row_stride];
jpeg_write_scanlines(&cinfo, row_pointer, 1);
++line;
}
/* Step 6: Finish compression */
jpeg_finish_compress(&cinfo);
free_in_buffer = cinfo.dest->free_in_buffer;
/* Step 7: release JPEG compression object */
jpeg_destroy_compress(&cinfo);
return 0;
}
int test_libjpeg_turbo()
{
#ifdef _MSC_VER
std::string image_path{ "E:/GitCode/OCR_Test/test_data/" };
#else
std::string image_path{ "test_data/" };
#endif
std::string name1 = image_path + "tirg.jpg";
parse_jpeg_file(name1.c_str());
std::string name2 = image_path + "lena.png";
std::string name3 = image_path + "lena.jpg";
int quality = 80;
cv::Mat mat = cv::imread(name2);
if (!mat.data || mat.channels() != 3) {
fprintf(stderr, "read image fail: %s\n", name2.c_str());
return -1;
}
write_jpeg_file(mat.data, mat.cols, mat.rows, mat.channels(), JCS_EXT_BGR, quality, name3.c_str()); // bgr data
name3 = image_path + "lena2.jpg";
cv::cvtColor(mat, mat, CV_BGR2RGB);
write_jpeg_file(mat.data, mat.cols, mat.rows, mat.channels(), JCS_RGB, quality, name3.c_str()); // rgb data
name3 = image_path + "lena3.jpg";
cv::cvtColor(mat, mat, CV_RGB2GRAY);
write_jpeg_file(mat.data, mat.cols, mat.rows, mat.channels(), JCS_GRAYSCALE, quality, name3.c_str()); // gray data
int length = mat.cols * mat.rows;
std::unique_ptr<unsigned char[]> data(new unsigned char[length]);
unsigned char* p = data.get();
unsigned long free_in_buffer;
get_jpeg_compress_data(mat.data, mat.cols, mat.rows, mat.channels(), JCS_GRAYSCALE, quality, &p, length, free_in_buffer);
name3 = image_path + "lena4.jpg";
FILE* outfile = nullptr;
if ((outfile = fopen(name3.c_str(), "wb")) == nullptr) {
fprintf(stderr, "can't open file: %s\n", name3.c_str());
return -1;
}
fwrite(data.get(), sizeof(unsigned char), length - free_in_buffer, outfile);
fclose(outfile);
return 0;
}
执行结果如下: