Karoshi MSX Community
23 de Marzo de 2019, 09:25:28 pm *
Bienvenido(a), Visitante. Por favor, ingresa o regístrate.

Ingresar con nombre de usuario, contraseña y duración de la sesión
Noticias:
 
   Inicio   Ayuda Buscar Ingresar Registrarse  
Páginas: [1]
  Imprimir  
Autor Tema: Image converter (24bpp -> SC2)  (Leído 9750 veces)
0 Usuarios y 1 Visitante están viendo este tema.
pitpan
Karoshi Forum's Guru
*******
Mensajes: 1812


« : 18 de Mayo de 2007, 09:13:38 am »

Well, this is it. A very simple yet functional image converter for TMS9918.

At the moment it only supports TGA format, 24bpp uncompressed, up to 512x512 pixels. It converts the image to the best non-dithered solution that verifies the TMS9918 colour restrictions (2 colores for each 8x1 pixel block). The conversion algorithm is quite simple and it requieres a high clocked computer.

The output of the program is the following:

- file.CHR - uncompressed TMS9918 pattern information
- file.CLR - uncompressed TMS9918 colour information
- file_TMS.TGA - TGA including the converted image

Enjoy. Any suggestions will be welcome. Please note that the RGB values included are very approximated. Better RGB palettes will output better fit images.  Wink

Código:
/*
---------------------------------------------------------------
TMSOPT v.0.02 - Eduardo A. Robsy Petrus, 2007
---------------------------------------------------------------
 TGA image converter (24 bpp, uncompressed) to TMS9918 format
---------------------------------------------------------------
Overview
---------------------------------------------------------------
Selects the best local solution for each 8x1 pixel block
Optimization uses the following algorithm:

(a) For each pixel, RGB differences are calculated
  - All RGB channels have the same weight
  - It uses absolute error instead of squared error

(b) This error value is added for all pixels in each 8x1 block

(c) The CHR-CLR combination with lowest error rate is selected

It is a brute-force method, computationally very expensive.
Some patience and a high-clocked CPU computed is recommended.
---------------------------------------------------------------
Compilation instructions
---------------------------------------------------------------
 Tested with GCC/Win32 [mingw]:

   GCC TMSopt.c -oTMSopt.exe -O3 -s

 It is standard C, so there is a fair chance of being portable!
---------------------------------------------------------------
History
---------------------------------------------------------------
 Ages ago   - algorithm created
 16/05/2007 - first C version (RAW format)
 17/05/2007 - TGA format included, some optimization included
---------------------------------------------------------------
Legal disclaimer
---------------------------------------------------------------
 Do whatever you want to do with this code/program.
 Use at your own risk, all responsability would be declined.
 It would be nice if you credit the author, though.
---------------------------------------------------------------
*/

// Headers!

#include<stdio.h>
#include<time.h>

// Just one function for everything

