blob: 43d19b636ca72aa76eb0f943c0e30cdacc769afe [file] [log] [blame]
// Copyright 2006 Google Inc.
// All Rights Reserved.
// Author: <renn@google.com> (Marius Renn)
// std includes
#include <math.h>
#include <stdio.h>
// Local includes
#include "debugging.h"
#include "histogram.h"
#include "helium_image.h"
#include "mathfunctions.h"
using namespace helium;
Histogram::Histogram()
: size_(0),
histogram_red_(new uint32[256]),
histogram_green_(new uint32[256]),
histogram_blue_(new uint32[256]),
expected_(0),
variance_(0) {
memset(histogram_red_, 0, 256 * 4);
memset(histogram_green_, 0, 256 * 4);
memset(histogram_blue_, 0, 256 * 4);
}
void Histogram::AddImage(const Image& image) {
ASSERT(histogram_red_ && histogram_green_ && histogram_blue_);
// Fill histogram with values of image
Color* image_ptr = image.data();
for (unsigned i = 0; i < image.width() * image.height(); i++)
AddColor(*(image_ptr++));
}
bool Histogram::Subtract(const Histogram& other) {
bool valid = true;
if (size_ < other.size_) {
fprintf(stderr, "Warning: size %d - %d\n", size_, other.size_);
valid = false;
}
for (unsigned i = 0; i < 256; i++) {
// Make sure we can subtract these values
if (!(histogram_red_[i] >= other.histogram_red_[i]) ||
!(histogram_green_[i] >= other.histogram_green_[i]) ||
!(histogram_blue_[i] >= other.histogram_blue_[i])) {
fprintf(stderr, "Warning: HistogramSubtract "
"[%d] -- R:%d-%d\tG:%d-%d\tB:%d-%d\n",
i,
histogram_red_[i], other.histogram_red_[i],
histogram_green_[i], other.histogram_green_[i],
histogram_blue_[i], other.histogram_blue_[i]);
valid = false;
}
histogram_red_[i] -= other.histogram_red_[i];
histogram_green_[i] -= other.histogram_green_[i];
histogram_blue_[i] -= other.histogram_blue_[i];
if (histogram_red_[i] < 0) histogram_red_[i] = 0;
if (histogram_green_[i] < 0) histogram_green_[i] = 0;
if (histogram_blue_[i] < 0) histogram_blue_[i] = 0;
}
size_ -= other.size_;
if (size_ < 0) size_ = 0;
return valid;
}
void Histogram::Done() {
ASSERT(histogram_red_ && histogram_green_ && histogram_blue_);
float exp_red, exp_green, exp_blue;
CalculateExpected(exp_red, exp_green, exp_blue);
CalculateVariance(exp_red, exp_green, exp_blue);
// Delete histograms
delete[] histogram_red_;
delete[] histogram_green_;
delete[] histogram_blue_;
histogram_red_ = histogram_green_ = histogram_blue_ = NULL;
}
void Histogram::CalculateExpected(float& exp_red,
float& exp_green,
float& exp_blue) {
exp_red = 0.0;
exp_green = 0.0;
exp_blue = 0.0;
float n = static_cast<float>(size_);
if (n <= 0.0) return;
for (unsigned i = 0; i < 256; i++) {
float v = static_cast<float>(i);
// Calculate probabilities
float p_r = static_cast<float>(histogram_red_[i]) / n;
float p_g = static_cast<float>(histogram_green_[i]) / n;
float p_b = static_cast<float>(histogram_blue_[i]) / n;
// Sum weighted value
exp_red += p_r * v;
exp_green += p_g * v;
exp_blue += p_b * v;
}
expected_ = MakeColor(static_cast<uint8>(exp_red),
static_cast<uint8>(exp_green),
static_cast<uint8>(exp_blue));
}
void Histogram::CalculateVariance(float exp_red,
float exp_green,
float exp_blue) {
float n = static_cast<float>(size_);
if (n <= 0.0) return;
float r = 0.0, g = 0.0, b = 0.0;
for (unsigned i = 0; i < 256; i++) {
float v = static_cast<float>(i);
// Calculate probabilities
float p_r = static_cast<float>(histogram_red_[i]) / n;
float p_g = static_cast<float>(histogram_green_[i]) / n;
float p_b = static_cast<float>(histogram_blue_[i]) / n;
// Sum square distances to expected value
r += Square(v - exp_red) * p_r;
g += Square(v - exp_green) * p_g;
b += Square(v - exp_blue) * p_b;
}
variance_ = MakeColor(static_cast<int>(sqrt(r)),
static_cast<int>(sqrt(g)),
static_cast<int>(sqrt(b)));
}
Color Histogram::Percentile(uint8 percentile) const {
return MakeColor(ChannelPercentile(histogram_red_, percentile),
ChannelPercentile(histogram_green_, percentile),
ChannelPercentile(histogram_blue_, percentile));
}
uint32 Histogram::ChannelPercentile(uint32* channel, uint8 percentile) const {
ASSERT(histogram_red_ && histogram_green_ && histogram_blue_);
ASSERT(percentile <= 100);
float p = static_cast<float>(percentile) / 100.0;
uint32 threshold = static_cast<uint32>(static_cast<float>(size_) * p);
uint32 i = 0;
for (uint32 sum = 0; sum < threshold; sum += channel[i++]);
return (i == 0) ? 0 : i - 1;
}