G'MIC exercises



Thanks! It doesn’t appear to work with a s-curve. BTW, what is a good formula for a s-curve? What I have been using


(I normalize it to 0-1. Edit: the midpoint could be set to any value.) Ideally, I would like it to flatten to y = x and then mirror along the y = x axis. The utility should be obvious: larger values add contrast and smaller ones (negative or fraction) flatten the contrast. Probably a G’MIC built-in already has this function…

(David Tschumperlé) #126

It does work for me, with a sigmoid function (typical function used for s-curve).

test : search_dichotomic "u {1/(1+exp(-$""1))}",0.15 val=${}

and then:

$ gmic test
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./test/ Set local variable 'val=-1.7421875'.
[gmic]-0./ End G'MIC interpreter.

which is indeed correct:

$ gmic e {"x=-1.7421875;1/(1+exp(-x))"}
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ 0.14903529447863062
[gmic]-0./ End G'MIC interpreter.


@afre to help a little with those questions:

  1. The “structure transfer” does seem to work as described in the paper, certainly if you test with a binary filter used on a guide image. For example try this:

gmic sp teddy n 0,1 +norm otsu. 256 median. 5 +guided. ..,5,0.01

  1. From memory G’MIC uses a box average when downscaling, depending on the interpolation chosen. Perhaps for linear, bicubic and lanczos. DPID apparently uses an inverse bilateral filter, maybe not so easy. Perhaps it could be approximated by a “bilateral sharpen” (see the many tone/detail filters) before downscale. I’ve read downscaling frequently has greater debate than upscale…


@garagecoder Thanks for the example. A while back, I tried using a binary guide but it didn’t work. Could you demonstrate good structural transfer among coloured images? Maybe my expectations are unrealistic. :sunny:

I typically do something like that but not always because it can be a chore to determine how much tone or edge to boost pre and post down sampling.

@David_Tschumperle I don’t know how to adapt y=1/(1+exp(-a*(x-b))) to search_dichotomic, where a is target_y and b is an arbitrary midpoint.

gmic sp tiger n 0,1 f 1/(1+exp(-10*(i-.5))) n 0,1
#' min = 0, max = 1, mean = 0.327845, std = 0.304187


Hmm I’m not so certain the filter is capable of that (no examples in the paper?). “Structure transfer” possibly wasn’t meant in general terms (sounds more like convolution then!).

(David Tschumperlé) #130

Indeed, the most natural way of downscaling an image when some continuous interpolation is expected is with box-averaging, so that’s what G’MIC does. For the other cases (none | nearest | grid), downscaling with no interpolation has also a sense.

Note that it’s then quite easy to create your own downscaling process, basically in two steps:

  1. Locally averaging your image data
  2. Downsizing in nearest neighbors mode.

Like in the example below, where I first apply a bilateral filter to locally average pixel values. As a result, the downsizing is sharper at the image contours, while keeping a smooth look on more flat regions.

$ gmic sp lena .x2 bilateral[2] 1,50 r[1,2] 25%,25%,1,3,1 r[0] 25%,25%,1,3,3 r2dx 512,1 to[0] Linear to[1] Nearest to[2] \"Bilateral + Nearest\" a x o res_downscaling.png

(David Tschumperlé) #131

I assumed you meant “searching for a such that target_y is the average of the processed image”.
Here is a quick attempt, so far it seems to work:

dichotomic_sigmoid :

  # Define the image processing function to consider.
  m "proc : n. 0,1 f. 1/(1+exp(-$""1*(i))) n. 0,1"

  # Find sigmoid parameter, such that processed image average is 0.5.
  search_dichotomic "+proc $""1 res={ia} rm. u $res",0.5 gamma=${}

  # Generate processed result.
  +proc $gamma


$ gmic sp tiger dichotomic_sigmoid mul. 255 to[0] Original to[1] Processed a x o res_sigmoid.png

EDIT: Oups, it doesn’t seem to work when I add -0.5 as a shift for the sigmoid function, I’ll investigate that :slight_smile:

EDIT2: It’s not that surprising in fact. The dichotomic search works only if the considered function is strictly increasing (according to the searched parameter), which is not the fact if we add a negative offset like -0.5 in it, as the plot below shows:

$ gmic plot "'1/(1+exp(-x*(-0.4)))'",1024,1,1,-10,10

What happen if that if your original image, normalized to [0,1] has most of its value below 0.5, then the average of all the applied sigmoid (with a shift of -0.5) will be a decreasing function of the searched parameter, thus making the dichotomic search fail.


This is what I would like to do. Have a curve that increases or decreases contrast and also brightens or darkens depending on where the inflection point is. The point of using a search algorithm is to find parameters that satisfy certain statistics; e.g., mean, std, etc.

This stuff is new to me but it is so interesting and likely applicable elsewhere. :slight_smile:

