• Advertisement
Sign in to follow this  

How to convert TGA image file format

This topic is 4221 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

In TGA file format specification, i see that there are 3 categories of TGA image file: Pseudo-Color, True-Color and Direct-Color. Now I wanna convert a TGA file from True-Color TARGA32 to a Pseudo-Color format, does there exist any free library to do this? Thanks in advance.

Share this post


Link to post
Share on other sites
Advertisement
This depends on the programming language you are using. Generally, it shouldn't be too hard to actually perform the conversion yourself. It is a nice exercise and allows for playing around with a bunch of algorithms and their parameters, which is fun, too [smile].

What you need is a colour reduction (also: colour quantisation) algorithm that creates a fixed (optimal) palette from the true colour values and allows for a fast remapping.

You can find an overview of the most common algorithms here.

Also try to include error diffusion, which is easy to implement and vastly improves the visual quality of the result.

HTH,
Pat.

Share this post


Link to post
Share on other sites
Thank you very much, darookie!

But I need to solve this problem asap, I will try to study the color quantization algorithm later,

Paintlib, devil, or corona, none of them support this conversion,

[Edited by - hygol on July 5, 2006 12:59:53 AM]

Share this post


Link to post
Share on other sites
Do you really need to convert them in code?
Otherwise you can use an offline tool like ImageMagick or if you need to implement in code use a library like DevIL.

Share this post


Link to post
Share on other sites
Quote:
Original post by hygol
c language.
API? em...Windows API?

I tinkered with a very basic C-based uniform quantiser to illustrate the basic idea:

/* a palette of 256 RGB colours */
typedef unsigned char palette_t[256 * 3];
/* colour index */
typedef unsigned char index_t;

/*
* Create a uniform 6x6x6 colour space palette.
*
* [IN/OUT] palette: pointer to a palette that will be initialised.
*/

void uniform_palette( palette_t palette ) {
int r, g, b, i = 0;
/* create the colour cube (NB: no gamma correction here) */
for ( r = 0; r < 6; ++r )
for ( g = 0; g < 6; ++g )
for ( b = 0; b < 6; ++b, i += 3 ) {
palette[i + 0] = r * 51; /* 255 / 5 = 51, of the 6 levels, one is zero */
palette[i + 1] = g * 51;
palette[i + 2] = b * 51;
}

/* zero out the remaining 40 entries */
memset( &palette[216 * 3], 0, 40 * 3 );
}

/*
* Return the best palette match for the given colour.
*
* [IN] palette: palette to map to.
* [IN] red: red colour value [0..255]
* [IN] green: green colour value [0..255]
* [IN] blue: blue colour value [0..255]
*
* RETURN: index of the best matching palette colour.
*/

index_t uniform_best_match( const palette_t palette, int red, int green, int blue ) {
int indexRed = (red / 51) * 36; /* 6 green and 6 blue cubes per red cube */
int indexGreen = (green / 51) * 6; /* 6 blue cubes per green cube */
int indexBlue = (blue / 51); /* 6 blue cubes */
return indexRed + indexBlue + indexGreen;
}

/*
* remap a colour image onto the created palette.
*
* [IN] image: input RGB true colour image.
* [IN] width: number of pixels per row
* [IN] height: number of rows in the input image
* [IN] palette: palette to be used
* RETURN: indexed image that will be both allocated and filled with data
*/

index_t * uniform_remap( const unsigned char * image, int width, int height, const palette_t palette ) {
int i, k;
index_t * mapped;
// assert( width > 0 && height > 0 && image != 0 && palette != 0 );
/* allocate mapped image */
mapped = (index_t*)malloc( width * height * sizeof( index_t ) );
if ( mapped == 0 )
return 0;
for ( i = 0, k = width * height; i < k; ++i ) {
mapped =
uniform_best_match( palette, image[i * 3 + 0], image[i * 3 + 1], image[i * 3 + 2] );
}
return mapped;
}

/*
* perform dithering to improve image quality.
*
* [IN] image: true colour input image
* [IN/OUT] mapped: indexed colour image, filled with valid data
* [IN] width: image width in pixels
* [IN] height: image height in rows
* [IN] palette: palette to be used
*/

