1 // xImaHist.cpp : histogram functions
\r
2 /* 28/01/2004 v1.00 - www.xdp.it
\r
3 * CxImage version 6.0.0 02/Feb/2008
\r
8 #if CXIMAGE_SUPPORT_DSP
\r
10 ////////////////////////////////////////////////////////////////////////////////
\r
11 long CxImage::Histogram(long* red, long* green, long* blue, long* gray, long colorspace)
\r
13 if (!pDib) return 0;
\r
16 if (red) memset(red,0,256*sizeof(long));
\r
17 if (green) memset(green,0,256*sizeof(long));
\r
18 if (blue) memset(blue,0,256*sizeof(long));
\r
19 if (gray) memset(gray,0,256*sizeof(long));
\r
21 long xmin,xmax,ymin,ymax;
\r
23 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;
\r
24 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;
\r
27 xmax = head.biWidth; ymax=head.biHeight;
\r
30 for(long y=ymin; y<ymax; y++){
\r
31 for(long x=xmin; x<xmax; x++){
\r
32 #if CXIMAGE_SUPPORT_SELECTION
\r
33 if (BlindSelectionIsInside(x,y))
\r
34 #endif //CXIMAGE_SUPPORT_SELECTION
\r
36 switch (colorspace){
\r
38 color = HSLtoRGB(BlindGetPixelColor(x,y));
\r
41 color = YUVtoRGB(BlindGetPixelColor(x,y));
\r
44 color = YIQtoRGB(BlindGetPixelColor(x,y));
\r
47 color = XYZtoRGB(BlindGetPixelColor(x,y));
\r
50 color = BlindGetPixelColor(x,y);
\r
53 if (red) red[color.rgbRed]++;
\r
54 if (green) green[color.rgbGreen]++;
\r
55 if (blue) blue[color.rgbBlue]++;
\r
56 if (gray) gray[(BYTE)RGB2GRAY(color.rgbRed,color.rgbGreen,color.rgbBlue)]++;
\r
62 for (int i=0; i<256; i++){
\r
63 if (red && red[i]>n) n=red[i];
\r
64 if (green && green[i]>n) n=green[i];
\r
65 if (blue && blue[i]>n) n=blue[i];
\r
66 if (gray && gray[i]>n) n=gray[i];
\r
71 ////////////////////////////////////////////////////////////////////////////////
\r
74 * \param method: 0 = luminance (default), 1 = linked channels , 2 = independent channels.
\r
75 * \param threshold: minimum percentage level in the histogram to recognize it as meaningful. Range: 0.0 to 1.0; default = 0; typical = 0.005 (0.5%);
\r
76 * \return true if everything is ok
\r
77 * \author [dave] and [nipper]; changes [DP]
\r
79 bool CxImage::HistogramStretch(long method, double threshold)
\r
81 if (!pDib) return false;
\r
83 double dbScaler = 50.0/head.biHeight;
\r
86 if ((head.biBitCount==8) && IsGrayScale()){
\r
89 memset(p, 0, 256*sizeof(double));
\r
90 for (y=0; y<head.biHeight; y++)
\r
92 info.nProgress = (long)(y*dbScaler);
\r
93 if (info.nEscape) break;
\r
94 for (x=0; x<head.biWidth; x++) {
\r
95 p[BlindGetPixelIndex(x, y)]++;
\r
100 for (y=0; y<255; y++) if (maxh < p[y]) maxh = p[y];
\r
103 while (minc<255 && p[minc]<=threshold) minc++;
\r
105 while (maxc>0 && p[maxc]<=threshold) maxc--;
\r
107 if (minc == 0 && maxc == 255) return true;
\r
108 if (minc >= maxc) return true;
\r
112 for (x = 0; x <256; x++){
\r
113 lut[x] = (BYTE)max(0,min(255,(255 * (x - minc) / (maxc - minc))));
\r
116 for (y=0; y<head.biHeight; y++) {
\r
117 if (info.nEscape) break;
\r
118 info.nProgress = (long)(50.0+y*dbScaler);
\r
119 for (x=0; x<head.biWidth; x++)
\r
121 BlindSetPixelIndex(x, y, lut[BlindGetPixelIndex(x, y)]);
\r
129 memset(p, 0, 256*sizeof(double));
\r
130 for (y=0; y<head.biHeight; y++)
\r
132 info.nProgress = (long)(y*dbScaler);
\r
133 if (info.nEscape) break;
\r
134 for (x=0; x<head.biWidth; x++) {
\r
135 RGBQUAD color = BlindGetPixelColor(x, y);
\r
137 p[color.rgbBlue]++;
\r
138 p[color.rgbGreen]++;
\r
142 for (y=0; y<255; y++) if (maxh < p[y]) maxh = p[y];
\r
145 while (minc<255 && p[minc]<=threshold) minc++;
\r
147 while (maxc>0 && p[maxc]<=threshold) maxc--;
\r
149 if (minc == 0 && maxc == 255) return true;
\r
150 if (minc >= maxc) return true;
\r
154 for (x = 0; x <256; x++){
\r
155 lut[x] = (BYTE)max(0,min(255,(255 * (x - minc) / (maxc - minc))));
\r
159 for (y=0; y<head.biHeight; y++) {
\r
160 if (info.nEscape) break;
\r
161 info.nProgress = (long)(50.0+y*dbScaler);
\r
163 for (x=0; x<head.biWidth; x++)
\r
165 RGBQUAD color = BlindGetPixelColor(x, y);
\r
167 color.rgbRed = lut[color.rgbRed];
\r
168 color.rgbBlue = lut[color.rgbBlue];
\r
169 color.rgbGreen = lut[color.rgbGreen];
\r
171 BlindSetPixelColor(x, y, color);
\r
179 memset(pR, 0, 256*sizeof(double));
\r
181 memset(pG, 0, 256*sizeof(double));
\r
183 memset(pB, 0, 256*sizeof(double));
\r
184 for (y=0; y<head.biHeight; y++)
\r
186 info.nProgress = (long)(y*dbScaler);
\r
187 if (info.nEscape) break;
\r
188 for (long x=0; x<head.biWidth; x++) {
\r
189 RGBQUAD color = BlindGetPixelColor(x, y);
\r
190 pR[color.rgbRed]++;
\r
191 pB[color.rgbBlue]++;
\r
192 pG[color.rgbGreen]++;
\r
197 for (y=0; y<255; y++) if (maxh < pR[y]) maxh = pR[y];
\r
198 double threshold2 = threshold*maxh;
\r
200 while (minR<255 && pR[minR]<=threshold2) minR++;
\r
202 while (maxR>0 && pR[maxR]<=threshold2) maxR--;
\r
205 for (y=0; y<255; y++) if (maxh < pG[y]) maxh = pG[y];
\r
206 threshold2 = threshold*maxh;
\r
208 while (minG<255 && pG[minG]<=threshold2) minG++;
\r
210 while (maxG>0 && pG[maxG]<=threshold2) maxG--;
\r
213 for (y=0; y<255; y++) if (maxh < pB[y]) maxh = pB[y];
\r
214 threshold2 = threshold*maxh;
\r
216 while (minB<255 && pB[minB]<=threshold2) minB++;
\r
218 while (maxB>0 && pB[maxB]<=threshold2) maxB--;
\r
220 if (minR == 0 && maxR == 255 && minG == 0 && maxG == 255 && minB == 0 && maxB == 255)
\r
225 BYTE range = maxR - minR;
\r
227 for (x = 0; x <256; x++){
\r
228 lutR[x] = (BYTE)max(0,min(255,(255 * (x - minR) / range)));
\r
230 } else lutR[minR] = minR;
\r
233 range = maxG - minG;
\r
235 for (x = 0; x <256; x++){
\r
236 lutG[x] = (BYTE)max(0,min(255,(255 * (x - minG) / range)));
\r
238 } else lutG[minG] = minG;
\r
241 range = maxB - minB;
\r
243 for (x = 0; x <256; x++){
\r
244 lutB[x] = (BYTE)max(0,min(255,(255 * (x - minB) / range)));
\r
246 } else lutB[minB] = minB;
\r
249 for (y=0; y<head.biHeight; y++)
\r
251 info.nProgress = (long)(50.0+y*dbScaler);
\r
252 if (info.nEscape) break;
\r
254 for (x=0; x<head.biWidth; x++)
\r
256 RGBQUAD color = BlindGetPixelColor(x, y);
\r
258 color.rgbRed = lutR[color.rgbRed];
\r
259 color.rgbBlue = lutB[color.rgbBlue];
\r
260 color.rgbGreen = lutG[color.rgbGreen];
\r
262 BlindSetPixelColor(x, y, color);
\r
270 memset(p, 0, 256*sizeof(double));
\r
271 for (y=0; y<head.biHeight; y++)
\r
273 info.nProgress = (long)(y*dbScaler);
\r
274 if (info.nEscape) break;
\r
275 for (x=0; x<head.biWidth; x++) {
\r
276 RGBQUAD color = BlindGetPixelColor(x, y);
\r
277 p[RGB2GRAY(color.rgbRed, color.rgbGreen, color.rgbBlue)]++;
\r
282 for (y=0; y<255; y++) if (maxh < p[y]) maxh = p[y];
\r
285 while (minc<255 && p[minc]<=threshold) minc++;
\r
287 while (maxc>0 && p[maxc]<=threshold) maxc--;
\r
289 if (minc == 0 && maxc == 255) return true;
\r
290 if (minc >= maxc) return true;
\r
294 for (x = 0; x <256; x++){
\r
295 lut[x] = (BYTE)max(0,min(255,(255 * (x - minc) / (maxc - minc))));
\r
298 for(y=0; y<head.biHeight; y++){
\r
299 info.nProgress = (long)(50.0+y*dbScaler);
\r
300 if (info.nEscape) break;
\r
301 for(x=0; x<head.biWidth; x++){
\r
303 RGBQUAD color = BlindGetPixelColor( x, y );
\r
304 RGBQUAD yuvClr = RGBtoYUV(color);
\r
305 yuvClr.rgbRed = lut[yuvClr.rgbRed];
\r
306 color = YUVtoRGB(yuvClr);
\r
307 BlindSetPixelColor( x, y, color );
\r
315 ////////////////////////////////////////////////////////////////////////////////
\r
316 // HistogramEqualize function by <dave> : dave(at)posortho(dot)com
\r
317 bool CxImage::HistogramEqualize()
\r
319 if (!pDib) return false;
\r
321 int histogram[256];
\r
323 int equalize_map[256];
\r
327 unsigned int YVal, high, low;
\r
329 memset( &histogram, 0, sizeof(int) * 256 );
\r
330 memset( &map, 0, sizeof(int) * 256 );
\r
331 memset( &equalize_map, 0, sizeof(int) * 256 );
\r
334 for(y=0; y < head.biHeight; y++){
\r
335 info.nProgress = (long)(50*y/head.biHeight);
\r
336 if (info.nEscape) break;
\r
337 for(x=0; x < head.biWidth; x++){
\r
338 color = BlindGetPixelColor( x, y );
\r
339 YVal = (unsigned int)RGB2GRAY(color.rgbRed, color.rgbGreen, color.rgbBlue);
\r
344 // integrate the histogram to get the equalization map.
\r
346 for(i=0; i <= 255; i++){
\r
354 if (low == high) return false;
\r
355 for( i = 0; i <= 255; i++ ){
\r
356 equalize_map[i] = (unsigned int)((((double)( map[i] - low ) ) * 255) / ( high - low ) );
\r
359 // stretch the histogram
\r
360 if(head.biClrUsed == 0){ // No Palette
\r
361 for( y = 0; y < head.biHeight; y++ ){
\r
362 info.nProgress = (long)(50+50*y/head.biHeight);
\r
363 if (info.nEscape) break;
\r
364 for( x = 0; x < head.biWidth; x++ ){
\r
366 color = BlindGetPixelColor( x, y );
\r
367 yuvClr = RGBtoYUV(color);
\r
369 yuvClr.rgbRed = (BYTE)equalize_map[yuvClr.rgbRed];
\r
371 color = YUVtoRGB(yuvClr);
\r
372 BlindSetPixelColor( x, y, color );
\r
375 } else { // Palette
\r
376 for( i = 0; i < (int)head.biClrUsed; i++ ){
\r
378 color = GetPaletteColor((BYTE)i);
\r
379 yuvClr = RGBtoYUV(color);
\r
381 yuvClr.rgbRed = (BYTE)equalize_map[yuvClr.rgbRed];
\r
383 color = YUVtoRGB(yuvClr);
\r
384 SetPaletteColor( (BYTE)i, color );
\r
389 ////////////////////////////////////////////////////////////////////////////////
\r
390 // HistogramNormalize function by <dave> : dave(at)posortho(dot)com
\r
391 bool CxImage::HistogramNormalize()
\r
393 if (!pDib) return false;
\r
395 int histogram[256];
\r
396 int threshold_intensity, intense;
\r
398 unsigned int normalize_map[256];
\r
399 unsigned int high, low, YVal;
\r
404 memset( &histogram, 0, sizeof( int ) * 256 );
\r
405 memset( &normalize_map, 0, sizeof( unsigned int ) * 256 );
\r
408 for(y=0; y < head.biHeight; y++){
\r
409 info.nProgress = (long)(50*y/head.biHeight);
\r
410 if (info.nEscape) break;
\r
411 for(x=0; x < head.biWidth; x++){
\r
412 color = BlindGetPixelColor( x, y );
\r
413 YVal = (unsigned int)RGB2GRAY(color.rgbRed, color.rgbGreen, color.rgbBlue);
\r
418 // find histogram boundaries by locating the 1 percent levels
\r
419 threshold_intensity = ( head.biWidth * head.biHeight) / 100;
\r
422 for( low = 0; low < 255; low++ ){
\r
423 intense += histogram[low];
\r
424 if( intense > threshold_intensity ) break;
\r
428 for( high = 255; high != 0; high--){
\r
429 intense += histogram[ high ];
\r
430 if( intense > threshold_intensity ) break;
\r
433 if ( low == high ){
\r
434 // Unreasonable contrast; use zero threshold to determine boundaries.
\r
435 threshold_intensity = 0;
\r
437 for( low = 0; low < 255; low++){
\r
438 intense += histogram[low];
\r
439 if( intense > threshold_intensity ) break;
\r
442 for( high = 255; high != 0; high-- ){
\r
443 intense += histogram [high ];
\r
444 if( intense > threshold_intensity ) break;
\r
447 if( low == high ) return false; // zero span bound
\r
449 // Stretch the histogram to create the normalized image mapping.
\r
450 for(i = 0; i <= 255; i++){
\r
451 if ( i < (int) low ){
\r
452 normalize_map[i] = 0;
\r
455 normalize_map[i] = 255;
\r
457 normalize_map[i] = ( 255 - 1) * ( i - low) / ( high - low );
\r
462 if( head.biClrUsed == 0 ){
\r
463 for( y = 0; y < head.biHeight; y++ ){
\r
464 info.nProgress = (long)(50+50*y/head.biHeight);
\r
465 if (info.nEscape) break;
\r
466 for( x = 0; x < head.biWidth; x++ ){
\r
468 color = BlindGetPixelColor( x, y );
\r
469 yuvClr = RGBtoYUV( color );
\r
471 yuvClr.rgbRed = (BYTE)normalize_map[yuvClr.rgbRed];
\r
473 color = YUVtoRGB( yuvClr );
\r
474 BlindSetPixelColor( x, y, color );
\r
478 for(i = 0; i < (int)head.biClrUsed; i++){
\r
480 color = GetPaletteColor( (BYTE)i );
\r
481 yuvClr = RGBtoYUV( color );
\r
483 yuvClr.rgbRed = (BYTE)normalize_map[yuvClr.rgbRed];
\r
485 color = YUVtoRGB( yuvClr );
\r
486 SetPaletteColor( (BYTE)i, color );
\r
491 ////////////////////////////////////////////////////////////////////////////////
\r
492 // HistogramLog function by <dave> : dave(at)posortho(dot)com
\r
493 bool CxImage::HistogramLog()
\r
495 if (!pDib) return false;
\r
497 //q(i,j) = 255/log(1 + |high|) * log(1 + |p(i,j)|);
\r
502 unsigned int YVal, high = 1;
\r
504 // Find Highest Luminance Value in the Image
\r
505 if( head.biClrUsed == 0 ){ // No Palette
\r
506 for(y=0; y < head.biHeight; y++){
\r
507 info.nProgress = (long)(50*y/head.biHeight);
\r
508 if (info.nEscape) break;
\r
509 for(x=0; x < head.biWidth; x++){
\r
510 color = BlindGetPixelColor( x, y );
\r
511 YVal = (unsigned int)RGB2GRAY(color.rgbRed, color.rgbGreen, color.rgbBlue);
\r
512 if (YVal > high ) high = YVal;
\r
515 } else { // Palette
\r
516 for(i = 0; i < (int)head.biClrUsed; i++){
\r
517 color = GetPaletteColor((BYTE)i);
\r
518 YVal = (unsigned int)RGB2GRAY(color.rgbRed, color.rgbGreen, color.rgbBlue);
\r
519 if (YVal > high ) high = YVal;
\r
523 // Logarithm Operator
\r
524 double k = 255.0 / ::log( 1.0 + (double)high );
\r
525 if( head.biClrUsed == 0 ){
\r
526 for( y = 0; y < head.biHeight; y++ ){
\r
527 info.nProgress = (long)(50+50*y/head.biHeight);
\r
528 if (info.nEscape) break;
\r
529 for( x = 0; x < head.biWidth; x++ ){
\r
531 color = BlindGetPixelColor( x, y );
\r
532 yuvClr = RGBtoYUV( color );
\r
534 yuvClr.rgbRed = (BYTE)(k * ::log( 1.0 + (double)yuvClr.rgbRed ) );
\r
536 color = YUVtoRGB( yuvClr );
\r
537 BlindSetPixelColor( x, y, color );
\r
541 for(i = 0; i < (int)head.biClrUsed; i++){
\r
543 color = GetPaletteColor( (BYTE)i );
\r
544 yuvClr = RGBtoYUV( color );
\r
546 yuvClr.rgbRed = (BYTE)(k * ::log( 1.0 + (double)yuvClr.rgbRed ) );
\r
548 color = YUVtoRGB( yuvClr );
\r
549 SetPaletteColor( (BYTE)i, color );
\r
556 ////////////////////////////////////////////////////////////////////////////////
\r
557 // HistogramRoot function by <dave> : dave(at)posortho(dot)com
\r
558 bool CxImage::HistogramRoot()
\r
560 if (!pDib) return false;
\r
561 //q(i,j) = sqrt(|p(i,j)|);
\r
567 unsigned int YVal, high = 1;
\r
569 // Find Highest Luminance Value in the Image
\r
570 if( head.biClrUsed == 0 ){ // No Palette
\r
571 for(y=0; y < head.biHeight; y++){
\r
572 info.nProgress = (long)(50*y/head.biHeight);
\r
573 if (info.nEscape) break;
\r
574 for(x=0; x < head.biWidth; x++){
\r
575 color = BlindGetPixelColor( x, y );
\r
576 YVal = (unsigned int)RGB2GRAY(color.rgbRed, color.rgbGreen, color.rgbBlue);
\r
577 if (YVal > high ) high = YVal;
\r
580 } else { // Palette
\r
581 for(i = 0; i < (int)head.biClrUsed; i++){
\r
582 color = GetPaletteColor((BYTE)i);
\r
583 YVal = (unsigned int)RGB2GRAY(color.rgbRed, color.rgbGreen, color.rgbBlue);
\r
584 if (YVal > high ) high = YVal;
\r
589 double k = 128.0 / ::log( 1.0 + (double)high );
\r
590 if( head.biClrUsed == 0 ){
\r
591 for( y = 0; y < head.biHeight; y++ ){
\r
592 info.nProgress = (long)(50+50*y/head.biHeight);
\r
593 if (info.nEscape) break;
\r
594 for( x = 0; x < head.biWidth; x++ ){
\r
596 color = BlindGetPixelColor( x, y );
\r
597 yuvClr = RGBtoYUV( color );
\r
599 dtmp = k * ::sqrt( (double)yuvClr.rgbRed );
\r
600 if ( dtmp > 255.0 ) dtmp = 255.0;
\r
601 if ( dtmp < 0 ) dtmp = 0;
\r
602 yuvClr.rgbRed = (BYTE)dtmp;
\r
604 color = YUVtoRGB( yuvClr );
\r
605 BlindSetPixelColor( x, y, color );
\r
609 for(i = 0; i < (int)head.biClrUsed; i++){
\r
611 color = GetPaletteColor( (BYTE)i );
\r
612 yuvClr = RGBtoYUV( color );
\r
614 dtmp = k * ::sqrt( (double)yuvClr.rgbRed );
\r
615 if ( dtmp > 255.0 ) dtmp = 255.0;
\r
616 if ( dtmp < 0 ) dtmp = 0;
\r
617 yuvClr.rgbRed = (BYTE)dtmp;
\r
619 color = YUVtoRGB( yuvClr );
\r
620 SetPaletteColor( (BYTE)i, color );
\r
626 ////////////////////////////////////////////////////////////////////////////////
\r