main(int argc, char **argv)
{

// Vars

 FILE *file,*CHR,*CLR;
 int bc,bp,i,j,x,y,c,p,k,MAXX,MAXY;
 unsigned int score[256][512],n,total=0,done=0,size;
 char *name;
 unsigned char image[512][512][3],header[18],palette[16][3]=

// TMS9918 RGB palette - approximated 50Hz PAL values

{{0x00,0x00,0x00}, //  0 Transparent - black (not used)
 {0x00,0x00,0x00}, //  1 Black
 {0x12,0xFF,0x00}, //  2 Green
 {0x76,0xFF,0x6F}, //  3 Light green
 {0x12,0x00,0xFF}, //  4 Blue
 {0x69,0x69,0xFF}, //  5 Light blue
 {0xA1,0x10,0x10}, //  6 Dark red
 {0x00,0xFF,0xFC}, //  7 Cyan
 {0xFF,0x00,0x00}, //  8 Red
 {0xFF,0x80,0x80}, //  9 Light red
 {0xFF,0xEA,0x00}, // 10 Yellow
 {0xFF,0xFB,0x80}, // 11 Light yellow
 {0x1D,0xA8,0x02}, // 12 Dark green
 {0xFF,0x00,0xFC}, // 13 Magenta
 {0xCC,0xCC,0xCC}, // 14 Light gray
 {0xFF,0xFF,0xFF}};// 15 White

// Get time

 clock();

// Application prompt

 printf("TMSopt v.0.01 - TGA 24bpp to TMS9918 converter\nCoded by Eduardo A. Robsy Petrus, 2007\n");
// Test if only one command-line parameter is available

 if (argc==1)
 {
  printf("Syntax: TMSopt [file.tga]\n");
  return;
 }

// Open source image (TGA, 24-bit, uncompressed)

 if ((file=fopen(argv[1],"rb"))==NULL)
 {
  printf("cannot open %s file!\n",argv[1]);
  return;
 }

// Read TGA header

 for (i=0;i<18;i++) header[i]=fgetc(file);

// Check header info

 for (i=0,n=0;i<12;i++) n+=header[i];

 if ((n!=2)||(header[2]!=2)||(header[17])||(header[16]!=24))
 {
  printf("Unsupported file format!\n");
  return;
 }

// Calculate size

 MAXX=header[12]|header[13]<<8;
 MAXY=header[14]|header[15]<<8;

 size=((MAXX+7)>>3)*MAXY;

// Check size limits

 if ((!MAXX)||(MAXX>512)||(!MAXY)||(MAXY>512))
 {
  printf("Unsupported size!");
  return;
 }

// Load image data

 for (y=MAXY-1;y>=0;y--)
  for (x=0;x<MAXX;x++)
   for (k=0;k<3;k++)
    image[x][y][2-k]=fgetc(file);

// Close file

 fclose(file);

// Information

 printf("Converting %s (%i,%i) to TMS9918 format...    ",argv[1],MAXX,MAXY);

// Create TMS output files (CHR, CLR)

 argv[1][strlen(argv[1])-3]='C';
 argv[1][strlen(argv[1])-2]='H';
 argv[1][strlen(argv[1])-1]='R';

 CHR=fopen(argv[1],"wb");

 argv[1][strlen(argv[1])-2]='L';
 CLR=fopen(argv[1],"wb");

// Image processing

 for (y=0;y<(MAXY+7)>>3;y++)
  for (x=0;x<(MAXX+7)>>3;x++)
   for (j=0;j<8;j++)

// Generate alternatives

    {
     bp=0;bc=0;
     score[0][0]=-1;
     for (p=0;p<256;p++)
      for (c=0x12;c<256;c++)
       if (c>>4<c&15)

// Score calculation

       {
        score[p][c]=0;
        n=p;
        for (i=0;i<8;i++)
        {
         for (k=0;k<3;k++)
          score[p][c]+=abs(palette[(n&0x80)?c>>4:c&0x0f][k]-image[x<<3|i][y<<3|j][k]);
         n<<=1;
        }

// Keep best solution found

        if (score[p][c]<score[bp][bc])
         {
          bp=p;bc=c;
         }
        total++;
        if (score[bp][bc]<6) {c=256;p=256;}
       }

// Save best pattern and colour combination

     fputc(bp,CHR);
     fputc(bc,CLR);

// Replace original image data with TMS result

     n=bp;
     for (i=0;i<8;i++)
     {
      for (k=0;k<3;k++)
       image[x<<3|i][y<<3|j][k]=palette[n&0x80?bc>>4:bc&0x0f][k];
      n<<=1;
     }

// Update status counter
   if (done*100/size<(done+1)*100/size) printf("\b\b\b%2i%%",100*done/size);
   done++;

   }

// Conversion done

 printf("\b\b\bOk   \n");

// Generate new name

 name=malloc(0x100);
 argv[1][strlen(argv[1])-4]=0;
 strcpy(name,argv[1]);
 strcat(name,"_tms.tga");

// Save file header

 file=fopen(name,"wb");

 for (i=0;i<18;i++) fputc(header[i],file);

// Save image data

 for (y=MAXY-1;y>=0;y--)
  for (x=0;x<MAXX;x++)
   for (k=0;k<3;k++)
    fputc(image[x][y][2-k],file);

// Close file

 fclose(file);

// Prompt elapsed time

 printf("%.2f million combinations analysed in %.2f seconds",total/1e6,(float)clock()/(float)CLOCKS_PER_SEC);

}
En línea
pitpan
Karoshi Forum's Guru
*******
Mensajes: 1812


« Respuesta #1 : 18 de Mayo de 2007, 09:18:31 am »

By the way, I've only tested it with TGAs created by Photoshop. I haven't reviewed if other applications would use the same TGA header format Sad. If you do, please let me know the result.
En línea
pitpan
Karoshi Forum's Guru
*******
Mensajes: 1812


« Respuesta #2 : 18 de Mayo de 2007, 05:46:31 pm »

Check some examples:

En línea
ARTRAG
Visitante
« Respuesta #3 : 18 de Mayo de 2007, 06:19:04 pm »

You can optimize your inner code a lot!!

1) why do you store all the tests situation in matrix score[][] ?
you do not need it: simply evalue the current score, if it is better than the previous keep it, otherwise go haed

2) why do you compute all the color/pattern combinations?
your program tests all the 105*256=26880 combinations....
you do not need it.

do a outer loop on the 105 color couples
like:
for (c1=1;c1<15;c1++)
for (c2=c1+1;c2<15;c2++)
{
etc.
}


In the inner loop do not scan all the 256 patterns!
do this :
compute the distance of the color of each point from c1, (costs 8 diffences + 8 products if you use squared errors)
compute the distance of the color of each point from c2, (costs 8 diffences + 8 products if you use squared errors)

select the minimum between the two sets of values for each point. (costs 8 comparisons)
the sum of the minima is the current score (costs 7 sums)
the results of your comparisons forms the current pattern p (the best given c1 and c2)

This cuts the time a lot: instead of doing 256 tests (like you do now)
you compute only 8+8 differences, 8 comparisons and 7 sums (+ 16 producs if you use squared errors)

En línea
ARTRAG
Visitante
« Respuesta #4 : 19 de Mayo de 2007, 10:11:40 am »

Now Floyd works!! minor adjustments (and testing) are still required....

