# Math evaluator improvements planed for G'MIC 2.9.3

Beware, that’s a quite “technical” post about G’MIC syntax!

I’d want to describe some advances I’ve made on the math evaluator of G’MIC, for next version 2.9.3.
Tonight, I’ve basically added two new (hopefully useful) functions:

1. `expr('expression',_w,_h,_d,_s)` returns a vector of size `w*h*d*s` whose values are computed just as if one were filling an image of size `(w,h,d,s)` with the specified `expression` (e.g. with command `fill`). That’s useful, if you want to initialize vectors with expressions, e.g you can write:
``````foo_293 :
eval "
V = expr('x + y',3,3);
print(V);
"
``````

in remplacement of

``````foo_292 :
eval "
V = vector9();
ind = 0;
for (y = 0, y<3, ++y,
for (x = 0, x<3, ++x,
V[ind++] = x + y;
)
)
print(V);
"
``````

``````\$ gmic foo_293
[gmic]-0./ Start G'MIC interpreter.
[gmic_math_parser] V = [ 0,1,2,1,2,3,2,3,4 ] (size: 9)
[gmic]-0./ End G'MIC interpreter.
``````

So, a quite useful function to initialize of fill vectors without having to write explicit loops for doing it.

1. `get('varname',_size)` returns a vector of size `size` whose values come from the content of the G’MIC named variable `varname`. If `size==0` (default), then a scalar is returned.
Example:
``````foo :
var1=1976
var2=10,20,30,40
sp lena,512 store. var3
eval "
v1 = get('var1');
print(v1);
v2 = get('var2',4);
print(v2);
v3 = get('var3',512*512*3);
print(v3);
"
``````

``````\$ ./gmic foo
[gmic]-0./ Start G'MIC interpreter.
[gmic_math_parser] v1 = 1976
[gmic_math_parser] v2 = [ 10,20,30,40 ] (size: 4)
[gmic_math_parser] v3 = [ 225,225,223,223,225,225,225,223,225,223,223,223,223,225,223,223,223,223,225,223,223,224,224,223,223,223,223,224,223,223,223,223,224,223,224,223,223,225,228,234,234,235,234,234,234,236,237,234,237,237,234,236,234,234,231,231,228,223,223,212,204,206,185,173,...,78,89,78,77,79,93,89,104,89,89,89,89,77,93,79,77,79,89,91,80,88,96,108,108,110,108,119,119,119,118,108,118,107,91,104,77,70,65,69,69,70,64,61,61,56,56,56,55,55,61,63,63,78,78,78,77,91,80,79,89,77,79,79,82 ] (size: 786432)
[gmic]-0./ End G'MIC interpreter.
``````

As you see, even image-encoded variables (with command or function `store`) are managed correctly. This way, it’s now possible to pass a vector-valued variable encoding an image, into a G’MIC pipeline, modify it within the pipeline, and get it back in the math evaluator, like this:

``````foo :
sp portrait1,512
eval "
V = crop(200,200,128,128);           # Get a 128x128 color patch
store(V,'V',128,128,1,3);            # Store it as an image-encoded variable 'V'
run('\$V rotate. 90 b. 3 store. V');  # Modify it in a G'MIC pipeline
W = get('V',size(V));                # Get the modified patch as a new vector 'W'
draw(W,200,200,0,0,128,128,1,3);     # Draw 'W' back on the image.
"
``````

which leads to the following result:

Here again, this opens new possibilities to pass data from/to a `run()` call.

Well that’s all for now, 11:24pm, time to go to bed

2 Likes

