G'MIC exercises

@David_Tschumperle one possible speedup is more to do with the algorithm. Because some vectors can converge early it will be simple to add an early exit from the iterations where totalDist = 0 (may have some precision problems to deal with). Thanks for the tip about threads!

Edit: indeed G’MIC is in no way to blame for the speed. With the “early exit” added it’s doing 7x7 12 iters of the “parrots” sample in under 30s on this ageing core2. For a JIT interpreter this is actually amazing!

@afre sorry, I shouldn’t have hijacked the thread this way… next time I’ll start a new post. If I have time at the weekend I’ll have a look at your questions (can’t promise good answers though :slight_smile: ). My time in game dev is long past, ETL/databases is my thing now!

No problem. It isn’t off topic, actually. :sunny:

@garagecoder, I’ve moved your nice geometric median in category ‘Repair/’. Thanks for this cool contribution ! :beers:

2 Likes

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

s-curve

(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…

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…
1 Like

@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!).

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

res_downscaling

1 Like

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

Then,

$ 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

gmic_negplot

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.

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

rose_sigmoid

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

Edit:
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):
rose_set_mean

geometric mean = 0.80000001635936169
harmonic mean = 0.79999999878325623

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

3 Likes

Source: HELP: Equation for a Contrast Curve (with precise location of the turning point).

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.