You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/libs/histogram/imagehistogram.cpp

549 lines
13 KiB

/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2004-07-21
* Description : image histogram manipulation methods.
*
* Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
*
* Some code parts are inspired from gimp 2.0
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* ============================================================ */
// C++ includes.
#include <cstdio>
#include <cmath>
#include <cstring>
// TQt includes.
#include <tqobject.h>
#include <tqevent.h>
// KDE includes.
#include <kapplication.h>
// Local includes.
#include "ddebug.h"
#include "dimg.h"
#include "imagehistogram.h"
namespace Digikam
{
class ImageHistogramPriv
{
public:
// Using a structure instead a class is more fast
// (access with memset() and bytes manipulation).
struct double_packet
{
double value;
double red;
double green;
double blue;
double alpha;
};
public:
ImageHistogramPriv()
{
parent = 0;
imageData = 0;
histogram = 0;
runningFlag = true;
}
/** The histogram data.*/
struct double_packet *histogram;
/** Image information.*/
uchar *imageData;
uint imageWidth;
uint imageHeight;
/** Numbers of histogram segments dependaing of image bytes depth*/
int histoSegments;
/** To post event from thread to parent.*/
TQObject *parent;
/** Used to stop thread during calculations.*/
bool runningFlag;
};
ImageHistogram::ImageHistogram(const DImg& image, TQObject *parent)
: TQThread()
{
setup(image.bits(), image.width(), image.height(), image.sixteenBit(), parent);
}
ImageHistogram::ImageHistogram(uchar *i_data, uint i_w, uint i_h, bool i_sixteenBits, TQObject *parent)
: TQThread()
{
setup(i_data, i_w, i_h, i_sixteenBits, parent);
}
void ImageHistogram::setup(uchar *i_data, uint i_w, uint i_h, bool i_sixteenBits, TQObject *parent)
{
d = new ImageHistogramPriv;
d->imageData = i_data;
d->imageWidth = i_w;
d->imageHeight = i_h;
d->parent = parent;
d->histoSegments = i_sixteenBits ? 65536 : 256;
if (d->imageData && d->imageWidth && d->imageHeight)
{
if (d->parent)
start();
else
calcHistogramValues();
}
else
{
if (d->parent)
postProgress(false, false);
}
}
ImageHistogram::~ImageHistogram()
{
stopCalcHistogramValues();
if (d->histogram)
delete [] d->histogram;
delete d;
}
int ImageHistogram::getHistogramSegment(void)
{
return d->histoSegments;
}
void ImageHistogram::postProgress(bool starting, bool success)
{
EventData *eventData = new EventData();
eventData->starting = starting;
eventData->success = success;
eventData->histogram = this;
TQApplication::postEvent(d->parent, new TQCustomEvent(TQEvent::User, eventData));
}
void ImageHistogram::stopCalcHistogramValues(void)
{
d->runningFlag = false;
wait();
}
// List of threaded operations.
void ImageHistogram::run()
{
calcHistogramValues();
}
void ImageHistogram::calcHistogramValues()
{
register uint i;
int max;
if (d->parent)
postProgress(true, false);
d->histogram = new ImageHistogramPriv::double_packet[d->histoSegments];
memset(d->histogram, 0, d->histoSegments*sizeof(ImageHistogramPriv::double_packet));
if ( !d->histogram )
{
DWarning() << ("HistogramWidget::calcHistogramValues: Unable to allocate memory!") << endl;
if (d->parent)
postProgress(false, false);
return;
}
memset(d->histogram, 0, d->histoSegments*sizeof(struct ImageHistogramPriv::double_packet));
if (d->histoSegments == 65536) // 16 bits image.
{
unsigned short blue, green, red, alpha;
unsigned short *data = (unsigned short*)d->imageData;
for (i = 0 ; (i < d->imageHeight*d->imageWidth*4) && d->runningFlag ; i+=4)
{
blue = data[ i ];
green = data[i+1];
red = data[i+2];
alpha = data[i+3];
d->histogram[blue].blue++;
d->histogram[green].green++;
d->histogram[red].red++;
d->histogram[alpha].alpha++;
max = (blue > green) ? blue : green;
if (red > max)
d->histogram[red].value++;
else
d->histogram[max].value++;
}
}
else // 8 bits images.
{
uchar blue, green, red, alpha;
uchar *data = d->imageData;
for (i = 0 ; (i < d->imageHeight*d->imageWidth*4) && d->runningFlag ; i+=4)
{
blue = data[ i ];
green = data[i+1];
red = data[i+2];
alpha = data[i+3];
d->histogram[blue].blue++;
d->histogram[green].green++;
d->histogram[red].red++;
d->histogram[alpha].alpha++;
max = (blue > green) ? blue : green;
if (red > max)
d->histogram[red].value++;
else
d->histogram[max].value++;
}
}
if (d->parent && d->runningFlag)
postProgress(false, true);
}
double ImageHistogram::getCount(int channel, int start, int end)
{
int i;
double count = 0.0;
if ( !d->histogram || start < 0 ||
end > d->histoSegments-1 || start > end )
return 0.0;
switch(channel)
{
case ImageHistogram::ValueChannel:
for (i = start ; i <= end ; i++)
count += d->histogram[i].value;
break;
case ImageHistogram::RedChannel:
for (i = start ; i <= end ; i++)
count += d->histogram[i].red;
break;
case ImageHistogram::GreenChannel:
for (i = start ; i <= end ; i++)
count += d->histogram[i].green;
break;
case ImageHistogram::BlueChannel:
for (i = start ; i <= end ; i++)
count += d->histogram[i].blue;
break;
case ImageHistogram::AlphaChannel:
for (i = start ; i <= end ; i++)
count += d->histogram[i].alpha;
break;
default:
return 0.0;
break;
}
return count;
}
double ImageHistogram::getPixels()
{
if ( !d->histogram )
return 0.0;
return(d->imageWidth * d->imageHeight);
}
double ImageHistogram::getMean(int channel, int start, int end)
{
int i;
double mean = 0.0;
double count;
if ( !d->histogram || start < 0 ||
end > d->histoSegments-1 || start > end )
return 0.0;
switch(channel)
{
case ImageHistogram::ValueChannel:
for (i = start ; i <= end ; i++)
mean += i * d->histogram[i].value;
break;
case ImageHistogram::RedChannel:
for (i = start ; i <= end ; i++)
mean += i * d->histogram[i].red;
break;
case ImageHistogram::GreenChannel:
for (i = start ; i <= end ; i++)
mean += i * d->histogram[i].green;
break;
case ImageHistogram::BlueChannel:
for (i = start ; i <= end ; i++)
mean += i * d->histogram[i].blue;
break;
case ImageHistogram::AlphaChannel:
for (i = start ; i <= end ; i++)
mean += i * d->histogram[i].alpha;
break;
default:
return 0.0;
break;
}
count = getCount(channel, start, end);
if (count > 0.0)
return mean / count;
return mean;
}
int ImageHistogram::getMedian(int channel, int start, int end)
{
int i;
double sum = 0.0;
double count;
if ( !d->histogram || start < 0 ||
end > d->histoSegments-1 || start > end )
return 0;
count = getCount(channel, start, end);
switch(channel)
{
case ImageHistogram::ValueChannel:
for (i = start ; i <= end ; i++)
{
sum += d->histogram[i].value;
if (sum * 2 > count) return i;
}
break;
case ImageHistogram::RedChannel:
for (i = start ; i <= end ; i++)
{
sum += d->histogram[i].red;
if (sum * 2 > count) return i;
}
break;
case ImageHistogram::GreenChannel:
for (i = start ; i <= end ; i++)
{
sum += d->histogram[i].green;
if (sum * 2 > count) return i;
}
break;
case ImageHistogram::BlueChannel:
for (i = start ; i <= end ; i++)
{
sum += d->histogram[i].blue;
if (sum * 2 > count) return i;
}
break;
case ImageHistogram::AlphaChannel:
for (i = start ; i <= end ; i++)
{
sum += d->histogram[i].alpha;
if (sum * 2 > count) return i;
}
break;
default:
return 0;
break;
}
return -1;
}
double ImageHistogram::getStdDev(int channel, int start, int end)
{
int i;
double dev = 0.0;
double count;
double mean;
if ( !d->histogram || start < 0 ||
end > d->histoSegments-1 || start > end )
return 0.0;
mean = getMean(channel, start, end);
count = getCount(channel, start, end);
if (count == 0.0)
count = 1.0;
switch(channel)
{
case ImageHistogram::ValueChannel:
for (i = start ; i <= end ; i++)
dev += (i - mean) * (i - mean) * d->histogram[i].value;
break;
case ImageHistogram::RedChannel:
for (i = start ; i <= end ; i++)
dev += (i - mean) * (i - mean) * d->histogram[i].red;
break;
case ImageHistogram::GreenChannel:
for (i = start ; i <= end ; i++)
dev += (i - mean) * (i - mean) * d->histogram[i].green;
break;
case ImageHistogram::BlueChannel:
for (i = start ; i <= end ; i++)
dev += (i - mean) * (i - mean) * d->histogram[i].blue;
break;
case ImageHistogram::AlphaChannel:
for (i = start ; i <= end ; i++)
dev += (i - mean) * (i - mean) * d->histogram[i].alpha;
break;
default:
return 0.0;
break;
}
return sqrt(dev / count);
}
double ImageHistogram::getValue(int channel, int bin)
{
double value;
if ( !d->histogram || bin < 0 || bin > d->histoSegments-1 )
return 0.0;
switch(channel)
{
case ImageHistogram::ValueChannel:
value = d->histogram[bin].value;
break;
case ImageHistogram::RedChannel:
value = d->histogram[bin].red;
break;
case ImageHistogram::GreenChannel:
value = d->histogram[bin].green;
break;
case ImageHistogram::BlueChannel:
value = d->histogram[bin].blue;
break;
case ImageHistogram::AlphaChannel:
value = d->histogram[bin].alpha;
break;
default:
return 0.0;
break;
}
return value;
}
double ImageHistogram::getMaximum(int channel)
{
double max = 0.0;
int x;
if ( !d->histogram )
return 0.0;
switch(channel)
{
case ImageHistogram::ValueChannel:
for (x = 0 ; x < d->histoSegments ; x++)
if (d->histogram[x].value > max)
max = d->histogram[x].value;
break;
case ImageHistogram::RedChannel:
for (x = 0 ; x < d->histoSegments ; x++)
if (d->histogram[x].red > max)
max = d->histogram[x].red;
break;
case ImageHistogram::GreenChannel:
for (x = 0 ; x < d->histoSegments ; x++)
if (d->histogram[x].green > max)
max = d->histogram[x].green;
break;
case ImageHistogram::BlueChannel:
for (x = 0 ; x < d->histoSegments ; x++)
if (d->histogram[x].blue > max)
max = d->histogram[x].blue;
break;
case ImageHistogram::AlphaChannel:
for (x = 0 ; x < d->histoSegments ; x++)
if (d->histogram[x].alpha > max)
max = d->histogram[x].alpha;
break;
default:
return 0.0;
break;
}
return max;
}
} // NameSpace Digikam