G'MIC exercises

@afre : To me the correct terminology for kernels is something like:

  • Kernels defined on finite domain :
    • size : Size of the kernel, for instance size=7 means a 7x7 kernel.
    • radius : Radius of the kernel, which means an equivalent size = 2 * radius + 1. You can’t have even-sized kernel when specifying a radius.
  • Kernel defined on infinite domain (usually gaussian) :
    • std_deviation : standard deviation (sigma) used in the gaussian function. Almost 100% of the significant values of the gaussian function are located in range |x|<3.5 * radius (see 68–95–99.7 rule - Wikipedia), so you can get an idea of what the equivalent radius could be, if the kernel had a finite domain.

It may happen that this terminology is not the one used in some commands (I’ve not checked all of them :slight_smile: ), but this is probably how it should be done.

Well, yes, as the number of shortcut is quite limited, we can always construct this list manually :slight_smile:

  +3d (+): Shortcut for command 'add3d'
  /3d (+): Shortcut for command 'div3d'
  *3d (+): Shortcut for command 'mul3d'
  -3d (+): Shortcut for command 'sub3d'
  ac : Shortcut for command 'apply_channels'
  apc : Shortcut for command 'apply_parallel_channels'
  apo : Shortcut for command 'apply_parallel_overlap'
  ap : Shortcut for command 'apply_parallel'
  a (+): Shortcut for command 'append'
  b (+): Shortcut for command 'blur'
  c3d : Shortcut for command 'center3d'
  col3d (+): Shortcut for command 'color3d'
  c (+): Shortcut for command 'cut'
  d0 : Shortcut for command 'display0'
  d3d (+): Shortcut for command 'display3d'
  da : Shortcut for command 'display_array'
  db3d (+): Shortcut for command 'double3d'
  dfft : Shortcut for command 'display_fft'
  dg : Shortcut for command 'display_graph'
  dh : Shortcut for command 'display_histogram'
  dp0 : Shortcut for command 'display_parallel0'
  dp : Shortcut for command 'display_parallel'
  dq : Shortcut for command 'display_quiver'
  drgba : Shortcut for command 'display_rgba'
  d (+): Shortcut for command 'display'
  dt : Shortcut for command 'display_tensors'
  dw : Shortcut for command 'display_warp'
  endl (+): Shortcut for command 'endlocal'
  e (+): Shortcut for command 'echo'
  f3d (+): Shortcut for command 'focale3d'
  fc : Shortcut for command 'fill_color'
  fi (+): Shortcut for command 'endif'
  frame : Shortcut for command 'frame_xy'
  f (+): Shortcut for command 'fill'
  g (+): Shortcut for command 'gradient'
  h : Shortcut for command 'help'
  ig : Shortcut for command 'input_glob'
  ir : Shortcut for command 'inrange'
  i (+): Shortcut for command 'input'
  j3d (+): Shortcut for command 'object3d'
  j (+): Shortcut for command 'image'
  k (+): Shortcut for command 'keep'
  l3d (+): Shortcut for command 'light3d'
  l (+): Shortcut for command 'local'
  m3d (+): Shortcut for command 'mode3d'
  md3d (+): Shortcut for command 'moded3d'
  m (+): Shortcut for command 'command'
  m/ (+): Shortcut for command 'mdiv'
  m* (+): Shortcut for command 'mmul'
  mv (+): Shortcut for command 'move'
  n3d : Shortcut for command 'normalize3d'
  nm (+): Shortcut for command 'name'
  n (+): Shortcut for command 'normalize'
  o3d (+): Shortcut for command 'opacity3d'
  on : Shortcut for command 'outputn'
  op : Shortcut for command 'outputp'
  o (+): Shortcut for command 'output'
  ow : Shortcut for command 'outputw'
  ox : Shortcut for command 'outputx'
  p3d (+): Shortcut for command 'primitives3d'
  p (+): Shortcut for command 'print'
  q (+): Shortcut for command 'quit'
  r2dx : Shortcut for command 'resize2dx'
  r2dy : Shortcut for command 'resize2dy'
  r3d (+): Shortcut for command 'rotate3d'
  r3dx : Shortcut for command 'resize3dx'
  r3dy : Shortcut for command 'resize3dy'
  r3dz : Shortcut for command 'resize3dz'
  rm (+): Shortcut for command 'remove'
  rr2d : Shortcut for command 'resize_ratio2d'
  r (+): Shortcut for command 'resize'
  rv3d (+): Shortcut for command 'reverse3d'
  rv (+): Shortcut for command 'reverse'
  s3d (+): Shortcut for command 'split3d'
  + (+): Shortcut for command 'add'
  & (+): Shortcut for command 'and'
  << (+): Shortcut for command 'bsl'
  >> (+): Shortcut for command 'bsr'
  / (+): Shortcut for command 'div'
  == (+): Shortcut for command 'eq'
  >= (+): Shortcut for command 'ge'
  > (+): Shortcut for command 'gt'
  <= (+): Shortcut for command 'le'
  < (+): Shortcut for command 'lt'
  % (+): Shortcut for command 'mod'
  * (+): Shortcut for command 'mul'
  != (+): Shortcut for command 'neq'
  | (+): Shortcut for command 'or'
  ^ (+): Shortcut for command 'pow'
  = (+): Shortcut for command 'set'
  - (+): Shortcut for command 'sub'
  sh (+): Shortcut for command 'shared'
  sl3d (+): Shortcut for command 'specl3d'
  sp : Shortcut for command 'sample'
  ss3d (+): Shortcut for command 'specs3d'
  s (+): Shortcut for command 'split'
  t3d (+): Shortcut for command 'texturize3d'
  to : Shortcut for command 'text_outline'
  t (+): Shortcut for command 'text'
  up : Shortcut for command 'update'
  u (+): Shortcut for command 'status'
  v (+): Shortcut for command 'verbose'
  w (+): Shortcut for command 'window'
  x (+): Shortcut for command 'exec'
  y (+): Shortcut for command 'unroll'
  z (+): Shortcut for command 'crop'
