I added CDEF box reading and writing to OpenJpeg. There is a
new field in opj_image_comp called comp_type (red, green, y, opacity,
etc...).
If jp2 image contains cdef box comp_type is set accordigly during decoding.
If you want to have cdef box in encoded image you must set proper
channel type
for each image component (otherwise cdef is not written).
Another problem was that METH field of COLR box was only set to
enumerated colorspace for images with 1 and 3 components.
If you saved jp2 image with OpenJpeg and included alpha channel most
image editors/viewers
couldn't opened it because of the METH value. With the proper METH they
opened it but
didn't recognize alpha channel because of the missing CDEF. These two
problems are resolved with
this patch.
But there is another problem when saving multichannel images:
When image is encoded (using lossy compression, lossless is ok),
all values of additional components (first three are ok) are somehow set
to 128.
Someone made a post about it some time ago:
http://groups.google.com/group/openjpeg/browse_frm/thread/bb402110a70...
It looks like it is somehow related to compiler or its settings. I get
this behaviour
with GCC on both Windows and Linux. Strange thing is: I use C++ Builder to
compile OpenJpeg to object files and the link them to Delphi application.
Encoding works ok in Delphi app but when I use those same object files
in C++
application the problem is back.
Another thing is it didn't happen with older versions of OpenJpeg. It
somehow
appeared between 1.0 and 1.2.
Anyone has any idea what can cause this?
[
cdef_svn507.patch 13K ]
Index: image.c
===================================================================
--- image.c (revision 507)
+++ image.c (working copy)
@@ -58,6 +58,7 @@
comp->prec = cmptparms[compno].prec;
comp->bpp = cmptparms[compno].bpp;
comp->sgnd = cmptparms[compno].sgnd;
+ comp->comp_type = cmptparms[compno].comp_type;
comp->data = (int*) opj_calloc(comp->w * comp->h, sizeof(int));
if(!comp->data) {
fprintf(stderr,"Unable to allocate memory for image.\n");
Index: jp2.c
===================================================================
--- jp2.c (revision 507)
+++ jp2.c (working copy)
@@ -34,6 +34,27 @@
/** @defgroup JP2 JP2 - JPEG-2000 file format reader/writer */
/*@{*/
+/* COLR Box colorspace types. */
+
+#define JP2_COLR_SRGB 16
+#define JP2_COLR_GRAY 17
+#define JP2_COLR_SYCC 18
+
+/* CDEF Box channel types. */
+
+#define JP2_CDEF_RGB_R 1
+#define JP2_CDEF_RGB_G 2
+#define JP2_CDEF_RGB_B 3
+
+#define JP2_CDEF_YCC_Y 1
+#define JP2_CDEF_YCC_CB 2
+#define JP2_CDEF_YCC_CR 3
+
+#define JP2_CDEF_GRAY_Y 1
+
+#define JP2_CDEF_TYPE_COLOR 0
+#define JP2_CDEF_TYPE_OPACITY 1
+
/** @name Local static functions */
/*@{*/
@@ -58,6 +79,8 @@
static bool jp2_read_bpcc(opj_jp2_t *jp2, opj_cio_t *cio);
static void jp2_write_colr(opj_jp2_t *jp2, opj_cio_t *cio);
static bool jp2_read_colr(opj_jp2_t *jp2, opj_cio_t *cio);
+static void jp2_write_cdef(opj_jp2_t *jp2, opj_cio_t *cio);
+static bool jp2_read_cdef(opj_jp2_t *jp2, opj_cio_t *cio);
/**
Write the FTYP box - File type box
@param jp2 JP2 handle
@@ -111,7 +134,7 @@
else if (box->length == 0) {
box->length = cio_numbytesleft(cio) + 8;
}
-
+
return true;
}
@@ -289,12 +312,146 @@
}
if (cio_tell(cio) - box.init_pos != box.length) {
- opj_event_msg(cinfo, EVT_ERROR, "Error with BPCC Box\n");
+ opj_event_msg(cinfo, EVT_ERROR, "Error with COLR Box\n");
return false;
}
return true;
}
+static void jp2_write_cdef(opj_jp2_t *jp2, opj_cio_t *cio) {
+ opj_jp2_box_t box;
+ int i, type, assoc;
+
+ for (i = 0; i < jp2->numcomps; i++) {
+ /* don't write cdef if any comp type is not defined */
+ if (jp2->comps[i].comp_type <= COMPTYPE_UNKNOWN ||
+ jp2->comps[i].comp_type > COMPTYPE_OPACITY) {
+ return;
+ }
+ }
+
+ box.init_pos = cio_tell(cio);
+ cio_skip(cio, 4);
+ cio_write(cio, JP2_CDEF, 4); /* CDEF */
+
+ cio_write(cio, jp2->numcomps, 2); /* number of comps */
+ for (i = 0; i < jp2->numcomps; i++) {
+ cio_write(cio, i, 2); /* channel number */
+
+ type = JP2_CDEF_TYPE_COLOR;
+ assoc = 0;
+
+ switch (jp2->comps[i].comp_type) {
+ case COMPTYPE_UNKNOWN:
+ break;
+ case COMPTYPE_R:
+ assoc = JP2_CDEF_RGB_R;
+ break;
+ case COMPTYPE_G:
+ assoc = JP2_CDEF_RGB_G;
+ break;
+ case COMPTYPE_B:
+ assoc = JP2_CDEF_RGB_B;
+ break;
+ case COMPTYPE_Y:
+ assoc = jp2->enumcs == JP2_COLR_SYCC ? JP2_CDEF_YCC_Y : JP2_CDEF_GRAY_Y;
+ break;
+ case COMPTYPE_CB:
+ assoc = JP2_CDEF_YCC_CB;
+ break;
+ case COMPTYPE_CR:
+ assoc = JP2_CDEF_YCC_CR;
+ break;
+ case COMPTYPE_OPACITY:
+ type = JP2_CDEF_TYPE_OPACITY;
+ break;
+ }
+
+ cio_write(cio, type, 2); /* channel type */
+ cio_write(cio, assoc, 2); /* color associated with channel */
+ }
+
+ box.length = cio_tell(cio) - box.init_pos;
+ cio_seek(cio, box.init_pos);
+ cio_write(cio, box.length, 4);
+ cio_seek(cio, box.init_pos + box.length);
+}
+
+static bool jp2_read_cdef(opj_jp2_t *jp2, opj_cio_t *cio) {
+ opj_jp2_box_t box;
+ int i, oldpos, numchannels, no, type, assoc;
+ opj_jp2_comps_t *comp;
+
+ opj_common_ptr cinfo = jp2->cinfo;
+
+ /* set default component type */
+ for (i = 0; i < jp2->numcomps; i++) {
+ jp2->comps[i].comp_type = COMPTYPE_UNKNOWN;
+ }
+
+ /* try to read CDEF box */
+ oldpos = cio_tell(cio);
+ jp2_read_boxhdr(cinfo, cio, &box);
+
+ if (JP2_CDEF != box.type) {
+ cio_seek(cio, oldpos);
+ return false;
+ }
+
+ numchannels = cio_read(cio, 2);
+
+ for (i = 0; i < numchannels; i++) {
+ no = cio_read(cio, 2);
+ type = cio_read(cio, 2);
+ assoc = cio_read(cio, 2);
+ comp = &jp2->comps[no];
+
+ /* set comp type according to channel type and color association */
+ if (type == JP2_CDEF_TYPE_OPACITY && assoc == 0) {
+ comp->comp_type = COMPTYPE_OPACITY;
+ } else if (type == JP2_CDEF_TYPE_COLOR && assoc >= 1 && assoc <= 65534) {
+ switch (jp2->enumcs) {
+ case JP2_COLR_SRGB:
+ switch (assoc) {
+ case JP2_CDEF_RGB_R:
+ comp->comp_type = COMPTYPE_R;
+ break;
+ case JP2_CDEF_RGB_G:
+ comp->comp_type = COMPTYPE_G;
+ break;
+ case JP2_CDEF_RGB_B:
+ comp->comp_type = COMPTYPE_B;
+ break;
+ }
+ break;
+ case JP2_COLR_SYCC:
+ switch (assoc) {
+ case JP2_CDEF_YCC_Y:
+ comp->comp_type = COMPTYPE_Y;
+ break;
+ case JP2_CDEF_YCC_CB:
+ comp->comp_type = COMPTYPE_CB;
+ break;
+ case JP2_CDEF_YCC_CR:
+ comp->comp_type = COMPTYPE_CR;
+ break;
+ }
+ break;
+ case JP2_COLR_GRAY:
+ if (assoc == JP2_CDEF_GRAY_Y)
+ comp->comp_type = COMPTYPE_Y;
+ break;
+ }
+ }
+ }
+
+ if (cio_tell(cio) - box.init_pos != box.length) {
+ opj_event_msg(cinfo, EVT_ERROR, "Error with CDEF Box\n");
+ return false;
+ }
+ return true;
+}
+
void jp2_write_jp2h(opj_jp2_t *jp2, opj_cio_t *cio) {
opj_jp2_box_t box;
@@ -308,6 +465,7 @@
jp2_write_bpcc(jp2, cio);
}
jp2_write_colr(jp2, cio);
+ jp2_write_cdef(jp2, cio);
box.length = cio_tell(cio) - box.init_pos;
cio_seek(cio, box.init_pos);
@@ -343,6 +501,8 @@
if (!jp2_read_colr(jp2, cio))
return false;
+ jp2_read_cdef(jp2, cio);
+
skip_len = box.init_pos + box.length - cio_tell(cio);
if (skip_len < 0) {
opj_event_msg(cinfo, EVT_ERROR, "Error with JP2H Box\n");
@@ -544,6 +704,7 @@
opj_image_t* jp2_decode(opj_jp2_t *jp2, opj_cio_t *cio, opj_codestream_info_t *cstr_info) {
opj_common_ptr cinfo;
opj_image_t *image = NULL;
+ int i;
if(!jp2 || !cio) {
return NULL;
@@ -564,16 +725,21 @@
return NULL;
}
- /* Set Image Color Space */
- if (jp2->enumcs == 16)
+ /* set image color space */
+ if (jp2->enumcs == JP2_COLR_SRGB)
image->color_space = CLRSPC_SRGB;
- else if (jp2->enumcs == 17)
+ else if (jp2->enumcs == JP2_COLR_GRAY)
image->color_space = CLRSPC_GRAY;
- else if (jp2->enumcs == 18)
+ else if (jp2->enumcs == JP2_COLR_SYCC)
image->color_space = CLRSPC_SYCC;
else
image->color_space = CLRSPC_UNKNOWN;
+ /* set image component types */
+ for (i = 0; i < image->numcomps; i++) {
+ image->comps[i].comp_type = jp2->comps[i].comp_type;
+ }
+
return image;
}
@@ -613,6 +779,7 @@
void jp2_setup_encoder(opj_jp2_t *jp2, opj_cparameters_t *parameters, opj_image_t *image) {
int i;
int depth_0, sign;
+ bool allset;
if(!jp2 || !parameters || !image)
return;
@@ -665,6 +832,15 @@
jp2->comps[i].bpcc = image->comps[i].prec - 1 + (image->comps[i].sgnd << 7);
}
+ /* CDEF box */
+
+ allset = true;
+ /* set component types */
+ for (i = 0; i < image->numcomps; i++) {
+ jp2->comps[i].comp_type = image->comps[i].comp_type;
+ allset = allset && jp2->comps[i].comp_type != COMPTYPE_UNKNOWN;
+ }
+
/* Colour Specification box */
if ((image->numcomps == 1 || image->numcomps == 3) && (jp2->bpc != 255)) {
@@ -672,13 +848,20 @@
} else {
jp2->meth = 2; /* METH: Restricted ICC profile */
}
+
+ /* Set to enum colorspace if all components have valid CDEF channel types.
+ METH would be set to 2 in the previous statement for 4 channel RGBA images. */
+ if (allset && jp2->meth != 1) {
+ jp2->meth = 1;
+ }
+
if (jp2->meth == 1) {
- if (image->color_space == 1)
- jp2->enumcs = 16; /* sRGB as defined by IEC 6196621 */
- else if (image->color_space == 2)
- jp2->enumcs = 17; /* greyscale */
- else if (image->color_space == 3)
- jp2->enumcs = 18; /* YUV */
+ if (image->color_space == CLRSPC_SRGB)
+ jp2->enumcs = JP2_COLR_SRGB; /* sRGB as defined by IEC 6196621 */
+ else if (image->color_space == CLRSPC_GRAY)
+ jp2->enumcs = JP2_COLR_GRAY; /* greyscale */
+ else if (image->color_space == CLRSPC_SYCC)
+ jp2->enumcs = JP2_COLR_SYCC; /* YUV */
} else {
jp2->enumcs = 0; /* PROFILE (??) */
}
Index: jp2.h
===================================================================
--- jp2.h (revision 507)
+++ jp2.h (working copy)
@@ -43,6 +43,7 @@
#define JP2_FTYP 0x66747970 /**< File type box */
#define JP2_JP2H 0x6a703268 /**< JP2 header box */
#define JP2_IHDR 0x69686472 /**< Image header box */
+#define JP2_CDEF 0x63646566 /**< Channel Definition */
#define JP2_COLR 0x636f6c72 /**< Colour specification box */
#define JP2_JP2C 0x6a703263 /**< Contiguous codestream box */
#define JP2_URL 0x75726c20 /**< URL box */
@@ -57,8 +58,9 @@
*/
typedef struct opj_jp2_comps {
int depth;
- int sgnd;
+ int sgnd;
int bpcc;
+ OPJ_COMPONENT_TYPE comp_type;
} opj_jp2_comps_t;
/**
Index: openjpeg.h
===================================================================
--- openjpeg.h (revision 507)
+++ openjpeg.h (working copy)
@@ -154,6 +154,20 @@
} OPJ_COLOR_SPACE;
/**
+Supported image component types
+*/
+typedef enum COMPONENT_TYPE {
+ COMPTYPE_UNKNOWN = 0, /** unknown component type, cdef box not present */
+ COMPTYPE_R = 1, /** red component of sRGB image */
+ COMPTYPE_G = 2, /** green component of sRGB image */
+ COMPTYPE_B = 3, /** blue component of sRGB image */
+ COMPTYPE_Y = 4, /** luminance component of YUV and grayscale images */
+ COMPTYPE_CB = 5, /** Cb component of YUV image */
+ COMPTYPE_CR = 6, /** Cr component of YUV image */
+ COMPTYPE_OPACITY = 7 /** opacity/alpha channel */
+} OPJ_COMPONENT_TYPE;
+
+/**
Supported codec
*/
typedef enum CODEC_FORMAT {
@@ -532,11 +546,13 @@
int resno_decoded;
/** number of division by 2 of the out image compared to the original size of image */
int factor;
+ /** type of this component: color channel, opacity, ... */
...
read more »