Pixel Format Conversions

Pixel Format Conversions

I recently found myself having to do a few interesting conversions in C++ to pass images between different formats and/ or libraries, including OpenCV, MATLAB, and some unusual custom formats. I'm going to post some code snippets here that do some of these conversions as I suspect they may be useful for others.

 

Keep in mind that, generally, image data is always stored in one long array, and you have a pointer to the beginning of that array. So, the question is, as you increment your way through the array contents, in what order are you traversing the image's data? E.g. are the first three elements the RGB values of the first pixel in the top left corner of the image? Or are they the R components of the first three pixels moving right along the top row of the image? Or something else entirely?

 

As it turns out, this depends on what library/ API/ SDK/ whatever you are using to handle images. You may often want to use multiple image types in the same project so that you can leverage the capabilities of different libraries or programs, for example using ImageMagick to load some obscure image format, then transferring the loaded image data into an OpenCV Mat structure so you can use OpenCV's routines on it. As a results, being able to quickly convert between these different pixel formats is very handy.

 

A few handy defiintions will make the conversions I'm describing a litte more clear.

 

Row-major order means that as we increment through the array, we first move along the top-most row of the matrix (image), then along columns, i.e. when we get to the last entry on the first row and increment one more place, we are at the first pixel of the next horizontal row. This is the most common way to handle matrices in C and C++. Column-major should now be self-explanatory; as we increment through an array, we are first moving down the left-most column of the matrix, then along rows.

 

Interleaved color channels (or color planes) means that the first thing we increment through is the color channels of the current pixel, then through pixels, i.e. for interleaved RGB data, the data is ordered R1, G1, B1, R2, G2, B2..., where the numbers index pixels. De-interleaved data seperates the color planes so that we first increment through all the pixels of the first color plane before moving to the next color plane, i.e. we would go through R1, R2, R3..., G1, G2, G3,...., B1, B2, B3.

I'll continue posting new conversions here as I find need for them in my own work, but in the meantime, if you don't see the exact conversion here that you want, the ones I've provided should at least help a bit in figuring out how to do your own, and you can always chain multiple conversions together.

 

MATLAB to RGB

...or column-major de-interleaved RGB to row-major interleaved RGB.

This may not be the most apt name for this conversion, but here's the idea. I was writing a MEX file so that I could call some C++ image processing code from within MATLAB. That code was working with interleaved RGB data in row-major order. However, mex files receive inputs from MATLAB as mxArrays, which use column-major de-interleaved data. The column major ordering of MATLAB's data is a throwback to when it was originally written in Fortran.

 

So, before using the data passed from MATLAB, it needs to be rearranged. This can be done as follows, where I'm assuming there could be multiple input images, indexed by int img. Also note that since we don't necessarily know the data type of the input ahead of time, I've included a check for this, which changes the cast of the input data pointer so it gets dereferenced correctly. Here I'm just allowing for double or uint8.

 

 

 

// Make array to hold rearranged data 
pInput_RGB = new char[height*width*depth]; 
// Get pointer to input data 
double *mxPtr = mxGetPr(prhs[img]); // img indexes input images 
//Rearrange data from MATLAB's column-major, de-interleaved RGB order to C-standard row-major interleaved RGB order: 
int idx_RGB_RC=0; 
// indexes target data format 
int idx_CR_RGB=0; // indexes MATLAB's format (column, row, color channel) 
// Don't know ahead of time the data type of input. Will accept double or uint8 
if(mxGetClassID(prhs[img])==mxDOUBLE_CLASS){ 
	// Cast incoming data ptr to double 
	for(j=0; j<height; j++){ 
		for(i=0; i<width; i++){ 
			for(k=0; k<depth; k++){ 
				idx_RGB_RC = (j*width*depth)+(i*depth)+k;
				idx_CR_RGB = (k*height*width)+(i*height)+j;
				((char*)pInput_RGB)[idx_RGB_RC] = (char)(( ( (double*)(mxGetPr(prhs[img])) ) [idx_CR_RGB]));
			}
		}
	}
}
else if (mxGetClassID(prhs[img])==mxUINT8_CLASS){ 
	// Cast incoming data ptr to char 
	for(j=0; j<height; j++){
		for(i=0; i<width; i++){
			for(k=0; k<depth; k++){
				idx_RGB_RC = (j*width*depth)+(i*depth)+k;
				idx_CR_RGB = (k*height*width)+(i*height)+j;
				((char*)pInput_RGB)[idx_RGB_RC] = (char)(( ( (char*)(mxGetPr(prhs[img])) ) [idx_CR_RGB]));
			}
		}
	}
}

 

RGB to ARGB

Some publicly released research code I was using recently required images stored in an unusual integer ARGB format where each pixel was represented by a single integer (4 bytes); bytes two through four were the RGB channels, and the first byte was an (unused as it turns out) alpha channel. This is of course basically the more common RGBA format, but with the alpha channel moved to first place for some reason.

 

Anyway, the conversion is pretty straightforward; you need to create space for the converted data, then use char pointers to access each of the four bytes making up a pixel individually. In the following snippet I've assumed the input is accessed by a char pointer pInput_RGB, the output is an unsigned int pointer pInput_ARGB. I've also just hard-coded the fact that there are four channels in the ARGB format.

 

unsigned int* pInput_ARGB = new unsigned int[height*width];
 
// Convert from RGB to ARGB:
int idx_RGB=0, idx_ARGB=0;
for(j=0; j<height; j++){
    for(i=0; i<width; i++){
        for(k=0; k<depth; k++){
            idx_RGB = (j*width*depth)+(i*depth)+k;
            idx_ARGB = (j*width*4)+(i*4)+k+1;
            ((char*)pInput_ARGB)[idx_ARGB] = ((char*)pInput_RGB)[idx_RGB];
        }
    }
}

 

 

Add new comment

Guest

  • No HTML tags allowed.
  • Web page addresses and email addresses turn into links automatically.