views:

652

answers:

3

I calculated the histogram(a simple 1d array) for an 3D grayscale Image. Now I would like to calculate the gradient for the this histogram at each point. So this would actually mean I have to calculate the gradient for a 1D function at certain points. However I do not have a function. So how can I calculate it with concrete x and y values?

For the sake of simplicity could you probably explain this to me on an example histogram - for example with the following values (x is the intensity, and y the frequency of this intensity):

x1 = 1; y1 = 3

x2 = 2; y2 = 6

x3 = 3; y3 = 8

x4 = 4; y4 = 5

x5 = 5; y5 = 9

x6 = 6; y6 = 12

x7 = 7; y7 = 5

x8 = 8; y8 = 3

x9 = 9; y9 = 5

x10 = 10; y10 = 2

I know that this is also a math problem, but since I need to solve it in c++ I though you could help me here.

Thank you for your advice Marc

+2  A: 

Hi

  1. Take some squared paper and draw on it your histogram. Draw also vertical and horizontal axes through the 0,0 point of your histogram.

  2. Take a straight edge and, at each point you are interested in, rotate the straight edge until it accords with your idea of what the gradient at that point is. It is most important that you do this, your definition of gradient is the one you want.

  3. Once the straight edge is at the angle you desire draw a line at that angle.

  4. Drop perpendiculars from any 2 points on the line you just drew. It will be easier to take the following step if the horizontal distance between the 2 points you choose is about 25% or more of the width of your histogram. From the same 2 points draw horizontal lines to intersect the vertical axis of your histogram.

  5. Your lines now define an x-distance and a y-distance, ie the length of the horizontal/ vertical (respectively) axes marked out by their intersections with the perpendiculars/horizontal lines. The gradient you want is the y-distance divided by the x-distance.

Now, to translate this into code is very straightforward, apart from step 2. You have to define what the criteria are for determining what the gradient at any point on the histogram is. Simple choices include:

a) at each point, set down your straight edge to pass through the point and the next one to its right;

b) at each point, set down your straight edge to pass through the point and the next one to its left;

c) at each point, set down your straight edge to pass through the point to the left and the point to the right.

You may want to investigate more complex choices such as fitting a curve (such as a quadratic or higher-order polynomial) through a number of points on your histogram and using the derivative of that to represent the gradient.

Until you understand the question on paper avoid coding in C++ or anything else. Once you do understand it, coding should be trivial.

Regards

Mark

High Performance Mark
essentially you have told him to make up whatever gradient is pleasing to him, either aesthetically or otherwise. You don't seem to have some up with a generic way to compute such angles, although I applaud the way your approach brings home the central question (what gradient do you want?).
Alex Brown
Yes, that's right. It's his job to define what the gradient should be and it's not a trivial issue. If he wants an aesthetically pleasing gradient he's entitled to have one, though I suspect that's not what he really needs in this situation. I also contend that measuring the slope of a straight line is exactly a generic way of computing a gradient. But it's the OP's choice.
High Performance Mark
Hello, thanks for your replys. Actually I need the gradient to determine whether my histograms slope is rising or falling. For now I want to start at a certain peak of the histogram and than find out in which intensity range to the left and riht of the peak the slope is falling. And from when does a new, rising slope towards another peak start.In general the approximation suggested below (by Andreas) would be ok, however I think that there may be outlier on the falling slope.It would be good if I could avoid such misinterpretations. Regards
Marc
+3  A: 

I think you can calculate your gradient using the same approach used in image border detection (which is a gradient calculus). If your histogram is in a vector you can calculate an approximation of the gradient as*:

for each point in the histogram compute 
     gradient[x] = (hist[x+1] - hist[x])

This is a very simple way to do it, but I'm not sure if is the most accurate.

  • approximation because you are working with discrete data instead of continuous

Edited:

Other operators will may emphasize small differences (small gradients will became more emphasized). Roberts algorithm derives from the derivative calculus:

lim delta -> 0 = f(x + delta) - f(x) / delta

delta tends infinitely to 0 (in order to avoid 0 division) but is never zero. As in computer's memory this is impossible, the smallest we can get of delta is 1 (because 1 is the smallest distance from to points in an image (or histogram)).

Substituting

