G'MIC exercises

Thanks, normally I look at the online gmic reference. Didn’t find it there.

I had not succeeded with drawing tiles. The end of the line seem to be the problem. Why is it that the first channel shows tiles, but the other channels are not affected?

Note: This is new_rep_form_pixel, not rep_form_pixel. The speed can be .25 to 2 times faster as according to the timing test.

draw(#0,tile,x*tw,y*th,z,c,tw,th,1,1);
Full Code
#@cli new_rep_form_pixel: form_id,form_quad_lx!=0,form_quad_ly!=0,_form_ratio[%]!=0,_angle, 0<=_reflect_dir<=2,_sublevel,_tile_boundary={ 0=periodic | 1=mirror_x | 2=mirror_y | 3=mirror },_image_boundary={ 0=neumann | 1=periodic | 2=mirror},_abs_zconvf>=0,_zconv_boundary={0=periodic | 1= mirror},_interpolation={ 0=nearest | 1=average | 2=grid | 3=linear | 4=bicubic | 5=lanczos},cs_mode={ 0=rgb | 1=ryb | 2=cmyk | 3=hcy | 4=hsi | 5=hsl | 6=hsv | 7=lab | 8=lch },_fit_tile={ 0=squash_to_fit | 1=resize_to_fit },_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 : _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 : _abs_zconvf defines the contrast level of each tiles using z-convolution within a small color tile reference 3D image.
#@cli : _zconv_boundary defines how out-of-range value within z-convolution kernet are treated.
#@cli : _interpolation refers to the interpolation of the scaling of shapes to fit each tile. See 'gmic h resize' _interpolation.
#@cli : _cs_mode refers to the color processing space that form_pixel filter utilize.
#@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 : _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.
#@cli :
#@cli : -- form_id --
#@cli : string | shape_name_id | shape_options
#@cli :
#@cli : [image] ---- image_as_shape
#@cli : 0  - australia
#@cli : 1  - barbedwire
#@cli : 2  - circle
#@cli : 3  - crosshair
#@cli : 4  - cupid
#@cli : 5  - diamond
#@cli : 6  - dragon ------ dragon_recursion>=0,-360<=dragon_curve_rotation_angle>=360
#@cli : 7  - dragonfly
#@cli : 8  - fern - size>=0,density[%]>=0,ang,0<=opacity<=1,type={ 0=Black Spleenwort | 1=Marsh }
#@cli : 9  - flip
#@cli : 10 - gear  ------- teeth>0,0<height<=100,0<=offset_teeth=100,0<ratio<=100
#@cli : 11 - gumleaf
#@cli : 12 - heart
#@cli : 13 - information
#@cli : 14 - kookaburra
#@cli : 15 - mail
#@cli : 16 - mapleleaf
#@cli : 17 - paint_splat
#@cli : 18 - paw
#@cli : 19 - phone
#@cli : 20 - polygon  ------- vertices>0
#@cli : 21 - rooster
#@cli : 22 - shopping_cart
#@cli : 23 - snowflake  ------- 1<=recursion<=6
#@cli : 24 - star  ------- branches>3,thickness[%]
#@cli :
#@cli : -- end of form_id --
#@cli :
#@cli : Note: _shape_option_1 can be empty. n refers to corresponding shape option.
#@cli : Default value: '_form_ratio=1','_ang=0','_reflect_dir=0','_sublevel=.5','_interpolation=2','tile_boundary=0','_image_boundary=2','_fit_tile=1','_kodl=0'\n
#@cli :
#@cli : Author: Reptorian.
#@cli : $ sp landscape new_rep_form_pixel star,20,,90%,45,0,1,1,2,75%,0,5,7,1,1,7,.5
new_rep_form_pixel:
skip ${2=},${3=},${4=},${5=},${6=},${7=},${8=},${9=},${10=},${11=},${12=},${13=},${14=},${15=}

if narg($2)  form_quad_lx=$2 else form_quad_lx=$3 fi
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=2 fi
if narg($8)  tile_boundary={abs($8)} else tile_boundary=0 fi
if narg($9)  image_boundary={abs($9)} else image_boundary=2 fi
if narg($10) z_convolve={abs($10)} else z_convolve=.5 fi
if narg($11) z_convolve_boundary={abs($11)%2+1} else z_convolve_boundary=1 fi
if narg($12) interpolation={abs($12)} else interpolation=5 fi
if narg($13) cs_mode={abs($13)%10} else cs_mode=7 fi
if narg($14) fit_tile={abs($14)} else fit_tile=1 fi
if narg($15) var_kodl={round(abs($15))%4} else var_kodl=0 fi

convert_colors_fwd=${arg\ 1+$cs_mode,none,rgb2ryb,cs_error,rgb2hcy,rgb2hsi,rgb2hsl,rgb2hsv,rgb2lab,rgb2lch}
convert_colors_bwd=${arg\ 1+$cs_mode,none,ryb2rgb,cs_error,hcy2rgb,hsi2rgb,hsl2rgb,hsv2rgb,lab2rgb,lch2rgb}

if $cs_mode>=0&&$cs_mode<3 cutf_vals=0,255,0,255,0,255,0,255
elif $cs_mode>=3&&$cs_mode<7 cutf_vals=0,360,0,100,0,1
elif $cs_mode==7 cutf_vals=0,100,-176,176,-176,176
else cutf_vals=0,100,0,255,{-pi},{pi}
fi

if $z_convolve
 offpixel={$z_convolve*-1}
 onpixel={($z_convolve)*2+1}
 ($offpixel/$onpixel/$offpixel)
 store. zmap
fi

__tw=abs($form_quad_lx)
__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 $#>15 shape_$sid {max($__tw,$__th)*$sub},${16--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 $ang-360*int($ang/360) 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. 200%,200%,100%,100%,0,2
elif $tile_boundary==1
 r. 200%,100%,100%,100%,0,3
 r. 100%,200%,100%,100%,0,2
elif $tile_boundary==2
 r. 100%,200%,100%,100%,0,3
 r. 200%,100%,100%,100%,0,2
elif $tile_boundary==3
 r. 200%,200%,100%,100%,0,3
fi

form_1={crop(#-1,0,0,0,0,$__tw,$__th,1,1)}
form_2={crop(#-1,$__tw,0,0,0,$__tw,$__th,1,1)}
form_3={crop(#-1,0,$__th,0,0,$__tw,$__th,1,1)}
form_4={crop(#-1,$__tw,$__th,0,0,$__tw,$__th,1,1)}
avgc={avg($form_1)}

rm.

repeat $! l[$>]

 rw={ceil(w#0/$__tw)*$__tw}
 rh={ceil(h#0/$__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

 
 if $cs_mode&&(s#$<>=3)
  if $cs_mode!=2
   $convert_colors_fwd.
  else
   if s#$<==3 rgb2cmyk.
   else s c,-3 rgb2cmyk.. a c
   fi
  fi
 fi
 
 itw={w/$__tw}
 ith={h/$__th}
 
 $itw,$ith,1,100%,:"begin(
  const tw="$__tw";
  const th="$__th";
  const wwhh=tw*th;
  const w_avg=wwhh*"$avgc";
  const iw_avg=wwhh-w_avg;
  form_0=["$form_1"];
  form_1=["$form_2"];
  form_2=["$form_3"];
  form_3=["$form_4"];
  inv_form_0=1-form_0;
  inv_form_1=1-form_1;
  inv_form_2=1-form_2;
  inv_form_3=1-form_3;
 );
 form_x=x%2;
 form_y=y%2*2;
 form=form_x+form_y;
 img_t=crop(#0,x*tw,y*th,z,c,tw,th,1,1);
 tile=vectorwwhh();
 form==3?(
  inside_shape_color=sum(img_t*form_3)/w_avg;
  outside_shape_color=sum(img_t*inv_form_3)/iw_avg;
  fill(tile,p,lerp(outside_shape_color,inside_shape_color,form_3[p]));
 ):
 form==2?(
  inside_shape_color=sum(img_t*form_2)/w_avg;
  outside_shape_color=sum(img_t*inv_form_2)/iw_avg;
  fill(tile,p,lerp(outside_shape_color,inside_shape_color,form_2[p]));
 ):
 form==1?(
  inside_shape_color=sum(img_t*form_1)/w_avg;
  outside_shape_color=sum(img_t*inv_form_1)/iw_avg;
  fill(tile,p,lerp(outside_shape_color,inside_shape_color,form_1[p]));
 ):(
  inside_shape_color=sum(img_t*form_0)/w_avg;
  outside_shape_color=sum(img_t*inv_form_0)/iw_avg;
  fill(tile,p,lerp(outside_shape_color,inside_shape_color,form_0[p]));
 );
 draw(#0,tile,x*tw,y*th,z,c,tw,th,1,1);
 "
 rm.
endl done

As usual, this is simpler than Rep’s questions. :stuck_out_tongue:

I would like to perform a median or something else on specific pixels under certain conditions in a neighbourhood. Say I want to do it within a certain range. I guess I would have to sort, cut and truncate N? Is this correct? What would the code be?

foo :
  f "
    N=crop(x-1,y-1,0,c,3,3,1,1);
    # M=???;
    med(M)
  "

I think that would require getting around const error.

foo :
  f "
    M=vector2();
    N=crop(x-1,y-1,0,c,3,3,1,1);
    rN=inrange(N,2,15,1);
    resize(M,abs(sum(rN)),-1,1);
    rN;
  "

Best attempt, but resize line has error output. Might actually need a multiple images to do the job.

I’d suggest you replace the values you don’t want to deal with, in vector N, by inf, then use N_sorted = sort(N) and get the median value from there.
Something as

val = N_sorted[sN/2];

where sN is the number of valid entries (not inf), in N_sorted.

I don’t know how to replace the values properly. In the example below, the 1s and 2 turn into 0s, which is what I don’t want.

foo :
  3,3,1,1,x+y f "
    N=crop(x-1,y-1,0,c,3,3,1,1);
    print(N);
    M=fill(N,k,inrange(N[k],1,3)?i:inf);
    print(M);
  " q
# [gmic_math_parser] N = [ 0,0,0,0,0,1,0,1,2 ] (size: 9)
# [gmic_math_parser] M = [ inf,inf,inf,inf,inf,0,inf,0,0 ] (size: 9)
# ...

Check inrange in gmic reference, and you’ll find that you can set whether the left/right value is part of the allowed set of numbers. There is also bool method.

M=fill(N,k,inrange(N[k],1,3)?i:inf);    # incorrect
M=fill(N,k,inrange(N[k],1,3)?N[k]:inf); # correct
1 Like

I don’t know how to determine sN yet. Also, if I were to evaluate something other than the median (e.g., average), changing unwanted values to inf doesn’t seem to help.


In general, my questions concerning local acting filters involve the membership of pixels based on value or space.

Could it be possible to create dynamic vectors per thread? I have a feeling this wouldn’t help because JIT wouldn’t allow vectors to be of different size. Seem like multiple image is the solution. Sorting to filter out inf and inserted into one image and using another to define counts per pixels. But, you are going to write multiple times slowing down the processing speed.

I should attempt this when I wake up.

For computing an average, you actually don’t need to store your values in a vector, as an average is just a weighted sum, and it can be computed in a “streamed” way (some for the min and max is needed).

More generally, if you need to deal with ‘variable-sized vectors’ to store local neighboring values in a filter, it is generally good to manage a large-enough vector and keep track of the number N of “valid” values you put in it, for a specific local configuration of your image.
Then, you can do (almost) anything you want with the subset [0,N-1] of values in your vector.

At least I have not encountered any impossibility so far.

I think I got it… Apparently, fill() evaluates in-place, so I will have to copy the input beforehand.

foo :
  3,3,1,1,x+y
  f "

    N0=crop(x-1,y-1,0,c,3,3,1,1);
    print(N0);

    N1=fill(N0,k,inrange(N0[k],1,3)?N0[k]:inf);
    print(N1);

    N2=sort(N1);
    print(N2);

    N3=N2;
    fill(N3,k,N3[k]!=inf?N3[k]=1:0);
    print(N3);

    N4=sum(N3);
    print(N4);

    N5=N2[N4/2];
    print(N5);

  " q
# [gmic_math_parser] N0 = [ 0,0,0,0,0,1,0,1,2 ] (size: 9)
# [gmic_math_parser] N1 = [ inf,inf,inf,inf,inf,1,inf,1,2 ] (size: 9)
# [gmic_math_parser] N2 = [ 1,1,2,inf,inf,inf,inf,inf,inf ] (size: 9)
# [gmic_math_parser] N3 = [ 1,1,1,0,0,0,0,0,0 ] (size: 9)
# [gmic_math_parser] N4 = 3
# [gmic_math_parser] N5 = 1
# ...

The next order of business is to find the median only if the centre pixel is an outlier and the neighbourhood has inliers. Otherwise, we would be overwriting data with non data.

1 Like

Question: how to get a vector of pal $1 or palette $1 directly within math evaluator? But, with order from first pixel and from first channel to third channel, then go into next pixel. I could always go by crop and reorder method if there is no answer to this.

I guess you could loop with an index offset for each xy.

I was more thinking of calling a command from the math evaluator and getting the vector from it.

I was thinking of it like this. Numbers correspond to normal pixel order.

R | 00 01 02 03
G | 04 05 06 07
B | 08 09 10 11

Instead, we act on 00 04 08, then 01 05 09, etc.

That is what I want. I think my request calling pal or palette into math evaluator can’t be done. I decided I would have to resort to doing this.

foo:
pal $1
permute. cxyz
color_vector={crop(#-1)}
rm.
f. "begin(v=$color_vector);
v[x%size(v)];
"

Something like that.

I’m trying to convert back into hex.

rep_argrgb2hex:
u {"vchar=["$*"];
 const ovs=size(vchar);
 const nvs=size(vchar)*2;
 nv=vectornvs(0);
 to_char(v)=v>=0&&v<=9?48+v:87+v;
 repeat(ovs,p,
  off=2*p;
  nv[off]=to_char(int(vchar[p]/16));
  nv[off+1]=to_char(int(vchar[p]%16));
 );
 (nv);
"}
rep_hex2argrgb:
strlowercase $1
u {"vchar=('"${}"');
 const nvs=int(size(vchar)/2);
 nv=vectornvs(0);
 from_char(v)=v>=48&&v<=57?v-48:v-87;
 repeat(nvs,p,
  off=2*p;
  nv[p]=from_char(vchar[off])*16+from_char(vchar[off+1]);
 );
 (nv);
 "}

The first case does not work:

C:\Windows\System32>gmic rep_argrgb2hex 32,158,53 echo ${}
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ 50,48,57,101,51,53
[gmic]-0./ End G'MIC interpreter.

The second case works:

C:\Windows\System32>gmic rep_hex2argrgb 209E35 echo ${}
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ 32,158,53
[gmic]-0./ End G'MIC interpreter.

And the use case is for allowing users to input hex as color arguments, and I’m going off from using it in the math parser akin to use_percent_mode="${is_percent \$1}".

Edit: This requires manipulation of c++ code to enable conversion of vector of int to vector of char, doesn’t it? I’ll have to see to that.

Edit (Solved): I added this line - (${}) u {t} rm. to first case and it works. Needless to say, a bit slow. .002 s.


Ok, new problem:

#Expected Output - min = 1 ; max = 5 ; #
#Actual Output - min = 1 ; max =2 ; #
256,256,1,1,"m=round(u(0,2));
m==0?(5;continue();2;
):m==1?(3;continue();2;
):(1
);"

How exactly do I break away?

Folks, I came across something that could be an exercise for you all. Piqued my interest because of the terms splat and slice—well, mostly splat.

I want to improve on this by rid of the additional “|” and have it as a new line.

  rep_thorn_fractal:
      -inf<=style<=47,_escape>0,_iteration>0,_subsampling_level>=1,_dx,_dy,
        _r_xy>0,_r_x>0,_r_y>0,_r_pi= { 0=r_xy multiplied by 1 | 1=r_xy
        multiplied by pi },_o_x,_o_y,_function_angle,_vx,_vy,..._vx_n,vy_n |
       |
       -inf<=style<=47,_escape>0,_iteration>0,_subsampling_level>=1,_dx,_dy,
        _r_xy>0,_r_x>0,_r_y>0,_r_pi= { 0=r_xy multiplied by 1 | 1=r_xy
        multiplied by pi },_o_x,_o_y,_function_angle,_vx,_vy,..._vx_n,vy_n,
        ovx,ovy,overload_freq>=1 |
       |
       -inf<=style<=47,_escape>0,_iteration>0,_subsampling_level>=1,_dx,_dy,
        _r_xy>0,_r_x>0,_r_y>0,_r_pi= { 0=r_xy multiplied by 1 | 1=r_xy
        multiplied by pi },_o_x,_o_y,_function_angle,_vx,_vy,..._vx_n,vy_n,
        ovx,ovy,cfa,cfb,overload_freq>=1