Reptorian G'MIC Filters

I forgotten to announce that I think the Popcorn Fractal issue is resolved or likely to be resolved. If someone ran into a issue, let me know as there is in theory a potential for undefined behavior as it is multithreaded and two threads might hit same coordinates. Now you know.

I will be resuming new filters for gmic in a few days. I was watching the US general election.


Okay, maybe not in a few day. I’m in the process of improving a few filters of mine. Right now, a slight change to rep_form_pixel is coming though with a bit of rewrite to improve performance and enable multithreading. Not much gain in performance, but maybe I’ll be surprised since I am stuck with this laptop. Only .1 s per image faster. I suppose that is better.

C:\WINDOWS\system32>gmic sp lena,cat tic new_rep_form_pixel 5,32,32 toc
[gmic]-0./ Start G'MIC interpreter.
[gmic]-1./ Input sample image 'lena' (1 image 512x512x1x3).
[gmic]-2./ Input sample image 'cat' (1 image 600x550x1x3).
[gmic]-2./ Initialize timer.
[gmic]-2./ Elapsed time: 1.096 s.
[gmic]-2./ Display images [0,1] = 'lena, cat'.
[0] = 'lena':
  size = (512,512,1,3) [3072 Kio of floats].
  data = (225.881,225.881,225.881,225.881,225.881,225.881,225.881,225.881,225.881,225.881,225.881,225.881,(...),81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322).
  min = 22.5919, max = 243.269, mean = 128.241, std = 52.3962, coords_min = (480,32,0,1), coords_max = (463,385,0,0).
[1] = 'cat':
  size = (608,576,1,3) [4104 Kio of floats].
  data = (211.003,211.003,211.003,211.003,211.003,211.003,211.003,211.003,211.003,211.003,211.003,211.003,(...),58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477).
  min = 4, max = 233.933, mean = 112.883, std = 60.041, coords_min = (335,1,0,0), coords_max = (367,225,0,0).
[gmic]-2./ End G'MIC interpreter.

C:\WINDOWS\system32>gmic sp lena,cat tic rep_form_pixel 5,32,32 toc
[gmic]-0./ Start G'MIC interpreter.
[gmic]-1./ Input sample image 'lena' (1 image 512x512x1x3).
[gmic]-2./ Input sample image 'cat' (1 image 600x550x1x3).
[gmic]-2./ Initialize timer.
[gmic]-2./ Elapsed time: 1.343 s.
[gmic]-2./ Display images [0,1] = 'lena, cat'.
[0] = 'lena':
  size = (512,512,1,3) [3072 Kio of floats].
  data = (225.881,225.881,225.881,225.881,225.881,225.881,225.881,225.881,225.881,225.881,225.881,225.881,(...),81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322,81.8322).
  min = 22.5919, max = 243.269, mean = 128.241, std = 52.3962, coords_min = (480,32,0,1), coords_max = (463,385,0,0).
[1] = 'cat':
  size = (608,576,1,3) [4104 Kio of floats].
  data = (211.003,211.003,211.003,211.003,211.003,211.003,211.003,211.003,211.003,211.003,211.003,211.003,(...),58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477,58.9477).
  min = 4, max = 233.933, mean = 112.883, std = 60.041, coords_min = (335,1,0,0), coords_max = (367,225,0,0).
[gmic]-2./ End G'MIC interpreter.

Edit: Now, I managed to get it under .5 s it seems. Will update Tiled Form by tonight.

Ok, I’m not succeeding here. If I use two sample image. Strange things happen. I know it has to do with the final repeat block, but I just can’t seem to point to where.

