Quick math questions

@Thanatomanic I don’t think so as I need to use information outside of spiral. That’s the hard part. From a point outside of spiral, picture a line that represent a line perpendicular to inner radii of spiral, and that meets the point and the line intersects with the spiral. Then find the length from start to the intersection of spiral and line.

I’ll provide more pictures to my problem tomorrow or two.

But, every point ‘in between’ is also represented by an Archimedean spiral, just with a different angular velocity. Or in the polar terms, r = a + b \theta with varying values of b you get all the ‘intermediate’ spirals. The derivation of the tangent I linked to is immediately applicable as far as I can tell, because it is for a general spiral. Only the arc length needs some work.
Edit: even the formula for the arc length can be used immediately, because it is derived from the case where a = 0 (and the a they use on that site, is actually b in the general formula).

So, maybe I’m still missing something from your request, but I think you should be able to calculate all intermediate points as well for your transformation.

@Reptorian
Would this be something in the right direction?
image

1 Like

After spending some more time with the spiral, I think the conclusion is that you will need some numerical solver to find the end points of the perpendicular line segments.

If we assume a simple spiral given by r = b\ \theta, then its parametric representation is given by:

\begin{cases} x(\theta) = b\ \theta \cos \theta\\ y(\theta) = b\ \theta \sin \theta \end{cases}

The perpendicular line at a point \theta_0 is described another parametric equation:

\begin{cases} x(\theta,\theta_0) = b\ (\theta_0 \cos \theta_0 + (\sin \theta_0 + \theta_0 \cos \theta_0) (\theta - \theta_0))\\ y(\theta,\theta_0) = b\ (\theta_0 \sin\theta_0 - (\cos\theta_0 - \theta_0 \sin \theta_0) (\theta - \theta_0)) \end{cases}

The endpoint of the perpendicular lies on the spiral at some value \theta_1, so we can equate the two to find intersections:

b\ (\theta_0 \cos \theta_0 + (\sin \theta_0 + \theta_0 \cos \theta_0) (\theta - \theta_0)) = b\ \theta_1 \cos \theta_1\\ b\ (\theta_0 \sin\theta_0 - (\cos\theta_0 - \theta_0 \sin \theta_0) (\theta - \theta_0)) = b\ \theta_1 \sin \theta_1

We see that the solution is independent of b, which is expected, since it’s just a scaling factor. We are interested in finding \theta_1 for a given \theta_0. By rearranging and rewriting the first part, we get an expression for \theta that can be substituted into the second part to be simplified further.

\theta = \theta_0 + \frac{\theta_1 \cos\theta_1 - \theta_0 \cos\theta_0}{\theta_0 \cos \theta_0 + \sin\theta_0}

After inserting this into the second part and rewriting, we get:

\frac{\theta_0-\theta_1 \cos(\theta_0-\theta_1)+\theta_0\theta_1\sin(\theta_0-\theta_1)}{\theta_0\cos\theta_0+\sin\theta_0} =0

which is true if the numerator is zero

\theta_0-\theta_1 \cos(\theta_0-\theta_1)+\theta_0\theta_1\sin(\theta_0-\theta_1) =0

I don’t know of a way to express this equation in a closed form to find values for \theta_1. However, due to the way the spiral is constructed, I believe we can set the following bounds:

\theta_0 + \pi < \theta_1 < \theta_0 + 2\pi

Some numerical solver should be fairly quick in finding suitable values for \theta_1. We can then use the formula for \theta we found earlier to determine until which value we need to draw the parametric perpendicular line.

This gives me these results:
image

2 Likes

That’s definitely what I want and every lines checks out. What I don’t get is how to get value using those information in context of either cartesian x,y coordinate or polar coordinates system, but I’m new to spiral-related stuff and polar coordinates. If you imagine all of those lines as a gradient ramp, what’s the elevation at x,y point or r, ang point?

log is not merely a math operator, you need to assess what it physically means for your application to assert how you will deal with outliers.

An RGB pixel encodes a light emission, which is energy, which can’t be zero, let alone negative. In any imaging app, zeros and negatives are made up by a technical offset meant to normalize signal between 0 and 1. But that’s only an encoding.

Personnaly, I just decode it. In darktable, I know rawspeed will prepare a black offset such that it ends at 0. So I add -12 EV early in the pipe, as a default in exposure module, to raise values and avoid zeros. Then, I know my image is encoded between -12 and 0 EV. At the end, I know 8 bits uint sRGB encodes the image between -12.67 EV and 0 EV, and everything below -12.67 EV gets rounded to zero all the same.

In other parts of the code, I add -12 or -16 EV, clip everything below that threshold to the threshold, apply log, and when I undo the log to go back to linear, I subtract back the threshold.

Point being, you need to know what “0” means in the context of what you are doing before deciding what you will do with it.

Thanks everyone for helping me think through the problem. I have all this knowledge in my mind but due to my disabilities and weak constitution I often can’t access it at will or make the necessary connections.

Could you remind me why we are working with negative EVs? What do these values represent? In my mind, [0,8]=log2([1,256]).

log(1) = 0, so that’s your display-referred white. Every intensity x below 1 will get log(x) < 0, so everything below white gets a negative EV.

But it’s not very important, it’s just a matter of reference. The common reference is display-referred white, that’s usually our 100% luminance and our 0 EV. But it could be anything else depending on context.

