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

我的上一篇博客[《openjpeg:jpeg2000(j2k)图像内存压缩编码》]1详细讲述了调用openjpeg实现jpeg2000(j2k)图像的内存压缩过程。本文讲述如何调用openjpeg来将jpeg2000格式的内存图像数据解码。

因为有了[《jpeg2000(j2k)图像编码解码:c++实现openjpeg内存流接口(memory stream)》]2实现的内存流接口,解压缩过程相对就比较简单了。

以下是jpeg2000内存解码的实现代码

代码中用到的opj_stream_interface,opj_stream_mem_input都在[《jpeg2000(j2k)图像编码解码:c++实现openjpeg内存流接口(memory stream)》]2一文中实现。raii_var,raii对象在更早的[《C++11实现模板化(通用化)RAII机制》]3文中实现。
info_callback,warning_callback,error_callback这三个用于解码过程中错误处理的函数在也在[《openjpeg:jpeg2000(j2k)图像内存压缩编码》]1中有实现

// 表达式判断为true抛出invalid_argument异常
#define throw_if(expression,msg) \
	if(expression)\
		throw std::invalid_argument(std::string(__FILE__).append(" line:" + __LINE__).append(msg));
// 表达式判断为true抛出指定的except异常		
#define throw_except_if(except,expression,msg) \
	if(expression)\
		throw except(std::string(__FILE__).append(" line:" + __LINE__).append(msg));
#define throw_except_if_null(except,p) throw_except_if(except,nullptr==p,#p" is null")
#define throw_if_null(p) throw_if(nullptr==p,#p" is null")

/* 从流对象(opj_stream_interface)中根据parameters提供的解码参数解码jpeg2000图像
 * 返回 opj_image_t,出错则抛出opj_exception异常
 */
opj_image_t* load_j2k(opj_stream_interface& src, opj_dparameters_t& parameters) {
	opj_image_t* image = nullptr; /* Handle to a image */
	opj_codec_t* l_codec = nullptr; /* Handle to a decompressor */
	// 创建stream对象
	auto l_stream = opj_stream_create_default_si(src); /* Stream */
	auto decompress_ok = false;
	// RAII资源对象,对象析构时自动释放资源
	gdface::raii guard([&]() {
		/* close and free the byte stream */
		opj_stream_destroy(l_stream);
		/* free remaining compression structures */
		opj_destroy_codec(l_codec);
		if (!decompress_ok) {
			opj_image_destroy(image);
			image = nullptr;
		}
	});
	/* decode the JPEG2000 stream */
	/* ---------------------- */
	switch (parameters.decod_format) {
	case OPJ_CODEC_J2K: /* JPEG-2000 codestream */
	case OPJ_CODEC_JP2: /* JPEG 2000 compressed image data */
	case OPJ_CODEC_JPT: /* JPEG 2000, JPIP */
	{
		/* Get a decoder handle */
		l_codec = opj_create_decompress((OPJ_CODEC_FORMAT) parameters.decod_format);
		break;
	}
	default:
		throw opj_exception(string(__FILE__).append(" line:" + __LINE__).append("invalid decod_format"));
	}
	/* 设信息/警告/错误处理函数 */
	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_dparameters_t参数对象设置解码器的解码参数 */
	if (!opj_setup_decoder(l_codec, &parameters)) {
		throw opj_exception("ERROR -> opj_compress: failed to setup the decoder");
	}
	/* 读取图像格式头的基本信息*/
	if (!opj_read_header(l_stream, l_codec, &image)) {
		throw opj_exception("ERROR -> opj_decompress: failed to read the header");
	}
	if (!parameters.nb_tile_to_decode) {
		/* Optional if you want decode the entire image */
		if (!opj_set_decode_area(l_codec, image, (OPJ_INT32) (parameters.DA_x0), (OPJ_INT32) (parameters.DA_y0),
				(OPJ_INT32) (parameters.DA_x1), (OPJ_INT32) (parameters.DA_y1)))
			throw opj_exception("ERROR -> opj_decompress: failed to set the decoded area");

		/* Get the decoded image */
		if (!(opj_decode(l_codec, l_stream, image) && opj_end_decompress(l_codec, l_stream)))
			throw opj_exception("ERROR -> opj_decompress: failed to decode image!");
	} else {
		if (!opj_get_decoded_tile(l_codec, l_stream, image, parameters.tile_index))
			throw opj_exception("ERROR -> opj_decompress: failed to decode tile!");
#ifndef NDEBUG
		printf("tile %d is decoded!\n", parameters.tile_index);
#endif
	}
	decompress_ok = true;
	return image;
}
/* 从流对象(opj_stream_interface)中解码指定格式(format)的jpeg2000图像
 * 返回 opj_image_t,出错则抛出opj_exception异常
 */
opj_image_t* load_j2k(opj_stream_interface& src, OPJ_CODEC_FORMAT format) {
	opj_dparameters_t parameters;
	/* set decoding parameters to default values */
	opj_set_default_decoder_parameters(&parameters);
	parameters.decod_format = format;
	return load_j2k(src, parameters);
}

上面代码将内存图像解码返回解压后的opj_image_t对象,opj_image_t对象是按颜色通道存储每个像素的数据的,所以需要每像素颜色通道值连续存储,还需要做一些转换下面的代码将opj_image_t转换为image_matrix_param,image_matrix_param中每个像素的各通道连续存储

/* 图像矩阵基本参数 */
typedef struct _image_matrix_param{
        uint32_t		width;					// 图像宽度
        uint32_t		height;					// 图像高度
        uint8_t		channels;				// 通道数
        J_COLOR_SPACE color_space; // 图像数据的色彩空间
        uint8_t		align;	// 内存对齐方式 0为不对齐,>0为以2的n次幂对齐
        std::vector <uint8_t> pixels; // 图像数据
}image_matrix_param,*image_matrix_param_ptr;

inline uint32_t get_row_stride(const image_matrix_param&matrix){
	return matrix.align?(matrix.width+(1 << matrix.align)-1)>>matrix.align<<matrix.align:matrix.width;
}
/* 从opj_image_t 创建 image_matrix_param
 * 失败则抛出 opj_exception 异常
 */
image_matrix_param create_matrix_from_opj_image(opj_image_t* image) {
	throw_if_null(image);
	image_matrix_param matrix;
	throw_if(0 == image->numcomps, "image->numcomps must >0");
	matrix.width = image->comps[0].w;
	matrix.height = image->comps[0].h;
	// 检查参数合法性
	if (image->numcomps > 1)
		for (auto i = image->numcomps - 1; i > 0; --i) {
			throw_except_if(opj_exception,matrix.width != image->comps[i].w || matrix.height != image->comps[i].h||image->comps[i].prec>8||image->comps[i].bpp>8,
					"each components has different size");
		}

	matrix.channels = (uint8_t) (image->numcomps);
	matrix.color_space = opj_to_jpeglib_color_space(image->color_space);
	matrix.align = 0;
	auto row_stride = get_row_stride(matrix);
	// 为image_matrix_param分配图像存储空间,失败则抛出opj_exception
	try{
		matrix.pixels = std::vector<uint8_t>(row_stride * matrix.channels * matrix.height);
	}catch(exception &e){
		throw opj_exception(string(__FILE__).append(" line:" + __LINE__).append(e.what()));
	}catch(...){
		throw opj_exception(string(__FILE__).append(" line:" + __LINE__).append("fail to constructe std::vector"));
	}
	uint32_t index = 0;
	uint8_t* scanline,*pixel;
	decltype(matrix.height) y;
	decltype(matrix.width) x;
	decltype(matrix.channels) ch;
	for ( y = 0; y < matrix.height; ++y ) {
		scanline = 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) {
				pixel[ch] = (uint8_t) (image->comps[ch].data[index]);
			}
			++index;
		}
	}
	return std::move(matrix);
}
/* 从jpeg_data和size指定的内存数据中解码指定格式(format)的jpeg2000图像
 * 返回 image_matrix_param对象,出错则抛出opj_exception异常
 */
image_matrix_param load_j2k(const uint8_t* jpeg_data, size_t size, OPJ_CODEC_FORMAT format) {
	throw_if_null(jpeg_data)
	throw_if(0 == size, "jpeg_data is empty")
	opj_stream_mem_input src(jpeg_data, size);
	gdface::raii_var<opj_image_t*> raii_image([&]() {
		return load_j2k(src, format);
	}, [](opj_image_t* image) {
		/* free image data */
		opj_image_destroy(image);
	});
	return create_matrix_from_opj_image(*raii_image);
}
/* 从jpeg_data指定的内存数据中解码指定格式(format)的jpeg2000图像
 * 返回 image_matrix_param对象,出错则抛出opj_exception异常
 */
image_matrix_param load_j2k(const std::vector<uint8_t>&jpeg_data, OPJ_CODEC_FORMAT format){
	return load_j2k(jpeg_data.data(),jpeg_data.size(),format);
}

代码实现参考了openjpeg的源码/src/bin/jp2/opj_decompress.c,部分代码是原样抄来的

调用样例

std::vector<uint8_t> load_binary_file(const char *input_jpg_file){
	std::vector<uint8_t> jpeg_data;
	std::ifstream is (input_jpg_file, std::ifstream::binary);
	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();
	}
	return std::move(jpeg_data);
}
int main()
{
	try {
		const char *output4_jpg_file = "D:/tmp/sample-1-out4.j2k";
		auto j2k_data=load_binary_file(output4_jpg_file);
		load_j2k(j2k_data,OPJ_CODEC_J2K);
	}catch (exception &e){
		cout<<e.what()<<endl;
	}
    return 0;
}

