Reptorian G'MIC Filters

Wait… what just happened? The image doesn’t match your command…

What are you seeing? Also, that’s a older version of the command, I’ll push a new version with h being accessible.

I pushed a new version of rep_kodl with h being accessible.

Decided to go with these options -

$2==0, then crop to original dimension
$2==1, then crop to original dimension after adding new layer and applying filter to new layer
$2==2, then resize to new dimension after adding new layer and applying filter to new layer

There won’t be resize to new dimension as that’s already done by command within $1.

Result:


Looks like I decided to refactor rep_form_pixel. Looking back at it and seeing no pass argument. I’m just gonna start over on that. :confused:

I also will refactor rep_stitch, but to be able to convert for other programs as well as maintainability.


Actually, I realized that I can refactor rep_form_pixel. I realized that if I create patterns of shape layer, and duplicate that. I can use crop and modulus operation to find the average, and lerp to interpolate between 0-1. So, instead of s x, s y to do this job, I can do this instead.


I pulled off the refactoring and added more features. I will be able to complete a new version of rep_form_pixel. For now, I’ll just share the code. And note the h doesn’t help for now.

So much faster too. .062 s!

#@cli rep_form_pixel: _form_id<24,_form_quad_lx!=0,_form_quad_ly!=0,_form_ratio[%]!=0,_angle,0<=_reflect_dir<=2,_sublevel,_interpolation<6,_keep_original_size={ 0=resized_result | 1=preserve_original_size },_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.
#@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.
#@cli : _keep_original_size places the image at center if it a value greater than 0. Otherwise, the filter will proceed keeping all the tiles at the same size.
#@cli : If _form_id is a negative number, then it will use the layer number 0 as form reference. Forms that supports dynamic options are fern,gear,polygon,snowflake,star. Dynamic options corresponds to _shape_option_n.\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'
#@cli : $ sp tiger rep_form_pixel star,35,35,,,,,,8
#@cli : $ sp lena rep_form_pixel cupid,35,35,,24,1
rep_form_pixel:
skip ${4=},${5=},${6=},${7=},${8=},${9=},${10=},${11=}
if narg($4)  form_ratio={abs($4)}         else form_ratio=1 fi
if narg($5)  ang=$5                       else ang=0 fi
if narg($6)  mi={abs($6)}                 else mi=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) __image_boundary={abs($10)}  else __image_boundary=2 fi
if narg($11) mkodl={abs($11)}             else mkodl=0  fi
__tw=abs($2)
__th=abs($3)
if !($__tw||$__th) v + error "Invalid Dimension!" v - fi
__tw={!$__tw?$__th:$__tw}
__th={!$__th?$__tw:$__th}
sub+=1
interpolation+=1
tile_boundary+=2
__image_boundary+=1

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

if $mkodl==3
    m "rep_form_pixel_generate_results: r[0] $""1,$""2,100%,100%,0,$__image_boundary,.5,.5"
elif $mkodl==2
    m "rep_form_pixel_generate_results: r[0] $""1,$""2,100%,100%,0,$__image_boundary,.5,.5"
elif $mkodl==1
    m "rep_form_pixel_generate_results: r[0] $""1,$""2,100%,100%,0,$__image_boundary,.5,.5"
else
    m "rep_form_pixel_generate_results: rep_form_pixel_generate_results_a $""1,$""2,$__tw,$__th,$__image_boundary"
fi

imgs={$!}

if ${is_image_arg\ $1}
    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,dragonfly,fern,flip,gear,gumleaf,heart,information,kookaburra,mail,mapleleaf,paint_splat,paw,phone,polygon,rooster,shopping_cart,snowflake,star"}
    fi fi
    if $#>11 shape_$sid {max($__tw,$__th)*$sub},${12--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   $mi==1 mirror. x 
elif $mi==2 mirror. y
fi

n. 0,1 autocrop. 0
autocrop. 0
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
cut. 0,1 n. 0,1
r. $nw,$nh,100%,100%,0,$tile_boundary