So it seems you are assuming that the domain of the log2() function coming from an input device is restricted to [1,256]. This is a wrong assumption:

  • The values from a camera are generally 12 or 14-bit, and in a raw editor they are generally mapped onto a floating point number in interval (0.0, 1.0].
  • The values direct from camera need to be white-balanced, which means some values can be multiplied, which can result in values >1.0.
  • There may be an exposure correction, which can end up scaling the values up even further. So in a true linear scene-referred pipeline, the individual channels will be floating point numbers >0.0, but the maximum value depends on the processing steps that have happened in the meantime.
  • At some point you are going to want to transform into another color space (eg. Linear Rec.2020), which will result in the numbers shifting around, possibly even resulting in negative values if some of the colours in the original image are out-of-gamut in the new colour space.

So, the input to the log2 function will be a floating point number, generally on the interval (0.0, 1.0) but not necessarily, and of course for 0.0 < x < 1.0, then log2(x) will be negative, and for x > 1.0, then log2(x) will be positive. The EV values are relative to when x=1.0, but that reference point is completely artibratry in a linear scene-referred pipeline, and so EV=0 doesn’t hold any special meaning.

My thoughts were in disarray. I think it is clearer now. So

One

[       -12,0]=log2(1/[1<<12,1]) # 12 bits? Perhaps, but consider it more as
[0.00024414,1]=     1/[1<<12,1]  # where ≤0.00024414 is essentially the epsilon

I could refer to the suggestions made above to deal with or avoid spurious values.

Two

The range doesn’t matter as long as I treat values the same way and adhere to a reference point. The lower bound could be clipped to deal with one. I am unsure about >1 but I suppose that it would involve remapping or compression to bring the image to an 8 bit encoded image.

Outside of colour maths, what I usually do (in engineering computing) if I know that R is not negative, is to compute M = log( (G+eps) / (R+eps) ) – where eps is the machine epsilon (i.e. the smallest number it can deal with that is not zero).
An alternative would be to use M = log( G / (R+eps) + eps ) – but that would not result in 1 if both G and R were zero.

The above has the advantage of working also in “blind” cases, i.e. a spreadsheet, matrix operations (where G and R are large arrays with lots of values) or other environments where you might not want to include any conditionals, but the disadvantage of introducing an error (the tiniest computationally possible error!) to the result.

Although that’s a tiny error, you might be particularly worried about continuity in some cases, but not that worried about execution time. In this case, I’d firstcheck if either B or G is zero, then:

  • if both are zero: M = 1
  • elif R is zero: return largest positive value which can be represented with the variable type that’s being used
  • elif G is zero: return largest negative value
  • elif neither is zero: return log(G/R)

This means that if you drew a graph of M as a function of either G or R, it would be nicely continuous but “clip” at the edge of the range that can be represented. In particular, if you plotted a graph where G = R = f(t), and f(t) becomes zero at some point, you will always get M=1.0.
Mind, however, that this will not work if 2 * G = R = f(t) … that will produce M=2.0, then jump to 1 and back again – but that kind of case is impossible to get right unless G(t) and R(t) are known functions, and you’re able to do calculus to them. Even then it’s tricky, and I don’t know if you could solve it automatically.

I am not sure I follow. What is f(t) and this expression 2 * G = R 0 f(t) ….

whoops, typo! The expression was meant to be
2 * G = R = f(t)

What I meant is: If you have a gradient where G=R, and both go to zero, then my method will give you constant M=0'. But if you have another gradient where 2G=R(that is: R is twice as large as G, soG/R = 0.5, and they follow some gradient (symbolized by f(t), maybe f(x)` would look more familiar?), then G/R would also be constant and they would reach zero at the same time, but the result you’re getting for M would jump from log(2) to 0 at the location where they reach zero.

All that said, after reading through the other replies: If you’re dealing with RAW photos, then zero should not occur in the input data, and if it does, then adding an assumed (very small) black value to all inputs at the start should be the safest thing to do.

Why shouldn’t it? The example at Can you fix a heavy banding problem in my exposure? - #30 by snibgo has many pixel values that are zero.

Because the dark current is not supposed to be 0.

But in a low-light or high-contrast situation, the arithmetic of the light energy might mean that the recorded value should be 0.1 on a scale of 0 to 16383. Cameras don’t record floating point, so the camera rounds that 0.1 to zero. Sure, the camera could set a floor so zero is never recorded, but that would be less accurate.

And the fact is that cameras do record zeros. We might complain that they shouldn’t, but our software needs to deal with them.

I think you’re forgetting about the sensor’s noise floor. Prior to black point subtraction there will never be any pixels at 0/16383 (other than dead pixels). On my A7III the native black point is 512/16383 and the lowest any channel ever dips to is about 480/16383. Of course after black point subtraction any pixels below 512 will be zero.

1 Like

Thanks, @NateWeatherly.

When a value in your A7III dips below 512, I suppose that is because of noise generated by the camera. Is that correct?

True, some cameras don’t record values that are proportional to the light energy. Instead, they record values that are proportional to the light energy plus some constant. To get a proportional value (which we might need for arithmetic, so we can accurately add light values etc) we must first subtract the constant.

But some cameras, such as Nikons and whatever was used in the thread I referenced, use a constant of zero. Thus when hardly any light hits the sensor, they record zero.

Makes sense – but to come back to the original topic: In that case you should add some very small number to the whole data to remove any such occurrences, to make the data conform to the assumptions implemented in the equation, or at least deviate less from them.

Since adding some constant to each of the colour channels will affect the ratios of values between each channel, it should then also be clear that the choice of black level affects the hue in the darkest parts of the image. Thus, subtracting black level before raw processing has that kind of effect, too.

And that’s how I just worked out why subtracting raw black levels early in the process is not a smart thing to do, although I’d thought we were just talking about algebra here :slight_smile: