G'MIC exercises

Yes, I did because there is no argument at 2. It’s empty there.

Changes:

image_a=${arg\ $mode,\"1~,1~,1~\",,\"0~,1~\"}
image_b=${arg\ $mode,\"1~,1~\",\"0~,1~,1~\",}
image_c=${arg\ $mode,\"0~,1~\",,\"1~,1~,1~\"}

Alternatively, fill in the default values.

That gives me 1~,1~,1~ for first argument in image_a. I’m basing the placement of quotation mark, and output based on the subcommands found in current rep_symmetrize_xy and each subcommands are copies with different parameters set, and I want to combine them.

If it is that tricky, it is too complicated, but you are the script writer. :stuck_out_tongue_closed_eyes: The trouble is you have to ensure that the arguments resolve properly as you go through the subcommands.

Some Python scripting to generate G’MIC code to ensure placement of characters are correct, multiple split-view in KDE Kate goes a long way to do this. It’s more that it doesn’t seem that G’MIC itself sees $image_a as an argument in of by itself rather than one separated by comma.

There’s the question of combining crop arguments after rm… though since they use {} in different places.

I recall using if-then statements to sort through command-argument combination possibilities. Takes up more room but makes it easier for me (and gmic) to parse.

1 Like

If you start having (ugly :slight_smile: ) syntax like this, I would suggest using this equivalent alternative:

arg $mode,"1~,1~,1~",,"0~,1~" image_a=${}

You won’t have to deal with those escaped characters in strings.

1 Like

True. Give a better solution when giving advice. :slight_smile:

I haven’t done one of these for a while, so here’s one based on the updated invert command which now can compute a pseudoinverse.

Say we have two images which were multiplied by unknown amounts, then added together. If we have the combined image and one of the original pair, how can we split them?

Perhaps you can see a ghostly outline of the second here (top = combined, bottom = one of the inputs):
added
peppers

Taking the inputs as A and B, the calculation to produce the output was Y = mA + nB. So in theory we can get back the other original by multiplying the one we have by a certain unknown amount, then subtract that from the combined one: mA = Y - nB. It will be multiplied by an unknown amount, but we can easily normalise it back to any range we want.

Luckily that’s just a linear combination, which means we can use linear regression. Simple regression would do, but it’s nice to try the invert command:

gcd_split_images :
  l[-2,-1] {
     +y +f. 1 a[-2,-1] x # turn images into vectors
     invert. 0,1e-6 # use the new pseudoinverse command
     rv[-2,-1] mmul[-2,-1] # solve to get our 2x1 vector of slope + offset
     mul[1] {i[0]} sub[0,1] k[0] # multiply and subtract
  }

And now to try it out:
gmic added.png peppers.png +gcd_split_images

I leave it as an exercise to try it out and get back the “hidden” second image :slight_smile:

1 Like

I haven’t tested but I think I recognize the mandrill’s eyes in the first image :slight_smile:

1 Like

I’ll bite -

osgood@bertha ~ $ gmic -debug added.gmic pepper_mixed.png  pepper_straight.png +gcd_split_image

[gmic]-0./ Start G'MIC interpreter (in debug mode).<gmic>-0./ Initial command line: 'cli_start , -debug added.gmic pepper_mixed.png pepper_straight.png +gcd_split_images'.
<gmic>./ Decompose command line into 7 items: 
<gmic>./   item[0] = 'cli_start'
<gmic>./   item[1] = ','
<gmic>./   item[2] = '-debug'
<gmic>./   item[3] = 'added.gmic'
<gmic>./   item[4] = 'pepper_mixed.png'
<gmic>./   item[5] = 'pepper_straight.png'
<gmic>./   item[6] = '+gcd_split_images'

<gmic>-0./ Enter scope './'.
<gmic>-0./ Item[0]: 'cli_start', selection [].
<gmic>-0./ Found custom command 'cli_start: ' (takes no arguments).
<gmic>-0./ Expand command line for command 'cli_start' to: ''.
<gmic>-0./cli_start/ Return from empty command 'cli_start/'.
<gmic>-0./ Item[2]: '-debug' -> 'debug', selection [].
<gmic>-0./ Item[3]: 'added.gmic', selection [].
<gmic>-0./ Command 'input': arguments = 'added.gmic'.
[gmic]-0./ Input custom command file 'added.gmic' (1 new, total: 4864).
<gmic>-0./ Item[4]: 'pepper_mixed.png', selection [].
<gmic>-0./ Command 'input': arguments = 'pepper_mixed.png'.
[gmic]-0./ Input file 'pepper_mixed.png' at position 0 (1 image 512x512x1x3).
<gmic>-1./ Item[5]: 'pepper_straight.png', selection [].
<gmic>-1./ Command 'input': arguments = 'pepper_straight.png'.
[gmic]-1./ Input file 'pepper_straight.png' at position 1 (1 image 512x512x1x3).
<gmic>-2./ Item[6]: '+gcd_split_images' -> 'gcd_split_images', selections [0,1].
<gmic>-2./ Found custom command 'gcd_split_images:  l[-2,-1] { +y +f. 1 a[-2,-1] x invert. 0,1e-6 rv[-2,-1] mmul[-2,-1] mul[1] {i[0]} sub[0,1] k[0] }' (takes no arguments).
<gmic>-2./ Expand command line for command 'gcd_split_images' to: ' l[-2,-1] { +y +f. 1 a[-2,-1] x invert. 0,1e-6 rv[-2,-1] mmul[-2,-1] mul[1] {i[0]} sub[0,1] k[0] }'.
<gmic>./ Decompose command line into 23 items: 
<gmic>./   item[0] = (debug info 0x1,1)
<gmic>./   item[1] = (debug info 0x2,1)
<gmic>./   item[2] = 'l[-2,-1]'
<gmic>./   item[3] = '{'
<gmic>./   item[4] = (debug info 0x3,1)
<gmic>./   item[5] = '+y'
<gmic>./   item[6] = '+f.'
<gmic>./   item[7] = '1'
<gmic>./   item[8] = 'a[-2,-1]'
<gmic>./   item[9] = 'x'
<gmic>./   item[10] = (debug info 0x4,1)
<gmic>./   item[11] = 'invert.'
<gmic>./   item[12] = '0,1e-6'
<gmic>./   item[13] = (debug info 0x5,1)
<gmic>./   item[14] = 'rv[-2,-1]'
<gmic>./   item[15] = 'mmul[-2,-1]'
<gmic>./   item[16] = (debug info 0x6,1)
<gmic>./   item[17] = 'mul[1]'
<gmic>./   item[18] = '{i[0]}'
<gmic>./   item[19] = 'sub[0,1]'
<gmic>./   item[20] = 'k[0]'
<gmic>./   item[21] = (debug info 0x7,1)
<gmic>./   item[22] = '}'

<gmic>-2./gcd_split_images/ Enter scope 'gcd_split_images/'.
<gmic>-2./gcd_split_images/#2 Item[2]: 'l[-2,-1]', selections [0,1].
[gmic]-2./gcd_split_images/*local#2/ Start 'local...done' block, with images [0,1].

<gmic>-2./gcd_split_images/*local#2/#2 Enter scope '*local#2/'.
<gmic>-2./gcd_split_images/*local#2/#3 Item[5]: '+y' -> 'y', selections [0,1].
<gmic>-2./gcd_split_images/*local#2/#3 Command 'unroll': arguments = '+f.'.
[gmic]-2./gcd_split_images/*local#2/ Unroll images [0,1] along the 'y'-axis.
<gmic>-4./gcd_split_images/*local#2/#3 Item[6]: '+f.' -> 'f.', selection [3].
<gmic>-4./gcd_split_images/*local#2/#3 Command 'fill': arguments = '1'.
[gmic]-4./gcd_split_images/*local#2/ Fill image [3] with 1.
<gmic>-5./gcd_split_images/*local#2/#3 Item[8]: 'a[-2,-1]', selections [3,4].
<gmic>-5./gcd_split_images/*local#2/#3 Command 'append': arguments = 'x'.
[gmic]-5./gcd_split_images/*local#2/ Append images [3,4] along the 'x'-axis, with alignment 0.
<gmic>-4./gcd_split_images/*local#2/#4 Item[11]: 'invert.', selection [3].
<gmic>-4./gcd_split_images/*local#2/#4 Command 'invert': arguments = '0,1e-6'.
[gmic]-4./gcd_split_images/*local#2/ Invert matrix image [3], using SVD solver and lambda 1e-06.
<gmic>-4./gcd_split_images/*local#2/#5 Item[14]: 'rv[-2,-1]', selections [2,3].
[gmic]-4./gcd_split_images/*local#2/ Reverse positions of images [2,3].
<gmic>-4./gcd_split_images/*local#2/#5 Item[15]: 'mmul[-2,-1]', selections [2,3].
<gmic>-4./gcd_split_images/*local#2/#5 Command 'mmul': arguments = 'mul[1]'.
[gmic]-4./gcd_split_images/*local#2/ Multiply matrix/vectors [2,3].
<gmic>-3./gcd_split_images/*local#2/#6 Item[17]: 'mul[1]', selection [1].
<gmic>-3./gcd_split_images/*local#2/#6 Command 'mul': arguments = '{i[0]}' -> '0.89858591556549072'.
[gmic]-3./gcd_split_images/*local#2/ Multiply image [1] by 0.898586.
<gmic>-3./gcd_split_images/*local#2/#6 Item[19]: 'sub[0,1]', selections [0,1].
<gmic>-3./gcd_split_images/*local#2/#6 Command 'sub': arguments = 'k[0]'.
[gmic]-3./gcd_split_images/*local#2/ Subtract images [0,1].
<gmic>-2./gcd_split_images/*local#2/#6 Item[20]: 'k[0]', selection [0].
[gmic]-2./gcd_split_images/*local#2/ Keep image [0] (1 image left).
<gmic>-1./gcd_split_images/*local#2/#7 Item[22]: '}', selection [0].
[gmic]-1./gcd_split_images/*local#2/ End 'local...done' block.
<gmic>-1./gcd_split_images/*local#2/#7 Exit scope '*local#2/'.

<gmic>-1./gcd_split_images/#7 Exit scope 'gcd_split_images/'.

[gmic]-3./ Display images [0,1,2] = 'pepper_mixed.png, pepper_straight.png, pepper_mixed_c1.png'.
[0] = 'pepper_mixed.png':
  size = (512,512,1,3) [3072 Kio of float32].
  data = 0x5632536062e0 = (124,149,150,154,164,154,153,148,153,157,197,203,(...),170,170,164,170,164,170,152,152,152,152,188,179).
  min = 0, max = 255, mean = 126.618, std = 67.2474, coords_min = (267,255,0,2), coords_max = (55,83,0,1).
[1] = 'pepper_straight.png':
  size = (512,512,1,3) [3072 Kio of float32].
  data = 0x5632539062f0 = (116,161,161,161,161,161,161,161,161,161,206,206,(...),192,192,185,192,185,192,172,172,172,172,212,202).
  min = 0, max = 255, mean = 124.296, std = 74.2691, coords_min = (1,0,0,1), coords_max = (371,37,0,1).
[2] = 'pepper_mixed_c1.png':
  size = (512,512,1,3) [3072 Kio of float32].
  data = 0x56325326a090 = (19.764,4.32767,5.32767,9.32767,19.3277,9.32767,8.32767,3.32767,8.32767,12.3277,11.8913,17.8913,(...),-2.5285,-2.5285,-2.23839,-2.5285,-2.23839,-2.5285,-2.55678,-2.55678,-2.55678,-2.55678,-2.50021,-2.51436).
  min = -3.45779, max = 34.4057, mean = 14.927, std = 8.11537, coords_min = (192,511,0,1), coords_max = (372,206,0,2).
<gmic>-3./ Exit scope './'.

[gmic]-3./ End G'MIC interpreter.

@David_Tschumperle saw through the curtain. I’m pondering a more nuanced example to evade those good at peeking.

1 Like

I considered that too, but decided to keep invert as the main focus - although it could be hidden further even with only that, without resorting to xor!

@garagecoder Just realized something. This can be used to store image signature onto images. A really neat trick imo.

By ‘nuance’ I’m not thinking of layers of bit twiddling. I’m thinking, I have to use invert in a non-trivial use case, with an example that should advance the cause, and could be mordant, and if I pull off the heist, I’ll have a tutorial item of note.

Yes I suppose as a very basic method - it’s easily defeated (and destroyed by image manipulations).

Ah, this sounds more interesting! Looking forward to reading if you get around to it :slight_smile:

I would like to know how G’MIC eval/fill process 1D images in multi-threaded case. Does it split images by threads equally? Or does it split by N number of pixels? Split isn’t the exact word, but hope you get the idea.

It subdivides the image into (almost) equally parts for each thread.
You can check it with:

$ gmic 4096,1,1,1,"*t"

where t is the thread number.

1 Like

Ah yes, now I see. By the way, there’s a scenario where if I have different dynamic array per threads, I would like to do this:

gmic 1,2800,1,1,:"begin_t(const thread_id=t;);thread_id;

That is not possible for now. Would help me dodge the need of code generation.

I don’t understand what you are looking for.
If you need as many dynamic arrays as your number of threads, this is a solution:

foo :
  1,1,1,2x$_cpus # Create as many dynamic arrays as the max number of threads
  4096,1,1,1,"*da_push(#t,[g,g]); end_t(da_freeze(#t))" rm.  # Make each thread fill its own dynamic array.
1 Like