Código:
/*
---------------------------------------------------------------
TMSOPT v.0.04 - Eduardo A. Robsy Petrus & Arturo Ragozini, 2007
---------------------------------------------------------------
 TGA image converter (24 bpp, uncompressed) to TMS9918 format
---------------------------------------------------------------
Overview
---------------------------------------------------------------
Selects the best local solution for each 8x1 pixel block
Optimization uses the following algorithm:

(a) For each pixel, RGB differences are calculated
  - All RGB channels have the same weight
  - It uses absolute error instead of squared error

(b) This error value is added for all pixels in each 8x1 block

(c) The CHR-CLR combination with lowest error rate is selected

It is a brute-force method, computationally very expensive.
Some patience and a high-clocked CPU computed is recommended.
---------------------------------------------------------------
Compilation instructions
---------------------------------------------------------------
 Tested with GCC/Win32 [mingw]:

   GCC TMSopt.c -oTMSopt.exe -O3 -s

 It is standard C, so there is a fair chance of being portable!
---------------------------------------------------------------
History
---------------------------------------------------------------
 Ages ago   - algorithm created
 16/05/2007 - first C version (RAW format)
 17/05/2007 - TGA format included, some optimization included
 18/05/2007 - Big optimization (200 times faster), support for
              square errors
 19/05/2007 - Floyd-Stenberg added, scaling for better rounding
---------------------------------------------------------------
Legal disclaimer
---------------------------------------------------------------
 Do whatever you want to do with this code/program.
 Use at your own risk, all responsability would be declined.
 It would be nice if you credit the author, though.
---------------------------------------------------------------
*/

// Headers!

#include<stdio.h>
#include<time.h>
#include<limits.h>


#define scale 32

#define inrange8(t) ((t)<0) ? 0 :(((t)>255) ? 255:(t))

#define clamp(t)    ((t)<0) ? 0 :(((t)>255*scale) ? 255*scale : (t))

/////////////////
// scanning direction
// DIR=1  ->
// DIR=0  <-
#define DIR 1

/////////////////////////////
// switch palette MSX1 MSX2
// (approssimate)

#define MSX1

// Just one function for everything