repeat $imgs
    rw={ceil(w#$</$__tw)*$__tw}
    rh={ceil(h#$</$__th)*$__th}
    l[$<,-1] 
        rep_form_pixel_generate_results $rw,$rh
    endl
done
rm.

rep_form_pixel_generate_results_a:
r[0] $1,$2,100%,100%,0,$5,.5,.5 
avgc={avg(crop(#1,0,0,abs($3),abs($4)))}
{w#0},{h#0},1,{s#0},i0#1*i#0 
{w#0},{h#0},1,{s#0},(1-i0#1)*i#0
repeat s#0
    ci={$>}
    repeat 2 sh.. $ci done
    sh[0] $ci
    
    f. ">begin(const ww=abs($3);const hh=abs($4);const wwhh=ww*hh;const length=floor(w/ww);avg_a=vectorlength(0);avg_b=vectorlength(0);const avg_c="$avgc";const w_avg=wwhh*avg_c;const iw_avg=wwhh-w_avg;);
    if(!x&&!(y%hh),
        for(v=0,v<length,v++,
            avg_a[v]=sum(crop(#-2,v*ww,y,ww,hh))/iw_avg;
            avg_b[v]=sum(crop(#-3,v*ww,y,ww,hh))/w_avg;
        );
    );
    lerp(avg_a[floor(x/ww)],avg_b[floor(x/ww)],i#1);
    "
    rm[-3--1]
done
rm[-2,-1]

Here’s a random filter you might have a use for (it was for another purpose, but can produce some nice broken effects):

gcd_tile2vec : skip ${1=2}
  repeat $! l[$>]
    {floor([w/$1,h/$1,s,$1*$1])} $1,$1,1,1,x +f. y
    f[1] ":begin(const boundary=1);i(#0,x*$1+i[#-2,c],y*$1+i[#-1,c],0,z)"
    k[1]
  endl done

gcd_vec2tile : skip ${1=2}
  repeat $! l[$>]
    k={floor(sqrt(s))} {[w*$k,h*$k,1,d]} $1,$1,1,1,x +f. y
    f[0] ":begin(const boundary=1);i(#1,x*$1+i[#-2,c],y*$1+i[#-1,c],0,z)=i"
    k[1]
  endl done

It splits n * n tiles of the image into channels (i.e. each tile as a vector per pixel) and channels into depth. Then you can use like gcd_tile2vec 8 median 3 gcd_vec2tile 8 where median 3 can be switched for a command of your choosing.


If you want to retain colour when using a command that works in z dimension, just split it along z first like gcd_tile2vec 4 s z dilate 3 a z gcd_vec2tile 4

Hmm, I may need to think about using that command later at some point. There was something that you gave.

gcd_boxfilter_local.

Had you figured out how to create custom blur for that?

Still have that command in my user file, but memory fails me :slight_smile:
Will see if I can remind myself!

I remember now… the problem was crop() requires a constant window, which would be the usual (fast) way.

That leaves the alternatives I suggested before, but a loop with an early exit might do the trick. Then a convolution kernel could be defined in the usual way, and it occurs to me all you would need for a custom “function” is a lookup table (a curve editor). I’ll see what I can do!

I believe I will be having a job at least this year, so it’s possible I will have to leave my g’mic works to other to continue from. Just mentioning that.

Well that’s bad news for us I suppose (I hope good for you though)!

I’ve made some progress with two things. First, we can use separable kernels to do the blur and keep good speed:

gcd_boxfilter_local2 : check ${"is_image_arg $1"}
  pass$1 0 r. {0,[w,h,d]},100%,0 round. 1 abs. store. arg_img
  repeat $! l[$>]
    i $arg_img
    f.. "begin(const boundary=1);for(S=0;M=i#1;k=-M,k<=M,k++,S+=j(0,k))/(M*2+1)"
    f.. "begin(const boundary=1);for(S=0;M=i#1;k=-M,k<=M,k++,S+=j(k))/(M*2+1)"
    rm.
  endl done

Secondly, we can approximate any image as a separable kernel quite easily (obviously accuracy is limited but it’s good for even a gaussian kernel):

gcd_separable_kernel : skip ${1=64}
  repeat $! l[$>] r $1,$1,1,1,3 b 4% +r 1,100%,1,1,3 +solve.. . endl done

separable_kernel
So now I just need to put it together…

3 Likes

Well that’s bad news for us I suppose (I hope good for you though)!

Which is why I’ll be leaving my current user.gmic file and other note files when it is the time. There’s something I always wanted to do, but only has some parts for that. That is connecting points to recreate gossamer plugins for Paint.NEThttps://forums.getpaint.net/topic/31509-gossamer-released-10022015/ . I would love to have the 3D version of g’mic thickline @David_Tschumperle so it would be possible to make a 3d version of gossamer, but haven’t looked into that.

Definitely bad news for the project yes, but if that’s good news for @Reptorian (which is probably the case), then we can only be happy for him and wish him good luck in his new adventure!
Doing G’MIC stuffs as a hobby is always possible :slight_smile:
Drawing thick lines in 3D is probably possible as well, not directly in the 3D drawing engine (it doesn’t manage that), but doing 3D projection “by hand” (with the math parser), and then use linethick with the 2D projected coordinates.
Another good way of having thick lines is to use dilate on an image with standard lines.

1 Like

Yeah, I think I might be doing only really light stuff for g’mic after the upgrade of gui filter of rep_form_pixel. I’m having a lot of trouble getting the image ordered correctly after generating the filter though. Just not sure what to do about that yet, but reordering them manually in Krita isn’t really a big deal as you can drag and drop to reorder.

If it is needed in GIMP, then I would definitely need a suggestion on how to reorder them. Reordering images neatly doesn’t seem to be possible when filter creates new layers. If it doesn’t, then it’s easy as setting up rv.

#@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.
#@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 : _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'
#@cli : $ sp tiger rep_form_pixel star,32,32,1,45,0,1,5,1,2,1,6
rep_form_pixel:
skip ${4=},${5=},${6=},${7=},${8=},${9=},${10=},${11=}

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) __image_boundary={abs($10)}  else __image_boundary=2 fi
if narg($11) var_kodl={abs($11)%4}        else var_kodl=0  fi

__tw=abs($2)
__th=abs($3)
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
tile_boundary+=2
__image_boundary+=1

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

if  $var_kodl==3  m "rep_form_pixel_generate_process: ow={w#0} oh={h#0} rep_form_pixel_process_tiles_alternative $""1,$""2,$__tw,$__th,$__image_boundary r[^-1] {$ow},{$oh},100%,100%,0,0,.5,.5"
elif $var_kodl==2 m "rep_form_pixel_generate_process: rep_form_pixel_process_tiles_alternative $""1,$""2,$__tw,$__th,$__image_boundary"
elif $var_kodl==1 m "rep_form_pixel_generate_process: ow={w#0} oh={h#0} rep_form_pixel_process_tiles $""1,$""2,$__tw,$__th,$__image_boundary r[0] {$ow},{$oh},100%,100%,0,0,.5,.5"
else              m "rep_form_pixel_generate_process: rep_form_pixel_process_tiles $""1,$""2,$__tw,$__th,$__image_boundary"
fi

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 $#>11 shape_$sid {max($__tw,$__th)*$sub},${12--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
autocrop. 0
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
cut. 0,1 n. 0,1
r. $nw,$nh,100%,100%,0,$tile_boundary

repeat $imgs
    rw={ceil(w#$</$__tw)*$__tw}
    rh={ceil(h#$</$__th)*$__th}
    l[$<,-1] 
        rep_form_pixel_generate_process $rw,$rh
    endl
done

rm.

if $var_kodl>1&&$imgs>1 repeat $imgs-1 mv[-1] {1+($>*2)} done fi

uncommand rep_form_pixel_generate_process
rep_form_pixel_process_tiles:
r[0] $1,$2,100%,100%,0,$5,.5,.5 
avgc={avg(crop(#1,0,0,abs($3),abs($4)))}
{w#0},{h#0},1,{s#0},i0#1*i#0 
{w#0},{h#0},1,{s#0},(1-i0#1)*i#0
repeat s#0
    ci={$>}
    repeat 2 sh.. $ci done
    sh[0] $ci
    
    f. ">begin(const ww=abs($3);const hh=abs($4);const wwhh=ww*hh;const length=floor(w/ww);avg_a=vectorlength(0);avg_b=vectorlength(0);const avg_c="$avgc";const w_avg=wwhh*avg_c;const iw_avg=wwhh-w_avg;);
    if(!x&&!(y%hh),
        for(v=0,v<length,v++,
            avg_a[v]=sum(crop(#-2,v*ww,y,ww,hh))/iw_avg;
            avg_b[v]=sum(crop(#-3,v*ww,y,ww,hh))/w_avg;
        );
    );
    lerp(avg_a[floor(x/ww)],avg_b[floor(x/ww)],i#1);
    "
    rm[-3--1]
done
rm[-2,-1]

rep_form_pixel_process_tiles_alternative:
r[0] $1,$2,100%,100%,0,$5,.5,.5 
avgc={avg(crop(#1,0,0,abs($3),abs($4)))}
{w#0},{h#0},1,{s#0},i0#1*i#0 
{w#0},{h#0},1,{s#0},(1-i0#1)*i#0
{w#0},{h#0},1,{s#0}
repeat s#0
    ci={$>}
    repeat 2 sh... $ci done
    sh[4] $ci
    
    f. ">begin(const ww=abs($3);const hh=abs($4);const wwhh=ww*hh;const length=floor(w/ww);avg_a=vectorlength(0);avg_b=vectorlength(0);const avg_c="$avgc";const w_avg=wwhh*avg_c;const iw_avg=wwhh-w_avg;);
    if(!x&&!(y%hh),
        for(v=0,v<length,v++,
            avg_a[v]=sum(crop(#-2,v*ww,y,ww,hh))/iw_avg;
            avg_b[v]=sum(crop(#-3,v*ww,y,ww,hh))/w_avg;
        );
    );
    lerp(avg_a[floor(x/ww)],avg_b[floor(x/ww)],i#1);
    "
    rm[-3--1]
done
k[0,1,-1]
rv[-2,-1]
#@gui Tiled Form : gui_rep_form_pixel, gui_rep_form_pixel
#@gui :_=note("<small><b>Note</b> - It is recommended to boost local contrast before applying!</small>"),_=separator()
#@gui :_=note("<b>Color Space Processing</b>")
#@gui :1.Colour Space=choice(0,"RGB","RYB","CMYK","HCY","HSI","HSL","HSV","LAB","LCH")
#@gui :_=separator(),_=note("<b>Tile Shape</b>")
#@gui :2.Tiled Shape=choice(1,"By Layer","Australia","Barbedwire","Circle","Crosshair","Cupid","Diamond","Dragon Curve-[D]","Dragonfly","Fern-[D]","Flip","Gear-[D]","Gumleaf","Heart","Information","Kookaburra","Mail","Mapleleaf","Paint Splat","Paw","Phone","Polygon-[D]","Rooster","Shopping Cart","Snowflake-[D]","Star-[D]")
#@gui :3.Reverse Layer =bool(0)
#@gui :_=note("<small>For the first option, you must use at least 2 layer to use layer as shape reference, else it'll use the only image itself as shape reference. Reverse Layer option might have to be used to generate the proper result when using first option.</small>")
#@gui :_=separator(),_=note("<b>Tiles</b>")
#@gui :4.Shape Width (px)=int(30,0,100)
#@gui :5.Shape Height (px)=int(30,0,100)
#@gui :6.Shape Ratio (%)=float(100,5,100)
#@gui :7.Shape Rotation=float(0,-180,180)
#@gui :8.Shape Mirror Axis=choice(0,"None","X","Y")
#@gui :9.Subpixel Level=float(.5,0,2)
#@gui :10.Interpolation=choice(5,"Nearest","Average","Linear","Grid","Bicubic","Lanczos")
#@gui :11.Tile Boundary=choice(0,"Periodic","Mirror")
#@gui :12.Preprocess Boundary=choice(2,"Neumann","Periodic","Mirror")
#@gui :13.Output=choice(0,"New Dimension","Old Dimension","New Dimension - New Layer","Old Dimension - New Layer")
#@gui :_=note("<small>Output is disabled for Paint.NET!</small>")
#@gui :_=separator(),_=note("<b>Dynamic Shape</b>")
#@gui :14.Dragon Curve Recursion=int(10,0,30)
#@gui :15.Dragon Curve Rotation=float(0,-180,180)
#@gui :16.Fern Type=choice("Asplenium Adiantum-Nigrum","Thelypteridaceae")
#@gui :17.Fern Density (%)=float(100,0,300)
#@gui :18.Gear Teeth Count=int(8,3,32)
#@gui :19.Gear Height (%)=float(25,0.1,100)
#@gui :20.Gear Offset Teeth (%)=float(0,0,100)
#@gui :21.Gear Inner Ratio (%)=float(50,0.1,100)
#@gui :22.Polygon Vertices=int(5,3,100)
#@gui :23.Snowflake Recursion=int(5,1,6)
#@gui :24.Star Branches=int(3,5,100)
#@gui :25.Star Thickness (%)=float(38,.1,100)
#@gui :_=note("<small>Dynamic Shapes Options are only visible for dynamic shapes!</small>")
#@gui :_=separator(),_=note("<b>Paint.NET Workaround</b>")
#@gui :26.Export Mode=choice("New Dimension","New Dimension - New Layer")
#@gui :27.Folder=folder()
#@gui :28.Filename=text("tiledform.png")
#@gui :29.Export Images=button()
#@gui :_=note("<small>This section is only for Paint.NET users! If you don't use this this software, then you don't need use this section!</small>")
#@gui :_=separator(),_=note("Original idea comes from <a href="https://forums.getpaint.net/topic/26758-trs-tiled-pixels-v12/">TR's Tiled Pixels V1.2</a> plugin by <b>TechnoRobbo</b> for Paint.NET. This version is a extended version utilizing weighted average sampling per area per tile, boundary option, anti-aliasing, dynamic shape, color space,and transformation. Dynamic shapes are also supported by this filter.")
#@gui :_=separator(),Preview Type=choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse"), Preview Split = point(50,50,0,0,200,200,200,0,10)_0
#@gui :_=separator(),_=note("<small>Author: Reptorian. Latest Update: <i>2020/6/08</i>.</small>")
gui_rep_form_pixel:
dynamic_shape=1
if $3 rv fi
if $2
    if $2==7  
        form_id=dragon 
        form_var=${14-15}
    elif $2==9  
        form_id=fern
        form_var=$17%,0,1,$16
    elif $2==11 
        form_id=gear
        form_var=${18-21}
    elif $2==21 
        form_id=polygon
        form_var=$22
    elif $2==24 
        form_id=snowflake
        form_var=$23
    elif $2==25 
        form_id=star
        form_var=$24,{$25/100}
    else 
        form_id={$2-1} 
        form_var=""
    fi
fi
if !('$_host'=='paintdotnet')
    if $1
        repeat !$2?($!-1):$! l[$>]
            if $1!=2
                sh 0,2
                if $1==1 rgb2ryb.
                elif $1==3 rgb2hcy.
                elif $1==4 rgb2hsi.
                elif $1==5 rgb2hsl.
                elif $1==6 rgb2hsv.
                elif $1==7 rgb2lab.
                elif $1==8 rgb2lch.
                fi
                rm.
            else
                s c,-3 rgb2cmyk.. a c
            fi
        endl done
    fi
    if !$2 rv rep_form_pixel[^-1] [-1],${4-5},$6%,${7-13}
    else   rep_form_pixel $form_id,${4-5},$6%,${7-13},$form_var
    fi
    if $1
        repeat !$2?($!-1):$! l[$>]
            if $1!=2
                sh 0,2
                if $1==1 ryb2rgb.
                elif $1==3 hcy2rgb.
                elif $1==4 hsi2rgb.
                elif $1==5 hsl2rgb.
                elif $1==6 hsv2rgb.
                elif $1==7 lab2rgb.
                elif $1==8 lch2rgb.
                fi
                rm.
            else
                s c,-4 cmyk2rgb.. a c
            fi
        endl done
    fi
else
    filename="$27/$28"
    if $1
        l[0]
            if $1!=2
                sh 0,2
                if $1==1 rgb2ryb.
                elif $1==3 rgb2hcy.
                elif $1==4 rgb2hsi.
                elif $1==5 rgb2hsl.
                elif $1==6 rgb2hsv.
                elif $1==7 rgb2lab.
                elif $1==8 rgb2lch.
                fi
                rm.
            else
                s c,-3 rgb2cmyk.. a c
            fi
        endl
    fi
    
    if !$2
        if !$29 rep_form_pixel[0] [1],${4-5},$6%,${7-12},1 k[0]
        else    rep_form_pixel[0] [1],${4-5},$6%,${7-12},{$26*2} rm.
        fi
    else
        k[0]
        if !$29
                rep_form_pixel $form_id,${4-5},$6%,${7-12},1,$form_var
        else
                rep_form_pixel $form_id,${4-5},$6%,${7-12},{$26*2},$form_var
        fi
    fi
    
    if $1
        repeat $! l[$>]
            if $1!=2
                sh 0,2
                if $1==1 ryb2rgb.
                elif $1==3 hcy2rgb.
                elif $1==4 hsi2rgb.
                elif $1==5 hsl2rgb.
                elif $1==6 hsv2rgb.
                elif $1==7 lab2rgb.
                elif $1==8 lch2rgb.
                fi
                rm.
            else
                s c,-4 cmyk2rgb.. a c
            fi
        endl done
    fi
    if $29 o $filename fi    
fi

EDIT: Order looks fine on CLI. Just not on Krita. :confused:

Now I see why. It create new layer between the first and second one. That is not suppose to happen.

This needs a bit of work, but the basic idea is done:

gcd_separable_kernel : skip ${1=64}
  repeat $! l[$>] r $1,$1,1,1,3 b 4% +r 1,100%,1,1,3 r.. 100%,1,1,1,3 endl done

gcd_boxfilter_local3 :
  pass$1 0 r. {0,[w,h,d]},100%,0 round. 1 abs. store. arg_img
  pass$2 0 gcd_separable_kernel. {min(w,h)} store. vrt store. hrz
  repeat $! l[$>]
    i $arg_img i $vrt transpose.
    f[0] "begin(const boundary=1;const W=w#2);
      M=i#1; R=W/(M*2+1);
      for(S=0;T=0;k=-M,k<=M,k++,P=i(#2,(k+M)*R); T+=P; S+=j(0,k)*P) / T"
    rm. i $hrz
    f[0] "begin(const boundary=1;const W=w#2);
      M=i#1; R=W/(M*2+1);
      for(S=0;T=0;k=-M,k<=M,k++,P=i(#2,(k+M)*R); T+=P; S+=j(k)*P) / T"
    k[0]
  endl done

Example usage:

gmic sp +mirror x n. 1,50 300,300,1,1 gaussian. 60 gcd_boxfilter_local3[0] [1],[2]

That does a per pixel convolution on image [0] with kernel sizes of image [1] and a kernel given by image [2] (a gaussian in this case). You can see there’s a normalize in there which is to limit the kernel size. I’m not sure how useful the custom kernel can be, changes in effect are barely noticable.

Edit: @Reptorian just to be clear, when I say “needs work” that’s the part I leave to any potential user! This is the end of the experiment for me :slight_smile:

1 Like

Well, it’s now the end of the road update from me until another pandemic comes or paid vacation comes. That being said, I added TODO list on my github repository instead as it is easier to do. Furthermore, I have reformatted reptorian.gmic and only added one actual update.

Also, I will leave this file for future developers - GMIC collection.zip (13.5 KB)

The above are just notes.

3 Likes

There’s a slim chance I’ll be active here again as cases are spiking. Meaning another lockdown is possible. I’m not holding my breath on that.

Continues on job search


And not being successful at finding a job that I like yet while the demand for the job that I like is low in this area, I decided to take this up for a challenge.

@garagecoder Remember you did unroll and sort, and then append back to normal dimension?
I wanted to do that but without unrolling.

In theory, the code here below should work, but I’m not sure why it doesn’t.

#Failed attempt to mimic garagecoder unroll to avoid vector of 4096^2#
to_gray
{w},{h},1,3
1,1,1,3,0

eval ${-math_lib}"
const end_ind=wh#0;
const end_id=end_ind-1;
const ww=w#0;
s_ind=0;
n_ind=0;
calc_xp(a)=(s_ind+a)%ww;
calc_yp(a)=floor((s_ind+a)/ww);
nonexistentval=im#0-1;
vm=iM#0;
sm=0;
do(
    xc=calc_xp(n_ind);
    yc=calc_yp(n_ind);
    if(i(#0,xc,yc,0,0)<vm,
        sm=max(sm,i(#0,xc,yc,0,0))
    );
    if((i(#0,xc,yc,0,0)==vm)&&(i(#0,xc,yc,0,0)!=nonexistentval),
        v=[vm,xc,yc];
        dar_insert(#-1,v);
        i(#0,xc,yc,0,0)=nonexistentval;
    );
    if((s_ind+n_ind)==end_id,
        for(p=0,p<dar_size(#-1),p++,
            xcp=calc_xp(p);
            ycp=calc_yp(p);
            temp=print(I[#-1,p]);
            I(#1,xcp,ycp)=temp;
        );
        s_ind+=dar_size(#-1);
        n_ind=0;
        vm=sm;
        dar_remove(#-1,dar_size(#-1));
    ,n_ind++;
    );
,s_ind<end_ind
);
v;
"

EDIT: Shorter version, no success.

1 Like

As the code you handle is complex, at least to me, again, I have no idea what you are asking. What is the code above supposed to do and what are you trying to replace exactly?

s_ind=Reference id on where to insert dynamic vector into.
n_ind=Is used to loop beginning from s_ind. The loop is done to find the maximum value.
end_ind=dimension of the image.
end_id=id number of bottom-right number.
vm=maximum value of 1st image
sm=secondary maximum value
nonexistentval=do I need to explain?

Here’s what the code is supposed to do.

  1. Convert the image to black and white - 1st image
  2. Create a new image which will be the reordered vector. - 2nd image
  3. Create the dynamic array image. This will be used to insert value into the 2nd image. - 3rd image
  4. While s_ind is less than end_ind, create a loop reordering 1st image value from greatest to smallest into the 2nd image.

Note: Ideally, the first image would be exactly nonexistentval with no variance meaning the second image is the reordered result. The 2nd, and 3rd channel of 2nd image is x id , and y id.

Only slightly clearer. :sweat_smile: One thing that may help is to split eval into parts; separate evals. That way you can concentrate on one part of the process at a time.

I think I did it, but it takes forever to run. Maybe there’s a problem I don’t know of.

#Failed attempt to mimic garagecoder unroll to avoid vector of 4096^2#
to_gray
{w},{h},1,3
1,1,1,3,0

eval ${-math_lib}"
const end_ind=wh#0;
const end_id=end_ind-1;
const ww=w#0;
s_ind=0;
n_ind=0;
calc_xp(a)=(s_ind+a)%ww;
calc_yp(a)=floor((s_ind+a)/ww);
nonexistentval=im#0-1;
vm=iM#0;
sm=0;
ms=0;
while(s_ind<end_ind,
    xc=calc_xp(n_ind);
    yc=calc_yp(n_ind);
    if(i(#0,xc,yc,0,0)<vm,sm=max(sm,i(#0,xc,yc,0,0)));
    if((i(#0,xc,yc,0,0)==vm)&&(i(#0,xc,yc,0,0)!=nonexistentval),
        v=[vm,xc,yc];
        dar_insert(#-1,v);
        i(#0,xc,yc,0,0)=nonexistentval;
        ms++;
    );
    if((s_ind+n_ind)==end_id,
        for(p=0,p<ms,p++,
            xcp=calc_xp(p);
            ycp=calc_yp(p);
            temp=print(I[#-1,p]);
            I(#1,xcp,ycp)=temp;
        );
        s_ind+=ms;
        n_ind=0;
        vm=sm;
        sm=0;
        ms=0;
        dar_remove(#-1,dar_size(#-1));
    ,n_ind++;
    );
);
s_ind;
dar_size(#-1);
"

Even for a small image.

You got that right. I tried it on sample tiger,5. Why is while looping so many times? What is that barely changing value that you are printing?