完整代码

完整代码参见gitee仓库:

https://gitee.com/l0km/jpegwrapper

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/50607008 ↩︎ ↩︎

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

  3. http://blog.csdn.net/10km/article/details/49847271 ↩︎

Changes from version 2.2.2 to version 2.2.3 ------------------------------------------- * Extremely minor changes to avoid mixed use of formatted and unformatted calls to "ostream" objects. These appear to excite a bug in GCC version 3.0. The only file affected is "params.cpp" in "coresys/parameters". Changes from version 2.2.1 to version 2.2.2 ------------------------------------------- Note: none of these have any impact whatsoever on executable code or DLL's. * Renamed the "core" directory as "coresys". A trivial change and my appologies for those whom this might adversely affect. However, the use of the name "core" was causing some people difficulties, since it is identical to the name of a Unix core dump file. * Made the Linux MMX-optimized functions "extern C" instead of "extern", so as to avoid problems caused by different name mangling conventions between various versions of gcc. * Eliminated multi-line comments from assembler files so as to avoid problems created by earlier versions of the gnu assembler. Changes from version 2.2 to version 2.2.1 ----------------------------------------- * Replaced the C++ I/O routines used for image and compressed data transfers with ANSI C I/O functions. This was motivated by the fact that the new-style ANSI C++ I/O package is unbelievably slow, at least as implemented in Microsoft's Visual C++ compiler. The change has no impact whatsoever on the Kakadu core system; it affects only the implementation of a few application level objects -- the core system abstracts all such I/O considerations through interface classes which are implemented by applications. Everything now runs a little faster than it did in version 2.1 and quite a bit faster than it did in the first release of version 2.2. * Made provision for compiling under versions of GCC prior to version 3.0. To use this, you should define GCC_VERSION_LESS_THAN_3. Changes from version 2.1 to version 2.2 --------------------------------------- * Extensive support for ROI (Region of Interest) specification at encode time (see "kakadu.pdf" for more on this). * Migrated from the old-style C++ iostream package to the new standard iostream package -- minimal use of "using namespace std" and never used in common header files, so this should enhance namespace protection and portability. * Added AT&T style versions of the small amount of Pentium assembly code to enable compilation of a high speed version under GCC as well as MSVC. * Some minor bug fixes. Changes from version 2.0.2 to version 2.1 ----------------------------------------- * Extensive support for working with the JP2 file format. The "kdu_show" application demonstrates the capabilities required of a conformant JP2 reader: palette mapping; interpolation of components to a common resolution; application of registration offsets in the CRG marker segment; and colour conversion to an appropriate rendering space (sRGB here). The "kdu_region_decompressor" object provides extensive support for general purpose interactive rendering applications, performing all of the above tasks in a platform independent manner. * It is now possible to directly control rate-distortion slope thresholds used in the construction of quality layers. This capability may also be used to significantly increase compression speed, if a suitable threshold is known, since the encoder then incrementally predicts the point at which there is no point in coding further coding passes. * A number of improvements to the "kdu_show" application, including the ability to arbitrarily zoom into images. * A number of minor bug fixes, including one important bug reported by Aaron Deever of Kodak, and a bug which occasionally manifested itself in the incremental rate prediction heuristic (reported by Jim Andrew of CISRA). * Improved documentation. Changes from version 2.0.1 to version 2.02 ------------------------------------------ * A PDF document (documentation.pdf) has been prepared to guide the new user into some of the more important aspects of the Kakadu system. The first draft is included here. * A very simple compression example and a very simple decompression example have been added to assist developers in familiarizing themselves with the Kakadu system -- the existing demo apps provide perhaps too much functionality to be quickly understood. * A full BIBO (Bounded Input Bounded Output) numerical analysis of the DWT and other processing steps is used to establish the best utilization of limited precision sample data representations. The new version should not be able to fall prey to numerical overflow or underflow difficulties under any circumstances (this could just have been possible with the last version). It also provides slightly higher accuracy. * The automatic heuristic for generating quality layer rate targets has been substantially improved. * A number of minor bugs/limitations were corrected, although these had not manifested themselves in any real examples. Changes from version 2.0 to version 2.01 ---------------------------------------- * One line change in each of "kdu_expand.cpp" and "kdu_compress.cpp" to correct a minor rare bug in these demo applications. * Minor changes in "kdu_show.cpp" to correct a rare bug and improve the user interface in regard to image rotation. * Four lines added to each of "encoder.cpp" and "decoder.cpp" to fix a minor memory leak.
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

10km

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

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

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

打赏作者

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

抵扣说明:

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

余额充值