前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >openjpeg:jpeg2000(j2k)图像内存解压缩(解码)

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

作者头像
10km
发布2019-05-25 22:41:24
1.7K0
发布2019-05-25 22:41:24
举报
文章被收录于专栏:10km的专栏10km的专栏

版权声明:本文为博主原创文章,转载请注明源地址。 https://cloud.tencent.com/developer/article/1433782

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

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

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

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

代码语言:javascript
复制
// 表达式判断为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中每个像素的各通道连续存储

代码语言:javascript
复制
/* 图像矩阵基本参数 */
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.height; ++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,部分代码是原样抄来的

调用样例

代码语言:javascript
复制
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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年02月04日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 完整代码
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档