Reptorian G'MIC Filters

Oh boy, my desktop motherboard got fried. So, no new filters until next two weeks.

Gave up on multithreading and hoping for parallel_for function support for g’mic one day.

That being said, I had trimmed off 10 seconds without any fancy tricks. That means no pixel count to verify, and no progress report. I will update Popcorn Fractal soon.

Before: 26 s
Now: 14 s

My ideal: 5 s going off multithreaded version attempt

EDIT: I just pushed the optimized version. 12 s trimmed off. The GUI version will be fixed to account for this.

EDIT: Now, I think I found my solution, but it’s not going to look pretty. I will create a 3D image, and each x position represent iteration within ptn<pts loop. Then, transfer that to 2D. From my test, in theory, I should be able to have 4 s-6 s as opposed to pdn 10 s. If it works, I should be able to do this for chirikov-taylor filter.


EDIT as of 10/29/2020:

I got something here.

#@cli rep_pfrac : eq. to 'rep_popcorn_fractal' : (+)
rep_pfrac: rep_popcorn_fractal $*
#@cli rep_popcorn_fractal: _points>0,_density>0,_H,_K,_zoom,_rotation_angle,_origin_x,_origin_y,_mode,_f1={ 0=sin | 1=cos | 2=tan | 3=atan},...
#@cli : Generates Pickover Popcorn Fractal. Code was adapted from Paul Bourke's c code, and extended for more possibilities. Fractal is attributed to Clifford Pickover.\n
#@cli : _points defines the maximum number of points to be added on image based on pixel location.
#@cli : _density defines the frequency of points to be added along row and height of image. A value of one implies n points to be added per pixel.
#@cli : _H is the function multiplier used to subtract from the new found values from each iteration.
#@cli : _K is the inner multiplier for the inside function. See popcorn_x(a,b), and popcorn_y embedded within the code of rep_popcorn_fractal for more information.
#@cli : _zoom defines the magnification of image. A negative value will "shrink" the structure of generated fractal.
#@cli : _rotation_angle defines the function angle of fractal.
#@cli : _origin_x defines the position of fractal. Center of image row will be treated as zero, and the ranges for image row are treated as -1,1.
#@cli : _origin_y defines the position of fractal. Center of image column will be treated as zero, and the ranges for image column are treated as -1,1.
#@cli : _mode defines whether to use 4 trigonometric functions or 6 trigonometric functions. Each halves of functions are used on 2 functions used by different axis.
#@cli : _fn defines individual function used for the popcorn fractal.\n
#@cli : Author: Reptorian.\n
#@cli : Default values: '_points=50','density=1','H=.05','_K=3','_rotation_angle','_origin_x=0','_origin_y=0','_mode=0',...\n
#@cli : \ \ \ \ If _mode=0: ... = '_f1=_f3=0','_f2=_f4=2'
#@cli : \ \ \ \ If _mode=1: ... = '_f1=_f4=0','_f2=_f5=1','_f3=_f6=2'\n
rep_popcorn_fractal:
skip ${1=50},${2=1},${3=.05},${4=3},${5=1},${6=0},${7=0},${8=0},${9=0},${10=},${11=},${12=},${13=},${14=},${15=}
if ($6-360*floor($6/360))?1
 fvx=round(((rot_x(xnew,ynew)-osx)*cx_zoom+cxsx)/sx)
 fvy=round(((rot_y(xnew,ynew)-osy)*cy_zoom+cysy)/sy)
else
 fvx=round(((xnew-osx)*cx_zoom+cxsx)/sx)
 fvy=round(((ynew-osy)*cy_zoom+cysy)/sy)