void dither_image( const unsigned char * image, index_t * mapped, int width, int height, const palette_t palette ) {
int * error, * nexterror;
int x, y, startx, endx, ix;
// assert( width > 0 && height > 0 && image != 0 && mapped != 0 && palette != 0 );

error = (int*)malloc( width * sizeof( int ) * 3);
nexterror = (int*)malloc( width * sizeof( int ) * 3 );
memset( error, 0, width * sizeof( int ) * 3 );
memset( nexterror, 0, width * sizeof( int ) * 3 );

ix = 1;
for ( y = 0; y < height; ++y ) {
if ( ix > 0 ) {
startx = 0; endx = width;
} else {
startx = width - 1; endx = -1;
}

for ( x = startx; x != endx; x += ix ) {

int index;
int qr, qg, qb;
int ofs = y * width + x;
int nextx;

/* read the mapped colour values */
int ri = image[ofs * 3 + 0];
int gi = image[ofs * 3 + 1];
int bi = image[ofs * 3 + 2];

/* add the current quantisation error */
ri += error[x * 3 + 0];
gi += error[x * 3 + 1];
bi += error[x * 3 + 2];

/* clamp the values. NB: this could be improved by taking into account neighbouring
pixels */

if ( ri > 255 ) ri = 255; else if ( ri < 0 ) ri = 0;
if ( gi > 255 ) gi = 255; else if ( gi < 0 ) gi = 0;
if ( bi > 255 ) bi = 255; else if ( bi < 0 ) bi = 0;

index = uniform_best_match( palette, ri, gi, bi );

mapped[ofs] = index;

/* assign quantisation errors */
qr = image[ofs * 3 + 0] + error[x * 3 + 0] - palette[index * 3 + 0];
qg = image[ofs * 3 + 1] + error[x * 3 + 1] - palette[index * 3 + 1];
qb = image[ofs * 3 + 2] + error[x * 3 + 2] - palette[index * 3 + 2];

/* distribute the error to the next row using Floyd-Steinberg coefficients */
nexterror[x * 3 + 0] += (qr * 5) / 16;
nexterror[x * 3 + 1] += (qg * 5) / 16;
nexterror[x * 3 + 2] += (qb * 5) / 16;

nextx = x + ix;

if ( nextx >= 0 && nextx < width ) {
error[nextx * 3 + 0] += (qr * 7) / 16;
error[nextx * 3 + 1] += (qg * 7) / 16;
error[nextx * 3 + 2] += (qb * 7) / 16;
nexterror[nextx * 3 + 0] += qr / 16;
nexterror[nextx * 3 + 1] += qg / 16;
nexterror[nextx * 3 + 2] += qb / 16;
}

nextx = x - ix;

if ( nextx >= 0 && nextx < width ) {
nexterror[nextx * 3 + 0] += (qr * 3) / 16;
nexterror[nextx * 3 + 1] += (qr * 3) / 16;
nexterror[nextx * 3 + 2] += (qr * 3) / 16;
}
}
/* flip error dithering direction */
ix = -ix;
/* swap errors */
memcpy( error, nexterror, width * sizeof( int ) * 3 );
memset( nexterror, 0, width * sizeof( int ) * 3 );
}
}



The above code is just a crude outline, put together with little care [smile].
Here's the program that I used to produce the screenshots and that illustrates the basic usage:

int main( int argc, char * argv[] ) {
/* my test image is just a 512x384 24-bit RGB RAW-file. */
FILE * input = fopen( "test.raw", "rb" );
if ( input != 0 ) {
unsigned char * img = (unsigned char*)malloc( 512 * 384 * 3 * sizeof( unsigned char ) );
unsigned char * mapped;
palette_t pal;

/* load the RGB image into a buffer */
fread( img, sizeof( unsigned char ) * 512 * 384 * 3, 1, input );
fclose( input );

/* create the palette (not actually saved/used here) */
uniform_palette( pal );
/* quantise the image */
mapped = uniform_remap( img, 512, 384, pal );
/* perform dithering */
dither_image( img, mapped, 512, 384, pal );

/* write image to disk just to see whether it actually worked :P */
input = fopen( "output.raw", "wb" );
if ( input != 0 ) {
fwrite( mapped, sizeof( unsigned char ) * 512 * 384, 1, input );
fclose( input );
}

free( mapped );
free( img );
}

return 0;
}



And finally some actual results:

Original image
Free Image Hosting at www.ImageShack.us

Uniformly quantised without dithering
Free Image Hosting at www.ImageShack.us

Uniformly quantised with dithering
Free Image Hosting at www.ImageShack.us

HTH,
Pat

[Edited by - darookie on July 5, 2006 10:17:44 AM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement