libjpeg-turbo介绍及测试代码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fengbingchun/article/details/89715416

很多年之前在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;
}

执行结果如下:

GitHub: https://github.com/fengbingchun/OCR_Test 

猜你喜欢

转载自blog.csdn.net/fengbingchun/article/details/89715416