abscut(): A new magic math function :)

Just a FYI post…
For next version 3.5.1 of G’MIC, I’ve implemented a new math function abscut() (and its associated native command abscut), and I’d like to say a word of two about it here.

1. How abscut() is defined?

It’s actually a simple math function that takes 4 arguments, and compute:

\text{abscut}(x,m,M,o) = \text{sign}(x)\;\text{cut}(|x| + o,m,M)

where

\text{sign}(x) = \left\{\begin{array}{lcr}-1 & \text{if} & x<0\\0 & \text{if} & x=0\\1 & \text{if} & x>0\end{array}\right.
and
\text{cut}(x,m,M) = \left\{\begin{array}{lcr}m & \text{if} & x<=m\\x & \text{if} & m<x<M\\M & \text{if} & x>=M\end{array}\right.

It is intended to “do something on x (details in next section :slight_smile: ).

In practice, this mathematical function can be used directly in the G’MIC math evaluator (e.g. in conjunction with commands fill or eval, like in fill "abscut(i,0,1,0)"), or with the new pipeline command abscut min,max,offset.

2. What it does?

Not sure you’ll understand the interest of abscut directly from the equations (I wouldn’t have!), so basically what abscut does to a value x is:

  • It keeps the sign of x unchanged.
  • It clamps and/or shifts its absolute value |x|.

That’s all: A pretty cool function to control the range of the “amplitude” of a value, without having to care about its sign.

So to sum up :

  • m is the minimum absolute value allowed for \text{abscut}(x).
  • M is the maximal absolute value allowed for \text{abscut}(x).
  • o is an offset applied to the absolute value of x.

3. Let me see that!

I illustrate the use of abscut with a toy example. We start from a sinusoidal 1D signal:

$ gmic 1024,1,1,1,"X=cos(20*x/w)" plot

This signals ranges from -1 to 1, and intersects the x-axis.

Now, you can easily apply a maximal threshold on the absolute value of this signal, so to saturate the extremum values of the sine:

$ gmic 1024,1,1,1,"X=cos(20*x/w);abscut(X,0,0.7)" plot

which would be equivalenty obtained using:

$ gmic 1024,1,1,1,"X=cos(20*x/w);X<-0.7?-0.7:X>0.7?0.7:X" plot

(but see how much complex is the latter :slight_smile: ).

Or you can do the opposite: You can also use abscut to impose a minimal absolute value, like this:

$ gmic 1024,1,1,1,"X=cos(20*x/w);abscut(X,0.25,inf)" plot

And in this case, your signal is either strictly higher than 0.25 or lower than -0.25.

Of course, you can mix the two, to have both a lower and a higher threshold:

$ gmic 1024,1,1,1,"X=cos(20*x/w);abscut(X,0.25,0.75)" plot

In the examples above, I’ve only use the m and M arguments of abscut().
Finally, let’s illustrate the role of the offset o, first with a positive offset:

$ gmic 1024,1,1,1,"X=cos(20*x/w);abscut(X,0,inf,1)" plot

which is basically equivalent to:

$ gmic 1024,1,1,1,"X=cos(20*x/w);X<0?X-1:X+1" plot

But you can also use a negative offset:

$ gmic 1024,1,1,1,"X=cos(20*x/w);abscut(X,0,inf,-0.5)" plot

And in this case, it is equivalent to:

$ gmic 1024,1,1,1,"X=cos(20*x/w);X>0?max(0,X - 0.5):min(0,X + 0.5)" plot

I like this toy example of a simple sinusoidal signal, because it clearly shows the different behaviors of the abscut() function. And it shows that the different equivalent formulations (that do not use abscut()) look very different.

3. But, in which situation does I need this?

Personally, I’ve implemented this function because I needed it for the development of my nn_lib: Often, I had to deal with the usual numerical trick to avoid “division by zero” cases.
Basically, when you have to compute a fraction a/b , you have to be sure that b is non zero (and even not too close from zero), to avoid a/b returning inf or very large values that would lead to numerical instabilities.
The classical trick is to write a/(eps + b) or a/max(eps,b) where eps is a small value, like 1e^-8. But that works only when b is >=0.
Now, I’m able to write a/abscut(b,1e-8) or a/abscut(b,0,inf,1e-8) and I’m good.

But I’m pretty sure there are lot of other use cases where abscut() can help.
If you think of one in particular, don’t hesitate to share your thoughts :slight_smile:

4 Likes

I can envision a new border filter with this.

That’s a pretty good explanation, even I can understand most of it. I think. Maybe.

Maybe this can be used to avoid drawing outside an image when using a loop or something like that?

You can use modulo for that. That is how my DLA filter works.

I wouldn’t know how to use modulo in this case …
Anyway now that I think about it, abscut seems to work like a compressor or a limiter ( for guitar, etc)

I forgot to detail this, but there is one case where abscut(x,m,M,o) can change the sign of x: It’s when m=-inf and o is negative.
Like in this example:

$ gmic 1024,1,1,1,"X=cos(20*x/w);abscut(X,-inf,inf,-0.5)" plot

or with an even more low offset:

$ gmic 1024,1,1,1,"X=cos(20*x/w);abscut(X,-inf,inf,-2)" plot

The last example, with the original signal (in blue), and the abscut() result (in green).
Signs are inverted:

Not sure if this has any interest though.

  • By the way, the sign of x will always be preserved if you choose m>=0 (which is the intended case).
1 Like

Nice addition. If you could add more of these examples to the reference page (or @grosgood could add a guide) and up the image resolution of the graph(s) slightly, that would be great.