openjpeg:jpeg2000(j2k)图像内存压缩编码

上一篇博文实现了[《jpeg2000(j2k)图像编码解码:c++实现openjpeg内存流接口(memory stream)》]1中实现了openjpeg的memory stream接口,本文介绍如何用memory stream实现jpeg2000图像的内存压缩。

create opj_image_t

openjpeg在处理图像是有定义自己的图像描述结构opj_image_t,如果要对内存的中的图像进行压缩,就必须首先将内存图像数据转换成opj_image_topj_image_t中每个颜色通道的数据独立存储在opj_image_t.comps数组中。
image_matrix_param是我在《libjpeg:实现jpeg内存解压缩塈转换色彩空间/压缩分辨率》中定义的内存图像描述结构)每个像素所有通道的颜色值连续),下面的代码就是实现从image_matrix_param创建一个opj_image_t对象(代码实现参考了openjpeg/src/bin/jp2/convert.c中的bmptoimage函数)

/* openjpeg编码解码异常类 */
class opj_exception:public std::logic_error{
public:
	// 继承基类构造函数
	using std::logic_error::logic_error;
};
/* 从image_matrix_param创建 opj_image_t
 * 失败则抛出 opj_exception 异常
 */
opj_image_t* opj_image_create_from_matrix(const image_matrix_param& matrix, opj_cparameters_t* parameters) {
	if(nullptr==parameters)
		throw std::invalid_argument(string(__FILE__).append(" line:" + __LINE__).append("parameters is null"));
	auto subsampling_dx = parameters->subsampling_dx;
	auto subsampling_dy = parameters->subsampling_dy;
	auto color_space = jpeglib_to_opj_color_space(matrix.color_space);
	opj_image_cmptparm_t cmptparm[4]; /* maximum of 4 components */
	opj_image_t * image = nullptr;
	/* initialize image components */
	if(matrix.channels>4||0==matrix.channels)
		throw std::invalid_argument(string(__FILE__).append(" line:" + __LINE__).append("matrix.channels must be 1/2/3/4"));
	memset(cmptparm, 0, matrix.channels * sizeof(opj_image_cmptparm_t));
	for (auto i = matrix.channels; i >0; --i) {
		cmptparm[i-1].prec = 8;
		cmptparm[i-1].bpp = 8;
		cmptparm[i-1].sgnd = 0;
		cmptparm[i-1].dx = (OPJ_UINT32) (subsampling_dx);
		cmptparm[i-1].dy = (OPJ_UINT32) (subsampling_dy);
		cmptparm[i-1].w = (OPJ_UINT32) (matrix.width);
		cmptparm[i-1].h = (OPJ_UINT32) (matrix.height);
	}
	/* create the image */
	if (nullptr == (image = opj_image_create((OPJ_UINT32) (matrix.channels), cmptparm, color_space)))
		throw opj_exception("failed to create image: opj_image_create\n");
	/* set image offset and reference grid */
	image->x0 = (OPJ_UINT32) (parameters->image_offset_x0);
	image->y0 = (OPJ_UINT32) (parameters->image_offset_y0);
	image->x1 = image->x0 + (OPJ_UINT32) ((matrix.width - 1)) * (OPJ_UINT32) (subsampling_dx) + 1;
	image->y1 = image->y0 + (OPJ_UINT32) ((matrix.height - 1)) * (OPJ_UINT32) (subsampling_dy) + 1;
	auto index = 0;
	uint8_t*scanline,*pixel;
	decltype(matrix.height) y;
	decltype(matrix.width) x;
	decltype(matrix.channels) ch;
	auto row_stride=get_row_stride(matrix);
	//将image_matrix_param中按像素连续存储的通道数据依照opj_image_t的格式拆开到不同的comps中
	for (y = 0; y <matrix.height; ++y) {
		scanline = const_cast<uint8_t*>(matrix.pixels.data())+ matrix.channels * row_stride * y;
		for (x = 0; x <matrix.width ; ++x) {
			pixel = scanline+matrix.channels * x;
			for (ch = 0; ch < matrix.channels; ++ch) {
				//image->comps[ch].data[index] = pixel[matrix.channels - ch];
				image->comps[ch].data[index] = (OPJ_INT32)pixel[ch];
			}
			++index;
		}
	}
	return image;
}

