G'MIC exercises

Doesn’t that just evaluate?

Yes, and I would love to have a one-liner solution that’s short. Right now, it seems I would have to use $=argpos and set for this one which is not what I want.

This won’t work either:

$=arg_pos
(${arg_pos{6-$end_reference_palette_arg}})

Even this won’t work:

rep_test_2:
a=2 b=4
test_command=(\$\{$a-$b\})
echo $test_command
m "test_command:"$test_command
test_command
um test_command

EDIT: Found it how to do it. It’s not my fave solution though.

 repeat $end_reference_palette_arg-6 {
  if $>
   reference_palette_arg.=,${arg_pos{$>+6}}
  else
   reference_palette_arg=${arg_pos{$>+6}}
  fi
 }
 
 ($reference_palette_arg)

You could truncate the statement into a one-liner. I am not proficient in string manipulation, but I hope you get the idea.

gmic run "v + repeat 4 a.={`$>?',$>':'$>'`} done e $a"    # 0,1,2,3

I would like to know what these parameter under convolve do…

    Default values: 'boundary_conditions=1', 'is_normalized=0',
     'channel_mode=1', 'xcenter=ycenter=zcenter=(undefined)',
     'xstart=ystart=zstart=0', 'xend=yend=zend=(max-coordinates)',
     'xstride=ystride=zstride=1', 'xdilation=ydilation=zdilation=1' and
     'interpolation_type=0'.

And does convolve really do dilation under the hood? Why?

Also, it seems to have issue assigning default arguments when not specified.

And it seems that many of those inputs are not used. I do plan to to use center arguments though for improving Splinter Blur as it’s very slow now, but not sure how to figure out a version that use 1d convolution.

I bet it is for machine learning (nn), which you have to set up to use.

I would like to know how _random_pattern_expr works. What I’m figuring out is how to create a iterative version of it.

All I know is this, it calls itself and the status reflects upon the result of itself. Shouldn’t it set status to one of the if else inside the termination block, then terminates? That’s the part I simply cannot understand.

Here it is:

_random_pattern_expr : skip "${1=0},${2=0}"
  nl:=$2+1
  if (u<$1" && "$2>1)" || "$2>6 # Termination node
    r:=u
    if $r<0.75 u z
    else u [{_round(u([-1,-1],[1,1]),0.1)}]
    fi
  else # Function or operator
    p1,p2:=[$1,$1]+u([0.2,0.2])
    r:=u(27)
    if $r<1 u ccos(${$0\ $p1,$nl})
    elif $r<2 u csin(${$0\ $p1,$nl})
    elif $r<3 u ctan(${$0\ $p1,$nl})
    elif $r<4 u ccosh(${$0\ $p1,$nl})
    elif $r<5 u csinh(${$0\ $p1,$nl})
    elif $r<6 u ctanh(${$0\ $p1,$nl})
    elif $r<7 u cexp(${$0\ $p1,$nl})
    elif $r<8 u clog(${$0\ $p1,$nl})
    elif $r<9 u [cabs(${$0\ $p1,$nl}),0]
    elif $r<10 u [0,cabs(${$0\ $p1,$nl})]
    elif $r<11 u [carg(${$0\ $p1,$nl}),0]
    elif $r<12 u [0,carg(${$0\ $p1,$nl})]
    elif $r<13 u cconj(${$0\ $p1,$nl})
    elif $r<14 u (${$0\ $p1,$nl})+(${$0\ $p2,$nl})
    elif $r<15 u (${$0\ $p1,$nl})-(${$0\ $p2,$nl})
    elif $r<16 u ${$0\ $p1,$nl}**${$0\ $p2,$nl}
    elif $r<17 u ${$0\ $p1,$nl}//${$0\ $p2,$nl}
    elif $r<18 u (${$0\ $p1,$nl})^^0.5
    elif $r<19 u (${$0\ $p1,$nl})^^2
    elif $r<20 u (${$0\ $p1,$nl})^^3
    elif $r<21 u ${$0\ $p1,$nl}*${$0\ $p2,$nl}
    elif $r<22 u ${$0\ $p1,$nl}/(0.01+cabs(${$0\ $p2,$nl}))
    elif $r<23 u abs(${$0\ $p1,$nl})^0.5
    elif $r<24 u (${$0\ $p1,$nl})^2
    elif $r<25 u [(${$0\ $p1,$nl})[0],0]
    elif $r<26 u [0,(${$0\ $p1,$nl})[1]]
    else u (${$0\ $p1,$nl})^3
    fi
  fi
