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

  • 1
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 28
    评论
实现步骤如下: 1. 读入待压缩的图像,并将其转换为灰度图像。 2. 将灰度图像分解成多个子带,可以使用小波变换实现。 3. 对每个子带进行量化,将每个系数量化为一个离散值,以减少数据的精度。 4. 对量化后的数据进行熵编码,将其压缩成更小的数据集。 5. 将压缩后的数据集重新解码,得到压缩后的图像,并计算其PSNR。 6. 重复上述步骤,改变量化参数,得到不同bpp下的压缩结果与PSNR。 以下是一个简单的实现代码,其中量化参数和压缩比率可以根据需求进行调整: ```matlab % 读入待压缩的图像 img = imread('lena.png'); img = rgb2gray(img); % 定义量化参数 qstep = 10; % 定义不同bpp的压缩比率 bpp = [0.1 0.2 0.3 0.4 0.5]; % 将图像分解成多个子带 [c, S] = wavedec2(img, 4, 'db2'); % 对每个子带进行量化 for i = 1:length(c) c(i) = round(c(i) / (qstep * bpp(1))) * (qstep * bpp(1)); end % 对量化后的数据进行熵编码 bits = wmpcompress(c, S, 'db2'); % 将压缩后的数据集重新解码,得到压缩后的图像,并计算其PSNR comp_img = wmpdecompress(bits, S, 'db2'); psnr = psnr(img, comp_img); % 输出结果 disp(['bpp = ', num2str(bpp(1)), ', PSNR = ', num2str(psnr)]); % 重复上述步骤,改变量化参数和bpp,得到不同bpp下的压缩结果与PSNR for i = 2:length(bpp) % 对每个子带进行量化 for j = 1:length(c) c(j) = round(c(j) / (qstep * bpp(i))) * (qstep * bpp(i)); end % 对量化后的数据进行熵编码 bits = wmpcompress(c, S, 'db2'); % 将压缩后的数据集重新解码,得到压缩后的图像,并计算其PSNR comp_img = wmpdecompress(bits, S, 'db2'); psnr = psnr(img, comp_img); % 输出结果 disp(['bpp = ', num2str(bpp(i)), ', PSNR = ', num2str(psnr)]); end ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

10km

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

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

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

打赏作者

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

抵扣说明:

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

余额充值