Hmm, I think I will use that in my latest work. It is already possible to do expr with images, and access with i(#…). For some reason, I am thinking this can be used to speed up my diffusion limited aggregation filter. You’d note that I am using a do while loop to create a dynamic array from existing image, and a do while loop to move coordinates by 1, as well as deleting array based on condition. So, that is pretty slow. I hope I’m not wrong about that.

I’ve made some improvements this morning, and everything looks good so far.
I’m currently building binary packages for Windows, Ubuntu 20.04 and 19.10. It should be available in the ‘pre-release’ download page in one hour or so (check the date) : Index of /files/prerelease

Would this be appropriate for a moving window? That would mean using `store()` and `run()` as many times as there are pixels on their neighbourhoods. E.g. I have

``````ref(crop(x-R,y-R,0,c,D,D,1,1),N)
``````

Yes, this means you can use it to apply a G’MIC pipeline to a moving window, just like this:

``````foo :
sp colorful,128
f "
const boundary = 1;
V = crop(x-5,y-5,0,c,11,11,1,1);
store(V,'V',11,11);
run('\$V n. 0,255 store. V');
V = get('V',11*11);
V[size(V)/2];
"
``````

Beware, the `run()` function is computationally intensive (it runs a complete G’MIC interpreter), so don’t expect such loops to be efficient.

That was what I wanted to know. I guess the proper way would be to have G’MIC and math interpreter versions of everything.

It always better to find a solution that doesn’t involve run() on each pixels. I do have to ask if it can be avoided at all costs?

I must ask: what is the best approach? `{}`, `fill` and `eval` all use the math interpreter, right? Right now, I tend to use the G’MIC intepreter (less precision but faster) and the other 3 when convenient. Is it expensive to constantly be switching among them? I take the convenient approach because I am not fluent in coding or math (i.e. I am not quick or natural at them).

I use fill if the filter utilize the traditional `for x, for y, for z, i(x,y,z)=result;` and if the target breaks away from traditional, then use eval. You can use fill under eval. However, if you are looping run fill per pixel, it will be very expensive. The reverse is naturally expensive.

That’s an interesting question indeed.
What I think is that when possible, it’s better to use “classical” pipelined commands when performing image processing operations, particularly if no explicit image loops are involved (and if you can use mostly native commands in your pipeline, then it’s even better). If you think about writing nested `repeat...done` loops for analyzing or rendering an image, then it’s probably you have to switch to the math evaluator, through a math expression to a `fill` or `eval` command.

``````foo1 :
1024,1024
repeat h,y
repeat w,x
=. {u(30,255)},\$x,\$y
done
done
``````

the math expression:

``````foo2 :
1024,1024
fill "u(30,255)"
``````

is better.

The math evaluator has been specifically designed for this use case : Sometimes you really need to do very specific pixel-to-pixel operations, which are not easily done with existing pipelined commands. In this case, the use of `fill` and `eval` is justified. You can see them as a bit like opengl shaders for doing custom local operations. But don’t forget that a mathematical expression has first to be compiled as a sequence of “bytecodes” before being evaluated by `fill` or `eval`, and this step induces additional computation time.
So conversely, using `fill`s or `eval`s to do things that can be done easily by commands is not a good idea either. So, for instance, the example above is finally better with:

``````foo3 :
1024,1024 rand. 30,255
``````

(`rand` is a native command in G’MIC!).

Moreover if we look at the timings of these 3 `foo` commands, which generate the kind of random image, we see that :

``````\$ gmic tic foo1 toc tic foo2 toc tic foo3 toc
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Initialize timer.
[gmic]-1./ Elapsed time: 11.273 s.
[gmic]-1./ Initialize timer.
[gmic]-2./ Elapsed time: 0.014 s.
[gmic]-2./ Initialize timer.
[gmic]-3./ Elapsed time: 0.004 s.
[gmic]-3./ Display images [0,1,2] = '[unnamed], [unnamed], [unnamed]'.
...
``````

and it is clear `foo3` is better than `foo2`.

Of course, the algorithms you want to write in practice are not as simple to analyze, and sometimes you find yourself in a “gray area” where you want to use the best of both worlds (pipelined commands and math evaluator). I don’t think there are general rules for deciding what’s the best approach then. Only experience, and `tic``toc` commands to help understanding what the best approach to use.

I tend to choose one of this two approach by defining first what I expect from my command, in terms of speed and flexibility. When you write a new command, you first constrain yourself to make it work before adding new options to make it more flexible. It is important to keep the goal in mind to be sure you won’t get stuck in the approach that won’t allow you to add the flexibility you want afterwards. Knowing exactly what you expect from your new command (and what you won’t expect) is a great helper to choose between one of the two approaches.

One more tip to add in addition to what @David_Tschumperle said: Do not be afraid to revisit your filters. You may find a faster solution. My example is fragment blur. The first version was unbearably slow. A rewrite made it over 5000 times faster.

This year has been figuring out this aspect of scripting. Indeed, there is a grey area. E.g. I discovered so many ways to do `rgb2jzazbz` but none of them ended up as efficient as @snibgo’s which you adopted.

Could you elaborate on your example?

Putting it to words, are you
– storing `V` as a vector of sets of 121 pixels;
– normalizing each set; and
– then averaging the first half of values of each set;
– where `get()` is like accessing the original neighbourhoods (now normalized)?

How can I use `display()` to show the whole image between the steps (not just individual sets of 121)?

LOL, all of my rewrites add more time because they are more complex. (And I often doubt they are better.)

I tried expr as I finally saw the use for it, I’m going to assume it haven’t arrived yet or I need to update gmic besides doing gmic up.

EDIT: Yep, had to delete the old gmic programs, and updated from gmic.eu. That fixes it.

• `V` is a variable that stores a 11x11x1x1 image (i.e. an image patch).
• In the call to `run()` , the patch is inserted at the end of the image list, then normalized, and stored as a variable with the same name `V`.
• The call to `get()` allows to get back the content of the modified patch in the math parser, as a `vector121`.
• Finally, the middle value of this vector is returned in the expression, so that `fill` set its as the new value in the image, at position `(x,y,0,c)`.
• For this, `display(#0)` can be used, but in the case of this `fill` you probably won’t see the image `[0]` updating, because the expression contains a call to `crop()`, which is detected by the math expression compiler, and prevent the image `[0]` to be modified ‘in-place’. Instead, the result of the `fill` is first computed as a new temporary image (that cannot be accessed inside the math expression), which replaces image `[0]` only when all the pixels have been processed.

I ended up using expr successfully. What I really like about it is that I don’t have to use a constant.

Chirikov-Taylor Standard Map
``````rep_chirikov_taylor_map:
skip \${2=5000},\${3=1.52},\${4=100},\${5=.25}
#\$1==size#
#\$2==loop#
#\$3==k#
#\$4==lines#
\$1,\$1,1,1
eval \${-math_lib}"
const pts=\$2;
const k=\$3;
const dpi=2*pi;
const lines=\$4;
fmod(a)=a-dpi*floor(a/dpi);
xi=0;
iy=expr('x/w*2*pi',lines,1);
for(n=0,n<lines,n++,
yi=iy[n];
for(ptn=0,ptn<pts,ptn++,
yi=fmod(yi+k*sin(xi));
xi=fmod(xi+yi);
i(#-1,round(xi/dpi*w#-1),round(yi/dpi*h#-1))=n;
);
);
"
``````

What I don’t like is having to use 2*pi instead of the constant dpi.

EDIT: More beautiful image using the command.

1 Like

That’s because the math expression provided to `expr()` is something totally independent on the current math expression being written (in a `fill` or `eval` command). Function `expr()` just instantiate a new math parser.

Actually, I’m not sure I’d like to have const variables shared between the two math parsers.
I imagine something as:

``````fill "
const x = 1976;
const y = 3210;
V = expr('x + y',3,3);
"
``````

could lead to V being a `vector9()` with the same value `5186` everywhere, rather than the expected vector `[ 0,1,2,1,2,3,2,3,4 ]`.

What if you could put constants outside of ‘something’?

That’s exactly what I discussed above. This is not desired.

@David_Tschumperle I would like to let you know that filters that utilizes expr and get will have a error on G’MIC-QT 2.9.2. Right now, PDN users will have to not touch these filters meanwhile.