On the road to 3.0

Dear @David_Tschumperle, I want to bother you again with the “Mixer [PCA]” filter :smiley: .

In some cases it would be useful to have the option to “invert” only one or two axes of the PCA-ellipsoid.

I did a lot of experiments with the “Transfer Color [PCA]” filter. Often it gives nice results. But I noticed that this filter sometimes “inverts” an axis of the PCA-ellipsoid. Therefore I wanted to be able to invert an axis in the “Mixer [PCA]” filter. in other cases this may also be useful.

I have the following solution; invert the value(s) for “gamma”.

nI[0] = (do_gamma(nI[0],vmax0,gamma0))*-1;

This works the way I want it.

I am not a programmer. Surely there will be a much more elegant way to do this, but maybe this is helpful to others?

#@gui Mixer [PCA_invert]:fx_mix_pca_invert,fx_mix_pca_invert_preview(1)+
#@gui :Primary Factor=float(0,-1.5,1.5)
#@gui :Primary Shift=float(0,-255,255)
#@gui :Primary Twist=float(0,-180,180)
#@gui :Primary Gamma=float(0,-100,100)
#@gui :Primary Invert=int(1,-1,1)
#@gui :_=separator()
#@gui :Secondary Factor=float(0,-1.5,1.5)
#@gui :Secondary Shift=float(0,-255,255)
#@gui :Secondary Twist=float(0,-180,180)
#@gui :Secondary Gamma=float(0,-100,100)
#@gui :Secondary Invert=int(1,-1,1)
#@gui :_=separator()
#@gui :Tertiary Factor=float(0,-1.5,1.5)
#@gui :Tertiary Shift=float(0,-255,255)
#@gui :Tertiary Twist=float(0,-180,180)
#@gui :Tertiary Gamma=float(0,-100,100)
#@gui :Tertiary Invert=int(1,-1,1)
#@gui :_=separator()
#@gui :Display Color Axes=bool(1)
#@gui :_=value(-1,-1,-1,-1)
#@gui :_=value(0,0,0,0,0,0,0,0,0,0,0,0)
#@gui :_=separator()
#@gui :Preview Type=choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse")
#@gui :Preview Split=point(50,50,0,0,200,200,200,0,10)_0
#@gui :_=separator()
#@gui :_=note("<small>Author: <i><a href="https://bit.ly/2WeKVPv">David Tschumperl&#233;</a></i>.&#160;&#160;&#160;&#160;&#160;&#160;Latest Update: <i>2018/07/18</i>.</small>")
fx_mix_pca_invert :
repeat $! l[$>] split_opacity l[0] to_rgb
if [${14-17}]==round(stats()[0,4],0.1) _avg=${18-20} C=${21-29} status=
+rr2d 256,256,0,2 C=${"covariance_colors. _avg"} rm.
if "$4 || $9 || $14" +l
f "begin(avg = ["$_avg"]; eig = eig(["$C"]); Pt = eig[3,9]); Pt*(I-avg)"
s c repeat $! vmax$>={$>,1.1*max(abs(im),abs(iM))} done
endl else vmax0,vmax1,vmax2=1 fi
f "begin(
do_gamma(val,vmax,gamma) = (vmax*sign(val)*(abs(val)/vmax)^gamma);
const gamma0 = 10^-($4%);
const gamma1 = 10^-($9%);
const gamma2 = 10^-($14%);
const vmax0 = "$vmax0";
const vmax1 = "$vmax1";
const vmax2 = "$vmax2";
avg = ["$_avg"];
eig = eig(["$C"]);
for (k = 3, k<12, k+=3, eig[k]<0?copy(eig[k],eig[k,3]*=-1,3));
Pt = eig[3,9];
P = transp(Pt,3);
T = mul(P,diag(10^[$1,$6,$11]),3);
R1 = rot(eig[3,3],$3);
R2 = rot(eig[6,3],$8);
R3 = rot(eig[9,3],$13);
T = mul(R1,mul(R2,mul(R3,T,3),3),3);
avg_shift = avg + $2*eig[3,3] + $7*eig[6,3] + $12*eig[9,3];
if ("0$_is_preview",
L = [ 2,5,10]*sqrt(1e-5 + eig[0,3]);
avg - L[0]*eig[3,3],
avg + L[0]*eig[3,3],
avg - L[1]*eig[6,3],
avg + L[1]*eig[6,3],
avg - L[2]*eig[9,3],
avg + L[2]*eig[9,3] ])));
nI = Pt*(I - avg);
nI[0] = (do_gamma(nI[0],vmax0,gamma0))*$5;
nI[1] = (do_gamma(nI[1],vmax1,gamma1))*$10;
nI[2] = (do_gamma(nI[2],vmax2,gamma2))*$15;
avg_shift + T*nI"
c 0,255
endl a c endl done u $__status
fx_mix_pca_invert_preview :
repeat $! l[$>]
gui_split_preview "fx_mix_pca_invert $*",${-3--1}
if $16
rr2d ${-gui_preview_wh},0,1
($__cols) r. 3,6,1,1,-1 permute. yzcx s. x,3
r[-3--1] {w#0/2},13,1,3,3 c[-3--1] 0,255
frame[-3--1] 1,1,0
to[0] Primary,4,2,13,1 j[0] ...,64,4
to[0] Secondary,4,17,13,1 j[0] ..,64,19
to[0] Tertiary,4,32,13,1 j[0] .,64,34
endl done
u $__status

Probably easier to read if you use ``` to wrap the code in the post.

1 Like

How can I change the text formatting?

Feature Any chance for font support?

Issue discard takes y as a parameter.
1 sort and discard should check their parameters more closely (e.g. both can take y as first parameter)
2 Maybe give unroll another shortcut.

gmic (1;2;3;4;3;2;1) sort discard unroll x
# vs
gmic (1;2;3;4;3;2;1) sort discard y x

3 discard should keep alignment horizontal if it was originally so

gmic (1;2;3;4;3;2;1) unroll x discard

Issue autocrop_coords returns the same coordinates for both images. Hint: depending on forum background, you may not see the black or white. One side of the images are black and the other white.

gmic sq-0.png sq-1.png repeat $! e[] ${autocrop_coords[$^>]} done q
# both return 251,0,0,499,499,0



Not for the moment.

I don’t see a problem here. y is a valid parameter for those commands, and then ?

Not valid. If we do that, we should also give another shortcut to all commands that can be also possible arguments of other commands, including x (exec), y (unroll), z (crop), c (cut), and others… Meaning probably we have to remove most of the one-letter shortcuts.

That’s a good suggestion indeed. Will probably do that for 2.9.2.

That’s expected in your case. autocrop_coords with no arguments tries to autodetect the crop value, and in this case, it takes black (first case) or white (second case) as it’s basically the first pixel it encounters.

sort: y is nonsense as a first parameter.

Food for thought.

Not at all. sort allows to sort image values according to a certain axis, ans this has been proved to be very useful in some of the stdlib commands.

Perhaps I forgot the behaviour. Does sort y smartly recognize that y isn’t for _ordering?

_ordering={ + | - },_axis={ x | y | z | c }

The ordering argument is optional, that’s why sort y is valid.
I could force the ordering argument to be present though, to avoid this issue. Not sure if it’s good or not.

You might use ImageMagick for that, and even call IM through G’MIC’s exec command. True, this means (a) having to learn both systems and (b) a performance hit, as images must be transferred by writing and reading files. But it works. For example, this Windows BAT command:

%GMIC% ^
  toes.png ^
  snibgo_imcaption ^
hello\ world,^
-background\ None\ -fill\ Yellow\ -font\ Calibri ^
  %TEMP%\gmicst_tmp.png ^
  normalize 0,255 ^
  to_rgba ^
  blend alpha ^
  output gmicst_hw2.png

… where snibgo_imcaption is …

snibgo_imcaption :
  skip "${3=}"
  if narg("$3") STR={/"$3"} fi
  skip "${4=}"
  if narg("$4") OUTFIL={/"$4"} fi
  skip "${5=}"
  if narg("$5") PREOPT={/"$5"} fi
  echo_stdout STR:$STR
  cmd=magick\ -size\ ${WW}x${HH}\ $PREOPT\ caption:\"$STR\"\ $OUTFIL
  echo_stdout $cmd
  exec $cmd

… makes this image:

Now that makes me want to try imagemagick at some point. Adds it to mental TODO

@snibgo Thanks. I roughly know how to do it. It is just that I prefer not to provide a public command that does that because the user would then be responsible for installing IM (or GM) and adding it to the path.

@Reptorian: My mental todo list keeps growing. It includes publishing a page that compares and contrasts IM with GMIC. They have some fundamental differences that confused me until I understood them.

@afre: Yes, using both systems in one application is klunky. But there will always be times when one system provides facilities the other doesn’t.

I haven’t looked at GMIC source code, but I think it includes a version of IM, perhaps for reading/writing image files of various formats. If so, perhaps GMIC could provide a command that called its internal IM, passing images in-memory.

A few words about the use of IM in G’MIC. I’ve read several times (not here fortunately), that G’MIC code is based on IM, which is patently untrue:

  • G’MIC doesn’t use IM for reading/writing common image file formats (JPEG, PNG, TIF, BMP, …). G’MIC is not even linked with the Magick++ library.
  • Only in rare cases, G’MIC calls a fallback function that may use IM (but also GraphicsMagick if IM is not installed), when an image file couldn’t be read (or written) with the native functions of the CImg library (which is the base image processing library used in the code of G’MIC). This fallback function just tries to do an exec() with IM (or GM) to convert data in a proper format in /tmp/ before reading it with CImg. This fallback function is called for instance if you try to read an animated .gif or a .pdf file with G’MIC.
  • Except to handle these very special cases, there are no other places where IM is used, in the G’MIC code.

Of course, G’MIC has been partially inspired by IM for some of its features, but it has a totally different scripting philosophy, and is more focused on the image processing itself than on the conversion between image formats. I can only advise to use these two pieces of software together :slight_smile:


Thanks for the clarification, David.

In my view, G’MIC and IM are two different systems with some similarities. I would love both systems to have all the facilities of the other, but I doubt that will ever be the case. For a workflow that needs both systems, it makes more sense to separate the functions so each system is called from a common script as required. But IM can be called from G’MIC, if we want.

In the other direction, IM has no equivalent to G’MIC’s exec, so I added one as a process module, so IM can call G’MIC:

magick xc: -process 'shell "gmic toes.png blur 3 output x.png"' x.png info:

R% in circle isn’t practical because anything ≥50% covers the whole image.

Not true, if the circle center is not located at the center of the image:

gmic 300,300 circle 0,0,50%,1,255


When used with R% the radius is actually computed as

\text{radius} = \frac{R}{100}*\sqrt{\text{width}^2 + \text{height}^2}

(that’s why the circle outline passes through the image center in the example above).

Just a request here. On erode commands, is it possible to add variants that disable computation of erodes on all area with 0 or less? Like erode_circ0 which is the erode_circ equilvalent of shapemax0.

Minor bug in GMIC 2.9.1. Not sure about pre.

First line of command doesn’t require a colon; however, help doesn’t work without it when it is only located in user.gmic but not in updateXXX.gmic (i.e. stdlib or community).

Could you exhibit an example. I can’t reproduce this behavior.