Reputation: 59
I am trying to understand how the gaussian blur in Gimp works. I downloaded the code, i could almost understand few things ... but i am puzzled on something else.
Here's the code :
make_rle_curve (gdouble sigma,
gint **p_curve,
gint *p_length,
gint **p_sum,
gint *p_total)
{
const gdouble sigma2 = 2 * sigma * sigma;
const gdouble l = sqrt (-sigma2 * log (1.0 / 255.0));
gint temp;
gint i, n;
gint length;
gint *sum;
gint *curve;
n = ceil (l) * 2;
if ((n % 2) == 0)
n += 1;
curve = g_new (gint, n);
length = n / 2;
curve += length; /* 'center' the curve[] */
curve[0] = 255;
for (i = 1; i <= length; i++)
{
temp = (gint) (exp (- (i * i) / sigma2) * 255);
curve[-i] = temp;
curve[i] = temp;
}
sum = g_new (gint, 2 * length + 1);
sum[0] = 0;
for (i = 1; i <= length*2; i++)
{
sum[i] = curve[i-length-1] + sum[i-1];
}
sum += length; /* 'center' the sum[] */
*p_total = sum[length] - sum[-length];
*p_curve = curve;
*p_sum = sum;
*p_length = length;
For me, curve and sum are 2 arrays. curve goes from let's say -3 to +3
and sum goes from 0 to 6. In other words, i have
curve[-3] = ...
curve[0] = 255
curve[3] = ...
but what does curve = curve + length
really does ?
Same thing, what does sum = sum + length
does ?
Thank you very much for your help !
ps: i'm not a genius in coding :(
Upvotes: 2
Views: 349
Reputation: 1482
Slick code. It appears that they are doing pointer arithmetic.
I'll start at the top and work down using curve
for the explanation. I know you are only asking about curve, but I will explain the other stuff too for clarity.
gint *curve;
This creates a pointer to a memory address that is big enough to hold an integer (of type gint). At the moment it is not initialized and so not very helpful
curve = g_new (gint, n);
Now, an array of integers (gint) are created and curve is assigned that starting address of this new array.
length = n / 2;
Here they are calculating the midpoint of the filter which is size n
. The size of the filter was based on the l
, which is based on the sigma parameter and log transform math earlier. Here, length
is referring to radius of the 1D kernel, not the length of the array which is twice as long.
curve += length; /* 'center' the curve[] */
Your question: What is going on here?
This changes the variable curve
so that it now points to the middle of the array that was previously initialized so that the follow-on for loop indexing is likely easier for the programmer to think about, and a bit cleaner to write, debug, etc.
Here, because the []
operators are not used, the reference to curve
is its address in memory. The memory that was allocated remains in place and is not going to move around (sure you can copy it from one place to another, but that's not what I am talking about). The pointer, curve
, can move around so to speak. The address of curve
itself is set, however, as a variable, the address it contains can be adjusted and changed so that it points to different things just like any other, non constant variable can have its value changed.
At first, curve
was pointing to the address of curve[0]. It's not the case, but since you say you are new, let's say curve[0] is at memory address 00. So curve
's value, is also equal address 00. The call curve += length
can be interpreted as curve = curve + length
which internally is Add the size (in bytes) of the length's data type to the address of curve and then update curve's address with the result. For the sake of example, let's say that gint requires 4 bytes and length
is 5, and curve
is still 00.
Thus the calls become
curve += length
curve = curve + length
curve = curve + sizeof(length)
curve =
Address curve points to +
number of bytes length uses of memorycurve =
Address 00 +
4 bytes * 8 bits/bytecurve = Address 32
(Address 32 would be written in hexadecimal as 0x20)Again, the memory that was declared with g_new(gint, n)
is still there, where it was declared (it does not move), but now, because the pointer was changed, it (the pointer) can be used to index into that array using negative indices. That is, curve[0]
is actually the value in the middle of the array now, while curve[-n]
is the first element of the array.
In the filter, the midpoint (curve[0]
) is set directly to the maximum value:
curve[0] = 255;
The follow on iterations work out from here (+/- i) to fill in the kernel.
You can see in the next for loop however that the curve values are being offset again to center up (i.e. curve[i-length-1]
). It's a little confusing now because it looks like they are mix and matching the two approaches (sometimes using pointer arithmetic to make the indexing cleaner and then sometimes not). But hey, they programmed it and likely had a good reason for doing it this way based on how well the rest of the code looks. And who knows, maybe they just wanted to mix things up for the sake of variety.
UPDATE
Regarding sum
, the values in the array are set first, and then the pointer is changed to point to the center of this filled-in array. However, the program could have followed the same pattern seen earlier with curve
and been written like this:
sum += length; /* 'center' the sum[] */
sum[-length] = 0;
for (i = -length+1; i <= length; i++)
{
sum[i] = curve[i] + sum[i-1];
}
As an aside, the call *p_total = sum[length] - sum[-length];
looks like it can simply be p_total = sum[length];
since sum[-length]
is always set to 0. That is, sum[0]=0
followed later by sum += length
means that sum[-length]
after the adjustment is the same as sum[0]
before the pointer adjustment. Both reference are to the first value in the array, which is 0.
Placing the sum
pointer to the middle of the array is consistent with curve
being set to the middle of its array when the function ends.
In the case with 'sum' however, the adjustment was made after the array was initialized with values, while in the case of curve
the adjustment was made first and then the values initialized. This could just be the programmers preference to mix things up, it may be easier to think of integrating with indexes going from 1 to K (so to speak). There are likely other ways to write this code that work just as well, which is the beauty of it all (or perhaps, at times, the curse).
Upvotes: 4