main(int argc, char **argv)
{

// Vars

 FILE *file,*CHR,*CLR;
 int bc,bp,i,j,x,y,c,p,k,MAXX,MAXY;
 unsigned int n,total=0,done=0,size;
 char *name;
 short image[512][512][3],header[18],palette[16][3];

// TMS9918 RGB palette - approximated 50Hz PAL values
 unsigned int pal[16][3]= {
#ifdef MSX1
{ 0,0,0},
{ 0,0,0},
{ 33,200,66},
{ 94,220,120},
{ 84,85,237},
{ 125,118,252},
{ 212,82,77},
{ 66,235,245},
{ 252,85,84},
{ 255,121,120},
{ 212,193,84},
{ 230,206,128},
{ 33,176,59},
{ 201,91,186},
{ 204,204,204},
{ 255,255,255}
#endif
#ifdef MSX2
{0x00,0x00,0x00}, //  0 Transparent - black (not used)
{0x00,0x00,0x00}, //  1 Black
{0x12,0xFF,0x00}, //  2 Green
{0x76,0xFF,0x6F}, //  3 Light green
{0x12,0x00,0xFF}, //  4 Blue
{0x69,0x69,0xFF}, //  5 Light blue
{0xA1,0x10,0x10}, //  6 Dark red
{0x00,0xFF,0xFC}, //  7 Cyan
{0xFF,0x00,0x00}, //  8 Red
{0xFF,0x80,0x80}, //  9 Light red
{0xFF,0xEA,0x00}, // 10 Yellow
{0xFF,0xFB,0x80}, // 11 Light yellow
{0x1D,0xA8,0x02}, // 12 Dark green
{0xFF,0x00,0xFC}, // 13 Magenta
{0xCC,0xCC,0xCC}, // 14 Light gray
{0xFF,0xFF,0xFF}  // 15 White
#endif
};
// Scale palette

 for (i=0;i<16;i++)
     for (k=0;k<3;k++)
        palette[i][k] = scale*pal[i][k];

// Get time

 clock();

// Application prompt

 printf("TMSopt v.0.04 - TGA 24bpp to TMS9918 converter\nCoded by Eduardo A. Robsy Petrus & Arturo Ragozini, 2007\n");
// Test if only one command-line parameter is available

// Guess the name of the image I used for testing
#ifdef DEBUG
argc = 2;
argv[1] = malloc(20);
argv[1][0] = 'e';
argv[1][1] = 'a';
argv[1][2] = 'r';
argv[1][3] = 't';
argv[1][4] = 'h';
argv[1][5] = '.';
argv[1][6] = 't';
argv[1][7] = 'g';
argv[1][8] = 'a';
argv[1][9] = 0;
#endif

 if (argc==1)
 {
  printf("Syntax: TMSopt [file.tga]\n");
  return;
 }

// Open source image (TGA, 24-bit, uncompressed)

 if ((file=fopen(argv[1],"rb"))==NULL)
 {
  printf("cannot open %s file!\n",argv[1]);
  return;
 }

// Read TGA header

 for (i=0;i<18;i++) header[i]=fgetc(file);

// Check header info

 for (i=0,n=0;i<12;i++) n+=header[i];

// I deleted the check on n, was it important ?
 if ((header[2]!=2)||(header[17])||(header[16]!=24))
 {
  printf("Unsupported file format!\n");
  return;
 }

// Calculate size

 MAXX=header[12]|header[13]<<8;
 MAXY=header[14]|header[15]<<8;

 size=((MAXX+7)>>3)*MAXY;

// Check size limits

 if ((!MAXX)||(MAXX>512)||(!MAXY)||(MAXY>512))
 {
  printf("Unsupported size!");
  return;
 }

// Load image data

 for (y=MAXY-1;y>=0;y--)
  for (x=0;x<MAXX;x++)
   for (k=0;k<3;k++)
    image[x][y][2-k]=((short)fgetc(file))*scale;        // Scale image

// Close file

 fclose(file);

// Information

 printf("Converting %s (%i,%i) to TMS9918 format...    ",argv[1],MAXX,MAXY);

// Create TMS output files (CHR, CLR)

 argv[1][strlen(argv[1])-3]='C';
 argv[1][strlen(argv[1])-2]='H';
 argv[1][strlen(argv[1])-1]='R';

 CHR=fopen(argv[1],"wb");

 argv[1][strlen(argv[1])-2]='L';
 CLR=fopen(argv[1],"wb");

// Image processing
     
 for (y=0;y<(MAXY+7)>>3;y++)
    for (x=0;x<(MAXX+7)>>3;x++)
        for (j=0;j<8;j++)
        {
            // Generate alternatives
            unsigned char c1,c2;
            unsigned int bs = INT_MAX;
            unsigned char bp = 0, bc = 0;
           
            for (c1=1;c1<16;c1++)
                for (c2=c1+1;c2<16;c2++)
                {
                    unsigned int cs = 0;
                    unsigned int cp = 0;
                    for (i=0;i<8;i++)
                    {
                        int xx = (x<<3)|i;
                        int yy = (y<<3)|j;           

                        unsigned int mc1=0,mc2=0;
                               
                        xx = (DIR) ? (xx) : (MAXX - 1 - xx);
                       
                        for (k=0;k<3;k++) {
                            int  d1 = abs(palette[c1][k]-image[xx][yy][k]);
                            int  d2 = abs(palette[c2][k]-image[xx][yy][k]);
                            mc1+=d1*d1;
                            mc2+=d2*d2;
                        }
                        if (mc1>mc2) {
                            cp = (cp<<1) | (0x01);
                            cs += mc2;
                        }
                        else {
                            cp = (cp<<1) | (0x00);
                            cs += mc1;
                        }
                    }
                    if  (cs<bs)
                    {
                        bs=cs;
                        bp=cp;
                        bc=c2*16+c1;
                    }
                }
            // Here we have the best colors and the best pattern for line j

            {           
                short newpixel[3], quant_error[3];
                unsigned char bc1 = bc & 15, bc2 = bc/16;
                unsigned char cp = 0;
               
                for (i=0;i<8;i++) {
                    int xx = (x<<3)|i;
                    int yy = (y<<3)|j;                   

                    // Decide again what is the best color between bc1 and bc2 for the current point - now dithered

                    unsigned int mc1=0,mc2=0;
                   
                    xx = (DIR) ? (xx) : (MAXX - 1 - xx);
                   
                    for (k=0;k<3;k++) {
                        int  d1 = abs(palette[bc1][k]-image[xx][yy][k]);
                        int  d2 = abs(palette[bc2][k]-image[xx][yy][k]);
                        mc1+=d1*d1;
                        mc2+=d2*d2;
                    }
                    if (mc1>mc2)    {   
                        cp = (cp<<1) | (0x01); 
                        for (k=0;k<3;k++)
                            newpixel[k] = palette[bc2][k];
                    }
                    else    {   
                        cp = (cp<<1) | (0x00); 
                        for (k=0;k<3;k++)
                            newpixel[k] = palette[bc1][k];
                    }

                    // compute the quantization error
                   
                    for (k=0;k<3;k++)     
                        quant_error[k] = image[xx][yy][k] - newpixel[k];

                    // store the current point - now quantized
                       
                    for (k=0;k<3;k++)     
                        image[xx][yy][k] = newpixel[k];

                    // spread the quantization error
                     
                    if (DIR){
                        if (xx+1<=MAXX-1)                   
                            for (k=0;k<3;k++)                         
                                image[xx+1][yy+0][k] =  clamp(image[xx+1][yy+0][k]+(7 * quant_error[k])/16);
   
                        if (yy+1<=MAXY-1) {
                       
                            for (k=0;k<3;k++)     
                                image[xx+0][yy+1][k] =  clamp(image[xx+0][yy+1][k]+(5 * quant_error[k])/16);
   
                            if (xx-1 >=0)
                                for (k=0;k<3;k++)                         
                                    image[xx-1][yy+1][k] = clamp(image[xx-1][yy+1][k]+(3 * quant_error[k])/16);
                                   
                            if (xx+1 <=MAXX-1)                         
                                for (k=0;k<3;k++)     
                                    image[xx+1][yy+1][k] = clamp(image[xx+1][yy+1][k]+(quant_error[k])/16);
                        }
                    }
                    else    {
                        if (xx-1>=0)                   
                            for (k=0;k<3;k++)                         
                                image[xx-1][yy+0][k] =  clamp(image[xx-1][yy+0][k]+(7 * quant_error[k])/16);

                        if (yy+1<=MAXY-1) {

                            for (k=0;k<3;k++)     
                                image[xx+0][yy+1][k] =  clamp(image[xx+0][yy+1][k]+(5 * quant_error[k])/16);
   
                            if (xx-1 >=0)
                                for (k=0;k<3;k++)                         
                                    image[xx-1][yy+1][k] = clamp(image[xx-1][yy+1][k]+( quant_error[k])/16);
                                   
                            if (xx+1 <=MAXX-1)                         
                                for (k=0;k<3;k++)     
                                    image[xx+1][yy+1][k] = clamp(image[xx+1][yy+1][k]+(3 * quant_error[k])/16);
                        }
                    }
                }
                // Forget the previous pattern not based on dithering
                bp = cp;           
            }
               
               
            // Save best pattern and colour combination
                fputc(bp,CHR);
                fputc(bc,CLR);

//            // Replace original image data with TMS result
//                n=bp;
//                for (i=0;i<8;i++)
//                {
//                    for (k=0;k<3;k++)
//                        image[x<<3|i][y<<3|j][k]=palette[n&0x80?bc>>4:bc&0x0f][k];
//                    n<<=1;
//                }

            // Update status counter
           
                if (done*100/size<(done+1)*100/size)
                    printf("\b\b\b%2i%%",100*done/size);
                done++;
                total++;
        }   

// Conversion done

 printf("\b\b\bOk   \n");

// Generate new name

 name = malloc(0x100);
 argv[1][strlen(argv[1])-4]=0;
 strcpy(name,argv[1]);
 strcat(name,"_tms.tga");

// Save file header

 file=fopen(name,"wb");

 for (i=0;i<18;i++) fputc(header[i],file);

// Save image data

 for (y=MAXY-1;y>=0;y--)
  for (x=0;x<MAXX;x++)
   for (k=0;k<3;k++)
    fputc(inrange8(image[x][y][2-k]/scale),file);       // Scale to char

// Close file

 fclose(file);

// Prompt elapsed time

 printf("%.2f million combinations analysed in %.2f seconds",total/1e6,(float)clock()/(float)CLOCKS_PER_SEC);

}



