Reptorian G'MIC Filters

Documentation is likely in the stdlib file. Maybe you could work on a snippets page or thread to make it more accessible for the rest of us, since you are already deep into this territory.

In general, exploring GUI and its design is important because that is how most people interact with and use G’MIC.

I don’t think a way to print all available variables exist within G’MIC. Would be nice to see which are local variables, which are global variables. If I had this, I would be able to print every variables with _gui_toolkit in it which would make my idea more of a reality and useful.

Yes, and to find ways around some approaches. Like in Transfer Color [Reduced Colors], _persistent is reserved for palettes info, and my idea makes it easier to avoid that issue and allocate _persistent to resulting image.

Other news, I released a new helper command named rep_print_files_dir. Yep, that what it does. So, you can use this in conjunction with input_text. Requires Python 3+ of course, but chances are that Python is installed.

Yes, sometimes, I wish for handy environment and authoring features, but that may be outside of the scope of G’MIC. Could you summarize the extra-G’MIC stuff you have done so far?

I recall you delving into syntax highlighting, and some Python code for cleanup and repetition.

Probably installed on Linux, but not sure on windows…
You could probably do a little check to see if a python executable exists on the system path?

For those who do not use Windows, for your information, it does not have Python by default. That means the command should have (1) help that informs the user of the dependency and (2) a way for the command to end gracefully should Python not be available.

May I suggest calling python (or any other external language) is used with extreme caution. There’s no doubt some will find such things useful, but it might be wise to mark in some way that external code is being executed. Not sure of the best way to do that - perhaps include python or “py” in the filter name?

I have used dynamic gui in some filters (Upscale Edge being one), but as you say it’s tricky so I tend to use sparingly. I haven’t needed to use persistent globals yet, but I understand the use case.

Hmm, I can change the name of the filters that rely on Python.

That being said, is there a OS-independent G’MIC code I can use to check if Python exists on one’s computer?

Yes, there was development of syntax highlighting for KDE Kate. It’s halted because of this bug I reported (and then, confirmed) - 473266 – Regex doesn't seem to respect flag state

And yes, there are Python codes. I can’t seem to find that thread, but if you need my Python codes used to help G’MIC Development:

Python codes for G'MIC development

GUI-Variable-Numbering.py - Copy GUI elements and then call this.

import pyperclip as pc
import re

pos=1
u_string=""

while True:
    init_mode=input("""
    Type in the one of the two characters in the set of two character at the left to use one of those mode?
    0F. Remove Number and Period. 
    1T. Add Numbers and Period 
    Choice: """)
    if init_mode=="0" or init_mode=="F":
        mode=False
        break
    elif init_mode=="1" or init_mode=="T":
        mode=True
        break

def output_number_dots(n):
    global pos
    global u_string
    out_str=""
    new_u_string=[]
    for p in range(n):
        out_str=out_str+str(pos+p)+"."
        new_u_string.append("$"+str(pos+p))
    new_u_string=",".join(new_u_string)
    u_string+="\"{"+new_u_string+"}\"\\\n"
    pos+=n
    return out_str
def count_number_color(inp_str):
    if inp_str[1]=='#':
        if len(inp_str)==9:
            return 3
        else:
            return 4
    else:
        return inp_str.count(",")+1
def process_level_2_mode_0(inp_str):
    current_pos=0
    for c in inp_str:
        if not (c.isnumeric() or c=='.'):
            break
        current_pos+=1
    return inp_str[current_pos:]
def process_level_2_mode_1(inp_str):
    try:
        variable_equal_type_str=re.search(level_2,inp_str).groups()
    except AttributeError:
        variable_equal_type_str=re.search(level_2, inp_str)
    if variable_equal_type_str is None:
        return inp_str
    separated_info=list(variable_equal_type_str)
    if separated_info[1]=="color" or separated_info[1]=="_color":
        num_of_cols=count_number_color(separated_info[2])
        new_variable_name=output_number_dots(num_of_cols)+separated_info[0]
        separated_info[0]=new_variable_name
        separated_info[1]="="+separated_info[1]
    elif separated_info[1]=="point" or separated_info[1]=="_point":
        separated_info[0]=output_number_dots(2)+separated_info[0]
        separated_info[1] = "=" + separated_info[1]
    else:
        separated_info[0]=output_number_dots(1)+separated_info[0]
        separated_info[1] = "=" + separated_info[1]
    return "".join(separated_info)

copied_code=pc.paste()
copied_code_str=str(copied_code)
copied_code_str_line=copied_code_str.splitlines()

