The tool is called rgb2pct.py. But it uses some internal GDAL code. I
attached the patched versions of the sourcefiles. But be careful. The
files might have changed since the last time I used it (> 1 year)


If GDAL has to rotate your file when referencing it it has to fill the
triangles at the borders caused by the rotation. You define a
transparent color for that if you want to stitch several files. Such a
32bit color file with a transparent channel, could not be handled back
in these days. But maybe the problem is already solved by GDAL. I
haven't had a look lately.



Am 07.10.2015 um 15:53 schrieb Flo:
>
> On 10/07/15 15:28, Oliver Eichler wrote:
>> Hi Flo,
>>
>> you need either 32bit colors or all files must have the same color
>> table. If referencing does not scale/rotate the file you can apply a
>> common table right before the referencing with a normal image
>> manipulation software like Gimp. If that is not possible you can expand
>> the files to 32bit color and do the referencing. If that results into
>> files too large, you can extract referencing information, reduce the
>> colors to a common color table with Gimp, and add the information again.
>>
> Hm, file size gets much bigger, that's right if I expand the table with
> gdal_translate. Now I use compression like deflate to reduce that effect.
>
> If this is becoming a problem for me I will go into Gimp to see if I can
> do these operations from the command line in order to integrate that in
> a loop/script.
>
>> There is even a GDAL tool to reduce colors. However it does not take a
>> transparent channel into account. Years ago I patched it. But that was
>> quite quick and dirty. It never made it into
>> the official code.
>>
> How is this tool called? I don't think I have to deal with a transparent
> channel now. Thus, I could work with it without a patch.
>
> Best Regards,
> Flo.
>
>> Am 07.10.2015 um 14:55 schrieb Flo:
>>> On 10/06/15 17:45, Oliver Eichler wrote:
>>>> Hi Flo,
>>>>
>>>> the file has reference points (GCP) but no reference information as such:
>>>>
>>>> Corner Coordinates:
>>>> Upper Left  (    0.0,    0.0)
>>>> Lower Left  (    0.0, 2336.0)
>>>> Upper Right ( 3125.0,    0.0)
>>>> Lower Right ( 3125.0, 2336.0)
>>>> Center      ( 1562.5, 1168.0)
>>>>
>>>>
>>>> This has to look a bit like:
>>>>
>>>> Corner Coordinates:
>>>> Upper Left  ( 1193772.946, 5877433.181) ( 10d43'25.84"E, 46d47'40.14"N)
>>>> Lower Left  ( 1193772.946, 5756585.800) ( 10d43'25.84"E, 46d 2'37.39"N)
>>>> Upper Right ( 1400769.640, 5877433.181) ( 12d34'59.98"E, 46d47'40.14"N)
>>>> Lower Right ( 1400769.640, 5756585.800) ( 12d34'59.98"E, 46d 2'37.39"N)
>>>> Center      ( 1297271.293, 5817009.490) ( 11d39'12.91"E, 46d25'13.45"N)
>>>>
>>> Thank you. That is very valueable for me.
>>>
>>>> Try gdalwarp instead of gdal_translate.
>>>>
>>> Hit!!! It's working with gdalwrap. However I have to postprocess the
>>> output with gdal_translate to expand the color table if I build a mapset
>>> of several images. At least I haven't found an option to do so with
>>> gdalwarp.
>>>
>>> Thank you very much!
>>>
>>> Best Regards,
>>> Flo.
>>>
>>> ------------------------------------------------------------------------------
>>> Full-scale, agent-less Infrastructure Monitoring from a single dashboard
>>> Integrate with 40+ ManageEngine ITSM Solutions for complete visibility
>>> Physical-Virtual-Cloud Infrastructure monitoring from one console
>>> Real user monitoring with APM Insights and performance trend reports 
>>> Learn More http://pubads.g.doubleclick.net/gampad/clk?id=247754911&iu=/4140
>>> _______________________________________________
>>> Qlandkartegt-users mailing list
>>> [email protected]
>>> https://lists.sourceforge.net/lists/listinfo/qlandkartegt-users
>>
>> ------------------------------------------------------------------------------
>> Full-scale, agent-less Infrastructure Monitoring from a single dashboard
>> Integrate with 40+ ManageEngine ITSM Solutions for complete visibility
>> Physical-Virtual-Cloud Infrastructure monitoring from one console
>> Real user monitoring with APM Insights and performance trend reports 
>> Learn More http://pubads.g.doubleclick.net/gampad/clk?id=247754911&iu=/4140
>> _______________________________________________
>> Qlandkartegt-users mailing list
>> [email protected]
>> https://lists.sourceforge.net/lists/listinfo/qlandkartegt-users
>>
> ------------------------------------------------------------------------------
> Full-scale, agent-less Infrastructure Monitoring from a single dashboard
> Integrate with 40+ ManageEngine ITSM Solutions for complete visibility
> Physical-Virtual-Cloud Infrastructure monitoring from one console
> Real user monitoring with APM Insights and performance trend reports 
> Learn More http://pubads.g.doubleclick.net/gampad/clk?id=247754911&iu=/4140
> _______________________________________________
> Qlandkartegt-users mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/qlandkartegt-users

/******************************************************************************
 * $Id: gdaldither.cpp 13342 2007-12-14 20:58:31Z rouault $
 *
 * Project:  CIETMap Phase 2
 * Purpose:  Convert RGB (24bit) to a pseudo-colored approximation using
 *           Floyd-Steinberg dithering (error diffusion). 
 * Author:   Frank Warmerdam, [email protected]
 *
 ******************************************************************************
 * Copyright (c) 2001, Frank Warmerdam
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ******************************************************************************
 *
 * Notes:
 *
 * [1] Floyd-Steinberg dither:
 *  I should point out that the actual fractions we used were, assuming
 *  you are at X, moving left to right:
 *
 *		    X     7/16
 *	     3/16   5/16  1/16    
 *
 *  Note that the error goes to four neighbors, not three.  I think this
 *  will probably do better (at least for black and white) than the
 *  3/8-3/8-1/4 distribution, at the cost of greater processing.  I have
 *  seen the 3/8-3/8-1/4 distribution described as "our" algorithm before,
 *  but I have no idea who the credit really belongs to.
 *  -- 
 *					    Lou Steinberg
 *
 */

#include "gdal_priv.h"
#include "gdal_alg.h"

CPL_CVSID("$Id: gdaldither.cpp 13342 2007-12-14 20:58:31Z rouault $");

#define C_LEVELS	32

static void FindNearestColor( int nColors, int *panPCT, GByte *pabyColorMap );

/************************************************************************/
/*                         GDALDitherRGB2PCT()                          */
/************************************************************************/

/**
 * 24bit to 8bit conversion with dithering.
 *
 * This functions utilizes Floyd-Steinberg dithering in the process of 
 * converting a 24bit RGB image into a pseudocolored 8bit image using a
 * provided color table.  
 *
 * The red, green and blue input bands do not necessarily need to come
 * from the same file, but they must be the same width and height.  They will
 * be clipped to 8bit during reading, so non-eight bit bands are generally
 * inappropriate.  Likewise the hTarget band will be written with 8bit values
 * and must match the width and height of the source bands. 
 *
 * The color table cannot have more than 256 entries.
 *
 * @param hRed Red input band. 
 * @param hGreen Green input band. 
 * @param hBlue Blue input band. 
 * @param hTarget Output band. 
 * @param hColorTable the color table to use with the output band. 
 * @param pfnProgress callback for reporting algorithm progress matching the
 * GDALProgressFunc() semantics.  May be NULL.
 * @param pProgressArg callback argument passed to pfnProgress.
 *
 * @return CE_None on success or CE_Failure if an error occurs. 
 */

int CPL_STDCALL 
GDALDitherRGB2PCT( GDALRasterBandH hRed, 
                   GDALRasterBandH hGreen, 
                   GDALRasterBandH hBlue, 
                   GDALRasterBandH hTarget, 
                   GDALColorTableH hColorTable,
                   GDALProgressFunc pfnProgress, 
                   void * pProgressArg )

{
    VALIDATE_POINTER1( hRed, "GDALDitherRGB2PCT", CE_Failure );
    VALIDATE_POINTER1( hGreen, "GDALDitherRGB2PCT", CE_Failure );
    VALIDATE_POINTER1( hBlue, "GDALDitherRGB2PCT", CE_Failure );
    VALIDATE_POINTER1( hTarget, "GDALDitherRGB2PCT", CE_Failure );
    VALIDATE_POINTER1( hColorTable, "GDALDitherRGB2PCT", CE_Failure );

    int		nXSize, nYSize;
    CPLErr err = CE_None;
        
    int hasNoData = 0;
    int iNoDataR = -1;
    int iNoDataG = -1;
    int iNoDataB = -1;
    int iNoData = GDALGetRasterNoDataValue(hTarget, &hasNoData);
    if(hasNoData)
    {
        iNoDataR = GDALGetRasterNoDataValue(hRed, 0);
        iNoDataG = GDALGetRasterNoDataValue(hGreen, 0);
        iNoDataB = GDALGetRasterNoDataValue(hBlue, 0);
    }
    
    
/* -------------------------------------------------------------------- */
/*      Validate parameters.                                            */
/* -------------------------------------------------------------------- */
    nXSize = GDALGetRasterBandXSize( hRed );
    nYSize = GDALGetRasterBandYSize( hRed );

    if( GDALGetRasterBandXSize( hGreen ) != nXSize 
        || GDALGetRasterBandYSize( hGreen ) != nYSize 
        || GDALGetRasterBandXSize( hBlue ) != nXSize 
        || GDALGetRasterBandYSize( hBlue ) != nYSize )
    {
        CPLError( CE_Failure, CPLE_IllegalArg,
                  "Green or blue band doesn't match size of red band.\n" );

        return CE_Failure;
    }

    if( GDALGetRasterBandXSize( hTarget ) != nXSize 
        || GDALGetRasterBandYSize( hTarget ) != nYSize )
    {
        CPLError( CE_Failure, CPLE_IllegalArg,
                  "GDALDitherRGB2PCT(): "
                  "Target band doesn't match size of source bands.\n" );

        return CE_Failure;
    }

    if( pfnProgress == NULL )
        pfnProgress = GDALDummyProgress;

/* -------------------------------------------------------------------- */
/*      Setup more direct colormap.                                     */
/* -------------------------------------------------------------------- */
    int		nColors, anPCT[768], iColor, maxColor;

    nColors = GDALGetColorEntryCount( hColorTable );
    
    if(hasNoData)
    {        
        nColors -= 1;
        
    }
    
    maxColor = nColors - 1;
    
    if (nColors == 0 )
    {
        CPLError( CE_Failure, CPLE_IllegalArg,
                  "GDALDitherRGB2PCT(): "
                  "Color table must not be empty.\n" );

        return CE_Failure;
    }
    else if (nColors > 256)
    {
        CPLError( CE_Failure, CPLE_IllegalArg,
                  "GDALDitherRGB2PCT(): "
                  "Color table cannot have more than 256 entries.\n" );

        return CE_Failure;
    }
    
    for( iColor = 0; iColor < nColors; iColor++ )
    {
        GDALColorEntry	sEntry;

        GDALGetColorEntryAsRGB( hColorTable, iColor, &sEntry );
        
        anPCT[iColor    ] = sEntry.c1;
        anPCT[iColor+256] = sEntry.c2;
        anPCT[iColor+512] = sEntry.c3;
    }
    
/* -------------------------------------------------------------------- */
/*      Build a 24bit to 8 bit color mapping.                           */
/* -------------------------------------------------------------------- */
    GByte	*pabyColorMap;

    pabyColorMap = (GByte *) CPLMalloc(C_LEVELS * C_LEVELS * C_LEVELS 
                                       * sizeof(int));
    
    FindNearestColor( nColors, anPCT, pabyColorMap );

/* -------------------------------------------------------------------- */
/*      Setup various variables.                                        */
/* -------------------------------------------------------------------- */
    GByte	*pabyRed, *pabyGreen, *pabyBlue, *pabyIndex;
    int		*panError;

    pabyRed = (GByte *) VSIMalloc(nXSize);
    pabyGreen = (GByte *) VSIMalloc(nXSize);
    pabyBlue = (GByte *) VSIMalloc(nXSize);

    pabyIndex = (GByte *) VSIMalloc(nXSize);

    panError = (int *) VSICalloc(sizeof(int),(nXSize+2) * 3);
    
    if (pabyRed == NULL ||
        pabyGreen == NULL ||
        pabyBlue == NULL ||
        pabyIndex == NULL ||
        panError == NULL)
    {
        CPLError( CE_Failure, CPLE_OutOfMemory,
                  "VSIMalloc(): Out of memory in GDALDitherRGB2PCT" );
        err = CE_Failure;
        goto end_and_cleanup;
    }

/* ==================================================================== */
/*      Loop over all scanlines of data to process.                     */
/* ==================================================================== */
    int		iScanline;

    for( iScanline = 0; iScanline < nYSize; iScanline++ )
    {
        int	nLastRedError, nLastGreenError, nLastBlueError, i;

/* -------------------------------------------------------------------- */
/*      Report progress                                                 */
/* -------------------------------------------------------------------- */
        if( !pfnProgress( iScanline / (double) nYSize, NULL, pProgressArg ) )
        {
            CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" );
            err = CE_Failure;
            goto end_and_cleanup;
        }

/* -------------------------------------------------------------------- */
/*      Read source data.                                               */
/* -------------------------------------------------------------------- */
        GDALRasterIO( hRed, GF_Read, 0, iScanline, nXSize, 1, 
                      pabyRed, nXSize, 1, GDT_Byte, 0, 0 );
        GDALRasterIO( hGreen, GF_Read, 0, iScanline, nXSize, 1, 
                      pabyGreen, nXSize, 1, GDT_Byte, 0, 0 );
        GDALRasterIO( hBlue, GF_Read, 0, iScanline, nXSize, 1, 
                      pabyBlue, nXSize, 1, GDT_Byte, 0, 0 );

/* -------------------------------------------------------------------- */
/*	Apply the error from the previous line to this one.		*/
/* -------------------------------------------------------------------- */
        for( i = 0; i < nXSize; i++ )
        {
            if(pabyRed[i] != iNoDataR)
            {
                pabyRed[i] = (GByte) MAX(0,MIN(maxColor,(pabyRed[i]   + panError[i*3+0+3])));
            }
            if(pabyGreen[i] != iNoDataG)
            {
                pabyGreen[i] = (GByte) MAX(0,MIN(maxColor,(pabyGreen[i] + panError[i*3+1+3])));
            }
            if(pabyBlue[i] != iNoDataB)
            {
                pabyBlue[i] =  (GByte) MAX(0,MIN(maxColor,(pabyBlue[i]  + panError[i*3+2+3])));
            }
        }

        memset( panError, 0, sizeof(int) * (nXSize+2) * 3 );

/* -------------------------------------------------------------------- */
/*	Figure out the nearest color to the RGB value.			*/
/* -------------------------------------------------------------------- */
        nLastRedError = 0;
        nLastGreenError = 0;
        nLastBlueError = 0;

        for( i = 0; i < nXSize; i++ )
        {
            int		iIndex, nError, nSixth, iRed, iGreen, iBlue;
            int		nRedValue, nGreenValue, nBlueValue;

            if(pabyRed[i] != iNoDataR || pabyGreen[i] != iNoDataG || pabyBlue[i] != iNoDataB)
            {
                nRedValue =   MAX(0,MIN(maxColor, pabyRed[i]   + nLastRedError));
                nGreenValue = MAX(0,MIN(maxColor, pabyGreen[i] + nLastGreenError));
                nBlueValue =  MAX(0,MIN(maxColor, pabyBlue[i]  + nLastBlueError));

                iRed   = nRedValue *   C_LEVELS   / 256;
                iGreen = nGreenValue * C_LEVELS / 256;
                iBlue  = nBlueValue *  C_LEVELS  / 256;
                
                iIndex = pabyColorMap[iRed + iGreen * C_LEVELS 
                                    + iBlue * C_LEVELS * C_LEVELS];
        
                pabyIndex[i] = (GByte) iIndex;
            }
            else
            {
                pabyIndex[i] = (GByte) iNoData;
                continue;
            }
/* -------------------------------------------------------------------- */
/*      Compute Red error, and carry it on to the next error line.      */
/* -------------------------------------------------------------------- */
            nError = nRedValue - anPCT[iIndex    ];
            nSixth = nError / 6;
            
            panError[i*3    ] += nSixth;
            panError[i*3+6  ] = nSixth;
            panError[i*3+3  ] += nError - 5 * nSixth;
            
            nLastRedError = 2 * nSixth;

/* -------------------------------------------------------------------- */
/*      Compute Green error, and carry it on to the next error line.    */
/* -------------------------------------------------------------------- */
            nError = nGreenValue - anPCT[iIndex+256];
            nSixth = nError / 6;

            panError[i*3  +1] += nSixth;
            panError[i*3+6+1] = nSixth;
            panError[i*3+3+1] += nError - 5 * nSixth;
            
            nLastGreenError = 2 * nSixth;

/* -------------------------------------------------------------------- */
/*      Compute Blue error, and carry it on to the next error line.     */
/* -------------------------------------------------------------------- */
            nError = nBlueValue - anPCT[iIndex+512];
            nSixth = nError / 6;
            
            panError[i*3  +2] += nSixth;
            panError[i*3+6+2] = nSixth;
            panError[i*3+3+2] += nError - 5 * nSixth;
            
            nLastBlueError = 2 * nSixth;
        }

/* -------------------------------------------------------------------- */
/*      Write results.                                                  */
/* -------------------------------------------------------------------- */
        GDALRasterIO( hTarget, GF_Write, 0, iScanline, nXSize, 1, 
                      pabyIndex, nXSize, 1, GDT_Byte, 0, 0 );
    }

    pfnProgress( 1.0, NULL, pProgressArg );

/* -------------------------------------------------------------------- */
/*      Cleanup                                                         */
/* -------------------------------------------------------------------- */
end_and_cleanup:
    CPLFree( pabyRed );
    CPLFree( pabyGreen );
    CPLFree( pabyBlue );
    CPLFree( pabyIndex );
    CPLFree( panError );
    CPLFree( pabyColorMap );

    return err;
}

/************************************************************************/
/*                          FindNearestColor()                          */
/*                                                                      */
/*      Finear near PCT color for any RGB color.                        */
/************************************************************************/

static void FindNearestColor( int nColors, int *panPCT, GByte *pabyColorMap )

{
    int		iBlue, iGreen, iRed;
    int		iColor;

/* -------------------------------------------------------------------- */
/*	Loop over all the cells in the high density cube.		*/
/* -------------------------------------------------------------------- */
    for( iBlue = 0; iBlue < C_LEVELS; iBlue++ )
    {
	for( iGreen = 0; iGreen < C_LEVELS; iGreen++ )
	{
	    for( iRed = 0; iRed < C_LEVELS; iRed++ )
	    {
		int  	nRedValue, nGreenValue, nBlueValue;
		int	nBestDist = 768, nBestIndex = 0;

		nRedValue   = (iRed * 255) / (C_LEVELS-1);
		nGreenValue = (iGreen * 255) / (C_LEVELS-1);
		nBlueValue  = (iBlue * 255) / (C_LEVELS-1);

		for( iColor = 0; iColor < nColors; iColor++ )
		{
		    int		nThisDist;

		    nThisDist = ABS(nRedValue   - panPCT[iColor    ]) 
		              + ABS(nGreenValue - panPCT[iColor+256])
		              + ABS(nBlueValue  - panPCT[iColor+512]);

		    if( nThisDist < nBestDist )
		    {
			nBestIndex = iColor;
			nBestDist = nThisDist;
		    }
		}
		
		pabyColorMap[iRed + iGreen*C_LEVELS 
                            + iBlue*C_LEVELS*C_LEVELS] = (GByte)nBestIndex;
	    }
	}
    }
}

/******************************************************************************
 * $Id: gdalmediancut.cpp 20629 2010-09-16 19:08:40Z rouault $
 *
 * Project:  CIETMap Phase 2
 * Purpose:  Use median cut algorithm to generate an near-optimal PCT for a 
 *           given RGB image.  Implemented as function GDALComputeMedianCutPCT.
 * Author:   Frank Warmerdam, [email protected]
 *
 ******************************************************************************
 * Copyright (c) 2001, Frank Warmerdam
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ******************************************************************************
 *
 * This code was based on the tiffmedian.c code from libtiff (www.libtiff.org)
 * which was based on a paper by Paul Heckbert:
 *
 *      "Color  Image Quantization for Frame Buffer Display", Paul
 *      Heckbert, SIGGRAPH proceedings, 1982, pp. 297-307.
 * 
 */

#include "gdal_priv.h"
#include "gdal_alg.h"

CPL_CVSID("$Id: gdalmediancut.cpp 20629 2010-09-16 19:08:40Z rouault $");

#define	MAX_CMAP_SIZE	256

#define	COLOR_DEPTH	8
#define	MAX_COLOR	256

#define	GMC_B_DEPTH		5		/* # bits/pixel to use */
#define	GMC_B_LEN		(1L<<GMC_B_DEPTH)

#define	COLOR_SHIFT	(COLOR_DEPTH-GMC_B_DEPTH)

typedef	struct colorbox {
	struct	colorbox *next, *prev;
	int	rmin, rmax;
	int	gmin, gmax;
	int	bmin, bmax;
	int	total;
} Colorbox;

static	void splitbox(Colorbox* ptr, int	(*histogram)[GMC_B_LEN][GMC_B_LEN],
                      Colorbox **pfreeboxes, Colorbox **pusedboxes);
static	void shrinkbox(Colorbox* box, int	(*histogram)[GMC_B_LEN][GMC_B_LEN]);
static	Colorbox* largest_box(Colorbox *usedboxes);

/************************************************************************/
/*                      GDALComputeMedianCutPCT()                       */
/************************************************************************/

/**
 * Compute optimal PCT for RGB image.
 *
 * This function implements a median cut algorithm to compute an "optimal"
 * pseudocolor table for representing an input RGB image.  This PCT could
 * then be used with GDALDitherRGB2PCT() to convert a 24bit RGB image into
 * an eightbit pseudo-colored image. 
 *
 * This code was based on the tiffmedian.c code from libtiff (www.libtiff.org)
 * which was based on a paper by Paul Heckbert:
 *
 * \verbatim
 *   "Color  Image Quantization for Frame Buffer Display", Paul
 *   Heckbert, SIGGRAPH proceedings, 1982, pp. 297-307.
 * \endverbatim
 *
 * The red, green and blue input bands do not necessarily need to come
 * from the same file, but they must be the same width and height.  They will
 * be clipped to 8bit during reading, so non-eight bit bands are generally
 * inappropriate. 
 *
 * @param hRed Red input band. 
 * @param hGreen Green input band. 
 * @param hBlue Blue input band. 
 * @param pfnIncludePixel function used to test which pixels should be included
 * in the analysis.  At this time this argument is ignored and all pixels are
 * utilized.  This should normally be NULL.
 * @param nColors the desired number of colors to be returned (2-256).
 * @param hColorTable the colors will be returned in this color table object.
 * @param pfnProgress callback for reporting algorithm progress matching the
 * GDALProgressFunc() semantics.  May be NULL.
 * @param pProgressArg callback argument passed to pfnProgress.
 *
 * @return returns CE_None on success or CE_Failure if an error occurs. 
 */

extern "C" int CPL_STDCALL
GDALComputeMedianCutPCT( GDALRasterBandH hRed, 
                         GDALRasterBandH hGreen, 
                         GDALRasterBandH hBlue, 
                         int (*pfnIncludePixel)(int,int,void*),
                         int nColors, 
                         GDALColorTableH hColorTable,
                         GDALProgressFunc pfnProgress, 
                         void * pProgressArg )

{
    VALIDATE_POINTER1( hRed, "GDALComputeMedianCutPCT", CE_Failure );
    VALIDATE_POINTER1( hGreen, "GDALComputeMedianCutPCT", CE_Failure );
    VALIDATE_POINTER1( hBlue, "GDALComputeMedianCutPCT", CE_Failure );

    int		nXSize, nYSize;
    CPLErr err = CE_None;
    
    int iNoDataOkR  = 0;
    int iNoDataOkG  = 0;
    int iNoDataOkB  = 0;
    
    int iNoDataR = GDALGetRasterNoDataValue(hRed, &iNoDataOkR);
    int iNoDataG = GDALGetRasterNoDataValue(hGreen, &iNoDataOkG);
    int iNoDataB = GDALGetRasterNoDataValue(hBlue, &iNoDataOkB);
    
    int hasNoData = iNoDataOkR && iNoDataOkG && iNoDataOkB;
    if(hasNoData)
    { // decrease nColors as the last entry will be the no data entry
        nColors -= 1;        
    }

/* -------------------------------------------------------------------- */
/*      Validate parameters.                                            */
/* -------------------------------------------------------------------- */
    nXSize = GDALGetRasterBandXSize( hRed );
    nYSize = GDALGetRasterBandYSize( hRed );

    if( GDALGetRasterBandXSize( hGreen ) != nXSize 
        || GDALGetRasterBandYSize( hGreen ) != nYSize 
        || GDALGetRasterBandXSize( hBlue ) != nXSize 
        || GDALGetRasterBandYSize( hBlue ) != nYSize )
    {
        CPLError( CE_Failure, CPLE_IllegalArg,
                  "Green or blue band doesn't match size of red band.\n" );

        return CE_Failure;
    }

    if( pfnIncludePixel != NULL )
    {
        CPLError( CE_Failure, CPLE_IllegalArg,
                  "GDALComputeMedianCutPCT() doesn't currently support "
                  " pfnIncludePixel function." );

        return CE_Failure;
    }

    if ( nColors <= 0 )
    {
        CPLError( CE_Failure, CPLE_IllegalArg,
                  "GDALComputeMedianCutPCT() : nColors must be strictly greater than 1." );

        return CE_Failure;
    }

    if ( nColors > 256 )
    {
        CPLError( CE_Failure, CPLE_IllegalArg,
                  "GDALComputeMedianCutPCT() : nColors must be lesser than or equal to 256." );

        return CE_Failure;
    }

    if( pfnProgress == NULL )
        pfnProgress = GDALDummyProgress;

/* ==================================================================== */
/*      STEP 1: crate empty boxes.                                      */
/* ==================================================================== */
    int	     i;
    Colorbox *box_list, *ptr;
    int	(*histogram)[GMC_B_LEN][GMC_B_LEN];
    Colorbox *freeboxes;
    Colorbox *usedboxes;

    histogram = (int (*)[GMC_B_LEN][GMC_B_LEN]) 
        CPLCalloc(GMC_B_LEN * GMC_B_LEN * GMC_B_LEN,sizeof(int));
    usedboxes = NULL;
    box_list = freeboxes = (Colorbox *)CPLMalloc(nColors*sizeof (Colorbox));
    freeboxes[0].next = &freeboxes[1];
    freeboxes[0].prev = NULL;
    for (i = 1; i < nColors-1; ++i) {
        freeboxes[i].next = &freeboxes[i+1];
        freeboxes[i].prev = &freeboxes[i-1];
    }
    freeboxes[nColors-1].next = NULL;
    freeboxes[nColors-1].prev = &freeboxes[nColors-2];

/* ==================================================================== */
/*      Build histogram.                                                */
/* ==================================================================== */
    GByte	*pabyRedLine, *pabyGreenLine, *pabyBlueLine;
    int		iLine, iPixel;

/* -------------------------------------------------------------------- */
/*      Initialize the box datastructures.                              */
/* -------------------------------------------------------------------- */
    ptr = freeboxes;
    freeboxes = ptr->next;
    if (freeboxes)
        freeboxes->prev = NULL;
    ptr->next = usedboxes;
    usedboxes = ptr;
    if (ptr->next)
        ptr->next->prev = ptr;

    ptr->rmin = ptr->gmin = ptr->bmin = 999;
    ptr->rmax = ptr->gmax = ptr->bmax = -1;
    ptr->total = nXSize * nYSize;

/* -------------------------------------------------------------------- */
/*      Collect histogram.                                              */
/* -------------------------------------------------------------------- */
    pabyRedLine = (GByte *) VSIMalloc(nXSize);
    pabyGreenLine = (GByte *) VSIMalloc(nXSize);
    pabyBlueLine = (GByte *) VSIMalloc(nXSize);
    
    if (pabyRedLine == NULL ||
        pabyGreenLine == NULL ||
        pabyBlueLine == NULL)
    {
        CPLError( CE_Failure, CPLE_OutOfMemory,
                  "VSIMalloc(): Out of memory in GDALComputeMedianCutPCT" );
        err = CE_Failure;
        goto end_and_cleanup;
    }

    for( iLine = 0; iLine < nYSize; iLine++ )
    {
        if( !pfnProgress( iLine / (double) nYSize, 
                          "Generating Histogram", pProgressArg ) )
        {
            CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" );
            err = CE_Failure;
            goto end_and_cleanup;
        }

        GDALRasterIO( hRed, GF_Read, 0, iLine, nXSize, 1, 
                      pabyRedLine, nXSize, 1, GDT_Byte, 0, 0 );
        GDALRasterIO( hGreen, GF_Read, 0, iLine, nXSize, 1, 
                      pabyGreenLine, nXSize, 1, GDT_Byte, 0, 0 );
        GDALRasterIO( hBlue, GF_Read, 0, iLine, nXSize, 1, 
                      pabyBlueLine, nXSize, 1, GDT_Byte, 0, 0 );

        for( iPixel = 0; iPixel < nXSize; iPixel++ )
        {
            int	nRed, nGreen, nBlue;
                        
            nRed = pabyRedLine[iPixel] >> COLOR_SHIFT;
            nGreen = pabyGreenLine[iPixel] >> COLOR_SHIFT;
            nBlue = pabyBlueLine[iPixel] >> COLOR_SHIFT;

            if(hasNoData)
            {
                if(nRed == pabyRedLine[iPixel] && nGreen == pabyGreenLine[iPixel] && nBlue == pabyBlueLine[iPixel])
                {   // skip no data colors
                    continue;
                }
            }
            
            ptr->rmin = MIN(ptr->rmin, nRed);
            ptr->gmin = MIN(ptr->gmin, nGreen);
            ptr->bmin = MIN(ptr->bmin, nBlue);
            ptr->rmax = MAX(ptr->rmax, nRed);
            ptr->gmax = MAX(ptr->gmax, nGreen);
            ptr->bmax = MAX(ptr->bmax, nBlue);

            histogram[nRed][nGreen][nBlue]++;
        }
    }

    if( !pfnProgress( 1.0, "Generating Histogram", pProgressArg ) )
    {
        CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" );
        err = CE_Failure;
        goto end_and_cleanup;
    }

/* ==================================================================== */
/*      STEP 3: continually subdivide boxes until no more free          */
/*      boxes remain or until all colors assigned.                      */
/* ==================================================================== */
    while (freeboxes != NULL) {
        ptr = largest_box(usedboxes);
        if (ptr != NULL)
            splitbox(ptr, histogram, &freeboxes, &usedboxes);
        else
            freeboxes = NULL;
    }

/* ==================================================================== */
/*      STEP 4: assign colors to all boxes                              */
/* ==================================================================== */
    for (i = 0, ptr = usedboxes; ptr != NULL; ++i, ptr = ptr->next) 
    {
        GDALColorEntry	sEntry;

        sEntry.c1 = (GByte) (((ptr->rmin + ptr->rmax) << COLOR_SHIFT) / 2);
        sEntry.c2 = (GByte) (((ptr->gmin + ptr->gmax) << COLOR_SHIFT) / 2);
        sEntry.c3 = (GByte) (((ptr->bmin + ptr->bmax) << COLOR_SHIFT) / 2);
        sEntry.c4 = 255;
        GDALSetColorEntry( hColorTable, i, &sEntry );
    }
    
    if(hasNoData)
    {   // add no data entry
        GDALColorEntry  sEntry;
        sEntry.c1 = (GByte) (iNoDataR);
        sEntry.c2 = (GByte) (iNoDataG);
        sEntry.c3 = (GByte) (iNoDataB);
        sEntry.c4 = 0;
        GDALSetColorEntry( hColorTable, nColors, &sEntry );        
    }

end_and_cleanup:
    CPLFree( pabyRedLine );
    CPLFree( pabyGreenLine );
    CPLFree( pabyBlueLine );

    /* We're done with the boxes now */
    CPLFree(box_list);
    freeboxes = usedboxes = NULL;

    CPLFree( histogram );
    
    return err;
}

/************************************************************************/
/*                            largest_box()                             */
/************************************************************************/

static Colorbox *
largest_box(Colorbox *usedboxes)
{
    Colorbox *p, *b;
    int size;

    b = NULL;
    size = -1;
    for (p = usedboxes; p != NULL; p = p->next)
        if ((p->rmax > p->rmin || p->gmax > p->gmin ||
             p->bmax > p->bmin) &&  p->total > size)
            size = (b = p)->total;
    return (b);
}

/************************************************************************/
/*                              splitbox()                              */
/************************************************************************/
static void
splitbox(Colorbox* ptr, int	(*histogram)[GMC_B_LEN][GMC_B_LEN],
         Colorbox **pfreeboxes, Colorbox **pusedboxes)
{
    int		hist2[GMC_B_LEN];
    int		first=0, last=0;
    Colorbox	*new_cb;
    int	*iptr, *histp;
    int	i, j;
    int	ir,ig,ib;
    int sum, sum1, sum2;
    enum { RED, GREEN, BLUE } axis;

    /*
     * See which axis is the largest, do a histogram along that
     * axis.  Split at median point.  Contract both new boxes to
     * fit points and return
     */
    i = ptr->rmax - ptr->rmin;
    if (i >= ptr->gmax - ptr->gmin  && i >= ptr->bmax - ptr->bmin)
        axis = RED;
    else if (ptr->gmax - ptr->gmin >= ptr->bmax - ptr->bmin)
        axis = GREEN;
    else
        axis = BLUE;
    /* get histogram along longest axis */
    switch (axis) {
      case RED:
        histp = &hist2[ptr->rmin];
        for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) {
            *histp = 0;
            for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) {
                iptr = &histogram[ir][ig][ptr->bmin];
                for (ib = ptr->bmin; ib <= ptr->bmax; ++ib)
                    *histp += *iptr++;
            }
            histp++;
        }
        first = ptr->rmin;
        last = ptr->rmax;
        break;
      case GREEN:
        histp = &hist2[ptr->gmin];
        for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) {
            *histp = 0;
            for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) {
                iptr = &histogram[ir][ig][ptr->bmin];
                for (ib = ptr->bmin; ib <= ptr->bmax; ++ib)
                    *histp += *iptr++;
            }
            histp++;
        }
        first = ptr->gmin;
        last = ptr->gmax;
        break;
      case BLUE:
        histp = &hist2[ptr->bmin];
        for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) {
            *histp = 0;
            for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) {
                iptr = &histogram[ir][ptr->gmin][ib];
                for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) {
                    *histp += *iptr;
                    iptr += GMC_B_LEN;
                }
            }
            histp++;
        }
        first = ptr->bmin;
        last = ptr->bmax;
        break;
    }
    /* find median point */
    sum2 = ptr->total / 2;
    histp = &hist2[first];
    sum = 0;
    for (i = first; i <= last && (sum += *histp++) < sum2; ++i)
        ;
    if (i == first)
        i++;

    /* Create new box, re-allocate points */
    new_cb = *pfreeboxes;
    *pfreeboxes = new_cb->next;
    if (*pfreeboxes)
        (*pfreeboxes)->prev = NULL;
    if (*pusedboxes)
        (*pusedboxes)->prev = new_cb;
    new_cb->next = *pusedboxes;
    *pusedboxes = new_cb;

    histp = &hist2[first];
    for (sum1 = 0, j = first; j < i; j++)
        sum1 += *histp++;
    for (sum2 = 0, j = i; j <= last; j++)
        sum2 += *histp++;
    new_cb->total = sum1;
    ptr->total = sum2;

    new_cb->rmin = ptr->rmin;
    new_cb->rmax = ptr->rmax;
    new_cb->gmin = ptr->gmin;
    new_cb->gmax = ptr->gmax;
    new_cb->bmin = ptr->bmin;
    new_cb->bmax = ptr->bmax;
    switch (axis) {
      case RED:
        new_cb->rmax = i-1;
        ptr->rmin = i;
        break;
      case GREEN:
        new_cb->gmax = i-1;
        ptr->gmin = i;
        break;
      case BLUE:
        new_cb->bmax = i-1;
        ptr->bmin = i;
        break;
    }
    shrinkbox(new_cb, histogram);
    shrinkbox(ptr, histogram);
}

