Skip to main content
gamedev.net
🔒 Locked

Histogram Scaling

Started by gooseserbus 3 replies 950+ views
Original Post
gooseserbus
gooseserbus
Sorry if this isn't the correct forum to put this in but I didn't think it really fit any where else (correct me if I'm wrong - maybe it belongs in the maths forum?) I'm currently programming some image processing software using directx (however it has been abstracted to the point where I do not need to deal with this directly anymore so this is *not* a question about directx). I am creating a histogram based on the image (for your information this is a greyscale image) so the histogram I am creating is comprised of an array of 255 values (integers). I process the image and store a 'sum' of the number of pixels of a certain intensity in the corresponding value of the array (so I end up with 255 values like -> histogramArray[60] = 30, histogramArray[180] = 1286, etc.) I next want to display these values in a histogram. I have therefore created a region next to the image to display this (a white 255x255 square [it is actually slightly bigger than this for a border but that's beside the point]). Next I want to draw a 'column' of pixels based on the values in the histogramArray[] for each of the pixels along the 255 squares' x-axis', i.e. at the bottom left pixel of the square (0,255) I draw a column corresponding in height to the number in histogramArray[0], then at the pixel 'one-over' from this (1,255) I draw a column corresponding in height to the number in histogramArray[1], and so on until the end of the array/white square. The problem I am having is with the scaling of this graph. My height is 'fixed' at 255 (my width is also fixed at this value but this is intentional - i.e. one x-coordinate for each intensity level and I can easily double this up if I want to). Well the height is not *actually* fixed but it *will* be fixed at a *certain* value. So therefore, because the values in histogramArray[] are not guaranteed to below 255 or any other fixed value (so I can use one pixel per value), I need to scale all the values down so that they will all appear relatively on the graph (and the maximum value in the array will be at or just below 255 - *not* above). How do I mathematically perform this scaling? For a certain test picture I am using I have created the histogram using a spreadsheet program and I worked out (through trial and error) what the best value for scaling like this (where the max value from the array is at or just below the max) was (it was 17 for this test picture if you really want to know). What is the best way for finding this value or is there another way to perform this scaling which I have completely missed?? Thanks in advance for any help anyone can give. -Goose
Winograd
Winograd
Often histograms are scaled with the total number of pixels. So if all pixels are say gray level 128, then histogram[128]=1 and other bins are 0. If half of the pixels are 0 and half 128, then corresponding bins of histogram would be scaled down to 0.5. Thus the bins of histogram would represent the percentage of pixels of the given intensity. If you like the max to be 255, then you just multiply the scaled histogram with 255 (every bin that is). Hope this helps.
smart_idiot
smart_idiot
#include <iostream>#include <iterator>#include <algorithm>struct scale {  double s;  scale(double s=1.0): s(s) {}  template<typename T> T operator () (T value) {return static_cast<T>(static_cast<double>(value)*s);} };template <typename I> void renormalize(I start, I end, typename std::iterator_traits::value_type maximum) {  std::transform(start, end, start, scale(static_cast<double>(maximum)/static_cast<double>(*std::max_element(start, end)))); }int main() {  // Example:  int start[10] = {5, 10, 7, 6, 8, 9, 12, 15, 17, 13}, *end=start+10;    std::cout << "Array contains: ";  std::copy(start, end, std::ostream_iterator<int>(std::cout, ", "));    std::cout << "\nRenormalizing to 255\n";  renormalize(start, end, 255);    std::cout << "Array now contains: ";  std::copy(start, end, std::ostream_iterator<int>(std::cout, ", "));  std::cout << std::endl;    return 0; }



Array contains: 5, 10, 7, 6, 8, 9, 12, 15, 17, 13,
Renormalizing to 255
Array now contains: 75, 150, 105, 90, 120, 135, 180, 225, 255, 195,
Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other
iMalc
iMalc
In other words, what smart_idiot is saying in code, is that you should scale the values according to the largest value. This means first a pass through the array to find the maximum value, then your scale factor is 255/maximum.
This will always make the tallest bar 255 pixels high, and if they are all the same value then all bars will be 255 pixels high.

If you don't go for a dynamically calculated scale factor then the image size will also affect things...

The other option is to have a fixed scale value, or at least peg the above calculated scale factor between a minimum and/or maximum scale factor. However then you run the risk of a bar being taller than 255 pixels. In that case you simply chop it off at 255.

gooseserbus
gooseserbus
While GameDev.net was having some server issues last night (just after I posted my original message) I think I worked out a way to do it (but I don't know if this is a good way and I don't think it is very efficient):

// first find the highest value in the histogram arrayint maxValue;for ( int i = 1; i < 255; i++ ){		if ( histogramArray > maxValue )	{            maxValue = histogramArray;        }}// find the value to scale the array by to create a histogram which fits in the 255 high windowint scale = 0;i = 1;while(true) // loops until scale value is found - breaks out from middle{	scale = maxValue/i;	if ( scale > 255 )	{		i++;	}	else	{		scale = i;		break;	}}//now scale the array using the scale value we just worked outfor ( i = 0; i < 255; i++ ){	histogramArray /= scale;}//now draw each of the bars of the graphfor ( i = 0; i<255; i++ ) //for each column{	if ( histogram != 0 ) // quick test as there are lots of 0's in the array (should speed things up but is not necessary)	{		for ( int j = (255-histogram); j <= 255; j++ ) //draw column height		{			PlotPix(i, j, 80, 80, 80); // this draws a pixel with an rgb value of 80,80,80		}	}}


So like iMalc said I have passed through the array to find the maximum value and then I have scaled the graph using this value, however I have scaled it differently. I loop from one until I find the first value which will scale the maximum to below 255.

A couple of you guys said that I should work out the scale factor as 255/maximum, but I don't really get that, some of the data from my array (for one picture) is like this:

histogram[241] = 405histogram[242] = 428histogram[243] = 407histogram[244] = 467histogram[245] = 472histogram[246] = 558histogram[247] = 576histogram[248] = 764histogram[249] = 807histogram[250] = 1005histogram[251] = 1208histogram[252] = 1553histogram[253] = 2130histogram[254] = 4202

So here I find the maximum value in the array is histogram[254] = 4202. The way I find the scale factor (by looping through) gives my a scale factor of 17, which I can then divide the rest of the array by to scale the data and ensure that all the data is below 255. If I find the scale factor like you said (by doing 255/maxValue) I get 0.06... which if then applied to the values in the graph gives me a really small graph.

I realise that I have probably misunderstood how I should use this calculation but I really don't get it so if you can point me in the right direction that would be great.

Thanks

-Goose

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.