static void splitbox(Colorbox* ptr, const int* histogram, const HashHistogram* psHashHistogram, int nCLevels, Colorbox **pfreeboxes, Colorbox **pusedboxes, GByte* pabyRedBand, GByte* pabyGreenBand, GByte* pabyBlueBand, int nPixels) { int hist2[256]; int first=0, last=0; Colorbox *new_cb; const int *iptr; int *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 */ int nIters = (ptr->rmax - ptr->rmin + 1) * (ptr->gmax - ptr->gmin + 1) * (ptr->bmax - ptr->bmin + 1); //printf("nIters = %d\n", nIters); switch (axis) { case RED: { if( nPixels != 0 && nIters > nPixels ) { memset(hist2, 0, sizeof(hist2)); const int rmin = ptr->rmin, rmax = ptr->rmax, gmin = ptr->gmin, gmax = ptr->gmax, bmin = ptr->bmin, bmax = ptr->bmax; for(int i=0; i<nPixels; i++) { int iR = pabyRedBand[i]; int iG = pabyGreenBand[i]; int iB = pabyBlueBand[i]; if( iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax && iB >= bmin && iB <= bmax ) { hist2[iR] ++; } } } else if( psHashHistogram ) { histp = &hist2[ptr->rmin]; for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { *histp = 0; for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) { *histp += FindColorCount(psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib)); } } histp++; } } else { histp = &hist2[ptr->rmin]; for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { *histp = 0; for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { iptr = &HISTOGRAM(histogram,nCLevels,ir,ig,ptr->bmin); for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) *histp += *iptr++; } histp++; } } first = ptr->rmin; last = ptr->rmax; break; } case GREEN: { if( nPixels != 0 && nIters > nPixels ) { memset(hist2, 0, sizeof(hist2)); const int rmin = ptr->rmin, rmax = ptr->rmax, gmin = ptr->gmin, gmax = ptr->gmax, bmin = ptr->bmin, bmax = ptr->bmax; for(int i=0; i<nPixels; i++) { int iR = pabyRedBand[i]; int iG = pabyGreenBand[i]; int iB = pabyBlueBand[i]; if( iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax && iB >= bmin && iB <= bmax ) { hist2[iG] ++; } } } else if( psHashHistogram ) { histp = &hist2[ptr->gmin]; for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { *histp = 0; for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) { *histp += FindColorCount(psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib)); } } histp++; } } else { histp = &hist2[ptr->gmin]; for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { *histp = 0; for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { iptr = &HISTOGRAM(histogram,nCLevels,ir,ig,ptr->bmin); for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) *histp += *iptr++; } histp++; } } first = ptr->gmin; last = ptr->gmax; break; } case BLUE: { if( nPixels != 0 && nIters > nPixels ) { memset(hist2, 0, sizeof(hist2)); const int rmin = ptr->rmin, rmax = ptr->rmax, gmin = ptr->gmin, gmax = ptr->gmax, bmin = ptr->bmin, bmax = ptr->bmax; for(int i=0; i<nPixels; i++) { int iR = pabyRedBand[i]; int iG = pabyGreenBand[i]; int iB = pabyBlueBand[i]; if( iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax && iB >= bmin && iB <= bmax ) { hist2[iB] ++; } } } else if( psHashHistogram ) { histp = &hist2[ptr->bmin]; for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) { *histp = 0; for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { *histp += FindColorCount(psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib)); } } histp++; } } else { histp = &hist2[ptr->bmin]; for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) { *histp = 0; for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { iptr = &HISTOGRAM(histogram,nCLevels,ir,ptr->gmin,ib); for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { *histp += *iptr; iptr += nCLevels; } } 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; } if( nPixels != 0 && (new_cb->rmax - new_cb->rmin + 1) * (new_cb->gmax - new_cb->gmin + 1) * (new_cb->bmax - new_cb->bmin + 1) > nPixels ) { shrinkboxFromBand(new_cb, pabyRedBand, pabyGreenBand, pabyBlueBand, nPixels); } else if( psHashHistogram != NULL ) { shrinkboxFromHashHistogram(new_cb, psHashHistogram); } else { shrinkbox(new_cb, histogram, nCLevels); } if( nPixels != 0 && (ptr->rmax - ptr->rmin + 1) * (ptr->gmax - ptr->gmin + 1) * (ptr->bmax - ptr->bmin + 1) > nPixels ) { shrinkboxFromBand(ptr, pabyRedBand, pabyGreenBand, pabyBlueBand, nPixels); } else if( psHashHistogram != NULL ) { shrinkboxFromHashHistogram(ptr, psHashHistogram); } else { shrinkbox(ptr, histogram, nCLevels); } }
int GDALComputeMedianCutPCTInternal( GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue, GByte* pabyRedBand, GByte* pabyGreenBand, GByte* pabyBlueBand, int (*pfnIncludePixel)(int,int,void*), int nColors, int nBits, int* panHistogram, /* NULL, or at least of size (1<<nBits)^3 * sizeof(int) bytes */ 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; /* -------------------------------------------------------------------- */ /* 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: create empty boxes. */ /* ==================================================================== */ int i; Colorbox *box_list, *ptr; int* histogram; Colorbox *freeboxes; Colorbox *usedboxes; int nCLevels = 1 << nBits; int nColorShift = 8 - nBits; int nColorCounter = 0; GByte anRed[256], anGreen[256], anBlue[256]; int nPixels = 0; HashHistogram* psHashHistogram = NULL; if( nBits == 8 && pabyRedBand != NULL && pabyGreenBand != NULL && pabyBlueBand != NULL && nXSize < INT_MAX / nYSize ) { nPixels = nXSize * nYSize; } if( panHistogram ) { if( nBits == 8 && (GIntBig)nXSize * nYSize <= 65536 ) { /* If the image is small enough, then the number of colors */ /* will be limited and using a hashmap, rather than a full table */ /* will be more efficient */ histogram = NULL; psHashHistogram = (HashHistogram*)panHistogram; memset(psHashHistogram, 0xFF, sizeof(HashHistogram) * PRIME_FOR_65536); } else { histogram = panHistogram; memset(histogram, 0, nCLevels*nCLevels*nCLevels * sizeof(int)); } } else { histogram = (int*) VSICalloc(nCLevels*nCLevels*nCLevels,sizeof(int)); if( histogram == NULL ) { CPLError( CE_Failure, CPLE_OutOfMemory, "VSICalloc(): Out of memory in GDALComputeMedianCutPCT" ); return CE_Failure; } } 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] >> nColorShift; nGreen = pabyGreenLine[iPixel] >> nColorShift; nBlue = pabyBlueLine[iPixel] >> nColorShift; 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); int* pnColor; if( psHashHistogram ) { pnColor = FindAndInsertColorCount(psHashHistogram, MAKE_COLOR_CODE(nRed, nGreen, nBlue)); } else { pnColor = &HISTOGRAM(histogram, nCLevels, nRed, nGreen, nBlue); } if( *pnColor == 0 ) { if( nColorShift == 0 && nColorCounter < nColors ) { anRed[nColorCounter] = nRed; anGreen[nColorCounter] = nGreen; anBlue[nColorCounter] = nBlue; } nColorCounter++; } (*pnColor) ++; } } if( !pfnProgress( 1.0, "Generating Histogram", pProgressArg ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" ); err = CE_Failure; goto end_and_cleanup; } if( nColorShift == 0 && nColorCounter <= nColors ) { //CPLDebug("MEDIAN_CUT", "%d colors found <= %d", nColorCounter, nColors); for(int iColor = 0; iColor<nColorCounter; iColor++) { GDALColorEntry sEntry; sEntry.c1 = (GByte) anRed[iColor]; sEntry.c2 = (GByte) anGreen[iColor]; sEntry.c3 = (GByte) anBlue[iColor]; sEntry.c4 = 255; GDALSetColorEntry( hColorTable, iColor, &sEntry ); } 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, psHashHistogram, nCLevels, &freeboxes, &usedboxes, pabyRedBand, pabyGreenBand, pabyBlueBand, nPixels); 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) << nColorShift) / 2); sEntry.c2 = (GByte) (((ptr->gmin + ptr->gmax) << nColorShift) / 2); sEntry.c3 = (GByte) (((ptr->bmin + ptr->bmax) << nColorShift) / 2); sEntry.c4 = 255; GDALSetColorEntry( hColorTable, i, &sEntry ); } end_and_cleanup: CPLFree( pabyRedLine ); CPLFree( pabyGreenLine ); CPLFree( pabyBlueLine ); /* We're done with the boxes now */ CPLFree(box_list); freeboxes = usedboxes = NULL; if( panHistogram == NULL ) CPLFree( histogram ); return err; }
static void shrinkbox(Colorbox* box, const int* histogram, int nCLevels) { const int *histp; int ir, ig, ib; //int count_iter; if (box->rmax > box->rmin) { //count_iter = 0; for (ir = box->rmin; ir <= box->rmax; ++ir) { for (ig = box->gmin; ig <= box->gmax; ++ig) { histp = &HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); for (ib = box->bmin; ib <= box->bmax; ++ib) { //count_iter ++; if (*histp++ != 0) { //if( count_iter > 65536 ) printf("iter rmin=%d\n", count_iter); box->rmin = ir; goto have_rmin; } } } } } have_rmin: if (box->rmax > box->rmin) { //count_iter = 0; for (ir = box->rmax; ir >= box->rmin; --ir) { for (ig = box->gmin; ig <= box->gmax; ++ig) { histp = &HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); ib = box->bmin; for (; ib <= box->bmax; ++ib) { //count_iter ++; if (*histp++ != 0) { //if( count_iter > 65536 ) printf("iter rmax=%d\n", count_iter); box->rmax = ir; goto have_rmax; } } } } } have_rmax: if (box->gmax > box->gmin) { //count_iter = 0; for (ig = box->gmin; ig <= box->gmax; ++ig) { for (ir = box->rmin; ir <= box->rmax; ++ir) { histp = &HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); for (ib = box->bmin; ib <= box->bmax; ++ib) { //count_iter ++; if (*histp++ != 0) { //if( count_iter > 65536 ) printf("iter gmin=%d\n", count_iter); box->gmin = ig; goto have_gmin; } } } } } have_gmin: if (box->gmax > box->gmin) { //count_iter = 0; for (ig = box->gmax; ig >= box->gmin; --ig) { for (ir = box->rmin; ir <= box->rmax; ++ir) { histp = &HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); ib = box->bmin; for (; ib <= box->bmax; ++ib) { //count_iter ++; if (*histp++ != 0) { //if( count_iter > 65536 ) printf("iter gmax=%d\n", count_iter); box->gmax = ig; goto have_gmax; } } } } } have_gmax: if (box->bmax > box->bmin) { //count_iter = 0; for (ib = box->bmin; ib <= box->bmax; ++ib) { for (ir = box->rmin; ir <= box->rmax; ++ir) { histp = &HISTOGRAM(histogram, nCLevels, ir, box->gmin, ib); for (ig = box->gmin; ig <= box->gmax; ++ig) { //count_iter ++; if (*histp != 0) { //if( count_iter > 65536 ) printf("iter bmin=%d\n", count_iter); box->bmin = ib; goto have_bmin; } histp += nCLevels; } } } } have_bmin: if (box->bmax > box->bmin) { //count_iter = 0; for (ib = box->bmax; ib >= box->bmin; --ib) { for (ir = box->rmin; ir <= box->rmax; ++ir) { histp = &HISTOGRAM(histogram, nCLevels, ir, box->gmin, ib); ig = box->gmin; for (; ig <= box->gmax; ++ig) { //count_iter ++; if (*histp != 0) { //if( count_iter > 65536 ) printf("iter bmax=%d\n", count_iter); box->bmax = ib; goto have_bmax; } histp += nCLevels; } } } } have_bmax: ; }
template<class T> static void splitbox(Colorbox* ptr, const T* histogram, const HashHistogram* psHashHistogram, int nCLevels, Colorbox **pfreeboxes, Colorbox **pusedboxes, GByte* pabyRedBand, GByte* pabyGreenBand, GByte* pabyBlueBand, T nPixels) { T hist2[256] = {}; int first = 0; int last = 0; 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. { int 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. const GUInt32 nIters = (ptr->rmax - ptr->rmin + 1) * (ptr->gmax - ptr->gmin + 1) * (ptr->bmax - ptr->bmin + 1); switch( axis ) { case RED: { if( nPixels != 0 && nIters > nPixels ) { const int rmin = ptr->rmin; const int rmax = ptr->rmax; const int gmin = ptr->gmin; const int gmax = ptr->gmax; const int bmin = ptr->bmin; const int bmax = ptr->bmax; for( T iPixel = 0; iPixel < nPixels; iPixel++ ) { int iR = pabyRedBand[iPixel]; int iG = pabyGreenBand[iPixel]; int iB = pabyBlueBand[iPixel]; if( iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax && iB >= bmin && iB <= bmax ) { hist2[iR]++; } } } else if( psHashHistogram ) { T *histp = &hist2[ptr->rmin]; for( int ir = ptr->rmin; ir <= ptr->rmax; ++ir ) { *histp = 0; for( int ig = ptr->gmin; ig <= ptr->gmax; ++ig ) { for( int ib = ptr->bmin; ib <= ptr->bmax; ++ib ) { *histp += FindColorCount(psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib)); } } histp++; } } else { T *histp = &hist2[ptr->rmin]; for( int ir = ptr->rmin; ir <= ptr->rmax; ++ir ) { *histp = 0; for( int ig = ptr->gmin; ig <= ptr->gmax; ++ig ) { const T *iptr = HISTOGRAM(histogram, nCLevels, ir, ig, ptr->bmin); for( int ib = ptr->bmin; ib <= ptr->bmax; ++ib ) *histp += *iptr++; } histp++; } } first = ptr->rmin; last = ptr->rmax; break; } case GREEN: { if( nPixels != 0 && nIters > nPixels ) { const int rmin = ptr->rmin; const int rmax = ptr->rmax; const int gmin = ptr->gmin; const int gmax = ptr->gmax; const int bmin = ptr->bmin; const int bmax = ptr->bmax; for( T iPixel = 0; iPixel < nPixels; iPixel++ ) { const int iR = pabyRedBand[iPixel]; const int iG = pabyGreenBand[iPixel]; const int iB = pabyBlueBand[iPixel]; if( iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax && iB >= bmin && iB <= bmax ) { hist2[iG]++; } } } else if( psHashHistogram ) { T *histp = &hist2[ptr->gmin]; for( int ig = ptr->gmin; ig <= ptr->gmax; ++ig ) { *histp = 0; for( int ir = ptr->rmin; ir <= ptr->rmax; ++ir ) { for( int ib = ptr->bmin; ib <= ptr->bmax; ++ib ) { *histp += FindColorCount(psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib)); } } histp++; } } else { T *histp = &hist2[ptr->gmin]; for( int ig = ptr->gmin; ig <= ptr->gmax; ++ig ) { *histp = 0; for( int ir = ptr->rmin; ir <= ptr->rmax; ++ir ) { const T *iptr = HISTOGRAM(histogram, nCLevels, ir, ig, ptr->bmin); for( int ib = ptr->bmin; ib <= ptr->bmax; ++ib ) *histp += *iptr++; } histp++; } } first = ptr->gmin; last = ptr->gmax; break; } case BLUE: { if( nPixels != 0 && nIters > nPixels ) { const int rmin = ptr->rmin; const int rmax = ptr->rmax; const int gmin = ptr->gmin; const int gmax = ptr->gmax; const int bmin = ptr->bmin; const int bmax = ptr->bmax; for( T iPixel = 0; iPixel < nPixels; iPixel++ ) { const int iR = pabyRedBand[iPixel]; const int iG = pabyGreenBand[iPixel]; const int iB = pabyBlueBand[iPixel]; if( iR >= rmin && iR <= rmax && iG >= gmin && iG <= gmax && iB >= bmin && iB <= bmax ) { hist2[iB]++; } } } else if( psHashHistogram ) { T *histp = &hist2[ptr->bmin]; for( int ib = ptr->bmin; ib <= ptr->bmax; ++ib ) { *histp = 0; for( int ir = ptr->rmin; ir <= ptr->rmax; ++ir ) { for( int ig = ptr->gmin; ig <= ptr->gmax; ++ig ) { *histp += FindColorCount(psHashHistogram, MAKE_COLOR_CODE(ir, ig, ib)); } } histp++; } } else { T *histp = &hist2[ptr->bmin]; for( int ib = ptr->bmin; ib <= ptr->bmax; ++ib ) { *histp = 0; for( int ir = ptr->rmin; ir <= ptr->rmax; ++ir ) { const T *iptr = HISTOGRAM(histogram, nCLevels, ir, ptr->gmin, ib); for( int ig = ptr->gmin; ig <= ptr->gmax; ++ig ) { *histp += *iptr; iptr += nCLevels; } } histp++; } } first = ptr->bmin; last = ptr->bmax; break; } } // Find median point. T *histp = &hist2[first]; int i = first; // TODO(schwehr): Rename i. { T sum = 0; T sum2 = static_cast<T>(ptr->total / 2); for( ; i <= last && (sum += *histp++) < sum2; ++i ) {} } if( i == first ) i++; // Create new box, re-allocate points. Colorbox *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]; { T sum1 = 0; for( int j = first; j < i; j++ ) sum1 += *histp++; T sum2 = 0; for( int 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; } if( nPixels != 0 && static_cast<T>(new_cb->rmax - new_cb->rmin + 1) * static_cast<T>(new_cb->gmax - new_cb->gmin + 1) * static_cast<T>(new_cb->bmax - new_cb->bmin + 1) > nPixels ) { shrinkboxFromBand(new_cb, pabyRedBand, pabyGreenBand, pabyBlueBand, nPixels); } else if( psHashHistogram != NULL ) { shrinkboxFromHashHistogram(new_cb, psHashHistogram); } else { shrinkbox(new_cb, histogram, nCLevels); } if( nPixels != 0 && static_cast<T>(ptr->rmax - ptr->rmin + 1) * static_cast<T>(ptr->gmax - ptr->gmin + 1) * static_cast<T>(ptr->bmax - ptr->bmin + 1) > nPixels ) { shrinkboxFromBand(ptr, pabyRedBand, pabyGreenBand, pabyBlueBand, nPixels); } else if( psHashHistogram != NULL ) { shrinkboxFromHashHistogram(ptr, psHashHistogram); } else { shrinkbox(ptr, histogram, nCLevels); } }
template<class T> int GDALComputeMedianCutPCTInternal( GDALRasterBandH hRed, GDALRasterBandH hGreen, GDALRasterBandH hBlue, GByte* pabyRedBand, GByte* pabyGreenBand, GByte* pabyBlueBand, int (*pfnIncludePixel)(int, int, void*), int nColors, int nBits, T* panHistogram, // NULL, or >= size (1<<nBits)^3 * sizeof(T) bytes. GDALColorTableH hColorTable, GDALProgressFunc pfnProgress, void * pProgressArg ) { VALIDATE_POINTER1( hRed, "GDALComputeMedianCutPCT", CE_Failure ); VALIDATE_POINTER1( hGreen, "GDALComputeMedianCutPCT", CE_Failure ); VALIDATE_POINTER1( hBlue, "GDALComputeMedianCutPCT", CE_Failure ); CPLErr err = CE_None; /* -------------------------------------------------------------------- */ /* Validate parameters. */ /* -------------------------------------------------------------------- */ const int nXSize = GDALGetRasterBandXSize( hRed ); const int 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."); 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: create empty boxes. */ /* ==================================================================== */ if( static_cast<GUInt32>(nXSize) > std::numeric_limits<T>::max() / static_cast<GUInt32>(nYSize) ) { CPLError(CE_Warning, CPLE_AppDefined, "GDALComputeMedianCutPCTInternal() not called " "with large enough type"); } T nPixels = 0; if( nBits == 8 && pabyRedBand != NULL && pabyGreenBand != NULL && pabyBlueBand != NULL && static_cast<GUInt32>(nXSize) <= std::numeric_limits<T>::max() / static_cast<GUInt32>(nYSize) ) { nPixels = static_cast<T>(nXSize) * static_cast<T>(nYSize); } const int nCLevels = 1 << nBits; T* histogram = NULL; HashHistogram* psHashHistogram = NULL; if( panHistogram ) { if( nBits == 8 && static_cast<GUIntBig>(nXSize) * nYSize <= 65536 ) { // If the image is small enough, then the number of colors // will be limited and using a hashmap, rather than a full table // will be more efficient. histogram = NULL; psHashHistogram = (HashHistogram*)panHistogram; memset(psHashHistogram, 0xFF, sizeof(HashHistogram) * PRIME_FOR_65536); } else { histogram = panHistogram; memset(histogram, 0, nCLevels*nCLevels*nCLevels * sizeof(T)); } } else { histogram = static_cast<T*>( VSI_CALLOC_VERBOSE(nCLevels * nCLevels * nCLevels, sizeof(T))); if( histogram == NULL ) { return CE_Failure; } } Colorbox *box_list = static_cast<Colorbox *>(CPLMalloc(nColors*sizeof (Colorbox))); Colorbox *freeboxes = box_list; freeboxes[0].next = &freeboxes[1]; freeboxes[0].prev = NULL; for( int 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. */ /* ==================================================================== */ /* -------------------------------------------------------------------- */ /* Initialize the box datastructures. */ /* -------------------------------------------------------------------- */ Colorbox *ptr = freeboxes; freeboxes = ptr->next; if( freeboxes ) freeboxes->prev = NULL; Colorbox *usedboxes = NULL; // TODO(schwehr): What? ptr->next = usedboxes; usedboxes = ptr; if( ptr->next ) ptr->next->prev = ptr; ptr->rmin = 999; ptr->gmin = 999; ptr->bmin = 999; ptr->rmax = -1; ptr->gmax = -1; ptr->bmax = -1; ptr->total = static_cast<GUIntBig>(nXSize) * static_cast<GUIntBig>(nYSize); /* -------------------------------------------------------------------- */ /* Collect histogram. */ /* -------------------------------------------------------------------- */ // TODO(schwehr): Move these closer to usage after removing gotos. const int nColorShift = 8 - nBits; int nColorCounter = 0; GByte anRed[256] = {}; GByte anGreen[256] = {}; GByte anBlue[256] = {}; GByte *pabyRedLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize)); GByte *pabyGreenLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize)); GByte *pabyBlueLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize)); if( pabyRedLine == NULL || pabyGreenLine == NULL || pabyBlueLine == NULL ) { err = CE_Failure; goto end_and_cleanup; } for( int iLine = 0; iLine < nYSize; iLine++ ) { if( !pfnProgress( iLine / static_cast<double>(nYSize), "Generating Histogram", pProgressArg ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" ); err = CE_Failure; goto end_and_cleanup; } err = GDALRasterIO( hRed, GF_Read, 0, iLine, nXSize, 1, pabyRedLine, nXSize, 1, GDT_Byte, 0, 0 ); if( err == CE_None ) err = GDALRasterIO( hGreen, GF_Read, 0, iLine, nXSize, 1, pabyGreenLine, nXSize, 1, GDT_Byte, 0, 0 ); if( err == CE_None ) err = GDALRasterIO( hBlue, GF_Read, 0, iLine, nXSize, 1, pabyBlueLine, nXSize, 1, GDT_Byte, 0, 0 ); if( err != CE_None ) goto end_and_cleanup; for( int iPixel = 0; iPixel < nXSize; iPixel++ ) { const int nRed = pabyRedLine[iPixel] >> nColorShift; const int nGreen = pabyGreenLine[iPixel] >> nColorShift; const int nBlue = pabyBlueLine[iPixel] >> nColorShift; ptr->rmin = std::min(ptr->rmin, nRed); ptr->gmin = std::min(ptr->gmin, nGreen); ptr->bmin = std::min(ptr->bmin, nBlue); ptr->rmax = std::max(ptr->rmax, nRed); ptr->gmax = std::max(ptr->gmax, nGreen); ptr->bmax = std::max(ptr->bmax, nBlue); bool bFirstOccurrence; if( psHashHistogram ) { int* pnColor = FindAndInsertColorCount(psHashHistogram, MAKE_COLOR_CODE(nRed, nGreen, nBlue)); bFirstOccurrence = ( *pnColor == 0 ); (*pnColor)++; } else { T* pnColor = HISTOGRAM(histogram, nCLevels, nRed, nGreen, nBlue); bFirstOccurrence = ( *pnColor == 0 ); (*pnColor)++; } if( bFirstOccurrence) { if( nColorShift == 0 && nColorCounter < nColors ) { anRed[nColorCounter] = static_cast<GByte>(nRed); anGreen[nColorCounter] = static_cast<GByte>(nGreen); anBlue[nColorCounter] = static_cast<GByte>(nBlue); } nColorCounter++; } } } if( !pfnProgress( 1.0, "Generating Histogram", pProgressArg ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" ); err = CE_Failure; goto end_and_cleanup; } if( nColorShift == 0 && nColorCounter <= nColors ) { #if DEBUG_VERBOSE CPLDebug("MEDIAN_CUT", "%d colors found <= %d", nColorCounter, nColors); #endif for( int iColor = 0; iColor < nColorCounter; iColor++ ) { const GDALColorEntry sEntry = { static_cast<GByte>(anRed[iColor]), static_cast<GByte>(anGreen[iColor]), static_cast<GByte>(anBlue[iColor]), 255 }; GDALSetColorEntry( hColorTable, iColor, &sEntry ); } 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, psHashHistogram, nCLevels, &freeboxes, &usedboxes, pabyRedBand, pabyGreenBand, pabyBlueBand, nPixels); else freeboxes = NULL; } /* ==================================================================== */ /* STEP 4: assign colors to all boxes */ /* ==================================================================== */ ptr = usedboxes; for( int i = 0; ptr != NULL; ++i, ptr = ptr->next ) { const GDALColorEntry sEntry = { static_cast<GByte>(((ptr->rmin + ptr->rmax) << nColorShift) / 2), static_cast<GByte>(((ptr->gmin + ptr->gmax) << nColorShift) / 2), static_cast<GByte>(((ptr->bmin + ptr->bmax) << nColorShift) / 2), 255 }; GDALSetColorEntry( hColorTable, i, &sEntry ); } end_and_cleanup: CPLFree( pabyRedLine ); CPLFree( pabyGreenLine ); CPLFree( pabyBlueLine ); // We're done with the boxes now. CPLFree(box_list); freeboxes = NULL; usedboxes = NULL; if( panHistogram == NULL ) CPLFree( histogram ); return err; }
template<class T> static void shrinkbox( Colorbox* box, const T* histogram, int nCLevels ) { if( box->rmax > box->rmin ) { for( int ir = box->rmin; ir <= box->rmax; ++ir ) { for( int ig = box->gmin; ig <= box->gmax; ++ig ) { const T *histp = HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); for( int ib = box->bmin; ib <= box->bmax; ++ib ) { if( *histp++ != 0 ) { box->rmin = ir; goto have_rmin; } } } } } have_rmin: if( box->rmax > box->rmin ) { for( int ir = box->rmax; ir >= box->rmin; --ir ) { for( int ig = box->gmin; ig <= box->gmax; ++ig ) { const T *histp = HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); for( int ib = box->bmin; ib <= box->bmax; ++ib ) { if( *histp++ != 0 ) { box->rmax = ir; goto have_rmax; } } } } } have_rmax: if( box->gmax > box->gmin ) { for( int ig = box->gmin; ig <= box->gmax; ++ig ) { for( int ir = box->rmin; ir <= box->rmax; ++ir ) { const T *histp = HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); for( int ib = box->bmin; ib <= box->bmax; ++ib ) { if( *histp++ != 0 ) { box->gmin = ig; goto have_gmin; } } } } } have_gmin: if( box->gmax > box->gmin ) { for( int ig = box->gmax; ig >= box->gmin; --ig ) { for( int ir = box->rmin; ir <= box->rmax; ++ir ) { const T *histp = HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); for( int ib = box->bmin; ib <= box->bmax; ++ib ) { if( *histp++ != 0 ) { box->gmax = ig; goto have_gmax; } } } } } have_gmax: if( box->bmax > box->bmin ) { for( int ib = box->bmin; ib <= box->bmax; ++ib ) { for( int ir = box->rmin; ir <= box->rmax; ++ir ) { const T *histp = HISTOGRAM(histogram, nCLevels, ir, box->gmin, ib); for( int ig = box->gmin; ig <= box->gmax; ++ig ) { if( *histp != 0 ) { box->bmin = ib; goto have_bmin; } histp += nCLevels; } } } } have_bmin: if( box->bmax > box->bmin ) { for( int ib = box->bmax; ib >= box->bmin; --ib ) { for( int ir = box->rmin; ir <= box->rmax; ++ir ) { const T *histp = HISTOGRAM(histogram, nCLevels, ir, box->gmin, ib); for( int ig = box->gmin; ig <= box->gmax; ++ig ) { if( *histp != 0 ) { box->bmax = ib; goto have_bmax; } histp += nCLevels; } } } } have_bmax: ; }