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

来源:互联网 发布:光大炒股软件下载 编辑:程序博客网 时间:2024/05/16 11:32

上一篇博文实现了《jpeg2000(j2k)图像编码解码:c++实现openjpeg内存流接口(memory stream)》中实现了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_JP2void 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)才能查看压缩结果

0 0