G'MIC math evaluator now deals with vectors (and matrices, and custom functions!)

It does not work with large images. Error shows up after 2^24th pixel But, it’s a easier solution to do forward and reverse transform at once.

Reminds me about using single precision float insteat of double precision float for accumulating single precision float values (just a thought). The accumulation may saturate at a point…

Yes seems so (I think David mentioned it in another thread), but there should still be a way around it. I expect directly calculating 2D coordinates would solve the problem (spiralbw uses 1D indexes). Somebody just needs to work out the maths (tempting…) :slight_smile:

On the math part, the first part of the curves going up to down looks like this - min(max(slope1,slope2),max(slope3,slope4)). slope1, and slope2 are positive slope while the others are negative. So, if one could figure out generate 1d slope generated by two variables being width and height, you solve the problem. But this is just a idea.

What reminded me about the 2^24 stuff is the following:

Let’s make this loop over a 30 MP image where all pixels are equal:

float sum = 0.f;  // single precision float
for (i = 0; i < numberOfPixels; ++i) {
    sum += image[i];
}

If all pixels are equal, the accumulation will saturate at ~16 Mp.

Ingo
1 Like

Run out of time to finish/tidy this tonight, but here’s a proof of concept:

gcd_spiralbw :
sh. 0
eval. "
begin(P=[0,0];M=0;LX=w-1;LY=h-1;D=[1,0];R=[0,-1,1,0];sx=0;sy=0;xl=w-1);
if(
  (P[0]==LX && (P[1]==M || P[1]==LY))
  || (P[0]==M && P[1]==LY)
  , D=R*D);
