Reptorian G'MIC Filters

I’m working on a new version of Stitch. It will be faster. It is very hard to code up too.

For picture of what Stitch looks like:

Here’s what the current code looks like:

Code to New Stitch
#@cli new_rep_stitch: offset,thickness,modulo,interpolation,boundary,0<sublevel,randomize_stitching,
#@cli : Stitches images.
new_rep_stitch:
skip ${3=0},${8=}
# offset = $1(%)
# thickness = $2(%)
# modulo = $3 >= 0
# interpolation = $4 > 0
# boundary = $5 >= 0
# sublevel = abs($6) + 1
# random_stitching = $7 => { 0=Do-Not-Randomize | 1=Use Randomization On Strip | 2=Use Randomization On Offset | 3= Use Randomization on Offset and Strip }
# seed_a = $8 => For offset
# seed_b = $9 => For each strip
# angles = $9.....$inf

if ${is_percent\ $1} use_perc_off=1
else 
 use_perc_off,offset=0,{abs($1)*$sublevel}
 if !$offset error abs("$1")!=0==F fi
fi

if ${is_percent\ $2} use_perc_thk=1
else 
 use_perc_thk,thickness=0,{abs($2)*$sublevel}
 if !$thickness error abs("$2")!=0==F fi
fi

modulo,interpolation,boundary,sublevel,random_stitch_mode,seed_off,seed_strip,generate_out_mode,timg,max_img_ind={abs([${3-5}])},{abs($6)+1},{$7%4},${8-9},0,{$!},{$!-1}

if $modulo if $modulo<2 error "$3">1==F fi fi