1 Like

At the end of the day — when it is no longer being induced into calling itself — _random_pattern_expr generates two-statement pel processors that look something like this:

*z=[ 12.119745126021966*((x+0.5)/w-0.5),4.4061004241011332*((y+0.5)/h-0.5)+0.5/h]; ctan([0,cabs([0,carg([0.8,-0.7])])])

or this:

*z=[ 6.2220579400244302*((x+0.5)/w-0.5), 12.107919448080454*((y+0.5)/h-0.5)+ 0.5/h];([((z)^^0.5)[0],0]//ccos(ccos([carg(((z)+(z))^^3),0])))^^3

or this:

*z=[ 12.832560095198582*((x+0.5)/w-0.5), 14.448331737529562*((y+0.5)/h-0.5) + 0.5/h ]; cconj(([0.9,-0.1]//[0,cabs([0,([cabs([0.3,-0.2]),0])[1]])])^2)

or any number of an infinite variety of pel processors that, nonetheless, has a very specific form. To wit, it consists of two expressions:

  1. a complex number, z, that varies as a function in x and y as the pel processor traverses its affiliated image, and:
  2. a nested series of various functions that have various functions as arguments, which, in turn, have various functions as arguments, which, in turn … and eventually bottoms out with a function that actually takes the complex number, z as an argument.

The caller utilizes this two statement pel processor to fill trial images of fantastic random things:

{round([$1,$2]*128/min($1,$2))},1,2,*$expr

where $expr is the pipeline variable containing the pel processor and the size of the image is a function of command line arguments. This trial image is subject to a test based on gradient_norm; if that detects a lot of edges, it produces an image with a high value median, ic, which — if ic exceeds a criterion value furnished by the user (argument 3 of random_pattern) the trial image is deemed “interesting” and the trial image is regenerated as a channel in the final output image. Otherwise, we call _random_pattern_expr again to generate a new pel processor, which it does through a recursive scheme.

Let’s look at that recursive scheme. The function is organized around an outer if…else…fi construct where one branch, (if), terminates recursion by writing an “innermost function” in z, and (else), which allows the recursion to go to the next level. You probably can tumble on to that because the second statement of the pel processor looks suspiciously like nested copies from the various twenty-six functions comprising the selection ladder part of _random_pattern_expr.

When the node level ($nl) is sufficiently low and none of the other semi-random conditions prevail, the processing path takes the else branch and encounters that selection ladder; the selection of any one of the twenty-six “function templates” follows from the particular value of $r, randomly derived from the semi-closed interval [0.75,…27). Any one of these function templates tells the story of all the others.

…
 elif $r<8 u clog(${$0\ $p1,$nl})  
…

The function fragment is clog() — the complex logarithm. It’s argument is ${$0\ $p1,$nl} — the secret sauce. The construct ${…} allows us to invoke a pipeline from a pipeline. That invoked pipeline runs just one command: $0, the $-expression substitution sequence for the calling G’MIC command, in this case _random_pattern_expr — again.

This recursion along the else branch can go through a number of levels. For example, here’s one call stack I harvested whilst debugging:

Call Stack ($/):  ./+random_pattern/*repeat/*do/*substitute/_random_pattern_expr/*if/*if/*substitute/_random_pattern_expr/*if/*if/*substitute/_random_pattern_expr/*if/*if/*substitute/_random_pattern_expr/*if/*if/*substitute/_random_pattern__expr/*if/*if/*substitute/_random_pattern_expr/*if/*if/*substitute/_random_pattern_expr/
Level: 7

That is a case where _random_pattern_expr calls itself seven times. At that juncture, $nl grows too large and forces termination of the recursion. Then, through the status return mechanism (u outside of math expressions), the second statement of the pel processor effectively gets written from the inside out as the calling stack unwinds. As it unwinds, randomly selected functions from the lower levels of the recursion get “written around” the already-written functions of the upper levels, creating nested function calls.

And that, ladies and gents of G’MIC land, is how _random_pattern_expr works. I like it. It’s fiendish. Sort of suggests a new discussion thread: “Gems from the Standard Lib”.

2 Likes

How exactly does one use addr()? Second, what is the point if I can’t take advantage of it?

addr(expr)': return the pointer address to the specified expression 'expr'

addr() is mainly used for debugging purposes.
I would suggest not to use it for doing anything else.

1 Like

What would be the recommended way to multiply a section of a vector by another vector? I know I can’t do this:

V=[.9,5,6,1.3,6,7];
print(V);
(V)[2,3]*=[9,5,-4];
print(V)

And expect it to work

As you noted, (V)[2,3] is strictly a retrieval function; it does not support direct assignment. In the present case, the function directs a retrieval starting at the third item of vector V (zero indexed) and including that and the two follow-on items; the one-item stride is implied. See sixth bullet point Vector Calculus in Mathematical Expressions.
However, (V)[2,3]*[9,5,-4]; stands as a new vector3 and can be assigned.

gmic run 'a={V=[.9,5,6,1.3,6,7];print(V);A=(V)[2,3]*[9,5,-4];print(V);A}'
[gmic]-0./ Start G'MIC interpreter.
[gmic_math_parser] V = (uninitialized) (mem[36]: vector6)
[gmic_math_parser] V = (uninitialized) (mem[36]: vector6)
[gmic_math_parser] V = [ 0.90000000000000002,5,6,1.3,6,7 ] (size: 6)
[gmic_math_parser] V = [ 0.90000000000000002,5,6,1.3,6,7 ] (size: 6)
[gmic]-0./run/__run/ Set local variable 'a=54,6.5,-24'.
[gmic]-0./ End G'MIC interpreter.

Would that move your game forward?

No. Thing is I want to directly apply arithmetic operations without creating a new vector.

So, I am left with repeat, fill, and copy to avoid creating new array. None looks nice in my opinion. Which should be used is the question.

As @grosgood said, V[2,3] is a new vector, constructed by copy, and not a reference to the values of V.
You have then two options to modify a subset of the values of a vector V in-place:

  • Extract the subset of the values, modify them, and copy them back to their initial location:
X = [ 0,1,2,3,4,5,6,7 ];
copy(X[2],X[2,3]*=-1);
# -> X = [ 0,1,-2,-3,-4,5,6,7 ]

or

  • Process each value in-place with an explicit loop:
X = [ 0,1,2,3,4,5,6,7 ];
for (k = 2, k<5, ++k, X[k]*=-1);
# -> X = [ 0,1,-2,-3,-4,5,6,7 ]

The latter does not require additional vector copy, so it may be faster.

So, let:

iplace:
   a={">
        V=[.9,5,6,1.3,6,7];
        Q=[9,5,-4];
        print(V);
        for(k=2,k<5,++k, V[k]*=Q[k-2]);
        print(V)"}
   echo $a

be iplace.gmic. Then:

 $ gmic -command iplace.gmic v + -iplace
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Import commands from file 'iplace.gmic', with debug info (1 new, total: 4620).
[gmic]-0./ Increment verbosity level (set to 2).
[gmic_math_parser] V = (uninitialized) (mem[36]: vector6)
[gmic_math_parser] V = (uninitialized) (mem[36]: vector6)
[gmic_math_parser] V = [ 0.9,5,6,1.3,6,7 ] (size: 6)
[gmic_math_parser] V = [ 0.9,5,54,6.5,-24,7 ] (size: 6)
[gmic]-0./iplace/ Set local variable 'a=0.9,5,54,6.5,-24,7'.
0.9,5,54,6.5,-24,7
[gmic]-0./ End G'MIC interpreter.

I would like to improve rep_sort_images_by_order by allowing users to sort given images name instead. At the moment, you can only sort given integer numbers.

Here’s the problem:

  1. How do I confirm that every images names exist in the user input?
  2. How do I check if duplicate image names exist?
  3. How to deal with empty names in this case?

It’s probably not possible at this time. It requires appending strings to multiple names at once to deal with empty names.

Not sure if this is related, but command sort_list is able to sort a list of images according to their names (lexicographic sort).
Example:

$ gmic repeat 10 { sp } sort_list +,n
[gmic]-0./ Start G'MIC interpreter (v.3.2.7).
[gmic]-1./*repeat/ Input sample image 'chick' (1 image 1024x682x1x3).
[gmic]-1./*repeat/ Input sample image 'gmicky_wilber' (1 image 900x1200x1x3).
[gmic]-1./*repeat/ Input sample image 'car' (1 image 1024x683x1x3).
[gmic]-1./*repeat/ Input sample image 'monalisa' (1 image 700x800x1x3).
[gmic]-1./*repeat/ Input sample image 'waterfall' (1 image 750x500x1x3).
[gmic]-1./*repeat/ Input sample image 'portrait2' (1 image 800x800x1x3).
[gmic]-1./*repeat/ Input sample image 'swan' (1 image 1024x682x1x3).
[gmic]-1./*repeat/ Input sample image 'tiger' (1 image 750x500x1x3).
[gmic]-1./*repeat/ Input sample image 'tulips' (1 image 800x512x1x3).
[gmic]-1./*repeat/ Input sample image 'wall' (1 image 600x500x1x3).
[gmic]-10./ Sort list of images [0,1,2,(...),7,8,9] in ascending order, according to the image criterion 'n'.
[gmic]-11./ Display images [0,1,2,(...),7,8,9] = 'car, chick, gmicky_wilber, (...) tulips, wall, waterfall'.
[0] = 'car':
  size = (1024,683,1,3) [8196 Kio of float32].
  data = (196,196,196,196,192,180,175,180,184,196,200,200, ... ,212,224,224,224,224,224,224,219,200,200,206,225).
  min = 3, max = 253, mean = 141.903, std = 77.74, coords_min = (230,158,0,2), coords_max = (445,98,0,0).
[1] = 'chick':
  size = (1024,682,1,3) [8184 Kio of float32].
  data = (134,134,134,134,134,134,118,134,118,118,118,118, ... ,34,34,34,34,34,34,36,49,48,48,48,48).
  min = 1, max = 247, mean = 142.249, std = 62.1051, coords_min = (616,208,0,2), coords_max = (616,94,0,0).
[2] = 'gmicky_wilber':
  size = (900,1200,1,3) [12.4 Mio of float32].
  data = (255,255,255,255,255,255,255,255,255,255,255,255, ... ,255,255,255,255,255,255,255,255,255,255,255,255).
  min = 0, max = 255, mean = 171.8, std = 96.7139, coords_min = (564,308,0,0), coords_max = (0,0,0,0).
[3] = 'monalisa':
  size = (700,800,1,3) [6562.5 Kio of float32].
  data = (118,98,125,98,118,112,102,118,106,106,112,106, ... ,17,28,35,21,50,30,9,25,31,37,37,24).
  min = 1, max = 255, mean = 87.5862, std = 55.0394, coords_min = (480,122,0,1), coords_max = (205,140,0,0).
[4] = 'portrait2':
  size = (800,800,1,3) [7500 Kio of float32].
  data = (184,189,193,196,196,193,193,193,196,193,192,192, ... ,185,184,184,184,187,184,186,186,186,186,186,181).
  min = 13, max = 236, mean = 162.034, std = 51.0614, coords_min = (477,88,0,0), coords_max = (359,0,0,0).
[5] = 'swan':
  size = (1024,682,1,3) [8184 Kio of float32].
  data = (70,63,71,59,71,63,63,59,71,71,70,70, ... ,67,67,67,70,67,70,76,76,67,55,34,34).
  min = 4, max = 254, mean = 147.325, std = 65.0194, coords_min = (2,9,0,2), coords_max = (348,0,0,0).
[6] = 'tiger':
  size = (750,500,1,3) [4394.5 Kio of float32].
  data = (150,134,134,134,134,134,134,134,151,164,181,192, ... ,58,58,56,54,65,56,65,65,70,65,56,56).
  min = 3, max = 248, mean = 98.0154, std = 48.22, coords_min = (305,74,0,2), coords_max = (707,471,0,0).
[7] = 'tulips':
  size = (800,512,1,3) [4800 Kio of float32].
  data = (200,199,201,209,216,222,226,226,229,233,238,242, ... ,0,0,0,0,0,0,1,0,0,0,1,2).
  min = 0, max = 255, mean = 112.553, std = 74.2938, coords_min = (535,493,0,0), coords_max = (146,0,0,0).
[8] = 'wall':
  size = (600,500,1,3) [3515.6 Kio of float32].
  data = (103,103,105,113,113,113,113,121,122,113,105,113, ... ,162,164,177,172,161,163,150,172,164,155,148,163).
  min = 16, max = 237, mean = 149.176, std = 40.4359, coords_min = (22,0,0,1), coords_max = (456,60,0,0).
[9] = 'waterfall':
  size = (750,500,1,3) [4394.5 Kio of float32].
  data = (40,58,75,57,70,52,48,58,58,39,45,67, ... ,27,22,17,24,24,27,27,27,27,27,28,27).
  min = 2, max = 252, mean = 55.7616, std = 52.8027, coords_min = (746,32,0,0), coords_max = (242,179,0,2).
[gmic]-10./ End G'MIC interpreter.
1 Like

Knowing Rep, he probably wants a super optimized custom method. :stuck_out_tongue:

Something like : rep_sort_images_by_order[0--1] A,C,Z,B,X,T that would sort images in the particular order you want instead of using mv?

It’s only partly related. The difference is that sort_list sorts by lexicographic order, and rep_sort_images_by_order sorts images so that it matches the input order.

It is optimized though a C++ solution would be faster, I just want to extend it.

Yes, something like that. Also, it use mv inside. See the more formatted code:

#@cli rep_sort_images_by_order: number_a,number_b,....
#@cli : Sort images accordingly to numerical arguments. The number of numerical arguments has to be equal to the number of images.
#@cli : $ sample cat,dog,lena,david,house,flower,waterfall,tiger name {expr('x',$!)} rep_sort_images_by_order 5,1,7,0,4,2,3,6
rep_sort_images_by_order:
  if $#!=$! error invalid_image_count fi

  input_v=[{[$*]%$#}]
  sorted_v=[{sort([expr('x',$#)])}]

  if $input_v==$sorted_v return
  elif $input_v==reverse($sorted_v) reverse
  else
     $#,1,1,1

     $#,1,1,1,>"begin(
        const max_index=w-1;
        index_counter=vector(#w,0);
        order=[$*];
      );
      value_index=order[x]%w;
      if(++index_counter[value_index]>1,run('error dup_ind_inp'););
      off_position=value_index-i(#-1,value_index);
      polygon(#-1,2,value_index,0,max_index,0,-1,1);
      off_position+x;
      "

     remove[-2]

     repeat $# {
       move[{i(#-1,$>)}] $>
     }

     remove[-1]

  fi

Hmm, sorted_v doesn’t need sort(), I will fix that.

How do I get this to work?

$  pal 10,50 move[\"Zenit-241 by Zenit40\"] 1

I would like to move images with spaces in its name. This name can be any arbitrary name. And this is why my current script to order images by user input set of names doesn’t work.