if((P[0]==M && P[1]==M+1), D=R*D;M++;LX--;LY--);
P+=D;
sx++;
I(#-2,P[0],P[1])=[sx,sy,0];
if(sx==xl,sx=0;sy++);
" rm.

# example use (second warp is reverse):
gmic sp rose +f 0 gcd_spiralbw. warp.. . warp.. .,2
1 Like

image

That looks like a gui message (probably an image with opacity channel?), so I’m not surprised - the code is at “scratchpad” level. Somebody needs to work it to a better state for gui filter :slight_smile: . Sorry, don’t have time for at least a day now!

@garagecoder, you are truly awesome.
I’ve been itching to say it for a while, but I have to admit that your different G’MIC scripts are always very well done. You have no idea how much I love noise_poissondisk :heart_eyes::+1:
hugs!

1 Like

Also, some more info about the 2^{24} = 4096\times4096 limit.

The limit observed by @Reptorian was due to the fact he was using dynamic arrays, where the number of elements is stored as a float value (because it is stored directly as an image value, and images are float-valued in G’MIC).
As a float is 32bits and has “only” 24 bits of signficant digits (see Single-precision floating-point format - Wikipedia), this means it can store accurately integers (so the size of the dynamic array) only up to 2^{24}.

The fact is that such dynamic arrays have not been designed to store this amount of elements.
But there are plenty other solutions to deal with that issue.
If you stay inside the math evaluator (which works only with double, so with 53 bits of significant digits, Double-precision floating-point format - Wikipedia), you can manage integers up to 2^{53} = 67108864\times134217728 which becomes more than enough for 2d image resolutions :slight_smile:

This means, for instance, the code @garagecoder has proposed can be simplified a little bit, using an offset off rather than coordinates sx and sy:

gcd_spiralbw :
  sh. 0
  eval. "
begin(P=[0,0];M=0;LX=w-1;LY=h-1;D=[1,0];R=[0,-1,1,0];off=0);
if(
  (P[0]==LX && (P[1]==M || P[1]==LY)) || (P[0]==M && P[1]==LY)
  , D=R*D);
if((P[0]==M && P[1]==M+1), D=R*D;M++;LX--;LY--);
P+=D;

I(#-2,P[0],P[1])=[off%w,int(off/w),0];
++off;
"
  rm.

while being still valid for images up to resolution 67108864\times134217728.

Another solution would be to create variants of dynamic arrays in the math evaluator, with the array size coded in a variable rather than a pixel value. Each image dimension in G’MIC is coded as an unsigned int, so virtually up to 2^{32}.

1 Like

I can correct the vector size by forcing the input to have two channels and then getting rid of the 0 in the affected vector.

gcd_spiralbw_warp :
to_graya.
sh. 0
eval. "
begin(P=[0,0];M=0;LX=w-1;LY=h-1;D=[1,0];R=[0,-1,1,0];off=0);
if(
(P[0]==LX && (P[1]==M || P[1]==LY)) || (P[0]==M && P[1]==LY), D=R*D);
if((P[0]==M && P[1]==M+1), D=R*D;M++;LX--;LY--);
P+=D;
I(#-2,P[0],P[1])=[off%w,int(off/w)];
++off;
"
rm.

There are still a few issues which can be seen using this:

gcd_spiralbw_warp
f "[i0+i1*w,0]" to_gray
n 0,255

There is always a white dot towards the top left when I run it using the GIMP plugin (I haven’t tested the CLI version). For some image dimensions there are also line artefacts near the centre. It might be easier to figure out what’s happening by commenting out the second line. It looks like some sort of overshoot to me.

This made my day, thankyou! :blush:
G’MIC just seems to fit how I think extremely well. It’s nice the method is still on topic too, using vectors and matrices (but that’s coincidence really).

@Joan_Rake1 thanks for the testing, I’ll have a look at that when I can (I promised to look at other things and got distracted with this already!)

1 Like

Is it possible to convert a vector (e.g. after solving an equation) to a GRAYscale image?
[1,2,1,2,4,2,1,2,1] → (1,2,1,2,4,2,1,2,1)
It will help me visualize the results.

I need something like:
M=[1,2,1,2,4,2,1,2,1] # square matrix as a result of a computation
??? # Conversion to vector/matrix in the RGBA form, Mg=[1,1,1,0;2,2,2,0;1,1,1,0;…]
input[-1] {$Mg} # create an image from the vector Mg

Thanks for the help.

Do you mean from within the math evaluator? If so:

gmic eval "M=[1,2,1,2,4,2,1,2,1];ext('(',vtos(M),')')"

Thanks. It is partially OK, but I need a square image 3x3, not an one row of 9 pixels.

A few ways to do that using resize with memory content mode (the -1):

# if size is known in advance
gmic eval "M=[1,2,1,2,4,2,1,2,1];ext('(',vtos(M),') r. 3,3,1,1,-1');"

# or outside the evaluator
gmic eval "M=[1,2,1,2,4,2,1,2,1];ext('(',vtos(M),')')"  r. 3,3,1,1,-1

# if size must be calculated
gmic eval "M=[1,2,1,2,4,2,1,2,1];W=sqrt(size(M));ext('(',vtos(M),') r. ',vtos([W,W]),',1,1,-1');"

And so on. Some other commands if you need to re-order afterwards: transpose unroll permute

Edit: and I forgot to say hello - welcome to the forum (and to G’MIC) @helour !

1 Like

Thank you for welcoming :slight_smile:

I’ve created this:

  _M=[1,2,1,2,4,2,1,2,1]
  ...
  size={sqrt(size($_M))}
  {ext('(',vtos($_M),')')} #  <- How to properly insert image from expression "(1,2,1,2,4,2,1,2,1)"?
  resize[-1] $size,$size,1,1,-1

but the error has appeared:

[gmic]-1.// Set global variable '_M=[1,2,1,2,4,2,1,2,1]'.
[gmic]-1.// Set local variable 'size=3'.
[gmic]-1.//*ext/ Input image at position 1, with values (1,2,1,2,4,2,1,2,1) (1 image 9x1x1x1).
[gmic]-2.// *** Error *** Command 'input': Invalid argument 'nan'.

In the G’MIC reference manual, under “Input Data Items”, https://gmic.eu/reference.shtml#section5, there appears this item:

(v1,v2,…): Insert a new image from specified prescribed values. Value separator inside parentheses can be , (column separator), ; (row separator), / (slice separator) or ^ (channel separator). For instance, expression (1,2,3;4,5,6;7,8,9) creates a 3x3 matrix (scalar image), with values running from 1 to 9.

1 Like

I’m wondering if a broader view would help, please forgive statements about which you already know.

There are primarily two ways to use matrix algebra in g’mic. First, you can treat an image as a matrix and use the various “native” commands with that (here using run to avoid command line escaping issues):

gmic run "(1,2,0;3,1,0;4,0,1) invert transpose (1;2;3) mmul"

That way you immediately get to visualise the output as an image. The second way is within the “math evaluator”, which is in a way a language within the g’mic language:

gmic eval "M=[1,2,0,3,1,0,4,0,1];M=inv(M);M=transp(M,3);R=M*[1,2,3];echo(vtos(R))"

Mixing the two can get a little tricky if you aren’t very familiar with g’mic string/macro handling. Anyway, if you really do want to mix both methods this is how you would:

test_matrix :
  M=[1,2,1,2,4,2,1,2,1] # an unquoted string, not a vector
  size={sqrt(size($M))} # the string $M is now evaluated as a vector
  ({$M}) # string -> vector -> comma sep. values -> input an image
  resize[-1] $size,$size,1,1,-1 # resize it
2 Likes
({$M})

Big Thanks. That’s exactly what I need ( string → vector → comma sep. values ). I want/need to separate the mathematical calculation(s) from “image processing”.

1 Like