level_0=r'(\#@gui\s*:|\)\,(?=([A-Z]|[0-9])))(.*)=(int|float|choice|text|bool|button|point|color|folder|file|text|_int|_float|_choice|_text|_bool|_button|_point|_color|_folder|_file|_text)(\(.*\)|{.*})(.*)'
level_1=r'((?<=\)),(?=[A-Z])|(?<=\}),(?=[A-Z])|(?<=\)),(?=[0-9])|(?<=\}),(?=[0-9]))'
level_2=r'(.*)=(int|float|choice|text|bool|button|point|color|folder|file|text|_int|_float|_choice|_text|_bool|_button|_point|_color|_folder|_file|_text)(\(.*\)|{.*})(.*)'

print("\n")

for line in copied_code_str_line:
    search_result=re.search(level_0, line)
    if search_result==None:
        print(line)
    else:
        if line[:7]=="#@gui :":
            new_line=line[7:]
        else:
            new_line=line[6:]
        level_1_search_result=re.search(level_1,new_line)
        if level_1_search_result==None:
            if mode==1:
                print("#@gui:"+process_level_2_mode_1(new_line))
            else:
                print("#@gui:" + process_level_2_mode_0(new_line))
        else:
            v_level_2=re.split(level_1,new_line)
            new_level_2=[]
            for index in v_level_2:
                if mode==1:
                    new_level_2.append(process_level_2_mode_1(index))
                else:
                    new_level_2.append(process_level_2_mode_0(index))
            print("#@gui:"+"".join(new_level_2))

if mode==1:
    print("\nu "+u_string)

Numberical-Argument-Shift.py - Copy cli code and then call this to shift numerical arguments.

from functools import partial
import re
import pyperclip as pc

gmic_str_paste=pc.paste()
gmic_str=str(gmic_str_paste)

pattern = re.compile(r"(?<=\$)(?:\{(\d+)-(\d+)\}|(\d+)|\{(\d+)=\})")

def addfunc(m, *, n, v):
    def incif(num):
        return num + n if num >= v else num

    a, b, c , d = m[1], m[2], m[3], m[4]
    if a and b:
        a = incif(int(a))
        b = incif(int(b))
        return f'{{{a}-{b}}}'
    elif c:
        c = incif(int(c))
        return f'{c}'
    elif d:
        d = incif(int(d))
        return f'{{{d}=}}'

inc_number=input("Increment Number: ")
greater_or_equal_to_num=input("Affect only number greater or equal to: ")

add = partial(addfunc, n=int(inc_number), v=int(greater_or_equal_to_num))

out_string = pattern.sub(add, gmic_str)

print("\n"+out_string)
1 Like

I will bookmark this post. I am quite forgetful. Do you have it up in community? If you could provide documentation for your Python tools and snippets in one location, that would be great.

Python could do many things including the untoward, so I agree that it should be clear that it is being used. That said, G’MIC uses other things too, albeit lightly, mostly for IO, I believe, so I do not know what the policy or threshold should be exactly. Maybe, provide the py files in a separate repository to be downloaded according to the discretion of the user.

You probably know this but there is this var :

$_os: A string describing the running operating system

You could use this to find out the os name and check environment variables , some default paths?

Or just ask for the path if G’mic allows to do this? That would probably cause some inconvenience though.

The thing is that I need to know other commands. For starters, we can check if Python exists without Python by doing where python in Windows, and which python in Linux. Then, to figure out how to get sys.exit to return 0/1 via G’MIC exec to confirm if Python is installed.

For now, I changed description of my G’MIC commands that calls Python, and clarification on version compatibility. This is what they look like now:

#@cli rep_python_print_files_dir:
#@cli : Print existing files in current directory.
#@cli : As the 'python' denotes, this command requires Python. Python Version Requirement: 3.6+
rep_python_print_files_dir:
exec 1,"python -c \"import os; d=os.listdir(); c=','; print(f'\\n[gmic][python]-"$^"./ {c.join(d)}')\""
#@cli rep_python_insert_integer_value:
#@cli : Allows user to insert integer values from the cli interface. Then, return the integer value.
#@cli :  As the 'python' denotes, this command requires Python. Python Version Requirement: 2.x+
rep_python_insert_integer_value:
exec 1,"python -c \"import sys; sys.exit(int(input('\\n[gmic][python]-"$^"./ Input Integer Value: ')))\""
#@cli rep_closest_number_in_ordered_list_of_numbers: number_to_search,number_a,number_b....

For Linux, this doesn’t work, but it’s the best I can do:

if which python; then exit 1 else exit 0 fi
1 Like

It should be something like that:
if [ $( which "python" ) ];then echo 0;else echo 1;fi

$(command) does the substitution.
Just replace “echo” with “exit”.

Back from a long hiatus, but still no update on filters for now. I do however have pretty good new. I’m finishing up on the gui toolkit I wanted to make palette things easier on GUI.

Current code looks like this:

#@cli rep_img2data:
#@cli : Return image to data info.
rep_img2data:
if $!!=1 error inv_imgs_count fi

status ({0,`"put_sep(s,_k) = (
                      for (k = _k; p = find(s,_','), p>=0, p = find(s,_',',++p),
                      s[p] = k%w?_',':k%wh?_';':k%whd?_'/':_'^'; ++k); s
                    );
                    s = v2s(crop(),-1); put_sep(s,1);"`}) # Code taken from print in G'MIC
#@cli rep_gui_toolkit_pal: palset_id,number_of_channels,delete_selected,deselect_selected,min_num_cols,max_num_cols,new_number_of_colors,color_a,selected,delete_color_a,color_b,selected,delete_color_b,....,add_color
#@cli : A helper command for generating palette and manipulating palettes via GUI.
#@cli : $ +rep_gui_toolkit_pal ,3,0,1,,240,235,110,0,1,110,59,235,0,0,0
+rep_gui_toolkit_pal:
skip ${1=},${5=0},${6=},${7=}
if size('$1') a=_$1 fi
old_status=${}

# For searching purpose : _gui_toolkit_pal

#################################################################
#  Available Global Variables used in this GUI toolkit command  #
#################################################################
#  Note : ? means user-defined variable. If there is, then      #
#  ? is replaced with _.id is replaced with id name. Otherwise, #
#  ?id is NULL.                                                 #
#                                                               #
#  _gui_toolkit_pal?id_number_of_colors                         #
#  _gui_toolkit_pal?id_color_values_$ind                        #
#  _gui_toolkit_pal?id_selected_color_$ind                      #
#  _gui_toolkit_pal?id_delete_color_$ind                        #
#  _gui_toolkit_pal?id_data                                     #
#################################################################

init_vars=number_of_channels,delete_selected,deselect_selected,min_num_cols,max_num_cols,new_number_of_colors,add_colors
size_init_vars={narg($init_vars)}
m "temp: skip \"$\{"{$size_init_vars-1}"=\}\" ,\"$\{"{$size_init_vars}"=\}\" u ${2-"$size_init_vars"}"
$init_vars=${temp\ $"*"},$-1
um temp

