G'MIC exercises

I guess these two options are equivalent. It highly depends on what you want to do with your data afterwards. Copying image data in an image on the stack could be also on good solution if you want to apply some complex operations that e.g. exist as a G’MIC command in an optimized way (e.g. resize, that you can call with an ext() in the math parser).

Yes, if you treat the neighborhoods as vector themselves, it is quite easy (and prob. efficient) to do this, e.g.

N = 0.1*crop(x-2,y-2,z,0,5,5,1,1) + 0.2*crop(x-2,y-2,z,1,5,5,1,1) + 0.3*crop(x-2,y-2,z,2,5,5,1,1);

will compute a weighted sum of R,G,B neighbor data.
Using a single crop would be also possible, then multiply it by a specific matrix.

1 Like

@David_Tschumperle below is a geometric median based on a modified Weiszfeld algorithm. Do you have some ideas for speeding it up? It’s ok for a prototype but still painfully slow for even medium size images:

geometric_median : skip ${1=3},${2=12}
  repeat $! l[$>]
  [0] +boxfilter. {$1+1-$1%2} sh. 0
  f. "
    init(
      const boundary = 1;
      const N = int($1/2);
      const W = N+1;
      weightedSum = I(#1);
      totalDist = weightedSum;
    );

    Y = I(#2,x,y); # centroid
    
    for (iters = 0, iters<$2, ++iters,
      weightedSum = 0;
      totalDist = 0;
      distSum = 0;
      flag = 0;
      
      for (j = -N, j<W, ++j,
        for (k = -N, k<W, ++k,
          X = J(#1,j,k);
          diff = X - Y;
          dist = norm2(diff);
          if(dist==0, flag=1,
            R = 1/dist;
            weightedSum += R * X;
            totalDist += R * diff;
            distSum += R;
          );
        );
      );
      
      if(totalDist!=0,
        bal = flag ? 1/norm2(totalDist) : 0;
        Y = max(0, 1 - bal) * weightedSum / distSum + min(1, bal) * Y
      )
    );
    
    I(#0,x,y) = Y; 1" k[0]
  endl done

Motivation: an example of comparison with “normal” median (original, median, geometric median):
testmedian

It can be seen a normal median produces “false” colours at certain edges, geometric doesn’t.

Edit: there’s an alternative version of that paper which has a nicer representation of the mapping function (taken from citations of this wikipedia page).

Edit2: spotted one obvious change - amended slightly!

1 Like

Result is pretty cool, GC. Can see such a use to uprez icons and clipart. :slight_smile:

I’ll take a quick look today.
Probably one good thing to add is a * as the first character of the fill expression: it will ensure multi-threading is activated, which seems to be possible here.
Usually, the G’MIC math parser tries to “auto-guess” if an expression can be evaluated in multi-threaded mode, but if the expression contains instructions to explicitly write pixels in images (as I(#0,x,y) = Y; ), then multi-threading is usually disabled by default, as it could lead to undefined behaviors.
In your case anyway, activating the multi-threading seems to be OK as you won’t write a color in any other location than (x,y).
Doing this already boosts the speed in a visible manner.

Just looked to your code, and I must admit I don’t have ideas right now to improve it.
What I see is that you have three nested loops : 12 iterations of (x,y) loops over all the image, I’m not really surprised this is somehow “slow” to compute, particularly if you think that at the end it is evaluated by a kind of ‘bytecode’ interpreter (the math expression is ‘compiled’ into bytecode, but the bytecode sequence itself is interpreted).

I wonder what @garagecoder is up to. Maybe he is a Nintendo dev. :stuck_out_tongue:


I still have a backlog of loose ends to look into in this thread, but I already have two questions that I have been waiting a very long time to ask.

1. In the paper, from what I could gather, the guided filter (which I love to use) could be used to transfer structures from image a to image b. My issue with the current implementation is that it is a blurring filter first. Any structure transferred is lost to the blur even when I use 1,1e-10. (As an aside, I see that apps like RawTherapee use radii that are less than one. Is it enlarging, filtering, then shrinking the image?) Another application is to refine masks. Again, I don’t know how to do what the paper shows.

2. Another question surfaced from my involvement with the PlayRaws. Take the recent one e.g., [PlayRaw] View from Fort Carré in France. There is text on the crane. When I down sample the image, it turns into an indiscernible blob. It looks like there are two groups that are equivalent: 1,4 and 2,3,5,6.

1=nearest | 2=average | 3=linear | 4=grid | 5=bicubic | 6=lanczos

Looks like IM has more possibilities but my head hurts from the reading.
Resizing or Scaling -- IM v6 Examples
Resampling Filters -- IM v6 Examples
Nicolas Robidoux Resampling -- IM v6 Examples

I also found this paper that uses what is called detail-preserving image downscaling (DPID) from another paper. Their implementation doesn’t look complicated but I don’t have the math or dev chops to figure it out. :blush:

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