lim delta -> 0 to lim delta -> 1

we get

f(x + 1) - f(x) / 1 = f(x + 1) - f(x) => vet[x+1] - vet[x]
Andres
Hi, for boarder detection in 2D Images they often use masks like: [-1 0 1 ][-2 0 2 ][-1 0 1 ](this should be a 3x3 mask - each part in [] is one line). WOuld some kind of mask like this also be appliable to 1D? For example like this: -1 0 1, or -2 0 2 (so for my first mask: -1*hist[x-1] + 0*hist[x] + 1*hist[x+1])Would this probably enhance your approach(which is basically a mask with 1 -1) or would results be worse?Regards
Marc
Actually the algorithm that I answered here is the Roberts border detection operators (the most simple edge detection algorithm). You can do it using convolution masks if you want [1 -1] but you'll end up with the same result and I think just computing the differences is faster. You can use any variation of image algorithm here like Laplace [-1 2 -1] - Laplace operator can be write as [0 -1 0][-1 4 -1][0 -1 0] but here I adapted it to 1D.
Andres
@Andres: A [1 -1] convolution mask *is* the difference! Not only the result, but also the computation is identical... although if you are thinking of it "left to right," [-1 1] is the corresponding sign. [-1 2 -1] can be considered as the sum of the differences on either side.
Hi, thanks for your replies so far. I have another question - that I hope is not too stupied - but since I am still a little at a loss I just try:Is there actually also a gaussian mask for 1D with standard values that can be used to calculate the gradient? Or is there a certain mask that is to be prefered for gradient calculation?
Marc
+2  A: 

Two generally approaches here:

  1. a discrete approximation to the derivative
  2. take the real derivative of a fitted function

In the first case try:

g = (y_(i+1) - y_(i-1))/2*dx

at all the points except the ends, or one of

g_left-end  = (y_(i+1) - y_i)/dx
g_right-end = (y_i - y_(i-1))/dx

where dx is the spacing between x points. (Unlike the equally correct definition Andres suggested, this one is symmetric. Whether it matters or not depends on you use case.)

In the second case, fit a spline to your data[*], and ask the spline library the derivative at the point you want.

[*] Use a library! Do not implement this yourself unless this is a learning project. I'd use ROOT because I already have it on my machine, but it is a pretty heavy package just to get a spline...


Finally, if you data is noisy, you ma want to smooth it before doing slope detection. That was you avoid chasing the noise, and only look at large scale slopes.

dmckee
Can you explain what do you mean by symmetric? +1
Andres
@Andres: Sure. Imagine a symmetric peak in the histogram with long flat tails. Because your function (the natural one if you're thinking about the differential limit, of course) involves `i` and `i+1`, but not `i-1`, the center of the bimodal gradient distribution will not be on the center of the peak. Like I said, it might or might not matter. And mine has two special cases where your's has only one. Decisions, decisions...
dmckee
That's great. In image processing there's a similar problem (not with bimodal function). The detected edge doesn't fit perfectly with the borders of the original image (edges can be seen as a kind of sigmoid function). In theory the zero crossing should solve this problem (double derivative) but due to the discrete nature of the image, this is not very precise. Cheers
Andres
@ dmckee: Can you probably also help me on the following concerns:Firstly what can I do about outliers? If I have a slope like the following for example:y(i) = 20; y(i+1) = 18; y(i+2) = 15; y(i+3) = 12; y(i+4) = 17; y(i+5) = 11; y(i+6) = 10; y(i+7) = 8;..If I calculated y_(i) at i+1 for example that would be fine. But if I calculate it at i+5, the gradient will be positive although the overall slope is falling. This is important for me since I want to find out the end point of the total slope-where the gradient starts to raise again.Secondly...see next comment
Marc
...In case it is only important for me wether the slope(gradient) is falling or rising (and I dont really know the spacing) - can I simply set dx to 1?!
Marc
Sure, fix `dx` at one, it won't cause any trouble here. This is a local function, that is it approximates the slope at each point, in the data given above you'd have signs (-, -, -, +, -, -, -, -). Whether the sensitivity of this method to the little bobble around i+4 is an issue or not depends on your use case. If you are *trying* to pick that peak off the background, you probably eed a more sophisticated method or more detailed data.
dmckee