New rep_form_pixel
#@cli rep_form_pixel: form_id={ 0=australia | 1=barbedwire | 2=circle | 3=crosshair | 4=cupid | 5=diamond | 6=dragon | 7=dragonfly | 8=fern | 9=flip | 10=gear | 11=gumleaf | 12=heart | 13=information | 14=kookaburra | 15=mail | 16=mapleleaf | 17=paint_splat | 18=paw | 19=phone | 20=polygon | 21=rooster | 22=shopping_cart | 23=snowflake | 24=star },form_quad_lx!=0,form_quad_ly!=0,_form_ratio[%]!=0,_angle, 0<=_reflect_dir<=2,_sublevel,_interpolation={ 0=nearest | 1=average | 2=grid | 3=linear | 4=bicubic | 5=lanczos},_tile_boundary={ 0=periodic | 1=mirror },_image_boundary={ 0=neumann | 1=periodic | 2=mirror},_kdol={ 0=default | 1=old_dim | 2=new_layer | 3=new_layer_old_dim },shape_option_1..._shape_option_n
#@cli : _form_id refers to the id or name of shape. circle is a valid form id, and any integer number that is 0-24 inclusive is a form id. You can also use image as an argument, so you can use any arbitrary shapes.
#@cli : _form_quad_lx refers to the size of the tile in pixel form. It cannot be 0 unless you want to use _form_quad_ly variable to define the tile width.
#@cli : _form_quad_ly refers to the size of the tile in pixel form. It cannot be 0 unless you want to use _form_quad_lx variable to define it tile height.
#@cli : _form_ratio refers to the how much the shape fills each tile.
#@cli : _angle refers to the rotation of shapes per tile
#@cli : _reflect_dir refers to the mirroring of each shape. 1,2 means the mirroring is applied.
#@cli : _sublevel refers to the initial size of the shape to be scaled down to fit tiles.
#@cli : _interpolation refers to the interpolation of the scaling of shapes to fit each tile. See 'gmic h resize' _interpolation.
#@cli : _tile_boundary refers to how the tiles are repeated within the canvas
#@cli : _fit_tile is a parameter used for whether to fit tile into specified tile width and height or to use stretch resize =method.
#@cli : _image_boundary refers to how the pixels are treated when out-of-range of original canvas before generating tiles
#@cli : _kdol can be used to resize the generated result to original dimension and/or to create new layer on top of existing image.
#@cli : _shape_option are used to define the behavior of shapes when available.\n
#@cli : Note: _shape_option_1 can be empty. n refers to corresponding shape option.\n
#@cli : Default value: '_form_ratio=1','_ang=0','_reflect_dir=0','_sublevel=.5','_interpolation=2'\n
#@cli : Author: Reptorian.
#@cli : $ sp tiger rep_form_pixel star,32,32,1,45,0,1,5,1,2,1,6
rep_form_pixel:
skip ${3=},${4=},${5=},${6=},${7=},${8=},${9=},${10=},${11=},${12=}

if narg($3)  form_quad_ly=$3 else form_quad_ly=$2 fi
if narg($4)  form_ratio={abs($4)} else form_ratio=1 fi
if narg($5)  ang=$5 else ang=0 fi
if narg($6)  mirror={abs($6)%3} else mirror=0 fi
if narg($7)  sub={abs($7)} else sub=.5 fi
if narg($8)  interpolation={abs($8)} else interpolation=5 fi
if narg($9)  tile_boundary={abs($9)} else tile_boundary=0 fi
if narg($10) fit_tile={abs($10)} else fit_tile=1 fi
if narg($11) __image_boundary={abs($10)} else __image_boundary=2 fi
if narg($12) __var_kodl={round(abs($11))%4} else __var_kodl=0  fi

__tw=abs($2)
__th=abs($form_quad_ly)
if $__tw&&$__tw<4 __$tw=4 fi
if $__th&&$__th<4 __$th=4 fi
if !($__tw||$__th) error "\$__tw||\$__th==F"  fi
__tw={!$__tw?$__th:$__tw}
__th={!$__th?$__tw:$__th}
sub+=1
interpolation+=1
__image_boundary+=1

mw=${-max_w}
mh=${-max_h}
nw={ceil($mw/$__tw)*$__tw}
nh={ceil($mh/$__th)*$__th}

imgs={$!}