save_j2k

下面的代码将一个opj_image_t对象的图像数据压缩成jpeg2000格式输出到指定的opj_stream_interface对象

/**
 error callback expecting a FILE* client object
 */
void error_callback(const char* msg, void* client_data) {
//	(void) (client_data);
	fprintf(stdout, "[ERROR] %s", msg);
	throw opj_exception(msg);
}

/**
 warning callback expecting a FILE* client object
 */
void warning_callback(const char* msg, void* client_data) {
//	(void) (client_data);
	fprintf(stdout, "[WARNING] %s", msg);
}

/**
 debug callback expecting no client object
 */
void info_callback(const char* msg, void* client_data) {
#ifndef NDEBUG
//	(void) (client_data);
	fprintf(stdout, "[INFO] %s", msg);
#endif
}
// 将opj_image_t对象的图像数据压缩成jpeg2000格式输出到opj_stream_interface对象
void save_j2k(opj_image_t* image, opj_cparameters_t *parameters ,opj_stream_interface& dest) {
	if(nullptr==image)
		throw std::invalid_argument(string(__FILE__).append(" line:" + __LINE__).append("image is null"));
	if(nullptr==parameters)
		throw std::invalid_argument(string(__FILE__).append(" line:" + __LINE__).append("parameters is null"));
	bool set_comment = false;
	/* Create comment for codestream */
	if (nullptr == parameters->cp_comment) {
		const char comment[] = "Created by OpenJPEG version ";
		const size_t clen = strlen(comment);
		const char* version = opj_version();
		parameters->cp_comment = (char*) (malloc(clen + strlen(version) + 1));
		sprintf(parameters->cp_comment, "%s%s", comment, version);
		set_comment = true;
	}
	opj_codec_t* l_codec = opj_create_compress((CODEC_FORMAT)parameters->cod_format);
	/* catch events using our callbacks and give a local context */
	opj_set_info_handler(l_codec, info_callback, 00);
	opj_set_warning_handler(l_codec, warning_callback, 00);
	opj_set_error_handler(l_codec, error_callback, 00);
	opj_setup_encoder(l_codec, parameters, image);
	opj_stream_t* l_stream = opj_stream_create_default_si(dest);
	gdface::raii guard([&]() {
		/* close and free the byte stream */
		opj_stream_destroy(l_stream);
		/* free remaining compression structures */
		opj_destroy_codec(l_codec);
		if (set_comment)
		free(parameters->cp_comment);
	});
	/* encode the image */
	if (!opj_start_compress(l_codec, image, l_stream))
		throw opj_exception("failed to encode image: opj_start_compress");

	if (!opj_encode(l_codec, l_stream))
		throw opj_exception("failed to encode image: opj_encode");

	if (!opj_end_compress(l_codec, l_stream))
		throw opj_exception("failed to encode image: opj_end_compress");
}

// 将一个image_matrix_param数据压缩成jpeg2000格式输出到opj_stream_interface对象
// 默认压缩质量100
// 默认压缩格式为OPJ_CODEC_JP2
void save_j2k(const image_matrix_param& matrix, opj_stream_interface& dest, const unsigned int quality=100, OPJ_CODEC_FORMAT format=OPJ_CODEC_JP2) {
	opj_cparameters_t parameters;
	/* set encoding parameters to default values */
	opj_set_default_encoder_parameters(&parameters);
	// 设置压缩图像质量参数
	parameters.tcp_numlayers=1;
	parameters.tcp_distoratio[0]=(float)(quality>100?100:quality);
	parameters.cp_fixed_quality=1;
	parameters.cod_format=format;
	gdface::raii_var<opj_image_t*> raii_image([&]() {
		// 调用opj_image_create_from_matrix创建opj_image_t对象
		return opj_image_create_from_matrix(matrix, &parameters);
	}, [](opj_image_t*image) {
		/* free image data */		
		opj_image_destroy (image);
	});
	save_j2k(*raii_image, &parameters,dest);
}
// 将一个image_matrix_param数据压缩成jpeg2000格式输出到内存流对象(opj_stream_mem_output)
// 默认压缩质量100
// 默认压缩格式为OPJ_CODEC_JP2
// 返回opj_stream_mem_output 对象
opj_stream_mem_output save_j2k_mem(const image_matrix_param& matrix, const unsigned int quality=100, OPJ_CODEC_FORMAT format=OPJ_CODEC_JP2) {
	opj_stream_mem_output dest;
	save_j2k(matrix, dest,quality, format);
	return std::move(dest);
}

