I have spent all day scouring the tutorials pages, stdlib.gmic, and the forum looking for documentation or examples of how to write scripts. I have managed to find a few things in the stdlib, but I have no idea how much of anything goes together. I am specifically looking for how to manage passed in arguments and what kind of image list the command has access to (things seem to be nonsensically out of bounds). Is there any place that gives a cohesive tutorial on how to write scripts?
@grosgood has done a great job at writing tutorials for creating scripts.
It starts here : Introduction
$-expressions
, predefined in custom commands, govern much of the translation from arguments users may write after your custom command and its body. Adding Custom Commands furnishes a terse list of them. You have already seen some of these: $0, $1, $2,âŠ
The very first resolves to the name of the script, $1
resolves to the first argument, $2
the second, and so forth. In addition to these, there are other pre-defined $-expressions
that furnish information about the command line: $#
furnishes the maximum number of known arguments. Some GâMIC commands are more-or-less dedicated to argument processing -check
furnishes the means to write argument tests; -skip
permits some arguments to be optional (but they need default values, set with this idiom ${2=3}
. That is, the second argument, if not expressly given by the user, defaults to the value 3
. 3D Bouncing Balls demonstrates the writing of custom commands through iterative versions.
Commands operate on every image on the list, unless constrained otherwise. I call such constrained commands âdecorated.â These decorations are the square brackets placed to the right of the command (no space!) that have a terse notation written within. That notation constitutes a mini-language in its own right. Command Decorations examines this notation. Their aim is to âselectâ images for being operated on by the command, so these decorations are commonly called âselections.â Tiny little pips ..
constitute a shorthand form of command decorations; these select the last (.
), penultimate (..
) and third from the last (...
) image, useful because the final images on the list are the most frequently selected.
Hope this helps.
Addendum:
Hereâs a common difficulty: you do not have a GâMIC vocabulary. You cannot connect your visual goals with some pipeline of GâMIC commands.
Thatâs just the way it is. We all start at that point. In tutorial land, the question of interest is: âHow best to get people quickly past that point?â.
Theory: Finger exercises. Conceive simple visual goals; challenge yourself to come up with pipelines that realizes those goals.
[Aside: My choice of the phrase âfinger exerciseâ is deliberate. You cannot play Beethovenâs Sonata 32, op. 111, until youâre past Chopsticks. You must have muscle memory. In your fingers, in your feet, verily â in your whole body. Muscle memory has to be instinctive so you can focus on the really hard part: interpretation.]
Start with simple â or what may seem to be simple â visual goals. For example, this morning, riding into Manhattan on the subway, I happened to glance down at the linoleum floor of the car and asked myself âHow, in GâMIC, do I make a texture like this floor tile?â
It took me a half hour, spread over the morning, while waiting for various meetings to start, to realize this visual goal â somewhat. The dreary color scheme is spot on. But the little color bits embedded in the base linoleum had different shapes: triangular, square, irregular polygons â like bits of chewed-up rubber balls. Look closely and my results: the ârubber bitsâ all have the same shape, the same size. A miss. Thatâs the way it is with these finger exercises. You get part of it right; part of it is a miss. You then find yourself pulling in other GâMIC commands to get it all right. Thatâs how you build vocabulary.
At the moment, you donât have much of a vocabulary. You can see your visual goal, but you are not sure how to build the pipeline to get there. Here are some vocabulary building tips.
-
Scroll through the Technical Reference. Donât read it. Just look at the pictures. You might see something thatâs half-way like your visual goal. If you do, stop and read about the command(s) that produced the picture.
-
Write test jigs so that you can quickly assess the effects of command arguments. Hereâs an example:
testcmd:
count=10
-repeat $count
-local[-1]
# Command being tested:
+blur. {($>+1)},0,1
-done
-reverse[-2,-1]
-done
-move[-1] 0
Take this to be an exercise in writing a custom command. This custom command exercises commands. The command being exercised here is in a repeat loop. We are testing blur
by repeatedly changing one argument. The bits in the curly braces constitutes a small mathematical expression. There is a predefined substitution variable, $>
, which GâMIC substitutes with the current loop count. So, loop on loop, the argument gets larger. This is the results of testing blur
with different initial arguments:
Play this game with any number of commands, and any number of arguments for each command.
- Get Git. Really? A version control system? Why? To snapshot your work as you go along. If you make a mess, you can backup to the last snapshot that worked. Every commit makes a snapshot. Commit often. If you want to try different things, you can make branches at any one of your commit points. Keep branches forever if you want: variations on a theme. Git repositories arenât just for large projects. They work for tiny projects too. Hereâs how to get started creating a repository for developing
testcmd
:
gosgood@lydia ~ $ git init testcmd
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: git branch -m <name>
Initialized empty Git repository in /home/gosgood/testcmd/.git/
gosgood@lydia ~ $ cd testcmd
gosgood@lydia ~/foo $ touch testcmd.gmic
gosgood@lydia ~/foo $ git add testcmd.gmic
gosgood@lydia ~/foo $ git commit -m "Starting to write the testcmd command"
[master (root-commit) 053f925] Starting to write the test command
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 testcmd.gmic
Now you can edit the empty testcmd.gmic
file. Perhaps copy the jig given above into testcmd.gmic
. Change things. Maybe even break things, deliberately, just to see how the pieces fly. Then perform git add --all
followed by `git commit -m â.â. You may wax prolific and write a little log note to yourself instead of the sole â.â but you donât have to. The important thing is, commit often. Every commit is a snapshot. Screw up and you can back up to the last working snapshot. Later on, you will want to try different approaches. Create branches. You can create a branch at any commit point, even one made days ago. If you develop something good in a branch, you can merge it back to the master (initial) branch. Working in a repository can save you from an enormous amount of grief. Before Git, weâve all worked into the wee hours, realized that weâve screwed up, and â even worse â canât remember how the the script was before we screwed up. No commit points. No way to travel back in time, to 9 PM, when we were still more-or-less awake. So yes. Get Git. Itâs in your distro, and its available for Windows.
Your finger exercises work best when your visual goals are immediate; the number of gmic commands are likely to be small. Scale up as your vocabulary and confidence grows. Look around you every day, especially textures. Ask yourself how you can get that texture through GâMIC.
How many finger execises? Lots and lots!. Maybe three or four hundred per year â one finger exercise per day. Ah! You mean you have a day job??? How unfortunate. But I suppose you have to eat, pay rent and such. Then try for one finger exercise a week. Fifty or sixty in the next year. Any slower than that and â donât take this unkindly! â youâre not serious. Thatâs also fine. One of the best bits of education I had ever received, back in college days, and â late again! with one assignment or another â was the professorâs sigh. And then he said: âYou know? People always find time to do what they really want to do.â
Procrastination is instructive. Itâs how your head tells you who you are.
Thanks for the response!
My main question here is when using a selection as an input on a decorated command, the image selection seems to be out of bounds. Here, calling the following command produces an error:
gmic differ.gmic repeat 2 +lorem 200,200 done color_difference[0] [1]
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Input custom command file 'differ.gmic' (1 new, total: 4568).
[gmic]-0./*repeat/ Input random image of size 200x200.
[gmic]-1./*repeat/ Input random image of size 200x200.
[gmic] *** Error in ./+color_difference/deltaE/ *** Command 'pass': Invalid selection '[1]' (contains index 1, not in range -1...0).
differ.gmic
:
+color_difference:
check ${"is_image_arg $1"}
rgb2lab
+deltaE $1
lab2rgb
The error message leads me to believe that the custom command only has access to the decorated selection; therefore, any passed in indices would be out of range? I know this is not the case, because I can use other commands that use guide images (bilateral). My plight is that documentation about what is actually happening inside of commands is very spread-out across commands and all of the examples/tutorials deal with isolated parameter-less scripts. Is there a place that I can start that gives me a good base knowledge of how to make a script rather than how to make an effect?
I apologize if this comes of as a little whiny.
Thank you for this treasure trove.
Finger exercises are definitely something I want to do and the Git advice is something I should be implementing everywhere.
Actually, I owe you thanks for sharing your trials and tribulations. We who have been writing GâMIC scripts for twelve or thirteen years have forgotten how we took our first steps. As one of the principle tutorial writers, Iâm very keen on what GâMIC looks like to newcomers. What kind of mental models do newcomers build inside their heads about how GâMIC works?
Thatâs right. There are no extensive tutorials on how to get inside a script and observe its inner workings. My first foray into this topic is a cheat sheet: Cheat #4: Youâve Moved Fast and Broken Things. Now What?.
Give it a read. From that, hereâs a few tips on GâMIC debugging:
TIP 1: Use -verbose
to make your custom command more (or less) noisy. See the verbosity section of the cheat sheet for background and some technique. For now, I am going to make your invocation of color_difference
a little more self-revealing. Here is how Iâve modified your command line:
gmic differ.gmic repeat 2 +lorem 200,200 done verbose + color_difference[0] [1]
That is, before I invoke your custom command, I up the verbosity level so that your custom command can emit log lines that are otherwise supressed.
TIP 2: Hijack the display command for single step debugging.
To that end, Iâve modified your custom command:
differ.gmic:
+color_difference:
check ${"is_image_arg $1"} d ,
rgb2lab d ,
+deltaE $1 d ,
lab2rgb d ,
Every other command is the display command (shortcut: d). The optional comma is to indicate that Iâm not passing any arguments to display. The effect of this to execute one command of your script, then stop to display the image list. For all practical purposes, you are single-stepping through your custom command. At each step, a display command stops further execution and provides you with the opportunity of examining the display list and inspecting what your command is doing.
With your script and command line modified, lets give it a run:
$ gmic differ.gmic repeat 2 +lorem 200,200 done -verbose + color_difference[0] [1] -verbose -
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Input custom command file 'differ.gmic' (2 new, total: 4568).
[gmic]-0./*repeat/ Input random image of size 200x200.
[gmic]-1./*repeat/ Input random image of size 200x200.
[gmic]-2./ Increment verbosity level (set to 2).
[gmic]-1./+color_difference/ Display image [0] = 'lorem', from point (100,100,0).
[0] = 'lorem':
size = (200,200,1,3) [468 Kio of float32].
data = (192,189,186,182,179,175,173,172,178,183,190,193,(...),5,5,3,0,1,1,0,0,0,0,0,0).
min = 0, max = 255, mean = 138.866, std = 74.2498, coords_min = (89,45,0,2), coords_max = (190,26,0,0).
[gmic]-1./+color_difference/ Convert color representation of image [0] from RGB to Lab, using the E illuminant.
[gmic]-1./+color_difference/ Display image [0] = 'lorem', from point (100,100,0).
[0] = 'lorem':
size = (200,200,1,3) [468 Kio of float32].
data = (88.9174,88.8173,88.2612,87.9715,87.255,86.9595,86.8896,86.6923,87.215,88.0006,88.3893,88.3279,(...),99.7119,99.7119,106.114,121.207,115.611,115.611,120.914,120.542,120.542,120.542,120.542,120.542).
min = -8.29922, max = 123.04, mean = 41.4644, std = 34.2813, coords_min = (65,0,0,2), coords_max = (199,172,0,2).
[gmic]-1./+color_difference/ Compute the CIE DeltaE_2000 color difference between image [0] and image[1], with to_Lab command 'srgb2lab'.
[gmic] *** Error in ./+color_difference/deltaE/ *** Command 'pass': Invalid selection '[1]' (contains index 1, not in range -1...0).
When we enter the scope of color_difference
single-stepping begins: and the first hint of trouble rears up. First, in this new scope, there is only one image being displayed by our debugging display
command, not two. Thatâs not good. Canât difference between two images when there is only one on the image list. Thereâs another indication that there is only one image on the image list: See how [gmic]-2.
notation transitions to [gmic]-1
on the transition into color_difference
scope? GâMICâs shell log indicates what images are visible on the image list. On the scope transition, the visibility count dropped from two to one.
What about $1
? Doesnât that reference the second image? No â there is a misunderstanding here. The $-expression
$1
only resolves to a string that looks like a selection decorator, that is $1 â [1]
, literally: open square bracket, numeric character â1â, close square bracket. Why? Well, that bracketed numeral is what was actually, literally, written in the argument list to color_difference
. GâMIC is not imaginative enough to interpret the argument list in any other way than the literal string characters that are written there. Thatâs all what âis_image_arg $1â tests: It resolve to True
when an argument looks like a selection decorator.
So how does one actually pass an image argument into a custom command scope? For that, use the pass command. So, letâs modify your custom command this way:
+color_difference:
check ${"is_image_arg $1"} d ,
pass$1 0 d , # New command!
rgb2lab d ,
+deltaE $1 d ,
lab2rgb d ,
When GâMIC is resolving all the command line substitutions, pass$1 0
â pass[1] 0.
After scope entry, with the pipeline interpreter working its way down the color_difference
pipeline, it encounters the pass
command. It is the nature of the pass command to interpret its selection decorator in the parent scope, So pass[1] 0
may be read as "Take the image with the index 1
in the parent scope and duplicate it into color_difference
scope. Argument 0
of the pass
command makes a full copy, requiring more image storage, but making a new, independent image.
Now you have both images present in your commandâs scope. Run it and youâll get your differences as expected. Then you can delete all the d ,
âbreak pointsâ and remove verbose
from your command line and get on with your next adventure.
Have fun.
Thank you so much for your patience. This some great instruction
For me, Iâm coming from a background in amateur programming; very clean, clear,terse syntax. Iâm used to:
def do_this_with(thing_one, thing_two="something")
I now understand how to do this with gmic, but the connections to conventional programming should be more readily made for introductory scripting (under the warrant that anyone crazy enough to jump down the gmic rabbit hole would have at least some programming knowledge).
Iâm also used to being able to read a line of code and have it describe to me what itâs doing; with gmic, I have to decipher. I get that eventually it will become familiar and I wonât have to constantly reference the shortcut list and technical reference (departure from tutorial land), but for comparison, right now, having had no experience with image processing pipelines, gmic is like going from dragânâdrop to c++, minus the countless bootcamp video series. Donât get me wrong, your tutorials are immensely helpful and enjoyable, but trying to self teach off of written instructions is always difficult.
Thank you again for your patience and heaps of advice, I will utilize them in my endeavors moving forward.
I want to point that GâMIC can be said to have 2 languages in one. Eval, and fill, and math expression have their own quirks and it use it own language. Math parser language has a lot of things in common with C++ syntax like â;â for end of line, the use of macros (GâMIC solution to function (and this comes with limitations in which requires pushing stack to solve)), ++/-- things and yes order matter. But, non-math parser thing comes first.
You can also use exec command to execute Python script and so, but chances are youâll never need this.
AndâŠ
In my other life, I work for New York City (Housing Preservation and Development, in particular), where code writing rules are enforced: these demand âvery clean, clear,terse syntaxâ so that the code approaches this ideal of âdescribing what it is doing.â That particular environment demands such a use-case. Software developers come and go; the code they leave behind has to be readable by an influx of new-hires. âSensible programmingâ puts a premium on these readability ideas.
But GâMIC comes from a research environment â and very different use-cases prevail. Sometime last December, in a riff off of @David_Tschumperle 's How to write clean GâMIC code ? I veered into a post on GâMICâs raison dâĂȘtre: as stenography for graphical pipeline builders. A bit tongue-in-cheek of me, perhaps, but I can make my case: David (and colleagues) are very much involved in testing graphical ideas. From David:
So a tool that gets past the junk-thinking very quickly is desirable. Above all, that puts a premium on terseness, and, in a priority stacking, itâs terseness above clarity, by very deliberate design. The aim is to test ideas without the damn code getting in the way, without exasperating code â compile â test cycles, without leaving the shell. So syncopation rules: status
â u
, append
â a
, fill
â f
, and â my favorite! â input
â
As with most design decisions, there are prices to be paid for a particular priority stacking:
Amen. The newcomers have to climb a steep learning curve.
However, there are silver linings. So while I am inventorying the various shortcomings in Tutorial Land, here is one of the most significant: âThe Power of the Colon ( :
)â, as in:
< a nice, apt, and memorable name > : < hundreds of lines of terse, syncopated GâMIC code, full of weird, incomprehensible constructs that â dammit! â does very useful things, now if I can only figure out how it does it. >
The colon âoperatorâ lies at the heart of custom command definitions. It is GâMICâs language extension mechanism. Among other things, one may write a comprehensible left-hand name to represent a right-hand horror show. You have already recounted your ventures into gmic_stdlib.gmic and have probably noticed that in its twisty, darkened hallways there are these one-line custom command definitions that give rise to the âshort-cutâ names for common commands:
You can push that pea in directions other than syncopation, come up with custom command names that better suit your taste and put them in your local GâMIC extensions file: %AppData%\user.gmic
(Windows) or ${HOME}/.gmic
(Unix-like). In that file, you could even be so bold as to override the default, but empty, cli_start
custom command. GâMIC references this particular command, automagically, on every startup. Itâs a hook. You can redefine cli_start
to include various *.gmic
files and introduce site-specific constructs without using -command
inclusions. These syncopation tools, used in the service for People Who Hate To Type, are amendable to other kinds of language extensions.
The only cautionary that I should make is that in sharing environments such as this Discourse server, the canonical GâMIC must rule; it can be hard enough for people to figure out what other people are doing without the babel of regional dialects cropping up.
Thatâs it for now. Good luck with finger exercises and spelunking gmic_stdlib.gmic
.
When I first started gâmic, I spent two solid weeks on it (yes, daily) before I could do even basic stuff. Most of it was just experiment and looking at scripts written by others. In style, it has something in common with maths; lots of symbols and layout with concise meaning that you just have to memorise.
Three things were a mystery: the image stack, the âsubstitutionâ mechanism (pretty much macros) and how images themselves worked.
Some old pages to help with the basics have also landed on the github wiki, they need to be updated but are still useful. Scroll to the âTechnical Documentationâ heading and you may find relevant partsâŠ
I spent way more than that before I get to my first filter. I feel like Iâm still learning.
gmic 128,128,1,1,255*x/(w-1)
which produced:
A ramp. That was the first exercise which gave me a sweet lift of victory. It was, I think, mid 2012. Hitherto, it was random stabs, extending back months. Since I didnât fully grasp at the time that I had to prepare output, a lot of the results were not usable in Gimp, which frustrated me to no end (but I can see them in the display window! Why not Gimp?). This exercise was procedural â an image generated from nothing at all! That intrigued me no end. From that victory, I began experimenting more consistently â and I started a notebook, which I put on my own web site. This was partially to keep track of how I did what I did, and to help others who might be curious about the tool. The notebook about the ramps eventually evolved into this tutorial, initially hosted at my own website. Somewhere during 2013 @David_Tschumperle stumbled across my notebooks and the rest is tutorial history. By then â first contact â I was on a roll. The few little victories that I had lay the foundation for the viral stage of learning, where results snowball into other results. I am still on that roll.
I think that is why I want to pursue this notion of âfinger exercise.â They lead to the first, little victories that, for me, at any rate, got me off the dime. It is missing in Tutorial Land; they are the first discoveries that open doors. Iâd like to capture those first âAh, ha!â moments, if I can.