Here the full package for who is interested in some testing
http://ragozini.googlepages.com/sc2conv.rar
« Última modificación: 20 de Mayo de 2007, 12:01:08 am por ARTRAG » En línea
WYZ
Visitante
« Respuesta #5 : 19 de Mayo de 2007, 07:48:24 pm »

Check some examples:


Genial. Fijandose en los detalles como la cara de Roger, o las gafas y los brillos de la calva y la nariz de mortadelo es cuando se da uno cuenta de lo bueno que es. Muy bueno.
En línea
Darth_Fistro
Karoshi Hero
*****
Mensajes: 507


Email
« Respuesta #6 : 22 de Mayo de 2007, 10:44:14 am »

Please, post the executable file! Fantastic!  Shocked
En línea

MSX FOREVER (hasta que saquen un ZX81 con TMS, PSG y 64K de RAM)
ARTRAG
Visitante
« Respuesta #7 : 22 de Mayo de 2007, 11:59:13 am »

The executable is into the RAR archive
Change the extension of the file *.ex_ into *.exe
En línea
Darth_Fistro
Karoshi Hero
*****
Mensajes: 507


Email
« Respuesta #8 : 22 de Mayo de 2007, 12:19:03 pm »

Thanks, ARTRAG!  Cheesy
En línea

MSX FOREVER (hasta que saquen un ZX81 con TMS, PSG y 64K de RAM)
ARTRAG
Visitante
« Respuesta #9 : 22 de Mayo de 2007, 11:56:53 pm »

new package released:

http://ragozini.googlepages.com/vdpenc.zip

Anyone willing to test it?
IMHO Jannone's conversions are still better (sc2 in the package), I do not know where I am wrong.

NOTE
The CHR and CLR files now can be b-loaded in screen 2
e.g.
10 screen 2: color 15,0,0
20 bload"lenna_.CHR",s
30 bload"lenna_.CLR",s
40 goto 40
En línea
jltursan
Karoshi Forum's Guru
*******
Mensajes: 1516


┬┐Que es lo que has aprendido hoy?


WWW Email
« Respuesta #10 : 23 de Mayo de 2007, 09:51:38 am »

Have you tried to contact with Rafael himself?, maybe he can throw some light...
En línea

Doom dee doom dee doom
ARTRAG
Visitante
« Respuesta #11 : 23 de Mayo de 2007, 10:48:09 am »

All problems solved thanks to Rafael Jannone itself:
here the full package
http://ragozini.googlepages.com/dithering

Test and let me know
« Última modificación: 24 de Mayo de 2007, 11:57:01 am por ARTRAG » En línea
ARTRAG
Visitante
« Respuesta #12 : 26 de Mayo de 2007, 04:33:17 pm »

