Reptorian G'MIC Filters

Now, going back to Photomosaic work again.

Top: RGB Indexing
Bottom: RYB Indexing

It seems that RYB indexing is better for skin. But, at the same time, it looks a little choppy. RGB remains the cleanest with some coloring artifacts, but it’s not a bug either.

It’s hard to say which one is better.
Looking at the guy :
I can recognize him easily with RVB indexing but the colors are a bit dull (mouth, chin, jaw)
I can barely identify him with RYB indexing. Though colors look a bit better, they are a bit muddy.

If you can get rid of the grayish colors in the RGB mode, a saturation boost maybe?

1 Like

Hmm, the solution is to change the picture input. But, that’s up to the user. I ended up making the decision to simply remove RYB indexing. There’s CMYK mode, but I put it as last option for the CLI filter because almost no one is going to use that, and it’s there because I do have a policy to support CMYK mode as a option as it isn’t intrusive if it isn’t accessible or rarely used.

I think I’m finished with the CLI version of Photomosaic. GUI is next to be addressed. One can play with the code:

CLI version of Photomosaic Index
#@cli rep_photomosaic_index: { foldername | [images] },tile_width>0,tile_height>0,0<=_dithering[%]<=100%,_channel_indexing_mode={ 0=color_only | 1=all_channels | all_channels_alpha_max },_alpha_threshold>=0,_crop_alpha_mode={ 0=no_changes | 1=crop_to_alpha_then_crop_to_dim | 2=resize_alp_img_to_fit },_use_alpha_on_imp_imgs={ 0=do_not_use_alp | 1=use_alp_on_imp_imgs },_tile_out_of_boundary_mode={ 0=none | 1=neumann | 2=periodic | 3=continuous },_image_boundary={ 0=neumann | 1=periodic | 2=mirror },_max_alpha_value,_cmyk_mode={ 0=rgb | 1=cmyk }
#@cli : Generate Photographic Mosaic using the indexing approach. This means to generate a picture from multiple set of picture to match target picture(s) using indexed color as reference. The color are chosen by dithering. CMYK is supported by this filter.
#@cli :
#@cli : -----
#@cli : dithering refers to the degree of error diffusion for indexing color
#@cli : _channel_indexing_mode is the mode in which the tiles are assigned into the image
#@cli : _alpha_threshold determines how dithering within alpha, and all tiles are set to the maximum alpha. Only active if and only if variable is set.
#@cli : _crop_alpha_mode determines how input images are to be cropped.
#@cli : _use_alpha_on_imp_imgs is used to determine whether alpha is to be used in the final image.
#@cli : _tile_out_of_boundary_mode is used to determine how out-of-boundary pixels are treated per tiles.
#@cli : _image_boundary refers to how images pixels are treated out-of-boundary in case of tiles not fitting exactly onto the image.
#@cli : _max_alpha_value refers to the maximum alpha value.
#@cli : _cmyk_mode is used to tell the filter to assume images are CMYK in case of 4 channels. 5 Channels is already treated as CMYKA regardless of this setting. 
#@cli : -----
#@cli :
#@cli : Author: Reptorian.
#@cli : Default values: 'dithering=100%','_channel_indexing_mode=2','_alpha_threshold=n/a','_crop_alpha_mode=2','_use_alpha_on_imp_imgs=1','_tile_out_of_boundary_mode=0','image_boundary=2','_max_alpha_value=255','_cmyk_mode=0'
rep_photomosaic_index:
skip ${2=},${3=},${4=100%},${5=1},${6=},${7=2},${8=1},${9=0},${10=2},${11=255},${12=0}

# Preliminary variables for use for later. Preliminary condition to make things easier.

remove_duplicates

is_img_arg=${is_image_arg\ $1}

t_w_arg,t_h_arg,dithering,channel_indexing_mode,use_thresholded_alpha_on_target_images,crop_alpha_mode,use_alpha_on_imp_imgs,tile_out_of_boundary_mode,image_boundary,max_alpha_value,use_cmyk_mode={[narg($2)?round(abs($2)),narg($3)?round(abs($3)),cut($4,0,1),int($5)%3,narg($6),int($7)%3,$8&1,int($9)%4,($10%3)+1,$11?abs($11):255,$12&1]}

multi_channel_alpha_maximum_indexing={$channel_indexing_mode==2}

generate_extra_image={$use_alpha_on_imp_imgs?($multi_channel_alpha_maximum_indexing&&!$use_thresholded_alpha_on_target_images)}

imp_imgs_contain_five,number_of_color_channels={[0,3+($use_cmyk_mode?1)]}

if $use_thresholded_alpha_on_target_images
 alpha_threshold_dither={cut(abs($7),0,1)}
fi

foreach {
 variance=0

 image_contain_alpha:=s==2||s>3

 repeat s {
  shared $<

  if iv
   rm.
   variance=1 break
  else
   if !$>&&$image_contain_alpha
    if !im rm. break fi
    if im<0 error alpha_>=0==F fi
   fi
  fi

  rm.
 }

 if !$variance rm fi # 2a. Remove image if the image contains a single color or if image contains alpha of value zero
}

init_imgs,d_imgs={$!-[0,1]}
names_of_images=${lof\ n}

# 1. Make a list of all images with depth of 1 and make a list of color mode arrays, and check if any images contain alpha.