+rep_find_nonduplicate_angles ${10--1} :: rad_ang
num_angs={w#-1}

if $!>2

 eval "
  diff=0;
  const ti=$timg;
  initial_dimensions=[w#0,h#0,d#0];
  for(p=1,p<ti,++p,
   if([w#p,h#p,d#p]!=initial_dimensions,
    diff=1;
    break();
   );
  );
  diff;"
  
  generate_out_mode=${}
  
fi

if !$generate_out_mode

 r[0--2] {w#0*$sublevel},{h#0*$sublevel},100%,100%,1

 length={norm(w#0,h#0)/2}
 if $use_perc_off offset={$length*$1} fi
 if $use_perc_thk thickness={int($length*$2)} fi
 
 if $offset&&$thickness
 
  +f. "begin(
    const thickness=$thickness;
    const cx=(w#0-1)/2;
    const cy=(h#0-1)/2;
    const pt_1=atan2(cy,-cx);
    const pt_0=atan2(cy,cx);
    count_bars(v)=int(v/thickness)+1;
   );
   if(inrange(i,pt_0,pt_1,1,0)
   ,count_bars(1/sin(i)*cy)
   ,count_bars(1/cos(i)*cx)
   );"
   
   :: number_of_bars
   
   if $random_stitch_mode>1
    #Insert multiple randomization here#
    error development
   else
   
    {$modulo?min($modulo,iM#-1):iM#-1},1,1,2
    
    eval "
    if(narg($seed_off),srand($seed_off););
    const length=w#-1;
    I(#-1)=[u(-1,1)*$offset,length];
    for(p=1,p<length,++p,
     a=u(-1,1)*$offset;
     b=u(-1,1)*$offset;
     if(int(u(0,2)),I(#-1,p)=[b,a],I(#-1,p)=[a,b]);
    );"
    
   fi
   
   f[0-$max_img_ind] "
    begin(
     if(narg($seed_strip),srand($seed_strip););
     const max_x=w-1;
     const max_y=h-1;
     const cx=max_x/2;
     const cy=max_y/2;
     const rs_mode=$random_stitch_mode;
     const num_angs=$num_angs;
     const length=w#-1;
     const sub_length=length-1;
     const thickness=$thickness;
     const half_thickness=thickness>>1;
     nbars=crop(#$number_of_bars);
     angs=crop(#$rad_ang);
     cos_ang=cos(angs);
     sin_ang=sin(angs);
     rot_x(a,b,c)=a*cos_ang[c]-b*sin_ang[c];
     rot_y(a,b,c)=a*sin_ang[c]+b*cos_ang[c];
     if(rs_mode<2,
      pos_off=crop(#-1,0,0,0,0,1,w#-1,1,1);
      neg_off=crop(#-1,0,0,0,1,1,w#-1,1,1);
      rs_mode?(
       neg_v=expr('>begin(if(narg($seed_strip),srand($seed_strip-1);););int(u(0,$num_angs+1));',length);
       pos_v=expr('>begin(if(narg($seed_strip),srand($seed_strip+1);););int(u(0,$num_angs+1));',length);
       neg_v=reverse(neg_v);
       middle=expr('if(narg($seed_strip),srand($seed_strip););int(u(0,$num_angs+1));',1);
       rand_v=[neg_v,middle,pos_v];
       off_out(v)=rand_v[v+nbars[k]];
      ):(
       off_out(v)=v%2;
      );
      strip_out(v)=v%num_angs;
     );
    );
    xx=x-cx;
    yy=y-cy;
    ind=0;
    repeat(num_angs,k,
     ind+=off_out(floor((rot_y(xx,yy,k)+half_thickness)/thickness));
    );
    ind%=num_angs;
    "
    
 fi
 
fi



# repeat $!-1 l[$>,-1] {
#   
# } 
# done
#@cli rep_find_nonduplicate_angles: angle_0,angle_1,....,angle_n
#@cli : Return all non-duplicate angles in radian form.
+rep_find_nonduplicate_angles:
($*) (0)

eval[-2] "res_to_0_180(angle)=(angle%180)/180;
 if(x,
  v=res_to_0_180(i);
  not_found=1;
  for(p=0,p<da_size(#-1),++p,
   if(i[#-1,p]==v,
    not_found=0;
    break();
   );  
  ); 
  if(not_found,da_push(#-1,v););
 ,
  da_push(#-1,res_to_0_180(i));
 );
 end(resize(#-1,1,da_size(#-1),1,1,0););"
 
*. {pi} rotate. -90 rm..

The idea is to use only one image to do what the old Stitch does.

Here’s the output:

The last image are offset values. The first image will be used to generate the stitch output.

The timing shows real potential in being a really fast filter.

LOL - that was a while back (2019). Could you link back to the first post on it and also your sample images? I did a search myself, but it would help to give our dear readers some context. :wink:

I don’t feel like looking at the first post of Stitch, but now I just posted a picture of Stitch.

That being said, I’m inching closer to being done with new version of Stitch. It is 5 times faster now, and the older one is fast enough for productive work.

Here are comparison test:

New Version of Stitch!

C:\Windows\System32>gmic sp cat tic new_rep_stitch 5%,.5%,0,2,2,0,0,10,10,45,135 toc
[gmic]-0./ Start G'MIC interpreter.
[gmic]-1./ Input sample image 'cat' (1 image 600x550x1x3).
[gmic]-1./ Initialize timer.
[gmic]-4./ Elapsed time: 0.011 s.
[gmic]-4./ Display images [0,1,2,3] = 'cat, rad_ang, number_of_bars, [unnamed]'.

Old version of Stitch!

C:\Windows\System32>gmic sp cat tic rep_stitch 5,2,0,5,2,0,0,45,135 toc
[gmic]-0./ Start G'MIC interpreter.
[gmic]-1./ Input sample image 'cat' (1 image 600x550x1x3).
[gmic]-1./ Initialize timer.
Stitches image with offset of 5 px, and thickness of 2 px.
[gmic]-1./ Elapsed time: 0.055 s.
[gmic]-1./ Display image [0] = '[image of 'sur=x*cos(0.785398163397448(...)ur/(2*(0+1)));0?abs(res)%0:abs(res)']'.
[0] = '[image of 'sur=x*cos(0.78539816(...)r/(2*(0+1)));0?abs(res)%0:abs(res)']':
  size = (600,550,1,3) [3867 Kio of floats].
  data = (127,219,219,167,164,151,134,116,87,219,219,219,(...),95,81,81,110,31,31,110,70,110,117,128,115).
  min = 4, max = 244, mean = 113.439, std = 66.7958, coords_min = (291,0,0,0), coords_max = (144,30,0,0).
[gmic]-1./ End G'MIC interpreter.

Thanks a lot for Your wonderful fractal filters.
At GimpChat, we also have fun using Your filters in the thread: P&M Cosmos.

If You have any ideas (each user with a bit of fantasy) for a better use of these filters - Welcome - and it will be more fun.

1 Like

You’re welcome.

A few remarks though I haven’t looked at the code.

  1. You should base the code off the cli version of the commands since they’re far less likely to be changed. I can’t tell if you did since I didn’t look at the code. I also have made rep_exponential_sigmoid_adjustment, and that command is used to change the shading of the fractals.

  2. Consider Popcorn Fractal Transformative, and maybe Complexion Burst.


On the new version of Stitch I’m working on. Here’s a bit of test of output time:

New Stitch: 0.019 s.
Old Stitch: 0.118 s.

Over 6 times faster. And the new features are near completion, I will have to add in modulo code, and some few edits to my code, and it’ll be pushed.

New features list:

  1. Randomize Offset={ 0=Single Randomize | Per-Iteration Randomize }
  2. Randomize Strip={ 0=Do Not Randomize | 1=Single Randomize | 2=Per-Iteration Randomize }
  3. Offset Seed
  4. Strip Seed

In addition, it detects whether the same angle are used. 0,180 will remove 180. 0,45,90,225,270 will become 0,45,90. This speeds up filter as well.

For those who wants current code
#@cli new_rep_stitch: offset,thickness,modulo,interpolation,boundary,0<sublevel,randomize_stitching,
#@cli : Stitches images.
new_rep_stitch:
skip ${3=0},${9=},${10=}
# offset = $1(%)
# thickness = $2(%)
# modulo = $3 >= 0
# interpolation = $4 > 0
# boundary = $5 >= 0
# sublevel = abs($6) + 1
# offset_mode = $7 => { 0=Single_Iteration Randomize | 1 = Multiple Iteration Randomize}
# strip_mode = $8 => { 0 = Do not Randomize | 1 = Single_Iteration Randomize | 2 = Multiple Iteration Randomize }
# seed_a = $9 => For offset
# seed_b = $10 => For each strip
# angles = $11.....$inf

if ${is_percent\ $1} use_perc_off=1
else 
 use_perc_off,offset=0,{abs($1)*$sublevel}
 if !$offset error abs("$1")!=0==F fi
fi

if ${is_percent\ $2} use_perc_thk=1
else 
 use_perc_thk,thickness=0,{max(1,abs($2)*$sublevel)}
fi

modulo_mode,interpolation,boundary,sublevel,random_offset_mode,random_strip_mode,seed_off,seed_strip,generate_out_mode,timg,max_img_ind=$3,{abs([${4-5}])},{abs($6)+1},{$7%2},{$8%3},${9-10},0,{$!},{$!-1}

modulo={abs($modulo_mode)}

if $modulo if $modulo<2 error "$3">1==F fi fi

+rep_find_nonduplicate_angles ${11--1} :: rad_ang
num_angs={w#-1}

if $num_angs==1
 random_offset_mode=0
 if $random_strip_mode==3 random_strip_mode=1 fi
fi

if $!>2 # Test if dimensions are different
 eval "
  diff=0;
  const ti=$timg;
  initial_dimensions=[w#0,h#0,d#0];
  for(p=1,p<ti,++p,
   if([w#p,h#p,d#p]!=initial_dimensions,
    diff=1;
    break();
   );
  );
  diff;"
 generate_out_mode=${}
fi

if !$generate_out_mode
 
 r[0--2] {w#0*$sublevel},{h#0*$sublevel},100%,100%,1

 length={norm(w#0,h#0)/2}
 if $use_perc_off offset={$length*$1} fi
 if $use_perc_thk thickness={max($sublevel,int($length*$2))} fi
 
 if $offset&&$thickness
  
  +f. "begin(
    const thickness=$thickness;
    const cx=(w#0-1)/2;
    const cy=(h#0-1)/2;
    const pt_1=atan2(cy,-cx);
    const pt_0=atan2(cy,cx);
    count_bars(v)=int(abs(v/thickness))+1;
   );
   if(inrange(i,pt_0,pt_1,1,0)
   ,count_bars(1/sin(i)*cy)
   ,count_bars(1/cos(i)*cx)
   );"
   
   :: number_of_bars
   
   if $random_offset_mode
   
    if narg($seed_off)
   
     {$modulo?$modulo*w#-1:is#-1},1,1,2,>"begin(
     
       seed=$seed_off;
       srand(seed);
       
       $modulo?(
        
        const modulo=$modulo;
        inc_seed_cond()=!(x%modulo);
        ins_length()=modulo;
        
       ):(
        
        num_of_bars_arr=crop(#$number_of_bars);
        
        const arr_length=w#-1;
        pos_bars=vectorarr_length(0);
        
        t=0;
        repeat(w#-1,p,
         pos_bars[p] = t+=num_of_bars_arr[p];
        );
        
        inc_seed_cond()=x==pos_bars[k];
        ins_length()=num_of_bars_arr[k];
        
       );
       
       k=0;
       activate_place_length=1;
       
      );
      
      if(inc_seed_cond(),
       ++k;
       activate_place_length=1;
       srand(++seed);
      );
      
      a=u(-1,1)*$offset;
      b=u(-1,1)*$offset;
      
      ov=int(u(0,2))?[b,a]:[a,b];
      
      if(activate_place_length,
       activate_place_length=0;
       [ov[0],ins_length()];
      ,ov;);
      
      "
      
    else
    
     if $modulo
     
      {$modulo*w#-1},1,1,2,"begin(
        const modulo=$modulo;
        const offset=$offset;
       );
       ov=[u(-1,1),u(-1,1)]*offset;
       if(!(x%modulo),[ov[0],modulo],ov);"
       
     else
     
      {is#-1},1,1,2,"begin(
        const offset=$offset;
       );
       [u(-1,1),u(-1,1)]*offset;"
       
      eval.. i(#-1,i,0,0,1)=i;
      
     fi
     
    fi
     
   else # Create strip for Single_Iteration
    
    {$modulo?min($modulo,iM#-1):iM#-1},1,1,2,>"begin(
      const offset=$offset;
      if(narg($seed_off),srand($seed_off););
     );
     a=u(-1,1)*offset;
     b=u(-1,1)*offset;
     ov=int(u(0,2))?[b,a]:[a,b];
     x? ov : [ ov[0],w ];"
    
   fi
   
   f[0-$max_img_ind] "
   begin(if(narg($seed_strip),srand($seed_strip););
    
    const max_x=w-1;
    const max_y=h-1;
    const cx=max_x/2;
    const cy=max_y/2;
    
    const random_offset_mode=$random_offset_mode;
    const random_strip_mode=$random_strip_mode;
    
    const length=w#-1;
    const sub_length=length-1;
    
    const num_angs=$num_angs;
    
    const thickness=$thickness;
    const half_thickness=thickness>>1;
    
    nbars=crop(#$number_of_bars);
    
    angs=crop(#$rad_ang);
    cos_ang=cos(angs);
    sin_ang=sin(angs);
    rot_x(a,b,c)=a*cos_ang[c]-b*sin_ang[c];
    rot_y(a,b,c)=a*sin_ang[c]-b*cos_ang[c];
    
    pos_off=crop(#-1,0,0,0,0,w#-1,1,1,1);
    neg_off=crop(#-1,0,0,0,1,w#-1,1,1,1);
    
    random_offset_mode?(
     offset_pos=[0,(nbars)[0,num_angs-1]];
     t=offset_pos[1];
     for(p=2,p<num_angs,++p,
      offset_pos[p]=t+=offset_pos[p];
     );
     find_offset(bar_pos)=(
      offset=bar_pos>=0?pos_off[bar_pos+offset_pos[ind]]:neg_off[abs(bar_pos)+offset_pos[ind]];
      off_x=cos_ang[ind]*offset;
      off_y=sin_ang[ind]*offset;
      [off_x,off_y];
     );
    ):(
     find_offset(bar_pos)=(
      offset=bar_pos>=0?pos_off[bar_pos]:neg_off[abs(bar_pos)];
      off_x=cos_ang[ind]*offset;
      off_y=sin_ang[ind]*offset;
      [off_x,off_y];
     );
    );
    
    random_strip_mode==2?(
    
     const multi_strip_length=2*length+num_angs;
     
     narg($seed_strip)?(
     
      total_neg_zer_pos(v)=(v<<1)+1;
      
      neg_v=expr('<begin(seed=$seed_strip-1+3*w#$rad_ang;srand(seed);nbars=crop(#$number_of_bars);nbars=reverse(nbars);k=0;const ina=$num_angs+1;);if(x==nbars[k],++k;seed-=3;srand(seed););int(u(0,ina));',length);
      pos_v=expr('>begin(seed=$seed_strip+1;srand(seed);nbars=crop(#$number_of_bars);const num_angs=$num_angs;const ina=$num_angs+1;k=0;);if(x==nbars[k]&&k<num_angs,++k;seed+=3;srand(seed););int(u(0,ina));',length);
      middle=expr('>begin(seed=$seed_strip-3;const ina=$num_angs+1;);seed+=3;srand(seed);int(u(0,ina));',num_angs);
      
      rand_v=vectormulti_strip_length();
      
      state_neg_zer_pos=neg_pos=pos_pos=nbars_pos=start_point=mid_pos=0;
      
      fill(rand_v,
      
       !state_neg_zer_pos?(
        current_pos=neg_pos+start_point;
        v=neg_v[current_pos];
        ++neg_pos;
        if(neg_pos==nbars[mid_pos],++state_neg_zer_pos;);
       ):
       state_neg_zer_pos==1?(
        v=middle[mid_pos];
        neg_pos=pos_pos=0;
        ++state_neg_zer_pos;
       ):(
        current_pos=pos_pos+start_point;
        v=pos_v[current_pos];
        ++pos_pos;
        if(pos_pos==nbars[mid_pos],
         state_neg_zer_pos=0;
         start_point+=nbars[mid_pos];
         ++mid_pos;
        );
       );
       
       v;
       
      );
      

     ):(
     
      rand_v=expr('begin(const ina=$num_angs+1;);int(u(0,int));',multi_strip_length);
      
     );

     non_zero_pos=(nbars)[0,num_angs-1]<<1;
     ++non_zero_pos;
     
     if(num_angs>2,
      t=non_zero_pos[0];
      for(k=1,k<num_angs,++k,
       non_zero_pos[k]=t+=non_zero_pos[k];
      );
     );
    
     strip_indices=[0,non_zero_pos];
     off_out(v)=rand_v[v+nbars[k]+strip_indices[k]];
     
    ):
    random_strip_mode==1?(
     neg_v=expr('<begin(if(narg($seed_strip),srand($seed_strip-1););const ina=$num_angs+1;);int(u(0,ina));',length);
     pos_v=expr('>begin(if(narg($seed_strip),srand($seed_strip+1););const ina=$num_angs+1;);int(u(0,ina));',length);
     middle=int(u(0,num_angs+1));
     rand_v=[neg_v,middle,pos_v];
     off_out(v)=rand_v[v+nbars[k]];
    ):(
     off_out(v)=v%2;
    );
    
   );
   xx=x-cx;
   yy=y-cy;
   current_bar=vector(#num_angs,0);
   ind=0;
   repeat(num_angs,k,
    t=floor((rot_y(xx,yy,k)+half_thickness)/thickness);
    current_bar[k]=t;
    ind+=off_out(t);
   );
   ind%=num_angs;
   J(find_offset(current_bar[ind]));
   "
 fi
fi
#@cli rep_find_nonduplicate_angles: angle_0,angle_1,....,angle_n
#@cli : Return all non-duplicate angles in radian form.
+rep_find_nonduplicate_angles:
($*) (0)

eval[-2] "res_to_0_180(angle)=(angle%180)/180;
 if(x,
  v=res_to_0_180(i);
  not_found=1;
  for(p=0,p<da_size(#-1),++p,
   if(i[#-1,p]==v,
    not_found=0;
    break();
   );  
  ); 
  if(not_found,da_push(#-1,v););
 ,
  da_push(#-1,res_to_0_180(i));
 );
 end(resize(#-1,1,da_size(#-1),1,1,0););"
 
*. {pi} rotate. -90 rm..
1 Like

EDIT: I managed to finish up on the CLI version of Stitch, so a release on the new version of Stitch will come soon.

EDIT: False alarm on earlier edit, it was easy to fix.

Stitch filter has been upgraded!


I know the GUI interface isn’t the most intuitive, I don’t consider that to be much of a priority to address for now. Will fix in a few. It’s easy to get used to it. This is addressed as well as bugs.

Fixed Graduated Filter. Now, you no longer need an alpha channel to work with it. It’s alpha-channel agnostic now.

Non-Convolution Edge Extraction will be repaired.

Hitomezashi has been upgraded with full-preview support.

1 Like

Im considering 360 flipbook generator, but not sure how likely. Really low, but might be interesting.

New Transfer Color[Reduced Color] progress:

image

I still need to find a better algorithm for this though. I’ll post details on that in the G’MIC exercise thread since the current implementation is by far slow with hardware stimulation.
Image as global variable, and using apply_tiles worked!

Nice. I am sure you will improve it over time.

For anyone reading this, look like I can’t complete it - G'MIC exercises - #955 by afre . I will move on to other filters that I can actually complete.

Hey, rather than cross-posting, it would be better to explain what it is or does or refer to an earlier post in this thread. Otherwise, it is just taking space.

Well, I explained it just now in the other post. It certainly doesn’t work, and I don’t know if it can be fixed, but I do know that it fits all the requirement for apply_tiles to work. So, it’s very possible that the upgrade will never happen.

We’ll see. You always manage to find a way. I don’t have the time to look at it, but my suggestion would be the usual. Step through all your commands with math and gmic interpreters’ echos, prints and displays. It takes so much time but you have to be that patient with complex commands.

Well, it turns out it was possible after all. It just that I didn’t know what sort() did it in its entirely.

Ok, it seem like now I have progress. For dithering with specified window size, I went with two different algorithm. First of all, it uses fake floyd-steinberg algorithm to diffuse error onto a different image which is temporary. Then, it uses Jarvis, Judice, and Ninke dithering to the target image. The temporary image is only used to find colors to use in a palette.

Here’s where I found the formula for different dithering algorithm: Image Dithering: Eleven Algorithms and Source Code | tannerhelland.com

The test result seems good for dithering with a specific window size:

image

image

Something not given proper mention when I’ve looked at dithering previously is to avoid doing it in sRGB. It’s fine to choose the palette in sRGB, just make sure you convert both the palette and the image to linear before dithering, then back again. The problems of not doing so become much more visible with small palettes (basically the brightness is totally wrong).

How would I convert to linear space using G’MIC?