fi
channels. 0 f. 0
ww={w-1}
hh={h-1}
sd={max(w,h)/min(w,h)}
sx={w>h?$sd:1}
sy={w>h?1:$sd}
cx={w/2}
cy={h/2}
density={1/abs($2)}
$1,{h/$density},{w/$density},2,"
begin(
 const ww="$ww";
 const hh="$hh";
 const sd="$sd";
 const sx="$sx";
 const sy="$sy";
 const cx="$cx";
 const cy="$cy";
 const zoom=1/$5;
 const origin_x=$7*-1*zoom;
 const origin_y=$8*zoom;
 const osx=origin_x*sx;
 const osy=origin_y*sy;
 const cx_zoom=cx/zoom;
 const cy_zoom=cy/zoom;
 const cxsx=cx*sx;
 const cysy=cy*sy;
 const zcxsx=zoom/cxsx;
 const zcysy=zoom/cysy;
 const ang=($6/180)*pi;
 const cos_ang=cos(ang);
 const sin_ang=sin(ang);
 rot_x(a,b)=a*cos_ang-b*sin_ang;
 rot_y(a,b)=a*sin_ang+b*cos_ang;
 const density="$density";
 const H=$3;
 const K=$4;
 if($9,
  if(narg($10),
   if(($10%4)==0,func_a(a)=sin(a);,
   if(($10%4)==1,func_a(a)=cos(a);,
   if(($10%4)==2,func_a(a)=tan(a);,
   if(($10%4)==3,func_a(a)=atan(a);
   );
   );
   );
   );,
  func_a(a)=sin(a);
  );
  if(narg($11),
   if(($11%4)==0,func_b(a)=sin(a);,
   if(($11%4)==1,func_b(a)=cos(a);,
   if(($11%4)==2,func_b(a)=tan(a);,
   if(($11%4)==3,func_b(a)=atan(a);
   );
   );
   );
   );,
  func_b(a)=cos(a);
  );
  if(narg($12),
   if(($12%4)==0,func_c(a)=sin(a);,
   if(($12%4)==1,func_c(a)=cos(a);,
   if(($12%4)==2,func_c(a)=tan(a);,
   if(($12%4)==3,func_c(a)=atan(a);
   );
   );
   );
   );,
  func_c(a)=tan(a);
  );
  if(!narg($13),
   if(narg($10),
    if(($10%4)==0,func_d(a)=sin(a);,
    if(($10%4)==1,func_d(a)=cos(a);,
    if(($10%4)==2,func_d(a)=tan(a);,
    if(($10%4)==3,func_d(a)=atan(a);
    );
    );
    );
    );,
   func_d(a)=sin(a);
   );,
  if(($13%4)==0,func_d(a)=sin(a);,
  if(($13%4)==1,func_d(a)=cos(a);,
  if(($13%4)==2,func_d(a)=tan(a);,
  if(($13%4)==3,func_d(a)=atan(a);
  );
  );
  );
  );
  );
  if(!narg($14),
   if(narg($11),
    if(($11%4)==0,func_e(a)=sin(a);,
    if(($11%4)==1,func_e(a)=cos(a);,
    if(($11%4)==2,func_e(a)=tan(a);,
    if(($11%4)==3,func_e(a)=atan(a);
    );
    );
    );
    );,
   func_e(a)=cos(a);
   );,
  if(($14%4)==0,func_e(a)=sin(a);,
  if(($14%4)==1,func_e(a)=cos(a);,
  if(($14%4)==2,func_e(a)=tan(a);,
  if(($14%4)==3,func_e(a)=atan(a);
  );
  );
  );
  );
  );
  if(!narg($15),
   if(narg($12),
    if(($12%4)==0,func_f(a)=sin(a);,
    if(($12%4)==1,func_f(a)=cos(a);,
    if(($12%4)==2,func_f(a)=tan(a);,
    if(($12%4)==3,func_f(a)=atan(a);
    );
    );
    );
    );,
   func_f(a)=tan(a);
   );,
  if(($15%4)==0,func_f(a)=sin(a);,
  if(($15%4)==1,func_f(a)=cos(a);,
  if(($15%4)==2,func_f(a)=tan(a);,
  if(($15%4)==3,func_f(a)=atan(a);
  );
  );
  );
  );
  );
  popcorn_x(a,b)=(kb=K*b;a-H*func_a(b+func_b(Kb+func_c(Kb))));
  popcorn_y(a,b)=(ka=K*a;b-H*func_d(a+func_e(Ka+func_f(Ka))));
 ,
  if(narg($10),
   if(($10%4)==0,func_a(a)=sin(a);,
   if(($10%4)==1,func_a(a)=cos(a);,
   if(($10%4)==2,func_a(a)=tan(a);,
   if(($10%4)==3,func_a(a)=atan(a);
   );
   );
   );
   );,
  func_a(a)=sin(a);
  );
  if(narg($11),
   if(($11%4)==0,func_b(a)=sin(a);,
   if(($11%4)==1,func_b(a)=cos(a);,
   if(($11%4)==2,func_b(a)=tan(a);,
   if(($11%4)==3,func_b(a)=atan(a);
   );
   );
   );
   );,
  func_b(a)=tan(a);
  );
  if(!narg($12),
   if(narg($10),
    if(($10%4)==0,func_c(a)=sin(a);,
    if(($10%4)==1,func_c(a)=cos(a);,
    if(($10%4)==2,func_c(a)=tan(a);,
    if(($10%4)==3,func_c(a)=atan(a);
    );
    );
    );
    );,
   func_c(a)=sin(a);
   );,
  if(($12%4)==0,func_c(a)=sin(a);,
  if(($12%4)==1,func_c(a)=cos(a);,
  if(($12%4)==2,func_c(a)=tan(a);,
  if(($12%4)==3,func_c(a)=atan(a);
  );
  );
  );
  );
  );
  if(!narg($13),
   if(narg($11),
    if(($11%4)==0,func_d(a)=sin(a);,
    if(($11%4)==1,func_d(a)=cos(a);,
    if(($11%4)==2,func_d(a)=tan(a);,
    if(($11%4)==3,func_d(a)=atan(a);
    );
    );
    );
    );,
   func_d(a)=tan(a);
   );,
  if(($13%4)==0,func_d(a)=sin(a);,
  if(($13%4)==1,func_d(a)=cos(a);,
  if(($13%4)==2,func_d(a)=tan(a);,
  if(($13%4)==3,func_d(a)=atan(a);
  );
  );
  );
  );
  );
  popcorn_x(a,b)=a-H*func_a(b+func_b(K*b));
  popcorn_y(a,b)=b-H*func_c(a+func_d(K*a));
 );
);
if(x==0,
 iy=density*y;
 ix=density*z;
 xx=(ix-cx)*zcxsx+origin_x;
 yy=(iy-cy)*zcysy+origin_y;
 xnew=popcorn_x(xx,yy);
 ynew=popcorn_y(xx,yy);
 coordinates=["$fvx","$fvy"];
 xx=xnew;
 yy=ynew;
 ,
 xnew=popcorn_x(xx,yy);
 ynew=popcorn_y(xx,yy);
 coordinates=["$fvx","$fvy"];
 xx=xnew;
 yy=ynew;
);
coordinates;
"
sh. 0
sh.. 1
eval ${-math_lib}"
begin(
 const width="$ww";
 const height="$hh";
);
for(px=0,px<d,px++,
for(py=0,py<h,py++,
for(pz=0,pz<w,pz++,
 cx=i(#-2,pz,py,px);
 cy=i(#-1,pz,py,px);
 if(inrange(cx,0,width,1)&&inrange(cy,0,height,1),i(#-4,cx,cy)++;);
);
);
);
"
rm[-3--1]

The output shows a heavily darkened version of the Popcorn Fractal. It should in theory worked, but I have no idea what went wrong here. But I do believe it may has to do with the wrong order of filling.

The good news. Less than 6 s. And that is acceptable given PDN version that I have made would do thiis in 10 s, and the current gmic version would do this in 15 s. The most ideal solution is to be able to use something akin to fill, but allowing users to increment values at dest(x,y,z,c), and eval is too slow for this.

Nice. Have a :fish: for your efforts.

2 Likes

Thanks. I hope to find an answer as to what went wrong. The older one use a traditional for x, for y, for n loop, and the new one use parallel processing via 3d where x is for n, and y is y and z is x and with 2 channels representing coordinates.

Another way I can do this. Add a helper image, and blank 3d image with 2 channels. Apply repeat loop and j[3D_Block] [helper],0,0,$> and fill 3d block. Then use eval to fill coordinates to the target image. Obviously, none of these workaround are ideal, but needed to get under 6 s.


EDIT: Ok, I did the “Another way” approach, and it is just as fast as PDN version., and it actually works. Now, what really bothers me is the eval for x,y,z part of command takes up 5 s of the time it takes to compute. Is there like a way I can optimize that without trying to do the earlier approach which did not work? I might attempt the earlier approach.


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.