1 Like

That’s very helpful, thanks. Can I ask that you make the list a part of your standard documentation? Perhaps as a file that you maintain, so other folk can download as and when needed?

It’s already a part of the official doc : http://gmic.eu/reference.shtml#subsection22

That’s great, thanks.

Thanks for connecting the dots. Typo?

|x|<3.5 * radius
If we are talking about percentages, would radius = 3.5 * max_dimension * std_deviation (expressed in %) / 100?

Yes, |x| < 3.5*std_dev.

Yes to this statement as well?


New question.

gmic sp tiger,dog,wall echo_test

# tiger
# dog
# wall

echo_test:
   echo_stdout "\n"
   repeat $! local[$>]
      echo_stdout ""{b}""
    endl done quit

What if I wanted to do this without local? Something like b#$> doesn’t work. I am asking because I cannot use local in some cases; e.g., when iterating over pixels like for MSE but I still want to echo the input names with the respective MSE values.

echo_stdout {$>,b}

More generally, {ind,feature} works for all kind of image features.

That’s right, I flipped the items by mistake to {feature,ind}!

What is the simplest way to store values and strings into a vector? Two ways demonstrated by @garagecoder post #9 and @David_Tschumperle post #12 of the other thread seem over complicated (or I am just not used to doing it that way). Here is an example. Goal: make each line a vector element in s and echo the fifth element of s somewhere down the pipeline.

This
is
the
number
0
okay?

For that, I usually use numbered variables as $lineN (where N is an integer) to simulate an array of variables. It’s quite easy to manage. For instance, the following code read each non-empty line of a text file:

read_lines:
   verbose -
   input raw:"$1",uchar   # Get ascii values of the file as a new image of data in [0,255].
   split -,{'\n'}  # Decompose as lines (and discard '\n').
   repeat $! line$>={$>,t} done # Assign line content to numbered variables
   remove
   verbose +
   echo[] "Fifth non-empty line : "$line4
   echo[] "First 5 non-empty-lines :"
   repeat 5 echo[] "    ["{1+$>}"] = "${line$>} done

Then, using it like this : $ gmic read_lines README gives:

$ gmic read_lines README
[gmic]-0./ Start G'MIC interpreter.
Fifth non-empty line :                       | |  __  |_| |   / | | || |
First 5 non-empty-lines :
    [1] = --------------------------------------------------------------------------------
    [2] = --------------------------------------------------------------------------------
    [3] =                         _____   _   __  __ _____ _____
    [4] =                        / ____| | | |  /  |_   _/ ____|
    [5] =                       | |  __  |_| |   / | | || |
[gmic]-0./ End G'MIC interpreter.
1 Like

Thanks: read_lines explains a lot. The part that I was missing was split -,{'\n'}. I don’t quite know when to use echo[] and echo_stdout though. Besides error versus standard output, echo[] appears to require less quoting ", so maybe I will stick with that one. I guess my choice would affect the logs…? I also discovered the string command, so I could do

echo_test:
  string "hello world"
  split -,{'\ '}
  repeat $! word$>={$>,t} done
  echo[] ""
  echo[] $word1
  echo[] ""
  q