New algorithm online with perceptually uniform distances in the color space
http://ragozini.googlepages.com/dithering

Código:
/*
---------------------------------------------------------------
TMSOPT v.0.1 - Eduardo A. Robsy Petrus & Arturo Ragozini 2007
Credits to Rafael Jannone for his Floyd-Steinberg implementation
---------------------------------------------------------------
 TGA image converter (24 bpp, uncompressed) to TMS9918 format
---------------------------------------------------------------
Overview
---------------------------------------------------------------
Selects the best solution for each 8x1 pixel block
Optimization uses the following algorithm:

(a) Select one 1x8 block, select a couple of colors, apply
    Floyd-Steinberg within the block, compute the squared error,
    repeat for all 105 color combinations, keep the best couple
    of colors.

(b) Apply Floyd-Steinberg to the current 1x8 block with the best
    two colors selected before and spread the errors to the
    adjacent blocks.

(c) repeat (a) and (b) on the next 1x8 block, scan all lines.

(d) Convert the image in pattern and color definitions (CHR & CLR)

To load in MSX basic use something like this:

10 screen 2: color 15,0,0
20 bload"FILE.CHR",s
30 bload"FILE.CLR",s
40 goto 40

---------------------------------------------------------------
Compilation instructions
---------------------------------------------------------------
 Tested with GCC/Win32 [mingw]:

   GCC TMSopt.c -oTMSopt.exe -O3 -s

 It is standard C, so there is a fair chance of being portable!
 NOTE
 In the current release the name of the C file has become scr2floyd.c
---------------------------------------------------------------
History
---------------------------------------------------------------
 Ages ago   - algorithm created
 16/05/2007 - first C version (RAW format)
 17/05/2007 - TGA format included, some optimization included
 18/05/2007 - Big optimization (200 times faster), support for
              square errors
 19/05/2007 - Floyd-Stenberg added, scaling for better rounding
 24/05/2007 - Floyd-Stenberg included in the color optimization.
---------------------------------------------------------------
Legal disclaimer
---------------------------------------------------------------
 Do whatever you want to do with this code/program.
 Use at your own risk, all responsability would be declined.
 It would be nice if you credit the authors, though.
---------------------------------------------------------------
*/

// Headers!

#include<stdio.h>
#include<time.h>
#include<limits.h>

typedef unsigned int    uint;
typedef unsigned char   uchar;
typedef unsigned short  ushort;
typedef unsigned long   ulong;

//#define DEBUG

#define scale 16
#define inrange8(t) ((t)<0) ? 0 :(((t)>255) ? 255:(t))
#define clamp(t)    ((t)<0) ? 0 :(((t)>255*scale) ? 255*scale : (t))


typedef struct {
   float r, g, b;
} RGB;

float ColourDistance(RGB e1, RGB e2)
{
  float r,g,b;
  float rmean;
 
  e1.r/=scale;
  e1.g/=scale;   
  e1.b/=scale;   
 
  e2.r/=scale;
  e2.g/=scale;   
  e2.b/=scale;   

  rmean = ( (int)e1.r + (int)e2.r ) / 2 ;
  r = ((int)e1.r - (int)e2.r);
  g = ((int)e1.g - (int)e2.g);
  b = ((int)e1.b - (int)e2.b);
//  return r*r+g*g+b*b;
  return ((((512+rmean)*r*r)/256) + 4*g*g + (((767-rmean)*b*b)/256));
}

// Just one function for everything