sample

#include <iostream>
#include <fstream>
#include <string>
#include <iostream>
#include "j2k_mem.h"
using namespace std;
void save_binary_file(const char *filename,const uint8_t *img, size_t size){
	std::ofstream ofs;
	ofs.open(filename, std::ofstream::binary);
	ofs.write((const char*)img, size);
	cout << filename << "saved,size=" << size << endl;
	ofs.close();
}
int main(){
	const char *input_jpg_file = "D:/tmp/example-1.jpg";
	const char *output4_jpg_file = "D:/tmp/example-1-out4.j2k";

	try{
		std::ifstream is (input_jpg_file, std::ifstream::binary);
		std::vector<uint8_t> jpeg_data;
		if (is) {
			// get length of file:
			is.seekg(0, is.end);
			auto length = is.tellg();
			is.seekg(0, is.beg);

			jpeg_data = std::vector<uint8_t>(length);
			// read data as a block:
			is.read((char*) jpeg_data.data(), jpeg_data.size());
			is.close();
		}
		image_matrix_param mat=load_jpeg_mem(jpeg_data);// 加载一个jpeg图像
		//将image_matrix_param 压缩成jp2k格式的内存数据,返opj_stream_mem_outputoutput对象
		opj_stream_mem_outputoutput=save_j2k_mem(mat,35,OPJ_CODEC_J2K);
		// 从opj_stream_mem_outputoutput取出压缩后的图像数据保存成.j2k文件以方便查看结果
		save_binary_file(output4_jpg_file,output.stream_data(),output.stream_length());
	}catch (exception &e){
		cout<<e.what()<<endl;
	}
}

上面例子中保存的.j2k文件,用普通的看图软件是无法识别的,需要用支持jpeg2000格式的图片浏览器(比如[XnView]2)才能查看压缩结果

2018/08/28补记:现在国产的免费看图软件就可以打开jpeg2000格式(jp2)的图像了,比如2345看图王.

下一篇该讲jpeg2000解码了
《openjpeg:jpeg2000(j2k)图像内存解压缩(解码)》

jpeg 编解码系列文章

《mingw(gcc)编译libjpeg-turbo》
《libjpeg:实现jpeg内存压缩暨error_exit错误异常处理和个性化参数设置》
《libjpeg:实现jpeg内存解压缩塈转换色彩空间/压缩分辨率》
《libjpeg:读取jpeg头获取图像基本信息(jpeg_read_header)》
《nmake(vs2015)命令行编译libjpeg-turbo(x86/amd64)》

jpeg2000 编码系列文章

《mingw(gcc)编译openjpeg》
《VS2015编译openjpeg(32/64位)》
《openjpeg:解决静态链接时未定义引用错误:undefined reference to `__imp_opj_xxxxxxx‘》
《jpeg2000(j2k)图像编码解码:c++实现openjpeg内存流接口(memory stream)》
《openjpeg:jpeg2000(j2k)图像内存压缩编码》
《openjpeg:jpeg2000(j2k)图像内存解压缩(解码)》
《cmake设置msvc的运行库(runtime library)塈指定openjpeg使用静态库》


  1. http://blog.csdn.net/10km/article/details/50606301 ↩︎

  2. http://www.xnview.com/en/ ↩︎

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值