From a789df141de28c52dfd738dd70c81fa57bf4c850 Mon Sep 17 00:00:00 2001 From: David Rose Date: Mon, 9 Nov 2009 01:11:37 +0000 Subject: [PATCH] histogram stuff --- panda/src/pnmimage/pnmImage.cxx | 65 +++++++ panda/src/pnmimage/pnmImage.h | 5 + panda/src/pnmimage/pnmImageHeader.I | 252 +++++++++++++++++++++++++- panda/src/pnmimage/pnmImageHeader.cxx | 11 +- panda/src/pnmimage/pnmImageHeader.h | 63 ++++++- 5 files changed, 376 insertions(+), 20 deletions(-) diff --git a/panda/src/pnmimage/pnmImage.cxx b/panda/src/pnmimage/pnmImage.cxx index a65bd8805e..2f857c5b9a 100644 --- a/panda/src/pnmimage/pnmImage.cxx +++ b/panda/src/pnmimage/pnmImage.cxx @@ -615,6 +615,44 @@ set_channel_val(int x, int y, int channel, xelval value) { } } +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::get_pixel +// Access: Published +// Description: Returns the (r, g, b, a) pixel value at the indicated +// pixel, using a PixelSpec object. +//////////////////////////////////////////////////////////////////// +PNMImage::PixelSpec PNMImage:: +get_pixel(int x, int y) const { + switch (_num_channels) { + case 1: + return PixelSpec(get_gray_val(x, y)); + case 2: + return PixelSpec(get_gray_val(x, y), get_alpha_val(x, y)); + case 3: + return PixelSpec(get_xel_val(x, y)); + case 4: + return PixelSpec(get_xel_val(x, y), get_alpha_val(x, y)); + } + + return PixelSpec(0); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::set_pixel +// Access: Published +// Description: Sets the (r, g, b, a) pixel value at the indicated +// pixel, using a PixelSpec object. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +set_pixel(int x, int y, const PixelSpec &pixel) { + xel p; + PPM_ASSIGN(p, pixel._red, pixel._green, pixel._blue); + set_xel_val(x, y, p); + if (_alpha != NULL) { + set_alpha_val(x, y, pixel._alpha); + } +} + //////////////////////////////////////////////////////////////////// // Function: PNMImage::blend // Access: Published @@ -1064,6 +1102,33 @@ expand_border(int left, int right, int bottom, int top, take_from(new_image); } +//////////////////////////////////////////////////////////////////// +// Function: PNMImage::make_histogram +// Access: Published +// Description: Computes a histogram of the colors used in the +// image. +//////////////////////////////////////////////////////////////////// +void PNMImage:: +make_histogram(PNMImage::Histogram &histogram) { + HistMap hist_map; + PixelCount pixels; + + int num_pixels = _x_size * _y_size; + + compute_histogram(hist_map, _array, _alpha); + + pixels.reserve(hist_map.size()); + HistMap::const_iterator hi; + for (hi = hist_map.begin(); hi != hist_map.end(); ++hi) { + if ((*hi).second <= num_pixels) { + pixels.push_back(PixelSpecCount((*hi).first, (*hi).second)); + } + } + sort(pixels.begin(), pixels.end()); + + histogram.swap(pixels, hist_map); +} + //////////////////////////////////////////////////////////////////// // Function: PNMImage::setup_rc // Access: Private diff --git a/panda/src/pnmimage/pnmImage.h b/panda/src/pnmimage/pnmImage.h index f5a7fdcbf8..dcebaaebc7 100644 --- a/panda/src/pnmimage/pnmImage.h +++ b/panda/src/pnmimage/pnmImage.h @@ -144,6 +144,9 @@ PUBLISHED: xelval get_channel_val(int x, int y, int channel) const; void set_channel_val(int x, int y, int channel, xelval value); + PixelSpec get_pixel(int x, int y) const; + void set_pixel(int x, int y, const PixelSpec &pixel); + // The corresponding get_xel(), set_xel(), get_red(), etc. functions // automatically scale their values by get_maxval() into the range // [0..1]. @@ -224,6 +227,8 @@ PUBLISHED: void quick_filter_from(const PNMImage ©, int xborder = 0, int yborder = 0); + void make_histogram(Histogram &hist); + private: INLINE void allocate_array(); INLINE void allocate_alpha(); diff --git a/panda/src/pnmimage/pnmImageHeader.I b/panda/src/pnmimage/pnmImageHeader.I index 47b0590de8..68eac94a87 100644 --- a/panda/src/pnmimage/pnmImageHeader.I +++ b/panda/src/pnmimage/pnmImageHeader.I @@ -241,11 +241,11 @@ set_type(PNMFileType *type) { // Description: Records the indicated color in the histogram. //////////////////////////////////////////////////////////////////// INLINE void PNMImageHeader:: -record_color(PNMImageHeader::Histogram &hist, +record_color(PNMImageHeader::HistMap &hist, const PNMImageHeader::PixelSpec &color) { // First, try to add the color with a count of 0, in case it does // not already exist in the table. - Histogram::iterator hi = hist.insert(Histogram::value_type(color, 0)).first; + HistMap::iterator hi = hist.insert(HistMap::value_type(color, 0)).first; // Now that either succeeded or failed, but either way hi is now the // iterator to the count value in the table associated with the @@ -255,7 +255,7 @@ record_color(PNMImageHeader::Histogram &hist, //////////////////////////////////////////////////////////////////// // Function: PNMImageHeader::PixelSpec::Constructor -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE PNMImageHeader::PixelSpec:: @@ -269,7 +269,7 @@ PixelSpec(xelval gray_value) : //////////////////////////////////////////////////////////////////// // Function: PNMImageHeader::PixelSpec::Constructor -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE PNMImageHeader::PixelSpec:: @@ -283,7 +283,7 @@ PixelSpec(xelval gray_value, xelval alpha) : //////////////////////////////////////////////////////////////////// // Function: PNMImageHeader::PixelSpec::Constructor -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE PNMImageHeader::PixelSpec:: @@ -297,7 +297,7 @@ PixelSpec(xelval red, xelval green, xelval blue) : //////////////////////////////////////////////////////////////////// // Function: PNMImageHeader::PixelSpec::Constructor -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE PNMImageHeader::PixelSpec:: @@ -309,9 +309,37 @@ PixelSpec(xelval red, xelval green, xelval blue, xelval alpha) : { } +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImageHeader::PixelSpec:: +PixelSpec(const xel &rgb) : + _red(PPM_GETR(rgb)), + _green(PPM_GETG(rgb)), + _blue(PPM_GETB(rgb)), + _alpha(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImageHeader::PixelSpec:: +PixelSpec(const xel &rgb, xelval alpha) : + _red(PPM_GETR(rgb)), + _green(PPM_GETG(rgb)), + _blue(PPM_GETB(rgb)), + _alpha(alpha) +{ +} + //////////////////////////////////////////////////////////////////// // Function: PNMImageHeader::PixelSpec::Copy Constructor -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE PNMImageHeader::PixelSpec:: @@ -325,7 +353,7 @@ PixelSpec(const PixelSpec ©) : //////////////////////////////////////////////////////////////////// // Function: PNMImageHeader::PixelSpec::Copy Assignment Operator -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE void PNMImageHeader::PixelSpec:: @@ -338,7 +366,7 @@ operator = (const PixelSpec ©) { //////////////////////////////////////////////////////////////////// // Function: PNMImageHeader::PixelSpec::Comparison Operator -// Access: Public +// Access: Published // Description: //////////////////////////////////////////////////////////////////// INLINE bool PNMImageHeader::PixelSpec:: @@ -354,3 +382,209 @@ operator < (const PixelSpec &other) const { } return _alpha < other._alpha; } + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::get_red +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImageHeader::PixelSpec:: +get_red() const { + return _red; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::get_green +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImageHeader::PixelSpec:: +get_green() const { + return _green; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::get_blue +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImageHeader::PixelSpec:: +get_blue() const { + return _blue; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::get_alpha +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImageHeader::PixelSpec:: +get_alpha() const { + return _alpha; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::set_red +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMImageHeader::PixelSpec:: +set_red(xelval red) { + _red = red; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::set_green +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMImageHeader::PixelSpec:: +set_green(xelval green) { + _green = green; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::set_blue +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMImageHeader::PixelSpec:: +set_blue(xelval blue) { + _blue = blue; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::set_alpha +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PNMImageHeader::PixelSpec:: +set_alpha(xelval alpha) { + _alpha = alpha; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::operator [] +// Access: Published +// Description: Indexes numerically into the components, in the order +// R, G, B, A. This also makes the PixelSpec work like +// a tuple in Python. +//////////////////////////////////////////////////////////////////// +INLINE xelval PNMImageHeader::PixelSpec:: +operator [](int n) const { + nassertr(n >= 0 && n < size(), 0); + return (&_red)[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpec::size +// Access: Published, Static +// Description: Specifies the number of components in the PixelSpec; +// this is always 4, regardless of the type of image it +// was taken from. +//////////////////////////////////////////////////////////////////// +INLINE int PNMImageHeader::PixelSpec:: +size() { + return 4; +} + +// Interrogate seems to have some problem with the syntax of this +// method. Whatever, we don't need it. +#ifndef CPPPARSER +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpecCount::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImageHeader::PixelSpecCount:: +PixelSpecCount(const PNMImageHeader::PixelSpec &pixel, int count) : + _pixel(pixel), + _count(count) +{ +} +#endif // CPPPARSER + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::PixelSpecCount::operator < +// Access: Public +// Description: Used to sort the pixels in order from most common to +// least common. +//////////////////////////////////////////////////////////////////// +INLINE bool PNMImageHeader::PixelSpecCount:: +operator < (const PNMImageHeader::PixelSpecCount &other) const { + return _count > other._count; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::Histogram::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PNMImageHeader::Histogram:: +Histogram() { +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::Histogram::get_num_pixels +// Access: Published +// Description: Returns the number of unique pixel colors in the +// histogram. +//////////////////////////////////////////////////////////////////// +INLINE int PNMImageHeader::Histogram:: +get_num_pixels() const { + return _pixels.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::Histogram::get_pixel +// Access: Published +// Description: Returns the nth unique pixel color in the histogram. +// These are ordered by default from most common to +// least common. +//////////////////////////////////////////////////////////////////// +INLINE const PNMImageHeader::PixelSpec &PNMImageHeader::Histogram:: +get_pixel(int n) const { + nassertr(n >= 0 && n < _pixels.size(), _pixels[0]._pixel); + return _pixels[n]._pixel; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::Histogram::get_count +// Access: Published +// Description: Returns the number of occurrences in the image of the +// nth unique pixel color in the histogram. +//////////////////////////////////////////////////////////////////// +INLINE int PNMImageHeader::Histogram:: +get_count(int n) const { + nassertr(n >= 0 && n < _pixels.size(), 0); + return _pixels[n]._count; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::Histogram::get_count +// Access: Published +// Description: Returns the number of occurrences in the image of the +// indicated pixel color. +//////////////////////////////////////////////////////////////////// +INLINE int PNMImageHeader::Histogram:: +get_count(const PNMImageHeader::PixelSpec &pixel) const { + HistMap::const_iterator hi; + hi = _hist_map.find(pixel); + if (hi == _hist_map.end()) { + return 0; + } + return (*hi).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: PNMImageHeader::Histogram::swap +// Access: Public +// Description: Swaps the data in the Histogram with the indicated +// data. This is normally used to load the Histogram +// data initially in PNMImage::make_histogram(). +//////////////////////////////////////////////////////////////////// +INLINE void PNMImageHeader::Histogram:: +swap(PixelCount &pixels, HistMap &hist_map) { + _pixels.swap(pixels); + _hist_map.swap(hist_map); +} diff --git a/panda/src/pnmimage/pnmImageHeader.cxx b/panda/src/pnmimage/pnmImageHeader.cxx index bfac826941..1b1b8b404b 100644 --- a/panda/src/pnmimage/pnmImageHeader.cxx +++ b/panda/src/pnmimage/pnmImageHeader.cxx @@ -437,6 +437,9 @@ output(ostream &out) const { // class, but it is defined at this level in case it has // general utilty for PNMImages. // +// Also see PNMImage::make_histogram(), which is a +// higher-level function. +// // The max_colors parameter, if greater than zero, // limits the maximum number of colors we are interested // in. If we encounter more than this number of colors, @@ -444,7 +447,7 @@ output(ostream &out) const { // false; otherwise, it returns true. //////////////////////////////////////////////////////////////////// bool PNMImageHeader:: -compute_histogram(PNMImageHeader::Histogram &hist, +compute_histogram(PNMImageHeader::HistMap &hist, xel *array, xelval *alpha, int max_colors) { int num_pixels = _x_size * _y_size; int pi; @@ -502,14 +505,14 @@ compute_histogram(PNMImageHeader::Histogram &hist, bool PNMImageHeader:: compute_palette(PNMImageHeader::Palette &palette, xel *array, xelval *alpha, int max_colors) { - Histogram hist; + HistMap hist; int num_pixels = _x_size * _y_size; // In case there are already entries in the palette, preserve them. Palette::const_iterator pi; for (pi = palette.begin(); pi != palette.end(); ++pi) { - hist.insert(Histogram::value_type(*pi, num_pixels + 1)); + hist.insert(HistMap::value_type(*pi, num_pixels + 1)); } if (!compute_histogram(hist, array, alpha, max_colors)) { @@ -519,7 +522,7 @@ compute_palette(PNMImageHeader::Palette &palette, // Now append the new entries discovered in the histogram onto the // end of the palette. palette.reserve(hist.size()); - Histogram::const_iterator hi; + HistMap::const_iterator hi; for (hi = hist.begin(); hi != hist.end(); ++hi) { if ((*hi).second <= num_pixels) { palette.push_back((*hi).first); diff --git a/panda/src/pnmimage/pnmImageHeader.h b/panda/src/pnmimage/pnmImageHeader.h index 33de6aadb4..5cae08c68c 100644 --- a/panda/src/pnmimage/pnmImageHeader.h +++ b/panda/src/pnmimage/pnmImageHeader.h @@ -103,32 +103,81 @@ PUBLISHED: void output(ostream &out) const; -public: - // These classes are used internally, but must be declared public - // for fiddly reasons. + // Contains a single pixel specification used in compute_histogram() + // and make_histogram(). Note that pixels are stored by integer + // value, not by floating-point scaled value. class PixelSpec { - public: + PUBLISHED: INLINE PixelSpec(xelval gray_value); INLINE PixelSpec(xelval gray_value, xelval alpha); INLINE PixelSpec(xelval red, xelval green, xelval blue); INLINE PixelSpec(xelval red, xelval green, xelval blue, xelval alpha); + INLINE PixelSpec(const xel &rgb); + INLINE PixelSpec(const xel &rgb, xelval alpha); INLINE PixelSpec(const PixelSpec ©); INLINE void operator = (const PixelSpec ©); INLINE bool operator < (const PixelSpec &other) const; + + INLINE xelval get_red() const; + INLINE xelval get_green() const; + INLINE xelval get_blue() const; + INLINE xelval get_alpha() const; + + INLINE void set_red(xelval red); + INLINE void set_green(xelval green); + INLINE void set_blue(xelval blue); + INLINE void set_alpha(xelval alpha); + + INLINE xelval operator [](int n) const; + INLINE static int size(); + void output(ostream &out) const; + public: xelval _red, _green, _blue, _alpha; }; - typedef pmap Histogram; + + // Associates a pixel specification with an appearance count, for + // use in Histogram, below. + class PixelSpecCount { + public: + INLINE PixelSpecCount(const PixelSpec &pixel, int count); + INLINE bool operator < (const PixelSpecCount &other) const; + + PixelSpec _pixel; + int _count; + }; + + typedef pmap HistMap; + typedef pvector PixelCount; typedef pvector Palette; + // Used to return a pixel histogram in PNMImage::get_histogram(). + class Histogram { + PUBLISHED: + INLINE Histogram(); + + INLINE int get_num_pixels() const; + INLINE const PixelSpec &get_pixel(int n) const; + INLINE int get_count(int n) const; + INLINE int get_count(const PixelSpec &pixel) const; + MAKE_SEQ(get_pixels, get_num_pixels, get_pixel); + + public: + INLINE void swap(PixelCount &pixels, HistMap &hist_map); + + private: + PixelCount _pixels; + HistMap _hist_map; + }; + protected: - bool compute_histogram(Histogram &hist, xel *array, xelval *alpha, + bool compute_histogram(HistMap &hist, xel *array, xelval *alpha, int max_colors = 0); bool compute_palette(Palette &palette, xel *array, xelval *alpha, int max_colors = 0); - INLINE void record_color(Histogram &hist, const PixelSpec &color); + INLINE void record_color(HistMap &hist, const PixelSpec &color); int _x_size, _y_size; int _num_channels;