main(int argc, char **argv)
{

// Vars

 FILE *file,*CHR,*CLR;
 int bc,bp,i,j,x,y,c,p,k,MAXX,MAXY;
 uint n,total=0,done=0,size;
 char *name;
 short image[512+2][512+2][3],header[18],palette[16][3];

// TMS9918 RGB palette - approximated 50Hz PAL values
 uint pal[16][3]= {
{ 0,0,0},                 // 0 Transparent
{ 0,0,0},                 // 1 Black           0    0    0
{ 33,200,66},             // 2 Medium green   33  200   66
{ 94,220,120},            // 3 Light green    94  220  120
{ 84,85,237},             // 4 Dark blue      84   85  237
{ 125,118,252},           // 5 Light blue    125  118  252
{ 212,82,77},             // 6 Dark red      212   82   77
{ 66,235,245},            // 7 Cyan           66  235  245
{ 252,85,84},             // 8 Medium red    252   85   84
{ 255,121,120},           // 9 Light red     255  121  120
{ 212,193,84},            // A Dark yellow   212  193   84
{ 230,206,128},           // B Light yellow  230  206  128
{ 33,176,59},             // C Dark green     33  176   59
{ 201,91,186},            // D Magenta       201   91  186
{ 204,204,204},           // E Gray          204  204  204
{ 255,255,255}            // F White         255  255  255
};
// Scale palette

 for (i=0;i<16;i++)
     for (k=0;k<3;k++)
        palette[i][k] = scale*pal[i][k];

// Get time

 clock();

// Application prompt

 printf("TMSopt v.0.1 - TGA 24bpp to TMS9918 converter.\nCoded by Eduardo A. Robsy Petrus & Arturo Ragozini 2007.\n\n");
 printf("Credits to Rafael Jannone for his Floyd-Steinberg implementation.\n \n");


// Guess the name of the image I used for testing
#ifdef DEBUG
argc = 2;
argv[1] = malloc(20);
argv[1][0] = 'l';
argv[1][1] = 'e';
argv[1][2] = 'n';
argv[1][3] = 'n';
argv[1][4] = 'a';
argv[1][5] = '_';
argv[1][6] = '.';
argv[1][7] = 't';
argv[1][8] = 'g';
argv[1][9] = 'a';
argv[1][10] = 0;
#endif

// Test if only one command-line parameter is available

 if (argc==1)
 {
  printf("Syntax: TMSopt [file.tga]\n");
  return;
 }

// Open source image (TGA, 24-bit, uncompressed)

 if ((file=fopen(argv[1],"rb"))==NULL)
 {
  printf("cannot open %s file!\n",argv[1]);
  return;
 }

// Read TGA header

 for (i=0;i<18;i++) header[i]=fgetc(file);

// Check header info

 for (i=0,n=0;i<12;i++) n+=header[i];

// I deleted the check on n, was it important ?
 if ((header[2]!=2)||(header[17])||(header[16]!=24))
 {
  printf("Unsupported file format!\n");
  return;
 }

// Calculate size

 MAXX=header[12]|header[13]<<8;
 MAXY=header[14]|header[15]<<8;

 size=((MAXX+7)>>3)*MAXY;

// Check size limits

 if ((!MAXX)||(MAXX>512)||(!MAXY)||(MAXY>512))
 {
  printf("Unsupported size!");
  return;
 }

// Load image data

 for (y=MAXY-1;y>=0;y--)
  for (x=0;x<MAXX;x++)
   for (k=0;k<3;k++)
    image[x+1][y+1][2-k]=((short)fgetc(file))*scale;        // Scale image

 for (x=0;x<MAXX;x++)
    for (k=0;k<3;k++)
        image[x][0][k] = image[x][1][k];

 for (y=0;y<MAXY;y++)
    for (k=0;k<3;k++)
        image[0][y][k] = image[1][0][k];


// Close file

 fclose(file);

// Information

 printf("Converting %s (%i,%i) to TMS9918 format ",argv[1],MAXX,MAXY);
 printf("in (%i,%i) screen 2 tiles...    ",((MAXX+7)>>3),((MAXY+7)>>3));


// Image processing

for (y=0;y<((MAXY+7)>>3);y++)
    for (j=0;j<8;j++)
        for (x=0;x<((MAXX+7)>>3);x++)
        {
            // Generate alternatives
            uchar  c1,  c2;
            uchar bc1, bc2;
            uint  bv;
            uint  bs = INT_MAX;

            uint  yy = 1+((y<<3)|j);

            for (c1=1;c1<16;c1++)
            {
                RGB cp1 = {palette[c1][0],palette[c1][1],palette[c1][2]};

                for (c2=c1+1;c2<16;c2++)
                {
                    RGB cp2 = {palette[c2][0],palette[c2][1],palette[c2][2]};

                    uint xx = 1+(x<<3);
                                       
                    RGB ppp = {clamp(image[xx][yy][0]),clamp(image[xx][yy][1]),clamp(image[xx][yy][2])};

                    uint  cs = 0;
                    uint  cv = 0;

                    for (i=0;i<8;i++)
                    {
                        short  e10 = (ppp.r-cp1.r);
                        short  e11 = (ppp.g-cp1.g);
                        short  e12 = (ppp.b-cp1.b);
                        long   mc1 = ColourDistance(cp1,ppp);

                        short  e20 = (ppp.r-cp2.r);
                        short  e21 = (ppp.g-cp2.g);
                        short  e22 = (ppp.b-cp2.b);
                        long   mc2 = ColourDistance(cp2,ppp);

                        cs += (mc1>mc2) ? mc2 : mc1;

                        if (cs>bs) break;

                        cv |= ((mc1>mc2)<<i);

                        xx++;
                        if (mc1>mc2)
                        {
                            ppp.r = clamp(image[xx][yy][0]) + 7*e20/16;
                            ppp.g = clamp(image[xx][yy][1]) + 7*e21/16;
                            ppp.b = clamp(image[xx][yy][2]) + 7*e22/16;
                        }
                        else
                        {
                            ppp.r = clamp(image[xx][yy][0]) + 7*e10/16;
                            ppp.g = clamp(image[xx][yy][1]) + 7*e11/16;
                            ppp.b = clamp(image[xx][yy][2]) + 7*e12/16;
                        }
                    }
                    if  (cs<bs)
                    {
                        bs  = cs;
                        bv  = cv;
                        bc1 = c1;
                        bc2 = c2;
                    }
                }
            }

          // Here we have the best colors and the best pattern for line j

          short quant_error;

          uint xx = 1+((x<<3));

          for (i=0;i<8;i++,xx++)
            for (k=0;k<3;k++)
            {
            // Compute the quantization error

              if (bv&(1<<i))
              {
                quant_error = (clamp(image[xx][yy][k]) - palette[bc2][k])/16;
                image[xx][yy][k] = palette[bc2][k];
              }
              else
              {
                quant_error = (clamp(image[xx][yy][k]) - palette[bc1][k])/16;
                image[xx][yy][k] = palette[bc1][k];
              }

            // Spread the quantization error

              short q2 = quant_error<<1;
              image[xx+1][yy+1][k] = clamp(image[xx+1][yy+1][k])+ quant_error; // 1 *
              quant_error += q2 ;
              image[xx-1][yy+1][k] = clamp(image[xx-1][yy+1][k])+ quant_error; // 3 *
              quant_error += q2 ;
              image[xx+0][yy+1][k] = clamp(image[xx+0][yy+1][k])+ quant_error; // 5 *
              quant_error += q2 ;
              image[xx+1][yy+0][k] = clamp(image[xx+1][yy+0][k])+ quant_error; // 7 *
            }


            // Update status counter

          if (done*100/size<(done+1)*100/size)
             printf("\b\b\b%2i%%",100*done/size);
          done++;
          total++;
        }


// Conversion done

 printf("\b\b\bOk   \n");


// Create TMS output files (CHR, CLR)

 argv[1][strlen(argv[1])-3]='C';
 argv[1][strlen(argv[1])-2]='H';
 argv[1][strlen(argv[1])-1]='R';
 CHR=fopen(argv[1],"wb");

 argv[1][strlen(argv[1])-2]='L';
 CLR=fopen(argv[1],"wb");

 fputc(0xFE,CLR);    // Binary data
 fputc(0x00,CLR);    // Start at 2000h
 fputc(0x20,CLR);
 fputc(0xFF,CLR);    // Stop at 37FFh
 fputc(0x37,CLR);
 fputc(0x00,CLR);    // Run
 fputc(0x00,CLR);


 fputc(0xFE,CHR);    // Binary data
 fputc(0x00,CHR);    // Start at 0000h
 fputc(0x00,CHR);
 fputc(0xFF,CHR);    // Stop at 17FFh
 fputc(0x17,CHR);
 fputc(0x00,CHR);    // Run
 fputc(0x00,CHR);

   // Save best pattern and colour combination
   // NOTE1:
   // THIS PART CAN BE LARGELY CUTTED AND OPTIMIZED REUSING
   // RESULTS FROM THE PREVIOUS LOOP, BUT WHO CARES?
   // NOTE2:
   // This code can be used for conversion without dithering

 for (y=0;y<((MAXY+7)>>3);y++)
    for (x=0;(x<(MAXX+7)>>3);x++)
        for (j=0;j<8;j++)
        {
            uchar c1,c2;
            uint bs = INT_MAX;
            uchar bp = 0, bc = 0;

            uint yy = 1+((y<<3)|j);

            for (c1=1;c1<16;c1++)
            {
                RGB cp1 = {palette[c1][0],palette[c1][1],palette[c1][2]};
                for (c2=c1+1;c2<16;c2++)
                {
                    RGB cp2 = {palette[c2][0],palette[c2][1],palette[c2][2]};
                    uint    cs = 0;
                    uint    cp = 0;
                    for (i=0;i<8;i++)
                    {
                        uint xx = 1+((x<<3)|i);
                        RGB ppp = {clamp(image[xx][yy][0]),clamp(image[xx][yy][1]),clamp(image[xx][yy][2])};

                        long   mc1 = ColourDistance(cp1,ppp);
                        long   mc2 = ColourDistance(cp2,ppp);

                        cp = (cp<<1) | (mc1>mc2);
                        cs += (mc1>mc2) ? mc2 : mc1;
                        if (cs>bs) break;
                    }
                    if  (cs<bs)
                    {
                        bs=cs;
                        bp=cp;
                        bc=c2*16+c1;
                    }
                }
            }

          fputc(bc,CLR);
          fputc(bp,CHR);
        }


 fclose(CHR);
 fclose(CLR);

// Generate new name

 name = malloc(0x100);
 argv[1][strlen(argv[1])-4]=0;
 strcpy(name,argv[1]);
 strcat(name,"_tms.tga");

// Save file header

 file=fopen(name,"wb");

 for (i=0;i<18;i++) fputc(header[i],file);

// Save image data

 for (y=MAXY-1;y>=0;y--)
  for (x=0;x<MAXX;x++)
   for (k=0;k<3;k++)
    fputc(inrange8(image[1+x][1+y][2-k]/scale),file);       // Scale to char

// Close file

 fclose(file);

// Prompt elapsed time

 printf("%.2f million combinations analysed in %.2f seconds.\n",total/1e6,(float)clock()/(float)CLOCKS_PER_SEC);
 printf("Note: the .CLR and .CHR files have correct headers only for 256x192 images. \n");

}
« Última modificación: 26 de Mayo de 2007, 04:35:00 pm por ARTRAG » En línea
ARTRAG
Visitante
« Respuesta #13 : 29 de Julio de 2007, 09:56:41 am »

« Última modificación: 29 de Julio de 2007, 11:27:38 am por ARTRAG » En línea
Páginas: [1]
  Imprimir  
 
Ir a:  

Impulsado por MySQL Impulsado por PHP Powered by SMF 1.1.21 | SMF © 2013, Simple Machines XHTML 1.0 válido! CSS válido!