if ${is_image_arg\ $1}
 pass$1 0
 if s<=4&&s>1
  if s==3
   to_gray.
  else
   s. c,{if(s==4,-3,if(s==2,-1,-s))}
   to_gray..
   *[-2,-1]
  fi
 elif s==5
  s. c,-4 cmyk2rgb.. to_gray.. *[-2,-1]
 fi
else
 sid="$1"
 if isnum($1) if isint($1)
  sid=${"-arg "1+$sid"","australia,barbedwire,circle,crosshair,cupid,diamond,dragon,dragonfly,fern,flip,gear,gumleaf,heart,information,kookaburra,mail,mapleleaf,paint_splat,paw,phone,polygon,rooster,shopping_cart,snowflake,star"}
 fi fi
 if $#>12 shape_$sid {max($__tw,$__th)*$sub},${13--1}
 else shape_$sid {max($__tw,$__th)*$sub}
 fi
 r. {ceil(w/$__tw)*$__tw},{ceil(h/$__th)*$__th},100%,100%,0,0,.5,.5
fi

if abs($ang)%360!=0 rotate. $ang,1 fi

if   $mirror==1 mirror. x
elif $mirror==2 mirror. y
fi

n. 0,1 autocrop. 0

if $fit_tile
 shape_image_ratio={w#-1/h#-1}
 target_image_ratio={$__tw/$__th}
 resize_width={$target_image_ratio>$shape_image_ratio?w#-1*($__th/h#-1):$__tw}
 resize_height={$target_image_ratio>$shape_image_ratio?$__th:h#-1*($__tw/w#-1)}
 r. {$resize_width*$form_ratio},{$resize_height*$form_ratio},1,1,{$interpolation},0,.5,.5
 r. $__tw,$__th,1,1,0,0,.5,.5
else
 r. {w#-1*(1/$form_ratio)},{h#-1*(1/$form_ratio)},1,1,0,0,.5,.5
 r. $__tw,$__th,1,1,{$interpolation},0,.5,.5
fi

cut. 0,1 n. 0,1

if $tile_boundary==0
 r. $nw,$nh,100%,100%,0,2
elif $tile_boundary==1
 r. $nw,100%,100%,100%,0,3
 r. 100%,$nh,100%,100%,0,2
elif $tile_boundary==2
 r. 100%,$nh,100%,100%,0,3
 r. $nw,100%,100%,100%,0,2
elif $tile_boundary==3
 r. $nw,$nh,100%,100%,0,3
fi

avgc={avg(crop(#-1,0,0,$__tw,$__th))}

+negate.

repeat $!-2

 rw={ceil(w#$</$__tw)*$__tw}
 rh={ceil(h#$</$__th)*$__th}
 
 if $__var_kodl>1
  if $__var_kodl==3 +store[$<] kodl_image fi
  ow={w#$<}
  oh={h#$<}
  r[$<] $rw,$rh,100%,100%,0,$__image_boundary,.5,.5
 elif $__var_kodl==1
  r[$<] $rw,$rh,100%,100%,0,$__image_boundary,.5,.5
  +store[$<] kodl_image
 else
  r[$<] $rw,$rh,100%,100%,0,$__image_boundary,.5,.5
 fi
 
 ttw={w#$</$__tw}
 tth={h#$</$__th}
 +*[$<] [-2]
 +*[$<] [-2]
 $ttw,$tth,2,{s#$<}
 
 repeat s#$<
  ci={$>}
  repeat 2 sh... $ci done
  sh... $ci
  f. "
  begin(
  const avg_c="$avgc";
  const tw="$__tw";
  const th="$__th";
  const wwhh=tw*th;
  const w_avg=wwhh*avg_c;
  const iw_avg=wwhh-w_avg;
  );
  if(z
  ,sum(crop(#-3,x*tw,y*th,0,0,tw,th,1,1,1))/w_avg;
  ,sum(crop(#-2,x*tw,y*th,tw,th))/iw_avg;
  );"
  rm[-3--1]
 done
 f[$<] "begin(
  const tw="$__tw";
  const th="$__th";
 );
 posx=int(x/tw);
 posy=int(y/th);
 lerp(i(#-1,posx,posy,0),i(#-1,posx,posy,1),i0#1);
 "
 rm[-3--1]
 if $__var_kodl
  l[$<]
   if $__var_kodl==1 $kodl_image
   else
   if $__var_kodl==3 $kodl_image fi
   r $ow,$oh,100%,100%,0,0,.5,.5
   fi
  endl
 fi
done 

rm[-2,-1]

How do I hide this again?

“Spoiler” → “details”

Thank you very much. Upon looking for the solution to my problem. I know that somewhere, somehow I screwed up. But it doesn’t make sense at all.

To explain :

If I type in return bottom to +*. lines. I should be able to get the 2 images of dog multiplied by the last two image. Instead, I’m getting something strange.

A small sample of code that can help me.

sp dog,cat,lena
+f. x/w channels. 0
+negate.
repeat $!-2
repeat 2
+*[$<] ..
done
return
done

What’s the problem here?

Using +*[$<] .. twice just gets me a funny color image which is the exact opposite of what I want.

Edit: I think I know why. If I have a tile of shape with only one channel next to a sample image. Then multiply the sample by tileset, I get a funny image. Such a shame because if it did work as expected, I would trim off so many time to compute result.

EDIT: I looked at technical reference. I think it is a bug unfortunately.

I don’t really get what you intend to do with your code.
Not tested thoroughly, but a construct like:

  repeat P
    repeat Q
      ...
    done
    return  # <- WTF?
  done

does not look good at all!

I left return there as a way to debug actually. I figured the problem has to do with multiplying based on two different size. I was expecting something like for x in first.image.width, for y in first.image.height, multiply first pixel by second image pixel.


After figuring it out, I upgraded Tiled Form/rep_form_pixel. It’s not exactly the best theoretical possible speed, but it is what I can do within g’mic limit. The best theoretical speed from what I recall seems to be .089 s, but that is if only mul command works with different size like I expected it to (it doesn’t, but not a bug either).


I think I realized how to do max, min on specified areas in context of rep_form_pixel. Right now, there’s only weighed average mode. If it turns out to be interesting, then I will update it, and I expect it to be the case that it is interesting. However, I’m wary of color space related issue with min, max, so it might have to be limited with rgb,rgb,cmyk in those cases.


This might take a while as I will factor into alpha channel and cmyka. Thanks to the help of @David_Tschumperle, I managed to implement max mode for rep_form_pixel.

See this thread for details: Return maximum value coordinates based from the sum of channel in a window in context of fill block. - #6 by David_Tschumperle

Left - Original ; Middle Left - Minimum ; Middle Right - Maximum ; Right - Average

This reminds me what you can get with the commands: blend and blending modes shapeaverage, shapemin, shapemax and shapemedian.

$ gmic sp lena shape_star 24 ri. ..,0,2 +blend shapeaverage0

1 Like

Shape median seem to be my workaround for getting median. My command utilize interpolation within edges of shape to define colors. Weighed average, min, and max are possible. Weighed median does not seem to be possible. To figure that out, well…

If you figure out (adaptive) weighted median let me know (or vector median). I haven’t been able to do anything productive in months.

@David_Tschumperle @afre

I’m wondering if there should be a new math expression called weighed_median which takes in even number of arguments. First half are values, and second half is the weigh. That could help us both. Or a vector on first argument and weighed values on second. Something that helps factor in color set instead of just channel by channel.

You should be able to do it already, with :

wmed = med(weights*values);

where weights and values are two vectors with same size.

Or do you need something different ?

Gonna work on Tiled Form later.

I wish I knew how to produce the distance at point spiral - Fast spiral - Variation 8 - Seamless Tiling (Reference). Would be nice for a new spiral distortion.

EDIT: I asked MJW at paint.net forum for this - and there is a code where I can work with - Rectangle to Archimedean Spiral (Beta) - Plugin Developer's Central - paint.net Forum


Added a small feature to rep_thorn_fractal - gmic-community/reptorian.gmic at master · dtschump/gmic-community · GitHub

If you are using overload functions, to activate the small new feature, just make sure the last variable is a negative number.

Basically here how it works. Let’s say you have alternating formula based on a variable that increments with modulus of a specific number (variable is altern in context of rep_thorn_fractal). There’s variables that are calculated within a position of the increment variable, and these variables are cfa,cfb.

With positive number:
[v_x][v_y][v_x2][v_y2] → Calculate cfa,cfb [ovx][ovy]. If you use cfa, and cfb. Your cfa, and cfb number are found before applying the overload function.

WIth negative number:
[v_x][v_y][v_x2][v_y2][ovx][ovy]-> Calculate cfa,cfb. If you use cfa, and cfb. Your cfa, and cfb number are found before applying the overload function.

I will add this to GUI soon.

So, what am I doing now?

3 Likes

You mentioned that the simple case of a circle can be done with atan2(). Could you demonstrate how that should look? It may be trivial, but I think such an example would go a long way to understanding the end goal…

Moving my response here.

I suggest you work backwards from your desired result, or break the problem into parts. You know what the gradient fill should look like in the spiral. This is my current thought process:

1 Do you know how to draw a spiral such that its line intensity changes according to its arc length? That is, black at the beginning and ever lighter as it unravels.

2 The gradient would be composed of n normal cords (where n is also the arc length). The cords are isolines (contain pixels of equal value, at least if we are talking about a ramp).They lengthen as we go further along the curve. Since they take their values from later points of the curve, they would have to interpolate with their neighbours to connect to an earlier point.

Even though @Thanatomanic answered the question, I am going to wait for a closed-form solution to continue the filter as I still don’t know how to get the answer with x,y coordinates or in polar coordinate where start of spiral is at 0,0.

Besides that, I think I can finally convert rep_stitch into a more sensible code.

Needless to say, I wish I had something new to show, but I’m in a refactoring stage for my code.

This means:

  1. Multithreading filters whenever possible. I attempted to multithread rep_sptbwgp, but turns out that it slowed it by half. Maybe parallel_for would help, but that’s not available. However, Popcorn Fractal is a succcess.

  2. Shortening code to make it more understandable.

  3. Making it easier to convert to other programming languages.

EDIT: It looks like my desktop is back. Finally, I think I can do things easier again.

EDIT: Holy moly, I think I found a fast Popcorn Fractal. Now less than 2 s! I do think that now that I copied and edit from a really old code, it needs a little bit more adjustment. Set the density back to 1 during the mean time.

EDIT: Finished refactoring Popcorn Fractal, what will be next is refactoring Stitch now that I figured out how I pulled it off. It is however admittedly tricky to refactor this.

Since the equation to solve is transcendental, it is highly unlikely there will be a simple solution.

1 Like

He does have the habit of entering transcendental planes.

1 Like

Yep, and often sometimes knowingly I might make a mistake or it’s a subject way out of my league. Like forgetting to save negative overload function which actually makes the thorn fractal more interesting, I ended up just leaving what I got there which points to that direction knowing it may not be solved but still does the description. This spiral distortion case is way out of my league it seems.

Maybe later…


12/12/2020: I found a new algorithm for Fragment Blur in case of alpha channel with variance. I think there might be a issue there, but honestly I doubt it.

EDIT: Just checked, no issues. New algorithm worked as expected.


I just had upgraded rep_form_pixel and its gui filter Tiled Form!

New features : Inherent color space conversion support, and z-convolution.

z-convolution options are used to boost contrast of tiles. Color space conversion option enables users to use other color space while minimizing artifacts.

New filter! Dynamic Contrast. It is pretty fast as well as I optimized it even though it doesn’t need it. Lowest time is 0.005 s.

2 Likes