/************************************************************************/
/*                             shrinkbox()                              */
/************************************************************************/
static void
shrinkbox(Colorbox* box, int	(*histogram)[GMC_B_LEN][GMC_B_LEN])
{
    int *histp, ir, ig, ib;

    if (box->rmax > box->rmin) {
        for (ir = box->rmin; ir <= box->rmax; ++ir)
            for (ig = box->gmin; ig <= box->gmax; ++ig) {
                histp = &histogram[ir][ig][box->bmin];
                for (ib = box->bmin; ib <= box->bmax; ++ib)
                    if (*histp++ != 0) {
                        box->rmin = ir;
                        goto have_rmin;
                    }
            }
      have_rmin:
        if (box->rmax > box->rmin)
            for (ir = box->rmax; ir >= box->rmin; --ir)
                for (ig = box->gmin; ig <= box->gmax; ++ig) {
                    histp = &histogram[ir][ig][box->bmin];
                    ib = box->bmin;
                    for (; ib <= box->bmax; ++ib)
                        if (*histp++ != 0) {
                            box->rmax = ir;
                            goto have_rmax;
                        }
                }
    }
  have_rmax:
    if (box->gmax > box->gmin) {
        for (ig = box->gmin; ig <= box->gmax; ++ig)
            for (ir = box->rmin; ir <= box->rmax; ++ir) {
                histp = &histogram[ir][ig][box->bmin];
                for (ib = box->bmin; ib <= box->bmax; ++ib)
                    if (*histp++ != 0) {
                        box->gmin = ig;
                        goto have_gmin;
                    }
            }
      have_gmin:
        if (box->gmax > box->gmin)
            for (ig = box->gmax; ig >= box->gmin; --ig)
                for (ir = box->rmin; ir <= box->rmax; ++ir) {
                    histp = &histogram[ir][ig][box->bmin];
                    ib = box->bmin;
                    for (; ib <= box->bmax; ++ib)
                        if (*histp++ != 0) {
                            box->gmax = ig;
                            goto have_gmax;
                        }
                }
    }
  have_gmax:
    if (box->bmax > box->bmin) {
        for (ib = box->bmin; ib <= box->bmax; ++ib)
            for (ir = box->rmin; ir <= box->rmax; ++ir) {
                histp = &histogram[ir][box->gmin][ib];
                for (ig = box->gmin; ig <= box->gmax; ++ig) {
                    if (*histp != 0) {
                        box->bmin = ib;
                        goto have_bmin;
                    }
                    histp += GMC_B_LEN;
                }
            }
      have_bmin:
        if (box->bmax > box->bmin)
            for (ib = box->bmax; ib >= box->bmin; --ib)
                for (ir = box->rmin; ir <= box->rmax; ++ir) {
                    histp = &histogram[ir][box->gmin][ib];
                    ig = box->gmin;
                    for (; ig <= box->gmax; ++ig) {
                        if (*histp != 0) {
                            box->bmax = ib;
                            goto have_bmax;
                        }
                        histp += GMC_B_LEN;
                    }
                }
    }
  have_bmax:
    ;
}
------------------------------------------------------------------------------
Full-scale, agent-less Infrastructure Monitoring from a single dashboard
Integrate with 40+ ManageEngine ITSM Solutions for complete visibility
Physical-Virtual-Cloud Infrastructure monitoring from one console
Real user monitoring with APM Insights and performance trend reports 
Learn More http://pubads.g.doubleclick.net/gampad/clk?id=247754911&iu=/4140
_______________________________________________
Qlandkartegt-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/qlandkartegt-users

Reply via email to