size_of_crop={($number_of_channels+2)}
_gui_toolkit_pal${a}_number_of_colors={($#-$size_init_vars-1)/$size_of_crop}
if !narg($new_number_of_colors) new_number_of_colors=${_gui_toolkit_pal${a}_number_of_colors} fi
if !isint(${_gui_toolkit_pal${a}_number_of_colors}) error inval_arg fi

if ${_gui_toolkit_pal${a}_number_of_colors}==$new_number_of_colors

    utilize_delete_single_pixel=0
    number_of_init_select_mode={$delete_selected+$deselect_selected}

    if $number_of_init_select_mode==2 
        error selec_mode_contradic
    elif $delete_selected||$deselect_selected
        1
    else
        m "temp: u ${"{$size_init_vars+$size_of_crop}"--2:"$size_of_crop"}"
        delete_pos={find([${temp\ $"*"}],1,0,1)}
        um temp
        utilize_delete_single_pixel={$delete_pos!=-1}
    fi
 
    contain_selected=0
    m "temp: u ${"{$size_init_vars+1}"--1}"
    1,{${_gui_toolkit_pal${a}_number_of_colors}-$utilize_delete_single_pixel},1,$number_of_channels,>"begin(
            const utilize_delete_single_pixel=$utilize_delete_single_pixel;
            const desel_sel=$deselect_selected;
            const del_sel=$delete_selected;
            const utilize_base_select_based_settings=desel_sel||del_sel;
            const number_of_channels=$number_of_channels;
            const size_of_crop=$size_of_crop;
            list_of_values=["${temp\ $"*"}"];
            
            utilize_base_select_based_settings?(
                contain_selected=0;
                const selection_index=size_of_crop-2;
                
                process_input_into_data()=(
                    data=(list_of_values)[y*size_of_crop,size_of_crop];
                    if(data[selection_index],
                        da_push(#-1,y);
                        contain_selected=1;
                    );
                );
            ):(
                utilize_delete_single_pixel?(
                    const delete_pos=$delete_pos;
                    process_input_into_data()=data=(list_of_values)[(y+(y>=$delete_pos))*size_of_crop,size_of_crop];
                ):(
                    process_input_into_data()=data=(list_of_values)[y*size_of_crop,size_of_crop];
                );
            );
        );
        process_input_into_data();
        (data)[0,number_of_channels];
        end(
            if(utilize_base_select_based_settings,
                if(contain_selected,
                    set('contain_selected',contain_selected);
                    if(contain_selected,
                        da_freeze(#-1);
                    );
                );
            );
        );"
    
    um temp
    
    if $number_of_init_select_mode
        
        if $delete_selected
            if $contain_selected
                if h#-1-h#-2<$min_num_cols error excess_remove fi # Error if removing colors would reduce the number of color below the minimum.
                ({h}) append[-2,-1] y
                eval[-2] <da_remove(#-1,i);end(da_freeze(#-1);); # Remove selected colors
                
                repeat ${_gui_toolkit_pal${a}_number_of_colors} {
                    if $><h#-1 _gui_toolkit_pal${a}_color_values_$>={I[#-1,$>]} fi
                    _gui_toolkit_pal${a}_selected_color_$>,_gui_toolkit_pal${a}_delete_color_$>=0
                }
                
            fi
        else
            if $contain_selected
                repeat h#-2 { _gui_toolkit_pal${a}_selected_color_{i[#-2,$>]}=0 }
            fi
        fi
        
        remove[-2]
    else
        if $utilize_delete_single_pixel
            _gui_toolkit_pal${a}_number_of_colors-=1
            ind=${_gui_toolkit_pal${a}_number_of_colors}
            _gui_toolkit_pal${a}_selected_color_$ind,_gui_toolkit_pal${a}_delete_color_$ind=0
            _gui_toolkit_pal${a}_color_values_$ind={vector(#${_gui_toolkit_pal${a}_number_of_colors},0)}
        fi
    fi
 
fi

_gui_toolkit_pal${a}_data=${-rep_img2data[-1]}

status $old_status

Of course, this doesn’t tell us how to use it yet. I’ll provide some examples when I finish.

I’m just gonna leave this here because I love the idea:

Something to do for later.

I added a new command named rep_nonexistent_colors_pal . It does what it says. Creates new color that does not exist in a palette.

It turned out a person has an answer to one of my question: python - Find the list of combination (with repetition) given rank and number of items. (Lexicographic ordering) - Code Review Stack Exchange

Yes, it’s in Python and I used more common language to reduce language barrier. I think I can improve one of my code with this finding.

EDIT: I tested it in G’MIC, and it is correct. I pushed this new algorithm. Now for the multi-threaded filling approach.

Final EDIT: I pushed the multi-threaded filling approach too thanks to this Joseph Wood.

Here are timings for 20,030,010 repeated combinations:

C:\Windows\System32
λ gmic tic +new_rep_r_combinations 20,10 toc
[gmic]./ Start G'MIC interpreter (v.3.3.2).
[gmic]./ Initialize timer.
[gmic]./ Elapsed time: 0.612 s.
[gmic]./ Display image [0] = '[: begin( const n_items=$n_it...'.
[0] = '[: begin( const n_items=$n_items; const max_ind=s-1; const ax...':
  size = (20030010,1,1,10) [764.1 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ... ,19,18,19,19,18,19,19,19,18,19,19,19,19,18,19,19,19,19,19,18,19,19,19,19,19,19,18,19,19,19,19,19,19,19,18,19,19,19,19,19,19,19,19,18,19,19,19,19,19,19,19,19,19,18,19,19,19,19,19,19,19,19,19,19).
  min = 0, max = 19, mean = 9.5, std = 5.76628, coords_min = (0,0,0,0), coords_max = (2.003e+07,0,0,0).
[gmic]./ End G'MIC interpreter.

C:\Windows\System32
λ gmic tic +rep_r_combinations 20,10 toc
[gmic]./ Start G'MIC interpreter (v.3.3.2).
[gmic]./ Initialize timer.
[gmic]./ Elapsed time: 1.398 s.
[gmic]./ Display image [0] = '[> begin( const n_items=$n_it...'.
[0] = '[> begin( const n_items=$n_items; const max_ind=s-1; const ax...':
  size = (20030010,1,1,10) [764.1 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ... ,19,18,19,19,18,19,19,19,18,19,19,19,19,18,19,19,19,19,19,18,19,19,19,19,19,19,18,19,19,19,19,19,19,19,18,19,19,19,19,19,19,19,19,18,19,19,19,19,19,19,19,19,19,18,19,19,19,19,19,19,19,19,19,19).
  min = 0, max = 19, mean = 9.5, std = 5.76628, coords_min = (0,0,0,0), coords_max = (2.003e+07,0,0,0).
[gmic]./ End G'MIC interpreter.

I have introduced preliminary sorting option logical_sort for the rep_string_permutation* family of command.

This is how characters are to be ordered by with logical_sort:

⊕⊖⊗⊘⊙♠♣♥♦←↑→↓↔↕©®$€¢¥£¤µμ§∫∇α∂δεηΓγλωφπτψρσθΔΩΦΠΣΨΘ∩∪∧∨∈√_[]{}()<>«»¬~!¡¿?&|^*×÷+-±=≠%`´'",./\:; @°#¯¼½¾¹²³∞0123456789¨AÀÁÂÃÄÅÆBCÇDÐEÈÉÊËFGHIÌÍÎÏJKLMNÑOÒÓÔÕÖØPQRSβTÞUÙÚÛÜVWXYÝZaàáâãäåæbcçdeèéêëfghiìíîïjklmnñoòóôõöðøpqrsßtþuùúûüvwxyýÿz·¸ºª ¦­¶

I started work on rep_string_combination* family of commands after discovering this - combinatorics - What is the formula for combinations with identical elements? - Mathematics Stack Exchange .

Need a opinion on this, yay or nay?

#@cli rep_cin_numbers:
#@cli : Allows users to type output number into status.
rep_cin:
screen_w,screen_h={*,u},{*,v}
screen_h>>=1

do
 input_changed=0
 
 if {*,0}   o.=0 input_changed=1
 elif {*,1} o.=1 input_changed=1
 elif {*,2} o.=2 input_changed=1
 elif {*,3} o.=3 input_changed=1
 elif {*,4} o.=4 input_changed=1
 elif {*,5} o.=5 input_changed=1
 elif {*,6} o.=6 input_changed=1
 elif {*,7} o.=7 input_changed=1
 elif {*,8} o.=8 input_changed=1
 elif {*,9} o.=9 input_changed=1
 elif {*,BACKSPACE} 
  if narg($o)
   ('$o') crop 0,{w-2} o={t} rm.
  fi
 elif {*,ENTER} break
 else w[] $screen_w,1,0,0,0,$screen_h,G'MIC\ input2output:\ $o
 fi
 
 wait 100
 wait
while {*}

status $o

Useful in case of when putting in inputs while G’MIC is being executed. That’s the reason why I made this.

Could also be useful when you’re using exec to run non-G’MIC scripts and you’re using multiple options. Like the one I posted on the thread that points to Python scripts to aid into G’MIC scripts. But, there is a good workaround in this case.

The last case I can think of is improving interactive filters with multiple options, but there are already workarounds to this.

Looks like it’s unnecessary long.
Something as:

repeat 10 { if {*,$>} o.=$> input_changed=1 fi }

seems appropriate, no ?

Yeah, that does work. Nice tip.

EDIT: I fixed my issues, I think I’ll replace one of my G’MIC code that executes python with it.

EDIT: Done. Replace command with more versatile one. · GreycLab/gmic-community@90d41b7 · GitHub

No longer need Python for that one. Kinda clunky solution, but it’ll do. Does it have lots of limitations? Yes, but it’ll do too.

So do I. See mix_rgb.

Goodness, gracious. I haven’t tormented the cat in ages. Let’s rotate the miserable little creature in color space, say around the white vector, maybe clockwise 145° will do it.

$ gmic run 'sp cat,512 f. begin(mat=rot([1,1,1],-145°));mat*I n. 0,255'

bluecat

The “white vector” aligns with the black-white diagonal of the color cube; the example uses such as an axis of rotation, passed as the first vector parameter to the mathematical expression function rot() The second parameter specifies the rotation around the axis. We stash the rot() transformation matrix in mat, in a begin() section, then apply that matrix to all of the image pixels in a fill iteration. We get a blue cat.

Any axis of rotation, and any rotation angle, may be used. The animations in mix_rgb are based upon random pixel choices — a pixel in the sky, a pixel on the beach — as rotation axes; the animation then steps through rotation angles. The axis color remains constant in the animations; all the other colors rotate around that axis. Stepping off point to a bunch of interesting effects. Some of that spills over into orientation and norm tutorials.

Have fun.

1 Like