# world

Next question. I have been playing around with norms and deciding what I could use them for. I wanted to try calculating the Frobenius norm but I don’t know a simple way to add all the elements of a matrix. norm is sqr compose_channels + sqrt; I guess that is supposed to be done before compose_channels +… Wait, are we already calculating the f-norm since an image is already a matrix?

I’m not sure how you define this for multi channel images because it’s equivalent to euclidean norm of an m x n matrix treated as a vector (i.e. output is a scalar). In other words sum the squares of the elements then take the square root of that. For single channel image it would just be:

sqr echo {sqrt(is)}  # output to console

Yes, that occurred to me after writing the post :blush:. I think norm takes the L2-norm of every I vector.

norm
Creative Commons Licence by @afre, using Inkscape :slight_smile:

If that is true, then a matrix would be a line of pixels… I think a more useful application might be to move a f-norm window over an image for each channel but I don’t know how to do that yet :cake:. Sure enough, Google Scholar does yield papers where people use norms as a filter.

Speaking of image filtering, one exercise would be to write a command that passes an arbitrary kernel (given by user) through an image like the Convolution matrix filter in GIMP, but it might not be able to do a f-norm, because I don’t know what the matrix is…?

Yes, it’s per pixel vector norm

One way is with a convolution kernel:

sp dog
sqr
(1)  # a single pixel image with value 1
resize. 5,5
convolve.. .  # convolve input by kernel
rm.
sqrt

For larger kernels you could do that with the boxfilter command (approximate but fast), there’s also the option of using the math parser. It’s very easy to specify a kernel by image values (here I use the backslash to escape newline for clarity):

(0,1,0;\
 1,1,1;\
 0,1,0)
1 Like

I need some ideas to get started with finding edges. There are so many commands that I don’t know where to start. So far, I have only made threshold images based on Boolean expressions. Not necessarily but something like this would be nice:

road

roadedges

Source: Images borrowed from this paper.

What I have so far. Not great but a start :slight_smile:.

edge_test0:
  l50_ 1  # L* channel from L*a*b* D50
  laplacian - {im}
  fill i<ia+sqrt(iv)&&i>ia-sqrt(iv)
  negate 1

edge_test

Edge detection is a large subject in itself, but the one I usually end up using is gradient_norm. Combined with an exponent or a threshold (of which there are many, perhaps give ‘otsu’ a try) it’s quite hard to beat in general situations, for such a simple operation.

Used gradient_norm before and wanted to try laplacian. Otsu is cleaner overall but may ignore weaker edges. Turns out it is still the right one to use:

Speaking of convolution, I came across Sharpening Images with Edge Detection in the Imagemagick docs. I am having trouble understanding and reproducing the first command.

convert face.png -define convolve:scale='100,100%' \
          -morphology Convolve 'Log:0x2' face_sharpen.png

Hint: jump here for details on -define and here for -morphology. Actually, 0x2 might have to do with -gaussian-blur. I am not quite sure; I haven’t used IM in a while. (@snibgo, maybe you could help me with the explanation.)

This is what I have but it doesn’t match.

convolve_test0:
  (-1,-1,-1;\
   -1,+8,-1;\
   -1,-1,-1)
  /. 8
  (0,0,0;\
   0,1,0;\
   0,0,0)
  +[-2,-1]
  b {2/3.5},1,1
  convolve.. .
  rm.

Hmm those docs have some suspect descriptions of so-called ‘LoG’. I assume the -define part is setting parameters for the convolution (I never use imagemagick). The idea of that kernel is to save doing two separate steps (blur then laplacian) by using laplacian of a gaussian as the kernel:

gmic image.png blur 2 laplacian

becomes:

gmic 11,11,1,1 gaussian 2,2 laplacian image.png convolve. ..

however for gmic I doubt this saves any time due to the fast blur command.

Edit: oops was in a hurry there, it should really be normalised and I guess you want to actually sharpen! In which case it’s:

gmic 11,11,1,1 gaussian 2,2 normalize_sum laplacian image.png +convolve. .. rm... +

I suppose some memory can be saved by adding 1 to the kernel centre instead of copy and add…

gmic 11,11,1,1 gaussian 2,2 normalize_sum laplacian +f 0 =. 1,50%,50% + sp dog convolve. ..
1 Like

:thinking: The trouble that I am having and your examples seem to do as well is that we end up with a softer image. At least they appear to have less of a punch.

sp tiger

'Log:0x2'

'Log:0x.5'

PS
I also came across fun effects.


And wrote a filter that highlights or darkens edges.

1 Like