Reptorian G'MIC Filters

Optimized Fragment Blur has been released. I don’t know if it works though. At the cli level, I think it does. For GUI, I don’t know as I only have access to 2.9.9 for the GUI version. The changes can be from 120% to 715% faster. Dependent on setup.

1 Like

Earlier post is deleted. Now, I had upgraded Emboss-Relief! I also adjusted Construction Material Texture in light of the update though I don’t know if it’ll work since I don’t have 3.0.0. If you could test it, that’d be wonderful.

@samj Now, it has been upgraded. Consider replacing the one you have with this one.

Here’s the changes:

  • Faster Processing. 1D convolution, and the plus prepend operator. Also, several command command tricks as they’re a fancy goto. Yes, I know gotos aren’t recommended.
  • Slightly more flexible in output. Not just color is changed.
  • Color Output is enabled.

I have something interesting here:

$ 512,512 rep_mt_test
rep_mt_test:
seed={u(0,1)}

{w*2},{h*2},$_cpus,4

half_diag={norm(w,h)/2}

pal 150

$_cpus,1,1,1,:"begin(
  srand($seed);
  diagonal(a,b)=sqrt(a^2+b^2);
  const ss=s#-2;
  const nc=w#-1;
  const nt=$_cpus;
  const tau=2*pi;
  const nt_mi=nt-1;
  const ww=w#-2;
  const hh=h#-2;
  const mx=ww-1;
  const my=hh-1;
  const max_dist=diagonal(mx,my);
  const min_dist=max_dist*.1;
  const steps=max(2,50);
  const div_step=steps-1;
  const radius=15;
  const length=sqr(radius)*ss;
  const off=ceil(radius/2);
  md_arr=expr('x',nt); # Matrix to define where to draw circle / Matriz para dibujar circulos
  fy_shuffle()=(    # Fisher-Yates Shuffle md_arr
   for(p=nt_mi,p>0,p--,
    r_ind=int(u(0,nt));
    swap(md_arr[p],md_arr[r_ind]);
   );
  );
  opacity=expr('
   begin(
    const half_dist=(w-1)/2;
    remap(a)=(a-half_dist)/half_dist;
   );
   xx=remap(x);
   yy=remap(y);
   norm(xx,yy)<=1;'
  ,radius,radius,1,1);
  paint_circles()=(
   paint_color=[I(#-1,int(u(nc)),0,0),255];
   sqr_col=resize(paint_color,length,1,0);
   ang=u(tau);
   dist=u(min_dist,max_dist);
   step_dist=dist/div_step;
   shift_x=cos(ang)*step_dist;
   shift_y=sin(ang)*step_dist;
   pos_x=int(u(ww));
   pos_y=int(u(hh));
   repeat(steps,
    draw(#-2,sqr_col,pos_x-off,pos_y-off,md_arr[x],0,radius,radius,1,ss,1,opacity,1);
    pos_x+=shift_x;
    pos_y+=shift_y;
   );
  );
  fy_shuffle();
 );
 repeat(20,
  critical(fy_shuffle(););
  paint_circles();
 );"

l... s z blend alpha endl

The big block of code inside rep_mt_test draws lines in parallel at random z using an array with unique integer number. Is this thread-safe? The reason I asked is because I want to draw lines of circles at different zs, and no threads should be drawing on the same z. Print only outputs md_arr once, so I can’t tell what it is on different threads.

To explain some more. fy_shuffle() shuffles an array which is used to define where to draw the lines of circle. And paint_circles() draws lines of circles.

And for those who’s wondering what is this test code for, I’m basing it off the principles found in this plugin for PDN - TR's iPollock (March 3rd 2015) - Plugins - Publishing ONLY! - paint.net Forum

Output:

I added this to the end of the code.

end_t(
  print(md_arr);
 );

Results shows that it isn’t thread-safe. :confused: I wanted to do multi-threaded drawing always at different z at different threads.

md_arr is supposed to be the same in all threads.


EDIT: Never mind, now I got it.

New Test Code
rep_mt_test:
nt,num_rep,pal_id,min_perc,steps,radius={$_cpus},90,150,{cut(.1,0,1)},{max(2,50)},15

$nt,1,1,1,x nm. pz
$nt,1,1,1 nm. swapper

pal $pal_id

m "rpz : eval. >r_ind=int(u(0,"$_cpus"));swap(i(#-2,x),i(#-2,r_ind));"
m "flatten : l. s z blend alpha endl r. {w#0},{h#0},100%,100%,3 sh. 0,2 sh.. 3 /. 255 /.. . *. 255 rm[-2,-1]"

repeat $!-3 l[$>,-3,-2,-1]

 {w#0*2},{h#0*2},$nt,4
 
 repeat $num_rep
  rpz[pz,swapper]
  
  eval[pz] :"begin(
    diagonal(a,b)=sqrt(a^2+b^2);
    const ss=s#-1;
    const nc=w#-2;
    const nt=$nt;
    const tau=2*pi;
    const nt_mi=nt-1;
    const ww=w#-1;
    const hh=h#-1;
    const mx=ww-1;
    const my=hh-1;
    const max_dist=diagonal(mx,my);
    const min_dist=max_dist*$min_perc;
    const steps=$steps;
    const div_step=steps-1;
    const radius=$radius;
    const length=sqr(radius)*ss;
    const off=ceil(radius/2);
    opacity=expr('
     begin(
       const half_dist=(w-1)/2;
       remap(a)=(a-half_dist)/half_dist;
      );
      xx=remap(x);
      yy=remap(y);
      norm(xx,yy)<=1;'
      ,radius,radius,1,1);
    paint_circles()=(
     paint_color=[I(#-2,int(u(nc)),0,0),255];
     sqr_col=resize(paint_color,length,1,0);
     ang=u(tau);
     dist=u(min_dist,max_dist);
     step_dist=dist/div_step;
     shift_x=cos(ang)*step_dist;
     shift_y=sin(ang)*step_dist;
     pos_x=int(u(ww));
     pos_y=int(u(hh));
     repeat(steps,
      draw(#-1,sqr_col,pos_x-off,pos_y-off,i,0,radius,radius,1,ss,1,opacity,1);
      pos_x+=shift_x;
      pos_y+=shift_y;
     );
    );
   );
   paint_circles();"
 done
 flatten
endl done

um rpz

1 Like

Looks like another pause to another script of mine. This one, well, I wish I knew how to calculate length of cubic spline. There is 5 point langrage formula, but I don’t know how to use it.

Here’s another:

Based on this idea: Hitomezashi Stitch Patterns - Numberphile - YouTube

And here’s the original spreadsheet code - Mobile Numbers: Hitomezashi Stitching | The Aperiodical (Found in bottom of blog post)

+rep_binary_hitomezashi:
skip "${3=}" #If there is no 3rd argument, then $3 is treated as null. So therefore, 2 arguments.

if narg($3) pass$3 1 # If there is a 3rd argument
else
 # Vowels as 1 is part of the rule. 
 # Whether number is even is also part of rule. 
 # Emoticons as 1 is part of rule. 
 # There's some few more arbitrary rule.
 (0,1,1,1,1,1,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,0,1,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,0,1,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0)
fi

('"$1"') # Get array of unicode value of input 1
('"$2"') # Get array of unicode value of input 2

f[-2,-1] i(#-3,i) # Convert unicode image/array to binary with rules.

{w#-1},1,1,1,>begin(v=0;);v=x?(j(#-1,-1)+v)%2:0; rm.. # Process first column. Creates auxilatory surface and then remove reference surface.

{w#-2},{w#-1},1,1 # Create a 2D surface to write on

eval.. "
 v=i(#-1,0,x)=i;
 row=x+1;
 repeat(w#-3-1,p,
  v=i(#-1,p+1,x)=sum(v,i(#-3,p,0),row,1)%2;
 );" #Generate value from spreadsheet rule.

rm[-4--2] # Keep only the Hitomezashi Pattern

image

4 Likes

You could approximate curve length with a series of line segments. Your (approximate) length is the sum of line segments drawn between curve plots.

Not exact, of course, but the approximation improves with increasing plot counts.

The rabbit to catch is a G’MIC-cal way to plot a Bézier curve. Here's how I would lay the trap.

TL;DR

  1. Usual Math Functions: lerp(a,b,t)→(1-t)*a+t*b, 15th bullet point down.
  2. De Casteljau's algorithm

Details
Apologies for the slovenly diagram.
post_01
A Lerp is a unit pyramid
The key idea is a linear interpolation between two points, samples, data items — at this level of generality, take your pick. This is a “lerp.” A lerp takes a weighted average of two given data items, a and b, at a parametric value of, say, t=0.6. A lerp of a and b is just ‘a*(1-0.6) + b*0.6’, or, using G’MIC's math function:

$ gmic a=27.6,34.2 b=9.875,4.75 param=0.6 aprime='{lerp([$a],[$b],$param)}'
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Set local variable 'a=27.6,34.2'.
[gmic]-0./ Set local variable 'b=9.875,4.75'.
[gmic]-0./ Set local variable 'param=0.6'.
[gmic]-0./ Set local variable 'aprime=16.965,16.530000000000001'.
[gmic]-0./ End G'MIC interpreter.

Diagrammatically, we might depict this as a an enactment of the “1st degree pyramid algorithm”, (see slovenly diagram), aka a lerp between points a and b and producing a weighted average point, a'. In numbers, the point a' is 3/5 of the way from point a, (x=27.6, y=34.2) to point b, (x=9.875,y=4.75), (x=16.965, y=16.53), point a'. If we do this for a lot of distinct parametric samples, we could plot the path of a' and make an animation of it:

Bezier_1_big
Author: Phil Tregoning, Wikimedia Commons

Well, um, OK. This is all just a linear interpolation between a and b. (P0 and P1 in Phil’s animation). But here is the kicker: Who’s to say that a and b aren't motionless? Could it be that a is actually an a' transiting in a lerpish kind of way between another a and b, and that b is also, actually, a b' transiting in a lerpish kind of way between b and c, and that our resultant is really a''? That is, we’re stacking a lerp on a pair of lerps: enacting a 2nd degree pyramid algorithm. Time for another slovenly diagram:
post_02
A stack of lerps

This added layer of lerps finds a' traversing between a and b, a linear path, as well as b' traversing between b and c, another linear path. But we also have a'' lerping betwixt a' and b', both endpoints undergoing linear traversals as a' traverses between them. So what does _a''_s traversal path look like?
Bezier_2_big
Author: Phil Tregoning, Wikimedia Commons

Algebraically collapsing the pyramidical mesh of lerps finds the straight polynomial relation in t and control points a, b, and c, a quadratic:
post_05

We can pile lerps arbitrarily high for any degree Bézier curve, not just linear, quadratic or cubic flavors. Now, since I haven't run out of slovenly diagrams yet, here's the pyramid for a fourth degree quartic Bézier, which takes five control points, a, b, c, d and e and bootstraps it's way up to a'''', the plot of a point on the quartic Bézier curve at some parameter t, drawn from the closed unit interval [0…1]:


Pyramid for a 4th degree Bézier curve

I've labeled this somewhat differently, marking all the places where a G'MIC ‘lerp(a,b,t)’ enters into play with various data items and their primes.

Now, if we look at this diagram sideways and squint a little bit, we can maybe see a way to dress it up G’MICally.

  1. Data structure: an array of arrays. Outer array length (width) equals the number of control points we care to deal with. Say, four for cubic Béziers, five for quartics, three for quadratics. Inner array length (depth) equals the spatial dimensions of the space where we plot points, two for surfaces, three for volumes and so forth. Let me label the elements of the outer array a, b, c, d, e instead of 0, 1, 2, 3 and 4.

  2. Process: For a given parametric value, t, taken from the closed interval [0…1]
    a. Bottom rank:
    a.1 ‘lerp(a,b,t)’, put the results, a' in array position a; we won’t use a again and can overwrite it.
    a.2 ‘lerp(b,c,t)’, put the results, b' in array position b; we won’t use b again and can overwrite it.
    a.3 ‘lerp(c,d,t)’, put the results, c' in array position c; we won’t use c again and can overwrite it.
    a.4 ‘lerp(d,e,t)’, put the results, d' in array position d; we won’t use d again and can overwrite it.
    b. Next rank up and each rank following to the top of the pyramid: Ditto the bottom rank, but, for the second rank, only use array elements a, b, c, and d, which now contain lerps a', b', c', and d'. We ignore array element e. We don’t need it any more.

Overall, the game is: pair-wise lerp array elements from left to right, putting the results in the left hand slot of the current pair, and then bootstrap up to the next rank, where we do it again, removing the rightmost array element from the computation and pair-wise lerping the remainder. At the top of the pyramid, we ‘lerp(a''',b''',t)’ to produce the point a'''' on the quartic curve corresponding to parametric value t. For the visually-oriented among you, all of this is just following the network of computations depicted in my previous slovenly diagram.

Here is an implementation:

bezlength.gmic
bezlength:
   check "isint(${1=10}) && ${1}>0 && isint(${2=512}) && ${2}>128"
   plotcount=$1
   imgsz=$2
   if size([$[]])==0
      # Default control point set; format: # xxxxx…^yyyyy…
      (0.0;-0.75;-0.9;0.1;0.9;0.95;0.6^0.0;-0.675;-0.9;-1.0;-0.9;-0.5;0.4)
   fi
   nm. cpoints
   permute[cpoints] xczy
   (0;1)
   nm. param
   r. 1,$plotcount,1,1,3
   $imgsz,$imgsz,1,1
   nm. canvas
   bzcl={"
           CP=crop(#$cpoints);
           T=crop(#$param);
           const vl=h#$cpoints;
           const ccnt=s#$cpoints;
           const pcnt=h#$param;
           const psz=pcnt*vl;
           const sspsz=pcnt*(vl+1);
           const sscpsz=ccnt*(vl+1);
           const slsz=pcnt-1;
           const rcnt=ccnt-1;
           P=vectorpsz(0);

           # Traverse pyramid; find plot points residing on Bézier
           repeat(
                     size(T),i,
                     LCP=CP;
                     t=T[i];
                     repeat(
                              rcnt,j,
                              repeat(
                                       rcnt-j,k,
                                       PT=lerp(LCP[vl*k,vl,1],LCP[vl*(k+1),vl,1],t);
                                       repeat(vl,m,LCP[vl*k+m]=PT[m])
                                    )
                           );
                     repeat(vl,m,P[vl*i+m]=LCP[m])
                 );

           # 1., 2., not relevant to pyramid alogorithms; these draw diagnostic
           # curves on the canvas and may be omitted, but retain 3.  

           # 1. Homogeneous Screen xform: 2 × Unit square, centered origin → canvas image
           specw=h#$canvas/2;
           id=eye(3);
           id[0]=specw/1.01;
           id[1]=0;
           id[2]=specw;
           id[3]=0;
           id[4]=-specw/1.01;
           id[5]=specw;

           # 2. Draw Bézier curve on canvas 
           SSP=vectorsspsz(0);
           repeat(pcnt,k,SSPPT=id*[P[vl*k,vl,1],1];repeat(vl+1,m,SSP[k*(vl+1)+m]=SSPPT[m]));
           SSCP=vectorsscpsz(0);
           repeat(ccnt,k,SSCPT=id*[CP[vl*k,vl,1],1];repeat(vl+1,m,SSCP[k*(vl+1)+m]=SSCPT[m]));
           repeat(pcnt-1,k,pt0=SSP[k*(vl+1),2,1];pt1=SSP[(k+1)*(vl+1),2,1];polygon(#$canvas,-2,[pt0,pt1],1,0xffffffff,255));
           repeat(ccnt-1,k,pt0=SSCP[k*(vl+1),2,1];pt1=SSCP[(k+1)*(vl+1),2,1];polygon(#$canvas,-2,[pt0,pt1],1,0xffffffff,127));
           repeat(ccnt,k,pt0=SSCP[k*(vl+1),2,1];ellipse(#$canvas,pt0,2,2,0,1,190));

           # 3. Find and export curve length: diff plots; find lengths; sum lengths.
           DIFF=P[vl,(pcnt-1)*vl,1]-P[0,(pcnt-1)*vl,1];
           SLEN=vectorslsz(0);
           fill(SLEN,k,norm2(DIFF[vl*k,vl,1]));
           sum(SLEN)
        "}
   text[canvas] "Est. curve length: "$bzcl", Samples: "$plotcount".",5%,5%,15,1,255
   rm[^-1]

And here’s some examples. The approximate length of a Bézier curve, at various resolutions (first argument - plot count, second argument: plotting image size):

bezlen_5
gmic bezlength.gmic (-1;-0.5;0;0.5;1^0;0.8;0;-0.625;0.7) bezlength. 5,512

bezlen_10

gmic bezlength.gmic (-1;-0.5;0;0.5;1^0;0.8;0;-0.625;0.7) bezlength. 10,512

bezlen_20

gmic bezlength.gmic (-1;-0.5;0;0.5;1^0;0.8;0;-0.625;0.7) bezlength. 20,512

bezlen_500

gmic bezlength.gmic (-1;-0.5;0;0.5;1^0;0.8;0;-0.625;0.7) bezlength. 500,512

Convergence to a useful results does not take too many samples for uncomplicated curves such as this. Convergence slows with more control points. Spline length follows from the approximate lengths of the constituent curve segments, added together.

Hope this helps.

3 Likes

I’ll keep that in note.

More work on the Hitomezashi project:

image

That’s a beauty.

Leaving this as backup for fix later

gmic bin=111000011111100011000001110100110000001110011000010010001111101001110000011100010010011011100010 +rep_binary_hitomezashi $bin,$bin pal steamlords,wcmyk tic +_rep_colorize_binary_hitomezashi[0] [-2],[-1] toc r. 200%,200%,100%,100%,0,3,0,0
1 Like

Unfortunately, the pattern is repetitive and less distinct about 75% to the edges from the centre.

You can have distinct output:

But, yeah you’re right that it looks more uniform when the number of colors are less than the number of shapes with areas greater than 1.

Here’s something else:

image

image

2 Likes

Just dropping this here. I made it animate-able. Only thing left to do is to allow users to have a upscaled version, and whether borders are there.

2 Likes

Is there already a command that does this? I’m aware of the resize and ax/ay/az/ac options , but that creates 2 pixel border on the center. This command I created solves it and these are the four options. I’ll push it though I"ll remove it if this is already implemented. My hitomezashi will be using this command.


With the new rep_symmetrize_xy, I used it on the hitomezashi.

Where is the 2 px border? Do you mean between the square patterns in the second image? That you could get rid of if you do some preprocessing before appending.

The 2 px border refers to what happens when you use only resize to stimulate symmetry in the fashion shown above. Zoom in the center after doing the command below. I have made my own command which does the same, but without it, and pushed it recently, but wanted to know if there was another command like that already.

$ gmic sp cat r 200%,200%,100%,100%,0,3

Seems normal behaviour to me for mirroring. You could lop off the edges before mirroring and insert them afterwards, or do a row/column shift and then crop… If you have a command already, no problem? I suppose you are looking for something more native or established in hopes of speed or simplicity.

image

Working on adding border. I find odd borders a bit difficult though. I think I’ll just not attempt that and stick with bad offset.

That being said, here’s one example of possible output:

image

I’m trying to get this to work.

rep_test:
pal 45
echo [${1-{0+s#-1}}]

Basically it should insert arguments with the same number as the spectrum size. I’m doing it for arguments that’s basically higher than arg1.

Reading up on this, it’s not possible: G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing - Command Items and Selections

So I have to use array of command argument and extract it.

Solved the above roadblock. Now, this is getting really close to release:

Looks much better. Kind of looks like a relief (elevation) map from a video game. Your animation earlier reminds me of a video game too, like a magic portal/booster.

I don’t get how it looks like elevation map other than contour.

That being said: Here’s a snapshot though there’s still the thorny problem of not being able to access target image dimensions from GUI. This is turning out to be a much tougher filter than I anticipated.

Ok, I think I’m done with the GUI filter. There’s also this:

Both are unfix-able at my end. Otherwise, the image generated on apply/ok are fine.


I have released Hitomezashi. Note that there are unfixable bugs. Particularly in relations with the fact that G’MIC can’t retrieve target image dimension, and with how preview works. Some are fixable, but I’ll do that later.


I also made +rep_split_gradient and pushed it. The cli instruction should be clear to understand in of by itself. What it does is create split gradients.

I think I have enough new equations for Thorn Fractal. And I found a really unique one:

I don’t know what to name it yet.

2 Likes