if $init_imgs>1
 1 => target_images_list
 1 => spectrum_count_list

 eval "
  const use_cmyk_mode=$use_cmyk_mode;

  const target_images_list=$target_images_list;
  const spectrum_count_list=$spectrum_count_list;

  const max_col_ind=$number_of_color_channels;
  const dec_col_ind=max_col_ind-1;
  const n_imgs=$init_imgs;
  test_full_arr=n_imgs>4;

  num_of_chan_arr=vector(#5,0);
  v=is_valid=contain_alpha=has_been_pushed=0;

  repeat(n_imgs,p,
   d#p==1?(

    is_valid=1;
    da_push(#target_images_list,p);
    chan_count=s#p;

    if(!contain_alpha,
     if(chan_count==2||chan_count>max_col_ind,contain_alpha=1;);
    );

    test_full_arr?(
     pos=chan_count-1;
     test_index=num_of_chan_arr[pos];

     if(!test_index,
      num_of_chan_arr[pos]=1;
      ++v;
     );

     if(v==5,test_full_arr=0;);
    ):(
      num_of_chan_arr[chan_count-1]=1;
    );

   );
  );

  if(!is_valid,run('error d([0])==1=F'););

  contain_alpha?(
   use_cmyk_mode?(
    contain_five=0;
    repeat(2,p,
     pos=p*4;
     inc_pos=pos+1;
     if(num_of_chan_arr[pos]||num_of_chan_arr[inc_pos],
      contain_five=1;
      da_push(#spectrum_count_list,2+3*p);
     );
    );
    if(num_of_chan_arr[2],
     if(contain_five
     ,da_insert(#spectrum_count_list,da_size(#spectrum_count_list)-1,4);
     ,da_push(#spectrum_count_list,4)
     );
    );
   ):(
    repeat(2,p,
     pos=p*2;
     inc_pos=pos+1;
     if(num_of_chan_arr[pos]||num_of_chan_arr[inc_pos],da_push(#spectrum_count_list,2<<p););
    );
    if(num_of_chan_arr[4],da_push(#spectrum_count_list,5));
   );
  ):(
   use_cmyk_mode?(
    if(num_of_chan_arr[0],da_push(#spectrum_count_list,1));
    if(num_of_chan_arr[2],da_push(#spectrum_count_list,3));
    if(num_of_chan_arr[3],da_push(#spectrum_count_list,4));
   ):(
    repeat(2,p,
     pos=p*2;
     if(num_of_chan_arr[pos],da_push(#spectrum_count_list,pos+1););
    );
   );
  );

  da_freeze(#target_images_list);
  da_freeze(#spectrum_count_list);

  contain_alpha;
  "

 target_images={crop(#-2)}
 list_of_available_specs={crop(#-1)}
 num_of_available_specs={narg($list_of_available_specs)}
 size_of_target_images_list={narg($target_images)}

 target_images_contain_alpha=${}

 rm[-2,-1]
elif $init_imgs==1
 if ${-max_d}>1 error d([0])==1=F fi
 target_images,target_images_contain_alpha,list_of_available_specs,num_of_available_specs,size_of_target_images_list=0,{s==2||s>$number_of_color_channels},{s},1,1
else
 error needs_images
fi

# 2. Define tile width and images

if $t_w_arg&&$t_h_arg
 tile_width,tile_height=$t_w_arg,$t_h_arg
elif $t_w_arg||$t_h_arg
 tile_width,tile_height:=round($t_w_arg?vector(#2,$t_w_arg):vector(#2,$t_h_arg))
else
 error tile_no_def_dim
fi

# 3. Convert Color Space of Images if applicable

# 4. Resize images

if $use_thresholded_alpha_on_target_images
 if $alpha_threshold_dither
  (0,$max_alpha_value) store. thresholded_values
 fi
fi

foreach[$target_images] {

 mini_w,mini_h,p_w,p_h={tile_v=[$tile_width,$tile_height];mini_wh=ceil([w,h]/tile_v);[mini_wh,mini_wh*tile_v]}
 contain_alpha=0

 if s==2||s>$number_of_color_channels
  +channels {s-1}
  if iv
   gt. 0
   shared.. 0,{s#-2-2}
   *. ..
   remove.
   append[-2,-1] c
   contain_alpha=1
  else rm. fi
 fi

 resize $p_w,$p_h,100%,100%,0,$image_boundary,.5,.5
 resize $mini_w,$mini_h,100%,100%,2

 if $contain_alpha
  shared 0,{s-3}
  shared.. {s+1}
  /.. .
  replace_nan.. 0
  remove[-2,-1]
  channels 0,{s-2}

  if $use_thresholded_alpha_on_target_images
   shared {s-1}

   if $alpha_threshold_dither
    contain_variable_alpha=0
    $thresholded_values
    index.. .,$alpha_threshold_dither,$channel_indexing_mode
    if iv#-2 contain_variable_alpha=1 fi
    rm[-2,-1]
    if !$contain_variable_alpha channels 0,{s-2} fi
   else
    gt. 0
    if $channel_indexing_mode *. $max_alpha_value fi
    rm.
   fi

  fi

 fi
}

# 5. Import Images

if $is_img_arg
 pass$1 0
else
 input_glob $1
fi

if $generate_extra_image $tile_width,$tile_height,1,2 fi

# 6. Check if there is more than 1 imported image, and if any images contains more than 5 channels

imp_imgs={$!-$init_imgs-$generate_extra_image}
max_imp_spec,max_imp_spec_5=${-max_s[$init_imgs--{1+$generate_extra_image}]},0

if $max_imp_spec==5 max_imp_spec_5=1
elif $max_imp_spec_5>5 error exc_chan
fi

if ($!-$init_imgs<2) error "_"#"imps>=2==F" fi
if ${-max_d[$init_imgs--1]}>1 error inv_dep_imp_imgs fi

# 7. Check which imported image contains alpha

# 7a-note. Temporary Image to insert dynamic pushing to find the list of images with alpha with variance, and remove unused alpha.

imp_imgs_contain_alp=0

1,$imp_imgs,1,1,"begin(
  const n_imgs=$init_imgs;
  const max_col_ind=$number_of_color_channels;
 );
 p=y+n_imgs;
 s_size=s#p;
 s_size==2||s_size>max_col_ind?p;"

if iM#-1

 discard. 0

 repeat h#-1-$generate_extra_image {

  pos={i(#-1,0,$<)}
  sh[$pos] 100%

  if !iv&&!im
   rm[-1,$pos]
   imp_imgs-=1
  else
   rm.
  fi

 }

 rm.

 if ($!-$init_imgs<2) error "_"#"imps_after_alp_test>=2==F" fi

 1,$imp_imgs,1,1,"begin(
   const n_imgs=$init_imgs;
   const max_col_ind=$number_of_color_channels;
  );
  p=y+n_imgs;
  s_size=s#p;
  s_size==2||s_size>max_col_ind?p;"

 if iM#-1
  discard. 0
  imp_imgs_contain_alp={h}
 else
  rm.
 fi
fi

# 8. When imported images has alpha, crop them accordingly to crop mode if applied.

utilize_crop_alpha={$imp_imgs_contain_alp&&$crop_alpha_mode}

if $imp_imgs_contain_alp
 list_imp_img_contain_alp={crop(#-1)}
else
 if $generate_extra_image
  rm.
 fi
fi rm.

m "rep_photomosaic_fix_color:
 sh 0,{s-3}
 sh.. {s+1}
 /.. .
 replace_nan.. 0
 rm[-2,-1]
 channels 0,{s-2}"

if $utilize_crop_alpha

 if $use_alpha_on_imp_imgs
  if $crop_alpha_mode==2
   m "rep_photomosaic_resize_output:
    resize2din $""1,$""2,2
    rep_photomosaic_fix_color
    resize $""1,$""2,100%,100%,0,$""3,.5,.5"
  else
   m "rep_photomosaic_resize_output:
    rep_aspect_crop_2d $""1,$""2
    resize $""1,$""2,100%,100%,2,$""3
    rep_photomosaic_fix_color"
  fi
 else
  if $crop_alpha_mode==2
   m "rep_photomosaic_resize_output:
    resize2din $""1,$""2,2
    resize $""1,$""2,100%,100%,0,$""3,.5,.5"
  else
   m "rep_photomosaic_resize_output:
    rep_aspect_crop_2d $""1,$""2
    resize $""1,$""2,100%,100%,2,$""3"
  fi
 fi

 foreach[$list_imp_img_contain_alp] {

  sh {s-1}

  if iv#-1
   autocrop_coords ,
   rm.
   crop ${}

   sh {s-1}

   if iv#-1
    rm.
    if $use_alpha_on_imp_imgs +channels {s-1} gt. 0 sh.. 0,{s#-2-2} *. .. rm. a[-2,-1] c fi
    rep_photomosaic_resize_output $tile_width,$tile_height,$tile_out_of_boundary_mode
   else
    imp_imgs_contain_alp-=1
    rm.
    channels 0,{s-2}
    rep_aspect_crop_2d $tile_width,$tile_height
    resize $tile_width,$tile_height,100%,100%,2,$tile_out_of_boundary_mode
   fi

  else
   rm.
   rep_aspect_crop_2d $tile_width,$tile_height
   resize $tile_width,$tile_height,100%,100%,2,$tile_out_of_boundary_mode
  fi

 }

 local[^0-$d_imgs,$list_imp_img_contain_alp] {
  rep_aspect_crop_2d $tile_width,$tile_height
  resize $tile_width,$tile_height,100%,100%,2,$tile_out_of_boundary_mode
 }

 imp_imgs_contain_five:=${-max_s[$init_imgs--1]}==5

 um rep_photomosaic_resize_output

else
 if $imp_imgs_contain_alp&&$use_alpha_on_imp_imgs
  foreach[$list_imp_img_contain_alp] {
   +channels {s-1} gt. 0 sh.. 0,{s#-2-2} *. .. rm. a[-2,-1] c
  }
  rep_aspect_crop_2d[$init_imgs--1] $tile_width,$tile_height
  resize[$init_imgs--1] $tile_width,$tile_height,100%,100%,2
  foreach[$list_imp_img_contain_alp] { rep_photomosaic_fix_color }
 else
  rep_aspect_crop_2d[$init_imgs--1] $tile_width,$tile_height
  resize[$init_imgs--1] $tile_width,$tile_height,100%,100%,2
 fi
fi

um rep_photomosaic_fix_color
remove_duplicates[$init_imgs--1]
imp_imgs={$!-$init_imgs}

end_imp_ind={$init_imgs+$imp_imgs}

# 9. Tree Path for each condition

if $imp_imgs_contain_alp&&$use_alpha_on_imp_imgs

 {$!-$init_imgs-$generate_extra_image},1,1,1,"begin(
   const init_imgs=$init_imgs;
   const number_of_color_channels=$number_of_color_channels;
  );
  pos=x+init_imgs;
  spec_size=s#pos;
  spec_size==2||spec_size>number_of_color_channels;"

 store. image_bool_has_alpha

 if !$target_images_contain_alpha list_of_available_specs:=[$list_of_available_specs]+1 fi

 if $imp_imgs_contain_five _rep_photomosaic_create_tiles_contain_five[$init_imgs--1] $list_of_available_specs
 else _rep_photomosaic_create_strip_for_less_than_five[$init_imgs--1] $list_of_available_specs
 fi

 repeat narg($list_of_available_specs) {

  image_target=-$num_of_available_specs

  $imp_imgs,1,1,{s#$image_target},:"begin(
    const img_target=$image_target;
    const color_chan_count=s>=3?max(3,s-1):1;
    const contain_alpha=s==2||s>color_chan_count;
    const tw=$tile_width;
    const th=$tile_height;
    const tile_dimensions=wh#img_target;
    const whc=tw*th*color_chan_count;
    contain_alpha?(
     output()=(
      color_data=crop(#img_target,0,0,x,0,tw,th,1,color_chan_count);
      alpha_data=crop(#img_target,0,0,x,color_chan_count,tw,th,1,1);
      var(alpha_data)?(
       sum_alpha=sum(alpha_data);
       alpha_value=avg(alpha_data);
       color_vector_output=vector(#color_chan_count,0);
       alpha_data/=sum_alpha;
       repeat(color_chan_count,p,
        color_vector_output[p]=sum((color_data)[p*tile_dimensions,tile_dimensions]*alpha_data);
       );
       [color_vector_output,alpha_value];
      ):(
       [resize(color_data,tw,th,1,color_chan_count,1,1,1,color_chan_count,2,0,0,0,0,0),alpha_data[0]];
      );
     );
    ):(
     output()=(
      color_data=crop(#img_target,0,0,x,0,tw,th,1,color_chan_count);
      resize(color_data,tw,th,1,color_chan_count,1,1,1,color_chan_count,2,0,0,0,0,0);
     );
    );
   );
   output();
   "

 }

 2,1,1,$size_of_target_images_list,"
  target_image_spec_list=["$target_images"];
  if(x,
   tile_spec_list=["$list_of_available_specs"];
   const size_tile_spec_list=size(tile_spec_list);
   const use_cmyk_mode=$use_cmyk_mode;
   output=vector(#s,0);
   use_cmyk_mode?(
    const last_tile_pos=size_tile_spec_list-1;
    repeat(s,a,
     tv=target_image_spec_list[a];
     num_of_chan=s#tv;
     if(num_of_chan>=4
     ,output[a]=last_tile_pos;
     ,
      repeat(size_tile_spec_list,b,
       if(num_of_chan<=tile_spec_list[b],break(););
      );
      output[a]=b;
     );
    );
   ):(
    repeat(s,a,
     tv=target_image_spec_list[a];
     num_of_chan=s#tv;
     repeat(size_tile_spec_list,b,
      if(num_of_chan<=tile_spec_list[b],break(););
     );
     output[a]=b;
    );
   );
   output;
  ,
   target_image_spec_list;
  );
  "

 repeat $size_of_target_images_list {

  contain_alpha,pos_target_image=0,{i(#-1,0,0,0,$>)}
  s1,pos_reference_tile={[s#$pos_target_image,$init_imgs+i(#-1,1,0,0,$>)]}
  s2,palette_position={[s#$pos_reference_tile,$pos_reference_tile+$num_of_available_specs]}

  simplify_final_spectrum=0

  if $s1==2||$s1>$number_of_color_channels contain_alpha=1 fi

  if $generate_extra_image
   shared[$palette_position] 0,{$s2-2}

   if $contain_alpha
    eval "
     const spectrum_size=s#-1;
     const last_index=w-1;
     const image_reference=$pos_target_image;
     const width=w#image_reference;
     const height=h#image_reference;
     I[#-1,last_index]=resize(crop(#image_reference,0,0,0,0,width,height,1,spectrum_size,0),spectrum_size,2,0);
     "
   else
    eval "
     const spectrum_size=s#-1;
     const last_index=w-1;
     I[#-1,last_index]=vector(#spectrum_size,0);
     "
   fi

   rm.
  fi

  if $contain_alpha&&!$channel_indexing_mode?$s1==$s2
   split[$pos_target_image] c,-{$s1-1}
   shared[{$palette_position+1}] 0,{$s2-2}
   index[$pos_target_image] [-1],$dithering,0
   remove[-1]

   +colormap[$pos_target_image] 0,0,0
   $image_bool_has_alpha
   map[-2] [-1]
   if !iM#-2 simplify_final_spectrum=1 fi

   remove[-2,-1]

   append[$pos_target_image,{$pos_target_image+1}] c
  elif $s1==$s2
   if $contain_alpha&&$channel_indexing_mode
    +channels[$pos_target_image] {$s1-1}
    index[$pos_target_image] [$palette_position],$dithering,0

    if $multi_channel_indexing
     +colormap[$pos_target_image] 0,0,0
     $image_bool_has_alpha
     map[-2] [-1]
     if !iM#-2 simplify_final_spectrum=1 fi
     rm[-2,-1]
    fi

    append[$pos_target_image,-1] c
   else
    index[$pos_target_image] [$palette_position],$dithering,0

    if !$contain_alpha?1:$multi_channel_indexing
     +colormap[$pos_target_image] 0,0,0
     $image_bool_has_alpha
     map[-2] [-1]
     if !iM#-2 simplify_final_spectrum=1 fi
     remove[-2,-1]
    fi

   fi

  else
   if $channel_indexing_mode
    {target_image=$pos_target_image;[w#target_image,h#target_image]},1,1,$max_alpha_value
    append[$pos_target_image,-1] c

    if $generate_extra_image
     +crop[$palette_position] 0,{w#$palette_position-2}
     index[$pos_target_image] [-1],$dithering,0
     remove[-1]
    else
     index[$pos_target_image] [$palette_position],$dithering,0
    fi

    if !$contain_alpha?1:$multi_channel_indexing

     +colormap[$pos_target_image] 0,0,0
     $image_bool_has_alpha
     map[-2] [-1]
     if !iM#-2 simplify_final_spectrum=1 fi

     remove[-2,-1]
    fi
   else
    shared[$palette_position] 0,{$s2-2}
    index[$pos_target_image] [-1],$dithering,0

    if !$contain_alpha

     +colormap[$pos_target_image] 0,0,0
     $image_bool_has_alpha
     map[-2] [-1]
     if !iM#-2 simplify_final_spectrum=1 fi
     remove[-2,-1]

    fi

    remove[-1]
   fi
  fi

  final_spectrum={s#$pos_reference_tile}

  if !$contain_alpha?(final_spectrum=$final_spectrum;(final_spectrum==2||final_spectrum>$number_of_color_channels)&&$simplify_final_spectrum)
   final_spectrum-=1
  fi

  {prt=$pos_target_image;d_wh=[$tile_width,$tile_height]*[w#prt,h#prt];[d_wh,1,$final_spectrum];}

  eval[$pos_target_image] :"begin(
    const multi_channel_alpha_maximum_indexing=$multi_channel_alpha_maximum_indexing;
    const max_alpha_value=$max_alpha_value;
    const use_thresholded_alpha_on_target_images=$use_thresholded_alpha_on_target_images;
    const tile_pos=$pos_reference_tile;
    const tw=$tile_width;
    const th=$tile_height;
    const ss=$final_spectrum;
    const d_ss=ss-1;
    const use_multi_mode=s>1;
    use_multi_mode?(
     use_thresholded_alpha_on_target_images||multi_channel_alpha_maximum_indexing?(
      action()=(
       if(img_val[1],
        color_tile=crop(#tile_pos,0,0,img_val[0],0,tw,th,1,d_ss);
        alpha_tile=crop(#tile_pos,0,0,img_val[0],d_ss,tw,th,1,1);
        draw(#-1,color_tile,x*tw,y*th,0,0,tw,th,1,d_ss);
        draw(#-1,alpha_tile,x*tw,y*th,0,d_ss,tw,th,1,1);
       );
      );
     ):(
      const palette_position=$palette_position;
      action()=(
       mini_tile_alpha=img_val[1];
       palette_alpha=i(#palette_position,img_val[0],0,0,d_ss);
       if(mini_tile_alpha,
        color_tile=crop(#tile_pos,0,0,img_val[0],0,tw,th,1,d_ss);
        alpha_tile=crop(#tile_pos,0,0,img_val[0],d_ss,tw,th,1,1);
        if(mini_tile_alpha<palette_alpha,
         alpha_tile*=mini_tile_alpha/palette_alpha;
        );
        draw(#-1,color_tile,x*tw,y*th,0,0,tw,th,1,d_ss);
        draw(#-1,alpha_tile,x*tw,y*th,0,d_ss,tw,th,1,1);
       );
      );
     );
    ):(
     action()=(
      tile=crop(#tile_pos,0,0,img_val[0],0,tw,th,1,ss);
      draw(#-1,tile,x*tw,y*th,0,0,tw,th,1,ss);
     );
    );
   );
   img_val=I;
   action();
   I;
   "

  reverse[$pos_target_image,-1]
  remove[-1]
 }

else

 if $target_images_contain_alpha list_of_available_specs:=[$list_of_available_specs]-1 fi

 if $use_cmyk_mode _rep_photomosaic_create_strip_for_nonalp_tiles_on_cmyk_mode[$init_imgs--1] $list_of_available_specs
 else _rep_photomosaic_create_strip_for_less_than_five[$init_imgs--1] $list_of_available_specs
 fi

 if $target_images_contain_alpha&&(!$use_alpha_on_imp_imgs||!$imp_imgs_contain_alp) list_of_available_specs:=[$list_of_available_specs]+1 fi

 repeat narg($list_of_available_specs) {
  image_target=-$num_of_available_specs

  $imp_imgs,1,1,{s#$image_target},:"begin(
    const img_target=$image_target;
    const tw=$tile_width;
    const th=$tile_height;
   );
   color_data=crop(#img_target,0,0,x,0,tw,th,1,s);
   resize(color_data,tw,th,1,s,1,1,1,s,2,0,0,0,0,0);
   "
 }

 2,1,1,$size_of_target_images_list,"
  target_image_spec_list=["$target_images"];
  output=vector(#s,0);
  if(x,
   tile_spec_list=["$list_of_available_specs"];
   const size_tile_spec_list=size(tile_spec_list);
   const last_tile_pos=size_tile_spec_list-1;
   repeat(s,a,
    tv=target_image_spec_list[a];
    num_of_chan=s#tv;
    if(num_of_chan==4||num_of_chan==5
    ,output[a]=last_tile_pos;
    ,
     repeat(size_tile_spec_list,b,
      if(num_of_chan<=tile_spec_list[b],break(););
     );
     output[a]=b;
    );
   );
   output;
  ,
   target_image_spec_list;
  );
  "

 repeat $size_of_target_images_list {
  pos_target_image={i(#-1,0,0,0,$>)}
  s1,pos_reference_tile={[s#$pos_target_image,$init_imgs+i(#-1,1,0,0,$>)]}
  s2={s#$pos_reference_tile}

  if $s1>$s2
   split[$pos_target_image] c,-{$s1-1}
   index[$pos_target_image] [{$pos_reference_tile+$num_of_available_specs+1}],$dithering,0
   a[$pos_target_image,{$pos_target_image+1}] c
  elif $s1<$s2
   split[$pos_target_image] c,{$s1-2}
   index[$pos_target_image] [{$pos_reference_tile+$num_of_available_specs}],$dithering,0
   a[$pos_target_image,{$pos_target_image+1}] c
  else
   index[$pos_target_image] [{$pos_reference_tile+$num_of_available_specs}],$dithering,0
  fi

  {prt=$pos_target_image;d_wh=[$tile_width,$tile_height]*[w#prt,h#prt];[d_wh,1,$s1];}

  eval[$pos_target_image] :"begin(
    const max_alpha_value=$max_alpha_value;
    const tile_pos=$pos_reference_tile;
    const tw=$tile_width;
    const th=$tile_height;
    const t_wh=tw*th;
    const ss=$s1;
    const d_ss=ss-1;
    const use_multi_mode=s>1;
    const use_thresholded_alpha_on_target_images=$use_thresholded_alpha_on_target_images;
    use_multi_mode?(
     use_thresholded_alpha_on_target_images?(
      action()=(
       alpha=img_val[1];
       alpha?(
        tile=[crop(#tile_pos,0,0,img_val[0],0,tw,th,1,d_ss),vector(#t_wh,max_alpha_value)];
        draw(#-1,tile,x*tw,y*th,0,0,tw,th,1,ss);
       );
      );
     ):(
      action()=(
       alpha=img_val[1];
       alpha?(
        tile=[crop(#tile_pos,0,0,img_val[0],0,tw,th,1,d_ss),vector(#t_wh,alpha)];
        draw(#-1,tile,x*tw,y*th,0,0,tw,th,1,ss);
       );
      );
     );
    ):(
     action()=(
      tile=crop(#tile_pos,0,0,img_val[0],0,tw,th,1,ss);
      draw(#-1,tile,x*tw,y*th,0,0,tw,th,1,ss);
     );
    );
   );
   img_val=I;
   action();
   I;
   "

  reverse[$pos_target_image,-1] rm.

 }


fi

keep[0-{$init_imgs-1}]
name[0--1] $names_of_images
_rep_photomosaic_create_strip_for_nonalp_tiles_on_cmyk_mode:
images={$!}
fin_img_ind={$!-1}
$=p

repeat $# {
 spec_size=${p{$>+1}}

 if $spec_size==4
  +foreach[0-$fin_img_ind] {
   if s==1 to_rgb fi
   rgb2cmyk
  }
 elif $spec_size==3
  +to_rgb[0-%fin_img_ind]
 else
  to_gray[0-%fin_img_ind]
 fi

 append[-$images--1] z
}

remove[0-$fin_img_ind]
_rep_photomosaic_create_strip_for_less_than_five:
images={$!}
fin_img_ind={$!-1}
$=p

m "loc_tmp_cmd_rgb2cmyka:
 foreach {
   if s==1 to_rgb rgb2cmyk 100%,100%,100%,1,255 a c
   elif s==2 to_rgba s. c,-3 rgb2cmyk.. a c
   elif s==3 rgb2cmyk 100%,100%,100%,1,255 a c
   elif s==4 s. c,-3 rgb2cmyk.. a c
   fi
 }"

repeat $# {
 spec_size=${p{$>+1}}

 cs_convert=${arg\ $spec_size,to_gray,to_graya,to_rgb,to_rgba,loc_tmp_cmd_rgb2cmyka}

 +$cs_convert[0-$fin_img_ind]

 append[-$images--1] z
}

remove[0-$fin_img_ind]

uncommand loc_tmp_cmd_rgb2cmyka
_rep_photomosaic_create_tiles_contain_five:

images={$!}
fin_img_ind={$!-1}
$=p

repeat $# {
 spec_size=${p{$>+1}}

 if $spec_size==5
  +foreach[0-$fin_img_ind] {
   if s==1 to_rgb rgb2cmyk 100%,100%,100%,1,255 a c
   elif s==2 to_rgba s. c,-3 rgb2cmyk.. a c
   elif s==3 rgb2cmyk 100%,100%,100%,1,255 a c
   elif s==4 s. c,-3 rgb2cmyk.. a c
   fi
  }
 elif $spec_size==4
  +foreach[0-$fin_img_ind] {
   if s<3 to_rgba
   elif s==5 s c,-4 cmyk2rgb.. a c
   fi
  }
 elif $spec_size==3
  +foreach[0-$fin_img_ind] {
   if s==5 channels 0,3 cmyk2rgb
   elif s!=3 to_rgb
   fi
  }
 elif $spec_size==2
  +foreach[0-$fin_img_ind] {
   if s==5 s c,-4 cmyk2rgb.. a c to_graya
   elif s!=2 to_graya
   fi
  }
 elif $spec_size==1
  +foreach[0-$fin_img_ind] {
   if s!=1&&s<5 to_gray
   elif s==5 s. c,-4 cmyk2rgb..
  }
 else error inv_spec_size_found
 fi

 a[-$images--1] z

}

remove[0-$fin_img_ind]

And yes, reading the code is a doozy.

You can do something like:

$ ig photomosaic +rep_photomosaic_index alpha_images,15,8,,1

ig imports images from a folder. alpha_images is the name of the folder you’re going to be using as image input.

I made my own expression generator:

rep_generate_expression:
v1,v2,started=0

if u<.5 status a mode=0
else    status b mode=1
fi

do
 v2+=1

 if u<$v1||$v2>6
  if !$started
   r:=u
   if $r<.75
    if u<.5 status a
    else status b
    fi
   fi
  else break
  fi
 else
  v1,v2:=vector(#2,$v1)+u(vector(#2,.2))
  r:=u(15)  
  
  if $mode symbol=b mode=0
  else symbol=a mode=1
  fi
  
  r_choice:=int(u(0,4,1,0))
  
  if $r<1     status cos(${})
  elif $r<2   status sin(${})
  elif $r<3   status tan(${})
  elif $r<4   status logabs(${})
  elif $r<5   status norm(${},a)
  elif $r<6   status norm(${},b)
  elif $r<7   status (${})
  elif $r<8   status ${}/${arg\ 1+$r_choice,cos($symbol),sin($symbol),tan($symbol),logabs($symbol)}
  elif $r<9   status ${}*${arg\ 1+$r_choice,cos($symbol),sin($symbol),tan($symbol),logabs($symbol)} 
  elif $r<10  status ${}+${arg\ 1+$r_choice,cos($symbol),sin($symbol),tan($symbol),logabs($symbol)}
  elif $r<11  status ${}-${arg\ 1+$r_choice,cos($symbol),sin($symbol),tan($symbol),logabs($symbol)}
  elif $r<12  status ${arg\ 1+$r_choice,cos($symbol),sin($symbol),tan($symbol),logabs($symbol)}/${}
  elif $r<13  status abs(${})
  elif $r<14  status avg($symbol,${})
  else        status sqr(${})
  fi
  
  started=1
 fi

while !((u<$v1&&$v2>1)||$v2>6)

('${}')

replace_str. "a,a","a,b"
replace_str. "b,b","a,b"

out={t}

remove[-1] 
status $out

Could use some improvement though.

Discovered this with the above code:

image

I’ll just keep the code here for use for later.

4 Likes

Can some one test this code?

$ rep_random_stripe 51,50
rep_random_stripe:
 {[$1,$2]},1,1,"
  !(x&1)?x>>1:-10;
  "
  
 end_row_is_at_end_ind={xM==w-1}
 
 1,{iM+1},1,1,y 1
 
 eval "
  repeat(int(u(2,h#-2,1,0)),
   if(da_size(#-2)==1,break(););
   index=int(u(0,da_size(#-2),1,1));
   value=i[#-2,index];
   da_remove(#-2,index);
   da_push(#-1,value);
  );
  da_freeze(#-1);
  "

 remove[-2]
 
 1,{h#-2+1},1,1,y 1
 
 eval[-3] >"
  position=int(u(0,da_size(#-2),1,1));
  value=i[#-2,position];
  da_remove(#-2,position);
  da_push(#-1,value);
  end(da_freeze(#-1););
  "
  
 remove[-2]
 append[-2,-1] c
 
 eval[-1] "begin(
   const end_row_is_at_end_ind=$end_row_is_at_end_ind;
   const maximal_column_position=iM#-2;
   end_row_is_at_end_ind?(
    is_middle(column_position)=inrange(column_position,1,maximal_column_position,1,0);
   ):(
    is_middle(column_position)=inrange(column_position,1,maximal_column_position,1,1);
   );
  );
  position=I;
  column=position[0];
  y_point=position[1];
  is_middle(column)?(
   u(0,1,1,1)>.5?(
    new_position=round(u(0,column-1,1,1))<<1;
    new_position+=1;
   ):(
    new_position=round(u(column+1,maximal_column_position,1,1))<<1;
    new_position-=1;    
   );
   polygon(#-2,2,column<<1,y_point,new_position,y_point,1,column);
  );
  "

Run it, see if the line match up with column.

It should look like this if mapped with color:

image

Then, remove the u(0,1,1,1)>.5 portion of the code and keep one way of finding new_position. Run again.

It looks correct now, but only in one direction.

See this result:

image

image

image

Seem to look fine on your end. I fixed the bug by going with a slight different approach.

image

image

image

1 Like

I added a new feature to rep_cubic_map and fixed a bug. See new results here:


The bottom one is the new option.

New CLI commands: rep_partial_permutations , rep_extract_partial_permutation_order, rep_find_partial_permutation_lexicographic_index . These were the missing ones.

$ rep_partial_permutations 8,5 echo {I[#-1,4097]} rm.
=> 4,7,0,6,2

$ echo ${rep_extract_partial_permutation_order\ 8,5,4097}
=> 4,7,0,6,2

$ echo ${rep_find_partial_permutation_lexicographic_index\ 8,4,7,0,6,2}
=> 4097
Additional thing
$ rep_partial_permutations 7,3 repeat whd#-1 { echo {I[#-1,"$>"]} }

0,1,2
0,1,3
0,1,4
0,1,5
0,1,6
0,2,1
0,2,3
0,2,4
0,2,5
0,2,6
0,3,1
0,3,2
0,3,4
0,3,5
0,3,6
0,4,1
0,4,2
0,4,3
0,4,5
0,4,6
0,5,1
0,5,2
0,5,3
0,5,4
0,5,6
0,6,1
0,6,2
0,6,3
0,6,4
0,6,5
1,0,2
1,0,3
1,0,4
1,0,5
1,0,6
1,2,0
1,2,3
1,2,4
1,2,5
1,2,6
1,3,0
1,3,2
1,3,4
1,3,5
1,3,6
1,4,0
1,4,2
1,4,3
1,4,5
1,4,6
1,5,0
1,5,2
1,5,3
1,5,4
1,5,6
1,6,0
1,6,2
1,6,3
1,6,4
1,6,5
2,0,1
2,0,3
2,0,4
2,0,5
2,0,6
2,1,0
2,1,3
2,1,4
2,1,5
2,1,6
2,3,0
2,3,1
2,3,4
2,3,5
2,3,6
2,4,0
2,4,1
2,4,3
2,4,5
2,4,6
2,5,0
2,5,1
2,5,3
2,5,4
2,5,6
2,6,0
2,6,1
2,6,3
2,6,4
2,6,5
3,0,1
3,0,2
3,0,4
3,0,5
3,0,6
3,1,0
3,1,2
3,1,4
3,1,5
3,1,6
3,2,0
3,2,1
3,2,4
3,2,5
3,2,6
3,4,0
3,4,1
3,4,2
3,4,5
3,4,6
3,5,0
3,5,1
3,5,2
3,5,4
3,5,6
3,6,0
3,6,1
3,6,2
3,6,4
3,6,5
4,0,1
4,0,2
4,0,3
4,0,5
4,0,6
4,1,0
4,1,2
4,1,3
4,1,5
4,1,6
4,2,0
4,2,1
4,2,3
4,2,5
4,2,6
4,3,0
4,3,1
4,3,2
4,3,5
4,3,6
4,5,0
4,5,1
4,5,2
4,5,3
4,5,6
4,6,0
4,6,1
4,6,2
4,6,3
4,6,5
5,0,1
5,0,2
5,0,3
5,0,4
5,0,6
5,1,0
5,1,2
5,1,3
5,1,4
5,1,6
5,2,0
5,2,1
5,2,3
5,2,4
5,2,6
5,3,0
5,3,1
5,3,2
5,3,4
5,3,6
5,4,0
5,4,1
5,4,2
5,4,3
5,4,6
5,6,0
5,6,1
5,6,2
5,6,3
5,6,4
6,0,1
6,0,2
6,0,3
6,0,4
6,0,5
6,1,0
6,1,2
6,1,3
6,1,4
6,1,5
6,2,0
6,2,1
6,2,3
6,2,4
6,2,5
6,3,0
6,3,1
6,3,2
6,3,4
6,3,5
6,4,0
6,4,1
6,4,2
6,4,3
6,4,5
6,5,0
6,5,1
6,5,2
6,5,3
6,5,4

Who needs Python itertools? I sure don’t.

EDIT: Distinct permutation/combinations haven’t been done. Though available algorithm appears to be O(N*N!) from what I saw.

EDIT as of 5/15: I have made my combinatorics easier to use. I will have to merge some combinations command into a cartesian_product command.

1 Like

Now onto the first test to see if it feels more intuitive.

@afre Can you confirm if this feels more intuitive than utilizing the various rep_combination commands in gmic-community which creates a image?

New command
$ rep_cartesian_product 5,4,3
$ rep_cartesian_product 5,4
$ rep_cartesian_product -5,4
$ rep_cartesian_product 5

#@cli rep_cartesian_product: number_of_items_a,number_of_items_b,...,_axis : -number_of_items,occurance_per_items,_axis  : number_of_items,_axis 
#@cli : Generates image representing cartesian product. How it is generated depends on the parameters assigned.
#@cli :
#@cli : ---------------------
#@cli :
#@cli : If you have 3 or more numbers, then the generated image will represent the cartersian product of a*b... In other words, the image can be represented as a list of every integer coordinates that fits within a n-dimension box.
#@cli :
#@cli : If you have exactly 2 numbers, then how the image is generated depends on whether the first number is positive number. If the first number is a positive number, then the generated image can be represented as a list of every integer coordinates that fits within a rectangle in which a rectangle is a integer. Otherwise, the generated image can be seen as representation of possibilities of number_of_items and their occurance.
#@cli :
#@cli : If you have only 1 number, then the generated image can be represented as a list of every integer coordinates that fits with n-dimension box in which all the dimensions are the same and they are integer and is equal to the input integer.
#@cli :
#@cli : ---------------------
#@cli : Default values: '_axis=x'
+rep_cartesian_product:
skip ${2=x}

if isnum($-1)&&isint($-1)
 axis,contain_xyz=0
elif inrange(_'$-1',_'x',_'z',1,1)
 axis,contain_xyz={_'$-1'-_'x'},1
else 
 error invalid_char
fi

variables_to_check={$#-$contain_xyz}
number_list={abs(([$*])[0,$variables_to_check])}
num_of_variables_is_int={v=[$number_list];sum(isint(v))}
if $num_of_variables_is_int!=$variables_to_check invalid_var_inp fi

mode={n=$num_of_variables_is_int;n>2?0:n==2?!($1>0):2}

num_of_combinations,s_size={$mode==2?[$1^$1,$1]:$mode==1?([$2^abs($1),abs($1)]):(v=[$number_list];[prod(v),$num_of_variables_is_int])}

if $num_of_combinations>3125 p_mode=:
else p_mode=>
fi

dims={v=vector(#3,1);v[$axis]=$num_of_combinations;v;}

$dims,$s_size,$p_mode"
 begin(
  const axis=$axis;
  const mode=$mode;
  const d_s=s-1;
  div_list=vector(#s,0);
  mode==2?(
   items_list=vector(#s,s);
  ):
  mode==1?(
   items_list=vector(#s,abs($2));
  ):(
   items_list=["$number_list"];
  );
  div_list[0]=tv=1;
  repeat(d_s,p,
   div_list[p+1]=tv*=items_list[d_s-p];
  );
  div_list=reverse(div_list);
  axis==2?(index()=z;):
  axis==1?(index()=y;):
           index()=x;
 );
 int(index()/div_list)%items_list;
 "

I shall take a look if I have time this weekend. Probably have to download the latest GIMP/GMIC.

Speaking of combinatories, I made rep_ncr_combinations faster:

C:\Windows\System32>gmic nk=27,10 tic new_rep_ncr_combinations $nk toc tic rep_ncr_combinations $nk toc -
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Set local variable 'nk=27,10'.
[gmic]-0./ Initialize timer.
[gmic]-1./ Elapsed time: 0.247 s.
[gmic]-1./ Initialize timer.
[gmic]-2./ Elapsed time: 1.144 s.
[gmic]-2./ Subtract images [0,1].
[gmic]-1./ Display image [0] = '[: begin_t( const n_items=$n_items; co(...) started?( comb[max_index]==max_value'.
[0] = '[: begin_t( const n_items=$n_it(...),0); ); started?( comb[max_index]==ma:
  size = (8436285,1,1,10) [321 Mio of float32].
  data = (0,0,0,0,0,0,0,0,0,0,0,0,(...),0,0,0,0,0,0,0,0,0,0,0,0).
  min = 0, max = 0, mean = 0, std = 0, coords_min = (0,0,0,0), coords_max = (0,0,0,0).
[gmic]-1./ End G'MIC interpreter.

D:\Programs\G'MIC\gmic-community>gmic nk=27,11 tic new_rep_ncr_combinations $nk toc tic rep_ncr_combinations $nk toc
[gmic]-0./ Start G'MIC interpreter.
[gmic]-0./ Set local variable 'nk=27,11'.
[gmic]-0./ Initialize timer.
[gmic]-1./ Elapsed time: 0.402 s.
[gmic]-1./ Initialize timer.
[gmic]-2./ Elapsed time: 1.859 s.
[gmic]-2./ Display images [0,1] = '[: begin_t( const n_items=$n_items; co(...) started?( comb[max_index]==max_value'.
[0] = '[: begin_t( const n_items=$n_it(...),0); ); started?( comb[max_index]==ma:
  size = (13037895,1,1,11) [547 Mio of float32].
  data = (0,0,0,0,0,0,0,0,0,0,0,0,(...),25,26,26,26,26,26,26,26,26,26,26,26).
  min = 0, max = 26, mean = 13, std = 7.78888, coords_min = (0,0,0,0), coords_max = (16,0,0,10).
[1] = '[: begin( const n_items=$n_item(...)hile(K<=index, ++V; R=permut(choices-:
  size = (13037895,1,1,11) [547 Mio of float32].
  data = (0,0,0,0,0,0,0,0,0,0,0,0,(...),25,26,26,26,26,26,26,26,26,26,26,26).
  min = 0, max = 26, mean = 13, std = 7.78888, coords_min = (0,0,0,0), coords_max = (16,0,0,10).
[gmic]-2./ End G'MIC interpreter.

The zeros you see is because of difference. 0 confirms they output the same.

Number of combinations - 8,436,285

New one - 0.247 s
Old one -1.144 s

Seem like it is 4.6x faster.

EDIT: Trimmed off .01 s by using copy(); Now it is 5x faster.

EDIT: Optimized rep_permutations

Made it nearly 4x faster with the by combining old algorithm I used for finding next permutations , and of course finding permutation at index with my own original code.

Also, the one to find next permutations at geeksforgeeks was slower than the one at techiedelight.

I don’t think that’s needed. I found that one can split a option from rep_cartesian_product into rep_repeat_permutations. The cartesian_product one will allow users to use a number of number_of_items, and it’ll generate the cartesian product from it. The repeat_permutation thing is actually the same as cartesian_product, but with a different approach and is more limited in scope while more flexible in what it’s limited for.

That being said, I will say to the viewer of this thread that the only thing I might be missing at the end is on repeat_combination. Here’s the problem, there is no way to get index from it, and there is no way to generate combination from index. The generation of repeat_combination is no problem. I know a solution exist, but I haven’t found that yet. Maybe after I do it, and generate repeat_combination as a image, I may find a solution. If not, it’ll remain missing like ncr_combinations which I only found the solution due to a single webpage that wasn’t there before for a long time in google results.

I just replaced the old rep_combinations tools with more intuitive set of tools such as rep_cartesian_product series and rep_r_permutations series of CLI commands.

Final thing to do is to solve the thorny problem of finding the rank of combinations with repetition allowed and to extract combinations from the rank of combinations with repetition allowed. I cannot find a single google result that points to that.

Based on Python itertools results and converting into G’MIC image, I can see it might be related to the ncr_combination_index2list and rep_ncr_combination_list2index. How, I don’t know.

Combinations with repetition allowed:

rep_ncr_combinations (repetition is not allowed of course):

EDIT:
Okay, I could only do rep_r_combinations

$ gmic rep_r_combinations 5,3,y
$ gmic rep_r_combinations 3,5,y
#@cli rep_r_combinations: number_of_items>0,choices>0,_axis={x,y,z}
rep_r_combinations:
skip ${3=x}

check "($1>0&&$2>0)&&(isint($1)&&isint($2))&&inrange(_'$3',_'x',_'z',1,1)"

n_items,choices=$1,$2
num_combs,axis={permut($choices,$n_items+$choices-1,0)},{_'$3'-_'x'}

if $axis==2   out_dim=1,1,$num_combs
elif $axis==1 out_dim=1,$num_combs,1
else          out_dim=$num_combs,1,1
fi

$out_dim,$choices,"
 begin(
  const n_items=$n_items;
  const max_ind=s-1;
  const axis=$axis;
  axis==2?(index()=z;):
  axis==1?(index()=y;):
           index()=x;
  comb=vector(#s,0);
  cur_ind=max_ind-1;
 );
 if(index(),
  val=++comb[max_ind];
  if(val==n_items,
   val=++comb[cur_ind];
   val==n_items?(
    --cur_ind;
    while(++comb[cur_ind]==n_items,
     --cur_ind;
    );
    val=comb[cur_ind];
   );
   copy(comb[cur_ind+1],val,max_ind-cur_ind,1,0); 
   cur_ind=max_ind-1;
  ); 
 );
 comb;
 "

In Python:

import itertools

count=0

# $ rep_r_combinations 3,5,y
# $ rep_r_combinations 5,3,y

for comb in itertools.combinations_with_replacement(range(3),5):
    print(comb)
    count+=1
    
print(count)

There might be a breakthrough though. I noticed that at v[max_ind-x], it appears to be (num_of_items-1)+(sum(num_of_items-1,num_of_items-2,num_of_items-3,num_of_items-4))… So, I can get index. I’ll figure that out on the weekend.


After playing with this code after receiving help in math discord, I think I have a direction to work with:

rep_Tkn:
n,k=$1,$2

echo {permut($k,$n+$k-1,0)}

Based on these comments:

Reptorian:
quick question: let's say T0(A) represents triangular number. 
Now, T1(A) would be the sum of triangular number. 
Like T1(6)=T0(1)+T0(2)+T0(3)....T0(6). T2(5)=T1(1)+T1(2)...T1(5). 
what can i look for to find expression for each Tn? 
-----
rat:
 — 
Today at 8:41 PM
it generalises very nicely actually
T0(n) = (n+1) choose 2
T1(n) = (n+2) choose 3
you can demonstrate this pattern continues inductively
Tk(n) = (n + k - 1) choose k

For the first time ever, we finally have a code that finds the index of repeated number combination (It does not exist anywhere in google results):

$ order=0,1,1,2,5 rep_r_combinations 6,{narg($order)},y echo ${rep_r_combinations_list2index\ 6,$order}
#@cli rep_r_combinations_list2index: number_of_items,index_a,index_b,...
#@cli : Return the list of numbers which correspond to the index provided based on a list of all possible combination with repetition.
#@cli : Note: All numbers must be in range [0,number_of_items). They must be in integer only.
#@cli : Author : Reptorian.
rep_r_combinations_list2index:
check "const v=$#;sum(isint([$*]))==v&&$1>-1&&(sum(inrange([${2--1}],0,$1,1,0))==v-1)"

if !$1
 status nan return
elif $#-1==1
 status {$2%$1} return
elif !sum(${2--1})
 status 0 return
fi

eval "
 const n_items=$1;
 const max_ind=n_items-1;
 const kv=$#-1;
 series_of_indexes=sort([${2--1}]);
 Tc=kv;
 index=0;
 a=permut(kv,n_items+kv-1,0); 
 repeat(kv-1,p,
  v_ind_p=series_of_indexes[p];
  diff=kv-v_ind_p;
  sum_nd=n_items+diff-p;
  b=permut(Tc,sum_nd-1,0);
  index+=a-b;
  if(v_ind_p==max_ind,break(););
  a=b-permut(Tc,sum_nd-2,0);  
  --Tc;
 );
 index+=series_of_indexes[kv-1]-series_of_indexes[kv-2];
 index;"
1 Like

EDIT: I found a formula to closest n possible given nCr==V. So, I pushed the very final combinatory tool.

Damn, it failed. So close…

EDIT: All fixed, I finally finished it.

1 Like

Added a new generic math-related code called digamma. It’s very accurate. Thanks to Mark Johnson for publishing his code and his permission for code use.

rep_digamma(5) -> 1.5061176683790356
rep_digamma(20) -> 2.9705239922421476

I do think this is better off as a built-in function though. Not really hard to implement in Cimg.h. Maybe I’ll do that later.

I think I will temporarily look at CImg.h and modifying it for a bit.

Here’s why:

I ran into a roadblock regarding G’MIC limitations. Right now, it seems that I am looking at implementing alpha-aware average downsizing. This means to resize image into a smaller images while factoring into alpha very existence. That means color images will show more vibrant colors as the black would not bleed into the downsized image.

Another reason is that I’m thinking of making a G’MIC math parser thing which is similar to resize, but with the caveat that it can only downsize, and you can choose to perform which operator to use. This would be downsize(V,new_size,operator).

V=[0,1,5,9,4,5,9,2,4,5,6,4];
NV=downsize(V,3,+);
# NV => [6,18,15,15]

Note that for your + case, you can use correlate() with a stride, like this:

foo :
  eval "
    V = [0,1,5,9,4,5,9,2,4,5,6,4];
    K = [ 1,1,1 ];
    W = correlate(V,size(V),1,1,1,  # I,wI,hI,dI,sI
                  K,size(K),1,1,1,  # K,wK,hK,dK,sK
                  0,0,0,            # boundary_conditions, is_normalized, channel_mode
                  0,0,0,            # xcenter,ycenter,zcenter
                  0,0,0,            # xtart,ystart,zstart
                  size(V)/size(K) - 1,0,0, # xend,yend,zend
                  size(K),1,1); # xstride,ystride,zstride

    print(V,W);
  "

[gmic]-0./ Start G'MIC interpreter.
[gmic_math_parser] V = (uninitialized) (mem[34]: vector12)
[gmic_math_parser] W = (uninitialized) (mem[52]: vector4)
[gmic_math_parser] V = [ 0,1,5,9,4,5,9,2,4,5,6,4 ] (size: 12)
[gmic_math_parser] W = [ 6,18,15,15 ] (size: 4)
[gmic]-0./ End G'MIC interpreter.

Or even better :

  eval "
    V = [0,1,5,9,4,5,9,2,4,5,6,4];
    W = resize(V,size(V)/3,2)*3;
    print(V,W);
  "

[gmic]-0./ Start G'MIC interpreter.
[gmic_math_parser] V = (uninitialized) (mem[34]: vector12)
[gmic_math_parser] W = (uninitialized) (mem[48]: vector4)
[gmic_math_parser] V = [ 0,1,5,9,4,5,9,2,4,5,6,4 ] (size: 12)
[gmic_math_parser] W = [ 6,18,15,15 ] (size: 4)
[gmic]-0./ End G'MIC interpreter.