Reptorian G'MIC Filters

Could you edit back in something for post #229 because it will look like I am replying to nothing?

1 Like

Well, I fixed some issue, and sped up the filter by 4 seconds. rep_tiles_shape also ignores areas where variance is zero, and therefore speed up calculation. Problem, when variables isn’t inserted, but is between commas, then it shows up as undefined variable. skip … isn’t gonna cut it it seems.

This line below doesn’t fix the issue -

if narg($*)>=8 skip ${5=0},${6=0} fi

Skip are actually applied as if narg($*)>=8 is completely ignored.

Yes, it seems that skip does its own thing regardless where it is. My multi-skip use doesn’t work either.

I’ll file a bug report about that.

Just wanna leave this here so I don’t forget

val_1=.05
val_2=.6
val_1=abs($val_1)
val_2=abs($val_2)
t_v=$val_1+$val_2
+channels {s-1}
f. i>0?1:0
+distance. 0,0
rv[^0]
blend[^0] shapemax0
min_a={min($val_1,$val_2)*iM#-1}
max_a={max($val_1,$val_2)*iM#-1}
sh[0] {s#0-1}
f. i0#-2<$min_a||i0#-2>$max_a?0:i
rm[-2,-1]

val_1 and val_2 must be in a range of 0-1.
Apply this to a image with alpha, and with many different separate objects of different size. See what happens.

EDIT: Forgot to mention that I’m redoing the rep_tiles_shape all over again, but keeping the blend_shape_average more as inspiration source. The method I’m doing is without using apply_tiles, and I think it is faster, but I need to finish the code to verify. For now, here’s what I have. I just need to figure out how to stitch them together.

fill_ratio=1
tw=200
th=200
sub=1
sub+=1
interpolation=4
interpolation+=1
r[0] {ceil(w/$tw)*$tw},{ceil(h/$th)*$th},100%,100%,0,1,.5,.5
shape 2,{max($tw,$th)*$sub}
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*$fill_ratio},{$resize_height*$fill_ratio},1,1,$interpolation,0,.5,.5
    r. {$tw},{$th},1,1,0,0,.5,.5
    cut. 0,1
rv
s[^0] x,{w/$tw}
ti_w={$!-1}
s[^0] y,{h/$th}
ti_h={($!-1)/$ti_w}
repeat $!-1 l[0,{$>+1}] .
repeat s#-1
sh. $>
if iv#-1!=0
sh... $>
f. i*i0#0
f.. i*(1-i0#0)
f.. is/((1-ia#0)*w*h)
f. is/(ia#0*w*h)
f.. i#-1*i0#0+i*(1-i0#0)
rm[^0-2]
else
rm.
fi
done
rm..
endl done

FYI, $# in a command gives the number of specified arguments, so basically $# = narg($*) .
I’m not sure there is any interest in using narg($*) then.

I’m not sure about what you expect here, but let me develop a bit on command instanciation:
When a G’MIC command is invoked in a script, the interpreter does the following things in this order:

  1. Command instanciation: the interpreter generates a new instance of the command, where all the arguments $1,.. are replaced by their provided values. So, basically, for each command call with different arguments, the interpreter internally generates a different version of the command to be executed.
  2. Command execution: the interpreter begins the interpretation of the instanced command.

So, you really have to view the command argument-related strings ${3=something} as something very different from variables. Their substitution is not done at the same time
(argument values are substituted during command instanciation, while variable values are substituted during command execution).
This particularly means that a string like ${3=something} in a command can be put anywhere, the argument $3 will always be substituted by something if it is not specified by the command caller. You cannot decide to put its default value only in a portion of code. (what could be its value anywhere else, if the argument is not specified ?).

If you want to deal with conditional values for arguments, just use variables instead. You are sure they will be substituted during command execution. Something like:

foo: skip ${3=}
  if narg($3) arg3=$3 else arg3=foo fi
  echo "ARG3  = "$arg3
1 Like

Ended up deciding more work on the tiled filter. Passed my 3 days… Anyways, I actually have something new here. Probably can replace the Kaleidoscope [Polar] made by @David_Tschumperle

1 Like

By just looking at the GUI, it looks nice. Three questions arise:

– Is this filter based on something else?
– What is the rectanglar box on the Center (%) line?
– What is Conical Start at 0? What and where is 0?

  1. It is based on Xhim’s Polar Kaleidoscope filter for Paint.NET, I just extended it.

  2. Color indicator

  3. atan2(yy,xx) - xx and yy are in -1,1 ranges for square and represents coordinate. This is the standard formula for conical gradient. If you test this, you’d note that the line start from the left.

1 Should credit be given?

2 For what? Doesn’t seem related to centering…

3 Don’t know if the user would understand this. I think the typical user would understand a cardinal direction.

  1. I’m not sure, but I will add credit anyway.
  2. Look at the interactive gui picture, there is a white dot that can be used to change position of filter.
  3. They don’t need to understand it unless they were using similar filter from another software. But, it just a ignorable note.

2 So the dot is a mouse-movable control point? And the rectangle is for the user to choose its colour? Maybe make a note on the control point.

3 So it changes where the pattern starts? There is probably a better way to inform the user.

2 You can’t change the color unless you do it by code. I wish you could.
3 You’re right.

I have some other news, I successfully had made object size filtering.

#@cli rep_objvf: (eq. to rep_objvolumefilt)
rep_objvf: rep_objvolumefilt $*
#@cli rep_objvolumefilt: 0<=_v1<=1,0<=_v2<=1,_mode= { 0=preserve || 1=eliminate },_colour1...
#@cli:_v1 and _v2 refers to boundary in percent. By default, this command use alpha as reference unless all channel values are specified next to mode.
#@cli: Default value: '_mode=1'
rep_objvolumefilt:
skip ${3=1}
repeat $! l[$>]
if $#>3
+f [${4--1}]
+eq
compose_channels. *
f. 1-i
rm..
a c
fi
val_a=$1
val_b=$2
minv={min($val_a,$val_b)}
maxv={max($val_a,$val_b)}
if ($1<-1||$1>1)&&($2<-1||$2>1) v + error "Invalid numbers for Variable 1 and/or 2." v - fi
if $1==$2 v + error "Variable 1 and 2 can't be the same" v - fi
ek=$3
vv=0
repeat s#0
sh[0] $>
vv+={iv#-1}
rm.
done
if $vv!=0
+channels {s-1}
f. i==0?0:1 .
f[2] "begin(A = resize([ 0,(s#0-1)],s,s#0-1));I+A" norm[2] round[2] 0.01 label[2] 0 {iM+1},1,1,{0,1}
f[1] ">i(#3,i(#2,x,y,z,0),0,0,c)+=i;i" n. 0,1 map[2] . 
f. i==im#-1?1:i
min_pas_zero={im#-1}
tmm={1-$min_pas_zero}
new_minv={$minv*$tmm+$min_pas_zero}
sh[0] {s#0-1}
rm[1,3]
f. i#-2<$new_minv||i#-2>$maxv?($ek?0:i):($ek?i:0)
rm[1,2]
fi
if $#>3
s c,-{s-1}
+f.. [${4--1}]
f... i0#-2?i#-1:i
rm[1,2]
fi
endl done

#@gui Object Size Filtering: gui_rep_objvf,gui_rep_objvf_preview(0)
#@gui : note = note("The purpose of this filter is to filter objects based on their volume. It is based on the principle of the <a href="https://forums.getpaint.net/topic/113962-object-pruner/">Object Pruner plugin</a> for Paint.NET made by <b>MJW</b>."), sep = separator()
#@gui : Mode of Object Filtering = choice(0,"Alpha-Based","Colour - 3 Channels","Colour - 4 Channels")
#@gui : sep = separator(), note = note("<b>Object Filtering Options</b>")
#@gui : Volume 1 = float(0,0,100)
#@gui : Volume 2 = float(100,0,100)
#@gui : Eliminate Objects? = bool(1)
#@gui : Colour = color(0,0,0)
#@gui : Colour = color(0,0,0,0)
#@gui : sep = separator(), note = note("<b>Preview Settings</b>")
#@gui : Reveal Erased Objects? = bool(1)
#@gui : Colour of Erased Object = color(0,0,0)
#@gui : sep  = separator(), note = note("<small>Author: Reptorian.      Latest Update: <i>2019/9/8</i>.</small>")
gui_rep_objvf:
if $1==0 rep_objvf {$2/100},{$3/100},$4
elif $1==1 rep_objvf {$2/100},{$3/100},$4,${5-7}
else rep_objvf {$2/100},{$3/100},$4,${8-11}
fi
gui_rep_objvf_preview:
if $12
    if $1==0 +rep_objvf {$2/100},{$3/100},$4
    elif $1==1 +rep_objvf {$2/100},{$3/100},$4,${5-7}
    else +rep_objvf {$2/100},{$3/100},$4,${8-11} fi
    +eq compose_channels. * *. 255 negate. rm.. {w#0},{h#0},1,3,[${13-15}]
    rv[-2,-1] a[-2,-1] c
    blend normal
else
    if $1==0 +rep_objvf {$2/100},{$3/100},$4
    elif $1==1 +rep_objvf {$2/100},{$3/100},$4,${5-7}
    else +rep_objvf {$2/100},{$3/100},$4,${8-11} fi
fi
u "{$1}"\
"{$2}"\
"{$3}"\
"{$4}"\
"{$5}_"{$1==1?2:0}\
"{$6}"\
"{$7}"\
"{$8}_"{$1==2?2:0}\
"{$9}"\
"{$10}"\
"{$11}"\
"{$12}"\
"{$13}"\
"{$14}"\
"{$15}"

I just have one little problem here. How exactly do I hide the color option? It doesn’t work for some reason.


Question above still apply. New update. I have recoded my tiled pixel altogether.

EDIT: [Older code deleted as it’s outdated]


@afre @David_Tschumperle Now I’m stumped as to why [0] can’t be used as reference when using apply_parallel. So, I had to add $isnis condition to get around it. So slow… Do I have to apply_parallel to multiple lines here just to get around that?

#@cli rep_form_pixel: _form_id<24,_form_quad_lx!=0,_form_quad_ly!=0,_form_ratio[%]!=0,_angle,0<=_reflect<=2,_sublevel,_interpolation<6,_shape_option_1..._shape_option_n
#@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.
#@cli: _shape_option_1 can be empty. n refers to corresponding shape option.
#@cli: Default value: '_form_ratio=1','_ang=0','_reflect=0','_sublevel=.5','_interpolation=2'
rep_form_pixel:
sid="$1"
isnum_sid={isnum($sid)}
isint_sid={isint($sid)}
isnis={$isnum_sid&&$isint_sid}
if $isnis if $sid>=0 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
isnum_sid={isnum($sid)}
isint_sid={isint($sid)}
isnis={$isnum_sid&&$isint_sid}
if $isnis _rep_form_pixel $*
else apply_parallel "_rep_form_pixel $*" fi
_rep_form_pixel:
skip ${4=},${5=},${6=},${7=},${8=}
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=2 fi


sid="$1"
isnum_sid={isnum($sid)}
isint_sid={isint($sid)}
isnis={$isnum_sid&&$isint_sid}

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
if $form_ratio==0||$form_ratio>1 v + error "Invalid Form Ratio Number!" v - fi

if $isnis if $sid>=0 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

isnum_sid={isnum($sid)}
isint_sid={isint($sid)}
isnis={$isnum_sid&&$isint_sid}

l[0] 
    if $isnis . 
    else 
        if $#>8 shape_$sid {max($tw,$th)*$sub},${9--1} 
        else shape_$sid {max($tw,$th)*$sub} 
        fi 
        rv 
    fi 
endl

if $mi==1 mirror[$isnis] x elif $mi==2 mirror[$isnis] y fi
if abs($ang)%360!=0 rotate[$isnis] $ang,1 fi
n[$isnis] 0,1
if $isnis
    if s#0<=4&&s#0>1 l[$isnis] s c,{if(s==4,-3,if(s==2,-1,-s))} if $!==2 to_gray.. *[-2,-1] else to_gray fi endl 
    elif s#0==5 l[$isnis] s c,-4 cmyk2rgb.. to_gray.. *[-2,-1] endl fi
fi
autocrop[$isnis] 0
r[^0,$isnis] {ceil(w/$tw)*$tw},{ceil(h/$th)*$th},100%,100%,0,1,.5,.5
shape_image_ratio={w#{$isnis}/h#{$isnis}}
target_image_ratio={$tw/$th}
resize_width={$target_image_ratio>$shape_image_ratio?w#{$isnis}*($th/h#{$isnis}):$tw}
resize_height={$target_image_ratio>$shape_image_ratio?$th:h#{$isnis}*($tw/w#{$isnis})}
r[{$isnis}] {$resize_width*$form_ratio},{$resize_height*$form_ratio},1,1,$interpolation,0,.5,.5
r[{$isnis}] {$tw},{$th},1,1,0,0,.5,.5
cut[{$isnis}] 0,1 n[{$isnis}] 0,1
ti_l={$!-1}
repeat $ti_l-$isnis l[$isnis,{$>+1+$isnis}]
    s. x,{w/$tw}
    ti_w={$!-1}
    s[^0] y,{h/$th}
    ti_h={($!-1)/$ti_w}
    repeat $!-1 l[0,{$>+1}] 
        .
        repeat s#-1
            sh. $>
            if iv#-1!=0
                sh... $>
                f. i*i0#0
                f.. i*(1-i0#0)
                f.. is/((1-ia#0)*w*h)
                f. is/(ia#0*w*h)
                f.. i#-1*i0#0+i*(1-i0#0)
                rm[^0-2]
            else
                rm.
            fi
        done
        rm..
    endl done
    l[^0]
        repeat $ti_w
            a[{$<*$ti_h}-{($<+1)*$ti_h-1}] y
        done
        a x
    endl
endl done
rm[$isnis]

EDIT: Oh, changes in image stack don’t seem to be allowed. So, how would I get around that? I believe the issue is related to l[0] . endl. The thing is that it is removed. So, I don’t know how to get around it.

Was reading the post then you edited the post 1-2 times… :sweat_smile: What do you mean by changing the stack? Could you share a code snippet that you are having trouble with and explain it step by step?

I don’t think I could find a short code that demonstrate my issue as I don’t know where is the issue. It should work.

A short command that reveals you can’t create new image with apo would be apo “.” . That is not related to my issue.

All I know is that if I change the current rep_form_pixel to rep_form_pixel: apply_parallel “_rep_form_pixel $*” , typing in rep_form_pixel -1,20,20, nothing gets changed. But yet, _rep_form_pixel -1,20,20 works.

The rep_pixel_code works like this

1 Setting up to using shape as reference image or using the [0] as reference by using a copy of it to avoid editing the original [0].

2 Then, it sets up basic transformation changes into the [$isnis]. [$isnis] layer is used as shape reference per tile.

3 Then, you can see that the shape reference [$isnis] are resized to fit specified tile size from tw, and th. Cut 0,1 and n 0,1 is used to ensure that $isnis stay in the 0,1 range.

4 Apply shape per every tiles per images. That’s the repeat $ti_l-$isnis l[$isnis,{$>+1$isnis}] block is about.

4a The deepest repeat block is an algorithm that use 0,1 image to transfer the average color per tone. Average sampling duo tone block.

4b After the repeat block, the tiles are reattached

5 Remove the shape layer reference

Also, I found out that separating the big repeat block as a new command and using apply_parallel just does not work.


Edit: Does the fact that it doesn’t work with -1 has anything to do differing image size?

Obvious but important questions to ask and then verify.
1 Are you escaping properly?

2 Are the parameters corresponding to the correct variables?
– Don’t assume they are.

3 Are there commands or conditions that apply_parallel cannot do or handle?
– I find shared for instance can sometimes cause things to fail.

1 What do you mean by that? No error throws up with _rep_form_pixels. Do I have to add something somewhere?

2 I checked, they work and are correct. You can check for yourself. -1 use [0] as shape layer while circle or 2 use circle, then change pixels to tiles of shapes, and all the other parameters correspond.

3 I guess that might be the problem, but it doesn’t explain how the apply_parallel works for defined shape without fail.

4 I don’t think this is getting anywhere to solving the issue. @Joan_Rake1 and David is a bit busy.

Sorry for not being helpful in tangible ways. Too over-exhausted.

1 The fact that you are quoting in apply_parallel means that you cannot just plop _rep_form_pixel $* next to it and assume it would work. Maybe double quoting isn’t enough.

2 As long as you double checked… I had lots of trouble with some of my own code; then I checked at every juncture and found that I made a mistake or misunderstood how G’MIC inherited the variable values.

3 Sure, it might work in the defined shape case but the rest of the code is different and that part may be why it doesn’t work or at least work as expected.

I think now I have a idea. I’ll have to change code to always use [0] as reference within _rep_form_pixels. But, within rep_form_pixels. I’ll make a copy of [0] and make the command apply to [1,{$!-1}]

That should do it.

I agree and that sounds roughly how I handled the matter in my case. (Just had trouble expressing myself yesterday.) I am wondering why you did [1,{$!-1}] instead of [1,-1]. Would like to know for my education.