What I was trying earlier was to generate an s-curve to increase contrast with an inflection point at 0.5 (edit: actually, I normally use ia, which may be more suitable).


You might find this of interest… you can set a desired generalised mean for values 0 < i < 1 with:

set_mean : skip ${1=0.5}
  repeat $! local[$>]
    m "_func : pow -1 sub 1"
    m "_finv : add 1 pow -1"
    m={[im,iM]} n 0.002,0.998 ($1) _func *.. {i/ia#0} rm. _finv n $m
    uncommand _func,_finv
  endl done

where _func and _finv can be defined for the particular mean. Above sets harmonic mean with hyperbolic curve. If you replace with

  m "_func : log"
  m "_finv : exp"

you get a geometric mean (“gamma” curve). Whether there are others for other means which don’t amount to a straight multiplier I don’t know. Usually it’s x^p and the inverse but that effectively multiplies hence no curve. This is based on a map function m(x) = f⁻¹( f(x) * f(d) / average(f(X)) ) for each point x in the set X where d is the desired mean value.


I found the following. Since it goes to ±inf, I have trouble applying it as I have done with the sigmoid.


Here’s another sigmoidal based on (1/x)-1 you can try:

rational_sig : -skip ${1=0.5},${2=255}
  x=0.5 a={($x-$x*$1)/($1-$x*$1)} m={$2/2}
  repeat $! local[$>]
    div $m sub 1 +abs mul. {1-$a}
    add. $a div add 1 mul $m
  endl done

(just one of many sigmoidals, nothing to do with what I posted about setting means!)


@garagecoder I am not exactly a quick study. Could you elaborate on what your filters do? Comments and visuals might help. Thanks.

(Alan Gibson) #137

On S-curves, I implemented a curve that passes through (0,0) and (1,1), with inflection at defined (x,y), a given slope at each end, and given contrast strength. See Clut cookbook and search for “inflection”. I stole the maths from a Luminous Landscape post. Doubtless it could be implemented in G’MIC.


@afre sure no problem. Results of the sigmoid filter at various settings between 0 and 1 (no srgb conversion)… changing the first parameter changes the contrast, the second parameter defines the maximum expected image value (default 255):

gmic sp rose 256,1,1,1,x rational_sig 0.8 dg. 200,200,1


I’ll add info about the “set mean” filter shortly…

The other is a bit harder to explain without boring everyone to death! It lets you choose a new mean (geometric, harmonic, quadratric etc.) using a relevant curve, so long as you set it up. Take the rose sample image, to begin with it has these mean values when in 0 to 1 range:

harmonic mean = 0.050968083153674328
geometric mean = 0.16769733544697091
standard mean = 0.27696302732763656
quadratic mean = 0.36893739428407202

The we choose to have a new harmonic (bottom) and geometric mean of 0.8 (extreme to show the curves):

geometric mean = 0.80000001635936169
harmonic mean = 0.79999999878325623

Floating point kills the accuracy a bit, but it would be exact otherwise.


Source: http://forum.luminous-landscape.com/index.php?topic=52364.0.

Funny how that was exactly what I had in mind but had trouble expressing it coherently and combining the two curves. I still have so much to learn from Guillermo Luijk.

@garagecoder Looks good. Now I have to figure out how to search for the optimum parameter. Since it is an increasing function, I should be able to use search_dichotomic. (I am still more comfortable with my own algorithm, which takes around the same time to run.)

PS I saw your edit. By your wording, I don’t know which image is geometric (top?) and which is harmonic (bottom?).


Yes sorry, typing too fast :slight_smile: harmonic is at the very bottom. The one above it may be familiar as a “gamma” curve, whereas bottom is “rectangular hyperbolic” and not seen as often in tone adjustment.


@garagecoder I wonder whether there is a way to shift the inflection point for rational_sig.

At the moment, I am using the piece-wise strategy suggested by @snibgo. It is good because it is using curves that I have already been using. However, when ab at the inflection point (a,b), I get a kink (or whatever it is called). Maybe I am doing something wrong.


@afre I’m pretty sure it is, it can almost be seen as “piecewise” for positive/negative so you’d only need to scale them to move the midpoint to a new (x,y). Sadly I can’t work on that just now - it’s not difficult, just time consuming!


No worries. Here is a comparison between the two. On tiger, I like gcd's performance when it comes to increasing contrast and pw when decreasing. pw is curvier at the extremes; gcd in the midsection.

(Hope I am not confusing the two, as the results are fairly close, at least for the settings I used for tiger. Also, for pw, I didn’t use the b and s parameters that Luijk, and @snibgo, used because I wouldn’t have thought of them and don’t think I need or understand them yet.)



Bonus: Smoothstep sounds :cool:. I wonder if it is relevant to our discussion. :laughing:

(Alan Gibson) #144

Smoothstep looks useful. Thanks for the link. The inflection is at (0.5,0.5). If you want it at (a,b), simply raise to the power [log(b)/log(a)].