#@gmic # # File : gmic_stdlib.gmic # ( G'MIC command file ) # # Description : GREYC's Magic for Image Computing - Standard library # ( https://gmic.eu ) # # Copyright : David Tschumperlé # ( https://tschumperle.users.greyc.fr/ ) # # Licenses : This file is 'dual-licensed', you have to choose one # of the two licenses below to apply. # # CeCILL-C # The CeCILL-C license is close to the GNU LGPL. # ( http://cecill.info/licences/Licence_CeCILL-C_V1-en.html ) # # or CeCILL v2.1 # The CeCILL license is compatible with the GNU GPL. # ( http://cecill.info/licences/Licence_CeCILL_V2.1-en.html ) # # This software is governed either by the CeCILL or the CeCILL-C license # under French law and abiding by the rules of distribution of free software. # You can use, modify and or redistribute the software under the terms of # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA # at the following URL: "http://cecill.info". # # As a counterpart to the access to the source code and rights to copy, # modify and redistribute granted by the license, users are provided only # with a limited warranty and the software's author, the holder of the # economic rights, and the successive licensors have only limited # liability. # # In this respect, the user's attention is drawn to the risks associated # with loading, using, modifying and/or developing or reproducing the # software by the user in light of its specific status of free software, # that may mean that it is complicated to manipulate, and that also # therefore means that it is reserved for developers and experienced # professionals having in-depth computer knowledge. Users are therefore # encouraged to load and test the software's suitability as regards their # requirements in conditions enabling the security of their systems and/or # data to be ensured and, more generally, to use and operate it in the # same conditions as regards security. # # The fact that you are presently reading this means that you have had # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms. # #------ Syntax rules for a G'MIC command file : #*** General syntax : # # - Each line starting with 'command_name :' starts a new definition of the G'MIC custom command 'command_name'. # - Each line starting with '#' is a comment line. # - Any other line is considered as the continuation of a previously started G'MIC custom command. # #*** Specific rules for the command-line interface 'gmic': # # - A comment line starting with '#@cli' will be parsed by 'gmic' to print help for # G'MIC custom commands (when invoked with option 'h'). More precisely : # # _ '#@cli :: subsection' defines a new command subsection in the displayed help. # _ '#@cli command_name : arguments_format1 : arguments_format2 : ... : (qualifier)' # starts a new command description. # _ '#@cli : description' add a new description line to the current command description. # _ '#@cli : $ command_line' defines a new example of use of the current command. # _ '#@cli : $$ _pagename' tells the command has a dedicated page in the web tutorial. # #*** Specific rules for the universal plug-in 'gmic-qt': # # - A comment line starting with '#@gui' will be parsed by the plug-in to define the filters tree. # - A comment line starting with '#@gui_xx' will define a filter only for a specific language 'xx' # (e.g. 'en','fr'...). # - A comment line starting with '#@gui_xx hide(/Filter or folder name)' will hide the existing # filter of folder for the locale 'xx'. # - More precisely, the syntax of a '#@gui' comment line is : # # '#@gui Folder name' # # or # # '#@gui Command name : command, preview_command (zoom_factor)[*|+] [: default_input_mode] # '#@gui : parameter1 = typedef(arguments1...), parameter2 = typedef(arguments2...)' # '#@gui : parameter3 = typedef(arguments3...), # # where : # # 'command' is the G'MIC command name called to process the image. # # 'preview_command' is the G'MIC command name called to process the preview. # # Note that you can optionally specify a float-valued factor>=0 between parentheses at the end of # the 'preview_command' to force the default zoom factor used by the preview for this filter. # Use (0) for a 1:1 preview, (1) for previewing the whole image, (2) for 1/2 image and so on... # You can put an additional '+' sign after the parenthesis to specify the rendered preview # is still accurate for different zoom factors. Use '*' instead to tell the plug-in that the preview filter # must get the entire image rather than a thumbnail. # # # 'default_input_mode' set the default input mode for that filter. It can be # { x:none | .:active (default) | *:all | +:active & below | -:active & above | v:all visible | i:all invisible } # # 'parameter = typedef' tells about the names, types and default values of the filter parameters. # # 'typedef' can be : # # _ 'bool(default_value={ 0 | 1 | false | true })': # Add a boolean parameter (0 or 1) (as a checkbutton). # # _ 'button(_alignment)': # Add a boolean parameter (0 or 1) (as a button). # # _ 'choice(_default_index,Choice0,..,ChoiceN)': # Add a integer parameter (as a combobox). # # _ 'color(R,_G,_B,_A)': # Add R,G,B[,A] parameters (as a colorchooser). # # _ 'point(_X,_Y,_removable={ -1 | 0 | 1 },_burst={ 0 | 1 },_R,_G,_B,_[-]A,_radius[%])': # Add X,Y parameters (as a moveable point over the preview). # # _ 'file(_default_filename)', 'file_in(_default_filename)' or 'file_out(_default_filename)': # Add a filename parameter (as a filechooser). # # _ 'float(default_value,min_value,max_value)': # Add a float-valued parameter (as a float slider). # # _ 'folder(_default_foldername)': # Add a foldername parameter (as a folderchooser). # # _ 'int(default_value,min_value,max_value)': # Add a integer parameter (as an integer slider). # # _ 'link(_alignment,_label,URL)': # Display a URL (do not add a parameter). # # _ 'note(_label)': # Display a label (do not add a parameter). # # _ 'text(_is_multiline={ 0 | 1 },_default text)': # Add a single or multi-line text parameter (as a text entry). # # _ 'separator()': # Display an horizontal separator (do not add a parameter). # # _ 'value(value)': # Add a pre-defined value parameter (not displayed). # # Type separators '()' can be replaced by '[]' or '{}' if necessary (for instance if parentheses are required in # an argument of the typedef, e.g in a text). You can also replace 'typedef' by '_typedef' to tell the plug-in not # being responsive, i.e not to update the image preview when the corresponding parameter is modified. # After the closing separator, you may specify a 'visibility state' character for the parameter, which can be # { _0:Hidden | _1:Grayed-out | _2:Visible (default) }, opt. followed by a propagation character that tells # if this visibility state must be propagated to neighboring non-valued interface widgets # (s.a. separator(), link() or note()). # This propagation character can be: # { +:propagate forward | -:propagate backward | *:propagate in both directions }. # # Use '_none_' as a special command or preview_command to tell the plug-in that the entry requires no G'MIC call. # # A G'MIC command can set new values for each filter parameter, through the status (see command ''status''). # To do so, the returned status must follow the syntax : # '{params1}{params2}{..}{paramsN}' where N must be exactly equal to the number of parameters # for the current filter. Optionally, you can append to each {param} its visibility state suffix ( e.g: {param}_1 ). # # A G'MIC command can also specify the output blending mode, the opacity and the position of each of the output image # (i.e. layer in the plug-in). To do so, set the image name to something like: # 'mode(grainmerge),opacity(50),pos(30,50),name(name)'. # # - Blending mode name should be the same as the argument of the 'blend' command. # - Opacity is a float number in [0,100]. # - X and Y positions are integers. # - 'name' is the layer name. # # Pre-defined plug-in variables: # # - _preview_enabled: Boolean that tells if the preview is enabled or not. # - _preview_width: Width of the preview image. # - _preview_height: Height of the preview image. # - _preview_area_width: Width of the preview widget area. # - _preview_area_height: Height of the preview widget area. # - _preview_x0: X-coordinate of the upper-left corner of the preview (expressed in the image space). # - _preview_y0: Y-coordinate of the upper-left corner of the preview (exressed in the image space). # - _preview_x1: X-coordinate of the lower-right corner of the preview (expressed in the image space). # - _preview_y1: Y-coordinate of the lower-right corner of the preview (expressed in the image space). # - _persistent : Variable that is shared between successive calls to the G'MIC interpreter, for a single filter. # # Values of variables $_preview[x/y][0/1] vary only when preview mode is set to * (full preview). # #----------------------------------------------------------------------------------------------------------------------- #--------------------------------- # #@cli :: Global Options # #--------------------------------- # This command is run when the cli tool 'gmic' is invoked without arguments on the command line. cli_noarg : v 0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r _cli_noarg=1 version +e "\n[gmic] No commands, options or data provided." gmic_help="gmic help" if !${-is_macos}" && "!${-is_windows} gmic_help.=" | less" if $_vt100 gmic_help.=" -r" fi fi if {*,u}>0 +e "[gmic] (type "${c}"'"$gmic_help"'"$n" to print help, "${c}"'gmic demos'"$n" to launch demos)." else +e "[gmic] (type "${c}"'"$gmic_help"'"$n" to print help)." fi file_update=${_path_rc}update$_version.gmic if "isfile(['"{/$file_update}"'])" update_old:="Y = date(0); M = date(1); D = date(2); date_current = Y*365 + M*31 + D; Y = date(0,'"{/$file_update}"'); M = date(1,'"{/$file_update}"'); D = date(2,'"{/$file_update}"'); date_file = Y*365 + M*31 + D; date_current - date_file" if $update_old>=14 +e "[gmic] Command update file is "$update_old" days old, type "$g"'gmic up'"$n" to update it." fi else +e "[gmic] Fetch update and documentation from the G'MIC server." l[] { up state=${g}Success!$n onfail state=${r}Failed!$n } +e "\r[gmic] Fetch update and documentation from the G'MIC server. "$state fi +e "\n" # cli_start # This command is called each time the cli interface 'gmic' starts. # Overload it in your local user command file if necessary. cli_start : #@cli debug : (+) #@cli : Activate debug mode. #@cli : When activated, the G'MIC interpreter becomes very verbose and outputs additional log #@cli : messages about its internal state on the standard output (stdout). #@cli : This option is useful for developers or to report possible bugs of the interpreter. #@cli h : eq. to 'help'. h : help $"*" #@cli help : command : (no arg) #@cli : Display help (optionally for specified command only) and exit. #@cli : (eq. to 'h'). help : skip ${1=""} use_vt100 foreach { if "w!=1 || d!=1 || s!=1" rm fi } if ['$1']==0 # Display global help. _no_examples,_no_default_values,_no_tutorial_link=1 reference ascii else # Display help for a single command. is_embedded_stdlib:=$!==1 if narg("$1")" && "isfile(['{/$_path_user}']) l[] { it[] $_path_user onfail } fi if narg("$1")" && "narg($_path_commands) l[] { $_path_commands foreach { l { it[] {n} onfail 0 } k. } onfail } fi if isfile(['{/$_path_rc/update$_version.gmic}']) l[] { i cimgz:$_path_rc/update$_version.gmic onfail l[] { it[] $_path_rc/update$_version.gmic onfail } } fi if $is_embedded_stdlib mv[0] $! fi if !$! return fi a y # Check that requested command exist. command={`" s = ['$1']; len = size(s); z = 0; (p = find(s,_'['))>0?(s[p] = 0; len = p); isin(s[0],_'-',_'+') && len>1 && !(s[z + 1]==_'3' && s[z + 2]==_'d')?(copy(s,s[z + 1],len - 1); s[len - 1] = 0); s"`} s +,{'"#@cli "$command" :"'} s +,{'"#@cli "$command":"'} s +,{'"#@cli "$command"\n"'} if $!==1 l[] { m "foo : "$command # Detect command misspelling. repeat 16 { um $command } # Be sure the specified command does not exist anymore ! foo um foo onfail ('${}') s -,{'"; did you mean "'} if $!>1 s[1] -,39 k[1] misspelling={t} fi rm } if narg($misspelling) misspelling="; did you mean '"$_vt100_g$misspelling$_vt100_n"' ?" fi +e "\n[gmic] No help available for command '"$_vt100_r$command$_vt100_n"'"$misspelling". "\ "\n Try '"${_vt100_c}"gmic help"$_vt100_n"' for global help." else a y _display_categories=0 +parse_cli ascii,$command if narg(${}) parse_cli ascii,${} fi # In case of shortcut, display also help for shortcut command. fi fi +e "\n" rm q # Command to write reference documentation with various output modes. # $1 = output mode, can be { ascii | html | man | pdf }. # $2 = name of the folder containing additional .gmd pages (optional). # Reference and command documentation is written using the G'MIC-markdown syntax. reference : skip "${2=}" m "_section : reference_section_$1 \"$""*\"" m "_text : reference_text_$1 \"$""*\"" l { reference_begin_$1 reference_header_$1 onfail } _section "Usage" _text \ "~~~\ngmic [command1 [arg1_1,arg1_2,..]] .. [commandN [argN_1,argN_2,..]]\n~~~"\n\n\ "`gmic` is the open-source interpreter of the \\\G'MIC language, a scripting programming "\ "language dedicated to the design of possibly complex image processing pipelines and operators."\n\ "It can be used to convert, manipulate, filter and visualize image datasets made of one or "\ "several 1D/2D or 3D multi-spectral images."\n\ \n\ "This reference documentation describes all the technical aspects of the G'MIC framework, "\ "in its current version ___"${-strver}"___."\n\ \n\ "As a starting point, you may want to visit our detailed tutorial pages, at: " _section "Overall Context" _text \ "* At any time, \\G'MIC manages one list of numbered (and optionally named) pixel-based images, "\ "entirely stored in computer memory (uncompressed)."\n\ "* The first image of the list has index '0' and is denoted by '[0]'. The second image of the "\ "list is denoted by '[1]', the third by '[2]' and so on."\n\ "* Negative indices are treated in a periodic way: '[-1]' refers to the last image of the list, '[-2]' to the "\ "penultimate one, etc. Thus, if the list has 4 images, '[1]' and '[-3]' both designate the second image of the list."\n\ "* A named image may be also indicated by '[name]', if 'name' uses the character set `[a-zA-Z0-9_]` and does not "\ "start with a number. Image names can be set or reassigned at any moment during the processing pipeline "\ "(see command ''name'' for this purpose)."\n\ "* G'MIC defines a set of various commands and substitution mechanisms to allow the design of complex "\ "pipelines and operators managing this list of images, in a very flexible way: You can insert or remove images "\ "in the list, rearrange image order, process images (individually or grouped), merge image data together, "\ "display and output image files, etc."\n\ "* Such a pipeline can define a new custom G'MIC command (stored in a user command file), and re-used "\ "afterwards as a regular command, in a larger pipeline if necessary." _section "Image Definition and Terminology" _text \ "* In \\G'MIC, each image is modeled as a 1D, 2D, 3D or 4D array of scalar values, uniformly "\ "discretized on a rectangular/parallelepipedic domain."\n\ "* The four dimensions of this array are respectively denoted by:"\n\ " - `width`, the number of image columns (size along the `x-axis`)."\n\ " - `height`, the number of image rows (size along the `y-axis`)."\n\ " - `depth`, the number of image slices (size along the `z-axis`). "\ "The depth is equal to '1' for usual color or grayscale 2D images."\n\ " - `spectrum`, the number of image channels (size along the `c-axis`). "\ "The spectrum is respectively equal to '3' and '4' for usual `RGB` and `RGBA` color images."\n\ \n\ "* There are no hard limitations on the size of the image along each dimension. For instance, the number of image "\ "slices or channels can be of arbitrary size within the limits of the available memory."\n\ "* The `width`, `height` and `depth` of an image are considered as _spatial_ dimensions, while the `spectrum` has a "\ "_multi-spectral_ meaning. Thus, a 4D image in G'MIC should be most often regarded as a 3D dataset of multi-spectral "\ "voxels. Most of the G'MIC commands will stick with this idea (e.g. command ''blur'' blurs images only along the "\ "spatial `xyz`-axes)."\n\ "* G'MIC stores all the image data as buffers of `float` values (32 bits, value range '[-3.4E38,+3.4E38]'. "\ "It performs all its image processing operations with floating point numbers. Each image pixel takes "\ "then 32bits/channel (except if double-precision buffers have been enabled during the compilation of the software, "\ "in which case 64bits/channel can be the default)."\n\ "* Considering `float`-valued pixels ensure to keep numerical precision when executing image processing "\ "pipelines. For image input/output operations, you may want to prescribe the image datatype to be different than "\ "`float` (like `bool`, `char`, `int`, etc.). This is possible by specifying it as a file option when using "\ "I/O commands (see section ''Input/Output Properties'' to learn more about file options)." _section "Items of a Processing Pipeline" _text \ "* In \\G'MIC, an image processing pipeline is described as a sequence of items separated by the "\ "__space__ character. Such items are interpreted and executed from the left to the right. For instance, "\ "the expression:"\n\ "~~~\nfilename.jpg blur 3,0 sharpen 10 resize 200%,200% output file_out.jpg\n~~~\n"\ "defines a valid pipeline composed of nine G'MIC items."\n\n\ "* Each G'MIC item is either a __command__, a list of __command arguments__, "\ "a __filename__ or a special __input string__."\n\ "* Escape characters '\\' and double quotes '\"' can be used to define items containing spaces or "\ "other special characters. For instance, the two strings `single\\ item` and `\"single item\"` "\ "both define the same single item, with a space in it." _section "Input Data" _text \ "* If a specified \\G'MIC item appears to be an existing filename, the corresponding image data "\ "are loaded and inserted at the end of the image list (which is equivalent to the use of `input filename`). "\n\ "* Special filenames `-` and `-.ext` stand for the standard input/output streams, optionally "\ "forced to be in a specific 'ext' file format (e.g. `-.jpg` or `-.png`). "\n\ "* The following special input strings may be used as G'MIC items to create and insert new "\ "images with prescribed values, at the end of the image list:"\n\ " - '[selection]' or '[selection]xN': Insert 1 or N copies of already existing images. "\ "'selection' may represent one or several images (see section ''Command Items and Selections'' to learn more "\ "about selections)."\n\ " - 'width[%],_height[%],_depth[%],_spectrum[%],_values[xN]': Insert one or N images with specified "\ "size and values (adding '%' to a dimension means __\"percentage of the size along the same axis\"__, "\ "taken from the last image '[-1]'). Any specified dimension can be also written as "\ "'[image]', and is then set to the size (along the same axis) of the existing specified image "\ "'[image]'. 'values' can be either a sequence of numbers separated by commas ',', "\ "or a mathematical expression, as e.g. in input item '256,256,1,3,[x,y,128]' which "\ "creates a `256x256` RGB color image with a spatial shading on the red and green channels. "\ "(see section ''Mathematical Expressions'' to learn more about mathematical expressions). "\n\ " - '(v1,v2,..)[xN]': Insert one or `N` new images from specified prescribed values. Value separator "\ "inside parentheses can be ',' (column separator), ';' (row separator), '/' (slice separator) or "\ "'^' (channel separator). For instance, expression '(1,2,3;4,5,6;7,8,9)' creates a 3x3 matrix (scalar image), "\ "with values running from 1 to 9. "\n\ " - '(\\'string\\'[:delimiter])[xN]': Insert one or N new images from specified string, by filling "\ "the images with the character codes composing the string. When specified, 'delimiter' tells about "\ "the main orientation of the image. Delimiter can be 'x' (eq. to ',' which is the default), "\ "'y' (eq. to ';'), 'z' (eq. to '/') or 'c' (eq. to '^'). "\ "When specified delimiter is ',', ';', '/' or '^', the expression is actually equivalent to "\ "'({\\'string\\'[:delimiter]})[xN]' (see section ''Substitution Rules'' for more information on the syntax)."\n\ " - '0[xN]': Insert one or N new `empty` images, containing no pixel data. "\ "Empty images are used only in rare occasions."\n\ \n\ "* Input item 'name=value' declares a new variable 'name', or assign a new string value to an existing variable. "\ "Variable names must use the character set `[a-zA-Z0-9_]` and cannot start with a number. "\n\ "* A variable definition is always local to the current command except :"\n\ " - When it starts by the underscore character '_'. In that case, it becomes also accessible by any command invoked "\ "outside the current command scope (global variable)."\n\ " - When defined in a _subcommand_ of the current command, a variable becomes also accessible in the parent command. "\ "A _subcommand_ of a command `foo` is a command whose name starts with `_foo` (e.g. `_foo_sub`) and that is called "\ "from `foo`."\n\ "* If a variable name starts with two underscores `__`, the global variable is also shared among different threads "\ "and can be read/set by commands running in parallel (see command ''parallel'' for this purpose). "\ "Otherwise, it remains local to the thread that defined it."\n\ "* Numerical variables can be updated with the use of these special operators: "\ "'+=' (addition), '-=' (subtraction), '*=' (multiplication), '/=' (division), '%=' (modulo), '&=' (bitwise and), "\ "'|=' (bitwise or), '^=' (power), '<<=' and '>>' (bitwise left and right shifts). For instance, 'foo=1' 'foo+=3'."\n\ "* Input item 'name.=string' appends specified `string` at the end of variable 'name'."\n\ "* Input item 'name..=string' prepends specified `string` at the beginning of variable 'name'."\n\ "* Multiple variable assignments and updates are allowed, with expressions: 'name1,name2,...,nameN=value' or "\ "'name1,name2,...,nameN=value1,value2,...,valueN' where assignment operator '=' can be replaced by one of the "\ "allowed operators (e.g. '+=')."\n\ "* Variables usually store numbers or strings. Use command ''store'' to assign variables from image data "\ "(and syntax `input $variable` to bring them back on the image list afterwards)." _section "Command Items and Selections" _text \ "* A \\G'MIC item that is not a filename nor a special input string designates a 'command' "\ "most of the time. Generally, commands perform image processing operations on one or several available images "\ "of the list."\n\ "* Reccurent commands have two equivalent names ('regular' and 'short'). For instance, command names "\ "'resize' and 'r' refer to the same image resizing action."\n\ "* A G'MIC command may have mandatory or optional __arguments__. Command arguments must be specified "\ "in the next item on the command line. Commas ',' are used to separate multiple arguments of a single command, "\ "when required."\n\ "* The execution of a G'MIC command may be restricted only to a __subset__ of the image list, by "\ "appending '[selection]' to the command name. Examples of valid syntaxes for 'selection' are: "\n\ " - 'command[-2]': Apply command only on the penultimate image '[-2]' of the list."\n\ " - 'command[0,1,3]': Apply command only on images '[0]', '[1]' and '[3]'."\n\ " - 'command[3-6]': Apply command only on images '[3]' to '[6]' (i.e, '[3]', '[4]', '[5]' and '[6]')."\n\ " - 'command[50%-100%]': Apply command only on the second half of the image list."\n\ " - 'command[0,-4--1]': Apply command only on the first image and the last four images."\n\ " - 'command[0-9:3]': Apply command only on images '[0]' to '[9]', with a step of 3 "\ "(i.e. on images '[0]', '[3]', '[6]' and '[9]')."\n\ " - 'command[0--1:2]': Apply command only on images of the list with even indices. "\n\ " - 'command[0,2-4,50%--1]': Apply command on images '[0]', '[2]', '[3]', '[4]' and on the second half of "\ "the image list."\n\ " - 'command[^0,1]': Apply command on all images except the first two."\n\ " - 'command[name1,name2]': Apply command on named images 'name1' and 'name2'."\n\ \n\ "* Indices in selections are always sorted in increasing order, and duplicate indices are "\ "discarded. For instance, selections '[3-1,1-3]' and '[1,1,1,3,2]' are both equivalent to "\ "'[1-3]'. If you want to repeat a single command multiple times on an image, use a "\ "'repeat..done' loop instead. Inverting the order of images for a command is achieved by "\ "explicitly inverting the order of the images in the list, with command 'reverse[selection]'."\n\ "* Command selections '[-1]', '[-2]' and '[-3]' are so often used they have their own shortcuts, respectively "\ "'.', '..' and '...'. For instance, command 'blur..' is equivalent to 'blur[-2]'. "\ "These shortcuts work also when specifying command arguments."\n\ "* G'MIC commands invoked without '[selection]' are applied on all images of the list, i.e. the "\ "default selection is '[0--1]' (except for command ''input'' whose default selection is '[-1]'')."\n\ "* Prepending a single hyphen '-' to a G'MIC command is allowed. This may be useful to recognize "\ "command items more easily in a one-liner pipeline (typically invoked from a shell). "\n\ "* A G'MIC command prepended with a plus sign '+' does not act __in-place__ but inserts its result as one or "\ "several new images at the end of the image list."\n\ "* There are two different types of commands that can be run by the G'MIC interpreter:"\n\ " - __Built-in commands__ are the hard-coded functionalities in the interpreter core. They are thus compiled as "\ "binary code and run fast, most of the time. Omitting an argument when invoking a built-in command is not permitted, "\ "except if all following arguments are also omitted. "\ "For instance, invoking 'plasma 10,,5' is invalid but 'plasma 10' is correct. "\n\ " - __Custom commands__, are defined as G'MIC pipelines of built-in or other custom commands. "\ "They are parsed by the G'MIC interpreter, and thus run a bit slower than built-in commands. "\ "Omitting arguments when invoking a custom command is permitted. For instance, expressions "\ "`flower ,,,100,,2` or `flower ,` are correct. "\n\ \n\ "* Most of the existing commands in G'MIC are actually defined as __custom commands__. "\n\ "* A user can easily add its own custom commands to the G'MIC interpreter (see section "\ " ''Adding Custom Commands'' for more details). New built-in commands cannot be added (unless you modify the "\ "G'MIC interpreter source code and recompile it)." _section "Input/Output Properties" _text \ "* \\G'MIC is able to read/write most of the classical image file formats, including:"\n\ " - 2D grayscale/color files: `.png`, `.jpeg`, `.gif`, `.pnm`, `.tif`, `.bmp`, ..."\n\ " - 3D volumetric files: `.dcm`, `.hdr`, `.nii`, `.cube`, `.pan`, `.inr`, `.pnk`, ..."\n\ " - Video files: `.mpeg`, `.avi`, `.mp4`, `.mov`, `.ogg`, `.flv`, ..."\n\ " - Generic text or binary data files: `.gmz`, `.cimg`, `.cimgz`, `flo`, `ggr`, `gpl`, `.dlm`, `.asc`, "\ "`.pfm`, `.raw`, `.txt`, `.h`."\n\ " - 3D mesh files: `.off`, `.obj`."\n\ \n\ "* When dealing with color images, G'MIC generally reads, writes and displays data using the usual "\ "sRGB color space."\n\ "* When loading a `.png` and `.tiff` file, the bit-depth of the input image(s) is returned to the status."\n\ "* G'MIC is able to manage __3D mesh objects__ that may be read from files or generated by G'MIC commands. "\ "A 3D object is stored as a one-column scalar image containing the object data, in the "\ "following order: { magic_number; sizes; vertices; primitives; colors; opacities }. "\ "These 3D representations can be then processed as regular images (see command ''split3d'' for accessing "\ "each of these 3D object data separately)."\n\ "* Be aware that usual file formats may be sometimes not adapted to store all the available image "\ "data, since G'MIC uses float-valued image buffers. For instance, saving an image that was "\ "initially loaded as a 16bits/channel image, as a `.jpg` file will result in a loss of "\ "information. Use the G'MIC-specific file extension `.gmz` to ensure that all data "\ "precision is preserved when saving images."\n\ "* Sometimes, file options may/must be set for file formats:"\n\ " - __Video files:__ Only sub-frames of an image sequence may be loaded, using the input expression "\ "'filename.ext,[first_frame[,last_frame[,step]]]'. Set 'last_frame==-1' to tell it must be "\ "the last frame of the video. Set 'step' to '0' to force an opened video file to be "\ "opened/closed. Output framerate and codec can be also set by using the output expression "\ "'filename.avi,_fps,_codec,_keep_open' where 'keep_open' can be { 0 | 1 }. 'codec' is a 4-char string "\ "(see ) or '0' for the default codec. 'keep_open' "\ "tells if the output video file must be kept open for appending new frames afterwards."\n\ " - `.cimg[z]` __files:__ Only crops and sub-images of .cimg files can be loaded, using the input "\ "expressions 'filename.cimg,N0,N1', 'filename.cimg,N0,N1,x0,x1', "\ "'filename.cimg,N0,N1,x0,y0,x1,y1', 'filename.cimg,N0,N1,x0,y0,z0,x1,y1,z1' or "\ "'filename.cimg,N0,N1,x0,y0,z0,c0,x1,y1,z1,c1'. "\ "Specifying '-1' for one coordinates stands for the maximum possible value. Output expression "\ "'filename.cimg[z][,datatype]' can be used to force the output pixel type. 'datatype' can be "\ "{ auto | bool | uint8 | int8 | uint16 | int16 | uint32 | int32 | uint64 | int64 | float32 | float64 }. "\n\ " - `.raw` __binary files:__ Image dimensions and input pixel type may be specified when loading `.raw` "\ "files with input expression 'filename.raw[,datatype][,width][,height[,depth[,dim[,offset]]]]]'. If no dimensions are "\ "specified, the resulting image is a one-column vector with maximum possible height. Pixel "\ "type can also be specified with the output expression 'filename.raw[,datatype]'. "\ "'datatype' can be the same as for `.cimg[z]` files. "\n\ " - `.yuv` __files:__ Image dimensions must be specified when loading, and only sub-frames of an image "\ "sequence may be loaded, using the input expression "\ "'filename.yuv,width,height[,chroma_subsampling[,first_frame[,last_frame[,step]]]'. "\ "'chroma_subsampling' can be { 420 | 422 | 444 }. "\ "When saving, chroma subsampling mode can be specified with output expression "\ "'filename.yuv[,chroma_subsampling]'. "\n\ " - `.tiff` __files:__ Only sub-images of multi-pages tiff files can be loaded, using the input "\ "expression 'filename.tif,_first_frame,_last_frame,_step'. "\ "Output expression 'filename.tiff,_datatype,_compression,_force_multipage,_use_bigtiff' can be used "\ "to specify the output pixel type, as well as the compression method. "\ "'datatype' can be the same as for `.cimg[z]` files. 'compression' can be "\ " { none (default) | lzw | jpeg }. 'force_multipage' can be { 0:no (default) | 1:yes }. "\ "'use_bigtiff' can be { 0:no | 1:yes (default) }."\n\ " - `.pdf` __files:__ When loading a file, the rendering resolution can be specified using the input expression "\ "'filename.pdf,resolution', where 'resolution' is an unsigned integer value."\n\ " - `.gif` __files:__ Animated gif files can be saved, using the input expression "\ "'filename.gif,fps>0,nb_loops'. Specify 'nb_loops=0' to get an infinite number of animation "\ "loops (this is the default behavior)."\n\ " - `.jpeg` __files:__ The output quality may be specified (in %), using the output expression "\ "'filename.jpg,30' (here, to get a 30% quality output). '100' is the default."\n\ " - `.mnc` __files:__ The output header can set from another file, using the output expression "\ "'filename.mnc,header_template.mnc'. "\n\ " - `.pan`, `.cpp`, `.hpp`, `.c` and `.h` __files:__ The output datatype can be selected with output expression "\ "'filename[,datatype]'. 'datatype' can be the same as for `.cimg[z]` files."\n\ " - `.gmic` __files:__ These filenames are assumed to be G'MIC custom commands files. Loading such a "\ "file will add the commands it defines to the interpreter. Debug information can be "\ "enabled/disabled by the input expression 'filename.gmic[,add_debug_info]' where 'debug_info' can be "\ "{ 0:false | 1:true }. "\n\ " - Inserting 'ext:' on the beginning of a filename (e.g. 'jpg:filename') forces G'MIC to "\ "read/write the file as it would have been done if it had the specified extension `.ext`."\n\ \n\ "* Some input/output formats and options may not be supported, depending on the configuration "\ "flags that have been set during the build of the G'MIC software." _section "Substitution Rules" _text \ "* \\G'MIC items containing '$' or '{}' are substituted before being interpreted. Use these "\ "substituting expressions to access various data from the interpreter environment."\n\ "* '$name' and '${name}' are both substituted by the value of the specified named variable "\ "(set previously by the item 'name=value'). If this variable has not been already set, the "\ "expression is substituted by the highest positive index of the named image '[name]'. If no "\ "image has this name, the expression is substituted by the value of the OS environment variable "\ "with same name (it may be thus an empty string if it is not defined). "\n\ "* The following reserved variables are predefined by the G'MIC interpreter: "\n\ " - '$!': The current number of images in the list."\n\ " - '$>' and '$<': The increasing/decreasing index of the latest (currently running) "\ "'repeat...done' loop. '$>' goes from `0` (first loop iteration) to `nb_iterations - 1` (last iteration). "\ "'$<' does the opposite. "\n\ " - '$/': The current call stack. Stack items are separated by slashes '/'."\n\ " - '$|': The current value (expressed in seconds) of a millisecond precision timer."\n\ " - '$^': The current verbosity level."\n\ " - '$_cpus': The number of computation cores available on your machine."\n\ " - '$_flags': The list of enabled flags when G'MIC interpreter has been compiled."\n\ " - '$_host': A string telling about the host running the G'MIC interpreter (e.g. `cli` or `gimp`)."\n\ " - '$_os': A string describing the running operating system."\n\ " - '$_path_rc': The path to the G'MIC folder used to store configuration files (its value is OS-dependent)."\n\ " - '$_path_user': The path to the G'MIC user file `.gmic` or `user.gmic` (its value is OS-dependent)."\n\ " - '$_path_commands': A list of all imported command files (stored as an image list)."\n\ " - '$_pid': The current process identifier, as an integer."\n\ " - '$_pixeltype': The type of image pixels (default: 'float32')."\n\ " - '$_prerelease': For pre-releases, the date of the pre-release as `yymmdd`. "\ "For stable releases, this variable is set to `0`."\n\ " - '$_version': A 3-digits number telling about the current version of the G'MIC interpreter "\ " (e.g. '"$_version"'). "\n\ " - '$_vt100': Set to `1` if colored text output is allowed on the console. Otherwise, set to `0`."\n\ \n\ "* '$$name' and '$${name}' are both substituted by the G'MIC script code of the specified named "\ "`custom command`, or by an empty string if no custom command with specified name exists. "\n\ "* '${\"-pipeline\"}' is substituted by the __status value__ after the execution of the specified "\ "G'MIC pipeline (see command ''status''). Expression '${}' thus stands for the current status value."\n\ "* '{``string}' (starting with two backquotes) is substituted by a double-quoted version of the specified string."\n\ "* '{/string}' is substituted by the escaped version of the specified string."\n\ "* '{\\'string\\'[:delimiter]}' (between single quotes) is substituted by the sequence of character codes "\ "that composes the specified string, separated by specified delimiter. Possible delimiters are "\ "',' (default), ';', '/', '^' or ' '. For instance, item '{'foo'}' is substituted by '102,111,111' and "\ "'{'foo':;}' by '102;111;111'."\n\ "* '{image,feature[:delimiter]}' is substituted by a specific feature of the image '[image]'. "\ "'image' can be either an image number or an image name. It can be also eluded, in which case, "\ "the last image '[-1]' of the list is considered for the requested feature. "\ "Specified 'feature' can be one of:"\n\ " - 'b': The image basename (i.e. filename without the folder path nor extension)."\n\ " - 'f': The image folder name."\n\ " - 'n': The image name or filename (if the image has been read from a file)."\n\ " - 't': The text string from the image values regarded as character codes."\n\ " - 'x': The image extension (i.e the characters after the last `.` in the image name)."\n\ " - '^': The sequence of all image values, separated by commas `,`."\n\ " - '@subset': The sequence of image values corresponding to the specified subset, and separated by commas `,`. "\n\ " - Any other 'feature' is considered as a __mathematical expression__ associated to the image '[image]' and is "\ "substituted by the result of its evaluation (float value). For instance, expression '{0,w+h}' is substituted by "\ "the sum of the width and height of the first image (see section ''Mathematical Expressions'' for more details). "\ "If a mathematical expression starts with an underscore `_`, the resulting value is truncated to a readable format. "\ "For instance, item '{_pi}' is substituted by '3.14159' (while '{pi}' is substituted by '3.141592653589793')."\n\ " - A 'feature' delimited by backquotes is replaced by a string whose character codes correspond to the list of "\ "values resulting from the evaluation of the specified mathematical expression. For instance, item "\ "'{`[102,111,111]`}' is substituted by 'foo' and item '{`vector8(65)`}' by 'AAAAAAAA'."\n\ \n\ "* '{*}' is substituted by the visibility state of the instant display window '#0' "\ "(can be { 0:closed | 1:visible }."\n\ "* '{*[index],feature1,...,featureN[:delimiter]}' is substituted by a specific set of features of the instant display "\ "window '#0' (or '#index', if specified). Requested 'features' can be:"\n\ " - 'u': screen width (actually independent on the window size)."\n\ " - 'v': screen height (actually independent on the window size)."\n\ " - 'uv': screen width*screen height."\n\ " - 'd': window width (i.e. width of the window widget)."\n\ " - 'e': window height (i.e. height of the window widget)."\n\ " - 'de': window width*window height."\n\ " - 'w': display width (i.e. width of the display area managed by the window)."\n\ " - 'h': display height (i.e. height of the display area managed by the window)."\n\ " - 'wh': display width*display height."\n\ " - 'i': X-coordinate of the display window."\n\ " - 'j': Y-coordinate of the display window."\n\ " - 'f': current fullscreen state of the instant display."\n\ " - 'n': current normalization type of the instant display."\n\ " - 't': window title of the instant display."\n\ " - 'x': X-coordinate of the mouse position (or -1, if outside the display area)."\n\ " - 'y': Y-coordinate of the mouse position (or -1, if outside the display area)."\n\ " - 'b': state of the mouse buttons { 1:left-but. | 2:right-but. | 4:middle-but. }."\n\ " - 'o': state of the mouse wheel."\n\ " - 'k': decimal code of the pressed key if any, 0 otherwise."\n\ " - 'c': boolean (0 or 1) telling if the instant display has been closed recently."\n\ " - 'r': boolean telling if the instant display has been resized recently."\n\ " - 'm': boolean telling if the instant display has been moved recently."\n\ " - Any other 'feature' stands for a keycode name (in capital letters), and is substituted "\ "by a boolean describing the current key state { 0:pressed | 1:released }."\n\ " - You can also prepend a hyphen '-' to a 'feature' (that supports it) to flush the "\ "corresponding event immediately after reading its state (works for keys, mouse and window events)."\n\ \n\ "* Item substitution is __never__ performed in items between double quotes. One must break the quotes "\ "to enable substitution if needed, as in '\"3+8 kg = \"{3+8}\" kg\"'. Using double quotes is then "\ "a convenient way to disable the substitutions mechanism in items, when necessary."\n\ "* One can also disable the substitution mechanism on items outside double quotes, by escaping the "\ "`{`, `}` or `$` characters, as in `\\{3+4\\}\\ doesn\47t\\ evaluate`." _section "Mathematical Expressions" _text \ "* \\G'MIC has an embedded __mathematical parser__, used to evaluate (possibly complex) math expressions "\ "specified inside braces '{}', or formulas in commands that may take one as an argument (e.g. ''fill'' or ''eval'')."\n\ "* When the context allows it, a formula is evaluated __for each pixel__ of the selected images "\ "(e.g. ''fill'' or ''eval'')."\n\ "* A math expression may return or take as an argument a __scalar__ or a __vector-valued__ result "\ "(with a fixed number of components)."\n\ "The mathematical parser understands the following set of functions, operators and variables:"\n\ "## Usual math operators:"\n\ "'||' (logical or), '&&' (logical and), '|' (bitwise or), '&' (bitwise and), "\ "'!=', '==', '<=', '>=', '<', '>', '<<' (left bitwise shift), '>>' (right bitwise shift), '-', '+', '*', '/', "\ "'%' (modulo), '^' (power), '!' (logical not), '~' (bitwise not), '++', '--', '+=', '-=', '*=', '/=', '%=', "\ "'&=', '|=', '^=', '>>', '<<=' (in-place operators)."\n\ "## Usual math functions:"\n\ "'abs()', 'acos()', 'acosh()', 'arg()', 'arg0()', 'argkth()', 'argmax()', 'argmaxabs()', "\ "'argmin()', 'argminabs()', 'asin()', 'asinh()', 'atan()', 'atan2()', 'atanh()', 'avg()', 'bool()', 'cbrt()', "\ "'ceil()', 'cos()', 'cosh()', 'cut()', 'deg2rad()', 'erf()', 'erfinv()', 'exp()', 'fact()', 'fibo()', 'floor()', "\ "'gamma()', 'gauss()', 'gcd()', 'hypot()', 'int()', 'isconst()', 'isnan()', 'isnum()', 'isinf()', 'isint()', "\ "'isbool()', 'isexpr()', 'isfile()', 'isdir()', 'isin()', 'kth()', 'lcm()', 'log()', 'log2()', 'log10()', 'max()', "\ "'maxabs()', 'med()', 'min()', 'minabs()', 'narg()', 'prod()', 'rad2deg()', 'rol()' (left bit rotation), 'ror()' "\ "(right bit rotation), 'round()', 'sign()', 'sin()', 'sinc()', 'sinh()', 'sqrt()', 'std()', 'srand(_seed)', 'sum()', "\ "'tan()', 'tanh()', 'var()', 'xor()'."\n\ \n\ "* 'cov(A,B,_avgA,_avgB)' estimates the covariance between vectors 'A' and 'B' (estimated averages of these vectors "\ "may be specified as arguments)."\n\ "* 'mse(A,B)' returns the mean-squared error between vectors 'A' and 'B'."\n\ "* 'atan2(y,x)' is the version of 'atan()' with two arguments 'y' and 'x' (as in C/C++)."\n\ "* 'perm(k,n,_with_order)' computes the number of permutations of 'k' objects from a set of 'n' objects."\n\ "* 'gauss(x,_sigma,_is_normalized)' returns `exp(-x^2/(2*s^2))/(is_normalized?sqrt(2*pi*sigma^2):1)`."\n\ "* 'cut(value,min,max)' returns 'value' if it is in range '[min,max]', or 'min' or 'max' otherwise."\n\ "* 'narg(a_1,...,a_N)' returns the number of specified arguments (here, 'N')."\n\ "* 'arg(i,a_1,..,a_N)' returns the `i`-th argument 'a_i'."\n\ "* 'isnum()', 'isnan()', 'isinf()', 'isint()', 'isbool()' test the type of the given number or expression, "\ "and return '0' (false) or '1' (true)."\n\ "* 'isfile(\\'path\\')' (resp. 'isdir('path')') returns '0' (false) or '1' (true) whether its string argument is a "\ "path to an existing file (resp. to a directory) or not."\n\ "* 'ispercentage(arg)' returns `1` (true) or `0` (false) whether 'arg' ends with a '%' or not."\n\ "* 'isvarname(\\'str\\')' returns '0' (false) or '1' (true) whether its string argument would be a valid to name "\ "a variable or not."\n\ "* 'isin(v,a_1,...,a_n)' returns '0' (false) or '1' (true) whether the first argument 'v' appears in the set of other "\ "argument 'a_i'."\n\ "* 'isint(x,_xmin,_xmax)' returns '1' (true), if 'x' is an integer in range '[xmin,xmax]', otherwise '0' (false)."\n\ "* 'inrange(value,m,M,include_m,include_M)' returns '0' (false) or '1' (true) whether the specified value lies in "\ "range '[m,M]' or not ('include_m' and 'includeM' tells how boundaries 'm' and 'M' are considered)."\n\ "* 'argkth()', 'argmin()', 'argmax()', 'argminabs()', 'argmaxabs()'', 'avg()', 'kth()', 'min()', 'max()', 'minabs()', "\ "'maxabs()', 'med()', 'prod()', 'std()', 'sum()' and 'var()' can be called with an arbitrary number of scalar/vector "\ "arguments."\n\ "* 'vargkth()', 'vargmin()', 'vargmax()', 'vargminabs()', 'vargmaxabs()', 'vavg()', 'vkth()', 'vmin()', "\ "'vmax()', 'vminabs()', 'vmaxabs()', 'vmed()', 'vprod()', 'vstd()', 'vsum()' and 'vvar()' are the versions of the "\ "previous function with vector-valued arguments."\n\ "* 'round(value,rounding_value,direction)' returns a rounded value. 'direction' can be "\ "{ -1:to-lowest | 0:to-nearest | 1:to-highest }."\n\ "* 'lerp(a,b,t)' returns 'a*(1-t)+b*t'."\n\ "* 'swap(a,b)' swaps the values of the given arguments."\n\ "## Predefined variable names:"\n\ "Variable names below are pre-defined. They can be overridden though."\n\ "* 'l': length of the associated list of images."\n\ "* 'k': index of the associated image, in '[0,l-1]'."\n\ "* 'w': width of the associated image, if any ('0' otherwise)."\n\ "* 'h': height of the associated image, if any ('0' otherwise)."\n\ "* 'd': depth of the associated image, if any ('0' otherwise)."\n\ "* 's': spectrum of the associated image, if any ('0' otherwise)."\n\ "* 'r': shared state of the associated image, if any ('0' otherwise)."\n\ "* 'wh': shortcut for 'width*height'."\n\ "* 'whd': shortcut for 'width*height*depth'."\n\ "* 'whds': shortcut for 'width*height*depth*spectrum' (i.e. number of image values)."\n\ "* 'im', 'iM', 'ia', 'iv', 'id', 'is', 'ip', 'ic', 'in': Respectively the minimum, maximum, average, variance, "\ "standard deviation, sum, product, median value and L2-norm of the associated image, if any ('0' otherwise)."\n\ "* 'xm', 'ym', 'zm', 'cm': The pixel coordinates of the minimum value in the associated image, "\ "if any ('0' otherwise)."\n\ "* 'xM', 'yM', 'zM', 'cM': The pixel coordinates of the maximum value in the associated image, "\ "if any ('0' otherwise)."\n\ "* All these variables are considered as __constant values__ by the math parser (for optimization purposes) "\ "which is indeed the case most of the time. Anyway, this might not be the case, if function 'resize(#ind,..)' "\ "is used in the math expression. If so, it is safer to invoke functions 'l()', 'w(_#ind)', 'h(_#ind)', ... 's(_#ind)' "\ "and 'in(_#ind)' instead of the corresponding named variables."\n\ "* 'i': current processed pixel value (i.e. value located at `(x,y,z,c)`) in the associated image, "\ "if any ('0' otherwise)."\n\ "* 'iN': N-th channel value of current processed pixel (i.e. value located at `(x,y,z,N)` in the associated image, "\ "if any ('0' otherwise). 'N' must be an integer in range '[0,9]'."\n\ "* 'R', 'G', 'B' and 'A' are equivalent to 'i0', 'i1', 'i2' and 'i3' respectively."\n\ "* 'I': current vector-valued processed pixel in the associated image, if any ('0' otherwise). "\ "The number of vector components is equal to the number of image channels (e.g. 'I' = `[ R,G,B ]` for a "\ "`RGB` image)."\n\ "* You may add '#ind' to any of the variable name above to retrieve the information for any "\ "numbered image '[ind]' of the list (when this makes sense). For instance 'ia#0' denotes the average value of the "\ "first image of the list)."\n\ "* 'x': current processed column of the associated image, if any ('0' otherwise)."\n\ "* 'y': current processed row of the associated image, if any ('0' otherwise)."\n\ "* 'z': current processed slice of the associated image, if any ('0' otherwise)."\n\ "* 'c': current processed channel of the associated image, if any ('0' otherwise)."\n\ "* 't': thread id when an expression is evaluated with multiple threads ('0' means __master thread__)."\n\ "* 'n': maximum number of threads when expression is evaluated in parallel (so that 't' goes from '0' to 'n-1')."\n\ "* 'e': value of e, i.e. `2.71828...`."\n\ "* 'pi': value of pi, i.e. `3.1415926...`."\n\ "* 'eps': value of machine epsilon, that is the difference between 1.0 and the next value representable by a double."\n\ "* 'u': a random value between '[0,1]', following a uniform distribution."\n\ "* 'g': a random value, following a gaussian distribution of variance 1 (roughly in '[-6,6]')."\n\ "* 'interpolation': value of the default interpolation mode used when reading pixel values with the pixel access "\ "operators (i.e. when the interpolation argument is not explicitly specified, see below for more details on pixel "\ "access operators). Its initial default value is '0'."\n\ "* 'boundary': value of the default boundary conditions used when reading pixel values with the pixel access "\ "operators (i.e. when the boundary condition argument is not explicitly specified, see below for more details "\ "on pixel access operators). Its initial default value is '0'."\n\ "* The last image of the list is always associated to the evaluations of 'expressions', e.g. G'MIC sequence "\ "\n~~~\n256,128 fill {w}\n~~~\n will create a 256x128 image filled with value 256."\n\ "## Vector-valued functions and operators:"\n\ "The math evaluator is able to work with vector-valued elements. A math function applied on a vector-valued "\ "argument usually returns a vector with same dimension, where each element of the input vector "\ "has been passed to the specified function (e.g. 'abs([-1,2,-3])' returns '[1,2,3]')."\n\n\ "There are specific functions and operators to define or compute vector-valued elements though :"\n\ "* '[a0,a1,...,aN-1]' defines a 'N'-dimensional vector with scalar coefficients 'ak'."\n\ "* 'vectorN(a0,a1,,...,aN-1)' does the same, with the 'ak' being repeated periodically if only a few are specified."\n\ "* 'vector(#N,a0,a1,,...,aN-1)' does the same, and can be used for any constant expression 'N'."\n\ "* In previous expressions, the 'ak' can be vectors themselves, to be concatenated into a single vector."\n\ "* The scalar element 'ak' of a vector 'X' is retrieved by 'X[k]'."\n\ "* The sub-vector '[X[p],X[p+s]...X[p+s*(q-1)]]' (of size 'q') of a vector 'X' is retrieved by 'X[p,q,s]'."\n\ "* Equality/inequality comparisons between two vectors is done with operators '==' and '!='."\n\ "* Some vector-specific functions can be used on vector values: "\ "'cross(X,Y)' (cross product), 'dot(X,Y)' (dot product), 'size(X)' (vector dimension), "\ "'sort(X,_is_increasing,_nb_elts,_size_elt)' (sorted values), 'reverse(A)' (reverse order of components), "\ "'map(X,P,_nb_channelsX,_nb_channelsP,_boundary_conditions)', "\ "'shift(A,_length,_boundary_conditions)' and 'same(A,B,_nb_vals,_is_case_sensitive)' (vector equality test)."\n\ "* Function 'normP(u1,...,un)' computes the LP-norm of the specified vector "\ "('P' being a constant or 'inf', as in e.g. 'norm1()')."\n\ "* Function 'normp(V,_p)' computes the Lp-norm of the specified vector 'V'. Here, 'p' can be variable. "\ "Default value for 'p' is 2."\n\ "* Function 'unitnorm(V,_p)' returns a normalized version 'V/normp(V)' of specified vector 'V'. "\ "Default value for 'p' is 2."\n\ "* Function 'resize(A,size,_interpolation,_boundary_conditions)' returns a resized version of a vector 'A' with "\ "specified interpolation mode. 'interpolation' can be "\ "{ -1:none (memory content) | 0:none | 1:nearest | 2:average | 3:linear | 4:grid | 5:bicubic | 6:lanczos }, and "\ "'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }."\n\ "* Function 'find(A,B,_starting_index,_search_step)' returns the index where sub-vector 'B' appears in vector 'A', "\ "(or '-1' if 'B' is not contained in 'A'). Argument 'A' can be also replaced by an image index '#ind'."\n\ "* Specifying a vector-valued math expression as an argument of a command that operates on image values "\ "(e.g. 'fill') modifies the whole spectrum range of the processed image(s), for each spatial coordinates `(x,y,z)`. "\ "The command does not loop over the `c`-axis in this case."\n\ "## Complex-valued functions:"\n\ "A `2`-dimensional vector may be seen as a complex number and used in those particular functions/operators: "\ "'**' (complex multiplication), '//' (complex division), '^^' (complex exponentiation), "\ "'**=' (complex self-multiplication), '//=' (complex self-division), '^^=' (complex self-exponentiation), "\ "'cabs()' (complex modulus), 'carg()' (complex argument), 'cconj()' (complex conjugate), "\ "'cexp()' (complex exponential), 'clog()' (complex logarithm), 'ccos()' (complex cosine), "\ "'csin()' (complex sine), 'csqr()' (complex square), 'csqrt()' (complex square root), 'ctan()' (complex tangent), "\ "'ccosh()' (complex hyperpolic cosine), 'csinh()' (complex hyperbolic sine) and "\ "'ctanh()' (complex hyperbolic tangent)."\n\ "## Matrix-valued functions:"\n\ "A `MN`-dimensional vector may be seen as a `M` x `N` matrix and used in those particular functions/operators: "\ "'*' (matrix-vector multiplication), 'det(A)' (determinant), 'diag(V)' (diagonal matrix from a vector), "\ "'eig(A)' (eigenvalues/eigenvectors), 'eye(n)' (n x n identity matrix), 'invert(A,_nb_colsA,_use_LU,_lambda)' "\ "(matrix inverse), 'mul(A,B,_nb_colsB)' (matrix-matrix multiplication), "\ "'rot(u,v,w,angle)' (3D rotation matrix), 'rot(angle)' (2D rotation matrix), "\ "'solve(A,B,_nb_colsB,_use_LU)' (solver of linear system A.X = B), 'svd(A,_nb_colsA)' (singular value decomposition), "\ "'trace(A)' (matrix trace) and 'transpose(A,nb_colsA)' (matrix transpose). Argument 'nb_colsB' may be omitted if "\ "it is equal to `1`".\n\ "## Image-valued functions:"\n\ "Some functions takes vector-valued arguments that represent image data :"\n\ "* Function 'expr(formula,_w,_h,_d,_s)' outputs a vector of size 'w*h*d*s' with values generated from "\ "the specified formula, as if one were filling an image with dimensions '(w,h,d,s)'."\n\ "* Function 'resize(A,wA,hA,dA,sA,nwA,_nhA,_ndA,_nsA,_interpolation,_boundary_conditions,_ax,_ay,_az,_ac)' is an "\ "extended version of the 'resize()' function. It allows to resize the vector 'A', seen as an image of size "\ "'(ow,oh,od,os)' as a new image of size '(nw,nh,nd,ns)', with specified resizing options."\n\ "* Function 'warp(A,wA,hA,dA,sA,B,wB,hB,dB,sB,_mode,_interpolation,_boundary_conditions)' returns the warped version "\ "of the image 'A' (of size '(wA,hA,dA,sA)', viewed as a vector of size 'wA*hA*dA*sA') by the warping field 'B' "\ "(of size '(wB,hB,dB,sB)'). The resulting image has size '(wB,hB,dB,sA)'. "\ "This is the math evaluator analog to command ''warp''."\n\ "* Function 'index(A,P,nb_channelsP,_dithering,_map_colors)' returns the indexed version "\ "of the image 'A' by the colormap 'P'. "\ "This is the math evaluator analog to command ''index''."\n\ "* Function 'permute(A,wA,hA,dA,sA,permutation_string)' returns a permuted version of the image 'A' "\ "(of size '(wA,hA,dA,sA)', viewed as a vector of size 'wA*hA*dA*sA'). "\ "This is the math evaluator analog to command ''permute''."\n\ "* Function 'mirror(A,wA,hA,dA,sA,axes_string)' returns a mirrored version of the image 'A' "\ "(of size '(wA,hA,dA,sA)', viewed as a vector of size 'wA*hA*dA*sA'). "\ "This is the math evaluator analog to command ''mirror''."\n\ "* Function 'cumulate(A,wA,hA,dA,sA,_axes_string)' returns a cumulated version of the image 'A' "\ "(of size '(wA,hA,dA,sA)', viewed as a vector of size 'wA*hA*dA*sA'). "\ "This is the math evaluator analog to command ''cumulate''."\n\ "* Function 'histogram(A,nb_levels,_min_value,_max_value)' returns the histogram of the vector 'A'. "\ "This is the math evaluator analog to command ''histogram''."\n\ "* Function 'equalize(A,nb_levels,_min_value,_max_value)' returns the equalized version of the vector 'A'. "\ "This is the math evaluator analog to command ''equalize''."\n\ "* Function 'normalize(A,_min_value,_max_value)' returns the normalized version of the vector 'A'. "\ "This is the math evaluator analog to command ''normalize''."\n\ "* 'mproj(S,nb_colsS,D,nb_colsD,method,max_iter,max_residual)' projects a matrix 'S' onto a dictionary (matrix) 'D'. "\ "This is the math evaluator analog to command ''mproj''."\n\ "* Function 'noise(A,amplitude,_noise_type)' returns the noisy version of the vector 'A'. "\ "This is the math evaluator analog to command ''noise''."\n\ "* Function 'rand(#size,_min_value,_max_value,_pdf,_precision)' returns the a vector of 'size' random values. "\ "This is the math evaluator analog to command ''rand''."\n\ "## String manipulation:"\n\ "Character strings are defined as vectors objects and can be then managed as is. "\ "Dedicated functions and initializers to manage strings exist:"\n\ "* `['string']` and `'string'` define a vector whose values are the character codes of the "\ "specified `character string` (e.g. `'foo'` is equal to `[ 102,111,111 ]`)."\n\ "* `_'character'` returns the (scalar) byte code of the specified character (e.g. `_'A'` is equal to '65')."\n\ "* A special case happens for __empty__ strings: Values of both expressions `['']` and `''` are '0'."\n\ "* Functions 'lowercase()' and 'uppercase()' return string with all string characters lowercased or uppercased."\n\ "* Function 's2v(str,_starting_index,_is_strict)' parses specified string 'str' and returns the value contained "\ "in it."\n\ "* Function 'v2s(expr,_nb_digits,_siz)' returns a vector of size 'siz' which contains the character representation "\ "of values described by expression 'expr'. "\ "'nb_digits' can be { <-1:0-padding of integers | -1:auto-reduced | 0:all | >0:max number of digits }."\n\ "* Function 'echo(str1,str2,...,strN)' prints the concatenation of given string arguments on the console."\n\ "* Function 'string(_#siz,str1,str2,...,strN)' generates a vector corresponding to the concatenation of given "\ "string/number arguments."\n\ "## Dynamic arrays:"\n\ "A dynamic array is defined as a one-column (or empty) image '[ind]' in the image list. "\ "It allows elements to be added or removed, each element having the same dimension "\ "(which is actually the number of channels of image '[ind]'). "\ "Dynamic arrays adapt their size to the number of elements they contain."\n\n\ "A dynamic array can be manipulated in a math expression, with the following functions:"\n\ "* 'da_size(_#ind)': Return the number of elements in dynamic array '[ind]'."\n\ "* 'da_back(_#ind)': Return the last element of the dynamic array '[ind]'."\n\ "* 'da_insert(_#ind,pos,elt_1,_elt_2,...,_elt_N)': Insert 'N' new elements 'elt_k' starting from index 'pos' "\ "in dynamic array '[ind]'."\n\ "* 'da_push(_#ind,elt1,_elt2,...,_eltN)': Insert 'N' new elements 'elt_k' at the end of dynamic array '[ind]'."\n\ "* 'da_pop(_#ind)': Same as 'da_back()' but also remove last element from the dynamic array '[ind]'."\n\ "* 'da_push_heap(_#ind,elt1,_elt2,...,_eltN)' and 'da_pop_heap(_#ind)' does the same but for a dynamic array viewed "\ "as a min-heap structure."\n\ "* 'da_remove(_#ind,_start,_end)': Remove elements located between indices 'start' and 'end' (included) "\ "in dynamic array '[ind]'."\n\ "* 'da_freeze(_#ind)': Convert a dynamic array into a 1-column image with height 'da_size(#ind)'."\n\ "* The value of the k-th element of dynamic array '[ind]' is retrieved with 'i[_#ind,k]' (if the element is a "\ "scalar value), or 'I[_#ind,k]' (if the element is a vector)."\n\n\ "In the functions above, argument '#ind' may be omitted in which case it is assumed to be '#-1'."\n\ "## Special operators:"\n\ "* ';': expression separator. The returned value is always the last encountered expression. "\ "For instance expression '1;2;pi' is evaluated as 'pi'."\n\ "* '=': variable assignment. Variables in mathematical parser can only refer to numerical "\ "values (vectors or scalars). Variable names are case-sensitive. Use this operator in conjunction with ';' to define "\ "more complex evaluable expressions, such as \n~~~\nt = cos(x); 3*t^2 + 2*t + 1\n~~~\n"\ "These variables remain __local__ to the mathematical parser and cannot be accessed outside the evaluated "\ "expression."\n\ "* Variables defined in math parser may have a __constant__ property, by specifying keyword 'const' before the "\ "variable name (e.g. 'const foo = pi/4;'). The value set to such a variable must be indeed a __constant scalar__. "\ "Constant variables allows certain types of optimizations in the math JIT compiler."\n\ \ "## Specific functions:"\n\ "* 'addr(expr)': return the pointer address to the specified expression 'expr'. "\n\ "* 'o2c(_#ind,offset)' and 'c2o(_#ind,x,_y,_z,_c)': Convert image offset to image coordinates and vice-versa. "\n\ "* 'fill(target,expr)' or 'fill(target,index_name,expr)' fill the content of the specified target "\ "(often vector-valued) using a given expression, e.g. `V = vector16(); fill(V,k,k^2 + k + 1);`. "\ "For a vector-valued target, it is basically equivalent to: "\ "`for (index_name = 0, index_name=0' (e.g., '46368' for 'N=24'). 'do(expression,condition)' always evaluates the specified "\ "expression at least once, then check for the loop condition. When done, it returns the last value of 'expression'."\n\ "* 'for(init,condition,_procedure,body)' first evaluates the expression 'init', then iteratively evaluates 'body' "\ "(followed by 'procedure' if specified) while 'condition' holds (i.e. not zero). It may happen that no iterations are "\ "done, in which case the function returns 'nan'. Otherwise, it returns the last value of 'body'. "\ "For instance, the expression: \n~~~\nif(N<2,N,for(n=N;F0=0;F1=1,n=n-1,F2=F0+F1;F0=F1;F1=F2))\n~~~\n "\ "returns the 'N'-th value of the Fibonacci sequence, for 'N>=0' (e.g., '46368' for 'N=24')."\n\ "* 'while(condition,expression)' is exactly the same as 'for(init,condition,expression)' without the specification of "\ "an initializing expression."\n\ "* 'repeat(nb_iters,expr)' or 'fill(nb_iters,iter_name,expr)' run 'nb_iters' iterations of the specified expression "\ "'expr', e.g. `V = vector16(); repeat(16,k,V[k] = k^2 + k + 1);`. "\ "It is basically equivalent to: "\ "`for (iter_name = 0, iter_namebegin(foo = 0); ++foo\"')."\n\ "* 'copy(dest,src,_nb_elts,_inc_d,_inc_s,_opacity)' copies an entire memory block of 'nb_elts' elements starting "\ "from a source value 'src' to a specified destination 'dest', with increments defined by 'inc_d' and 'inc_s' "\ "respectively for the destination and source pointers."\n\ "* 'stats(_#ind)' returns the statistics vector of the running image '[ind]', i.e the vector "\ "`[ im,iM,ia,iv,xm,ym,zm,cm,xM,yM,zM,cM,is,ip ]` (14 values)."\n\ "* 'ref(expr,a)' references specified expression 'expr' as variable name 'a'."\n\ "* 'unref(a,b,...)' destroys references to the named variable given as arguments."\n\ "* 'breakpoint()' inserts a possible computation breakpoint (useless with the cli interface)."\n\ "* '_(comment) expr' just returns expression 'expr' (useful for inserting inline comments in math expressions)."\n\ "* 'run('pipeline')' executes the specified G'MIC pipeline as if it was called outside the currently evaluated "\ "expression."\n\ "* 'set(\\'variable_name\\',A)' set the G'MIC variable '$variable_name' with the value of expression 'A'. If 'A' is"\ " a vector-valued variable, it is assumed to encode a string."\n\ "* 'store(\\'variable_name\\',A,_w,_h,_d,_s,_is_compressed)' transfers the data of vector 'A' as a "\ "`(w,h,d,s)` image to the G'MIC variable '$variable_name'. Thus, the data becomes available outside the math "\ "expression (that is equivalent to using the regular command ''store'', but directly in the math expression)."\n\ "* 'get(\\'variable_name\\',_size,_return_as_string)' returns the value of the specified variable, as a vector of "\ "'size' values, or as a scalar (if 'size' is zero or not specified)."\n\ "* 'name(_#ind,size)' returns a vector of size 'size', whose values are the characters codes of the name of image "\ "'[ind]' (or default image selected if 'ind' is not specified)."\n\ "* 'correlate(I,wI,hI,dI,sI,K,wK,hK,dK,sK,_boundary_conditions,_is_normalized,_channel_mode,_xcenter,_ycenter,"\ "_zcenter,_xstart,_ystart,_zstart,_xend,_yend,_zend,_xstride,_ystride,_zstride,_xdilation,_ydilation,_zdilation,"\ "_interpolation_type)' returns the correlation, unrolled as a vector, of the `(wI,hI,dI,sI)`-sized image 'I' "\ "with the `(wK,hK,dK,sK)`-sized kernel 'K' (the meaning of the other arguments are the same as in command "\ "'correlate'). Similar function 'convolve(...)' is also defined for computing the convolution between 'I' and 'K'."\n\ \ "## User-defined macros:"\n\ "* Custom macro functions can be defined in a math expression, using the assignment operator "\ "'=', e.g. \n~~~\nfoo(x,y) = cos(x + y); result = foo(1,2) + foo(2,3)\n~~~\n"\n\ "* Trying to override a built-in function (e.g. 'abs()') has no effect."\n\ "* Overloading macros with different number of arguments is possible. Re-defining a previously defined macro with "\ "the same number of arguments discards its previous definition."\n\ "* Macro functions are indeed processed as __macros__ by the mathematical evaluator. You should avoid invoking them "\ "with arguments that are themselves results of assignments or self-operations. "\ "For instance, \n~~~\nfoo(x) = x + x; z = 0; foo(++z)\n~~~\n returns '4' rather than expected value '2'."\n\ "* When substituted, macro arguments are placed inside parentheses, except if a number sign "\ "'#' is located just before or after the argument name. For instance, expression \n"\ "~~~\nfoo(x,y) = x*y; foo(1+2,3)\n~~~\n "\ "returns '9' (being substituted as '(1+2)*(3)'), while expression \n~~~\nfoo(x,y) = x#*y#; foo(1+2,3)\n~~~\n "\ "returns '7' (being substituted as '1+2*3')."\n\ "* Number signs appearing between macro arguments function actually count for __empty__ separators. They may be used "\ "to force the substitution of macro arguments in unusual places, e.g. as in \n~~~\nstr(N) = ['I like N#'];\n~~~\n"\ "* Macros with variadic arguments can be defined, by specifying a single argument name followed by `...`. "\ "For instance,\n~~~\nfoo(args...) = sum([ args ]^2);\n~~~\n "\ "defines a macro that returns the sum of its squared arguments, so `foo(1,2,3)` returns `14` and "\ "`foo(4,5)` returns `41`.\n"\ \ "## Multi-threaded and in-place evaluation:"\n\ "* If your image data are large enough and you have several CPUs available, it is likely that the math expression "\ "passed to a 'fill', 'eval' or 'input' commands is evaluated in parallel, using multiple computation threads."\n\ "* Starting an expression with ':' or '*' forces the evaluations required for an image to be run in parallel, "\ "even if the amount of data to process is small (beware, it may be slower to evaluate in this case!). "\ "Specify ':' (rather than '*') to avoid possible image copy done before evaluating the expression "\ "(this saves memory, but do this only if you are sure this step is not required!)"\n\ "* Expression starting with '+' are evaluated in a single-threaded way, with possible image copy."\n\ "* If the specified expression starts with '>' or '<', the pixel access operators 'i()', 'i[]', 'j()' and 'j[]' "\ "return values of the image being currently modified, in forward ('>') or backward ('<') order. "\ "The multi-threading evaluation of the expression is disabled in this case."\n\ "* Function 'critical(expr)' forces the execution of the given expression in a single thread at a time."\n\ "* 'begin_t(expr)' and 'end_t(expr)' evaluates the specified expression once for each running thread "\ "(so possibly several times) at the beginning and the end of the evaluation procedure."\n\ "* 'merge(variable,operator)' tells to merge the local variable value computed by threads, with the specified "\ "operator, when all threads have finished computing."\n\ "* Expressions 'i(_#ind,x,_y,_z,_c)=value', 'j(_#ind,x,_y,_z,_c)=value', 'i[_#ind,offset]=value' and "\ "'j[_#ind,offset]=value' set a pixel value at a different location than the running one in the image '[ind]' "\ "(or in the associated image if argument '#ind' is omitted), either with global coordinates/offsets "\ "(with 'i(...)' and 'i[...]'), or relatively to the current position `(x,y,z,c)` (with 'j(...)' and 'j[...]'). "\ "These expressions always return 'value'." _section "Adding Custom Commands" _text \ "* New custom commands can be added by the user, through the use of \\G'MIC __custom commands files__."\n\ "* A command file is a simple text file, where each line starts either by "\ "\n~~~\ncommand_name: command_definition\n~~~\n or \n~~~\ncommand_definition (continuation)\n~~~\n"\n\ "* At startup, G'MIC automatically includes user's command file '$HOME/.gmic' (on __Unix__) or "\ "'%USERPROFILE%\\user.gmic' (on __Windows__). The CLI tool 'gmic' automatically runs the command "\ "'cli_start' if defined."\n\ "* Custom command names must use character set `[a-zA-Z0-9_]` and cannot start with a number."\n\ "* Any `# comment` expression found in a custom commands file is discarded by the G'MIC parser, "\ "wherever it is located in a line."\n\ "* In a custom command, the following '$-expressions' are recognized and substituted:"\n\ " - '$""\*' is substituted by a verbatim copy of the specified string of arguments "\ "(do not include arguments set to default values)."\n\ " - '$\"*\"' is substituted by the sequence of specified arguments, separated by commas ',', "\ "each being double-quoted (include arguments set to default values)."\n\ " - '$""#' is substituted by the maximum index of known arguments (either specified by the user or set to a default "\ "value in the custom command)."\n\ " - '$""[]' is substituted by the list of selected image indices that have been specified in the command "\ "invocation."\n\ " - '$""?' is substituted by a printable version of '$""[]' to be used in command descriptions."\n\ " - '$i' and '${i}' are both substituted by the `i`-th specified argument. Negative indices such as '${-j}' are "\ "allowed and refer to the `j`-th latest argument. '$""0' is substituted by the custom command name."\n\ " - '${i=default}' is substituted by the value of '$i' (if defined) or by its new value set to 'default' otherwise "\ "('default' may be a `$-expression` as well)."\n\ " - '${subset}' is substituted by the argument values (separated by commas ',') of a specified argument subset. "\ "For instance expression '$""{2--2}' is substituted by all specified command arguments except the first and the last "\ "one. Expression '$""{^0}' is then substituted by all arguments of the invoked command (eq. to '$""*' if all "\ "arguments have been indeed specified)."\n\ " - '$""=var' is substituted by the set of instructions that will assign each argument '$i' to the named variable "\ "'var$i' (for i in '[0...$""#]'. This is particularly useful when a custom command want to manage variable numbers "\ "of arguments. Variables names must use character set `[a-zA-Z0-9_]` and cannot start with a number."\n\ \n\ "* These particular `$-expressions` for custom commands are __always substituted__, even in "\ "double-quoted items or when the dollar sign '$' is escaped with a backslash '\\$'. To avoid substitution, place an "\ "empty double quoted string just after the '$' (as in '$\"\"1')."\n\ "* Specifying arguments may be skipped when invoking a custom command, by replacing them by commas ',' as in "\ "expression \n~~~\nflower ,,3\n~~~\n Omitted arguments are set to their default values, which must be thus explicitly "\ "defined in the code of the corresponding custom command (using default argument expressions as '$""{1=default}')."\n\ "* If one numbered argument required by a custom command misses a value, an error is thrown by the G'MIC "\ "interpreter."\n\ "* It is possible to specialize the invocation of a '+command' by defining it as "\ "\n~~~\n+command_name: command_definition\n~~~\n"\ "* A +-specialization takes priority over the regular command definition when the command is invoked with a "\ "prepended '+'."\n\ "* When only a +-specialization of a command is defined, invoking 'command' is actually equivalent to '+command'." _section "List of Commands" _text \ "All available \\G'MIC commands are listed below, by categories. An argument specified between '[]' "\ "or starting by '_' is optional except when standing for an existing image '[image]', where 'image' "\ "can be either an index number or an image name. In this case, the '[]' characters are mandatory when writing the "\ "item. Note that all images that serve as illustrations in this reference documentation are normalized in "\ "range `[0,255]` before being displayed. You may need to do this explicitly (command `normalize 0,255`) if you want "\ "to save and view images with the same aspect than those illustrated in the example codes."\n\ "The examples accompanying this 'List of Commands' illustrate the use of the \\G'MIC language and are written as they "\ "would appear in a custom command. While some examples may work if entered directly at a shell prompt, there is no "\ "guarantee. No attempt has been made to escape special characters in these examples, which many shells reserve." # Insert list of commands. l { reference_list_of_commands_$1 onfail } # Insert additional sections if specified. xfolder="$2" if "['$1']=='html' && ['$2']==0" xfolder=$HOME/work/src/gmic-community/reference fi if ['$xfolder']!=0 files $xfolder/*.gmd files=${} repeat narg({/$files}) { arg0 $>,$files file=${} l[] { it $file s={b} t={t} rm } _section {/$s} _text {/$t} } fi _section "Examples of Use" _text \ "`gmic` is a generic image processing tool which can be used in a wide variety of situations. "\ "The few examples below illustrate possible uses of this tool:"\n\ "### View a list of images: "\n\ "\n~~~\n$ gmic file1.bmp file2.jpeg\n~~~"\n\n\ "### Convert an image file: "\n\ "\n~~~\n$ gmic input.bmp output output.jpg\n~~~"\n\n\ "### Create a volumetric image from a movie sequence: "\n\ "\n~~~\n$ gmic input.mpg append z output output.hdr\n~~~"\n\n\ "### Compute image gradient norm: "\n\ "\n~~~\n$ gmic input.bmp gradient_norm\n~~~"\n\n\ "### Denoise a color image: "\n\ "\n~~~\n$ gmic image.jpg denoise 30,10 output denoised.jpg\n~~~"\n\n\ "### Compose two images using overlay layer blending: "\n\ "\n~~~\n$ gmic image1.jpg image2.jpg blend overlay output blended.jpg\n~~~"\n\n\ "### Evaluate a mathematical expression: "\n\ "\n~~~\n$ gmic echo \"cos(pi/4)^2+sin(pi/4)^2={cos(pi/4)^2+sin(pi/4)^2}\"\n~~~"\n\n\ "### Plot a 2D function: "\n\ "\n~~~\n$ gmic 1000,1,1,2 fill \"X=3*(x-500)/500;X^2*sin(3*X^2)+(!c?u(0,-1):cos(X*10))\" plot\n~~~"\n\ "===\n![2D Plot](../img/example_plot.png)\n==="\n\n\ "### Plot a 3D elevated function in random colors: "\n\ "\n~~~\n$ gmic 128,128,1,3,\"u(0,255)\" plasma 10,3 blur 4 sharpen 10000 n 0,255 "\ "elevation3d[-1] \"'X=(x-64)/6;Y=(y-64)/6;100*exp(-(X^2+Y^2)/30)*abs(cos(X)*sin(Y))'\"\n~~~"\n\n\ "===\n![3D Elevation](../img/example_elevation3d.png)\n==="\n\n\ "### Plot the isosurface of a 3D volume: "\n\ "\n~~~\n$ gmic mode3d 5 moded3d 5 double3d 0 isosurface3d \"'x^2+y^2+abs(z)^abs(4*cos(x*y*z*3))'\",3\n~~~"\n\ "===\n![3D Isosurface](../img/example_isosurface3d.png)\n==="\n\n\ "### Render a G'MIC 3D logo: "\n\ "\n~~~\n$ gmic 0 text G\\\47MIC,0,0,53,1,1,1,1 expand xy,10 blur 1 normalize 0,100 +plasma 0.4 add blur 1 "\ "elevation3d -0.1 moded3d 4\n~~~"\n\ "===\n![3D G'MIC Logo](../img/example_logo.png)\n==="\n\n\ "### Generate a 3D ring of torii: "\n\ "\n~~~\n$ gmic repeat 20 torus3d 15,2 color3d[-1] \"{u(60,255)},{u(60,255)},{u(60,255)}\" *3d[-1] 0.5,1 if \"{$>%2}\" "\ "rotate3d[-1] 0,1,0,90 fi add3d[-1] 70 add3d rotate3d 0,0,1,18 done moded3d 3 mode3d 5 double3d 0\n~~~"\n\ "===\n![3D Ring](../img/example_torii.png)\n==="\n\n\ "### Create a vase from a 3D isosurface: "\n\ "\n~~~\n$ gmic moded3d 4 isosurface3d \"'x^2+2*abs(y/2)*sin(2*y)^2+z^2-3',0\" sphere3d 1.5 sub3d[-1] 0,5 "\ "plane3d 15,15 rotate3d[-1] 1,0,0,90 center3d[-1] add3d[-1] 0,3.2 color3d[-1] 180,150,255 color3d[-2] 128,255,0 "\ "color3d[-3] 255,128,0 add3d\n~~~"\n\ "===\n![3D Vase](../img/example_vase.png)\n==="\n\n\ "### Launch a set of interactive demos: "\n\ "\n~~~\n$ gmic demos\n~~~\n" l { reference_footer_$1 reference_end_$1 onfail } um _section,_text rm # # Implement output mode 'ascii' for command 'reference'. # reference_begin_ascii : use_vt100 if !narg($_shell_cols) _shell_cols:=${-shell_cols}-5 fi _section=0 +e "" reference_header_ascii : str=\n\ " "${_vt100_b}"gmic: GREYC\'s Magic for Image Computing:"$_vt100_n" command-line interface"\n\ " "${_vt100_c}${_vt100_b}"Version "${strver" "$_version,$_prerelease}$_vt100_n\n\ " "$_vt100_g$_vt100_u"(https://gmic.eu)"$_vt100_n\n\ \n\ " Copyright (c) Since 2008, David Tschumperlé / GREYC / CNRS."\n\ " "$_vt100_g$_vt100_u"(https://www.greyc.fr)"$_vt100_n +e $str reference_section_ascii : _section+=1 +e "" ('$_section." "') ('"$*"') +f.. {'" "'} +f.. {'-'} a[-4,-3] x a[-2,-1] x +e " "$_vt100_r$_vt100_b{-2,t}$_vt100_n +e " "$_vt100_r{t}$_vt100_n\n rm[-2,-1] reference_text_ascii : l[] { ('"$*"') gmd2ascii $_shell_cols,1 # Ensure output text contains no more than two consecutive newlines. # Also add a 2-chars left margin on each line. s +,{'\n'} eval "repeat (l,p, i(#p)==_'\n'?(h#p==1?resize(#p,0,0,0,0):(resize(#p,1,1,1,1);i(#p) = 0)): (resize(#p,1,h#p + 2,1,1,0,0,0,1); copy(i[#p,0],_' ',2,1,0)))" foreach { if w +e {/{t}} fi } rm } reference_list_of_commands_ascii : l { if !$! it $_path_rc/update$_version.gmic fi _display_categories=1 parse_cli ascii onfail rm +e \n" "$_vt100_r${_vt100_b}"No command descriptions available!"$_vt100_n +e " "${_vt100_r}"Try updating your command files, with command "$_vt100_b"'update'."$_vt100_n } reference_footer_ascii : +e \n" "$_vt100_r$_vt100_b"** G\47MIC comes with ABSOLUTELY NO WARRANTY; "\ "for details visit: https://gmic.eu **"$_vt100_n # # Implement output mode 'html' for command 'reference'. # reference_section_html : name="$*" reference_end_section_html ('""\n\ ""\n\ " "\n\ " "\n\ " "\n\ " G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\ "- "$name""\n\ " "\n\ " "\n\ " "\n\n\ " "\n\ " "\n\n\ "
"\n\ " "\n\ ""\n\n\ ""\n\n\ "

"$name"

"\n':y) => $name reference_end_section_html : if $! # End previous section ('"
"\n\n\ ""\n\n\ ""\n\ "
"\n\ " "\n\ " "':y) a[-2,-1] y fi reference_text_html : ('"$*"':y) gmd2html. 0 if $!>1 a[-2,-1] y fi reference_footer_html : reference_end_section_html reference_finalize_html : # Generate table of contents. html=""\n\ ""\n\ " "\n\ " "\n\ " "\n\ " G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\ "- Table of Contents"\n\ " "\n\ " "\n\ " "\n\n\ " "\n\ " "\n\n\ "
"\n\n\ " "\n\ ""\n\n\ ""\n\n\ "

Preamble

"\n\ " "\n\ "

Version

"\n\ "
"\n\ " G'MIC: "\ "GREYC's Magic for Image Computing
"\n\ " https://gmic.eu
"\n\ " Version "${strver" "$_version,$_prerelease}"

"\n\ " Copyright © Since 2008, "\ "David Tschumperlé / "\ "GREYC / "\ "CNRS
"\n\ " https://www.greyc.fr
"\n\ "
"\n\ "

Table of Contents

"\n\ "
    "\n ind_loc=${"-nmd 1,\"List of Commands\""} if narg($ind_loc) foreach { name={n} strvar $name url=${}.html if $>==$ind_loc html.="
  • "$name"
  • " else html.="
  • "$name"
  • " fi } html.="\n
"\n\n\ ""\n\n\ ""\n\ "
"\n\ " "\n\ " " i[0] ({'$html'}:y) =>[0] "Table of Contents" ind_loc+=1 if narg($ind_loc)" && "isfile('list_of_commands.html') # Merge with existing 'List of commands' page if it exists it list_of_commands.html if find(crop(),'""')<0 l[$ind_loc,-1] { s[0] -,{'""'} s. -,{'""'} i[1] ('""':y) k[0,1,-1] a y } else => "List of Commands" rv[$ind_loc,-1] rm. fi fi fi # Insert top and bottom navigation bar into pages. if !0$_pdf_output repeat $! { current={$>,n} strvar[] $current url_current=${}.html if $>>1 previous={{$>-1},n} strvar[] $previous url_previous=${}.html else previous= fi if $< next={{$>+1},n} strvar[] $next url_next=${}.html else next= fi html_top="
"\ "Table of Contents" if $> html_top.="  ▸  "$current"" fi html_top.="" if ['$previous']!=0 html_top.="◀  "$previous"" fi if ['$previous']!=0" && "['$next']!=0 html_top.="    |    " fi if ['$next']!=0 html_top.=""$next"  ▶" fi html_top.="
" html_bottom="
" if ['$previous']!=0 html_bottom.="◀  "$previous"" fi if ['$previous']!=0" && "['$next']!=0 html_bottom.="    |    " fi if ['$next']!=0 html_bottom.=""$next"  ▶" fi html_bottom.="
" replace_str[$>] "",$html_top replace_str[$>] "",$html_bottom } fi reference_end_html : reference_finalize_html # Save html pages. foreach { strvar {n} ot ${}.html } if isfile('table_of_contents.html') x "ln -fs table_of_contents.html index.html" fi rm # # Implement output mode 'man' for command 'reference'. # (It is based on the 'ascii' output with some tricks to generate a roff file). # reference_begin_man : _section=0 +e ".TH G\47MIC 1\n\ .SH NAME\n\ gmic \\- Perform image processing operations using the G\47MIC framework.\n\ \n\ .SH HELP\n" reference_header_man : _vt100_b="\\fB" _vt100_c="\\fB" _vt100_g="\\fB" _vt100_m= _vt100_n="\\fR" _vt100_r="\\fB" _vt100_u="\\fI" _prerelease= reference_header_ascii reference_section_man : reference_section_ascii "$*" reference_text_man : reference_text_ascii "$*" reference_list_of_commands_man : _vt100_c= reference_list_of_commands_ascii _vt100_m="\\fB" _vt100_b= reference_footer_man : reference_footer_ascii "$*" # # Implement output mode 'pdf' for command 'reference'. # (It is based on the 'html' output with some tricks to generate a pdf file with 'wkhtmltopdf'). # reference_begin_pdf : _pdf_output=1 reference_section_pdf : reference_section_html "$*" reference_text_pdf : reference_text_html "$*" reference_footer_pdf : reference_end_section_html reference_end_pdf : 1024,4,1,3 o. reference_pdf.png rm. reference_finalize_html # Load existing html page for each command. l[] { it ../../src/gmic_stdlib.gmic +parse_cli. list loc=${} parse_cli html repeat narg($loc) { command=${"arg "1+$>","$loc} l[] { if ['$command'][0]!=_'_' it $command.html fi onfail } } sort_list +,n } s +,{'""'} s +,{'""'} k[2--1:5] # Add specific header and footer. i[0] ('""\n\ ""\n\ " "\n\ " "\n\ " "\n\ " reference_pdf"\n\ " "\n\ " "\n\n\ " "\n\ "
"\n\ " "\n\ "

The Handbook

"\n\ "

Version "${strver" "$_version,$_prerelease}"

"\n\ "

© David Tschumperlé / GREYC / CNRS

"\n\ "

"{date(0)}"/"{date(1)}"/"{date(2)}"

"\n\ "
"':y) ('""\n\ "

□ End of document

"\n\ ""':y) a y ot reference_pdf.html rm # Copy css to current folder. it ../style.css ot. style.css rm # Use 'wkhtmltopdf' to convert html to a pdf file. # (There is a bug in wkhtmltopdf that prevents all images to appear in the generated document! # I have to print the .pdf manually 'in a file' first). delete reference_pdf.pdf v 0 e " > Waiting for file 'reference_pdf.pdf'." for !isfile('reference_pdf.pdf') { wait 5000 } e " > Removing links in file 'reference_pdf.pdf'." x "pdfjam reference_pdf.pdf" e " > Compressing file 'reference_pdf.pdf' to 'gmic_reference.pdf'." x "gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=gmic_reference.pdf reference_pdf-pdfjam.pdf" e " > Clean temporary files." delete style.css,reference_pdf.html,reference_pdf.png,reference_pdf.pdf,reference_pdf-pdfjam.pdf e " > Upload file 'gmic_reference.pdf' to G'MIC server." x "lftp sftp://"$GMIC_LOGIN":@ovh -e \"put -O /home/"$GMIC_LOGIN"/www/gmic/reference gmic_reference.pdf; "\ "quit\" >/dev/null" # use_vt100 # This command defines some global variables used to output colored text on VT100 terminals. use_vt100 : if !0$_vt100" || "['$_vt100_n']!=0 return fi _vt100_b="\33[1m" # Bold _vt100_c="\33[0;36;59m" # Cyan _vt100_g="\33[0;32;59m" # Green _vt100_i="\33[3m" # Italic _vt100_m="\33[0;35;59m" # Magenta _vt100_n="\33[0;0;0m" # Normal _vt100_r="\33[0;31;59m" # Red _vt100_s="\33[9m" # Strikethrough _vt100_u="\33[4m" # Underline #@cli version #@cli : Display current version number on stdout. version : use_vt100 reference_header_ascii[] if !0$_cli_noarg +e "\n" fi #------------------------------- # #@cli :: Input / Output # #------------------------------- #@cli camera : _camera_index>=0,_nb_frames>0,_skip_frames>=0,_capture_width>=0,_capture_height>=0 : (+) #@cli : Insert one or several frames from specified camera. #@cli : When 'nb_frames==0', the camera stream is released instead of capturing new images. #@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default). #@cli : Default values: 'camera_index=0' (default camera), 'nb_frames=1', 'skip_frames=0' and \ # 'capture_width=capture_height=0' (default size). # [Internal] Use this command to 'clean' a .gmz file that represents CLUT keypoints. # What it does is: # # - Standardize CLUT name. # - Convert RGB CLUTs to Grayscale when possible. # - Remove duplicates and sort by lexicographic order. # - Display list of CLUTs to ease documentation update of command 'clut'. # clean_cluts : e[^-1] "Clean CLUT dataset.\n" round c 0,255 # Standardize names. foreach { nm={n} # Standardize names for 'Moviz'. if "str = lowercase(['"$nm"*']); !find(str,'tpf_-_cinematica_')" l[] { if !narg($tpf) tpf=1 fi ('moviz_$tpf') tpf+=1 nm={t} rm } fi if "str = lowercase(['"$nm"*']); find(str,'_-_standard-vk')>=0" l[] { if !narg($tpf) tpf=1 fi ('moviz_$tpf') tpf+=1 nm={t} rm } fi # Standardize names for 'SmallHD MovieLook'. if "str = lowercase(['"$nm"*']); find(str,'_-_rec_709*')>=0" l[] { ('$nm') z. 0,{w-11} nm={t} rm } fi # Standardize names for 'SmallHD MovieLook'. if "str = lowercase(['"$nm"']); !find(str,'smallhd_movielook_')" l[] { ('$nm') z. 18,100% replace_str "apocalypsethisverymoment","apocalypse_this_very_moment" replace_str "bobford","bob_ford" replace_str "lifegivingtree","life_giving_tree" replace_str "savingprivatedamon","saving_private_damon" replace_str "thematrices","the_matrices" nm={t} rm } fi # Standardize names for 'Fuji XTrans III'. if "str = lowercase(['"$nm"']); !find(str,'fuji_xtrans_iii')" l[] { ('$nm') replace_str "_-_","_" nm={t} rm } fi # Standardize names for 'RawTherapee'. if "str = lowercase(['"$nm"']); !find(str,'kodak') || !find(str,'polaroid') || !find(str,'fuji') || !find(str,'ilford')" l[] { ({'$nm'},{'*'}) replace_str " ","_" replace_str "xp_2","xp2" replace_str "hp_5","hp5" repeat 8 { n:=1+$> replace_str "_"${n}"_+","_+" replace_str "_"${n}"_-","_-" replace_str "_"${n}"_alt","_alt" replace_str "_"${n}"_Alt","_alt" replace_str "_"${n}"*","*" } = 0,0,100% nm={t} rm } fi strclut $nm nm=${} # Standardize names for 'PictureFX'. if "str = lowercase(['"$nm"']); !find(str,'technicalfx') || !find(str,'picturefx') || !find(str,'analogfx') || !find(str,'goldfx') || !find(str,'zilverfx')" l[] { ('$nm') replace_str "-","_" nm={t} rm } fi # Other name changes. l[] { ({'$nm'},{'*'}) replace_str "_v_2*","*" replace_str "_v_1*","*" replace_str "_*","*" replace_str "_b_w","_bw" replace_str "&","" replace_str "rec_709_-_","rec709_" replace_str "s-log","slog" replace_str "__","_" replace_str "action_-_","action_" replace_str "-version-","" replace_str "picturefx_","" = 0,0,100% nm={t} rm } =>[^] $nm } # Convert RGB CLUTs to Grayscale when possible. foreach { if "ref(crop(#0,0,0,0,3,1,h,1,1),R); ref(crop(#0,0,0,0,4,1,h,1,1),G); ref(crop(#0,0,0,0,5,1,h,1,1),B); R==G && G==B" channels 0,3 fi } # Search for duplicates and sort. p=0 for $p<$! { nm0={$p,n} e "\r- Search duplicates for ["$p"] = '"$nm0"' " q:=$p+1 for $q<$! { nm={$q,n} if ['$nm0']==['$nm'] e " > Found duplicate ["$q"] -> Original 1x"{$p,h}", new 1x"{$q,h}"\n" rv[$p,$q] rm[$q] else q+=1 fi } p+=1 } sort_list +,n # Display all clut names. doc="#@cli clut : \"clut_name\",_resolution>0,_cut_and_round={ 0:no | 1:yes }\n"\ "#@cli : Insert one of the "$!" pre-defined CLUTs at the end of the image list.\\n\n"\ "#@cli : 'clut_name' can be {" sep="|" nbc=28 foreach { if !$< sep="}" fi str=" "{n}" "$sep s_str:=size(['$str']) nbc+=$s_str if $nbc<118 doc=${doc}${str} else doc=${doc}" \\\n#"${str} nbc:=1+$s_str fi } doc=${doc}"\n"\ "#@cli : Default values: 'resolution=33' and 'cut_and_round=1'.\n"\ "#@cli : $ clut summer clut alien_green,17 clut orange_dark4,48\n" e "\n"$doc #@cli m : eq. to 'command'. : (+) #@cli command : _add_debug_info={ 0 | 1 },{ filename | http[s]://URL | "string" } : (+) #@cli : Import G'MIC custom commands from specified file, URL or string. #@cli : (eq. to 'm').\n #@cli : Imported commands are available directly after the 'command' invocation. #@cli : Specified filename is not allowed to contain colons ':'. #@cli : Default value: 'add_debug_info=1' (except for a "string" argument, in which case 'add_debug_info=0'). #@cli : $ image.jpg command "foo : mirror y deform $""1" +foo[0] 5 +foo[0] 15 #@cli compress_to_keypoints : _method,_max_keypoints>=0,_err_avg[%]>=0,_err_max[%]>=0,_"err_command" #@cli : Compress each of the selected images into a set of keypoints that can be further decompressed \ # using command ''decompress_from_keypoints''. #@cli : **Beware**: This type of compression is effective only for images with very smooth content. #@cli : 'method' can be { 0:PDE | 1:RBF }. Add '2' to 'method' to enable removal step. #@cli : - 'max_keypoints' is the maximal number of keypoints generated by the compression method. \ # If 'max_keypoints<0', the removal step is not done when number of maximal keypoints has been reached. \ # 'max_keypoints=0' means 'no limits'. #@cli : - 'err_avg' is the desired average compression error. #@cli : - 'err_max' is the desired pointwise max compression error. #@cli : - 'err_command' is the code of a command that inputs the two images '[reference]' and '[compressed]' \ # and compute a single error map as a last image. #@cli : Defaults values: 'method=3', 'max_keypoints=0', 'err_avg=1%', 'err_max=5%' and 'err_command=-. [0] norm.' compress_to_keypoints : check "inrange(${1=3},0,3) && isint(${2=0}) && ${3=1%}>=0 && ${4=5%}>=0" \ skip "${5=-. [0] norm.}" use_rbf,is_removal,nb_max_keypoints,skip_removal_if_max_keypoints:=$1%2,$1>1,abs($2),$2<0 m0,m1=PDE,RBF M0,M1=$nb_max_keypoints" max keypoints, ", e[^-1] "Compress image$? as sets of keypoints, using "${m{$1%2}}" method, "${M{!$2}}"average error $3 and "\ "pointwise max error $4." pass[$[]] -1 sel=${} nb_img=$! foreach { nm={n} d_ind=[{arg0($>,$sel)}]='{b}'" "({$>+1}/$nb_img) e "" # Initialize keypoints with pixel values at image corners. dim,im,iM:=d>1?3:h>1?2:1,im,iM if $dim==3 1,1,1,{3+s} eval "const w1 = w#0 - 1; const h1 = h#0 - 1; const d1 = d#0 - 1; dims = vals = vectors(); copy(dims,[ w#0,h#0,d#0 ]); copy(vals,[ $im,$iM,$use_rbf ]); da_push(dims,vals, [0,0,0,I(#0,0,0,0)], [w1,0,0,I(#0,w1,0,0)], [w1,h1,0,I(#0,w1,h1,0)], [0,h1,0,I(#0,0,h1,0)], [0,0,d1,I(#0,0,0,d1)],[w1,0,d1,I(#0,w1,0,d1)],[w1,h1,d1,I(#0,w1,h1,d1)],[0,h1,d1,I(#0,0,h1,d1)])" elif $dim==2 1,1,1,{2+s} eval "const w1 = w#0 - 1; const h1 = h#0 - 1; const m = min(s,3); dims = vals = vectors(); copy(dims,[ w#0,h#0,1 ],m); copy(vals,[ $im,$iM,$use_rbf ],m); da_push(dims,vals, [0,0,I(#0,0,0)],[w1,0,I(#0,w1,0)],[w1,h1,I(#0,w1,h1)],[0,h1,I(#0,0,h1)])" else 1,1,1,{1+s} eval "const w1 = w#0 - 1; const m = min(s,3); dims = vals = vectors(); copy(dims,[ w#0,1,1 ],m); copy(vals,[ $im,$iM,$use_rbf ],m); da_push(dims,vals, [0,I(#0,0,0)],[w1,I(#0,w1,0)])" fi target_err_avg,target_err_max:=ispercentage($3)?($iM-$im)*$3:$3,ispercentage($4)?($iM-$im)*$4:$4 d_target_err_avg,d_target_err_max={_[$target_err_avg,$target_err_max]} # Step #1: Greedily add improving keypoints. err_avg,err_max=inf,inf nb_keypoints:="da_size(#1) - 2" do +da_freeze[1] if $dim==3 [0],[0],[0] eval.. "begin(ret = I); i(#-1,i0,i1,i2) = 1; ret" elif $dim==2 [0],[0] eval.. "begin(ret = I); i(#-1,i0,i1) = 1; ret" else [0] eval.. "begin(ret = I); i[#-1,i0] = 1; ret" fi distance. 1 n. 0.1,1 decompress_from_keypoints.. , rv[-2,-1] if {*} w. fi l[0,-2,-1] { $5 err_avg,err_max:=ia,iM *. .. xM,yM,zM:=xM,yM,zM k[0] } eval "$dim==3?da_push(#1,[$xM,$yM,$zM,I(#0,$xM,$yM,$zM)]): $dim==2?da_push(#1,[$xM,$yM,I(#0,$xM,$yM)]): da_push(#1,[$xM,I(#0,$xM)])" stats="avg = "{_$err_avg}" / "$d_target_err_avg", max = "{_$err_max}" / "$d_target_err_max nb_keypoints+=1 e "\r "$d_ind" > Add \#"$nb_keypoints": "$stats" " while (!$nb_max_keypoints" || "$nb_keypoints<$nb_max_keypoints)" && "\ ($err_avg>$target_err_avg" || "$err_max>$target_err_max) da_freeze[1] # Step #2: Remove keypoints. if $is_removal" && "($nb_keypoints<$nb_max_keypoints" || "!$skip_removal_if_max_keypoints) s:=s if $err_avg>$target_err_avg # Constraints not reached : relax them to allow keypoint removal anyway target_err_avg:=$err_avg*1.05 d_target_err_avg={_$target_err_avg} fi if $err_max>$target_err_max target_err_max:=$err_max*1.05 d_target_err_max={_$target_err_max} fi stats="avg = "{_$err_avg}" / "$d_target_err_avg", max = "{_$err_max}" / "$d_target_err_max ind=0 repeat h-2 { +point. 0,{$ind+2},0,1,-1 discard. -1 r. 1,{h/$s},1,$s,-1 +decompress_from_keypoints. l[0,-1] { $5 err_avg,err_max,xM,yM,zM:=ia,iM,xM,yM,zM k[0] } is_deleted=0 if $err_avg<=$target_err_avg" && "$err_max<=$target_err_max rv[1,-1] nb_keypoints-=1 is_deleted=1 stats="avg = "{_$err_avg}" / "$d_target_err_avg", max = "{_$err_max}" / "$d_target_err_max fi e "\r "$d_ind" > Rem \#"{1+$ind}/$nb_keypoints": "$stats" " if !$is_deleted ind+=1 fi rm. } fi k. => $nm } # compress_gmic # Compress .gmic custom command files for compressing update files a little bit, # by removing empty lines, and useless comments. compress_gmic : merge_multiline_comments merge_multiline eval " # Remove useless comments p = 0; while (p look for comment at line end l = find(#-1,_'#',p); l>=0 && l1 ('""\n\n\ ""\n\ ""\n\ ""\n\ " FilterTextTranslator"\n\n':y) repeat h#0 { str={0,`I(0,$>)`} tr={0,`I(1,$>)`} if s=['$tr'];s!=0" && norm(s-=_' ')" ({'$str'}) autocrop. {'" "'} _gmd_ascii2html. str={t} rm. ({'$tr'}) autocrop. {'" "'} _gmd_ascii2html. tr={t} rm. if {0,!i(2,$>)} # Global translation ('" "\n\ " "$str""\n\ " "$tr""\n\ " "\n\n':y) else # Filter specialization i=2 for {0,i($i,$>)} { path={0,`I($i,$>)`} ({'$path'}) autocrop. {'" "'} _gmd_ascii2html. path={t} rm. ('" "\n\ " "$str""\n\ " "$path""\n\ " "$tr""\n\ " "\n':y) i+=1 } r. 1,{h+1},1,1,0,1 # Last item has two done fi fi } ('"\n"\n':y) a[^0] y k. fi } #@cli delete : filename1[,filename2,...] : (+) #@cli : Delete specified filenames on disk. Multiple filenames must be separated by commas. #@cli d : eq. to 'display'. d : _gmic_s="$?" v + _display "",1,$[] +d : _gmic_s="$?" v + _display +,{$^>=0},$[] #@cli display #@cli : Display selected images in an interactive window. #@cli : (eq. to 'd'). #@cli : When invoked with a '+' prefix (i.e. '+display'), the command outputs its log messages on 'stdout' \ # rather than on 'stderr'. #@cli : Display window \#0 is used as the default window for the display, if already opened. #@cli : \nAvailable controls are shown below \ # (where 'LMB' = Left mouse button, 'RMB' = Right mouse button, 'MMB' = Middle mouse button and 'MW' = Mouse wheel). #@cli : \n- **Thumbnail navigation bar:** #@cli : 'TAB': Show/hide thumbnails - 'LMB': Select thumbnail or shift thumbnail bar - \ # '0'-'9','ARROWS' (opt. '+SHIFT'),'B','BACKSPACE','C','E','END','H','HOME','SPACE': Navigate and select thumbnails \ # (add 'CTRL' if mouse pointer is outside thumbnail bar). #@cli : \n- **Image view:** #@cli : 'LMB' or 'MMB': Image pan - 'RMB' or 'MW': Image zoom - 'ARROWS' (opt. '+SHIFT'),'HOME','END': Shift view - \ # 'A': Switch alpha rendering - 'C': Center view - 'E': Go to lower-right corner - 'ENTER': Reset view - \ # 'G': Toggle grid - 'H': Go to upper-left corner - 'K': Switch background - 'M': Toggle 3D view - \ # 'N': Switch normalization - 'P': Print info about current image pixel on 'stdout' - \ # 'PAGEUP' or 'PAGEDOWN': Raise/lower base channel - 'R': Rotate image - 'V': Crop image - 'Z': Switch zoom factor - \ # '0'-'9': Set zoom factor. #@cli : \n- **3D mesh view:** #@cli : 'LMB': Mesh rotation - 'CTRL+LMB' or 'MMB': Mesh pan - 'RMB': Mesh zoom - \ # 'A': Toggle axes - 'D': Switch face side mode - 'F': Change focale - 'J': Start/stop animation - \ # 'K': Switch background - 'O': Switch outline mode - 'P': Print 3D pose matrix on 'stdout' - \ # 'R': Switch rendering mode - 'T': Switch motion rendering mode - 'X': Show/hide bounding-box - \ # 'U': Switch animation mode - 'Z': Toggle z-buffer. #@cli : \n- **2D images specific:** #@cli : 'CTRL+LMB': Rectangular selection. #@cli : \n- **3D volumetric images specific:** #@cli : 'CTRL+MW': Pan along orthogonal axis - 'X': Reset area layout. #@cli : \n- **Window size, decoration and data I/O:** #@cli : 'CTRL+C': Decrease window size - 'CTRL+D': Increase window size - 'CTRL+F': Toggle fullscreen - \ # 'CTRL+I': Toggle info label - 'CTRL+O': Save copy of image as a `.gmz` file - \ # 'CTRL+L': Save copy of image list as `.gmz` file - 'CTRL+S': Save screenshot as a `.png` file - \ # 'CTRL+W': Start/stop window recording - 'CTRL+X': Toggle cursor. #@cli : \n- **Configuration variables:** #@cli : The viewer configuration can be tuned by assigning the following variables: #@cli : - '_display_selected' is an integer or an image name that tells which image is selected by default. #@cli : - '_display_alpha' can be { 0:off | 1:on | 2:over black | 3:over gray | 4:over white } (default value: '0'). #@cli : - '_display_background', an integer in range [ 0,9 ] (default value: '3'). #@cli : - '_display_cursor' can be { 0:off | 1:on (2D only) | 2:on (+3D volumetric images) } (default value: '1'). #@cli : - '_display_is_grid' can be { 0:off | 1:on } (default value: '1'). #@cli : - '_display_is_info' can be { 0:off | 1:on } (default value: '1'). #@cli : - '_display_normalization' can be { -1:auto | 0:off | 1:cut | 2:stretch channelwise | 3:stretch global | \ # 4: stretch (global once) } (default value: '-1'). #@cli : - '_display_print_images' can be { 0:off | N>0 } (default value: '5'). It sets the max number 'N' of images \ # whose information is initially printed on `stderr` or `stdout`. #@cli : - '_display_3d_is_rendered' can be { 0:off | 1:on } (default value: '1'). #@cli : - '_display_3d_rendering_mode' can be { 0:dots | 1:wireframe | 2:flat | 3:flat-shaded | 4:gouraud-shaded | \ # 5=phong-shaded } (default value: '4'). #@cli : - '_display_3d_outline_mode' can be { 0:no-outline | 1:black-outline | 2:gray-outline | 3:red-outline | \ # 4:green-outline | 5:blue-outline | 6:white-outline } (default value: '0'). #@cli : - '_display_3d_motion_rendering_mode' can be { -1:bounding-box | 0:dots | 1:wireframe | 2:flat | \ # 3:flat-shaded | 4:gouraud-shaded | 5=phong-shaded } (default value: '3'). #@cli : - '_display_3d_motion_time_limit' is specified in ms. Above this time, motion rendering toggle to \ # 'bounding-box' mode (default value: '300'). #@cli : - '_display_3d_side_mode' can be { 0:single-sided | 1:double-sided | 2:single-sided (flipped) } \ # (default value: '0'). #@cli : - '_display_3d_is_zbuffer' can be { 0:off | 1:on } (default value: '1'). #@cli : - '_display_3d_focale' can be { <0: perspective projection w/o sprite zooming, 0: parallel projection | \ # >0: perspective projection } (default value: 1.5). #@cli : - '_display_3d_is_axes' can be { 0:off | 1:on } (default value: '1'). #@cli : - '_display_3d_is_bounding_box' can be { 0:off | 1:on } (default value: '0'). #@cli : - '_display_3d_background' is an unsigned integer in range [0,11] (default value: '11'). #@cli : - '_display_3d_pose' is a sequence of 12 values that defines the current 3D pose matrix (read/write). #@cli : - '_display_3d_animation' can be { 0:off | 1:forward | 2:backward } (default value: '0'). #@cli : - '_display_3d_animation_mode' can be { 0-3:X-axis | 4-7:Y-axis | 8-11:Z-axis | 12-15:XYZ-axes } \ # (default value: '4'). display : _gmic_s="$?" v + _$0 "",1,$[] +display : _gmic_s="$?" v + _$0 +,{$^>=0},$[] # $1: '+' or '' to specify output on stdout or stderr. # $2: display_header={ 0 | 1 } # $3: list of selected images indices. _display : skip ${1=},${3=} if !$! if $2 e[0--5] "Display image []." fi is_change 0 return fi check_display "display" m "$0_rmn : nmd 2,$""* rm[${}]" nb_images=$! # Initialize setting variables. if ['$_display_selected']!=0 setting_selected=$_display_selected else setting_selected=0 fi setting_alpha:=narg($_display_alpha)?cut(int(0$_display_alpha),0,4):0 setting_background:=narg($_display_background)?cut(int(0$_display_background),0,9):3 setting_cursor:=narg($_display_cursor)?cut(int(0$_display_cursor),0,2):1 setting_is_grid:=narg($_display_is_grid)?cut(int(0$_display_is_grid),0,1):1 setting_is_info:=narg($_display_is_info)?cut(int(0$_display_is_info),0,1):1 setting_normalization:=narg($_display_normalization)?cut(int(0$_display_normalization),-1,4):-1 setting_print_images:=narg($_display_print_images)?max(0,$_display_print_images):5 setting_3d_is_rendered:=narg($_display_3d_is_rendered)?cut(int(0$_display_3d_is_rendered),0,1):1 setting_3d_rendering_mode:=narg($_display_3d_rendering_mode)?\ cut(int(0$_display_3d_rendering_mode),0,5):\ narg($_mode3d)?cut(int(0$_mode3d),0,5):4 setting_3d_outline_mode:=narg($_display_3d_outline_mode)?cut(int(0$_display_3d_outline_mode),0,6):0 setting_3d_motion_rendering_mode:=narg($_display_3d_motion_rendering_mode)?\ cut(int(0$_display_3d_motion_rendering_mode),-1,5):\ narg($_moded3d)?cut(int(0$_moded3d),-1,5):3 setting_3d_motion_time_limit:=narg($_display_3d_motion_time_limit)?max(0$_display_3d_motion_time_limit,0):300 setting_3d_side_mode:=narg($_display_3d_side_mode)?cut(int(0$_display_3d_side_mode),0,2):1 setting_3d_is_zbuffer:=narg($_display_3d_is_zbuffer)?cut(int(0$_display_3d_is_zbuffer),0,1):1 setting_3d_focale:=narg($_display_3d_focale)?0$_display_3d_focale:1.5 setting_3d_is_axes:=narg($_display_3d_is_axes)?cut(int(0$_display_3d_is_axes),0,1):1 setting_3d_is_bounding_box:=narg($_display_3d_is_bounding_box)?cut(int(0$_display_3d_is_bounding_box),0,1):0 setting_3d_background:=narg($_display_3d_background)?cut(int(0$_display_3d_background),0,12):11 setting_3d_animation:=narg($_display_3d_animation)?cut(int(0$_display_3d_animation),0,1):0 setting_3d_animation_mode:=narg($_display_3d_animation_mode)?cut(int(0$_display_3d_animation_mode),0,15):4 if !narg($_display_3d_pose) _display_3d_pose=1,0,0,0,0,1,0,0,0,0,1,0 fi if !narg($_display_3d_zoom) _display_3d_zoom=0.65 fi thumb_height_factor=0.15 thumb_min_ratio=0.45 thumb_is_visible:=$nb_images>1 is_opencv:="find([' "$_flags"'],'opencv')>0" l { # Set default selected image index if isint($setting_selected) selected:=$setting_selected%$nb_images else nmd 1,$setting_selected selected=${} if !narg($selected) selected=0 fi fi onfail selected=0 } # Save original image names to prevent name conflicts with those used in this command. foreach { image_name$>={n} => _display$> } # Generate window and log titles and print log message. (${3--1}:y) => image_indices if $nb_images<=6 $nb_images,1,1,1,x sel={^} rm. is_ellipsized=0 else sel=0,1,2,{$nb_images-[3,2,1]} is_ellipsized=1 fi log_title,window_title,sep= repeat narg($sel) { l_ind:=arg0($>,$sel) g_ind={image_indices,i[$l_ind]} basename {``${image_name$l_ind}} if ['{/${}}']!=0 ({'{/${}}'}) else ('{``${image_name$l_ind}}') fi basename={`"b = crop(); size(b)>32?copy(b[32-3],[['...'],0]); b"`} rm. log_title.=$sep$basename window_title.=$sep[$g_ind]" "$basename if $is_ellipsized" && "$>==2 sep=", (...) " else sep=", " fi } if $nb_images>1 window_title.=" (#"$nb_images")" else window_title.=" ("{0,[w,h,d,s]}")" fi if $2 e[0--4] "Display image"$_gmic_s" = '"$log_title"'." fi # Initialize display window. window_previous_normalization,windows_previous_fullscreen= if {*} window_width,window_height={*,d,e} window_previous_normalization,windows_previous_fullscreen={*,n,f} if $setting_normalization<0 setting_normalization:={*,n}?4:0 fi w[] $window_width,$window_height,0,$window_title else window_width,window_height=\ ${"$0_default_window_size[0-"{$nb_images-1}"] "$thumb_height_factor,$thumb_min_ratio} if $setting_normalization<0 setting_normalization=4 fi w[] $window_width,$window_height,0,$window_title fi window_nonfullscreen_width,window_nonfullscreen_height={*,w,h} is_fullscreen,mouse_x,mouse_y={*,f,x,y} mouse_area,is_area1,is_area2,is_area3,is_area4,is_area5,is_area234=0 previous_mouse_x,previous_mouse_y,previous_mouse_area=-1,-1,0 event_type0,is_clicked0,is_clicked1,is_clicked2,is_released0,is_released1,is_released2,\ is_drag0,is_drag1,is_drag2=0 window_no_wait=0 # Initialize other viewer variables. thumb_mouse_over=-1 thumb_view_update=0 thumb_x= view_center_x,view_center_y,view_center_z,view_zoom=0.5,0.5,0.5,1 view_zoom_mode,view_is_rotated,view_base_channel=3,0,0 canvas_rect_x0,canvas_rect_y0,canvas_rect_x1,canvas_rect_y1,canvas_cross_x,canvas_cross_y= canvas_mouse_x,canvas_mouse_y,window_record_frame=-1 canvas_label_alignment=0 area_width,area_height=0 area_mouse_x,area_mouse_y=-1 zoom_1_1,view_3d_draw_on_canvas,area_3d_is_motion=0 if $nb_images>1 (255,0) r. 33,1,1,1,3 channels. -3,0 +mirror. x => thumb_shade_left,thumb_shade_right fi 1,${nb_images}x5 => is_printed,is_infnan,is_rotated,is_mesh3d,thumb_is_cached f[is_infnan,is_mesh3d] -1 # Print information of first images. repeat min($nb_images,$setting_print_images) { =>[$>] ${image_name$>} v + _print[$>] $1,0,{image_indices,i[$>]} v - =>[$>] _display$> =[is_printed] 1,0,$> } # Enter user interaction loop. for {*}" && "!{*,ESC} { window_width,window_height={*,w,h} is_volumetric={$selected,d>1} if {is_mesh3d,i[$selected]<0} sh[$selected] if {is_rotated,i[$selected]} y. fi if h>6" && "int(crop(0,0,1,6))=='CImg3d' =[is_mesh3d] ${is_mesh3d.},0,$selected else =[is_mesh3d] 0,0,$selected fi rm. fi is_mesh3d={is_mesh3d,i[$selected]} is_view3d:=$setting_3d_is_rendered" && "($is_volumetric" || "$is_mesh3d) # Rotate image if necessary. if {is_rotated,i[$selected]!=$view_is_rotated} rotate[$selected] {is_rotated,90*($view_is_rotated-i[$selected])} =[is_rotated] $view_is_rotated,0,$selected fi # Render thumbnail navigation bar. if !$thumb_is_visible thumb_height,thumb_height2=0 else # Generate thumbnail coordinates and sizes for current window resolution. thumb_height:=max(48,round($window_height*$thumb_height_factor)) if !0$thumb_coords 1,$nb_images,1,2,"begin(CImg3d = 'CImg3d'); const ratio = $thumb_min_ratio; w = w#y; h = h#y; d = d#y; s = s#y; w==1 && h>6 && d==1 && s==1 && int(crop(#y,0,0,0,0,1,6,1,1))==CImg3d?[ 1,1 ]:( # is 3D mesh? d>1?(w+=d + 2; h+=d + 2); Mwh = max(w,h); wt = Mwh?max(ratio,w/Mwh):0.5; ht = Mwh?max(ratio,h/Mwh):0.5; i[#$is_rotated,y]%2?swap(wt,ht); [ wt,ht ])" sh. 100% /.. {iM} rm. *. $thumb_height round. +f. ">begin(Sw = 0); res = [ Sw, round(($thumb_height - i1)/2) ]; Sw+=i0 + 2; res" rv[-2,-1] a[-2,-1] c => thumb_coords thumb_sumw={thumb_coords,max(i[h-1]+i(0,h-1,0,2),$window_width)} thumb_x_px_max={thumb_coords,max(-16,i[h-1]+i(0,h-1,0,2)-$window_width+17)} if !narg($thumb_x) x0,w={thumb_coords,[i(0,$selected,0,0),i(0,$selected,0,2)]} thumb_x:=($x0-48)/$thumb_sumw fi $0_flush_thumb_cache fi thumb_height2:=$thumb_height+2 thumb_x_px:=cut($thumb_x*$thumb_sumw,-16,$thumb_x_px_max) # Generate thumnail view. if $thumb_view_update $0_rmn thumb_view thumb_view_update=0 fi if !0$thumb_view $0_rmn thumb_index_map $window_width,$thumb_height,1,3 100%,1,1,1,-1 => thumb_view,thumb_index_map # Retrieve index range of visible thumbnails. ind0,ind1={thumb_coords," find_ind(val) = ( ref(val,v); i0 = 0; i1 = h - 1; v<=i[i0]?i0:v>=i[i1]?i1:(while (i1 - i0>1, ic = int((i0 + i1)/2); i[ic]<=v?(i0 = ic):(i1 = ic)); i0) ); ind0 = find_ind($thumb_x_px); ind1 = find_ind($thumb_x_px + $window_width); [ ind0, ind1 ]"} # Render visible thumbnails. ind=$ind0 repeat $ind1-$ind0+1 { x,y,w,h={thumb_coords,I[$ind]} if {thumb_is_cached,i[$ind]} ${thumb_cache_$ind} r. $w,$h,1,100%,2 if {thumb_is_cached,i[$ind]==2} sh[thumb_3d_icon] 100% j.. [thumb_3d_icon],0.05~,0.95~,0,0,1,.,255 rm. fi else if {$ind,!w} # Empty image font_size:=cut($thumb_height/8,16,32) $w,$h,1,3,100 to. \330,0.42~,0.5~,$font_size,1,1,255 else # Generate 3D icon if necessary. if {is_mesh3d,i[$ind]<0} sh[$ind] if {is_rotated,i[$ind]} y. fi =[is_mesh3d] ${is_mesh3d.},0,$ind rm. fi if {is_mesh3d,i[$ind]} if !0$thumb_3d_icon box3d 35 col3d. 0,200,255 c3d. r3d. 1,1,1,45 64,64,1,3,-1 j3d. ..,50%,50%,0,1,3,0 rm.. +channels. 0 neq. -1 dilate. 3 *. 200 f.. "I!=J(-1) || I!=J(1) || I!=J(0,-1) || I!=J(0,1)?[0,0,0]:I+1" to.. 3D,0.5~,0.5~,24,1,1,255 a[-2,-1] c autocrop. r. 42,42,1,4,2 => thumb_3d_icon fi fi is_alpha={$ind,$setting_alpha" && "(s==2||s==4)} sh[$ind] 0,{$ind,$is_alpha?s-1:min(2,s-1)} if d>1 +rs3d. {ceil(min($w/(w+d+2),$h/(h+d+2))*[w,h,d])},1 volumetric2d. {round([w-(w==1),h-(h==1),d-(d==1)]/2)},2 rm.. fi if w>$w" || "h>$h +r. $w,$h else . fi rm.. if {is_rotated,i[$ind]} rotate. {is_rotated,-90*i[$ind]} fi if !$setting_normalization # Normalize thumb: none and. 255 elif $setting_normalization==1 # Normalize thumb: cut c. 0,255 else if {is_infnan,i[$ind]<0} =[is_infnan] ${_display_is_infnan[$ind]},0,$ind fi if $setting_normalization==2 # Normalize thumb: stretch (channelwise) if {is_infnan,i[$ind]} l. { s c $0_normalize_infnan a c } else l. { s c n 0,255 a c } fi else # Normalization thumb: stretch (global and global-once) if {is_infnan,i[$ind]} $0_normalize_infnan. else n. 0,255 fi fi fi r. {[$w,$h]-2} # -2: Keep space for frame if $is_alpha to_rgba. if $setting_alpha==1 (64,96;96,64) r. 24,24,1,1 r. $w,$h,1,3,0,2 else $w,$h,1,3,{arg($setting_alpha-1,0,128,255)} fi sh.. 100% j.. ...,0,0,0,0,1,.,255 rm[-3,-1] elif s==1 r. 100%,100%,1,3 elif s==2 r. 100%,100%,1,3,0 fi if {is_mesh3d,i[$ind]} sh[thumb_3d_icon] 100% j.. [thumb_3d_icon],0.05~,0.95~,0,0,1,.,255 rm. fi r. {[w,h]+2},1,100%,0,0,0.5,0.5 rectangle. 0,0,100%,100%,1,0x55555555,96 fi +store. thumb_cache_$ind =[thumb_is_cached] 1,0,$ind fi offx:=$x-$thumb_x_px line[thumb_index_map] {[$offx,0,$offx+w-1,0]},1,$ind j[thumb_view] .,$offx,$y rm. ind+=1 } # Decorate selected/mouse over thumbnail. x0,y0,w,h={thumb_coords,I[$selected]} x0-=$thumb_x_px x1,y1:=[$x0,$y0]+[$w,$h]-1 rectangle[thumb_view] $x0,$y0,$x1,$y1,1,0xFFFFFFFF,255 rectangle[thumb_view] {[[$x0,$y0]+1,[$x1,$y1]-1]},1,0xFFFFFFFF,255 rectangle[thumb_view] {[[$x0,$y0]+2,[$x1,$y1]-2]},1,0xFFFFFFFF,0 font_size:=cut($thumb_height/6,16,32) to[thumb_view] "#"{image_indices,i[$selected]},{[$x0,$y0]+1},$font_size,2 if $thumb_mouse_over>=0 x0,y0,w,h={thumb_coords,I[$thumb_mouse_over]} x0-=$thumb_x_px x1,y1:=[$x0,$y0]+[$w,$h]-1 rectangle[thumb_view] $x0,$y0,$x1,$y1,0.25,200 if $thumb_mouse_over!=$selected rectangle[thumb_view] $x0,$y0,$x1,$y1,0.75,0x33333333,0 rectangle[thumb_view] $x0,$y0,$x1,$y1,0.75,0xCCCCCCCC,255 to[thumb_view] "#"{image_indices,i[$thumb_mouse_over]},{[$x0,$y0]+1},$font_size,1,0.75 fi fi # Add left/right shading in thumb view. if $thumb_x_px>0 r[thumb_shade_left] 100%,$thumb_height sh[thumb_shade_left] 100% j[thumb_view] [thumb_shade_left],0,0,0,0,{min(1,$thumb_x_px/32)},.,255 rm. fi if $thumb_x_px+$window_width<$thumb_sumw r[thumb_shade_right] 100%,$thumb_height sh[thumb_shade_right] 100% j[thumb_view] [thumb_shade_right],1~,0,0,0,{min(1,($thumb_sumw-$window_width-$thumb_x_px)/32)},.,255 rm. fi $0_rmn window_view fi fi canvas_width,canvas_height=$window_width,{$window_height-$thumb_height2} # Render canvas background. if !0$canvas_background" || "{0$canvas_background,[w,h]!=[$canvas_width,$canvas_height]} $0_rmn canvas_background $canvas_width,$canvas_height,1,3,{arg0($setting_background>>1,0,64,128,192,255)} if $setting_background&1 amp:=$setting_background==1?32:12 24,24,1,1,-$amp +f. $amp a[-2,-1] x +mirror. x a[-2,-1] y r. $canvas_width,$canvas_height,1,3,0,2 +[-2,-1] c. 0,255 fi => canvas_background $0_rmn canvas_base,canvas_volumetric_background fi # Generate canvas base. if !0$canvas_base # Print information about image on stderr/stdout (if not already done). if {is_printed,$setting_print_images" && "!i[$selected]} =>[$selected] ${image_name$selected} v + _print[$selected] $1,0,{image_indices,i[$selected]} v - =>[$selected] _display$selected =[is_printed] 1,0,$selected fi # Determine size of the different XY,XZ,ZY,3D areas. if $is_volumetric if !narg($canvas_cross_x) w,h,d={$selected,i[#$is_rotated,$selected]%2?[h,w,d]:[w,h,d]} whd={$selected,`string($w,'_',$h,'_',$d)`} if narg(${view_coords_$whd}) canvas_cross_x,canvas_cross_y:=f=[${view_coords_$whd}];f[size(f)-2,2] else canvas_cross_x,canvas_cross_y={$selected,[min(0.95,w/(w+d)),min(0.95,h/(h+d))]} fi fi canvas_cross_x,canvas_cross_y:="[ cut("$canvas_cross_x",16/$canvas_width,($canvas_width-17)/$canvas_width), cut("$canvas_cross_y",16/$canvas_height,($canvas_height-17)/$canvas_height) ]" area_xy_width,area_xy_height,area_xz_width,area_xz_height,area_zy_width,area_zy_height,\ area_3d_width,area_3d_height:=" const w_ratio = "$canvas_cross_x"; const h_ratio = "$canvas_cross_y"; w_xy = round(w_ratio*$canvas_width) - 1; h_xy = round(h_ratio*$canvas_height) - 1; w_zy = $canvas_width - w_xy - 1; h_xz = $canvas_height - h_xy - 1; [ w_xy,h_xy,w_xy,h_xz,w_zy,h_xy,w_zy,h_xz ]" elif $is_mesh3d" && "$is_view3d area_xy_width,area_xy_height,area_xz_width,area_xz_height,area_zy_width,area_zy_height=0 area_3d_width,area_3d_height=$canvas_width,$canvas_height else area_xy_width,area_xy_height=$canvas_width,$canvas_height area_xz_width,area_xz_height,area_zy_width,area_zy_height,area_3d_width,area_3d_height=0 fi # Render canvas base image. if {$selected,!w} [canvas_background] => canvas_base elif $is_mesh3d" && "$is_view3d view_3d_draw_on_canvas=1 else is_alpha={$selected,$setting_alpha" && "(s==2||s==4)} view_base_channel={$selected,cut($view_base_channel,0,max(0,s-3))} cmin,cmax={$selected,$is_alpha?[0,s-1]:[$view_base_channel,min($view_base_channel+2,s-1)]} area_xy_x0,area_xy_y0,area_xy_x1,area_xy_y1,area_xy_dx,area_xy_dy,\ area_xz_x0,area_xz_z0,area_xz_x1,area_xz_z1,area_xz_dx,area_xz_dz,\ area_zy_z0,area_zy_y0,area_zy_z1,area_zy_y1,area_zy_dz,area_zy_dy={$selected," const x = $view_center_x*w; const y = $view_center_y*h; const z = $view_center_z*d; const w_xy = $area_xy_width; const h_xy = $area_xy_height; const w_xz = $area_xz_width; const h_xz = $area_xz_height; const w_zy = $area_zy_width; const h_zy = $area_zy_height; const mw4 = max(w,4); const mh4 = max(h,4); const md4 = max(d,4); const f_xy = max(mw4/w_xy,mh4/h_xy); $is_volumetric?( const f_xz = max(mw4/w_xz,md4/h_xz); const f_zy = max(md4/w_zy,mh4/h_zy); const f = 0.5*min(f_xy,f_xz,f_zy)/$view_zoom; ):(const f = 0.5*f_xy/$view_zoom); [ x - f*w_xy, y - f*h_xy, x + f*w_xy, y + f*h_xy, 2*f*w_xy, 2*f*h_xy, x - f*w_xz, z - f*h_xz, x + f*w_xz, z + f*h_xz, 2*f*w_xz, 2*f*h_xz, z - f*w_zy, y - f*h_zy, z + f*w_zy, y + f*h_zy, 2*f*w_zy, 2*f*h_zy ]"} # Generate and draw XY, XZ and YZ views. if $is_volumetric if !0$canvas_volumetric_background" || "\ {0$canvas_volumetric_background,[w,h]!=[$canvas_width,$canvas_height]} $0_rmn canvas_volumetric_background +j[canvas_background] [canvas_background],{$area_xy_width+2},0 j. [canvas_background],0,{$area_xy_height+2} rectangle. 0,$area_xy_height,100%,{$area_xy_height+1},1,0 rectangle. $area_xy_width,0,{$area_xy_width+1},100%,1,0 rectangle. {[$area_xy_width,$area_xy_height]+2},100%,100%,0.25,0 => canvas_volumetric_background fi [canvas_volumetric_background] => canvas_base else [canvas_background] => canvas_base fi repeat $is_volumetric?3:1 { if !$> # XY-view can_x0,can_y0,can_x1,can_y1,can_dx,can_dy,can_width,can_height=\ $area_xy_x0,$area_xy_y0,$area_xy_x1,$area_xy_y1,\ $area_xy_dx,$area_xy_dy,$area_xy_width,$area_xy_height w_sel,h_sel={$selected,[w,h]} plane={$selected,cut(round($view_center_z*d,1,-1),0,d-1)} joffx,joffy=0 elif $>==1 # XZ-view can_x0,can_y0,can_x1,can_y1,can_dx,can_dy,can_width,can_height=\ $area_xz_x0,$area_xz_z0,$area_xz_x1,$area_xz_z1,\ $area_xz_dx,$area_xz_dz,$area_xz_width,$area_xz_height w_sel,h_sel={$selected,[w,d]} plane={$selected,cut(round($view_center_y*h,1,-1),0,h-1)} joffx,joffy=0,{$area_xy_height+2} else # ZY-view can_x0,can_y0,can_x1,can_y1,can_dx,can_dy,can_width,can_height=\ $area_zy_z0,$area_zy_y0,$area_zy_z1,$area_zy_y1,\ $area_zy_dz,$area_zy_dy,$area_zy_width,$area_zy_height w_sel,h_sel={$selected,[d,h]} plane={$selected,cut(round($view_center_x*w,1,-1),0,w-1)} joffx,joffy={$area_xy_width+2},0 fi ix0,iy0,ix1,iy1:=floor([$can_x0,$can_y0,$can_x1,$can_y1]) # Crop coordinates ix0,ix1:=cut([$ix0,$ix1],0,$w_sel-1) iy0,iy1:=cut([$iy0,$iy1],0,$h_sel-1) dix,diy:=$ix1-$ix0+1,$iy1-$iy0+1 # Dimension of cropped image p0:=ceil(($ix0-$can_x0)/$can_dx*$can_width) q0:=ceil(($iy0-$can_y0)/$can_dy*$can_height) p1:=ceil(($ix1+1-$can_x0)/$can_dx*$can_width) q1:=ceil(($iy1+1-$can_y0)/$can_dy*$can_height) dp,dq:=max(1,$p1-$p0),max(1,$q1-$q0) # Dimension of resized image if {$selected,(w==1" || "h==1)" && "($view_zoom>4*$zoom_1_1" || "$zoom_1_1>1)} if $dp<8 joffx-=round((8-$dp)/2) dp=8 fi if $dq<8 joffy-=round((8-$dq)/2) dq=8 fi fi fact:=max($can_width/$can_dx,$can_height/$can_dy)*100 if $fact<100 # When image is larger than portion displayed on canvas: downsize before crop. sh[$selected] $cmin,$cmax if !$> if $is_volumetric +z. 0,0,$plane,100%,100%,$plane fi # XY elif $>==1 +z. 0,$plane,0,100%,$plane,100% permute. xzyc # XZ else +z. $plane,0,0,$plane,100%,100% permute. zyxc # ZY fi if $is_volumetric r. $fact%,$fact% else +r. $fact%,$fact% fi rm.. z. {floor([$ix0*w/$w_sel,$iy0*h/$h_sel,$ix1*w/$w_sel,$iy1*h/$h_sel])} else if !$> # XY-view +z[$selected] $ix0,$iy0,$plane,$cmin,$ix1,$iy1,$plane,$cmax elif $>==1 # XZ-view +z[$selected] $ix0,$plane,$iy0,$cmin,$ix1,$plane,$iy1,$cmax permute. xzyc else # ZY-view +z[$selected] $plane,$iy0,$ix0,$cmin,$plane,$iy1,$ix1,$cmax permute. zyxc fi fi if !$setting_normalization # Normalize: none and. 255 elif $setting_normalization==1 # Normalize: cut c. 0,255 else if {is_infnan,i[$selected]<0} =[is_infnan] ${$0_is_infnan[$selected]},0,$selected fi if $setting_normalization==2 # Normalize: stretch (channelwise) if {is_infnan,i[$selected]} l. { s c $0_normalize_infnan a c } else l. { s c n 0,255 a c } fi elif $setting_normalization==3 # Normalize: stretch (global) if {is_infnan,i[$selected]} $0_normalize_infnan. else n. 0,255 fi else # Normalize: stretch (global once) if !narg(${display_range_global$selected}) if {is_infnan,i[$selected]} display_range_global$selected=${-_display_minmax_infnan} else sh[$selected] $cmin,$cmax display_range_global$selected:=[im,iM] rm. fi fi im,iM:=${display_range_global$selected} if $im==$iM f. 0 else if {is_infnan,i[$selected]} $0_normalize_infnan. $im,$iM else -. $im *. {255/($iM-$im)} c. 0,255 fi fi fi fi r. $dp,$dq dx,dy,dl:=dx=$can_width/$can_dx;dy=$can_height/$can_dy;[dx,dy,max(dx,dy)] if $setting_is_grid" && "max($dx,$dy)>8 # Draw grid opacity,color:=lerp(0,0.5,min(($dl-8)/10,1)^2),ia<64?128:0 $dix,1,1,1,1 r. $dp,1,1,1,4 eval. "begin(col = $color); i || x==w - 1?polygon(#-2,2,x,0,x,$dq-1,$opacity,col,col,col,255)" rm. $diy,1,1,1,1 r. $dq,1,1,1,4 eval. "begin(col = $color); i || x==w - 1?polygon(#-2,2,0,x,$dp-1,x,$opacity,col,col,col,255)" rm. fi if $is_volumetric # Slightly crop to remove extra borders if $p0<0 cx0,p0={-$p0},0 else cx0=0 fi if $q0<0 cy0,q0={-$q0},0 else cy0=0 fi if $p1>$can_width cx1:=$can_width+w-$p1-1 else cx1=100% fi if $q1>$can_height cy1:=$can_height+h-$q1-1 else cy1=100% fi z. $cx0,$cy0,$cx1,$cy1 fi np0,nq0:=[$p0,$q0]+[$joffx,$joffy] np1,nq1:=[$np0,$nq0]+[$dp,$dq]-1 if $is_alpha to_rgba. if $setting_alpha==1 sh. 100% j[canvas_base] ..,$np0,$nq0,0,0,1,.,255 rm. else $dp,$dq,1,3,{arg($setting_alpha-1,0,128,255)} sh.. 100% j.. ...,0,0,0,0,1,.,255 rm[-3,-1] j[canvas_base] .,$np0,$nq0 fi else if s==1 r. 100%,100%,1,3 elif s==2 r. 100%,100%,1,3,0 fi j[canvas_base] .,$np0,$nq0 fi rm. } # Draw central cursor for volumetric images. if $is_volumetric w2,h2:=int([$area_xy_width,$area_xy_height]/2) line[canvas_base] 0,$h2,100%,$h2,0.5,0x55555555,0 line[canvas_base] 0,$h2,100%,$h2,0.5,0xAAAAAAAA,255 line[canvas_base] $w2,0,$w2,100%,0.5,0x55555555,0 line[canvas_base] $w2,0,$w2,100%,0.5,0xAAAAAAAA,255 w2,h2:=[$area_xy_width,$area_xy_height]+2+int([$area_zy_width,$area_xz_height]/2) line[canvas_base] 0,$h2,$area_xy_width,$h2,0.5,0x55555555,0 line[canvas_base] 0,$h2,$area_xy_width,$h2,0.5,0xAAAAAAAA,255 line[canvas_base] $w2,0,$w2,$area_xy_height,0.5,0x55555555,0 line[canvas_base] $w2,0,$w2,$area_xy_height,0.5,0xAAAAAAAA,255 if $is_view3d view_3d_draw_on_canvas=1 fi fi fi $0_rmn canvas_label_coords fi # Generate 3D mesh objects. if $is_view3d if $setting_3d_is_axes" && "!0$area_3d_axes" && "!$is_volumetric axes3d 40,40,40,20,X,Y,Z,0 col3d. 0,255,0 l. { s3d a[0-3] y area_3d_axes_offset={0,h} a y } => area_3d_axes fi if !0$area_3d_mesh if $is_volumetric +projections3d[$selected] {$selected,floor([$view_center_x*w,$view_center_y*h,$view_center_z*d])},1,10 asiz={$selected,max(w,h,d)/3} axes3d $asiz,$asiz,$asiz,16 o3d. 0.5 col3d. 255,128,0 +3d[-2,-1] else [$selected] if {is_rotated,i[$selected]} rotate. {is_rotated,-90*i[$selected]} fi fi c3d. n3d. => area_3d_mesh $0_rmn area_3d_mesh_notexture,area_3d_bounding_box,area_3d_view fi if !0$area_3d_bounding_box" && "(\ $setting_3d_is_bounding_box" || "\ ($area_3d_is_motion" && "$setting_3d_motion_rendering_mode<0" && "!$is_volumetric)) +boundingbox3d[area_3d_mesh] => area_3d_bounding_box if $setting_3d_is_bounding_box +o3d[area_3d_bounding_box] 0.35 +3d[area_3d_mesh,-1] fi fi if $setting_3d_outline_mode" && "!0$area_3d_mesh_notexture +l[area_3d_mesh] { p3d 2 nbp:=f2ui(i[7]) if $nbp 1,$nbp,1,1,y j.. .,0,{-2,h-4*$nbp} j.. .,0,{-2,h-3*$nbp} j.. .,0,{-2,h-2*$nbp} rm. fi } => area_3d_mesh_notexture fi # Render and draw 3D mesh object. if !0$area_3d_view if !0$canvas_3d_background" || "{0$canvas_3d_background,[w,h]!=[$area_3d_width,$area_3d_height]} $0_rmn canvas_3d_background background,is_checkerboard:=int([$setting_3d_background>>1,$setting_3d_background&1]) if $background<3 {*,w,h},1,3,{arg(1+$background,0,128,255)} elif $background==3 3,2,1,1,"16,16,48,44,96,76" permute. cyzx elif $background==4 3,2,1,1,"0,0,0,0,64,96" permute. cyzx else 3,3,1,1,"0,0,0,0,0,100,100,0,50" permute. cyzx fi r. $area_3d_width,$area_3d_height,1,3,3 if $is_checkerboard 24,24,1,1,48 +f. 64 a[-2,-1] x +mirror. x a[-2,-1] y r. {-2,[w,h]},1,3,0,2 +[-2,-1] /. 2 fi round. => canvas_3d_background fi [canvas_3d_background] => area_3d_view if !0$view_3d_pose ($_display_3d_pose) r. 4,3,1,1,-1 => view_3d_pose view_3d_zoom=$_display_3d_zoom fi is_bounding_box_only:=$area_3d_is_motion" && "$setting_3d_motion_rendering_mode<0" && "!$is_volumetric if $is_bounding_box_only [area_3d_bounding_box] else [area_3d_mesh] fi if $setting_3d_side_mode==2 rv3d. fi # Apply pose to 3D mesh. nbv:=f2ui(i[6]) if $nbv +z. 0,8,0,{7+3*$nbv} r. 3,$nbv,1,1,-1 permute. yzcx fact:=min($area_3d_width,$area_3d_height) eval. "begin( R = crop(#$view_3d_pose,0,0,3,3); S = crop(#$view_3d_pose,3,0,1,3); R*=$view_3d_zoom*$fact; S*=$fact; ); copy(i[#-2,8+3*x],(R*I)+=S)" rm. else fact=1 fi if $is_bounding_box_only j3d[area_3d_view] .,50%,50%,0,0.5,1,0,0,{1.5*$fact} else time0=$| j3d[area_3d_view] .,50%,50%,0,1,\ {$is_volumetric?[2,1,1,1.5*$fact]:[$area_3d_is_motion?min($setting_3d_motion_rendering_mode,\ $setting_3d_rendering_mode):\ $setting_3d_rendering_mode,\ $setting_3d_side_mode==1,\ $setting_3d_is_zbuffer,\ $setting_3d_focale*$fact]} if $setting_3d_outline_mode" && "!$is_volumetric # Draw primitive outlines +z. 0,8,0,{7+3*$nbv} j[area_3d_mesh_notexture] .,0,8 rm. [area_3d_view],[area_3d_view],1,1,-1 j3d. [area_3d_mesh_notexture],50%,50%,0,1,2,{$setting_3d_side_mode==1},$setting_3d_is_zbuffer,\ {$setting_3d_focale*$fact} g. xy,1 !=[-2,-1] 0 or[-2,-1] 100%,100%,1,3 fc. ${"arg "$setting_3d_outline_mode,\ "\"0,0,0\",\"128,128,128\",\"255,0,0\",\"0,128,0\",\"0,0,255\",\"255,255,255\""} j[area_3d_view] .,0,0,0,0,0.5,.. rm[-2,-1] fi dtime:=$|-$time0 if $area_3d_is_motion" && "1000*$dtime>$setting_3d_motion_time_limit setting_3d_motion_rendering_mode=-1 fi fi rm. if $setting_3d_is_axes" && "!$is_volumetric [area_3d_axes] nbv={@6} +z. 0,8,0,{7+3*$nbv} r. 3,$nbv,1,1,-1 permute. yzcx # Apply pose to 3D axes eval. "begin(R = crop(#$view_3d_pose,0,0,3,3)); copy(i[#-2,8+3*x],R*I)" rm. eval " # Colorize axes depending on their Z-sign const off = "$area_3d_axes_offset"; ref([ 255,0,0 ],col); i[13]>0?copy(i[off],col,3); i[19]>0?copy(i[off + 3],col,3); i[25]>0?copy(i[off + 6],col,3)" j3d[area_3d_view] .,50,{area_3d_view,h-50},0,0.75,1,0,0,150 rm. fi if {thumb_is_cached,$thumb_is_visible" && "i[$selected]<2" && "!$is_volumetric} w,h,Mwh={$thumb_coords,wh=I[$selected][2,2];[wh,max(wh,256)]} +rs[area_3d_view] $Mwh,$Mwh,2,3 store. 1,thumb_cache_$selected =[thumb_is_cached] 2,0,$selected thumb_view_update,window_no_wait=1 fi view_3d_draw_on_canvas=1 fi if $view_3d_draw_on_canvas" && "0$area_3d_view if $is_mesh3d # Small hack to avoid image copy $0_rmn canvas_base =>[area_3d_view] canvas_base 0 => area_3d_view else j[canvas_base] [area_3d_view],{[$area_xy_width,$area_xy_height]+($is_volumetric?2:0)} fi view_3d_draw_on_canvas=0 $0_rmn canvas_view fi fi # Generate label for image title. if !0$canvas_label_info 0 basename {``${image_name$selected}} if ['{/${}}']!=0 ({'{/${}}'}) else ('{``${image_name$selected}}') fi basename={`"b = crop(); size(b)>32?copy(b[32-3],[['...'],0]); b"`} rm. if $nb_images==1" && "!$is_fullscreen str="["{image_indices,i[$l_ind]}"] "$basename" " if $is_mesh3d" && "$is_view3d str.="("{$selected,f2ui(i[6])}" vertices, "{$selected,f2ui(i[7])}" primitives)" else str.="("{$selected,[w,h,d,s]}")" fi w[] -1,-1,$str else str= if $thumb_mouse_over>=0" && "$thumb_mouse_over!=$selected basename {``${image_name$thumb_mouse_over}} basename_mo={`"b = ['"{``${}}"']; size(b)>32?copy(b[32-3],[['...'],0]); b"`} str.="#"{image_indices,i[$thumb_mouse_over]}": " if {is_mesh3d,$is_view3d" && "i[$thumb_mouse_over]} str.=$basename_mo" ("\ {$thumb_mouse_over,f2ui(i[6])}" vertices, "{$thumb_mouse_over,f2ui(i[7])}" primitives)\n" else whds={$thumb_mouse_over,i[#$is_rotated,$thumb_mouse_over]%2?[h,w,d,s]:[w,h,d,s]} str.=$basename_mo" ("{``$whds}")\n" fi fi if $nb_images>1 str.="#"{image_indices,i[$selected]}": " fi if $is_mesh3d" && "$is_view3d str.=$basename" ("\ {$selected,f2ui(i[6])}" vertices, "{$selected,f2ui(i[7])}" primitives)" else whds={$selected,i[#$is_rotated,$selected]%2?[h,w,d,s]:[w,h,d,s]} str.=$basename" ("{``$whds}")" fi font_size:=cut(round($canvas_height/40),16,32) t. {``$str},0,0,$font_size,1,255 fi => canvas_label_info $0_rmn canvas_label fi # Generate label for image coordinates. if !0$canvas_label_coords" && "{$selected,w} 0 view_coords= str= x,y,z={$selected,"floor( $is_area2?[ lerp($area_xy_x0,$area_xy_x1,$area_mouse_x/$area_xy_width), lerp($area_xy_y0,$area_xy_y1,$area_mouse_y/$area_xy_height), cut($view_center_z*d,0,d - 1) ]: $is_area3?[ lerp($area_xz_x0,$area_xz_x1,$area_mouse_x/$area_xz_width), cut($view_center_y*h,0,h - 1), lerp($area_xz_z0,$area_xz_z1,$area_mouse_y/$area_xz_height) ]: $is_area4?[ cut($view_center_x*w,0,w - 1), lerp($area_zy_y0,$area_zy_y1,$area_mouse_y/$area_zy_height), lerp($area_zy_z0,$area_zy_z1,$area_mouse_x/$area_zy_width) ]: [ -1,-1,-1 ])"} view_xyz=$x,$y,$z view_xy_disp={is_rotated,"const w1 = w#$selected - 1; const h1 = h#$selected - 1; ang = i[$selected]; !ang?[ $x, $y ]:ang==1?[ $y, w1 - $x ]:ang==2?[ w1 - $x, h1 - $y ]:[ h1 - $y, $x ]"} if narg($canvas_crop_x0)" || "narg($canvas_rect_x0) if narg($canvas_crop_x0) x0,y0,z0,x1,y1,z1=$canvas_crop_x0,$canvas_crop_y0,$canvas_crop_z0,$view_xyz else x0,y0,z0,x1,y1,z1=$canvas_rect_x0,$canvas_rect_y0,0,$canvas_rect_x1,$canvas_rect_y1,0 fi if $x0>$x1 x0,x1=$x1,$x0 fi if $y0>$y1 y0,y1=$y1,$y0 fi if $z0>$z1 z0,z1=$z1,$z0 fi dx,dy,dz:=$x1-$x0,$y1-$y0,$z1-$z0 view_xy0_disp={is_rotated,"const w1 = w#$selected - 1; const h1 = h#$selected - 1; ang = i[$selected]; !ang?[ $x0, $y0 ]:ang==1?[ $y0, w1 - $x0 ]:ang==2?[ w1 - $x0, h1 - $y0 ]:[ h1 - $y0, $x0 ]"} view_xy1_disp={is_rotated,"const w1 = w#$selected - 1; const h1 = h#$selected - 1; ang = i[$selected]; !ang?[ $x1, $y1 ]:ang==1?[ $y1, w1 - $x1 ]:ang==2?[ w1 - $x1, h1 - $y1 ]:[ h1 - $y1, $x1 ]"} view_dxy_disp={is_rotated,(i[$selected]%2?[$dy,$dx]:[$dx,$dy])+1} if $is_volumetric str="Box ( "$view_xy0_disp,$z0" ) - ( "$view_xy1_disp,$z1" ), Size = ( "$view_dxy_disp,{$dz+1}" )"\n\ "Length = "{_norm($dx,$dy,$dz)} else str="Box ( "$view_xy0_disp" ) - ( "$view_xy1_disp" ), Size = ( "$view_dxy_disp" )"\n\ "Length = "{_norm($dx,$dy)}", Angle = "{_rad2deg(atan2($y1-$y0,$x1-$x0))%360}"\260" fi else if $is_volumetric view_xyz_disp=$view_xy_disp,$z else view_xyz_disp=$view_xy_disp fi if {$selected,inrange($x,0,w,1,0)" && "inrange($y,0,h,1,0)" && "inrange($z,0,d,1,0)} s_value={`"val = I(#$selected,$x,$y,$z); size(val)<=6?v2s(val,-1):( vals = vale = vector3(); copy(vals,val,3); copy(vale,val[size(val)-3],3); string(#256,v2s(vals,-1),', ... ,',v2s(vale,-1)); )"`} s_hex= if {$selected,"s>4?0:(val = I($x,$y,$z); min(isint(val,0,255)))"} s_hex=" = \#"{$selected,`" to_hex(x) = (ref(x,_x); _x<10?_'0'+_x:_'A'+_x-10); res = vector(#2*s); off = 0; repeat (s,k, val = i($x,$y,$z,k); res[off++] = to_hex(val>>4); res[off++] = to_hex(val&15)); res"`} fi if $is_volumetric str="Point ( "$view_xyz_disp" ) = [ "$s_value" ]"$s_hex else str="Point ( "$view_xyz_disp" ) = [ "$s_value" ]"$s_hex fi fi fi font_size:=cut(round($canvas_height/40),16,32) t. {``$str},0,0,$font_size,1,255 => canvas_label_coords $0_rmn canvas_label fi # Generate label for notification. if !0$canvas_label_notification" && "['$notification']!=0 font_size:=cut(round($window_height/40),16,32) 0 t. {``$notification},0,0,{round(1.25*$font_size)},1,255 r. {[w,h]+20},1,1,0,0,0.5,0.5 frame. xy,2,255 r. {[w,h]+6},1,1,0,0,0.5,0.5 +n. 200,255 r.. 100%,100%,1,3 a[-2,-1] c => canvas_label_notification notification_opacity,notification=3, $0_rmn window_view fi # Render canvas label. if !0$canvas_label 0 if $setting_is_info if $canvas_label_info a. [canvas_label_info],y fi if $canvas_label_coords a. [canvas_label_coords],y fi fi if w r. {[w,h]+10},1,1,0,0,0.5,0.5 +n. 150,255 r.. 100%,100%,1,3 a[-2,-1] c fi => canvas_label $0_rmn canvas_view fi if $window_record_frame>=0 $0_rmn canvas_view fi if !0$canvas_view [canvas_base] => canvas_view if {$selected,!w}" || "$is_area1 cursor[0] 1 else # Draw cursor. ix,iy,iz={$selected," $is_area2?[ lerp($area_xy_x0,$area_xy_x1,$area_mouse_x/$area_width), # XY lerp($area_xy_y0,$area_xy_y1,$area_mouse_y/$area_height), cut($view_center_z*d,0,d - 1) ]: $is_area3?[ lerp($area_xz_x0,$area_xz_x1,$area_mouse_x/$area_width), # XZ cut($view_center_y*h,0,h - 1), lerp($area_xz_z0,$area_xz_z1,$area_mouse_y/$area_height) ]: $is_area4?[ cut($view_center_x*w,0,w - 1), lerp($area_zy_y0,$area_zy_y1,$area_mouse_y/$area_height), # ZY lerp($area_zy_z0,$area_zy_z1,$area_mouse_x/$area_width) ]: [ -1,-1,-1 ]"} rix,riy,riz:=floor([$ix,$iy,$iz]); is_cursor_over_image={$selected,(!$is_view3d" || "$is_volumetric)" && "\ (inrange($rix,0,w-1)" && "inrange($riy,0,h-1)" && "inrange($riz,0,d-1))} if {$selected,!$is_cursor_over_image" || "!$setting_cursor" || "($setting_cursor==1" && "$is_volumetric)} cursor[0] 1 else cursor[0] 0 wxy2,hxy2:=[$area_xy_width,$area_xy_height]+2 repeat $is_volumetric?3:1 { cx,cy:=!$>?[($ix-$area_xy_x0)/$area_xy_dx*$area_xy_width,\ ($iy-$area_xy_y0)/$area_xy_dy*$area_xy_height]:\ $>==1?[($ix-$area_xz_x0)/$area_xz_dx*$area_xz_width,\ ($iz-$area_xz_z0)/$area_xz_dz*$area_xz_height]:\ [($iz-$area_zy_z0)/$area_zy_dz*$area_zy_width,\ ($iy-$area_zy_y0)/$area_zy_dy*$area_zy_height] if !$> w1,h1:=[$area_xy_width,$area_xy_height]-1 line[canvas_view] 0,$cy,$w1,$cy,0.65,0x00FF00FF,0 line[canvas_view] 0,$cy,$w1,$cy,0.65,0xFF00FF00,255 line[canvas_view] $cx,0,$cx,$h1,0.65,0x00FF00FF,0 line[canvas_view] $cx,0,$cx,$h1,0.65,0xFF00FF00,255 elif $>==1 w1,h1:=[$area_xz_width,$area_xz_height]-1 cy+=$hxy2 if $cy>=$hxy2 line[canvas_view] 0,$cy,$w1,$cy,0.65,0x00FF00FF,0 line[canvas_view] 0,$cy,$w1,$cy,0.65,0xFF00FF00,255 fi line[canvas_view] $cx,$hxy2,$cx,100%,0.65,0x00FF00FF,0 line[canvas_view] $cx,$hxy2,$cx,100%,0.65,0xFF00FF00,255 elif $>==2 w1,h1:=[$area_zy_width,$area_zy_height]-1 cx+=$wxy2 line[canvas_view] $wxy2,$cy,100%,$cy,0.65,0x00FF00FF,0 line[canvas_view] $wxy2,$cy,100%,$cy,0.65,0xFF00FF00,255 if $cx>=$wxy2 line[canvas_view] $cx,0,$cx,$h1,0.65,0x00FF00FF,0 line[canvas_view] $cx,0,$cx,$h1,0.65,0xFF00FF00,255 fi fi } fi if $is_area234" && "$is_cursor_over_image repeat $is_volumetric?3:1 { dx,dy:=!$>?[$area_xy_width/$area_xy_dx,$area_xy_height/$area_xy_dy]:\ $>==1?[$area_xz_width/$area_xz_dx,$area_xz_height/$area_xz_dz]:\ [$area_zy_width/$area_zy_dz,$area_zy_height/$area_zy_dy] dl:=max($dx,$dy) if $dl>8 # Draw outline of pixel/voxel under cursor if zoomed enough opacity:=lerp(0,1,min(($dl-8)/10,1)) cx,cy:=!$>?[($rix-$area_xy_x0)/$area_xy_dx*$area_xy_width,\ ($riy-$area_xy_y0)/$area_xy_dy*$area_xy_height]:\ $>==1?[($rix-$area_xz_x0)/$area_xz_dx*$area_xz_width,\ ($riz-$area_xz_z0)/$area_xz_dz*$area_xz_height]:\ [($riz-$area_zy_z0)/$area_zy_dz*$area_zy_width,\ ($riy-$area_zy_y0)/$area_zy_dy*$area_zy_height] ncx,ncy:=$cx+$dx,$cy+$dy cx,cy,ncx,ncy:=ceil([$cx,$cy,$ncx-1,$ncy-1]) if $setting_is_grid cx+=1 cy+=1 fi if $>==1 cy,ncy+=$area_xy_height+2 elif $>==2 cx,ncx+=$area_xy_width+2 fi if $is_volumetric # Crop cursor properly if needed, for volumetric images +z[canvas_view] $cx,$cy,$ncx,$ncy rectangle. 0,0,100%,100%,$opacity,0x33333333,0 rectangle. 0,0,100%,100%,$opacity,0xCCCCCCCC,255 if isin($>,0,1)" && "$ncx>=$area_xy_width x:=w-2-$ncx+$area_xy_width z. 0,0,$x,100% fi if isin($>,0,2)" && "$ncy>=$area_xy_height y:=h-2-$ncy+$area_xy_height z. 0,0,100%,$y fi if $>==1" && "$cy<$area_xy_height+2 y,cy:=a=$area_xy_height+2;[a-$cy,a] if $y==2" && "$cx<$area_xy_width+2 x,cx:=a=$area_xy_width+2;[a-$cx,a] if $x?[($canvas_crop_x0-$area_xy_x0)/$area_xy_dx*$area_xy_width,\ ($canvas_crop_y0-$area_xy_y0)/$area_xy_dy*$area_xy_height]:\ $>==1?[($canvas_crop_x0-$area_xz_x0)/$area_xz_dx*$area_xz_width,\ ($canvas_crop_z0-$area_xz_z0)/$area_xz_dz*$area_xz_height]:\ [($canvas_crop_z0-$area_zy_z0)/$area_zy_dz*$area_zy_width,\ ($canvas_crop_y0-$area_zy_y0)/$area_zy_dy*$area_zy_height] cx1,cy1:=!$>?[($rix-$area_xy_x0)/$area_xy_dx*$area_xy_width,\ ($riy-$area_xy_y0)/$area_xy_dy*$area_xy_height]:\ $>==1?[($rix-$area_xz_x0)/$area_xz_dx*$area_xz_width,\ ($riz-$area_xz_z0)/$area_xz_dz*$area_xz_height]:\ [($riz-$area_zy_z0)/$area_zy_dz*$area_zy_width,\ ($riy-$area_zy_y0)/$area_zy_dy*$area_zy_height] cx1,cy1:=$cx1+$dx,$cy1+$dy cx0,cy0,cx1,cy1:=ceil([$cx0,$cy0,$cx1-1,$cy1-1]) if $>==1 cy0,cy1+=$area_xy_height+2 elif $>==2 cx0,cx1+=$area_xy_width+2 fi rectangle[canvas_view] $cx0,$cy0,$cx1,$cy1,0.5,128 rectangle[canvas_view] $cx0,$cy0,$cx1,$cy1,0.75,0x33333333,0 rectangle[canvas_view] $cx0,$cy0,$cx1,$cy1,0.75,0xCCCCCCCC,255 line[canvas_view] $cx0,$cy0,$cx1,$cy1,0.75,0xF0F0F0F0,0 line[canvas_view] $cx0,$cy0,$cx1,$cy1,0.75,0x0F0F0F0F,255 fi } fi # Draw rectangular selection. if $is_area2" && "narg($canvas_rect_x0)" && "!narg($canvas_crop_x0) x0:=($canvas_rect_x0-$area_xy_x0)/$area_xy_dx*$canvas_width y0:=($canvas_rect_y0-$area_xy_y0)/$area_xy_dy*$canvas_height x1:=($canvas_rect_x1-$area_xy_x0)/$area_xy_dx*$canvas_width y1:=($canvas_rect_y1-$area_xy_y0)/$area_xy_dy*$canvas_height if $x0>$x1 x0,x1=$x1,$x0 fi if $y0>$y1 y0,y1=$y1,$y0 fi x0,y0,x1,y1:=ceil([$x0,$y0,$x1,$y1]) x1+=$canvas_width/$area_xy_dx-1 y1+=$canvas_height/$area_xy_dy-1 if $is_setting_is_grid x0+=1 x1+=1 fi rectangle[canvas_view] $x0,$y0,$x1,$y1,0.5,128 rectangle[canvas_view] $x0,$y0,$x1,$y1,0.75,0x33333333,0 rectangle[canvas_view] $x0,$y0,$x1,$y1,0.75,0xCCCCCCCC,255 dx,dy:=0.5*[$canvas_width,$canvas_height]/[$area_xy_dx,$area_xy_dy] x0,y0,x1,y1+=$dx,$dy,-$dx+1,-$dy+1 line[canvas_view] $x0,$y0,$x1,$y1,0.75,0xF0F0F0F0,0 line[canvas_view] $x0,$y0,$x1,$y1,0.75,0x0F0F0F0F,255 fi fi # Draw area cross button (volumetric images). if $is_volumetric if !narg($canvas_cross_button) 21,21,1,4 circle. 50%,50%,10,1,0,0,0,255 circle. 50%,50%,8,1,200,200,200,255 => canvas_cross_button fi sh[canvas_cross_button] 100% j[canvas_view] [canvas_cross_button],\ {canvas_cross_button,round([$canvas_cross_x*$canvas_width-w/2,$canvas_cross_y*$canvas_height-h/2])},\ 0,0,0.5,.,255 rm. fi # Draw labels. if {canvas_label,w} sh[canvas_label] 100% j[canvas_view] [canvas_label],0,$canvas_label_alignment~,0,0,1,.,255 rm. fi $0_rmn window_view fi # Render global view. if !0$window_view $window_width,$window_height,1,3 => window_view if $thumb_is_visible j[window_view] [thumb_view] fi j[window_view] [canvas_view],0,$thumb_height2 # Record window frames. if $window_record_frame>=0 if !$window_record_frame # First frame: determine filename if $is_opencv repeat 256 { filename_animation=${"filename gmic.mp4,"$>} if !isfile('$filename_animation') break fi } else repeat 256 { filename_animation=${"filename gmic.jpg,"$>,0} if !isfile('$filename_animation') filename_animation=${"filename gmic.jpg",$>} break fi } fi notification="Start recording: "$filename_animation $0_rmn canvas_label_notification fi font_size:=cut(round($canvas_height/40),16,32) if $is_opencv if {window_view,max([w,h]%2)} # One dimension is odd -> resize frame +r[window_view] {[w,h]+([w,h]%2)},1,100%,0 o. $filename_animation,20,0,1 rm. else o[window_view] $filename_animation,20,0,1 fi else o[window_view] ${"filename "$filename_animation,$window_record_frame},75 fi 0 t. "Frame \#"$window_record_frame,0,0,$font_size,1,255 +dilate. 3 !=. 0 r.. 100%,100%,1,3 j[window_view] ..,{window_view,[w,h]-[w#-1,h#-1]-5},0,0,0.75,.,1 rm[-2,-1] window_record_frame+=1 window_no_wait=1 fi if $canvas_label_notification sh[canvas_label_notification] 100% j[window_view] [canvas_label_notification],1~,0,0,0,{min(1,$notification_opacity)},.,255 rm. window_no_wait=1 fi w[window_view] fi if $window_no_wait wait 40 else wait fi # Convert low-levels events to higher-level (buttons / drag). is_CTRL,is_SHIFT:={*,CTRLLEFT}" || "{*,CTRLRIGHT},{*,SHIFTLEFT}" || "{*,SHIFTRIGHT} previous_mouse_x,previous_mouse_y,previous_mouse_area=$mouse_x,$mouse_y,$mouse_area mouse_x,mouse_y,mouse_button={*,x,y,b} mouse_area,canvas_mouse_x,canvas_mouse_y:=" const x = $mouse_x; const y = $mouse_y; const ht = $thumb_height; const ht2 = $thumb_height2; x<0?[0,-1,-1]: # Mouse area = 0: cursor outside window y}" && "$mouse_area" && "!${is_clicked$>} # Possible start of a drag is_clicked$>,is_released$>,is_drag$>=$mouse_area,0,0 # At this point, cannot ensure drag has started drag_start_x$>,drag_start_y$>,drag_end_x$>,drag_end_y$>=$mouse_x,$mouse_y,$mouse_x,$mouse_y elif ${is_button$>}" && "$mouse_area" && "${is_clicked$>} # Possible update of a drag drag_end_x$>,drag_end_y$>=$mouse_x,$mouse_y if !${is_drag$>} # Determine if that is really a drag is_drag$>:=norm(${drag_end_x$>}-${drag_start_x$>},${drag_end_y$>}-${drag_start_y$>})>5?$mouse_area:0 fi elif !${is_button$>}" && "$mouse_area" && "${is_clicked$>} # Possible end of a drag is_clicked$>,is_released$>=0,$mouse_area drag_end_x$>,drag_end_y$>=$mouse_x,$mouse_y # 'is_drag$>' is not reset to 0 to ease drag end detection (must be then zero-ed manually afterwards). elif $mouse_area" && "!${is_clicked$>} is_drag$>=0 fi } # Manage events for thumbnail navigation. previous_selected=$selected if $thumb_is_visible if $is_drag0==1" && "$is_clicked0==1 n_thumb_x:=cut($thumb_x-($drag_end_x0-$drag_start_x0)/$thumb_sumw,-16/$thumb_sumw,$thumb_x_px_max/$thumb_sumw) if $n_thumb_x!=$thumb_x thumb_x=$n_thumb_x drag_start_x0=$drag_end_x0 $0_rmn thumb_view fi elif $is_drag0==1" && "($is_released0" || "!$mouse_area) is_drag0,is_released0=0 fi if $is_area1 canvas_label_alignment=0 ind={thumb_index_map,i[$mouse_x]} if $ind>=0 if $ind!=$thumb_mouse_over thumb_mouse_over=$ind $0_rmn thumb_view,canvas_label_info fi if !$is_drag0" && "$is_released0 if $selected!=$ind selected=$ind $0_rmn thumb_view,canvas_label_info fi fi fi else if $thumb_mouse_over>=0 $0_rmn thumb_view,canvas_label_info fi thumb_mouse_over=-1 fi fi if $nb_images>1 if (($is_area1" || "$is_CTRL)" && "(\ {*,ARROWLEFT}" || "{*,ARROWRIGHT}" || "{*,ARROWUP}" || "{*,ARROWDOWN}" || "\ {*,PAGEUP}" || "{*,PAGEDOWN}" || "{*,HOME}" || "{*,END}" || "{*,H}" || "{*,E}\ ))" || "{*,SPACE}" || "{*,BACKSPACE}" || "{*,B} step:={*,-ARROWLEFT}||{*,-ARROWUP}||{*,BACKSPACE}||{*,B}?($is_SHIFT?-4:-1):\ {*,-ARROWRIGHT}||{*,-ARROWDOWN}||{*,SPACE}?($is_SHIFT?4:1):\ {*,-PAGEUP}?-max(2,round($nb_images/16))*($is_SHIFT?4:1):\ {*,-PAGEDOWN}?max(2,round($nb_images/16))*($is_SHIFT?4:1):\ {*,-HOME}||{*,-H}?-$selected:\ {*,-END}||{*,-E}?$nb_images-1-$selected:0 if {*,-BACKSPACE}" || "{*,-SPACE}" || "{*,-B} selected:=($selected+$step)%$nb_images else selected:=cut($selected+$step,0,$nb_images-1) fi $0_rmn thumb_view,canvas_label_info elif ($is_area1" || "$is_CTRL)" && "\ ({*,0}" || "{*,1}" || "{*,2}" || "{*,3}" || "{*,4}" || "\ {*,5}" || "{*,6}" || "{*,7}" || "{*,8}" || "{*,9}" || "\ {*,PAD0}" || "{*,PAD1}" || "{*,PAD2}" || "{*,PAD3}" || "{*,PAD4}" || "\ {*,PAD5}" || "{*,PAD6}" || "{*,PAD7}" || "{*,PAD8}" || "{*,PAD9}) ind=-1 repeat 10 { if {*,$>}" || "{*,PAD$>} ind=$> break fi } if inrange($ind,0,$nb_images,1,0)" && "$selected!=$ind selected=$ind $0_rmn thumb_view,canvas_label_info skip {*,-PAD$ind},{*,-$ind} fi fi if $is_area1 if {*,o} step:={*,-o}*max(1,round($nb_images/32)) selected:=cut($selected+$step,0,$nb_images-1) $0_rmn thumb_view,canvas_label_info elif !$is_CTRL" && "{*,C} # C: Center thumbnail view selected:=floor($nb_images/2) $0_rmn thumb_view,canvas_label_info fi fi # Do stuffs when new thumbnail has been selected. if $selected!=$previous_selected canvas_crop_x0,canvas_crop_y0,canvas_crop_z0,canvas_rect_x0,canvas_rect_y0,canvas_rect_x1,canvas_rect_y1= drag_start_x0,drag_start_y0,drag_end_x0,drag_end_y0= is_clicked0,is_clicked1,is_clicked2,is_release0,is_release1,is_release2,is_drag0,is_drag1,is_drag2=0 # Ensure that selected thumbnail becomes visible. if $thumb_is_visible x0,w={thumb_coords,[i(0,$selected,0,0),i(0,$selected,0,2)]} if $x0<$thumb_x_px thumb_x:=($x0-48)/$thumb_sumw elif $x0+$w>=$thumb_x_px+$window_width thumb_x:=($x0+$w-$window_width+48)/$thumb_sumw fi fi # Remember view properties for each different image resolution. pw,ph,pd={$previous_selected,i[#$is_rotated,$previous_selected]%2?[h,w,d]:[w,h,d]} w,h,d={$selected,i[#$is_rotated,$selected]%2?[h,w,d]:[w,h,d]} if [$w,$h,$d]!=[$pw,$ph,$pd] whd={$previous_selected,`string($pw,'_',$ph,'_',$pd)`} view_coords_$whd=$view_center_x,$view_center_y,$view_center_z,$view_zoom,$view_base_channel,\ $view_is_rotated,$canvas_cross_x,$canvas_cross_y whd={$selected,`string($w,'_',$h,'_',$d)`} if narg(${view_coords_$whd}) view_center_x,view_center_y,view_center_z,view_zoom,view_base_channel,\ view_is_rotated,canvas_cross_x,canvas_cross_y=${view_coords_$whd} else view_center_x,view_center_y,view_center_z,view_zoom,view_base_channel,\ view_is_rotated,canvas_cross_x,canvas_cross_y=0.5,0.5,0.5,1,0,0,, fi fi $0_rmn canvas_volumetric_background,canvas_base,area_3d_mesh fi fi # Manage events for image exploration and visualization. if {$selected,w" && "($is_volumetric" || "!$is_view3d)} zoom_1_1,zoom_min,zoom_max={$selected," const mw4 = max(w,4); const mh4 = max(h,4); const md4 = max(d,4); const M = max(mw4/$area_xy_width,mh4/$area_xy_height); [ M,min(M,0.25),M*max($area_xy_width,$area_xy_height,$area_xz_height,$area_zy_width)/min(max(w,h,d),12) ]"} pan_x_min,pan_x_max,pan_y_min,pan_y_max,pan_z_min,pan_z_max={$selected," const mw4 = max(w,4); const mh4 = max(h,4); const md4 = max(d,4); const delta = 0.5/$view_zoom*min(mw4/$area_xy_width,mh4/$area_xy_height,md4/$area_xz_height,md4/$area_zy_width); const delta_x = ceil(delta*$area_xy_width/w); const delta_y = ceil(delta*$area_xy_height/h); const delta_z = ceil(delta*min($area_xz_height,$area_zy_width)/d); [ w==1?[ 0.5,0.5 ]:[ min(0,1 - delta_x), max(1,delta_x) ], h==1?[ 0.5,0.5 ]:[ min(0,1 - delta_y), max(1,delta_y) ], d==1?[ 0.5,0.5 ]:[ min(0,1 - delta_z), max(1,delta_z) ] ]"} # Image pan, using LMB or MMB (drag). repeat 2 { but:=2*$> if !$but" && "$is_CTRL continue fi if isin($event_type0,0,1)" && "inrange(${is_drag$but},2,4)" && "inrange(${is_clicked$but},2,4) # Pan starts drag_dx,drag_dy:=[${drag_end_x$but},${drag_end_y$but}]-[${drag_start_x$but},${drag_start_y$but}] ar=${is_drag$but} if $ar==2 view_center_x-=$drag_dx/$area_width*$area_dx/w#$selected view_center_y-=$drag_dy/$area_height*$area_dy/h#$selected elif $ar==3 view_center_x-=$drag_dx/$area_width*$area_dx/w#$selected view_center_z-=$drag_dy/$area_height*$area_dy/d#$selected elif $ar==4 view_center_z-=$drag_dx/$area_width*$area_dx/d#$selected view_center_y-=$drag_dy/$area_height*$area_dy/h#$selected fi view_center_x,view_center_y,view_center_z:=\ [cut($view_center_x,$pan_x_min,$pan_x_max),\ cut($view_center_y,$pan_y_min,$pan_y_max),\ cut($view_center_z,$pan_z_min,$pan_z_max)] drag_start_x$but,drag_start_y$but=${drag_end_x$but},${drag_end_y$but} event_type0=1 $0_rmn canvas_base,area_3d_mesh elif $event_type0==1" && "${is_drag$but}==2" && "${is_released$but} # Pan ends event_type0,is_drag0,is_released0,is_drag2,is_released2=0 fi } # Rectangular selection, using CTRL+LMB (drag). if !$is_volumetric" && "$is_CTRL" && "$mouse_x>=0 if isin($event_type0,0,2)" && "$is_drag0==2" && "$is_clicked0==2" && "!narg($canvas_crop_x0) # Selection starts if !narg($canvas_rect_x0) x,y=$drag_start_x0,$drag_start_y0 else x,y=$drag_end_x0,$drag_end_y0 fi cx,cy={$selected,[cut(floor(lerp($area_xy_x0,$area_xy_x1,$x/$canvas_width)),0,w-1),\ cut(floor(lerp($area_xy_y0,$area_xy_y1,($y-$thumb_height2)/$canvas_height)),0,h-1)]} if !narg($canvas_rect_x0) canvas_rect_x0,canvas_rect_y0,canvas_rect_x1,canvas_rect_y1=$cx,$cy,$cx,$cy $0_rmn canvas_label_coords elif [$cx,$cy]!=[$canvas_rect_x1,$canvas_rect_y1] canvas_rect_x1,canvas_rect_y1=$cx,$cy $0_rmn canvas_label_coords fi n_view_center_x,n_view_center_y=$view_center_x,$view_center_y if $canvas_mouse_x<64 n_view_center_x-={$selected,lerp(128,8,$canvas_mouse_x/64)/$view_zoom/w} elif $canvas_mouse_x>=$canvas_width-64 n_view_center_x+={$selected,lerp(128,8,($canvas_width-$canvas_mouse_x)/64)/$view_zoom/w} fi if $canvas_mouse_y<64 n_view_center_y-={$selected,lerp(128,8,$canvas_mouse_y/64)/$view_zoom/h} elif $canvas_mouse_y>=$canvas_height-64 n_view_center_y+={$selected,lerp(128,8,($canvas_height-$canvas_mouse_y)/64)/$view_zoom/h} fi n_view_center_x,n_view_center_y:=\ [cut($n_view_center_x,$pan_x_min,$pan_x_max),\ cut($n_view_center_y,$pan_y_min,$pan_y_max)] if [$n_view_center_x,$n_view_center_y]!=[$view_center_x,$view_center_y] view_center_x,view_center_y=$n_view_center_x,$n_view_center_y is_rect_scroll=1 $0_rmn canvas_base else is_rect_scroll=0 fi event_type0=2 fi fi if $event_type0==2" && "$is_drag0==2" && "$is_released0" && "!narg($canvas_crop_x0) # Selection ends if $canvas_rect_x0>$canvas_rect_x1 canvas_rect_x0,canvas_rect_x1=$canvas_rect_x1,$canvas_rect_x0 fi if $canvas_rect_y0>$canvas_rect_y1 canvas_rect_y0,canvas_rect_y1=$canvas_rect_y1,$canvas_rect_y0 fi if max($canvas_rect_x1-$canvas_rect_x0,$canvas_rect_y1-$canvas_rect_y0)>8 view_center_x,view_center_y,view_zoom={$selected," const wc = $canvas_width; const hc = $canvas_height; const f = 0.5*max(w/wc,h/hc); [ cut(0.5*($canvas_rect_x0 + $canvas_rect_x1)/w,0,1), cut(0.5*($canvas_rect_y0 + $canvas_rect_y1)/h,0,1), 2*f*min(wc/max(8,$canvas_rect_x1 - $canvas_rect_x0), hc/max(8,$canvas_rect_y1 - $canvas_rect_y0)) ]"} $0_rmn canvas_base fi canvas_rect_x0,canvas_rect_y0,canvas_rect_x1,canvas_rect_y1= event_type0,is_drag0,is_released0=0 $0_rmn canvas_label_coords fi # Image zoom, using RMB (drag). if inrange($is_drag1,2,4)" && "inrange($is_clicked1,2,4) # Zoom starts amount:=" dx = $drag_end_x1 - $drag_start_x1; dy = $drag_end_y1 - $drag_start_y1; sign(dx)*norm(dx,dy)/200" if !narg($zoom_focus_x) zoom_focus_x,zoom_focus_y=$area_mouse_x,$area_mouse_y fi n_view_zoom:=cut($view_zoom*(1+$amount),$zoom_min,$zoom_max) ratio:=$view_zoom/$n_view_zoom if $n_view_zoom!=$view_zoom if !$is_volumetric if $is_drag1==2 xc:=lerp($area_xy_x0,$area_xy_x1,$zoom_focus_x/$area_width) yc:=lerp($area_xy_y0,$area_xy_y1,$zoom_focus_y/$area_height) view_center_x:=cut(lerp($xc,$view_center_x*w#$selected,$ratio)/w#$selected,$pan_x_min,$pan_x_max) view_center_y:=cut(lerp($yc,$view_center_y*h#$selected,$ratio)/h#$selected,$pan_y_min,$pan_y_max) elif $is_drag1==3 xc:=lerp($area_xz_x0,$area_xz_x1,$zoom_focus_x/$area_width) zc:=lerp($area_xz_z0,$area_xz_z1,$zoom_focus_y/$area_height) view_center_x:=cut(lerp($xc,$view_center_x*w#$selected,$ratio)/w#$selected,$pan_x_min,$pan_x_max) view_center_z:=cut(lerp($zc,$view_center_z*d#$selected,$ratio)/d#$selected,$pan_z_min,$pan_z_max) elif $is_drag1==4 zc:=lerp($area_zy_z0,$area_zy_z1,$zoom_focus_x/$area_width) yc:=lerp($area_zy_y0,$area_zy_y1,$zoom_focus_y/$area_height) view_center_z:=cut(lerp($zc,$view_center_z*d#$selected,$ratio)/d#$selected,$pan_z_min,$pan_z_max) view_center_y:=cut(lerp($yc,$view_center_y*h#$selected,$ratio)/h#$selected,$pan_y_min,$pan_y_max) fi fi view_zoom=$n_view_zoom $0_rmn canvas_base fi drag_start_x1,drag_start_y1=$drag_end_x1,$drag_end_y1 elif inrange($is_drag1,2,5)" && "$is_released1 # Zoom ends zoom_focus_x,zoom_focus_y= is_drag1,is_released1=0 fi # Volumetric images: Area cross drag, with LMB. if $is_volumetric if $is_clicked0>=2" && "$canvas_mouse_x>=0" && "(\ $event_type0==3" || "(!$event_type0" && "narg($canvas_cross_x)" && "\ norm($canvas_mouse_x-$canvas_cross_x*$canvas_width,\ $canvas_mouse_y-$canvas_cross_y*$canvas_height)<16)) is_drag0=$mouse_area # Button clicked -> activate drag (avoid drag latency) canvas_cross_x,canvas_cross_y:=cut($canvas_mouse_x/$canvas_width,0,1),\ cut($canvas_mouse_y/$canvas_height,0,1) drag_start_x0,drag_start_y0=$drag_end_x0,$drag_end_y0 event_type0=3 $0_rmn canvas_volumetric_background,canvas_base,area_3d_view elif $event_type0==3" && "$is_drag0>=2" && "$is_released1 # Drag ends event_type0,is_drag0,is_released0=0 fi fi if $event_type0" && "!$is_clicked0" && "!$is_released0" && "!$is_drag0 event_type0=0 $0_rmn canvas_view fi if $is_area234 if {*,o}" || "({*,PADSUB}" || "{*,PADADD}) if $is_CTRL" && "$is_volumetric # Pan volumetric image along orthogonal axis amount:={*,PADADD}?1:{*,PADSUB}?-1:{*,-o} view_center_x,view_center_y,view_center_z={$selected," const cx = $view_center_x; const cy = $view_center_y; const cz = $view_center_z; const amount = $amount; P = $is_area2?[ cx,cy,cz + amount/d ]: $is_area3?[ cx,cy + amount/d,cz ]: [ cx + amount/w,cy,cz ]; [ cut(P[0],$pan_x_min,$pan_x_max), cut(P[1],$pan_y_min,$pan_y_max), cut(P[2],$pan_z_min,$pan_z_max) ]"} $0_rmn canvas_base,area_3d_mesh else # Zoom image amount:={*,PADADD}?0.25:{*,PADSUB}?-0.25:{*,-o}/5 n_view_zoom:=cut($view_zoom*(1+$amount),$zoom_min,$zoom_max) ratio:=$view_zoom/$n_view_zoom if $n_view_zoom!=$view_zoom xc,yc,zc={$selected,"$is_volumetric?( $is_area2?[ ($area_xy_x0 + $area_xy_x1)/2, ($area_xy_y0 + $area_xy_y1)/2, $view_center_z*d ]: $is_area3?[ ($area_xz_x0 + $area_xz_x1)/2, $view_center_y*h, ($area_xz_z0 + $area_xz_z1)/2 ]: [ $view_center_x*w, ($area_zy_y0 + $area_zy_y1)/2, ($area_zy_z0 + $area_zy_z1)/2 ]): [ lerp($area_xy_x0,$area_xy_x1,$area_mouse_x/$area_width), lerp($area_xy_y0,$area_xy_y1,$area_mouse_y/$area_height), 0 ]"} view_center_x:=cut(lerp($xc,$view_center_x*w#$selected,$ratio)/w#$selected,$pan_x_min,$pan_x_max) view_center_y:=cut(lerp($yc,$view_center_y*h#$selected,$ratio)/h#$selected,$pan_y_min,$pan_y_max) view_center_z:=cut(lerp($zc,$view_center_z*d#$selected,$ratio)/d#$selected,$pan_z_min,$pan_z_max) view_zoom=$n_view_zoom $0_rmn canvas_base fi fi fi if !$is_CTRL if ({*,0}" || "{*,1}" || "{*,2}" || "{*,3}" || "{*,4}" || "\ {*,5}" || "{*,6}" || "{*,7}" || "{*,8}" || "{*,9}" || "\ {*,PAD0}" || "{*,PAD1}" || "{*,PAD2}" || "{*,PAD3}" || "{*,PAD4}" || "\ {*,PAD5}" || "{*,PAD6}" || "{*,PAD7}" || "{*,PAD8}" || "{*,PAD9}) ind=-1 repeat 10 { if {*,$>}" || "{*,PAD$>} ind=$> break fi } if $ind>=0 view_zoom_mode:=($ind-1)%10 n_view_zoom:=cut(arg0($view_zoom_mode,$zoom_1_1,2*$zoom_1_1,4*$zoom_1_1,\ 1,2,4,$zoom_max/8,$zoom_max/4,$zoom_max/2,$zoom_max),$zoom_min,$zoom_max) if $n_view_zoom!=$view_zoom view_zoom=$n_view_zoom $0_rmn canvas_base fi notification="Zoom: "${"arg0 "$view_zoom_mode,"1:1,2:1,4:1,x1,x2,x4,Max/8,Max/4,Max/2,Max"} $0_rmn canvas_label_notification skip {*,-PAD$ind},{*,-$ind} fi elif {*,-END} view_center_x,view_center_y,view_center_z={$selected,1-0.5/[w,h,d]} $0_rmn canvas_base,area_3d_mesh elif {*,-HOME} view_center_x,view_center_y,view_center_z={$selected,0.5/[w,h,d]} $0_rmn canvas_base,area_3d_mesh elif {*,-K} # K: Switch background setting_background:={$setting_background+1}%10 str=${"arg0 "$setting_background>>1,"Black,\"Dark gray\",Gray,\"Light gray\",White"} if $setting_background&1 str.=" checkerboard" fi notification="Background: "$str $0_rmn canvas_background,canvas_label_notification elif {*,-P}" && "['$view_xyz']!=0 # Print current pixel value on stdout +e "Command 'display': I(#"$selected,$view_xyz_disp") = [ "{$selected,I($view_xyz)}" ]" elif ({*,PAGEUP}" || "{*,PAGEDOWN})" && "{$selected,s>3}" && "!$setting_alpha n_view_base_channel={$selected,cut($view_base_channel+({*,-PAGEUP}?1:{*,-PAGEDOWN}?-1:0),0,s-3)} if $n_view_base_channel!=$view_base_channel view_base_channel=$n_view_base_channel notification="Base channel: "$view_base_channel $0_rmn canvas_base,canvas_label_notification fi elif $is_area234" && "({*,ARROWLEFT}" || "{*,ARROWRIGHT}" || "{*,ARROWUP}" || "{*,ARROWDOWN}) step:=$is_volumetric" || "$view_zoom>=$zoom_max/4?1:($area_xy_dx+$area_xy_dy)/40 dx,dy:={*,ARROWLEFT}?-$step:{*,ARROWRIGHT}?$step:0,\ {*,ARROWUP}?-$step:{*,ARROWDOWN}?$step:0 if $is_SHIFT dx,dy*=4 fi if $is_area2 dx,dy,dz={$selected,[$dx/w,$dy/h,0]} elif $is_area3 dx,dy,dz={$selected,[$dx/w,0,$dy/d]} else dx,dy,dz={$selected,[0,$dy/h,$dx/d]} fi view_center_x:=cut($view_center_x+$dx,$pan_x_min,$pan_x_max) view_center_y:=cut($view_center_y+$dy,$pan_y_min,$pan_y_max) view_center_z:=cut($view_center_z+$dz,$pan_z_min,$pan_z_max) $0_rmn canvas_base,area_3d_mesh fi fi if [$mouse_x,$mouse_y]!=[$previous_mouse_x,$previous_mouse_y]" && "$is_area234 $0_rmn canvas_label_coords fi elif inrange($previous_mouse_area,2,4) $0_rmn canvas_label_coords fi if {*,-A} # A: Switch alpha mode setting_alpha:=($setting_alpha+1)%5 notification="Alpha: "${"arg0 "$setting_alpha,"Off,On,\"Over black\",\"Over gray\",\"Over white\""} $0_flush_thumb_cache $0_rmn canvas_base,canvas_label_notification elif {*,-G} # G: Toggle grid setting_is_grid:=!$setting_is_grid notification="Grid: "${"arg0 "$setting_is_grid,"Off,On"} $0_rmn canvas_base,canvas_label_notification elif {*,-H} # H: Go upper-left view_center_x,view_center_y,view_center_z={$selected,0.5/[w,h,d]} $0_rmn canvas_base,area_3d_mesh elif {*,-E} # E: Go lower-right view_center_x,view_center_y,view_center_z={$selected,1-0.5/[w,h,d]} $0_rmn canvas_base,area_3d_mesh elif {*,-N} # N: Switch normalization mode setting_normalization:=($setting_normalization+1)%5 notification="Normalization: "${"arg0 "$setting_normalization,\ "Off,Cut,\"Stretch (channelwise)\",\ \"Stretch (global)\",\"Stretch (global once)\""} $0_flush_thumb_cache $0_rmn canvas_base,canvas_label_notification elif {*,-R} # Rotate view view_is_rotated:=($view_is_rotated+1)%4 notification="Angle: "{$view_is_rotated*90}\260 $0_rmn canvas_base,canvas_label_info,canvas_label_notification,area_3d_mesh elif {*,-V} # Crop image x,y,z=$view_xyz if !narg($canvas_crop_x0) canvas_crop_x0,canvas_crop_y0,canvas_crop_z0=$x,$y,$z notification="Start crop" else x0,y0,z0,x1,y1,z1:=" x0 = $canvas_crop_x0; y0 = $canvas_crop_y0; z0 = $canvas_crop_z0; x1 = $x; y1 = $y; z1 = $z; x0>x1?swap(x0,x1); y0>y1?swap(y0,y1); z0>z1?swap(z0,z1); [ x0,y0,z0,x1,y1,z1 ]" notification="End crop" z[$selected] $x0,$y0,$z0,$x1,$y1,$z1 canvas_crop_x0,canvas_crop_y0,canvas_crop_z0= view_center_x,view_center_y,view_center_z,view_zoom,view_3d_zoom=0.5,0.5,0.5,1,1 if $is_volumetric $0_rmn area_3d_mesh fi =[thumb_is_cached] 0,0,$selected $0_rmn thumb_view,canvas_base,canvas_label_info fi $0_rmn canvas_label_notification elif !$is_CTRL" && "{*,X}" && "$is_volumetric # Reset area cross position canvas_cross_x,canvas_cross_y= $0_rmn canvas_volumetric_background,canvas_base,area_3d_view skip {*,-X} elif {*,-Z} # Switch zoom factor (1:1,2:1,4:1,x1,x2,x4,max/8,max/4,max/2,max) view_zoom_mode:=($view_zoom_mode+1)%10 n_view_zoom:=cut(arg0($view_zoom_mode,$zoom_1_1,2*$zoom_1_1,4*$zoom_1_1,\ 1,2,4,$zoom_max/8,$zoom_max/4,$zoom_max/2,$zoom_max),$zoom_min,$zoom_max) if $n_view_zoom!=$view_zoom view_zoom=$n_view_zoom $0_rmn canvas_base fi notification="Zoom: "${"arg0 "$view_zoom_mode,"1:1,2:1,4:1,x1,x2,x4,Max/8,Max/4,Max/2,Max"} $0_rmn canvas_label_notification fi fi if {canvas_label,($canvas_mouse_x5*h)} canvas_label_alignment:=!$canvas_label_alignment $0_rmn canvas_view fi # Manage events for 3D mesh visualization. if $is_view3d # 3D mesh rotation, using LMB (drag). if !$is_CTRL" && "isin($event_type0,0,4)" && "$is_drag0==5" && "$is_clicked0==5 # 3D rotation starts eval " const w2 = $area_3d_width/2; const h2 = $area_3d_height/2; const radius = 0.75*min(w2,h2); const offx = $area_xy_width + ($is_volumetric?2:0); const offy = $thumb_height2 + $area_xy_height + ($is_volumetric?2:0); const u0 = $drag_start_x0 - offx - w2; const v0 = $drag_start_y0 - offy - h2; const u1 = $drag_end_x0 - offx - w2; const v1 = $drag_end_y0 - offy - h2; n0 = norm(u0,v0); nu0 = n0>radius?u0*radius/n0:u0; nv0 = n0>radius?v0*radius/n0:v0; nw0 = sqrt(max(0,radius^2 - nu0^2 - nv0^2)); n1 = norm(u1,v1); nu1 = n1>radius?u1*radius/n1:u1; nv1 = n1>radius?v1*radius/n1:v1; nw1 = sqrt(max(0,radius^2 - nu1^2 - nv1^2)); u = nv0*nw1 - nw0*nv1; v = nw0*nu1 - nu0*nw1; w = nv0*nu1 - nu0*nv1; n = norm(u,v,w); R = rot(u,v,w,-asin(n/radius^2)); R = mul(R,crop(#$view_3d_pose,0,0,3,3),3); draw(#$view_3d_pose,R,0,0,3,3)" drag_start_x0,drag_start_y0=$drag_end_x0,$drag_end_y0 $0_rmn area_3d_view area_3d_is_motion=1 event_type0=4 elif $event_type0==4" && "$is_drag0==5" && "$is_released0 # 3D rotation ends event_type0,is_drag0,is_released0=0 fi # 3D mesh pan, using CTRL-LMB or MMB (drag). pan_3d_x_min,pan_3d_x_max,pan_3d_y_min,pan_3d_y_max:=" const w = $area_3d_width; const h = $area_3d_height; const mwh = min(w,h); [ -0.47*$view_3d_zoom, 0.47*$view_3d_zoom, -0.47*$view_3d_zoom, 0.47*$view_3d_zoom ]" repeat 2 { but:=2*$> if !$but" && "!$is_CTRL continue fi if isin($event_type0,0,5)" && "${is_drag$but}==5" && "${is_clicked$but}==5 # Pan starts drag_dx,drag_dy:=[${drag_end_x$but},${drag_end_y$but}]-[${drag_start_x$but},${drag_start_y$but}] eval " const mwh = min($area_3d_width,$area_3d_height); i[#$view_3d_pose,3] = cut(i[#$view_3d_pose,3] + $drag_dx/mwh,$pan_3d_x_min,$pan_3d_x_max); i[#$view_3d_pose,7] = cut(i[#$view_3d_pose,7] + $drag_dy/mwh,$pan_3d_y_min,$pan_3d_y_max)" drag_start_x$but,drag_start_y$but=${drag_end_x$but},${drag_end_y$but} area_3d_is_motion=1 event_type0=5 $0_rmn area_3d_view elif $event_type0==5" && "${is_drag$but}==5" && "${is_released$but} # Pan ends event_type0,is_drag0,is_released0,is_drag2,is_released2=0 fi } # 3D mesh zoom, using RMB (drag) or MW. zoom_3d_min,zoom_3d_max=0.1,10 if {*,o}" || "($is_drag1==5" && "$is_clicked1==5)" || "{*,PADADD}" || "{*,PADSUB} # 3D zoom starts amount:="const mw = "{*,-o}"; mw?mw: "{*,-PADADD}"?3: "{*,-PADSUB}"?-3:( const dx = $drag_end_x1 - $drag_start_x1; const dy = $drag_end_y1 - $drag_start_y1; sign(dx)*norm(dx,dy)/10)" n_view_3d_zoom:=cut($view_3d_zoom*1.05^$amount,$zoom_3d_min,$zoom_3d_max) if $n_view_3d_zoom!=$view_3d_zoom eval " const mwh = min($area_3d_width,$area_3d_height); const mx = $is_volumetric?0:$area_mouse_x - $area_width/2; const my = $is_volumetric?0:$area_mouse_y - $area_height/2; tx = i[#$view_3d_pose,3]; ty = i[#$view_3d_pose,7]; x = (mx/mwh - tx)/$view_3d_zoom; y = (my/mwh - ty)/$view_3d_zoom; const dalpha = $view_3d_zoom - $n_view_3d_zoom; i[#$view_3d_pose,3] = cut(dalpha*x + tx,$pan_3d_x_min,$pan_3d_x_max); i[#$view_3d_pose,7] = cut(dalpha*y + ty,$pan_3d_x_min,$pan_3d_x_max)" drag_start_x1,drag_start_y1=$drag_end_x1,$drag_end_y1 view_3d_zoom=$n_view_3d_zoom area_3d_is_motion=1 $0_rmn area_3d_view fi elif $is_drag1==5" && "$is_released0 # 3D zoom ends event_type0,is_drag0,is_released0,is_drag1,is_released2 fi # 3D mesh pan with ARROW keys. if $is_area5" && "({*,ARROWLEFT}" || "{*,ARROWRIGHT}" || "{*,ARROWUP}" || "{*,ARROWDOWN}) eval " const stepx = ($pan_3d_x_max - $pan_3d_x_min)/30; const stepy = ($pan_3d_y_max - $pan_3d_y_min)/30; dx = "{*,-ARROWLEFT}"?-stepx:"{*,-ARROWRIGHT}"?stepx:0; dy = "{*,-ARROWUP}"?-stepy:"{*,-ARROWDOWN}"?stepy:0; $is_SHIFT?(dx*=2; dy*=2); x = cut(i[#$view_3d_pose,3] + dx,$pan_3d_x_min,$pan_3d_x_max); y = cut(i[#$view_3d_pose,7] + dy,$pan_3d_y_min,$pan_3d_y_max); i[#$view_3d_pose,3] = x; i[#$view_3d_pose,7] = y" $0_rmn area_3d_view fi if {*,-K} # K: Switch 3D background setting_3d_background:={$setting_3d_background+1}%12 notification="3D background: \#"$setting_3d_background $0_rmn canvas_3d_background,area_3d_view,canvas_label_notification elif {*,-J} # J: Toggle 3D animation setting_3d_animation:=($setting_3d_animation+1)%3 notification="3D animation: "${"arg0 "$setting_3d_animation,Off,Forward,Backward} $0_rmn canvas_label_notification elif {*,-U} # U: Switch 3D animation mode setting_3d_animation_mode:=($setting_3d_animation_mode+1)%16 if $setting_3d_animation_mode<12 str={`_'X'+int($setting_3d_animation_mode/4)`}-axis else str=XYZ-axes fi str.=" ("${"arg0 "{$setting_3d_animation_mode%4},slow,normal,fast,fast+}")" notification="3D animation mode: "$str $0_rmn canvas_label_notification fi if !$is_volumetric if {*,-A} # A: Toggle 3D axes setting_3d_is_axes:=!$setting_3d_is_axes notification="Axes: "${"arg0 "$setting_3d_is_axes,Off,On} $0_rmn area_3d_view,canvas_label_notification elif !$is_CTRL" && "{*,D} # D: Switch 3D side mode setting_3d_side_mode:={($setting_3d_side_mode+1)%3} notification="3D side mode: "${"arg0 "$setting_3d_side_mode,\ "Single-sided,Double-sided,\"Single-sided (flipped)\""} $0_rmn area_3d_view,canvas_label_notification skip {*,-D} elif !$is_CTRL" && "{*,F} # F: Change 3D focale setting_3d_focale:="_const f = $setting_3d_focale; lof = [ 0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,1,1.5,2,3,4,6,8,10 ]; for (k = 0, k} if !isfile('$filename') break fi } if {*,-L} f[is_rotated] "i?run('rotate[',y,'] ',-90*i); 0" foreach[^$nb_images--1] { => ${image_name$>} } # Set back original images names o[^$nb_images--1] $filename foreach[^$nb_images--1] { => _display$> } notification="Saved list copy: "$filename elif {*,-O} ang={is_rotated,i[$selected]} if $ang rotate[$selected] {-90*$ang} =[is_rotated] 0,0,$selected fi =>[$selected] ${image_name$selected} o[$selected] $filename =>[$selected] _display$selected notification="Saved image copy: "$filename fi $0_rmn canvas_label_notification elif {*,-S}" && "0$canvas_base # CTRL+S: Save screenshot repeat 256 { filename=${"filename gmic.png,"$>} if !isfile('$filename') break fi } notification="Save screenshot: "$filename $0_rmn canvas_label_notification o[canvas_base] $filename elif $is_CTRL" && "{*,-W} # CTRL+W: Start/stop window recording if $window_record_frame<0 # Start recording window_record_frame=0 else # Stop recording if $is_opencv o[] $filename_animation,0,0,0 fi # Close video stream notification="Stop recording (\#"$window_record_frame" frames)" window_record_frame=-1 $0_rmn canvas_label_notification fi $0_rmn area_3d_view elif {*,-X} # CTRL+X: Toggle cursor mode setting_cursor:=a=($setting_cursor+1)%3;$is_volumetric?(a?2:0):(a==1) notification="Cursor: "${"arg0 "$setting_cursor,"Off,\"On (2D)\",\"On (3D)\""} $0_rmn canvas_view,canvas_label_notification fi fi if $nb_images>1" && "{*,-TAB} # Hide/show thumbnails thumb_is_visible:=!$thumb_is_visible area_mouse_x,area_mouse_y=-1 $0_rmn thumb_view,canvas_base,area_3d_view fi if !$is_clicked0 event_type0=0 fi if $is_released0 is_released0=0 fi if $canvas_label_notification notification_opacity-=0.1 if $notification_opacity<=0 rm[canvas_label_notification] notification_opacity=0 fi if $notification_opacity<1 $0_rmn window_view fi fi } # Return current 3D pose and zoom. if 0$view_3d_pose _display_3d_pose={view_3d_pose,^} fi if narg($view_3d_zoom) _display_3d_zoom=$view_3d_zoom fi # Reset image list and display window to their previous states. if $is_opencv" && "$window_record_frame>=0 o[] $filename_animation,0,0,0 fi # Close recording stream f[is_rotated] "i?run('rotate[',y,'] ',-90*i); 0" # Rotate back image to their original angle if narg($window_previous_normalization) w[] -1,-1,$window_previous_normalization,$window_previous_fullscreen else w[] 0 fi rm[$nb_images--1] foreach { => ${image_name$>} } # Set back original images names um "$0_rmn" is_change 0 # Flush thumb cache: discard all images except 3D mesh rendering. _display_flush_thumb_cache : ==[thumb_is_cached] 2 *[thumb_is_cached] 2 _display_rmn thumb_view # Return default window size for selected image list. # $1 : thumb_height_factor # $2 : thumb_min_ratio _display_default_window_size : u {"const fmax = 0.75; const u = min(1280,fmax*"{*,u}"); const v = min(1280,fmax*"{*,v}"); const muv = min(u,v); CImg3d = ['CImg3d']; Mw = Mh = 0; repeat (l,k, w = w#k; h = h#k; is_3d = w==1 && h>6 && d#k==1 && s#k==1 && int(crop(#k,0,0,0,0,1,6,1,1))==CImg3d; !w || is_3d?(nw = 1.25*muv; nh = muv):( d>1?(w+=d; h+=d); fact = min(u/w,v/h); Mwh = max(w,h); Mwh<16?(f = lerp(2,1,Mwh/16); w/=f; h/=f); nw = ceil(fact*w); nh = ceil(fact*h) ); nw>Mw?(Mw = nw); nh>Mh?(Mh = nh); ); Mwh = max(Mw,Mh); Mw = Mwh*max($2,Mw/Mwh); Mh = Mwh*max($2,Mh/Mwh); l>1?(Mh = (Mh + 2)/(1 - $1)): !w#0?(Mw/=2; Mh/=2); ceil([ Mw,Mh ])"} # Return 1 or 0 if one of the 3 first channels of the selected image contains 'inf' or 'NaN' values. _display_is_infnan : if w sh 0,{min(s-1,2)} u {s=is;isinf(s)||isnan(s)} rm. else u 0 fi # Return non-infnan min and max values of the selected image. _display_minmax_infnan : eval. " begin( val_min = inf; val_max = -inf ); val = i; !isinf(val)?( valval_max?(val_max = val) ); end( merge(val_min,min); merge(val_max,max); set('{}',v2s([val_min,val_max])); )" # Normalize selected image without considering inf and NaN values. # Optional arguments: min-max values. _display_normalize_infnan : skip "${1=none},${2=none}" is_minmax:="isnum($1) && isnum($2)" if !$is_minmax noarg fi foreach { if $is_minmax val_min,val_max:=$1,$2 else val_min,val_max=${-_display_minmax_infnan} fi f " const min = $val_min; const max = $val_max; const delta = max - min; const epsilon = delta>0?delta:1; const nmin = min - epsilon; const nmax = max + epsilon; const ndelta = nmax - nmin; const nfact = 255/(ndelta>0?ndelta:1); val = i; isinf(val)?(val<0?0:255):isnan(val)?0:(val - nmin)*nfact" } #@cli d0 : eq. to 'display0'. d0 : _gmic_s="$?" prev_display_normalization=$_display_normalization prev_display_alpha=$_display_alpha _display_normalization=0 _display_alpha=1 v + _display "",1,$[] v - _display_normalization=$prev_display_normalization _display_alpha=$prev_display_alpha +d0 : prev_display_normalization=$_display_normalization prev_display_alpha=$_display_alpha _display_normalization=0 _display_alpha=1 _gmic_s="$?" v + _display +,1,$[] v - _display_normalization=$prev_display_normalization _display_alpha=$prev_display_alpha #@cli display0 #@cli : Display selected images in an interactive window, without normalization and alpha mode activated. display0 : prev_display_normalization=$_display_normalization prev_display_alpha=$_display_alpha _display_normalization=0 _display_alpha=1 _gmic_s="$?" v + _display "",1,$[] v - _display_normalization=$prev_display_normalization _display_alpha=$prev_display_alpha +display0 : prev_display_normalization=$_display_normalization prev_display_alpha=$_display_alpha _display_normalization=0 _display_alpha=1 _gmic_s="$?" v + _display +,1,$[] v - _display_normalization=$prev_display_normalization _display_alpha=$prev_display_alpha #@cli da : eq. to 'display_array'. da : _gmic_s="$?" v + _display_array $* #@cli display_array : _width>0,_height>0 #@cli : Display images in interactive windows where pixel neighborhoods can be explored. #@cli : Default values: 'width=13' and 'height=width'. display_array : _gmic_s="$?" v + _$0 $* _display_array : check ${1=13}>0" && "${2=$1}>0 e[0--3] "Display $1x$2 array of pixel values for image"$_gmic_s"." dxb:=round($1/2,1,1) dxf:=$1-1-$dxb dyb:=round($2/2,1,1) dyf:=$2-1-$dyb foreach { if w<128" && "h<128 r 128,128,100%,100%,0,0,0.5,0.5 fi # Manage cases of small and large images. x0,y0,w,h=0,0,{w},{h} wmax:=0.9*{*,u} hmax:=0.9*{*,v} do if w>=$wmax" || "h>=$hmax n={n} => "Image "'{b}.{x}'" is too large, please select a sub-image." +select. 2 x0,y0,w,h:=i[0],i[1],1+i[3]-i[0],1+i[4]-i[1] rm. => $n fi +z. $x0,$y0,0,{$x0+$w-1},{$y0+$h-1},0 round. 1 n. 0,255 while w>=$wmax" || "h>=$hmax x1=-1 y1=-1 c1=0 ox1=-1 oy1=-1 oc1=-1 x2=-1 y2=-1 c2=0 ox2=-1 oy2=-1 oc2=-1 x3=-1 y3=-1 c3=0 ox3=-1 oy3=-1 oc3=-1 c0=0 oxm=-1 oym=-1 w. -1,-1,0,0,{-2,b}.{-2,x} do # Enter event loop. # Manage user interactions. wait[0-3] oc0=$c0 repeat 4 { if $>" && "!{*$>}" && "${x$>}>=0 w$> 0 x$>=-1 y$>=-1 c$>=0 fi if {*$>,o} c$>:=(${c$>}+sign({*$>,o}))%s wait[$>] -1 fi if {*$>,SPACE}" || "{*$>,ENTER}" || "{*$>,ARROWRIGHT}" || "{*$>,ARROWDOWN} c$>:=(${c$>}+1)%s wait[$>] -1 fi if {*$>,BACKSPACE}" || "{*$>,ARROWLEFT}" || "{*$>,ARROWUP} c$>:=(${c$>}-1)%s wait[$>] -1 fi } if $oc0!=$c0 c1=$c0 c2=$c0 c3=$c0 fi xm,ym={*,x,y} if $xm>=0" && "{*,b}&1 x1=$xm y1=$ym fi if $xm>=0" && "{*,b}&2 x2=$xm y2=$ym fi if $xm>=0" && "{*,b}&4 x3=$xm y3=$ym fi # Generate main image view. if $xm>=0" && "($oxm!=$xm" || "$oym!=$ym) w[] -1,-1,{-2,b}.{-2,x}" - ("$xm,$ym")" fi if $x1!=$ox1" || "$y1!=$oy1" || "$x2!=$ox2" || "$y2!=$oy2" || "$x3!=$ox3" || "$y3!=$oy3 . if $x1>=0 xb:=$x1-$dxb yb:=$y1-$dyb xe:=$x1+$dxf ye:=$y1+$dyf rectangle. $xb,$yb,$xe,$ye,0.2,0,255,255 rectangle. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,0,255,255 fi if $x2>=0 xb:=$x2-$dxb yb:=$y2-$dyb xe:=$x2+$dxf ye:=$y2+$dyf rectangle. $xb,$yb,$xe,$ye,0.2,255,32,255 rectangle. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,255,32,255 fi if $x3>=0 xb:=$x3-$dxb yb:=$y3-$dyb xe:=$x3+$dxf ye:=$y3+$dyf rectangle. $xb,$yb,$xe,$ye,0.2,255,255,0 rectangle. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,255,255,0 fi w. {-2,w},{-2,h} rm. oxm=$xm oym=$ym fi # Generate zoomed views. if $x1>=0" && "($ox1!=$x1" || "$oy1!=$y1" || "$oc1!=$c1) +z.. {$x1-$dxb},{$y1-$dyb},0,$c1,{$x1+$dxf},{$y1+$dyf},0,$c1 +z.. {$x1-$dxb},{$y1-$dyb},0,{$x1+$dxf},{$y1+$dyf},0 __display_array[-2,-1] $1,$2,0,255,255 w1. {w},{h},0,0,{-3,b}" - ("$x1,$y1,c=$c1")" rm. ox1=$x1 oy1=$y1 oc1=$c1 fi if $x2>=0" && "($ox2!=$x2" || "$oy2!=$y2" || "$oc2!=$c2) +z.. {$x2-$dxb},{$y2-$dyb},0,$c2,{$x2+$dxf},{$y2+$dyf},0,$c2 +z.. {$x2-$dxb},{$y2-$dyb},0,{$x2+$dxf},{$y2+$dyf},0 __display_array[-2,-1] $1,$2,255,32,255 w2. {w},{h},0,0,{-3,b}" - ("$x2,$y2,c=$c2")" rm. ox2=$x2 oy2=$y2 oc2=$c2 fi if $x3>=0" && "($ox3!=$x3" || "$oy3!=$y3" || "$oc3!=$c3) +z.. {$x3-$dxb},{$y3-$dyb},0,$c3,{$x3+$dxf},{$y3+$dyf},0,$c3 +z.. {$x3-$dxb},{$y3-$dyb},0,{$x3+$dxf},{$y3+$dyf},0 __display_array[-2,-1] $1,$2,255,255,0 w3. {w},{h},0,0,{-3,b}" - ("$x3,$y3,c=$c3")" rm. ox3=$x3 oy3=$y3 oc3=$c3 fi while {*}" && "\ !{*,ESC}" && "!{*,Q}" && "\ !{*1,ESC}" && "!{*1,Q}" && "\ !{*2,ESC}" && "!{*2,Q}" && "\ !{*3,ESC}" && "!{*3,Q} k[0] w 0 w1 0 w2 0 w3 0 } __display_array : round.. 1 c.. 0,999 r. 100%,100%,1,3,{s==1} +luminance. r.. {$1*24},{$2*24} grid.. {100/$1}%,{100/$2}%,0,0,1,0 xb:=24*int($1/2) yb:=24*int($2/2) xe:=$xb+24 ye:=$yb+24 rectangle.. $xb,$yb,$xe,$ye,1,0xFFFFFFFF,$3,$4,$5 repeat $2 { yg=$> repeat $1 { xg=$> t.. {-3,i($xg,$yg)},{5+$xg*24},{5+$yg*24},13,0.8,{i($xg,$yg)>128?0:255} } } rm[-3,-1] #@cli dc : eq. to 'display_camera'. dc : check_opencv $0 v + _display_camera #@cli display_camera #@cli : Open camera viewer. #@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default). display_camera : check_opencv $0 v + _$0 _display_camera : e[0--3] "Open camera stream viewer." # Initialize camera and get resolution. l[] { camera onfail use_vt100 e[0--4] ${_vt100_r}${_vt100_b}"Command 'display_camera': Unable to read camera stream. Exiting."$_vt100_n return } wc,hc:=w,h # Open interactive window. w ${"fitscreen "$wc,$hc},0,"G'MIC Camera Stream Viewer" wnfs,hnfs={*,w,h} angle,fullscreen,brightness,contrast=0 do # Display frame from camera. ww,wh={*,w,h} camera if $angle rotate. {90*$angle} fi if $brightness +. {10*$brightness} c. 0,255 fi if $contrast /. 255 *. {1.2^$contrast} *. 255 c. 0,255 fi rs. $ww,$wh,1,1 w. rm wait 30 # Manage user events. if {*,r} w[] {*,d,e} elif {*,-R}" || "{*,-SPACE} angle:=($angle+1)%4 elif {*,-ARROWUP} brightness:=min(5,$brightness+1) elif {*,-ARROWDOWN} brightness:=max(-5,$brightness-1) elif {*,-ARROWRIGHT} contrast:=min(5,$contrast+1) elif {*,-ARROWLEFT} contrast:=max(-5,$contrast-1) elif {*,-F}" || "{*,-ENTER}" || "{*,-F5} fullscreen:=1-$fullscreen if $fullscreen wwnfs,whnfs={*,w,h} w[] {*,u,v},0,1 else w[] $wwnfs,$whnfs,0,0 fi fi while {*}" && "!{*,ESC} camera 0,0 w[] 0 #@cli dclut : eq. to 'display_clut'. dclut : check "isint(${1=256},1) && isint(${2=33},1)" v + _display_clut $* #@cli display_clut : _image_resolution>0,_clut_resolution>0 #@cli : Display selected 3D color LUTs. #@cli : Default values: 'image_resolution=320' and 'clut_resolution=33'. #@cli : $ clut tealorange clut summer clut 60s display_clut 400 display_clut : v + _$0 $* _display_clut : check "isint(${1=320},1) && isint(${2=33},1)" e[0--3] "Display selected 3D color LUTs, with image resolution $1x$1 and CLUT resolution $2x$2x$2." foreach { nm={n} rs3d $2 pointcloud3d circles3d 1 o3d 0.1 *3d {255/($2-1)} colorcube3d 1 +3d pose3d 0.815360904,-0.144610703,-0.560602367,0,\ -0.149177685,0.883129835,-0.444778264,0,\ 0.559404552,0.446283728,0.698496521,0 c3d n3d *3d. {2.10*$1} {2*$1},{2*$1},1,3,-1 j3d. ..,47%,46%,0,1,2,0,0,{4*$1} rm.. to_rgba replace_color 0,0.5%,-1,-1,-1,255,96,96,96,0 rs $1 => $nm } #@cli dfft : eq. to 'display_fft'. dfft : v + _display_fft #@cli display_fft #@cli : Display fourier transform of selected images, with centered log-module and argument. #@cli : (eq. to 'dfft'). #@cli : $ image.jpg +display_fft display_fft : v + _$0 _display_fft : e[0--3] "Render fourier transform of image$? with centered log-module and argument." foreach { fftpolar +.. 1 log.. n 0,255 a x } s x,2 #@cli dg : eq. to 'display_graph'. dg : check "${1=0}>=0 && ${2=0}>=0" skip ${3=1},${4=0},${5=0},${6=0},${7=0},${8=0},"${9=x-axis}","${10=y-axis}" _display_graph ${1-8},"$9","$10" #@cli display_graph : _width>=0,_height>=0,_plot_type,_vertex_type,_xmin,_xmax,_ymin,_ymax,_xlabel,_ylabel #@cli : Render graph plot from selected image data. #@cli : 'plot_type' can be { 0:none | 1:lines | 2:splines | 3:bar }. #@cli : 'vertex_type' can be { 0:none | 1:points | 2,3:crosses | 4,5:circles | 6,7:squares }. #@cli : 'xmin','xmax','ymin','ymax' set the coordinates of the displayed xy-axes. #@cli : if specified 'width' or 'height' is '0', then image size is set to half the screen size. #@cli : Default values: 'width=0', 'height=0', 'plot_type=1', 'vertex_type=1', 'xmin=xmax=ymin=ymax=0 (auto)', \ # 'xlabel="x-axis"' and 'ylabel="y-axis"'. #@cli : $ 128,1,1,1,'cos(x/10+u)' +display_graph 400,300,3 display_graph : check "${1=0}>=0 && ${2=0}>=0" skip ${3=1},${4=0},${5=0},${6=0},${7=0},${8=0},"${9=x-axis}","${10=y-axis}" _display_graph ${1-8},"$9","$10" _display_graph : check "${1=0}>=0 && ${2=0}>=0" skip ${3=1},${4=0},${5=0},${6=0},${7=0},${8=0},"${9=x-axis}","${10=y-axis}" e[0--3] "Render $1x$2 graph plot from data of image$?." foreach { nm={n} # Determine output size. if $1>0" && "$2>0 w,h=$1,$2 else w,h:=[{*,u,v}]/2 fi w,h:=max($w,33),max($h,33) # Determine xmin,xmax/ymin,ymax. one:=$3!=3 siz:=w*h*d if $5==$6 xmin=0 xmax:=$siz-$one else xmin:=min($5,$6) xmax:=max($5,$6) fi if $7==$8 ymin:=im-(iM-im)/20 ymax:=iM+(iM-im)/20 else ymin:=min($7,$8) ymax:=max($7,$8) fi dx,dy:=$xmax-$xmin,$ymax-$ymin # Determine number of axes tick marks. u=${"_axes[] "$xmin,$xmax",{0.3*"$w"/14}"} offx:=arg(1,$u) deltax:=arg(2,$u) u=${"_axes[] "$ymin,$ymax",{0.3*"$h"/14}"} offy:=arg(1,$u) deltay:=arg(2,$u) # Create plot canvas. gw:=$w-32 gh:=$h-32 gg:=($gw-$one)/($siz-$one) $gw,$gh,1,3,255 grid. {$deltax*$gw/$dx},{$deltay*$gh/$dy},{($offx-$xmin)*$gw/$dx},{$gh-($offy-$ymin)*$gh/$dy},0.25,0xCCCCCCCC,0 # Define color palette for curves. if s#-2==1 (120,120,200) elif s#-2<=3 (220,10,10;10,220,10;10,10,220) else (0,255) r. 256,1,1,1,3 map. 2 z. 2,100% permute. cxyz r. 3,{-3,max(3,s)},1,1,0,2 sh. 0,2,0,0 f. 255,0,0,0,255,0,0,0,255 rm. fi # Draw plot for each channel. repeat s#-3 { sh... $> graph... .,$3,$4,$ymax,$ymin,1,{-2,@0-2} rm. shift. 0,-1 } rm[-3,-1] line. 0,0,100%,0,1,110 line. 100%,0,100%,100%,1,110 line. 100%,100%,0,100%,1,255 line. 0,100%,0,0,1,255 100%,100%,1,1,255 axes. $xmin,$xmax,$ymax,$ymin,14,1,0 if $xmin>0 axes. 0,0,$ymax,$ymin,14,1,160 fi if $xmax<0 axes. {w-1},{w-1},$ymax,$ymin,14,1,160 fi if $ymin>0 axes. $xmin,$xmax,{h-1},{h-1},14,1,160 fi if $ymax<0 axes. $xmin,$xmax,0,0,14,1,160 fi +erode. 3 !=. 255 r.. 100%,100%,1,3 j... ..,0,0,0,0,1,.,1 rm[-2,-1] frame. xy,16,220 0 t. "$9",0,0,14,1,-220,-220,-220 j.. .,{({-2,w}-w)/2},{{-2,h}-16},0,0,-1 rm. 0 t. "$10",0,0,14,1,-220,-220,-220 rotate. -90 j.. .,2,{({-2,h}-h)/2},0,0,-1 rm. => $nm } c 0,255 #@cli dh : eq. to 'display_histogram'. dh : _gmic_s="$?" v + _display_histogram $"*" #@cli display_histogram : _width>=0,_height>=0,_clusters>0,_min_value[%],_max_value[%],_show_axes={ 0 | 1 },_expression. #@cli : Render a channel-by-channel histogram. #@cli : If selected images have several slices, the rendering is performed for all input slices. #@cli : 'expression' is a mathematical expression used to transform the histogram data for visualization purpose. #@cli : (eq. to 'dh'). #@cli : if specified 'width' or 'height' is '0', then image size is set to half the screen size. #@cli : Default values: 'width=0', 'height=0', 'clusters=256', 'min_value=0%', 'max_value=100%', 'show_axes=1' \ # and 'expression=i'. #@cli : $ image.jpg +display_histogram 512,300 display_histogram : _gmic_s="$?" v + _$0 $"*" _display_histogram : check "${1=0}>=0 && ${2=0}>=0 && ${3=256}>0" skip ${4=0%},${5=100%},${6=1},"${7=i}" e[0--3] "Render $1x$2 channel-by-channel histogram of image"$_gmic_s", with $3 clusters, minimum value $4 and maximum value $5." foreach { nm={n} if ispercentage($4) m:=im+(iM-im)*$4 else m=$4 fi if ispercentage($5) M:=im+(iM-im)*$5 else M=$5 fi s:=s s c repeat $s { l[{-1-$>}] { s z histogram $3,$m,$M a z } } a c f "${7--1}" vM:=iM s z foreach { if $1>0" && "$2>0 wh=$1,$2 else wh:=[{*,u,v}]/2 fi $wh,1,{s},-255 repeat s { sh[-2,-1] $> graph. ..,3,0,$vM,0,1,0 rm[-2,-1] } rm.. + 255 if $6 100%,100% axes. $m,$M,$vM,0,14,1,255 if $m>0 axes. 0,0,$vM,0,14,1,200 fi if $M<0 axes. {w-1},{w-1},$vM,0,14,1,200 fi +dilate. 3 ri.. ... j... ..,0,0,0,0,1,.,255 rm[-2,-1] fi } a z => $nm } #@cli display_parametric : _width>0,_height>0,_outline_opacity,_vertex_radius>=0,_is_antialiased={ 0 | 1 },\ # _is_decorated={ 0 | 1 },_xlabel,_ylabel #@cli : Render 2D or 3D parametric curve or point clouds from selected image data. #@cli : Curve points are defined as pixels of a 2 or 3-channel image. #@cli : If the point image contains more than 3 channels, additional channels define the (R,G,B) color for each vertex. #@cli : If 'outline_opacity>1', the outline is colored according to the specified vertex colors and #@cli : 'outline_opacity-1' is used as the actual drawing opacity. #@cli : Default values: 'width=512', 'height=width', 'outline_opacity=3', 'vertex_radius=0', 'is_antialiased=1',\ # 'is_decorated=1', 'xlabel="x-axis"' and 'ylabel="y-axis"'. #@cli : $ 1024,1,1,2,'t=x/40;(!c?sin(t):cos(t))*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)' display_parametric 512,512 #@cli : $ 1000,1,1,2,u(-100,100) quantize 4,1 noise 12 channels 0,2 +normalize 0,255 append c \ # display_parametric 512,512,0.1,8 display_parametric : check "${1=512}>0 && ${2=$1}>0 && ${4=0}>=0" skip ${3=3},${5=1},${6=1},"${7=x-axis}","${8=y-axis}" s0="no " s1="" o0="" o1="colored " e[^-1] "Render $1x$2 parametric graph plot from data of image$?, with "${o{$3>1}}"outline opacity "\ {$3>1?$3-1:$3}", vertex radius $4, "${s{$5!=0}}"antialiasing and "${s{$6!=0}}"decoration." foreach { nm={n} N:=w*h*d i[0] ('CImg3d') +[0] 0.5 i[1] ($N;$N) # Header + nb of vertices/primitives. # Calibrate colors of vertices. if s==4 +channels. 3,3 r. 100%,100%,1,2 a[-2,-1] c is_grayscale=1 else is_grayscale:=s<4 channels. 0,5 fi # Manage coordinates of vertices. sh. 0 xm,xM:=im,iM rm. sh. 1 ym,yM:=im,iM rm. sh. 2 zm,zM:=im,iM rm. permute. cxyz s. x,2 i.. (1,0;1,{$N-1}) r.. 2,$N,1,1,3 round.. 1,$N,1,1,1 # Primitives, colors and opacities. y a y c3d n3d *3d 1,-1,1 {$6?[max(1,$1-32),max(1,$2-32)]:[$1,$2]},1,{$is_grayscale?1:3},255 *3d[0] {0.96*min(w,h)} if $6 L:=0.1*max($1,$2) grid[1] $L,$L,0,0,0.25,0xCCCCCCCC,0 fi if $5 # Anti-aliased. r[1] 200%,200%,1,100%,1 *3d[0] 2 if $4 +circles3d[0] {2*$4} j3d[1] [2],50%,50%,0,1,3,0,0 rm[2] fi elif $4 # Aliased. +circles3d[0] $4 j3d[1] [2],50%,50%,0,1,3,0,0 rm[2] fi # Convert point cloud to connected segments. if $3 l[0] { s3d f[1] "i-y" rm[3] i[3] (2,0,1;2,{$N-2},{$N-1}) r[3] 3,{$N-1},1,1,3 round[3] r[5] 1,{h-1},1,1,0 if $3>1 r[4] 3,{4,h/3},1,1,-1 r[4] 3,{4,h-1},1,1,2 else rm[4] i[4] 3,{$N-1} fi y a y } j3d[1] [0],50%,50%,0,{$3>1?$3-1:$3},2,0,0 fi rm[0] if $5 r. 50%,50%,1,100%,2 fi if $6 # Add decoration. xc:=0.5*($xm+$xM) yc:=0.5*($ym+$yM) dx:=0.5*($xM-$xm)/0.96 dy:=0.5*($yM-$ym)/0.96 xm,xM,ym,yM:=$xc-$dx,$xc+$dx,$yc-$dy,$yc+$dy 100%,100%,1,1,255 axes. $xm,$xM,$yM,$ym,14,1,0 if $xm>0 axes. 0,0,$yM,$ym,14,1,160 fi if $xM<0 axes. {w-1},{w-1},$yM,$ym,14,1,160 fi if $ym>0 axes. $xm,$xM,{h-1},{h-1},14,1,160 fi if $yM<0 axes. $xm,$xM,0,0,14,1,160 fi +erode. 3 !=. 255 r.. 100%,100%,1,3 j... ..,0,0,0,0,1,.,1 rm[-2,-1] frame xy,1,128 frame xy,15,220 0 t. "$7",0,0,14,1,-220,-220,-220 j.. .,{({-2,w}-w)/2},{{-2,h}-16},0,0,-1 rm. 0 t. "$8",0,0,14,1,-220,-220,-220 rotate. -90 j.. .,2,{({-2,h}-h)/2},0,0,-1 rm. fi => $nm } #@cli display_polar : _width>32,_height>32,_outline_type,_fill_R,_fill_G,_fill_B,_theta_start,_theta_end,_xlabel,_ylabel #@cli : Render polar curve from selected image data. #@cli : 'outline_type' can be { r<0:dots with radius -r | 0:no outline | r>0:lines+dots with radius r }. #@cli : 'fill_color' can be { -1:no fill | R,G,B:fill with specified color }. #@cli : Default values: 'width=500', 'height=width', 'outline_type=1', 'fill_R=fill_G=fill_B=200', 'theta_start=0', \ # 'theta_end=360', 'xlabel="x-axis"' and 'ylabel="y-axis"'. #@cli : $ 300,1,1,1,'0.3+abs(cos(10*pi*x/w))+u(0.4)' display_polar 512,512,4,200,255,200 #@cli : $ 3000,1,1,1,'x^3/1e10' display_polar 400,400,1,-1,,,0,{15*360} display_polar : check "${1=500}>32 && ${2=$1}>32" skip ${3=1},${4=200},${5=$4},${6=$5},${7=0},${8=360},"${9=x-axis}","${10=y-axis}" e[^-1] "Render $1x$2 polar graph plot from data of image"$_gmic_s", with outline $4 and fill color ($4,$5,$6)." foreach { nm={n} # Compute (x,y) coordinates of the polar curve points. M:=max(abs(iM),abs(im)) * {0.48*min($1,$2)/$M} y ({$7*pi/180};{-$8*pi/180}) r. 1,..,1,1,3 +sin. cos.. *. ... *[-3,-2] a[-2,-1] x N:=h => coords # Generate 3D object for curve outline. if $3 ('CImg3d') +. 0.5 ($N,$N) +z[coords] 0,2 1,$N,1,1,2 1,$N,1,1,'y' ++. 1 a[-3--1] x =. 0,2,100% 3,$N,1,1,0 1,$N,1,1,1 y[-6--1] a[-6--1] y => _plot_polar_outline fi # Generate 3D object for filling. if "$4>=0 && $5>=0 && $6>=0" ('CImg3d') +. 0.5 ({$N+1},$N) +z[coords] 0,-1,2,100% z. 0,2 1,$N,1,1,3 1,$N 1,$N,1,1,'1+y' ++. 1 a[-4--1] x =. 1,3,100% 3,$N,1,1,$4,$5,$6 1,$N,1,1,1 y[-6--1] a[-6--1] y => _plot_polar_fill fi rm[coords] # Remove original curve coordinates. # Render graph image. {$1-32},{$2-32},1,3,255 L:=0.1*max($1,$2) grid. $L,$L,0,0,0.25,0xCCCCCCCC,0 # Draw background grid if "$4>=0 && $5>=0 && $6>=0" # Draw curve filling j3d. [_plot_polar_fill],50%,50%,0,1,2,1,0 rm[_plot_polar_fill] fi if $3 if $3>=0 # Draw curve outline j3d. [_plot_polar_outline],50%,50%,0,1,1,0,0 fi if $3!=0 # Draw curve vertices if abs($3)>1 circles3d[_plot_polar_outline] {abs($3)} fi j3d. [_plot_polar_outline],50%,50%,0,0.2,2,0,0 fi rm[_plot_polar_outline] fi # Draw axes and frame. nM:=$M/0.96 100%,100%,1,1,255 axes. {-$nM},$nM,$nM,{-$nM},14,1,0 +erode. 3 !=. 255 r.. 100%,100%,1,3 j... ..,0,0,0,0,1,.,1 rm[-2,-1] frame. xy,1,128 frame. xy,15,220 0 t. "$9",0,0,13,1,-220,-220,-220 j.. .,{({-2,w}-w)/2},{{-2,h}-16},0,0,-1 rm. 0 t. "$10",0,0,13,1,-220,-220,-220 rotate. -90 j.. .,2,{({-2,h}-h)/2},0,0,-1 rm. => $nm } #@cli dq : eq. to 'display_quiver'. dq : _gmic_s="$?" v + _display_quiver $* #@cli display_quiver : _size_factor>0,_arrow_size>=0,_color_mode={ 0:monochrome | 1:grayscale | 2:color } #@cli : Render selected images of 2D vectors as a field of 2D arrows. #@cli : (eq. to 'dq'). #@cli : Default values: 'size_factor=16', 'arrow_size=1.5' and 'color_mode=1'. #@cli : $ image.jpg +luminance gradient[-1] xy rv[-2,-1] *[-2] -1 a[-2,-1] c crop 60,10,90,30 +display_quiver[1] , display_quiver : _gmic_s="$?" v + _$0 $* _display_quiver : check "${1=16}>0 && ${2=1.5}>=0 && isint(${3=2},0,2)" e[0--3] "Render field of 2D arrows from image"$_gmic_s", with size factor $1, arrow size $2 in "\ ${arg0\ $3,monochrome,grayscale,color}" mode." foreach { +norm. /.. {max(1e-6,iM)} rm. # Normalize vector values. {$1*w},{$1*h},1,{"1<[0] {1,n} sh. {s-1} j[0] [1],0,0,0,0,1,[2],255 k[0] fi } to_rgb u $is_rgb #@cli dt : eq. to 'display_tensors'. dt : _gmic_s="$?" v + _display_tensors $* #@cli display_tensors : _size_factor>0,_ellipse_size>=0,_color_mode={ 0:monochrome | 1:grayscale | 2:color },_outline>=0 #@cli : Render selected images of tensors as a field of 2D ellipses. #@cli : (eq. to 'dt'). #@cli : Default values: 'size_factor=16', 'ellipse_size=1.5', 'color_mode=2' and 'outline=2'. #@cli : $ image.jpg +diffusiontensors 0.1,0.9 rescale2d. 64 +display_tensors. 16,2 #@cli : $$ https://gmic.eu/oldtutorial/_display_tensors display_tensors : _gmic_s="$?" v + _$0 $* _display_tensors : check "${1=16}>0 && ${2=1.5}>=0 && isint(${3=2},0,2) && ${4=2}>=0" e[0--3] "Render field of 2x2 tensors from image"$_gmic_s", with size factor $1, ellipse size $2 in "\ ${arg0\ $3,monochrome,grayscale,color}" mode and outline $4." foreach { * {($2*$1/2)/max(abs(im),abs(iM))} # Normalize tensor values. {$1*w},{$1*h},1,{"1<=0, --k, ellipse(#1,X,r1 + k*$4,r2 + k*$4,ang°,1,arg(k + 1,C,Co))); I" rm.. } #@cli dv3d : eq. to 'display_voxels3d'. dv3d : _gmic_s="$?" v + _display_voxels3d #@cli display_voxels3d #@cli : Display selected images as set of 3D voxels. #@cli : (eq. to 'dv3d'). display_voxels3d : _gmic_s="$?" v + _$0 _display_voxels3d : nb_images=$! foreach { nm$>={n} } +foreach { w,h,d={w},{h},{d} nm=${nm$>} bnm={b} l. { if ['{x}']!=0 bnm.=.{x} fi onfail bnm.=.{x} } asiz:=max(w,h,d)/5 +norm1. !=. 0 nbv:=is rm. # Count voxels e[0--4] "Display image ["{arg(1+$>,$[])}"] = '"$nm"' ("${w}x${h}x${d}", "$nbv" voxels)." args=0.5,1,0.75 if s==1 im,iM:=im,iM eval. "*begin(is_bin = 1); i!=$im && i!=$iM?(is_bin = 0; break()); end(merge(is_bin,&&); set('is_bin',is_bin))" if $is_bin # Colorize binary images according to rough normal estimation >. {lerp($im,$iM,0.5)} +expand. xyz,1 b. {min(w,h,d)*2%},0 g. xyz a[-3--1] c orientation. n. 0,220 shrink. xyz,1 *. .. k. args=0.9,1.1,1 fi fi surfels3d $args +3d. 0.5,0.5,0.5 axes3d $asiz,$asiz,$asiz,16 o3d. 0.5 col3d. 255,128,0 +3d } foreach[$nb_images--1] { => ${nm$>} } _display_3d_rendering_mode=2 _display_3d_is_bounding_box=1 _display[$nb_images--1] "",0,$[] rm[$nb_images--1] is_change 0 #@cli dw : eq. to 'display_warp'. dw : _gmic_s="$?" v + _display_warp $* #@cli display_warp : _cell_size>0 #@cli : Render selected 2D warping fields. #@cli : (eq. to 'dw'). #@cli : Default value: 'cell_size=15'. #@cli : $ 400,400,1,2,'x=x-w/2;y=y-h/2;r=sqrt(x*x+y*y);a=atan2(y,x);5*sin(r/10)*[cos(a),sin(a)]' +display_warp 10 display_warp : _gmic_s="$?" v + _$0 $* _display_warp : check "${1=15}>0" e[0--3] "Render 2D warping field"$_gmic_s", with cell size $1." foreach { if d!=1" || "s!=2 error[0--3] "Command 'display_warp': Invalid image ["{$!-$>-1}"]: Dimensions "{w}","{h}","{d}","{s}" does not represent a 2D field of 2D vectors." fi i[0] 100%,100%,1,1,1 grid[0] $1,$1 =>[0] {1,n} warp[0] [1],1,1,0 rm[1] } * 255 #@cli e : eq. to 'echo'. : (+) #@cli echo : message : (+) #@cli : Output specified message on the error output. #@cli : (eq. to 'e').\n #@cli : Command selection (if any) stands for displayed call stack subset instead of image indices. #@cli : When invoked with a '+' prefix (i.e. '+echo'), the command output its message on stdout rather than stderr. #@cli echo_file : filename,message #@cli : Output specified message, appending it to specified output file. #@cli : (similar to 'echo' for specified output file stream). echo_file : skip "${2='\n'}" l[] { it "$1" onfail 0 } ('"${2--1}\n"':y) a[-2,-1] y ot. $1 rm. #@cli font : { 'Font_name' | font_number | font.gmz },_font_height[%]>0,_is_bold={ 0 | 1 } #@cli : Return font identifier (variable name) that can be further used in command `text` as a custom font. #@cli : 'Font_name' can be { Acme | Arial | Arial Black | Black Ops One | BlackChancery | Cabin Sketch | \ # Caprasimo | Carnevalee Freakshow | Cheese Burger | Cheque | Cheque-Black | Chlorinar | Comic Sans MS | \ # Courier New | Creepster | Georgia | Impact | Lobster | Luckiest Guy | Macondo | MedievalSharp | Odin Rounded | \ # Oswald | Palatino Linotype | Playfair Display | Roboto | Sacramento | Satisfy | Sofia | Tex Gyre Adventor | \ # Times New Roman | Titan One | Verdana }. #@cli : If a filename 'font.gmz' is specified, it must be a file converted with command `font2gmz`. #@cli : Default values: 'font_height=64' and 'is_bold=0'. #@cli : $ 400,300,1,3 text "Hello World!",0.5~,0.5~,${"font \"Cheese Burger\",80"},1,255,255,128 +font : check "isint(${2=24},1) && isbool(${3=0})" fonts="Acme","Arial","Arial Black","Black Ops One","BlackChancery","Cabin Sketch","Caprasimo","Carnevalee Freakshow",\ "Cheese Burger","Cheque","Cheque-Black","Chlorinar","Comic Sans MS","Courier New","Creepster","Georgia",\ "Impact","Lobster","Luckiest Guy","Macondo","MedievalSharp","Odin Rounded","Oswald","Palatino Linotype",\ "Playfair Display","Roboto","Sacramento","Satisfy","Sofia","Tex Gyre Adventor","Times New Roman","Titan One",\ "Verdana" if "s = ['$1']; find(s,'.gmz')==size(s)-4" font="$1" is_file=1 elif "isint($1)" arg0 {$1%narg($fonts)},$fonts font=${} is_file=0 else font="$1" is_file=0 fi qualifier0,qualifier1="regular","bold" e[^-1] "Return default font '"$font"' with height $2 and "${qualifier$3}" face." height:=ispercentage($2)?h*$2:$2 strvar $font fontname=${} varname=__font_${fontname}_$qualifier$3$height if narg(${$varname}) u $varname return fi # Font already instancied l[] { if $is_file "$1" else input_cached gmic_fonts.gmz k[$fontname] unserialize fi if $3 # Bold face dsiz:="s = round(h/32); s+=1 - (s%2); max(3,s)" expand xy,{round($dsiz/2)} dilate $dsiz fi if h!=$height if h>$height foreach { r {max(1,round(w*$height/h))},$height,1,1,2 } else sigma:=($height-h)/64 foreach { r {max(1,round(w*$height/h))},$height,1,1,3 } b $sigma,0 function1d[] 1,0,0,128,10,200,255,255,255 c. 0,255 map[^-1] . rm. fi round fi [^] f[0-255] 255 foreach { if $><256 => char$> else => mask{$>-256} fi } store $varname onfail v + error[0--3] "Command 'font': Unknown font '$1'." v - varname=$2 # Fallback rm } u $varname #@cli font2gmz : _font_name,_font_size>0,_font_qualifier #@cli : Convert specified font to G'MIC format, so that it can be used as a custom font for command `text`. #@cli : 'font_name' can be either a filename as 'font.ttf', or a 'Google Font Name'. #@cli : This command requires the command line tool `cutycapt` to be installed on your system. #@cli : Beware, 'font_size' is the size of font used for the rendering, it does **not** correspond to the font height. #@cli : Default values: 'font_name=Sofia', 'font_size=24' and 'font_qualifier=""'. +font2gmz : skip "${1=Sofia},${3=}" check "isint(${2=64},1)" # Extract font name/file information. basename "$1",_dir basename=${} if ['$_dir']==0 _dir=${-path_current} fi path=$_dir 0 => $basename base={b} ext={x} rm. strvar $base varname=${} e[^-1] "Convert font '"$basename"' to G'MIC format, with font size $2 and font qualifier '$3'." # Generate fontchart codes. 256,1,1,1,"x; end( codes0 = [ 32,32,8853,8854,8855,8856,8857,8747,8711,32,32,8800,8730,32,9824,9827, 9829,9830,8745,8746,8743,8744,8712,32,32,32,32,32,32,32,32,8364 ]; draw(codes0,0,size(codes0)); codes1 = [ 32,8734,945,946,8706,948,949,951,947,955,956,969,966,960,968,961, 963,964,952,916,931,915,937,934,928,936,920,8592,8593,8594,8595,8596,8597 ]; draw(codes1,127,size(codes1)); ") codes={^} rm. # Generate HTML page. if "isin(['"$ext"'],'ttf','otf')" # Font specified as a filename str1= str2="@font-face {\n\ font-family: gmicFont;\n\ src: url('"$path$basename"');\n\ }" font_family="gmicFont" else str1="" str2= font_family="\"$1\" $3" fi ('" \n\ \n\ \n\ \n\ "$str1"\n\ \n\ \n"') ({'"\n\n"'}) ind=0 repeat 256 code:=[$codes][$>] bgcolor=${dec2hex\ {16752640+$>}} if !($ind%16) ('"\n"') fi ('"\n"') ind+=1 if !($ind%16)" || "!$< ('"\n"') fi done ({'"\
"') ('"&#"$code;') ('"
\n\ \n\ "'}) y a y file_html=${-path_tmp}gmic_$0_$varname.html ot $file_html # Render as a PNG file, using 'cutycapt'. file_png=${-path_tmp}gmic_$0_$varname.png x 0,"cutycapt --min-width="{32*$2}" --min-height="{32*$2}" --url=file://"$file_html" --out="$file_png rm $file_png to_rgb # Extract letters. autocrop eval[0] ": begin( x0s = y0s = vector256(inf); x1s = y1s = vector256(-inf); void = [ 0,0,0 ]; ); i0==255 && i1==160?( ind = i2; xx1s[ind]?(x1s[ind] = x); y>y1s[ind]?(y1s[ind] = y); ); void; end( merge(x0s,min); merge(y0s,min); merge(x1s,max); merge(y1s,max); set('x0s',v2s(x0s)); set('y0s',v2s(y0s)); set('x1s',v2s(x1s)); set('y1s',v2s(y1s)); )" repeat narg($x0s) { x0,y0,x1,y1:=([$x0s][$>]),([$y0s][$>]),([$x1s][$>]),([$y1s][$>]) +z[0] $x0,$y0,0,0,$x1,$y1,0,0 } rm[0] negate # Autocrop characters along the X-axis. foreach { if iM>0 autocrop_coords 0 x0,y0,z0,x1,y1,z1=${} z $x0,$x1 fi } # Autocrop font along the Y-axis. my0,My1=inf,-inf foreach { if iM autocrop_coords 0 x0,y0,z0,x1,y1,z1=${} my0,My1:=min($my0,$y0),max($My1,$y1) fi } z 0,$my0,100%,$My1 # Set default width for invisible characters. W={{'f'},w} foreach { if !iM r $W,100%,1,1,0 fi } # Transform to G'MIC gmz font format. n 0,255 sharpen 100 round foreach { => mask$> } #@cli function1d : 0<=smoothness<=1,x0>=0,y0,x1>=0,y1,...,xn>=0,yn #@cli : Insert continuous 1D function from specified list of keypoints (xk,yk) #@cli : in range [0,max(xk)] (xk are positive integers). #@cli : $ function1d 1,0,0,10,30,40,20,70,30,80,0 +display_graph 400,300 +function1d : e[^-1] "Input continuous 1D function, with smoothness $1 and keypoints (${2--1})." l[] { # Sort and normalize input keypoints. smoothness:=max(0,min(1,$1)) (${2--1}) r 2,{int(w/2)},1,1,-1 sort +,y s x size={0,iM>=0?1+int(iM):0} if !$size rm 0 break fi a x # Compute slopes for splines. +f "(j(0,1,0,0,0,1) - j(0,-1,0,0,0,1))/2" s. x max.. 0.01 /. .. rm.. a x # Determine spline coefficients for each part of the curve. $size,1,1,1,-1 repeat h#0-1 { x0,y0,x1,y1={0,[i(0,$>),i(1,$>),i(0,$>+1),i(1,$>+1)]} slope:=($y1-$y0)/max(0.01,$x1-$x0) yp0={0,i(2,$>)*$smoothness+(1-$smoothness)*$slope} yp1={0,i(2,$>+1)*$smoothness+(1-$smoothness)*$slope} i:=round($x0,1,1) j:=round($x1,1,0) line[1] $i,0,$j,0,1,$> if $j-$i<=1 # Linear interpolation for very close points. ({$y0-$x0*$slope}^{$slope}^0^0) else # Cubic interpolation otherwise. (1,$x0,{($x0)^2},{($x0)^3};\ 1,$x1,{($x1)^2},{($x1)^3};\ 0,1,{2*$x0},{3*($x0)^2};\ 0,1,{2*$x1},{3*($x1)^2}) ($y0;$y1;$yp0;$yp1) invert.. 1 mmul[-2,-1] y. c fi } a[2--1] x map.. . rm. # Render final curve. 100%,1,1,1,1 (0,{w-1}) r. {-2,w},1,1,1,3 round. +sqr. +*[-2,-1] a[-4--1] c *[-2,-1] s. c +[-4--1] rm.. } # [Internal] Generate all custom G'MIC fonts and upload them to the G'MIC server. update_fonts : fonts="Acme",\ "Arial",\ "Arial Black",\ "Black Ops One",\ "Cabin Sketch",\ "Caprasimo",\ "Comic Sans MS",\ "Courier New",\ "Creepster",\ "Georgia",\ "Impact",\ "Luckiest Guy",\ "Macondo",\ "MedievalSharp",\ "Oswald",\ "Palatino Linotype",\ "Playfair Display",\ "Roboto",\ "Satisfy",\ "Sofia",\ "Titan One",\ "Times New Roman",\ "Verdana" files $HOME/work/src/gmic/html/fonts/*.otf if narg(${}) fonts.=,${} fi files $HOME/work/src/gmic/html/fonts/*.ttf if narg(${}) fonts.=,${} fi target_height=512 nb_fonts:=narg($fonts) repeat narg($fonts) { arg0 $>,$fonts font=${} basename $font 0 => ${} name={b} rm. strvar $name varname=${} outfile=$_path_rc/font_$varname.gmz e " - "$name if !isfile(['$outfile']) l[] { outgmz=${-path_tmp}gmic_$0_$varname.gmz size:=round($target_height/1.18) best_size=0 best_font= do rm if $size!=$best_size" || "!narg($best_font) x 0,"gmic font2gmz \\\""$font"\\\","$size" o "$outgmz $outgmz if h>$target_height +store best_font best_size=$size fi e " \#"$>" -> "{h} else $best_font e " \#"$>" -> Best guess: "{h} break fi if h!=$target_height nsize:=$size+$target_height-h size:=round(lerp($nsize,$size,0.35)) fi while h!=$target_height delete $outgmz if h>$target_height rs ,$target_height fi ge 75% * 255 serialize auto,1,0 => $varname o $outfile rm } fi } # Generate font collection file. files ${_path_rc}font_*.gmz files=${} repeat narg($files) { arg0 $>,$files ${} } sort_list +,n outfile1=${_path_rc}gmic_fonts.gmz outfile2=$HOME/work/src/gmic/resources/gmic_fonts.gmz o $outfile1 o $outfile2 e " - Upload all fonts to G'MIC server." _upload $outfile1 rm # Generate documentation string for command 'font'. list_fonts,sep= repeat narg($fonts) { arg0 $>,$fonts font=${} basename $font 0 => ${} name={b} rm. strvar $name varname=${} list_fonts.=$sep$name sep=, } str="#@cli : 'Font name' can be { " 0x{narg($list_fonts)} => $list_fonts sort_list +,n lof n list_fonts=${} rm repeat narg($list_fonts) { arg0 $>,$list_fonts str.=${} if $< str.=" | " else str.=" }." fi } e " - Generate documentation string for command 'font':\n"$str #@cli identity : _width>=0,_height>=0,_depth>=0 #@cli : Insert an identity map of given size at the end of the image list. #@cli : Default values: 'height=width' and 'depth=1'. #@cli : $ identity 5,1 identity 8,8 +identity : check "isint($1,0) && isint(${2=$1},0) && isint(${3=1},0)" e[^-1] "Input $1x$2x$3 identity map." if $3>1 $1,$2,$3,3,[x,y,z] # 3D elif $2>1 $1,$2,1,2,[x,y] # 2D elif $1>0 $1,1,1,1,x # 1D else 0 fi #@cli i : eq. to 'input'. : (+) #@cli input : \ # [type:]filename : \ # [type:]http://URL : \ # [selection]x_nb_copies>0 : \ # { width>0[%] | [image_w] },{ _height>0[%] | [image_h] },{ _depth>0[%] | [image_d] },{ _spectrum>0[%] \ # | [image_s] },_{ value1,_value2,... | 'formula' } : \ # (value1{,|;|/|^}value2{,|;|/|^}...[:{x|y|z|c|,|;|/|^}]) : \ # 0 : (+) #@cli : Insert a new image taken from a filename or from a copy of an existing image [index], #@cli : or insert new image with specified dimensions and values. Single quotes may be omitted in #@cli : 'formula'. Specifying argument '0' inserts an 'empty' image. #@cli : (eq. to 'i' | (no arg)). #@cli : Default values: 'nb_copies=1', 'height=depth=spectrum=1' and 'value1=0'. #@cli : $ input image.jpg #@cli : $ input (1,2,3;4,5,6;7,8,9^9,8,7;6,5,4;3,2,1) #@cli : $ image.jpg (1,2,3;4,5,6;7,8,9) (255^128^64) 400,400,1,3,'(x>w/2?x:y)*c' #@cli : $$ #@cli input_565 : filename,width>0,height>0,reverse_endianness={ 0 | 1 } #@cli : Insert image data from a raw RGB-565 file, at the end of the list. #@cli : Default value: 'reverse_endianness=0'. +input_565 : check "isint($2,1) && isint($3,1) && isbool(${4=0})" e[^-1] "Input raw RGB-565 file '"{/"$1"}"', with size $2x$3." l[] { raw:"$1",uint16 if $4 endian uint16 fi r $2,$3,1,1,-1 +>> 5 &. 63 +&.. 31 >>... 11 *[-3,-1] 8 *.. 4 a c } #@cli ib : eq. to 'input_bytes'. +ib : v + _input_bytes "$*" #@cli input_bytes : filename #@cli : Input specified filename as a 1D array of bytes. #@cli : (eq. to 'ib'). +input_bytes : v + _$0 "$*" _input_bytes : e[0--3] "Input file '"{/"$*"}"' as a 1D array of bytes." i raw:"$*",uint8 #@cli input_csv : "filename",_read_data_as={ 0:numbers | 1:strings | _variable_name } #@cli : Insert number of string array from specified .csv file. #@cli : If 'variable_name' is provided, the string of each cell is stored in a numbered variable '_variable_name_x_y', \ # where 'x' and 'y' are the indices of the cell column and row respectively (starting from '0'). #@cli : Otherwise, a 'WxH' image is inserted at the end of the list, with each vector-valued pixel 'I(x,y)' encoding \ # the number or the string of each cell. #@cli : This command returns the 'W,H' dimension of the read array, as the status. #@cli : Default value: 'read_data_as=1'. +input_csv : check "isvarname('${2=1}') || isin($2,0,1,2)" l[] { is_var:="s = ['$2']; s!='0' && s!='1'" if $is_var # Read data as new variables e[0--4] "Input string array from file '"{/"$1"}"', in variable '$2'." it "$1" replace {'" "'},255 s -,{'\n'} W,H=0,$! repeat $! { _input_csv_var[] $2,$>,${u\ {$>,t}} W:=max($W,${}) } rm u $W,$H else # Read data as a new image s0,s1="number","string" e[0--4] "Input "${s$2}" array from file '"{/"$1"}"'." it "$1" s -,{'\n'} replace {'" "'},255 if $2 repeat $! { _input_csv_str[] ${u\ {$>,t}} rv[$>,-1] rm. } a y replace 255,{'" "'} # Read as strings else repeat $! { _input_csv_val[] ${u\ {$>,t}} rv[$>,-1] rm. } a y # Read as values fi => "$1" u {[w,h]} fi } _input_csv_var : $=arg W:=$#-2 repeat $W { arg=${arg{3+$>}} if ['$arg']!=0 ({'${arg{3+$>}}'}) replace. 255,{'" "'} $1_$>_$2={t} rm. else $1_$>_$2= fi } u $W _input_csv_str : $=arg repeat $# { arg=${arg{1+$>}} if ['$arg']!=0 ({'$arg'}:c) else (0) fi } a x _input_csv_val : $=arg repeat $# { arg=${arg{1+$>}} if ['$arg']!=0 ({s2v(['$arg'])}) else (nan) fi } a x #@cli input_cube : "filename",_convert_1d_cluts_to_3d={ 0 | 1 }. #@cli : Insert CLUT data from a .cube filename (Adobe CLUT file format). #@cli : Default value: 'convert_1d_cluts_to_3d=1'. +input_cube : skip ${2=1} e[^-1] "Input CLUT from file '"{/"$1"}"'" l[] { it[] "$1" f "i<_' ' && i!=10?_' ':i" s -,10 i[0] 0 range:=" ref(vector128(),line); dmin = [ 0,0,0 ]; dmax = [ 1,1,1 ]; dim = size = 0; target = 0; for (k = 1, k1 && h==1 && d==1 && $2" size:=w s c y.. y. z r $size,$size,$size a c fi => "$1" } #@cli input_flo : "filename" #@cli : Insert optical flow data from a .flo filename (vision.middlebury.edu file format). +input_flo : e[^-1] "Input optical flow from file '"{/"$1"}"'." l[] { i raw:"$1",float32 if i!=202021.25 endian. fi if i!=202021.25 error[0--3] "Command 'input_flo': Filename '$1' is not a valid .flo file." return fi +rows 1,2 cast. float32,uint32 w,h={^} rm. rows 3,100% r 2,$w,$h,1,-1 permute yzcx } #@cli ig : eq. to 'input_glob'. +ig : v + _input_glob "$*" #@cli input_glob : pattern #@cli : Insert new images from several filenames that match the specified glob pattern. #@cli : (eq. to 'ig'). +input_glob : _input_glob "$*" _input_glob : e[0--3] "Input all files that match glob pattern '"{/"$*"}"'." files 3,"$*" N=$! m "_ig : $""=arg repeat $""# { i ${arg{1+$>}} }" _ig ${} um _ig if $N==$! error[0--3] "Command 'input_glob': No matching filenames for pattern '$*'." fi #@cli input_gpl : filename #@cli : Input specified filename as a .gpl palette data file. +input_gpl : e[^-1] "Input palette from file '"{/"$*"}"'." l[] { it[] "$*" discard 13 replace 9,32 s -,10 colors=0 foreach { l { s -,32 if $!>=3" && "isint({0,t})" && "isint({1,t})" && "isint({2,t}) colors=$colors;{0,t},{1,t},{2,t} fi rm 0 onfail rm 0 } } rm ($colors) rows 1,100% => "$1" permute yzcx } #@cli input_cached : "basename.ext",_try_downloading_from_gmic_server={ 0 | 1 } #@cli : Input specified filename, assumed to be stored in one of the G'MIC resource folder. #@cli : If file not found and 'try_downloading=1', file is downloaded from the G'MIC server and stored #@cli : in the '${-path_cache}' folder. #@cli : Default value: 'try_downloading_from_gmic_server=1'. +input_cached : check "isbool(${2=1})" basename "$1" basename=${} e[^-1] "Input cached file '"$basename"'." if ['$GMIC_SYSTEM_PATH']!=0 g_path_unix=$GMIC_SYSTEM_PATH/ else g_path_unix=/usr/lib/gimp/2.0/plug-ins/ fi path_test0=${-path_cache} path_test1=$_path_rc path_test2=${-path_gimp}plug-ins/ path_test3=${-path_gimp}plug-ins/gmic_gimp_qt/ if !${-is_windows} path_test4=/usr/share/gmic/ path_test5=$g_path_unix else path_test4=$g_path_unix fi file_found=0 repeat inf { if ['{/${path_test$>}}']==0 break fi file=${path_test$>}$basename l[] { i $file file_found=1 onfail } if $file_found break fi } if !$file_found" && $2" # Try downloading from the G'MIC server url=https://gmic.eu/"$1" l[] { i $url o ${-path_cache}$basename file_found=1 is_change 1 onfail } fi if !$file_found error[0--3] "Command 'input_cached': Unknown filename '"{/"$1"}"'." fi #@cli input_obj : filename #@cli : Input specified 3D mesh from a .obj Wavefront file. +input_obj : e[^-1] "Input 3D mesh from file '"{/"$*"}"'" l[] { it "$1" f "i<=_' ' && i!=_'\n'?_' ':i" # Normalize blank characters. 0 => "$1" folder={f} rm. # Retrieve file folder 1 # [1]: vertices (unrolled) 1 # [2]: primitives (unrolled) 1 # [3]: colors (unrolled) 1 # [4]: opacity 1,1,1,2 # [5]: texture 2D pts 0 # [6]: vertices weights/colors (RGB) input_obj_rgb,input_obj_alpha= eval " next_item() = ( while (line[q] && line[q]!=_' ' && line[q]!=_'/',++q); while (line[q] && line[q]==_' ', ++q); ); push_texture() = ( s_ind_prim = string('ind_prim',p_img); ind_prim = get(s_ind_prim); isnan(ind_prim)?( # Texture never used before da_push(#3,-128,ui2f(p_whs[0]),p_whs[1],p_whs[2]); dsiz = da_size(#3); isiz = prod(p_whs); resize(#3,1,h(#3) + isiz,1,1,0); copy(i[#3,dsiz],i[#p_img,0],isiz); i[#3,h(#3) - 1] = ui2f(dsiz + isiz); set(s_ind_prim,nbp); ):( # Texture used before -> shared version da_push(#3,-128,ui2f(ind_prim),0,0); ); ); id_v = 'v '; id_f = 'f '; id_l = 'l '; id_p = 'p '; id_vt = 'vt '; id_mtllib = 'mtllib '; id_usemtl = 'usemtl '; line = vector1024(0); varname = string(#256,'default'); p_rgb = [ 200,200,200 ]; p_alpha = 1; p_img = -1; p_whs = [ 0,0,0 ]; line_number = 1; nbv = nbp = p = 0; while (p0?( copy(line,line[q],len + 1); run('_input_obj_parse_mtl \""{/$folder}"\",\"',line,'\"'); ); ):line[0,7]==id_usemtl?( # Material: change material q = 0; next_item(); len = find(line,0,q); len>0?( # Convert material name to corresponding variable name. pp = q; qq = 0; inrange(line[pp],_'0',_'9')?varname[qq++]=_'_'; while (pp=0" # Convert vertex weights to colored primitives 1,$nbp,1,1,">begin(N = p = 0); nbv = i[#2,p++]; R = G = B = nR = nG = nB = 0; repeat (nbv, ind = i[#2,p++]; val = i(#6,0,ind,0,0); val>=0?(R+=val; ++nR); val = i(#6,0,ind,0,1); val>=0?(G+=val; ++nG); val = i(#6,0,ind,0,2); val>=0?(B+=val; ++nB); ); R = nR>0?round(255*R/nR):200; G = nG>0?round(255*G/nG):R; B = nB>0?round(255*B/nB):B; off = 3*N; i[#3,off++] = R; i[#3,off++] = G; i[#3,off] = B; ++N" rm. fi rm[0,5--1] i[0] ({'CImg3d'},{ui2f([$nbv,$nbp])}:y) a y => "$1" } _input_obj_parse_mtl : skip "${1=}" img_ind=$! mtl_filename="$1$2" if !isfile(['{/$mtl_filename}']) return fi l[] { it $mtl_filename f "i<=_' ' && i!=_'\n'?_' ':i" # Normalize blank characters. s -,{'\n'} autocrop {'" "'} N:=$! m=default input_obj_rgb_$m=200,200,200 input_obj_alpha_$m=1 input_obj_img_$m=-1 input_obj_whs_$m=0,0,0 1,$!,1,1,"> begin( img_ind = $img_ind; line = filename = vector1024(); varname = string(#256,'default'); id_newmtl = 'newmtl'; id_Kd = 'Kd '; id_d = 'd '; id_map_Kd = 'map_Kd '; id_200_200_200 = '200,200,200'; id_0_0_0 = '0,0,0'; ); i[#y,0]!=_'#'?( siz = h#y; copy(line,i[#y,0],min(siz,size(line))); line[siz] = 0; same(line,id_newmtl,size(id_newmtl))?( # New material # Convert material name to corresponding variable name. p = 7; q = 0; inrange(line[p],_'0',_'9')?varname[q++]=_'_'; while (p0?( filename = 0; copy(filename,line[7],p - 7); run(string('l[] { $1',filename,' input_obj_img_',varname,'=',img_ind++, ' input_obj_whs_',varname,':=w,h,s y u 0 onfail input_obj_img_', varname,'=-1 input_obj_whs_',varname,'=0,0,0 u=1 }')); get('{}')?--img_ind; # Error occured when loading texture ); ); )" rm[0-$N] } #@cli it : eq. to 'input_text'. +it : v + _input_text "$*" #@cli input_text : filename #@cli : Input specified text-data filename as a new image. #@cli : (eq. to 'it'). +input_text : v + _$0 "$*" _input_text : e[0--3] "Input text-data file '"{/"$*"}"'." i raw:"$*",uint8 if i[0]==239" && "i[1]==187" && "i[2]==191 rows. 3,100% fi # Remove BOM discard. {'\r'} # Remove CR #@cli lorem : _width>0,_height>0 #@cli : Input random image of specified size, retrieved from Internet. #@cli : Default values: 'width=height=800'. +lorem : check "isint(${1=800},1) && isint(${2=$1},1)" e[^-1] "Input random image of size $1x$2." i jpg:"https://picsum.photos/$1/$2" => lorem # Merge multi-lines (lines continued by backslash before NL) in a string image. merge_multiline : eval[^] "*i==_'\\' && j[-1]!=_'\\' && j[+1]==_'\n'?( for (p = 2, j[p] && j[p]<=_' ' && j[p]!=_'\n', ++p); copy(i(),-1,p,1,0))" discard -1 # Merge multi-line comments in a string image. merge_multiline_comments : eval[^] "*i==_'\\' && j[-1]!=_'\\' && j[+1]==_'\n' && j[+2]==_'#'?( for (p = 3, j[p] && j[p]<=_' ' && j[p]!=_'\n', ++p); copy(i(),-1,p,1,0))" discard -1 #@cli network : mode={ -1=disabled | 0:enabled w/o timeout | >0:enabled w/ specified timeout in seconds } : (+) #@cli : Enable/disable load-from-network and set corresponding timeout. #@cli : (Default mode is 'enabled w/o timeout'). #@cli o : eq. to 'output'. : (+) #@cli output : [type:]filename,_format_options : (+) #@cli : Output selected images as one or several numbered file(s). #@cli : (eq. to 'o'). #@cli : Default value: 'format_options'=(undefined). #@cli output_565 : "filename",reverse_endianness={ 0:false | 1:true } #@cli : Output selected images as raw RGB-565 files. #@cli : Default value: 'reverse_endianness=0'. output_565 : check "isbool(${2=0})" e[^-1] "Output image$? as raw RGB-565 file '"{/"$1"}"'." N=$! +foreach { s c c 0,255 /[-3,-1] 8 /.. 4 round bsl... 11 bsl.. 5 + if $N>1 fn=${"filename \"$1\",$>"} else fn="$1" fi if $2 endian uint16 fi o raw:$fn,uint16 rm } is_change 0 #@cli output_cube : "filename" #@cli : Output selected CLUTs as a .cube file (Adobe CLUT format). output_cube : e[^-1] "Output CLUT$? as file '"{/"$1"}"'." N=$! +foreach { to_rgb l:=round((w*h*d)^(1/3)) if w*h*d!=$l^3 error "Command '$0': CLUT '"{n}"' has invalid dimensions "({w},{h},{d},{s}). fi r $l,$l,$l,3,-1 permute cxyz / 255 if $N>1 fn=${"filename \"$1\",$>"} else fn="$1" fi o dlm:$fn rm it[] $fn replace {','},32 0 => $fn basename={b} rm. header="\# Created by: G'MIC (https://gmic.eu)\n"\ "TITLE \""$basename"\"\n\n"\ "# LUT size\n"\ "LUT_3D_SIZE "$l"\n\n"\ "# Data domain\n"\ "DOMAIN_MIN 0.0 0.0 0.0\n"\ "DOMAIN_MAX 1.0 1.0 1.0\n\n"\ "# LUT data points\n" i[0] ('$header') y a y ot $fn rm } is_change 0 #@cli output_flo : "filename" #@cli : Output selected optical flow as a .flo file (vision.middlebury.edu file format). output_flo : e[^-1] "Output optical flow$? as file '"{/"$1"}"'." N=$! +foreach { w,h={w},{h} channels 0,1 permute cxyz i[0] (202021.25) i[1] ($w,$h) cast[1] uint32,float32 y a y if $N>1 fn=${"filename \"$1\",$>"} else fn="$1" fi o raw:$fn,float32 rm } is_change 0 #@cli output_ggr : filename,_gradient_name #@cli : Output selected images as .ggr gradient files (GIMP). #@cli : If no gradient name is specified, it is deduced from the filename. output_ggr : skip "${2=}" e[^-1] "Output image$? as .ggr gradient file '"{/"$1"}"'." N=$! +foreach { r 1,{w*h*d},1,100%,-1 to_rgba / 255 if narg("$2") name="$2" else l[] { 1 => "$1" ('{b}') f "!x && i>=_'a' && i<=_'z'?i-_'a'+_'A':i" name={t} rm } fi ('"GIMP Gradient\nName: "$name\n{0,h}\n') repeat h#0 { start={_$>/{0,h}} end={_($>+1)/{0,h}} mid={_0.5*($start+$end)} rgba={0,I(0,$>)} r:=arg(1,$rgba) g:=arg(2,$rgba) b:=arg(3,$rgba) a:=arg(4,$rgba) ('$start" "$mid" "$end" "$r" "$g" "$b" "$a" "$r" "$g" "$b" "$a" 0 0\n"') } rm[0] a x if $N>1 ot ${"filename \"$1\",$>"} else ot "$1" fi rm } is_change 0 #@cli output_gmz : filename,_datatype #@cli : Output selected images as .gmz files (G'MIC native file format). #@cli : 'datatype' can be \ # { bool | uint8 | int8 | uint16 | int16 | uint32 | int32 | uint64 | int64 | float32 | float64 }. output_gmz : skip ${2=auto} e[^-1] "Output image$? as gmz file '"{/"$1"}"', with pixel type '$2'." 1,64 eval " draw([_'G',_'M',_'Z'],0,0,1,3); pos = 4; repeat (l - 1,k, ref(name(#k,1026),nam); len = find(nam,0); len>=0?( pos + len>=h?resize(#-1,1,2*h + len,1,1,0); len>0?draw(nam,0,pos,1,len); pos+=1 + len; ); ); resize(#-1,1,pos,1,1,0)" o cimgz:$1,$2 rm. #@cli output_obj : filename,_save_materials={ 0:no | 1:yes } #@cli : Output selected 3D meshes as Wavefront 3D object files. #@cli : Set 'save_materials' to '1' to produce a corresponding material file (`.mtl`) and eventually texture files. #@cli : Beware, the export to `.obj` files may be quite slow for large 3D objects. #@cli : Default value: 'save_materials=1'. output_obj : check "isbool(${2=1})" check3d s0,s1=out, e[^-1] "Output 3D object$? as Wavefront 3D object file '"{/"$1"}"' (with"${s$2}" materials)." N=$! +foreach { nm={n} nb_materials,nb_primitives,nb_textures,nb_tcoords=0 # Generate basename, file header and eval macros. if $N>1 filename=${"filename \"$1\",$>"} else filename="$1" fi 0 => $filename basename={b} folder={f} ext={x} if narg($ext) ext=.$ext fi rm. fnobj="begin(out = 0; s_rgba = vector8()); tos(x) = v2s(x,6); strhex(v,str) = ( ref(v,_v); hex(x) = (_x = int(x); _x + (_x<10?_'0':_'A' - 10)); fill(str,_k,hex(_k%2?_v[(_k-1)/2]%16:_v[_k/2]/16)); ); write(ind,s) = ( l_s = find(s,0); l_s<=0?(l_s = size(s)); out + l_s>h(#ind)?resize(#ind,1,2*h(#ind) + l_s,1,1,0); copy(i[#ind,out],s,l_s); out+=l_s)" # Decompose into vertices, primitives and colors. s3d nb_vertices,nbp={1,f2ui([i[0],i[1]])} rm[0,1] e " > File '"$folder$basename$ext"' ("$nb_vertices" vertices, "$nbp" primitives)." # Export vertices. l[0] { e " - Export vertices." r 3,{h/3},1,1,-1 permute zycx 0 => out_obj eval.. ">"$fnobj"; !(y%10000)?run('e \"\r - Export vertices: ',round(100*y/h),' % \"'); ref(string('v ',tos(i0),' ',tos(i1),' ',tos(i2),'\n'),str); write(#"$out_obj",str); I; end(resize(#"$out_obj",1,out,1,1,0))" rm[0] i[0] ('"\n# Vertices.\n"':y) a y => out_obj e "\r - Export vertices: Done." } # Export materials (colors/textures + opacities). if $2 l[2,3] { e " - Export materials." 1,$nbp,1,3 => materials 0 => out_mtl mv[0] $! eval $fnobj"; const nbp = "$nbp"; off_p = off_o = nb_materials = nb_textures = 0; rgba = vector4(); s_opac = vector32(); repeat (nbp,p, !(p%10000)?run('e \"\r - Export materials: ',round(100*p/nbp),' % \"'); s_opac[0] = 0; opac = i[#0,off_o]; i[off_p]!=-128?( # RGB[A] color ref(cut(round(crop(0,off_p,1,4)),0,255),rgba); rgba[3] = opac!=-128?round(255*cut(opac,0,1)):255; strhex(rgba,s_rgba); vname = string('mat_',s_rgba); m = get(vname); isnan(m)?( ++nb_materials; opac!=-128 && opac<1?copy(s_opac,string('d ',tos(cut(opac,0,1)),'\n')); ref(string('newmtl m',nb_materials,'\n', 'Kd ',tos(rgba[0]/255),' ',tos(rgba[1]/255),' ',tos(rgba[2]/255),'\n',s_opac),str); write(#"$out_mtl",str); set(vname,v2s(m = nb_materials)); ); i[#"$materials",p] = m; off_p+=3; ):i[off_p + 2]?( # Non-shared texture ++nb_materials; ++nb_textures; wt = i[off_p + 1]; ht = i[off_p + 2]; st = i[off_p + 3]; opac!=-128 && opac<1?copy(s_opac,string('d ',tos(cut(opac,0,1)),'\n')); run('+rows. ',off_p + 4,',',off_p + 3 + wt*ht*st,' r. ',wt,',',ht,',1,',st,',-1 tfn=$folder${basename}_t', nb_textures,'.png o. $tfn rm.'); tfile = get('tfn',1024,1); ref(string('newmtl m',nb_materials,'\n', 'map_Kd ',get('tfn',1024,1),'\n',s_opac),str); write(#"$out_mtl",str); I[#"$materials",p] = [ nb_materials,wt,ht ]; off_p+=4 + wt*ht*st; ):( # Shared texture tind = i[off_p + 1]; I[#"$materials",p] = I[#"$materials",tind]; off_p+=4; ); off_o+=(opac==-128?4 + prod(crop(#0,0,off_o + 1,1,3)):1); ); resize(#"$out_mtl",1,out,1,1,0); run('nb_materials,nb_textures=',nb_materials,',',nb_textures)" e "\r - Export materials: Done." k[materials,out_mtl] } fi # Export primitives. e " - Export primitives." 0 => out_prim mv[1] $! eval $fnobj"; const materials = 0"$materials"; const nbp = "$nbp"; off = p_material = material = nb_primitives = nb_tcoords = 0; repeat (nbp,p, !(p%10000)?run('e \"\r - Export primitives: ',round(100*p/nbp),' % \"'); type = i[off]; $2?material = i[#materials,p]; material!=p_material?ref(string('usemtl m',material,'\n'),str_mtl); type==1?( # Colored point ref(string('p ',f2ui(i[off + 1] + 1),'\n'),str); write(#"$out_prim",str); ++nb_primitives; ):type==2 || type==6?( # Colored or textured segment $2 && material!=p_material?write(#"$out_prim",str_mtl); ref(string('l ',f2ui(i[off + 1]) + 1,' ',f2ui(i[off + 2]) + 1,'\n'),str); write(#"$out_prim",str); ++nb_primitives; ):type==3 || (type==9 && !$2)?( # Colored triangle $2 && material!=p_material?write(#"$out_prim",str_mtl); ref(string('f ',f2ui(i[off + 1]) + 1,' ',f2ui(i[off + 2]) + 1,' ',f2ui(i[off + 3]) + 1,'\n'),str); write(#"$out_prim",str); ++nb_primitives; ):type==4 || (type==12 && !$2)?( # Colored quadrangle $2 && material!=p_material?write(#"$out_prim",str_mtl); ref(string('f ',f2ui(i[off + 1]) + 1,' ',f2ui(i[off + 2]) + 1,' ', f2ui(i[off + 3]) + 1,' ',f2ui(i[off + 4]) + 1,'\n'),str); write(#"$out_prim",str); ++nb_primitives; ):type==9?( # Textured triangle material!=p_material?write(#"$out_prim",str_mtl); wt = i(#materials,0,p,0,1); ht = i(#materials,0,p,0,2); ref(string('vt ',tos(i[off + 4]/wt),' ',tos(1 - i[off + 5]/ht),'\n', 'vt ',tos(i[off + 6]/wt),' ',tos(1 - i[off + 7]/ht),'\n', 'vt ',tos(i[off + 8]/wt),' ',tos(1 - i[off + 9]/ht),'\n', 'f ',f2ui(i[off + 1]) + 1,'/',nb_tcoords + 1,' ',f2ui(i[off + 2]) + 1,'/',nb_tcoords + 2, ' ',f2ui(i[off + 3]) + 1,'/',nb_tcoords + 3,'\n'),str); write(#"$out_prim",str); nb_tcoords+=3; ++nb_primitives; ):type==12?( # Textured quadrangle material!=p_material?write(#"$out_prim",str_mtl); wt = i(#materials,0,p,0,1); ht = i(#materials,0,p,0,2); ref(string('vt ',tos(i[off + 5]/wt),' ',tos(1 - i[off + 6]/ht),'\n', 'vt ',tos(i[off + 7]/wt),' ',tos(1 - i[off + 8]/ht),'\n', 'vt ',tos(i[off + 9]/wt),' ',tos(1 - i[off + 10]/ht),'\n', 'vt ',tos(i[off + 11]/wt),' ',tos(1 - i[off + 12]/ht),'\n', 'f ',f2ui(i[off + 1]) + 1,'/',nb_tcoords + 1,' ',f2ui(i[off + 2]) + 1,'/',nb_tcoords + 2, ' ',f2ui(i[off + 3]) + 1,'/',nb_tcoords + 3,' ',f2ui(i[off + 4]) + 1,'/',nb_tcoords + 4,'\n'),str); write(#"$out_prim",str); nb_tcoords+=4; ++nb_primitives; ):( # Unsupported primitive run(['warn \"Command 'output_obj': Cannot convert primitive \#'],p,'/',nbp - 1, ' (size ',type,'). Ignoring it.\"'); ); off+=type + 1; p_material = material; ); resize(#"$out_prim",1,out,1,1,0); run('nb_primitives=',nb_primitives)" e "\r - Export primitives: Done." l[out_obj,out_prim] { i[1] ('"\n# Primitives.\n"':y) a y } if $2 k[out_mtl,out_obj] else k[out_obj] fi # File header. header="# Object name: "$nm\n\ "# Vertices: "$nb_vertices\n\ "# Primitives: "$nb_primitives\n\ "# Materials: "$nb_materials\n\ "# Textures: "$nb_textures\n\ {`"d2(x) = (s = date(x); s>9?v2s(s,-1,2):[0,s]+_'0'); string('# Generated on ',date(0),'/',d2(1),'/',d2(2),' at ',d2(4),':',d2(5),':',d2(6), ' by G\47MIC (https://gmic.eu).\n')"`} # Save object geometry (.obj file). l[out_obj] { i[0] ('"# File: "$folder$basename$ext\n$header':y) if $2 i[1] ('"\n# Materials.\n\ mtllib "$folder$basename".mtl\n"':y) fi a y ot $folder$basename$ext } # Save object materials (.mtl file). if $2 l[out_mtl] { i[0] ('"# File: "$folder$basename.mtl\n$header\n':y) a y ot $folder$basename.mtl } fi rm } is_change 0 #@cli ot : eq. to 'output_text'. ot : _gmic_s="$?" v + _output_text "$*" #@cli output_text : filename #@cli : Output selected images as text-data filenames. #@cli : (eq. to 'ot'). output_text : _gmic_s="$?" v + _$0 "$1" _output_text : e[0--3] "Output image"$_gmic_s" as text-data file '"{/"$1"}"'." o raw:"$1",uint8 #@cli on : eq. to 'outputn'. on : _gmic_s="$?" v + _outputn $* #@cli outputn : filename,_index #@cli : Output selected images as automatically numbered filenames in repeat...done loops. #@cli : (eq. to 'on'). outputn : _gmic_s="$?" v + _$0 $* _outputn : skip "${2=}" if $#==1 filename=${filename\ "$1",$>} else filename=${filename\ "$1",$2} fi e[0--3] "Output image"$_gmic_s" as file '"{/$filename}"'." o $filename #@cli op : eq. to 'outputp'. op : _gmic_s="$?" v + _outputp $* #@cli outputp : prefix #@cli : Output selected images as prefixed versions of their original filenames. #@cli : (eq. to 'op'). #@cli : Default value: 'prefix=_'. outputp : _gmic_s="$?" v + _$0 $* _outputp : skip ${1="_"} if $!>1 e[0--4] "Output image"$_gmic_s" as their initial locations, prefixed by '$1'." else e[0--4] "Output image"$_gmic_s" as its initial location, prefixed by '$1'." fi foreach { if ['{x}']!=0 ext=.{x} else ext= fi o {f}$1{b}$ext } #@cli ow : eq. to 'outputw'. ow : _gmic_s="$?" v + _outputw #@cli outputw #@cli : Output selected images by overwriting their original location. #@cli : (eq. to 'ow'). outputw : _gmic_s="$?" v + _$0 $* _outputw : if $!>1 e[0--4] "Output image"$_gmic_s" as their initial location." else e[0--4] "Output image"$_gmic_s" as its initial location." fi repeat $! { o[$>] {$>,n} } #@cli ox : eq. to 'outputx'. ox : _gmic_s="$?" v + _outputx $* #@cli outputx : extension1,_extension2,_...,_extensionN,_output_at_same_location={ 0 | 1 } #@cli : Output selected images with same base filenames but for N different extensions. #@cli : (eq. to 'ox'). #@cli : Default value: 'output_at_same_location=0'. outputx : _gmic_s="$?" v + _$0 $* _outputx : $=arg is_last_arg=0 is_same_location=0 if isnum($-1) is_last_arg:=isbool($-1) is_same_location=$-1 fi N:=$#-$is_last_arg s0= s1=s if !$N e[0--3] "Output image"$_gmic_s" at same location, with same base filename but extension '' (skipped, no extension provided)." return fi if $is_same_location if $is_last_arg e[0--4] "Output image"$_gmic_s" at same location, with same base filename but extension"${s{$N>1}}"' ${^-1}'." else e[0--4] "Output image"$_gmic_s" at same location, with same base filename but extension"${s{$N>1}}" '$*'." fi foreach { repeat $N { ext=${arg{1+$>}} if isin(lowercase(['$ext']),'jpg','jpeg') ext.=,85 fi o {0,f}{0,b}.$ext } } else if $is_last_arg e[0--4] "Output image"$_gmic_s" with same base filename but extension"${s{$N>1}}"' ${^-1}'." else e[0--4] "Output image"$_gmic_s" with same base filename but extension"${s{$N>1}}" '$*'." fi foreach { repeat $N { ext=${arg{1+$>}} if isin(lowercase(['$ext']),'jpg','jpeg') ext.=,85 fi o {0,b}.$ext } } fi #@cli parse_cli : _output_mode,_{ * | command_name } #@cli : Parse definition of '@cli'-documented commands and output info about them in specified output mode. #@cli : 'output_mode' can be { ascii | bashcompletion | html | images | print }. #@cli : Default values: 'output_mode=print' and 'command_name=*'. parse_cli : skip "${1=print},${2=*}" e[^-1] "Parse '#@cli' command(s) '$2' and output in '$1' mode." # Check that specified output mode is actually implemented. l[] { ({'$$parse_cli_$1'}) rm onfail error[0--2] "Command 'parse_cli': Invalid output mode '$1'." } if !$! l[] { it ${_path_rc}update$_version.gmic onfail } fi if !$! return fi i[0] ('\n') y a y merge_multiline_comments # Split commands/section into different blocks. eval " for (p = 0, p=h || i[r]==_'\n'?(copy(i[p],0,r - p,1,0); p = r; continue()); (i[r]==_':' && i[r + 1]==_':')?( # Category r = find(#0,_'\n',r)%h; ):i[r]!=_':'?do( # Command r = find(#0,_'\n',r)%h; crop(0,r,1,6)=='\n#@cli'?( for (s = r + 6, s Display warning r = find(#0,_'\n',r)%h; ref(vector1024(),line); copy(line,i[q + 1],min(size(line),r - q - 1)); run('warn[0--3] \"Unexpected line: \'',line,'\'.\"'); copy(i[p],0,r - p,1,0); p = r; continue() ); copy(i[p],0,q - p + 1,1,0); # Set values to discard p = r)" s -,0 # Keep only command blocks and name them. if $! $!,1,1,1,"> begin( ref(vector1024(),command); ref(vector1024(),category); ); for (p = 5, p=h#x || i[#x,p]==_'\n'?(run('=>[',x,'] __to_discard__'); break()); i[#x,p]==_':' && i[#x,p + 1]==_':'?( # Category for (p+=2, i[#x,p]<=_' ', ++p); for (q = h#x - 1, i[#x,q]<=_' ', --q); copy(category,i[#x,p],l = min(q - p + 1,size(category) - 1)); category[l] = 0; run('=>[',x,'] __to_discard__'); ):( # Command for (q = p, q_' ', ++q); copy(command,i[#x,p],l = min(q - p,size(command) - 1)); command[l] = 0; run('=>[',x,'] \"',string(command,'@',category),'\"'); )" rm. rmn __to_discard__ l { parse_cli_trigger_$1 $2 onfail } fi parse_cli_$1 # Return 1 if last of the selected block describes a shortcut command, 0 otherwise. parse_cli_is_eqto : nbl:="n = 1; for (p = 0, (q = find(#-1,_'\n',p))>=0, ++n, p = ++q); n" u {$nbl"==1 && find(#-1,'eq. to \'')>=0"} # # Implements 'bashcompletion' mode for command 'parse_cli'. # parse_cli_bashcompletion : v 0 use_vt100 sort_list +,n +e "#"\n\ "# Bash completion rules for 'gmic'."\n\ "#"\n\ "# This file has been generated automatically."\n\ "# Do not edit!"\n\ "#"\n\ "# This file should be copied/renamed in '/usr/share/bash-completion/completions/gmic'."\n\ "#"\n\n\ "_gmic()"\n\ "{"\n\ " local cur prev opts coms"\n\ " if type -t _init_completion >/dev/null; then"\n\ " _init_completion -n = || return"\n\ " else"\n\ " COMPREPLY=()"\n\ " cur=\"${COMP_WORDS[COMP_CWORD]}\""\n\ " prev=\"${COMP_WORDS[COMP_CWORD-1]}\""\n\ " fi" coms=" coms=\"" c= # Extract list of available commands. foreach { if s=['{n}'];s[0]==_'_' continue fi ('{n}') l. { s -,{'@'} name={0,t} rm } => $name coms.=$c$name c=" " } +e $coms"\""\n\ " opts=$(echo \"$coms\" | sed \"s: \\([^ ]\\+\\): \\1 -\\1 \\+\\1:g\")"\n # Duplicate list of arguments for command shortcuts. repeat $! { if s=['{$>,n}'];s[0]==_'_' continue fi name={$>,n} if ${parse_cli_is_eqto[$>]} +l[$>] { s -,{'"eq. to "'} k. s -,39 eqto={0,t} rm } l[$>] { pass[$eqto] 0 k. => $name onfail warn "Warning: Ignoring shortcut '"$_vt100_c$name$_vt100_m$_vt100_b"', "\ "links to unknown command '"$_vt100_c$eqto$_vt100_m$_vt100_b"'." => __to_discard__ } fi } rmn __to_discard__ # Extract list of arguments. +e " case \"${prev}\" in" repeat $! { if s=['{$>,n}'];s[0]==_'_' continue fi l[$>] { name={n} if n=['$name'];"n[0]!=_'_' && find(n,'input')!=0 && find(n,'output')!=0 && "\ "find(n,'load')<0 && find(n,'save')<0 && "\ "n!='i' && n!='o' && n!='m' && n!='it' && n!='ot'" if isin(['$name'],'help','h') +e " \""$name"\" | \"-"$name"\" | \"+"$name"\")" +e " COMPREPLY=( $(compgen -W \"$coms\" -- \"$cur\") ); return 0;;" else s -,{'\n'} k[0] discard {'#@cli'} max {'" "'} autocrop {'" "'} s -,{'": "'} autocrop {'" "'} rm[0] replace {'" "'},{'_'} n_args=0 args,c= repeat $! { if ['{/{$>,t}}']!='(+)' args.=$c{$>,t} c=" " n_args+=1 fi } if $n_args==1 args="> "$args fi if ['$args']!=0 +e " \""$name"\" | \"-"$name"\" | \"+"$name"\")" +e " COMPREPLY=( $(compgen -W \""{/{/$args}}"\") ); return 0;;" fi fi rm 0 fi } } +e " esac"\n\n\ " COMPREPLY=( $(compgen -W \"$opts\" -- \"$cur\") )"\n\ " if type -t _filedir >/dev/null; then"\n\ " _filedir"\n\ " else"\n\ " comptopt -o filenames 2>/dev/null"\n\ " COMPREPLY=( $(compgen -f -- ${cur}) )"\n\ " fi"\n\ "}"\n\ "complete -F _gmic -o filenames gmic" rm parse_cli_trigger_bashcompletion : parse_cli_trigger_print $* # # Implements 'ascii' mode for command 'parse_cli'. # parse_cli_ascii : use_vt100 if !narg($_shell_cols) _shell_cols:=${-shell_cols}-5 fi category= n_category=0 if !narg($_section) _section=1 fi repeat $! { if s=['{$>,n}'];s[0]==_'_' continue fi l[$>] { # Detect and display new category. if 0$_display_categories ('{n}') l. { s -,{'@'} if $!>1 cat={t} fi rm } if ['$cat']!=['$category'] n_category+=1 category=$cat ('$_section.$n_category." "') ('$category') +f.. {'" "'} +f.. {'-'} a[-4,-3] x a[-2,-1] x +e \n" "$_vt100_r$_vt100_b{-2,t}$_vt100_n +e " "$_vt100_r{t}$_vt100_n rm[-2,-1] fi fi # Parse command declaration. s -,{'\n'} discard {'#@cli'} max {'" "'} autocrop {'" "'} +l[0] { s -,{'": "'} autocrop {'" "'} name={0,t} rm[0] is_builtin=0 if $! is_builtin:="find(#-1,'(+)')>=0" if $is_builtin rm. fi fi +e "\n "$_vt100_m$_vt100_b$name${"s1,s0=\" (+)\", u $s"$is_builtin}:$_vt100_n foreach { if ${parse_cli_is_eqto.} s -,39 eqto={1,t} k[0] +e " "${_vt100_i}"Shortcut for command '"$_vt100_m$_vt100_b$eqto$_vt100_n"'." else eqto= l. { _gmd2ascii_cut $_shell_cols,8 if $!>1 i[1--2] ('"\\\\\n "':y) a y fi } str=" "{t}${"if "$<" u \" |\" else u \"\" fi"} +e ${_vt100_c}$str$_vt100_n fi } rm } rm[0] # Parse command description. n_example=0 nl="\n" repeat $! { if {$>,i==_':'} l[$>] { if "h==1 || (h==2 && i[-1,2]==_' ')" rm ('": "':y) fi # Empty description lines rows {1+(i[1]==_'" "')},100% if "find(#0,'(eq. to ')>=0" # Found 'Eq. to' description. +l { s -,{'\47'} shortcut={1,t} rm onfail shortcut="(unknown)" rm } +e $nl" ("${_vt100_i}"equivalent to shortcut command '"$_vt100_m$_vt100_b$shortcut$_vt100_n"')." nl="\n" elif "find(#0,'Default value:')>=0 || find(#0,'Default values:')>=0" # Found 'Default value(s):' description if !0$_no_default_values s +,{':'} if $!>2" && "h#1==1" && "i("#1")==_':' i[0] ('$_vt100_b':y) i[2] ('$_vt100_n':y) fi a y gmd2ascii $_shell_cols,5 if $!>1 i[1--2] ('"\n "':y) a y fi autocrop {'\n'} +e "\n "{/{t}} nl= fi elif i==_'$'" && "i[1]!=_'$' # Found example description if !0$_no_examples rows 1,100% autocrop {'" "'} _gmd2ascii_cut {$_shell_cols-8},7 if $!>1 i[1--2] ('"\\\\\n "':y) a y fi autocrop {'\n'} example={/{t}} n_example+=1 if $n_example==1 example_str=${_vt100_b}"Example:"$_vt100_n"\n "[#$n_example] nl="\n" else example_str=" "[#$n_example] fi +e $nl" "$example_str" "$_vt100_c$example$_vt100_n nl= fi elif i==_'$'" && "i[1]==_'$' # Found link to tutorial page if !0$_no_tutorial_link if h==2 url=https://gmic.eu/tutorial/$name else rows 2,100 autocrop {'" "'} url={t} fi +e "\n "${_vt100_b}"Tutorial: "$_vt100_n$_vt100_u$url$_vt100_n nl= fi else # Other kind of description line parse_cli_text_ascii. if w +e $nl{/{t}} nl= fi fi } fi } rm 0 } } rm u $eqto parse_cli_text_ascii : if !narg($_shell_cols) _shell_cols:=${-shell_cols}-5 fi replace_str "\\n","\n" gmd2ascii $_shell_cols,0 # Ensure output text contains no more than two consecutive newlines. # Also add a 4-chars left margin on each line. if w s +,{'\n'} if {i==_'\n'} rm. fi # Remove last newline. eval "repeat (l,p, i(#p)==_'\n'?( h(#p)>2?resize(#p,1,1,1,1,0) ):( resize(#p,1,h#p + 4,1,1,0,0,0,1); copy(i[#p,0],_' ',4,1,0)) )" a y fi parse_cli_trigger_ascii : if "['$1']!='*'" 1,$!,1,1," begin(ref([lowercase(['$1']),_'@'],str)); ref(lowercase(name(#y)),nm); !find(nm,str)?y:-1" discard. -1 if h k[{i}] else rm fi fi # # Implements 'html' mode for command 'parse_cli'. # parse_cli_html : v 0 use_vt100 e "" # Sort by categories. # repeat $! { ('{$>,n}') l. { s +,{'@'} rv a y nm={t} } rm. =>[$>] $nm } # Rename as 'category@name' # sort_list +,n # Sort by categories # repeat $! { ('{$>,n}') l. { s +,{'@'} rv a y nm={t} } rm. =>[$>] $nm } # Rename as 'name@category' # Generate html page 'List of Commands'. html=""\n\ ""\n\ " "\n\ " "\n\ " "\n\ " "\n\ " "\n\n\ " G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\ "- Reference Documentation"\n\ " "\n\ " "\n\ " "\n\n\ " "\n\ " "\n\n\ "
"\n\n\ " "\n\n\ ""\n\n\ ""\n list_categories,c= category,p_category= n,is_tr,is_table,row=0 repeat $! { if s=['{$>,n}'];s[0]==_'_' continue fi l[$>] { ('{n}') l. { s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm } if ['$category']!=['$p_category'] strvar $category category_id=${} if $is_tr if $n%5" && "$row>1 html.=" "\n fi html.=" "\n is_tr=0 fi if $is_table html.="
"\n is_table=0 fi html.="\n

"$category":

"\n\ " "\n ('$category') replace_str. ",","," lcategory={t} rm. list_categories.=$c$lcategory c=, is_table=1 n,row=0 fi if !${parse_cli_is_eqto.} is_builtin:="find(#-1,'(+)')>=0" if !($n%5) if $is_tr html.=" "\n fi html.=" "\n is_tr=1 row+=1 fi if ['$name']=='index' url_name=_index.html else url_name=$name.html fi if $is_builtin html.=" "\n else html.=" "\n fi n+=1 fi p_category=$category } } if $is_tr if $n%5" && "$row>1 html.=" "\n fi html.=" "\n fi if $is_table html.="
"$name""$name"
"\n fi # Add 'Shortcuts' category. html.="\n

Command Shortcuts:

"\n\ " "\n\ " "\n repeat $! { if s=['{$>,n}'];s[0]==_'_' continue fi l[$>] { ('{n}') l. { s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm } if ${parse_cli_is_eqto.} is_builtin:="find(#-1,'(+)')>=0" +l { s -,{'"eq. to \47"'} k. s -,{'\47'} k[0] autocrop {'" "'} eqto={t} rm } ('$name') replace_str. ">",">" replace_str. "<","<" html_name={t} rm. html.=" " if $is_builtin html.="" else html.="" fi html.=""\n n+=1 fi } } if $is_table html.="
Shortcut name"\ "Equivalent command name
"$html_name""$eqto""$eqto"
"\n fi html.=\n\ ""\n\n\ ""\n\ "
"\n\ " "\n\ " " ({'$html'}:y) # Insert TOC for categories. toc_html="

Categories:

    "\n repeat narg({/$list_categories}) { arg0 $>,{/$list_categories} category=${} ('$category') replace_str. ",","," category={t} rm. strvar $category category_id=${} toc_html.="
  • "$category"
  • "\n } toc_html.="
  • Command Shortcuts
  • "\n toc_html.="
" replace_str. "",$toc_html ot. list_of_commands.html if !isfile('index.html') x "ln -fs list_of_commands.html index.html" fi rm. # Retrieve full list of commands and discard those starting with '_'. repeat $! { if s=['{$<,n}'];s[0]==_'_' rm[$<] else l[$<] { ('{n}') l. { s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm } is_eqto=${parse_cli_is_eqto.} if !$is_eqto _is_$name=1 else rm fi onfail rm } fi } # Generate html page for each command. foreach { ('{n}') l. { s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm } e "\r > "$_vt100_c[#{1+$>}]$_vt100_n" "{`s=vector48(_'" "');copy(s,['$category" / "$name']);s`} # Find previous and next commands. if !$> previous= else pass[{$>-1}] 1 ('{n}') discard. {'_c1'} previous={t} rm[-2,-1] fi if !$< next= else pass[{$>+1}] 1 ('{n}') l. { s -,{'@'} next={0,t} rm } rm. fi if ['$name']=='index' url_name=_index.html else url_name=$name.html fi if ['$previous']=='index' url_previous=_index.html else url_previous=$previous.html fi if ['$next']=='index' url_next=_index.html else url_next=$next.html fi strvar[] $category category_id=${} html=""\n\ ""\n\ " "\n\ " "\n\ " "\n\ " "\n\ " "\n\n\ " G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image Processing "\ "- Reference Documentation - "$name""\n\ " "\n\ " "\n\ " "\n\ " "\n\ " "\n\n\ " "\n\ " "\n\n\ " "\ "
"\n\n\ " "\n\n\ ""\n\n if !0$_pdf_output html.=" " if narg($previous)" || "narg($next) html.=""\n fi html.="
"\ "Table of Contents  ▸  "\ "List of Commands  ▸  "\ ""$category"  ▸  "\ ""$name"" if narg($previous) html.="◀  "$previous"" fi if narg($previous)" && "narg($next) html.="    |    " fi if narg($next) html.=""$next"  ▶" fi html.="
\n" else html.=""\n fi # Parse command declaration. s -,{'\n'} discard {'#@cli'} max {'" "'} autocrop {'" "'} +l[0] { s -,{'": "'} autocrop {'" "'} rm[0] is_builtin=0 if $!" && find(#-1,'(+)')>=0" is_builtin=1 rm. fi if $is_builtin html.="

"\ $name"

Built-in command
"\n else html.="

"$name"

"\n fi if $! html.="\n

Arguments:

\n
    "\n if $!>1 or="    or" else or= fi foreach { if !$< or= fi html.="
  • "{t}""$or"
  • "\n } html.="
"\n else html.="\n

No arguments

"\n fi rm } rm[0] # Parse command description. html.="\n

Description:

"\n line=0 n_example=0 nb_examples=0 is_tutorial_tag=0 tutorial_html= repeat $! { if {$>,i==_':'} l[$>] { if "h==1 || (h==2 && i[-1,2]==_' ')" rm ('": "':y) fi # Empty description lines rows {1+(i[1]==_'" "')},100% if i==_'$'" && "i[1]!=_'$' nb_examples+=1 fi } fi } foreach { if "find(#0,'(eq. to ')>=0" # Found 'Eq. to' description. +l { s -,{'\47'} replace_str[1] "&","&" replace_str[1] "<","<" replace_str[1] ">",">" shortcut={1,t} rm onfail shortcut="(unknown)" rm } html.="

(equivalent to shortcut command "$shortcut\ ").

"\n elif "find(#0,'See also:')>=0" # Found 'See also:' description. s +,{':'} autocrop {'" "'} if $!>2" && "h#1==1" && "i("#1")==_':' html.="\n

See also:

"\n rm[0,1] fi a y gmd2html 0 html.=" "{t}\n elif "find(#0,'Default value:')>=0 || find(#0,'Default values:')>=0" # Found 'Default value(s):' description s +,{':'} autocrop {'" "'} if $!>2" && "h#1==1" && "i("#1")==_':' html.="\n

Default values:

"\n rm[0,1] fi a y gmd2html 0 html.="

"{t}"

"\n elif i==_'$'" && "i[1]!=_'$' # Found 'Example of use' description. rows 1,100% autocrop {'" "'} if !0$_pdf_output replace_str "image.jpg",""\ "image.jpg" fi example={/{t}} n_example+=1 if $n_example>1 basename=${name}_$n_example.jpg else basename=${name}.jpg fi if $n_example==1 if !$is_tutorial_tag html.="" is_tutorial_tag=1 fi html.="\n

Example"${"if "$nb_examples">1 u s else u \"\" fi"}" of use:

"\n fi if $nb_examples!=1 html.="

• Example \#"$n_example"

"\n fi html.="
"$example"

"\n if 0$_pdf_output html.="
"\n fi if isfile('img/f_$basename') # Single output image if !0$_pdf_output html.=" "\ ""\ "
Command: "$example"
"\n else html.=""\n fi elif isfile('img/f0_$basename') # Multiple output images i=0 html.="
" for isfile('img/f${i}_$basename') { if !0$_pdf_output html.=" "\ ""\ "
Command: "$example"
"\n else html.=""\n fi i+=1 } html.="
" fi if 0$_pdf_output html.="
"\n fi elif i==_'$'" && "i[1]==_'$' # Found link to tutorial page. if !$is_tutorial_tag html.="" is_tutorial_tag=1 fi if h==2 url=https://gmic.eu/tutorial/$name l[] { it $url if "find(#-1,'404 Not Found')>=0" url=https://gmic.eu/oldtutorial/_$name fi rm } else rows 2,100 autocrop {'" "'} url={t} fi tutorial_html.="

This command has a "\ "tutorial page.

" else # Other kind of description line +autocrop {"' '"} is_list:="isin(i,_'*',_'-',_'.')" rm. ('\n') a y replace_str "\\n","\n" gmd2html 0 if $line==1" && "!$is_list html.="
"\n fi html.={t} if i[-1,2]!=_'\n' html.="\n" fi line+=1 fi } # Generate page. html.=\n\ ""\n\n\ ""\n\ "
"\n\ " "\n\ " " rm ({'$html'}:y) replace_str. "",$tutorial_html # Insert tutorial button => $name ot $url_name } rm e "\r > "${_vt100_g}{`s=vector64(_'" "');copy(s,'"Parsing done!"');s`}$_vt100_n parse_cli_trigger_html : parse_cli_trigger_print $* # # Implements 'image' mode for command 'parse_cli'. # parse_cli_images : v 0 use_vt100 e "" old_category,category= n_category,n_example=0 if !isfile('image.jpg') l[] { sp bottles,640 onfail testimage2d 500 } o. image.jpg rm. fi n_global=0 repeat $! { if s=['{$>,n}'];s[0]==_'_' continue fi l[$>] { ('{n}') l. { s -,{'@'} name={0,t} if $!>1 category={1,t} else category= fi rm } if ['$category']!=['$old_category'] n_category+=1 e $_vt100_r"\n ** Section \#"$n_category": "$category"."$_vt100_n"\n" fi s -,{'\n'} discard {'#@cli'} max {'" "'} autocrop {'" "'} n_example=0 foreach { if "i==_':' && ( for (p = 1, p1 basename=${name}_$n_example.jpg else basename=${name}.jpg fi e $_vt100_c[$n_global]$_vt100_n" Command '"$_vt100_g$name$_vt100_n"': $ "$example skip_rendering:="['"$name"'][0]==_'_' || "\ "(isfile(['"{/${_parse_cli_images_path}f_$basename}"']) &&"\ " isfile(['"{/${_parse_cli_images_path}t_$basename}"'])) || "\ "(isfile(['"{/${_parse_cli_images_path}f0_$basename}"']) && "\ "isfile(['"{/${_parse_cli_images_path}t0_$basename}"']))" if !$skip_rendering # Generate picture only if the file doesn't yet exist m "run_example : "$example l[] { reset etime=$| run_example etime="done in "$_vt100_n{_round($|-$etime,0.01)}"s"$_vt100_m _parse_cli_images 1 if $! if $!>1 repeat $! { o[$>] ${_parse_cli_images_path}f$>_$basename,85 } else o ${_parse_cli_images_path}f_$basename,85 fi rs 480,320,2 if $!>1 repeat $! { o[$>] ${_parse_cli_images_path}t$>_$basename,75 } else o ${_parse_cli_images_path}t_$basename,75 fi rm fi # onfail # rm gui_error_preview "Unable to generate preview image" # o ${_parse_cli_images_path}f_$basename,85 # rs 480,320,2 # o ${_parse_cli_images_path}t_$basename,75 # rm # etime="failed" } um run_example else etime="skipped" fi e "\r"$_vt100_c[$n_global]$_vt100_n" Command '"$_vt100_g$name$_vt100_n"': $ "\ $example" "${_vt100_m}"("$etime")."$_vt100_n fi } old_category=$category rm 0 } } rm # Generate a single image from a list of images, for the reference documentation. # $1 : boolean that tells if the image name is inserted at the botton of the generated image. _parse_cli_images : l[] { check "isbool(${1=1})" is_arg=1 arg1=$1 onfail is_arg=0 arg1=1 } if !$is_arg noarg fi if !$! rm return fi W,H=640,480 foreach { nm={n} label=[$>]:" "'$nm' if ${-is_mesh3d} # 3D mesh label2="("{i[6]}" vert., "{i[7]}" prim.)" r3d 1,1,0,-80 r3d 0,1,0,80 snapshot3d {max($W,$H)} else # Regular 1D,2D or 3D image label2="("{w}x{h}x{d}x{s}")" if s>4 channels 0,2 fi r 100%,100%,100%,{max(s,3)},{s==1} if d>1 # Volumetric image -> Render as a 2D image +slices 50% +z[0] 50%,0,0,50%,100%,100% permute. zyxc +z[0] 0,50%,0,100%,50%,100% permute. xzyc rs... $W,$H,2 rs.. ,{-3,h},,1 rs. {-3,w},,1 s:=min(w#-2,h) if $s>64 projections3d[0] 50%,50%,50%,1 mv[0] $! r3d. 1,1,0,-80 r3d. 0,1,0,80 snapshot3d. $s else rm[0] 0 fi eW,eH:=[w#0+w#1,h#0+h#2] if $eW>$W" || "$eH>$H fact:=min($W/$eW,$H/$eH)*100 r $fact%,$fact%,1,100%,2 fi n 0,255 fs={0,max(w,h)*7%} if {0,h>1.5*$fs} to[0] XY,4,2,$fs fi if {1,h>1.5*$fs} to[1] XZ,4,2,$fs fi if {2,h>1.5*$fs} to[2] YZ,4,2,$fs fi if w to[3] 3D,4,2,$fs frame[3] xy,1,200 fi frame[0-2] xy,1,200 a[0,1] x a[1,2] x a y fi fi if w>5*h r $W,{$H/3},1,100%,1 elif h>5*w r {$W/3},$H,1,100%,1 else rs $W,$H,2 fi n 0,255 if $arg1 # Insert label at bottom if s>3 drgba fi r {[w,h]+2},1,100%,0,0,0.5,0.5 - 245 r {[w+10,h+5]},1,100%,0,0,0.5,0.5 r 100%,{h+1},1,100%,0,0,0,1 + 245 0 t. $label" "$label2,5,0,32,1,1 if w>w#0" || "h>h#0 rm. 0 t. $label\n$label2,5,0,32,1,1 if w>w#0" || "h>h#0 rs. {0,[w,h]},2 fi fi *. -1 n. 0,255 to_rgb. - 245 a y,0.5 + 245 fi } c 0,255 parse_cli_trigger_images : parse_cli_trigger_print $* # # Implements 'list' mode for command 'parse_cli'. # (return a list of all commands, separated by commas). # parse_cli_list : res= repeat $! { ('{$>,n}') l. { s -,{'@'} res.=$c{0,t} c=, rm } } rm u $res # # Implements 'print' mode for command 'parse_cli'. # parse_cli_print : v 0 e "" sort_list +,n foreach { is_native:="find(#-1,' : (+)')>0" ('{n}') l. { s -,{'@'} name={0,t} rm } if $is_native +e $name" (+)" else +e $name fi } rm parse_cli_trigger_print : if "['$1']!='*'" 1,$!,1,1," begin(ref(lowercase(['$1']),str)); ref(lowercase(name(#y)),nm); arobace = find(nm,_'@'); inrange(find(nm,str),0,arobace - 1)?y:-1" discard. -1 k[{^}] fi #@cli parse_gmd #@cli : Parse and tokenize selected images, viewed as text strings formatted with the G'MIC markdown syntax. parse_gmd : e[^-1] "Parse and tokenize images$? viewed as text strings formatted with the G\47MIC markdown syntax." y 1 a[^-1] .,y rm. eval[^] ">"${-_gmd_tokens}" begin( section = subsection = subsubsection = subsubsubsection = anchor = bullet = subbullet = subsubbullet = center = right = table = blockquote = detail_block = code_block = shell = bold_italic_a = bold_italic_u = bold_a = bold_u = italic_a = italic_u = strikethrough = underline = monospace = value_set = word_highlight = url = page_link = text_link1 = text_link2 = img1 = img2 = pipeline = formula = opening_offset = -1; blank(c) = isin(c,_' ',_'\n',s_whitespace,s_tab,0); semiblank(c) = isin(c,_' ',_'\n',_'.',_',',_';',_':',_'!',_'?', _')',_'(',_'[',_']',_'|',_'-',s_whitespace,s_tab,0); newline(c) = isin(c,_'\n',0); reset(c) = (#c = -1); res = crop(); ); pc = j[-1]; c = i; nc = j[1]; ac = j[2]; ym1 = y - 1; y1 = y + 1; y2 = y + 2; linestart = newline(pc); is_raw = max(blockquote,code_block,shell,monospace,url,page_link,text_link2,img2,pipeline,formula)>=0; non_escaped = is_raw || (pc!=_'\\'); # Manage document layout. #------------------------ # Escaped character. !non_escaped && isin(c,_'%',_'$',_'\\',_'\'',_'`',_'*',_'_',_'{',_'}',_'[',_']',_'<',_'>',_'(',_')',_'#',_'+',_'-',_'.',_'!')?( res[ym1] = i[ym1] = 0; ): # Section: '# Section name'. linestart && c==_'#' && nc==_' '?( # Opening section = y; ): section>=0 && newline(c)?( # Closing opening_offset = section; res[section++] = s_section; while (blank(res[section]), res[section++] = 0); # Remove leading whitespaces res[y] = e_section; for (p = opening_offset - 1, p>=0 && res[p]==_'\n', res[p--] = 0); # Remove newlines preceding for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks for (p = y1, p=0 && newline(c)?( # Closing opening_offset = subsection; res[subsection++] = s_subsection; res[subsection++] = 0; while (blank(res[subsection]), res[subsection++] = 0); # Remove leading whitespaces res[y] = e_subsection; for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks for (p = y1, p=0 && newline(c)?( # Closing opening_offset = subsubsection; res[subsubsection++] = s_subsubsection; copy(res[subsubsection],0,2,1,0); subsubsection+=2; while (blank(res[subsubsection]), res[subsubsection++] = 0); # Remove leading whitespaces res[y] = e_subsubsection; for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks for (p = y1, p=0 && newline(c)?( # Closing opening_offset = subsubsubsection; res[subsubsubsection++] = s_subsubsubsection; copy(res[subsubsubsection],0,3,1,0); subsubsubsection+=3; while (blank(res[subsubsubsection]), res[subsubsubsection++] = 0); # Remove leading whitespaces res[y] = e_subsubsubsection; for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks for (p = y1, p=0 && newline(c)?( # Closing opening_offset = anchor; res[anchor++] = s_anchor; while (blank(res[anchor]), res[anchor++] = 0); # Remove leading whitespaces res[y] = e_anchor; res[y1]==_'\n'?(res[y1] = 0); for (p = ym1, p>opening_offset && blank(res[p]), res[p--] = 0); # Remove trailing blanks ): # List bullet: '* List item'. linestart && isin(c,_'-',_'*',_'+') && nc==_' ' && bullet<0?( # Opening bullet = y; ): bullet>=0 && newline(c)?( # Closing opening_offset = bullet; res[bullet++] = s_bullet; res[bullet] = 0; res[y] = e_bullet; ): # Sublist bullet: ' * Sub-list item'. linestart && c==_' ' && nc==_' ' && isin(ac,_'-',_'*',_'+') && j[3]==_' ' && subbullet<0?( # Opening subbullet = y; ): subbullet>=0 && newline(c)?( # Closing opening_offset = subbullet; res[subbullet++] = s_subbullet; copy(res[subbullet],0,2,1,0); res[y] = e_subbullet; ): # Subsublist bullet: ' * Subsub-list item'. linestart && c==_' ' && nc==_' ' && ac==_' ' && j[3]==_' ' && isin(j[4],_'-',_'*',_'+') && j[5]==_' ' && subsubbullet<0?( # Opening subsubbullet = y; ): subsubbullet>=0 && newline(c)?( # Closing opening_offset = subsubbullet; res[subsubbullet++] = s_subsubbullet; copy(res[subsubbullet],0,4,1,0); res[y] = e_subsubbullet; ): # Centered block: '\n===\n Centered text\n===\n'. linestart && c==_'=' && nc==c && ac==c && newline(j[3])?( center<0?( # Opening center = y; ): center>=0 && y>center + 3?( # Closing opening_offset = center; res[center++] = s_center; copy(res[center],0,3,1,0); center+=3; newline(res[ym1])?(res[ym1] = 0); res[y] = e_center; copy(res[y + 1],0,3,1,0); ); ): # Right block: '\n>>>\n Right-aligned text\n>>>\n'. linestart && c==_'>' && nc==c && ac==c && newline(j[3])?( right<0?( # Opening right = y; ): right>=0 && y>right + 3?( # Closing opening_offset = right; res[right++] = s_right; copy(res[right],0,3,1,0); right+=3; newline(res[ym1])?(res[ym1] = 0); res[y] = e_right; copy(res[y + 1],0,3,1,0); ); ): # Table : '\n| foo | bar\n' (with first delimiter '||', '|+' or '|-'). table<0 && linestart && c==_'|'?( # Opening table = y; ): table>=0 && newline(c) && nc!=_'|'?( # Closing opening_offset = table; d = i[table + 1]; d==_'|'?(res[table++] = s_htable; res[table++] = 0): d==_'-'?(res[table++] = s_vtable; res[table++] = 0): d==_'+'?(res[table++] = s_hvtable; res[table++] = 0):(res[table++] = s_table); i[table]==_' '?(res[table++] = 0); newline(i[table]) && i[table + 1]==_'|'?(copy(res[table],0,2,1,0); table+=2); for (p = table, p line1\n> line2'. code_block<0 && blockquote<0 && linestart && c==_'>' && blank(nc)?( # Opening blockquote = y; ): blockquote>=0 && newline(c) && (nc!=_'>' || !blank(ac))?( # Closing opening_offset = blockquote; res[blockquote++] = s_blockquote; res[blockquote] = 0; # Remove leading '>' when multi-lines: for (p = blockquote, p'?copy(res[p],0,res[p + 1]==_' '?2:1,1,0)); res[y] = e_blockquote; ): # Detail block: '\n??? Details:\nDetail block\n???\n'. linestart && c==_'?' && nc==c && ac==c && blank(j[3])?( detail_block<0?( # Opening detail_block = y; ): detail_block>=0 && y>detail_block + 3 && newline(j[3])?( # Closing opening_offset = detail_block; res[detail_block++] = s_detail_block; copy(res[detail_block],0,2,1,0); res[ym1] = e_detail_block; copy(res[y],0,4,1,0); ); ): # Code block: '\n~~~\n Code block\n~~~\n'. linestart && isin(c,_'~',_'`') && nc==c && ac==c && newline(j[3])?( code_block<0?( # Opening code_block = y; ): code_block>=0 && y>code_block + 3?( # Closing opening_offset = code_block; res[code_block++] = s_code_block; copy(res[code_block],0,3,1,0); code_block+=3; copy(res[code_block],i[code_block],y - code_block); # Cancel tokens found inside for (p = code_block, p=0 && y>shell + 3?( # Closing opening_offset = shell; res[shell++] = s_shell; copy(res[shell],0,3,1,0); shell+=3; copy(res[shell],i[shell],y - shell); # Cancel tokens found inside res[ym1] = e_shell; copy(res[y],0,4,1,0); ); ): # Horizontal rule: '---', '___' or '***'. linestart && isin(c,_'-','_','*') && nc==c && ac==c && newline(j[3])?( res[y] = s_hrule; copy(res[y1],0,3,1,0); ): # Manage text style. #------------------- # Bold italic (asterisk): '***bold italic text***'. non_escaped && c==_'*' && nc==c && ac==c?( # Opening bold_italic_a<0?( bold_italic_a = y; ): bold_italic_a>=0 && y>bold_italic_a + 3 && j[3]!=_'*'?( # Closing opening_offset = bold_italic_a; res[bold_italic_a++] = s_bold_italic_a; res[bold_italic_a++] = 0; res[bold_italic_a] = 0; res[y] = e_bold_italic_a; res[y1] = res[y2] = 0; ); ): # Bold italic (underscore): '___bold italic text___'. non_escaped && c==_'_' && nc==c && ac==c?( bold_italic_u<0 && semiblank(pc)?( # Opening bold_italic_u = y; ): bold_italic_u>=0 && y>bold_italic_u + 3 && semiblank(j[3])?( # Closing opening_offset = bold_italic_u; res[bold_italic_u++] = s_bold_italic_u; res[bold_italic_u++] = 0; res[bold_italic_u] = 0; res[y] = e_bold_italic_u; res[y1] = res[y2] = 0; ); ): # Bold (asterisk): '**bold text**'. non_escaped && c==_'*' && nc==c?( # Opening bold_a<0?( bold_a = y; ): bold_a>=0 && y>bold_a + 2 && ac!=_'*'?( # Closing opening_offset = bold_a; res[bold_a++] = s_bold_a; res[bold_a] = 0; res[y] = e_bold_a; res[y1] = 0; ); ): # Bold (underscore): '__bold text__'. non_escaped && c==_'_' && nc==c?( bold_u<0 && semiblank(pc)?( # Opening bold_u = y; ): bold_u>=0 && y>bold_u + 2 && semiblank(ac)?( # Closing opening_offset = bold_u; res[bold_u++] = s_bold_u; res[bold_u] = 0; res[y] = e_bold_u; res[y1] = 0; ); ): # Italic (asterisk): '*italic text*'. non_escaped && c==_'*' && pc!=c?( # Opening italic_a<0?( italic_a = y; ): italic_a>=0 && y>italic_a + 1 && ac!=_'*'?( # Closing opening_offset = italic_a; res[italic_a] = s_italic_a; res[y] = e_italic_a; ); ): # Italic (underscore): '_italic text_'. non_escaped && c==_'_' && pc!=c?( italic_u<0 && semiblank(pc)?( # Opening italic_u = y ): italic_u>=0 && y>italic_u + 1 && semiblank(nc)?( # Closing opening_offset = italic_u; res[italic_u] = s_italic_u; res[y] = e_italic_u; ); ): # Strikethrough: '~~strikethrough text~~'. non_escaped && c==_'~' && nc==c && pc!=c?( # Opening strikethrough<0?( strikethrough = y; ): strikethrough>=0 && y>strikethrough + 2?( # Closing opening_offset = strikethrough; res[strikethrough++] = s_strikethrough; res[strikethrough] = 0; res[y] = e_strikethrough; res[y1] = 0; ); ): # Underline: '==strikethrough text=='. non_escaped && c==_'=' && nc==c && pc!=c?( # Opening underline<0?( underline = y; ): underline>=0 && y>underline + 2?( # Closing opening_offset = underline; res[underline++] = s_underline; res[underline] = 0; res[y] = e_underline; res[y1] = 0; ); ): # Monospace: '`monospace text`'. non_escaped && c==_'`'?( monospace<0?( # Opening monospace = y; ): monospace>=0 && y>monospace + 1?( # Closing opening_offset = monospace; res[monospace++] = s_monospace; copy(res[monospace],i[monospace],y - monospace); # Cancel tokens found inside for (p = monospace, p=0 && y>value_set + 1 && semiblank(nc)?( # Closing opening_offset = value_set; res[value_set++] = s_value_set; copy(res[value_set],i[value_set],y - value_set); # Cancel tokens found inside res[y] = e_value_set; ): # Word highlight: ' 'word_highlight' '. non_escaped && c==_'\'' && nc!=c?( word_highlight<0 && semiblank(pc)?( # Opening word_highlight = y; ): word_highlight>=0 && y>word_highlight + 1 && semiblank(nc)?( # Closing opening_offset = word_highlight; res[word_highlight++] = s_word_highlight; copy(res[word_highlight],i[word_highlight],y - word_highlight); # Cancel tokens found inside res[y] = e_word_highlight; ); ): # URL: ''. non_escaped && c==_'<' && url<0 && (crop(0,y1,1,8)=='https://' || crop(0,y1,1,7)=='http://' || crop(0,y1,1,6)=='ftp://')?( # Opening url = y; ): non_escaped && c==_'>' && url>=0 && y>url + 1?( # Closing opening_offset = url; res[url++] = s_url; copy(res[url],i[url],y - url); # Cancel tokens found inside res[y] = e_url; ): # Page link (command or reference): ' ''command or page name'' ' (not a URL!). non_escaped && c==_'\'' && nc==c?( page_link<0 && semiblank(pc)?( # Opening page_link = y; ): page_link>=0 && y>page_link + 2 && semiblank(ac)?( # Closing opening_offset = page_link; res[page_link++] = s_page_link; res[page_link++] = 0; copy(res[page_link],i[page_link],y - page_link); # Cancel tokens found inside res[y] = e_page_link; res[y1] = 0; ); ): # Text link: '[link text](https://url.com)'. non_escaped && c==_'[' && text_link1<0 && text_link2<0 && img1<0?( # Opening text_link1 = y; ): non_escaped && c==_']' && nc==_'(' && text_link1>=0 && text_link2<0 && img1<0?( # Middle text_link2 = y; ): non_escaped && c==_')' && text_link2>=0 && img1<0?( # Closing opening_offset = text_link1; res[text_link1] = s_text_link; res[text_link2++] = m_text_link; res[text_link2++] = 0; copy(res[text_link2],i[text_link2],y - text_link2); # Cancel tokens found inside res[y] = e_text_link; ): # Image: '![alt caption](https://url.com/image.png)'. non_escaped && c==_'!' && nc==_'[' && img1<0 && img2<0?( # Opening img1 = y; ): non_escaped && c==_']' && nc==_'(' && img1>=0 && img2<0?( # Middle img2 = y; ): non_escaped && c==_')' && img2>=0?( # Closing opening_offset = img1; res[img1++] = s_img; res[img1++] = 0; res[img2++] = m_img; res[img2++] = 0; copy(res[img1],i[img1],img2 - img1 - 2); copy(res[img2],i[img2],y - img2); # Cancel tokens found inside res[y] = e_img; ): # G'MIC pipeline (image): '%% G'MIC pipeline %%'. non_escaped && c==_'%' && nc==c?( pipeline<0 && semiblank(pc)?( # Opening pipeline = y; ): pipeline>=0 && y>pipeline + 2 && semiblank(ac)?( # Closing opening_offset = pipeline; res[pipeline++] = s_pipeline; res[pipeline++] = 0; copy(res[pipeline],i[pipeline],y - pipeline); # Cancel tokens found inside while (blank(i[pipeline]), res[pipeline++] = 0); # Remove leading blanks for (p = ym1, blank(res[p]), res[p--] = 0); # Remove trailing blanks res[y] = e_pipeline; res[y1] = 0; ); ): # Formula in LaTeX: '$$ x = cos(x + 2) $$`'. non_escaped && c==_'$' && nc==c?( formula<0 && semiblank(pc)?( # Opening formula = y; ): formula>=0 && y>formula + 2 && semiblank(ac)?( # Closing opening_offset = formula; res[formula++] = s_formula; res[formula++] = 0; copy(res[formula],i[formula],y - formula); # Cancel tokens found inside while (blank(i[formula]), res[formula++] = 0); # Remove leading blanks for (p = ym1, blank(res[p]), res[p--] = 0); # Remove trailing blanks res[y] = e_formula; res[y1] = 0; ); ): # G'MIC word: '\G'MIC'. semiblank(pc) && c==_'\\' && nc==_'G' && ac==_'\'' && j[3]==_'M' && j[4]==_'I' && j[5]==_'C' && semiblank(j[6])?( res[y] = s_gmic; copy(res[y1],0,4,1,0); res[y + 5] = e_gmic; ): # Whitespaces. c==_' '?( # Keep consecutive whitespaces. c==_' ' && (linestart || pc==c)?( res[y] = s_whitespace; ); # Reset tokens that cannot contain whitespace. reset(word_highlight); reset(url); text_link2>=0?(reset(text_link1); reset(text_link2)); img2>=0?(reset(img1); reset(img2)); ): # Escaped newline. c==_'\\' && nc==_'n'?( res[y] = _'\n'; res[y + 1] = 0; ): # Tab. c==_'\t'?( res[y] = s_tab; ): # Newline. c==_'\n'?( # Reset tokens that cannot contain newlines. reset(bold_italic_a); reset(bold_italic_u); reset(bold_u); reset(bold_a); reset(italic_a); reset(italic_u); reset(strikethrough); reset(underline); reset(monospace); reset(word_highlight); reset(url); reset(page_link); text_link2>=0?(reset(text_link1); reset(text_link2)); img2>=0?(reset(img1); reset(img2)); ); # Cancel opened tokens starting after the opening offset of the latest closed token. #------------------------------------------------------------------------------------ opening_offset>=0?( section>=opening_offset ? reset(section); subsection>=opening_offset ? reset(subsection); subsubsection>=opening_offset ? reset(subsubsection); subsubsubsection>=opening_offset ? reset(subsubsubsection); anchor>=opening_offset ? reset(anchor); bullet>=opening_offset ? reset(bullet); subbullet>=opening_offset ? reset(subbullet); subsubbullet>=opening_offset ? reset(subsubbullet); center>=opening_offset ? reset(center); right>=opening_offset ? reset(right); table>=opening_offset ? reset(table); blockquote>=opening_offset ? reset(blockquote); detail_block>=opening_offset ? reset(detail_block); code_block>=opening_offset? reset(code_block); shell>=opening_offset? reset(shell); bold_italic_a>=opening_offset ? reset(bold_italic_a); bold_italic_u>=opening_offset ? reset(bold_italic_u); bold_a>=opening_offset ? reset(bold_a); bold_u>=opening_offset ? reset(bold_u); italic_a>=opening_offset ? reset(italic_a); italic_u>=opening_offset ? reset(italic_u); strikethrough>=opening_offset ? reset(strikethrough); underline>=opening_offset ? reset(underline); monospace>=opening_offset ? reset(monospace); value_set>=opening_offset ? reset(value_set); word_highlight>=opening_offset ? reset(word_highlight); url>=opening_offset ? reset(url); page_link>=opening_offset ? reset(page_link); text_link1>=opening_offset || text_link2>=opening_offset ? (reset(text_link1); reset(text_link2)); img1>=opening_offset || img2>=opening_offset ? (reset(img1); reset(img2)); pipeline>=opening_offset ? reset(pipeline); formula>=opening_offset ? reset(formula); opening_offset = -1; ); end(copy(i[0],res))" discard 0 #@cli gmd2html : _include_default_header_footer={ 0:none | 1:Reference | 2:Tutorial | 3:News } : (no arg) #@cli : Convert selected gmd-formatted text images to html format. #@cli : Default values: 'include_default_header_footer=1'. gmd2html : skip "${1=}" l[] { is_arg:=isint("$1") onfail is_arg=0 } if $is_arg embed_html=$1 else embed_html=1 noarg fi parse_gmd s_section,e_section={${-_gmd_tokens}"[s_section,e_section]"} foreach { nm$>={b} strvar ${nm$>} fnm=${} if "i=="$s_section" && find(#-1,"$e_section")>0" +rows 1,{"find(#-1,"$e_section")-1"} title$>={t} rm. else title$>= fi . # [0] = Output, [1] = Input eval. "> begin("${-_gmd_tokens}${-_gmd_write}"); c = i; c>0?( c==_'\n' ? write('
\n'): c==_'&' ? write('&'): c==_'\47' ? write('''): c==_'>' ? write('>'): c==_'\"' ? write('"'): c==_'<' ? write('<'): write(c); ):( isin(c,e_bold_a,e_bold_u,e_bold_italic_a,e_bold_italic_u,e_italic_a,e_italic_u, e_monospace,e_strikethrough,e_underline,e_word_highlight) ? write(''): isin(c,e_bullet,e_subbullet,e_subsubbullet) ? write('\n'): isin(c,e_section,e_subsection,e_subsubsection,e_subsubsubsection) ? write('\n'): c==s_section ? ( ind_e = find(#1,e_section,y); run('_gmd2html_section. ',y + 1,',',ind_e - 1); ref(get('_gmd_name',1024,1),str_nam); write_nl(); write('
'); ): c==s_subsection ? ( ind_e = find(#1,e_subsection,y); run('_gmd2html_section. ',y + 1,',',ind_e - 1); ref(get('_gmd_name',1024,1),str_nam); write_nl(); write('
'); ): c==s_subsubsection ? ( ind_e = find(#1,e_subsubsection,y); run('_gmd2html_section. ',y + 1,',',ind_e - 1); ref(get('_gmd_name',1024,1),str_nam); write_nl(); write('
'); ): c==s_subsubsubsection ? ( ind_e = find(#1,e_subsubsubsection,y); run('_gmd2html_section. ',y + 1,',',ind_e - 1); ref(get('_gmd_name',1024,1),str_nam); write_nl(); write('
'); ): c==s_anchor ? ( ind_e = find(#1,e_anchor,y); run('_gmd2html_section. ',y + 1,',',ind_e - 1); ref(get('_gmd_name',1024,1),str_nam); write('\n'); copy(i[y],0,ind_e - y + 1,1,0); ): c==s_bullet ? write('
'): c==s_subbullet ? write('
'): c==s_subsubbullet ? write('
'): c==s_center ? write('
\n'): c==e_center ? (write_nl(); write('
')): c==s_right ? write('
\n'): c==e_right ? (write_nl(); write('
')): c==s_table ? (write_nl(); write('\n
')): c==s_htable ? (write_nl(); write('\n
')): c==s_vtable ? (write_nl(); write('\n
')): c==s_hvtable ? (write_nl(); write('\n\n\n
')): c==m_table ? write(''): c==n_table ? write('
'): c==e_table ? write('
\n'): c==s_blockquote ? (write_nl(); write('
\n')): c==e_blockquote ? (write('\n
\n')): c==s_detail_block ? ( write_nl(); write('
\n'); ny = y + 1; nc = j[1]; nc!=e_detail_block?( j[1] = 0; ind_e = find(#1,e_detail_block,ny); nc==_' '?( # Has a title ind_nl = find(#1,_'\n',ny); ind_nl<0 || ind_nl>ind_e?(ind_nl = ind_e); write(#1,y + 2,ind_nl - y - 2); copy(i[ny],0,ind_nl - y - 1,1,0); i[ind_nl]!=e_detail_block?(i[ind_nl] = 0); ):write('Details:'); write('\n'); ) ): c==e_detail_block ? (write_nl(); write('
\n')): c==s_code_block ? write('
'): c==e_code_block ? write('
\n'); c==s_shell ? ( ind_e = find(#1,e_shell,y); run('_gmd2html_shell. ',y + 1,',',ind_e - 1); ref(get('_gmd_command',1024,1),str_com); write('
$ '); len = find(str_com,0); write(str_com,len); write('

\n'); write(#-1,0,h(#-1)); run('rm.'); write('
\n'); copy(i[y],0,ind_e - y + 1,1,0); ): c==s_hrule ? (write_nl(); write('
\n')): c==s_bold_italic_a ? write(''): c==s_bold_italic_u ? write(''): c==s_bold_a ? write(''): c==s_bold_u ? write(''): c==s_italic_a ? write(''): c==s_italic_u ? write(''): c==s_strikethrough? write(''): c==s_underline? write(''): c==s_monospace ? write(''): c==s_value_set ? write('{'): c==e_value_set ? write('}'): c==s_word_highlight ? write(''): c==s_url ? ( ind_e = find(#1,e_url,y); write(''); ): c==e_url ? write(''): c==s_page_link ? ( ind_e = find(#1,e_page_link,y); run('_gmd2html_page_link. ',y + 1,',',ind_e - 1); ref(get('_gmd_link',1024,1),str_link); ref(get('_gmd_text',1024,1),str_text); write(''); len = find(str_text,0); write(str_text,len); write(''); copy(i[y],0,ind_e - y + 1,1,0); ): c==s_text_link ? ( ind_m = find(#1,m_text_link,y); ind_e = find(#1,e_text_link,ind_m); write(''): # Local link write('\" target=\"_blank\">'); ): c==m_text_link ? ( ind_e = find(#1,e_text_link,y); copy(i[y],0,ind_e - y + 1,1,0); write(''); ): c==s_img ? ( ind_m = find(#1,m_img,y); ind_e = find(#1,e_img,ind_m); ind_d = find(#1,_'.',ind_e - 1,-1); is_video = 0; ind_d>ind_m?( # Check for a video ref(crop(#1,0,ind_d + 1,1,3),ext3); ref(crop(#1,0,ind_d + 1,1,4),ext4); is_video = isin(ext3,'mp4','ogg') || ext4=='webm' ); is_video?( write('
" else td_$cat="" fi } html=""\n\ ""\n\ " "\n\ " "\n\ " "\n\ " "\n\ " "\n\n\ " G'MIC - GREYC's Magic for Image Computing: A Full-Featured Open-Source Framework for Image "\ "Processing - Gallery"\n\ " "\n\ " "\n\ " "\n\ " "\n\n\ " "\n\ " "\n\ " "\n\n\ " "\n\ " "\n\n\ "

Image Gallery

"\n\n\ "

This gallery gives a quick overview of the kind of features and generic filters available in the "\ "G'MIC open-source image processing framework.

"\n\ "

All the images below have been processed by the CLI interface "\ "gmic"\ " of G'MIC, from a set of initial 2D color images."\n\ " Click on an image to enlarge it and display the G'MIC command-line "\ "used for the processing (note: to reproduce this, you may have to escape some characters, "\ "according to the type of shell you use!).

"\n\ "

Remember, G'MIC lets you define your own image pipelines through"\ "custom command files."\n\ " Your custom filters can be easily added afterwards in the plug-in for "\ "GIMP or Krita.

"\n\ "

For more details, visit the tutorial pages as well as the "\ "technical reference to get a full documentation on this "\ "software.

"\n html_menu=" \n "\ $td_arrays"Arrays & Frames"\ $td_artistic"Artistic"\ $td_blackandwhite"B&W"\ $td_colors"Colors"\ $td_deformations"Deformations"\ $td_filtering"Filtering"\ $td_patterns"Patterns"\ $td_3dmeshes"3D Meshes"\ $td_stylization"Stylization"\ $td_codesamples"Code samples"\n\ "

\n\n" html=${html}${html_menu}" \n" repeat $nb_commands { command=${"arg "1+$>,$commands} is_stylization:=['$command']=='_gallery_stylization' e $_vt100_m" - Command '"$_vt100_b$command$_vt100_n"' ["{1+$>}/$nb_commands"]." +l { # Parse command definition and examples s -,{'"#@cli "$command" :"'} if $!<2 s -,{'"#@cli "$command"\n"'} i[1] ('\n') a[-2,-1] y fi if $!<2 warn " ** Command '"$command"' not found! **" else k. eval " ref(crop(#-1),str); ind = 0; while ((ind = find(str,'\n#@cli ',ind))>=0, ++ind; str[ind+6]!=_':' ? break() : str[ind+7]==_' ' && str[ind+8]==_'$' && str[ind+9]==_' '?( beg = ind + 10; end = find(str,_'\n',beg) - 1; ref(vector1024(0),com); run('+rows[0] ',beg,',',end); ); )"; rm[0] nb_examples=$! repeat $nb_examples { # For each example found example$nex={$>,t} e $_vt100_g" $ "${example$nex}$_vt100_n sample=${"arg0 "{($nex+8*$ncat)%narg($pics)},$pics} is_codesample=0 l[] { # Create a html representation of the command ('${example$nex}') y is_input:="find(#-1,'image.jpg')>=0" if !$is_input" && find(#-1,'sample ')>=0" s +,{'"sample "'} if {0,crop()=='"sample "'" && "$!>1} l[1] { s +,{'" "'} if "find(#0,_',')==-1" sample={0,t} is_input=1 fi a y } fi a y fi replace_str. "image.jpg",\ ""$sample".png" replace_str. "_output_mode=1","" # Discard '_fps=?' and '_label=?' l. { s +,{'" "'} repeat $! { if {$<,crop(0,0,1,5)=='_fps='" || "crop(0,0,1,7)=='_label='} rm[$<] fi } a y } l. { s +,{'" "'} repeat $! { if {$<,crop(0,0,1,8)=='https://'} l[$<] { # Add hyperlink if crop(0,0,1,24)=='https://gmic.eu/samples/' is_codesample=1 basename_codesample={`crop(0,24,1,h-24)`} filename_codesample="../../resources/samples"/$basename_codesample url_codesample="https://gmic.eu/samples/"$basename_codesample if $1 x "lftp sftp://"$GMIC_LOGIN":@ovh -e \"put -O /home/"$GMIC_LOGIN"/www/gmic/samples "\ "\\\""$filename_codesample"\\\"; quit\" >/dev/null" fi i[0] ('""') else i[0] ('""') fi ('') y a y } fi } a y } replaced_example$nex={t} rm } ('${example$nex}') replace_str. "https://gmic.eu/samples/","../../resources/samples/" example$nex={t} rm. m "_run : _preview_area_width,_preview_area_height=450,300 "${example$nex} if $is_input sample_=${sample}_ else sample_= fi filename_original=img/${category}_${sample_}original_$nex.jpg filename_thumb_original=img/${category}_${sample_}thumb_original_$nex.jpg if "find(['"${example$nex}"'],' _fps=')>=0" filename_full=img/${category}_${sample_}full_$nex.gif filename_thumb=img/${category}_${sample_}thumb_$nex.gif _is_animated=1 else filename_full=img/${category}_${sample_}full_$nex.jpg filename_thumb=img/${category}_${sample_}thumb_$nex.jpg _is_animated=0 fi etime= _label= if !isfile('{/$filename_thumb}') l[] { if $is_input sp $sample,600 o image.jpg rm fi db3d m3d md3d f3d l3d sl3d ss3d 0.8 srand 512 etime=$| _run etime:=_round($|-$etime,0.01) if $is_stylization +l { _gallery o $filename_full,70 rm } k. _gallery width,height:=[w,h] else _gallery width,height:=[w,h] if $_is_animated o $filename_full,$_fps else o $filename_full,70 fi fi e "\r"$_vt100_g" $ "${example$nex}" (done in "$_vt100_n${etime}"s"$_vt100_g")."$_vt100_n crop 5,5,{w-6},{h-6} # Remove frame added by '_gallery' frame xy,3%,255 rs 440,440,3 drop_shadow 2,2,2 if $_is_animated rs 230,230,3 else rs 300,300,3 fi 100%,100%,1,3,245 blend[^-1] .,alpha,1,1 rm. if $_is_animated o $filename_thumb,$_fps else o $filename_thumb,60 fi rm if $is_input image.jpg _gallery rs $width,$height,5 c 0,255 to "Input",2%,2%,6% - 255 r $width,$height,1,3,0,0,0.5,0.5 + 255 o $filename_original,60 crop 5,5,{w-6},{h-6} # Remove frame added by '_gallery' frame xy,3%,255 rs 440,440,3 drop_shadow 2,2,2 if $_is_animated rs 230,230,3 else rs 300,300,3 fi 100%,100%,1,3,245 blend[^-1] .,alpha,1,1 rm. o $filename_thumb_original,60 rm fi } fi is_samesize=0 l[] { $filename_full width,height:=round([w,h]*(max(w,h)<300?1.75:1)) if isfile(['{/${filename}_original}']) $filename_original is_samesize:=w==w#0" && "h==h#0 fi rm } if !$is_samesize filename_original=$filename_full fi if !($col%$nb_cols) html=${html}" \n" fi if $nb_examples==1 counter= else counter=" "[{$>+1}/$nb_examples]"" fi html_etime= if narg($etime) html_etime="
(generated in "${etime}"s)" fi html_codesample= if $is_codesample html_codesample="\n
"\ "\n
\n"
               it[] $url_codesample html_codesample=${html_codesample}{t} rm.
               html_codesample=${html_codesample}"\n
\n"\ "
\n"\ "[ Source code ]

\n" fi if ['$_label']==0 _label=$command$counter else ('$_label') replace_str. "~"," " _label={t} rm. fi if $is_input html=${html}\ " \n" else html=${html}\ " \n" fi nex+=1 col:=($col+1)%$nb_cols if !$col html=${html}" \n" fi } fi rm } } if $col html=${html}" \n" fi html=${html}"
\n"\ " \""gallery_$command$nex"\"
"\ ${_label}${html_codesample}"
\n"\ "
\n"\ " \""gallery_$command$nex"\"\n"\ "
\n"\ "
Command: "\ "$ gmic "${replaced_example$nex}""\ ${html_etime}"
\n"\ " \""gallery_$command$nex"\""\ "
"\ ${_label}${html_codesample}"
\n"\ "
\n"\ " \""gallery_$command$nex"\"\n"\ "
\n"\ "
Command: "\ "$ gmic "${replaced_example$nex}""\ ${html_etime}"

\n"\ ${html_menu}\ "
"\n\ " "\n\ " " ('$html') ot. $category.html rm. } rm # Transfer on G'MIC server x "ln -fs artistic.html index.html" x "ln -fs "$HOME/work/src/gmic/html/header1.html" ." x "ln -fs "$HOME/work/src/gmic/html/footer.html" ." if $1 e "\n * Transfer gallery on G'MIC server.\n" x "lftp sftp://"$GMIC_LOGIN":@ovh -e \"mirror -eRL . /home/"$GMIC_LOGIN"/www/gmic/gallery ; quit\"" fi e " * All done!\n" # Generate a single image from a list of images, for the gallery. _gallery : foreach { if ${-_is_mesh3d} r3d 1,1,0,-80 r3d 0,1,0,30 animate3d 20,0,5,0,1.5,0 s z +rv[1--2] else if w>8192 z 0,8191 elif h>8192 rows 0,8191 fi n 0,255 fi } if !$_is_animated +__gallery if w>1024 r:=round(1024*100/w,0.1) r[^-1] $r%,$r%,1,100%,2 fi rm. fi foreach { if s==1 r {w},{h},1,3 elif s==4 drgba else r {w},{h},1,3,0 fi if w<=h" && "h<256 rs ,256,2 elif h<=w" && "w<256 rs 256 fi if w<=h" && "h>620 rs ,620,2 elif h<=w" && "w>620 rs 620 fi if h<48 r 100%,48 fi if w<48 r 48,100% fi if $_is_animated" && "(w>480" || "h>480) rs 480,480,2 fi frame xy,1,0 frame xy,4,255 } if $_is_animated - 255 r ${-max_wh},1,3,0,0,0.5,0.5 + 255 else - 255 __gallery + 255 if w<256 - 255 r 256,100%,1,100%,0,0,0.5,0.5 + 255 fi if h<256 - 255 r 100%,256,1,100%,0,0,0.5,0.5 + 255 fi fi __gallery : if $!==2 if w>h a y else a x fi else montage A # append_tiles 2 fi # Generate data files for the G'MIC Online website. update_gmicol : e[^-1] "Generate XML file and thumbnails for the G'MIC Online website." filename=$_path_rc/update$_version.gmic if !isfile('$filename') up fi rm it $filename # Generate XML file. m "parse_gui_trigger_gmicol : _update_gmicol $*" v + +parse_gui. gmicol v - um parse_gui_trigger_gmicol ot. gmicol.xml rm. # Generate thumbnails. m "parse_gui_trigger_thumbnails : _update_gmicol $*" w[] v + parse_gui thumbnails v - um parse_gui_trigger_thumbnails _update_gmicol : # Keep only a subset of existing filters nmd 3,\ "About/♥ Support Us ! ♥",\ "About/About G'MIC",\ "Arrays & Tiles/Annular Steiner Chain Round Tile",\ "Arrays & Tiles/Array [Faded]",\ "Arrays & Tiles/Array [Mirrored]",\ "Arrays & Tiles/Array [Random Colors]",\ "Arrays & Tiles/Array [Random]",\ "Arrays & Tiles/Array [Regular]",\ "Arrays & Tiles/Ascii Art",\ "Arrays & Tiles/Chessboard",\ "Arrays & Tiles/Dices",\ "Arrays & Tiles/Grid [Cartesian]",\ "Arrays & Tiles/Grid [Hexagonal]",\ "Arrays & Tiles/Grid [Triangular]",\ "Arrays & Tiles/Loose Photos",\ "Arrays & Tiles/Make Seamless [Diffusion]",\ "Arrays & Tiles/Make Seamless [Patch-Based]",\ "Arrays & Tiles/Ministeck",\ "Arrays & Tiles/Puzzle",\ "Arrays & Tiles/Shuffle Patches",\ "Arrays & Tiles/Taquin",\ "Arrays & Tiles/Tileable Rotation",\ "Arrays & Tiles/Tiled Isolation",\ "Arrays & Tiles/Tiled Normalization",\ "Arrays & Tiles/Tiled Parameterization",\ "Arrays & Tiles/Tiled Random Shifts",\ "Arrays & Tiles/Tiled Rotation",\ "Artistic/Angoisse Anguish",\ "Artistic/Aurora",\ "Artistic/Barbouillage Paint Daub",\ "Artistic/Black Crayon Graffiti",\ "Artistic/Blockism",\ "Artistic/Bokeh",\ "Artistic/Brushify",\ "Artistic/Cartoon",\ "Artistic/Circle Abstraction",\ "Artistic/Color Abstraction Paint",\ "Artistic/Colored Pencils",\ "Artistic/Comic Book",\ "Artistic/Cubism",\ "Artistic/Cutout",\ "Artistic/Diffusion Tensors",\ "Artistic/Dream Smoothing",\ "Artistic/Ellipsionism",\ "Artistic/Felt Pen",\ "Artistic/Finger Paint",\ "Artistic/Fractalize",\ "Artistic/Ghost",\ "Artistic/Graphic Boost",\ "Artistic/Graphic Novel",\ "Artistic/Hard Sketch",\ "Artistic/Highlight Bloom",\ "Artistic/Hope Poster",\ "Artistic/Hough Sketch",\ "Artistic/Illustration Look",\ "Artistic/Kuwahara",\ "Artistic/Lineart",\ "Artistic/Lylejk's Painting",\ "Artistic/Make Squiggly",\ "Artistic/Morphology Painting",\ "Artistic/Paint With Brush",\ "Artistic/Painting",\ "Artistic/Pen Drawing",\ "Artistic/Photoillustration",\ "Artistic/Polygonize [Delaunay]",\ "Artistic/Polygonize [Energy]",\ "Artistic/Poster Edges",\ "Artistic/Posterize",\ "Artistic/Posterized Dithering",\ "Artistic/Quadtree Variations",\ "Artistic/Rodilius",\ "Artistic/Sharp Abstract",\ "Artistic/Simple Noise Canvas",\ "Artistic/Skeletik",\ "Artistic/Sketch",\ "Artistic/Smooth Abstract",\ "Artistic/Stringify",\ "Artistic/Stylize",\ "Artistic/Vector Painting",\ "Artistic/Warhol",\ "Artistic/Whirl Drawing",\ "Black & White/B&W Stencil",\ "Black & White/Black & White",\ "Black & White/Charcoal",\ "Black & White/Colorize [with Colormap]",\ "Black & White/Colorize Lineart [Auto-Fill]",\ "Black & White/Colorize Lineart [Smart Coloring]",\ "Black & White/Desaturate Norm",\ "Black & White/Dithering",\ "Black & White/Emboss",\ "Black & White/Engrave",\ "Black & White/Filaments",\ "Black & White/Freaky B&W",\ "Black & White/Ink Wash",\ "Black & White/Multi-Layer Etch",\ "Black & White/Pencil",\ "Black & White/Pencil Portrait",\ "Black & White/Stamp",\ "Black & White/Threshold Etch",\ "Colors/Abstraction",\ "Colors/Auto Balance",\ "Colors/Basic Adjustments",\ "Colors/Boost Chromaticity",\ "Colors/Boost-Fade",\ "Colors/Brightness",\ "Colors/Channel Processing",\ "Colors/CMYK Tone",\ "Colors/Color Balance",\ "Colors/Color Blindness",\ "Colors/Color Grading",\ "Colors/Color Temperature",\ "Colors/Colormap",\ "Colors/Contrast",\ "Colors/Customize CLUT",\ "Colors/Dark Sky",\ "Colors/Equalize HSI-HSL-HSV",\ "Colors/Equalize HSV",\ "Colors/HSL Adjustment",\ "Colors/HSV Select",\ "Colors/Hue Lighten-Darken",\ "Colors/LMS Adjustment",\ "Colors/Local Contrast",\ "Colors/Metallic Look",\ "Colors/Mixer [CMYK]",\ "Colors/Mixer [HSV]",\ "Colors/Mixer [Lab]",\ "Colors/Mixer [PCA]",\ "Colors/Mixer [RGB]",\ "Colors/Mixer [YCbCr]",\ "Colors/Normalize Brightness",\ "Colors/Retinex",\ "Colors/Retro Fade",\ "Colors/RGB Tone",\ "Colors/Saturation EQ",\ "Colors/Select-Replace Color",\ "Colors/Selective Desaturation",\ "Colors/Sepia",\ "Colors/Softlight",\ "Colors/Specific Saturation",\ "Colors/Temperature Balance",\ "Colors/Tone Presets",\ "Colors/Tune HSV Colors",\ "Colors/User-Defined",\ "Colors/Vintage Style",\ "Colors/Zone System",\ "Contours/Convolve",\ "Contours/Curvature",\ "Contours/Difference of Gaussians",\ "Contours/Distance Transform",\ "Contours/Edge",\ "Contours/Edges",\ "Contours/Edges (Canny)",\ "Contours/Edges Offsets",\ "Contours/Gradient Norm",\ "Contours/Gradient RGB",\ "Contours/Isophotes",\ "Contours/Laplacian",\ "Contours/Local Orientation",\ "Contours/Morphological Filter",\ "Contours/Segmentation",\ "Contours/Skeleton",\ "Contours/Super-Pixels",\ "Contours/Thin Edges",\ "Deformations/Breaks",\ "Deformations/Cartesian Transform",\ "Deformations/Circle Transform",\ "Deformations/Conformal Maps",\ "Deformations/Continuous Droste",\ "Deformations/Crease",\ "Deformations/Distort Lens",\ "Deformations/Drop Water",\ "Deformations/Equirectangular to Nadir-Zenith",\ "Deformations/Euclidean - Polar",\ "Deformations/Fish-Eye",\ "Deformations/Flower",\ "Deformations/Kaleidoscope [Blended]",\ "Deformations/Kaleidoscope [Polar]",\ "Deformations/Kaleidoscope [Reptorian-Polar]",\ "Deformations/Kaleidoscope [Symmetry]",\ "Deformations/Logarithmic Distortion",\ "Deformations/Moon2panorama",\ "Deformations/Perspective",\ "Deformations/Pixel Push",\ "Deformations/Pointcaré Disk",\ "Deformations/Point Warp",\ "Deformations/Polar Transform",\ "Deformations/Quadrangle",\ "Deformations/Raindrops",\ "Deformations/Random",\ "Deformations/Reflection",\ "Deformations/Ripple",\ "Deformations/Seamcarve",\ "Deformations/Sinusoidal Water Distortion",\ "Deformations/Sphere",\ "Deformations/Spherize",\ "Deformations/Square to Circle",\ "Deformations/Stereographic Projection",\ "Deformations/Symmetrize",\ "Deformations/Textured Glass",\ "Deformations/Twirl",\ "Deformations/Water",\ "Deformations/Wave",\ "Deformations/Wind",\ "Deformations/Zoom",\ "Degradations/Add Grain",\ "Degradations/Blur [Angular]",\ "Degradations/Blur [Bloom]",\ "Degradations/Blur [Depth-Of-Field]",\ "Degradations/Blur [Gaussian]",\ "Degradations/Blur [Glow]",\ "Degradations/Blur [Linear]",\ "Degradations/Blur [Multidirectional]",\ "Degradations/Blur [Radial]",\ "Degradations/Blur [Splinter]",\ "Degradations/Chromatic Aberrations",\ "Degradations/CRT Phosphors",\ "Degradations/CRT Scanlines",\ "Degradations/CRT Sub-pixels",\ "Degradations/Dirty",\ "Degradations/Flip & Rotate Blocks",\ "Degradations/Fragment Blur",\ "Degradations/JPEG Artefacts",\ "Degradations/Lomo",\ "Degradations/Mess with Bits",\ "Degradations/Noise [Additive]",\ "Degradations/Noise [Gradient]",\ "Degradations/Noise [Perlin]",\ "Degradations/Noise [Spread]",\ "Degradations/Old-Movie Stripes",\ "Degradations/Oldschool 8bits",\ "Degradations/Pixel Sort",\ "Degradations/Rain & Snow",\ "Degradations/Random Shade Stripes",\ "Degradations/Rebuild From Similar Blocks",\ "Degradations/Scanlines",\ "Degradations/Self Glitching",\ "Degradations/Streak",\ "Degradations/UltraWarp++++",\ "Degradations/Visible Watermark",\ "Degradations/Warp by Intensity",\ "Details/Constrained Sharpen",\ "Details/DCP Dehaze",\ "Details/Details Equalizer",\ "Details/Dynamic Range Increase",\ "Details/Easy Skin Retouch",\ "Details/Emboss-Relief",\ "Details/Equalize Local Histograms",\ "Details/Freaky Details",\ "Details/High Pass",\ "Details/Local Contrast Enhancement",\ "Details/Local Normalization",\ "Details/Local Processing",\ "Details/Local Variance Normalization",\ "Details/Magic Details",\ "Details/Make Up",\ "Details/Mighty Details",\ "Details/Portrait Retouching",\ "Details/Pyramid Processing",\ "Details/Quick Tonemap",\ "Details/Sharpen [Alpha]",\ "Details/Sharpen [Deblur]",\ "Details/Sharpen [Gold-Meinel]",\ "Details/Sharpen [Gradient]",\ "Details/Sharpen [Hessian]",\ "Details/Sharpen [Inverse Diffusion]",\ "Details/Sharpen [Multiscale]",\ "Details/Sharpen [Octave Sharpening]",\ "Details/Sharpen [Richardson-Lucy]",\ "Details/Sharpen [Shock Filters]",\ "Details/Sharpen [Texture]",\ "Details/Sharpen [Tones]",\ "Details/Sharpen [Unsharp Mask]",\ "Details/Sharpen [Whiten]",\ "Details/Simple Local Contrast",\ "Details/Spotify",\ "Details/Texture",\ "Details/Texture Enhance",\ "Details/Tone Enhance",\ "Details/Tone Mapping",\ "Details/Tone Mapping [Fast]",\ "Details/YAG Effect",\ "Frames/Droste",\ "Frames/Frame [Blur]",\ "Frames/Frame [Cube]",\ "Frames/Frame [Fuzzy]",\ "Frames/Frame [Mirror]",\ "Frames/Frame [Painting]",\ "Frames/Frame [Pattern]",\ "Frames/Frame [Regular]",\ "Frames/Frame [Relief]",\ "Frames/Frame [Round]",\ "Frames/Frame [Smooth]",\ "Frames/Old Photograph",\ "Frames/Polaroid",\ "Frames/Tunnel",\ "Frames/Vignette",\ "Frequencies/Bandpass",\ "Frequencies/Fourier Analysis",\ "Frequencies/Fourier Transform",\ "Frequencies/Fourier Watermark",\ "Lights & Shadows/Burn",\ "Lights & Shadows/Contrast Swiss Mask",\ "Lights & Shadows/Dodge and Burn",\ "Lights & Shadows/Drop Shadow",\ "Lights & Shadows/Drop Shadow 3D",\ "Lights & Shadows/Equalize Light",\ "Lights & Shadows/Equalize Shadow",\ "Lights & Shadows/Guided Light Rays",\ "Lights & Shadows/Illuminate 2D Shape",\ "Lights & Shadows/Light Glow",\ "Lights & Shadows/Light Leaks",\ "Lights & Shadows/Light Patch",\ "Lights & Shadows/Light Rays",\ "Lights & Shadows/Pop Shadows",\ "Lights & Shadows/Relief Light",\ "Lights & Shadows/Shadow Patch",\ "Map Projection/Albers Equal Area Conic",\ "Map Projection/Azimuthal Equidistant",\ "Map Projection/Conic Equidistant",\ "Map Projection/Cylindrical Equal-Area Projection",\ "Map Projection/Eckert IV",\ "Map Projection/Eckert VI",\ "Map Projection/Lambert Azimuthal Equal-Area",\ "Map Projection/Lambert Conformal Conic",\ "Map Projection/Mercator Projection",\ "Map Projection/Mollweide",\ "Map Projection/Orthographic",\ "Map Projection/Rotate Equirectangular Map",\ "Map Projection/Sinusoidal Map",\ "Map Projection/Triangular Projection",\ "Patterns/Bayer Filter",\ "Patterns/Box Fitting",\ "Patterns/Camouflage",\ "Patterns/Canvas",\ "Patterns/Canvas Texture",\ "Patterns/Clouds",\ "Patterns/Cracks",\ "Patterns/Crystal",\ "Patterns/Crystal Background",\ "Patterns/Denim Texture",\ "Patterns/Fibers",\ "Patterns/Freqy Pattern",\ "Patterns/Halftone",\ "Patterns/Halftone [Generic]",\ "Patterns/Halftone Shapes",\ "Patterns/Hearts",\ "Patterns/Hedcut",\ "Patterns/Lava",\ "Patterns/Marble",\ "Patterns/Maze",\ "Patterns/Mineral Mosaic",\ "Patterns/Mosaic",\ "Patterns/Op Art",\ "Patterns/Pack Ellipses",\ "Patterns/Paper Texture",\ "Patterns/Periodic Dots",\ "Patterns/Pills",\ "Patterns/Plaid",\ "Patterns/Polka Dots",\ "Patterns/Random Color Ellipses",\ "Patterns/Random Pattern",\ "Patterns/Random Rectangles",\ "Patterns/Rays",\ "Patterns/Reaction-Diffusion",\ "Patterns/Reptile",\ "Patterns/Resynthetize Texture [FFT]",\ "Patterns/Resynthetize Texture [Patch-Based]",\ "Patterns/Rorschach",\ "Patterns/Satin",\ "Patterns/Seamless Deco",\ "Patterns/Seamless Turbulence",\ "Patterns/Shock Waves",\ "Patterns/Soft Random Shades",\ "Patterns/Sponge",\ "Patterns/Stained Glass",\ "Patterns/Stars",\ "Patterns/Stencil",\ "Patterns/Strip",\ "Patterns/Tetris",\ "Patterns/Triangular Pattern",\ "Patterns/Truchet",\ "Patterns/Turbulent Halftone",\ "Patterns/Voronoi",\ "Patterns/Weave",\ "Patterns/Whirls",\ "Rendering/3D Blocks",\ "Rendering/3D Colored Object",\ "Rendering/3D Elevation",\ "Rendering/3D Extrusion",\ "Rendering/3D Image Object",\ "Rendering/3D Lathing",\ "Rendering/3D Random Objects",\ "Rendering/Algorithm A",\ "Rendering/Ball",\ "Rendering/Circle Art",\ "Rendering/Construction Material Texture",\ "Rendering/Disco",\ "Rendering/Equation Plot [Parametric]",\ "Rendering/Equation Plot [Y=f(X)]",\ "Rendering/Gradient [Corners]",\ "Rendering/Gradient [Custom Shape]",\ "Rendering/Gradient [from Line]",\ "Rendering/Gradient [Linear]",\ "Rendering/Gradient [Radial]",\ "Rendering/Gradient [Random]",\ "Rendering/Hair Locks",\ "Rendering/Hypotrochoid",\ "Rendering/Kitaoka Spin Illusion",\ "Rendering/Lightning",\ "Rendering/Lissajous",\ "Rendering/Mandelbrot - Julia Sets",\ "Rendering/Nebulous",\ "Rendering/Neon Lightning",\ "Rendering/Newton Fractal",\ "Rendering/Plasma",\ "Rendering/Popcorn Fractal",\ "Rendering/Pseudorandom Noise",\ "Rendering/Pythagoras Tree",\ "Rendering/Quick Copyright",\ "Rendering/Rainbow",\ "Rendering/Random Signature",\ "Rendering/Rectangular Tiling",\ "Rendering/Shade Bobs",\ "Rendering/Sine Curve",\ "Rendering/Snowflake 2",\ "Rendering/Spiral",\ "Rendering/Spiral RGB",\ "Rendering/Superformula",\ "Rendering/Symmetric 2D Shape",\ "Rendering/Thorn Fractal - Secant Sea",\ "Rendering/Tree",\ "Rendering/Turbulence",\ "Rendering/Twisted Rays",\ "Rendering/Twisted Rays [Samj]",\ "Rendering/Wiremap",\ "Repair/Anti Alias",\ "Repair/Banding Denoise",\ "Repair/Bayer Reconstruction",\ "Repair/Clean Text",\ "Repair/Compression Blur",\ "Repair/Deinterlace",\ "Repair/Deinterlace2x",\ "Repair/Denoise",\ "Repair/Denoise Smooth",\ "Repair/Denoise Smooth Alt",\ "Repair/Descreen",\ "Repair/Despeckle",\ "Repair/Iain Noise Reduction 2019",\ "Repair/Iain's Fast Denoise",\ "Repair/Inpaint [Holes]",\ "Repair/Inpaint [Morphological]",\ "Repair/Inpaint [Multi-Scale]",\ "Repair/Inpaint [Patch-Based]",\ "Repair/Inpaint [Transport-Diffusion]",\ "Repair/JPEG Smooth",\ "Repair/Local Similarity Mask",\ "Repair/Moire Removal",\ "Repair/Pixel Denoise",\ "Repair/Recursive Median",\ "Repair/Red-Eye Attenuation",\ "Repair/Remove Hot Pixels",\ "Repair/Repair Scanned Document",\ "Repair/Smooth [Anisotropic]",\ "Repair/Smooth [Antialias]",\ "Repair/Smooth [Bilateral]",\ "Repair/Smooth [Block PCA]",\ "Repair/Smooth [Diffusion]",\ "Repair/Smooth [Geometric-Median]",\ "Repair/Smooth [Guided]",\ "Repair/Smooth [IUWT]",\ "Repair/Smooth [Mean-Curvature]",\ "Repair/Smooth [Median]",\ "Repair/Smooth [NL-Means]",\ "Repair/Smooth [Patch-Based]",\ "Repair/Smooth [Patch-PCA]",\ "Repair/Smooth [Perona-Malik]",\ "Repair/Smooth [Selective Gaussian]",\ "Repair/Smooth [Skin]",\ "Repair/Smooth [Thin Brush]",\ "Repair/Smooth [Total Variation]",\ "Repair/Smooth [Wavelets]",\ "Repair/Smooth [Wiener]",\ "Repair/Solidify",\ "Repair/Unpurple",\ "Repair/Unquantize [JPEG Smooth]",\ "Repair/Unstrip",\ "Repair/Upscale [DCCI2x]",\ "Repair/Upscale [Diffusion]",\ "Repair/Upscale [Edge]",\ "Repair/Upscale [Scale2x]" k[${}] sort_list +,n # Generate reference documentation pages for the G'MIC website. # $1 = upload to G'MIC server, can be { 0 | 1 }. update_reference_html : check "isbool(${1=0})" path_current=${-path_current} path_ok=$HOME/work/src/gmic/html/reference/ if ['$path_current']!=['$path_ok'] error[0--3] "Command 'update_reference_html: Command run from wrong path: '"$path_current"', "\ "should be '"$path_ok"'." fi x "rm -f *.pdf" rm e[^-1] "Generate reference documentation pages for the G'MIC website." it $HOME/work/src/gmic/src/gmic_stdlib.gmic a y # Generate image of examples. if $1 _prerelease= fi x "mkdir -p img" _parse_cli_images_path="img/" +parse_cli images parse_cli html # Generate reference pages in html. rm x "ln -fs "$HOME/work/src/gmic/html/header1.html" ." x "ln -fs "$HOME/work/src/gmic/html/footer.html" ." x "cp -rf "$HOME/work/src/gmic-community/reference/images" ." reference html,$HOME/work/src/gmic-community/reference # Upload to G'MIC server. if $1 e "\n > Transfer reference documentation on G'MIC server.\n" x "lftp sftp://"$GMIC_LOGIN":@ovh -e \"mirror -RL . /home/"$GMIC_LOGIN"/www/gmic/reference ; quit\"" fi e "\n > All done.\n" # Generate tutorial pages for the G'MIC website. # $1 = upload to G'MIC server, can be { 0 | 1 }. update_tutorial_html : check "isbool(${1=0})" path_current=${-path_current} path_ok=$HOME/work/src/gmic/html/tutorial/ if ['$path_current']!=['$path_ok'] error[0--3] "Command 'update_tutorial_html: Command run from wrong path: '"$path_current"', "\ "should be '"$path_ok"'." fi rm e[^-1] "Generate tutorial pages for the G'MIC website." path_tutorial=$HOME/work/src/gmic-community/tutorial use_vt100 # Build directory structure. e " > Build directory structure." x "ln -fs "$HOME/work/src/gmic/html/header1.html" ." x "ln -fs "$HOME/work/src/gmic/html/footer.html" ." x "mkdir -p images scripts" files 5,$path_tutorial/* l[] { ({'${}'}:y) s -,{','} for $! { file={0,t} rm[0] basename $file basename=${} 0 => $basename ext={`lowercase(['{x}'])`} rm. if ['$basename']!='img' if isdir(['$file']) files 5,$file/* ({'${}'}:y) s. -,{','} # Folder -> Recursively add files elif isin(['$ext'],'png','jpg','jpeg','gif','mp4','svg','webm') x "cp -f \""$file"\" images/" # Image/videos: Copy to sub-folder 'images/' elif isin(['$ext'],'gmic','py') x "cp -f \""$file"\" scripts/" # Scripts: Copy to sub-folder 'scripts/' elif ['$ext']=='gmd' # G'MIC Markdown -> Copy to current folder './' it[] $file ot. $basename rm. fi fi } } # Generate html pages. files 0,*.gmd files=${} repeat narg({/$files}) { arg0 $>,$files file=${} e " > Generate '"$_vt100_c$file$_vt100_n"'." t0=$| it $file replace_str. "../listmanip/","" replace_str. "../images/","images/" gmd2html 2 ot {n} rm. t1=$| e "\r > Generate '"$_vt100_c$file$_vt100_n"' (done in "$_vt100_g{_round($t1-$t0,0.1)}"s"$_vt100_n")." } x "rm -f *.gmd" # Upload to G'MIC server. if $1 e "\n > Transfer tutorial pages on G'MIC server.\n" x "lftp sftp://"$GMIC_LOGIN":@ovh -e \"mirror -RL . /home/"$GMIC_LOGIN"/www/gmic/tutorial ; quit\"" fi e "\n > All done.\n" # string2ts : ts_file.ts,strings_en.txt,strings_fr.txt # Regenerate .ts file from new translated strings (separated by newline in files strings_*). # Specified .ts file can be an empty argument (""). strings2ts : skip "${1=}" e[^-1] "Regenerate translation file by merging file '$1' and source/translated strings '$2/$3'." # Import data and extract original/translated strings. if narg($1) l[] { it "$1" lang={`" lang = vector256(); p = find(#-1,'language=\"'); p>=0?( p+=10; q = find(#-1,'\">',p); q>=0?copy(lang,i[p],q - p); ); lang"`} e " > File '$1', detected language : "$lang. s -,10 N0=0 foreach { autocrop {'" "'} if s=crop();find(s,'')>=0" && "find(s,'')>=0 discard {''} discard {''} src$N0={t} elif s=crop();find(s,'')>=0" && "find(s,'')>=0 discard {''} discard {''} dest$N0={t} N0+=1 fi } rm } else N0=0 lang= fi if $N0 e " > File '$1' contains "$N0" strings." fi l[] { it "$2" s -,10 N1=$! repeat $! { nsrc$>={$>,t} } rm } l[] { it "$3" replace_str " ;",";" s -,10 N2=$! repeat $! { ndest$>={$>,t} } rm } if $N1!=$N2 error[0--3] "Command 'strings2ts': Number of lines do not match in files '$2' and '$3'." fi e " > Files '$2' and '$3' contain "$N1" strings." # Merge all strings together (set higher priority to strings that were already in the .ts file). l[] { repeat $N1 { ({'${ndest$>}'}) => ${nsrc$>} } y } repeat $N0 { rmn ${src$>} } # Discard already-translated strings in the .ts file if $N0 i[0] 0 l[0] { rm repeat $N0 { i ({'${dest$>}'}) => ${src$>} } y } fi foreach { src={n} dest={t} if lowercase(['$src'])==lowercase(['$dest']) rm fi } sort_list +,n e " > "$!" strings remain after cleaning/merging ("{$!-$N0}" new)." # Generate .ts file. foreach { _strings2ts_src {n} src=${} _strings2ts_dest {t} dest=${} rm ({'" "\n\ " "$src""\n\ " "$dest""\n\ " "\n\n'}) } i[0] ({'""\n\ ""\n\ ""\n\ " "\n\ " FilterTextTranslator"\n\n'}) ({'" "\n\ ""\n'}) y a y _strings2ts_src : ({'"$*"'}) replace_str. "°","°" replace_str. "à","à" replace_str. "&","&" replace_str. "<","<" replace_str. ">",">" replace_str. "\"",""" replace_str. "'","'" u {t} rm. _strings2ts_dest : ({'"$*"'}) html2utf8. replace_str. "°","°" replace_str. "à","à" replace_str. "&","&" replace_str. "<","<" replace_str. ">",">" replace_str. "\"",""" replace_str. "'","'" u {t} rm. # # Output mode 'zart' (output in stdout). # parse_gui_parseparams_zart : u 1 # Tell parser to parse filter parameters parse_gui_trigger_zart : foreach { # Keep only 1-level folders => {`s=[['{n}'],0];p=find(s,_'/',size(s)-1,0);p>=0?(p=find(s,_'/',p-1,0);p>0?copy(s,s[p+1],size(s)-p));s`} } sort_list +,n parse_gui_zart : e " >> Generate output, in 'zart' mode.\n" +e "" +e "\n" current_group= N:=$_nb_filters-1 repeat $_nb_filters { f=$> e "\r >> "$_vt100_c[#$f/$N]$_vt100_n" "{`s=vector48(_'" "');copy(s,['${_f${f}_path}${_f${f}_name}']);s`} # Manage groups. 0 => {`s=['${_f${f}_path}'];s[0,size(s)-1]`} path={b} rm. if ['$current_group']!=['$path'] if ['$current_group']!=0 +e "\n" fi +e "" +e "\n" current_group=$path fi # Filter definition. _parse_gui_zart[] ${_f${f}_name} fname=${} +e "" +e "" +e " "${_f${f}_command_preview}" $""*" repeat ${_f${f}_nb_params} { p=$> _parse_gui_zart ${_f${f}_p{$p}_name} name=${} type=${_f${f}_p{$p}_type} nbargs=${_f${f}_p{$p}_nb_args} arg0= repeat $nbargs { ('${_f${f}_p${p}_a$>}') autocrop. {'\"'} _parse_gui_zart {t} arg$>=${} rm. } if ['$type']=='bool' if lowercase(['$arg0'])=='false' arg0=0 elif lowercase(['$arg0'])=='true' arg0=1 elif !isnum($arg0) arg0=0 fi +e " " elif ['$type']=='choice' default=0 n=0 args= l[] { if isint($arg0) default=$arg0 n+=1 fi onfail } c= repeat $nbargs-$n { a=$> args.=${c}"choice"$>"=\""${arg{$n+$a}}"\"" c=" " } +e " " elif ['$type']=='color' if $nbargs==1" && "['$arg0'][0]==_'#' # Convert colors specified as '#RRGGBB[AA]' l[] { ('$arg0') autocrop. {'#'} s x,-2 nbargs=$! repeat $! { a=$> hex2dec {$>,t} arg$a=${} } rm } fi args= c= repeat $nbargs { a=$> args.=$c${arg$a} c="," } +e " " elif isin(['$type'],'int','float') +e " <"$type" name=\""$name"\" default=\""$arg0"\" min=\""$arg1"\" max=\""$arg2"\" />" elif isin(['$type'],'file','filein','fileout') +e " " elif ['$type']=='folder' +e " " elif ['$type']=='link' align=-1 name= url= n=0 l[] { if isnum($arg0) align=$arg0 n+=1 fi onfail } if $nbargs-$n>1 name=${arg$n} url=${arg{$n+1}} else url,name=${arg$n} fi if !$align align=left elif $align==1 align=right else align=center fi +e " " elif ['$type']=='note' text={/$arg0} +e " " elif ['$type']=='point' +e " " elif ['$type']=='separator' +e " " elif ['$type']=='text' +e " " elif ['$type']=='value' +e " " else # Unsupported parameter +e " " fi } +e "\n" } if narg($current_group) +e "" fi +e "" e "\r >> "${_vt100_g}{`s=vector64(_'" "');copy(s,'"Output done!"');s`}$_vt100_n _parse_gui_zart : l[] { ('"$*"') replace_str "&","#amp;" replace_str. "&","&" replace_str. "<","<" replace_str. ">",">" replace_str. "\"",""" replace_str "#amp;","&" u {t} rm. onfail u "" } # Convert last ascii buffer with html codes (e.g. 配), as an UTF-8 encoded buffer. html2utf8 : if h eval " ref(crop(),source); ref(vector8(),svalue); p = 0; while (1, p = p0 = find(source,'&#',p); p<0?break(); p+=2; p>=size(source)?break(); q = find(source,';',p); q>p && q<=p+8 && !isnan(val=s2v(source,p))?( copy(svalue,source[p],q-p); svalue[q-p] = 0; val = s2v(svalue,0,1); !isnan(val) && isint(val) && val>0?( # Encode in UTF-8. val<=0x007f?( # 7 bits or less -> 1 byte i[p0++] = val; ):val<=0x07ff?( # 11 bits or less -> 2 bytes i[p0++] = (val>>6)|0xc0; i[p0++] = (val&0x3f)|0x80; ):val<=0xffff?( # 16 bits or less -> 3 bytes i[p0++] = (val>>12)|0xe0; i[p0++] = ((val>>6)&0x3f)|0x80; i[p0++] = (val&0x3f)|0x80; ):( # 21 bits or less -> 4 bytes i[p0++] = (val>>18)|0xf0; i[p0++] = ((val>>12)&0x3f)|0x80; i[p0++] = ((val>>6)&0x3f)|0x80; i[p0++] = (val&0x3f)|0x80; ); copy(i[p0],-1,q-p0+1,1,0); p = q + 1; ); ); )" discard. -1 fi # Convert last UTF-8 encoded buffer to ascii buffer with html codes. utf82html : if h eval " write_seq() = ( copy(res[q],'&#'); q+=2; s = v2s(val); l = find(s,0); copy(res[q],s,l); q+=l; res[q++]=_';' ); ref(vector(#4*wh),res); q = 0; repeat (wh,p, i = i[p]; !(i&0x80)?( # 1-byte res[q++] = i; ):(i&0xe0)==0xc0?( # 2-bytes val = (i&0x1f)<<6; i = i[p + 1]; val|= (i&0x3f); write_seq(); ):(i&0xf0)==0xe0?( # 3-bytes val = (i&0xf)<<12; i = i[p + 1]; val|= (i&0x3f)<<6; i = i[p + 2]; val|= (i&0x3f); write_seq(); ):(i&0xf8)==0xf0?( # 4-bytes val = (i&0x7)<<18; i = i[p + 1]; val|= (i&0x3f)<<12; i = i[p + 2]; val|= (i&0x3f)<<6; i = i[p + 3]; val|= (i&0x3f); write_seq(); ); ); store('res',res,1,q)" rm. $res fi # Force certain filters to move from one path to another. _upload_filters_move : m "move_filter : skip \"${""2=}\" nmd 3,\"$""1\" ind=${} if $ind basename \"$""1\" =>[$ind] \"$""2\"/${} else v 0 e[0--4] \"Cannot move unknown filter '\"${_vt100_r}\"$""1\"${_vt100_n}\"' to '\"${_vt100_g}\"$""2\"${_vt100_n}\"'\" fi" sort_list +,n move_filter "Testing/Garagecoder/Anti Alias","Repair" move_filter "Testing/Garagecoder/Auto Balance","Colors" move_filter "Testing/Garagecoder/LMS Adjustment","Colors" move_filter "Testing/Garagecoder/Compression Blur","Repair" move_filter "Testing/Garagecoder/Emboss","Black & White" move_filter "Testing/Garagecoder/JPEG Smooth","Repair" move_filter "Testing/Garagecoder/Quick Tonemap","Details" move_filter "Testing/Garagecoder/Normalize Brightness","Colors" move_filter "Testing/Garagecoder/Sharpen [Gradient]","Details" move_filter "Testing/Garagecoder/Sharpen [Tones]","Details" move_filter "Testing/Garagecoder/Temperature Balance","Colors" move_filter "Testing/Garagecoder/Unquantize [JPEG Smooth]","Repair" move_filter "Testing/Garagecoder/Upscale [Edge]","Repair" move_filter "Testing/Garagecoder/Wiremap","Rendering" move_filter "Testing/Garagecoder/Smooth [Geometric-Median]","Repair" move_filter "Testing/Iain Fergusson/Easy Skin Retouch","Details" move_filter "Testing/Iain Fergusson/Moire Removal","Repair" move_filter "Testing/Iain Fergusson/Halftone Shapes","Patterns" move_filter "Testing/Iain Fergusson/Simple Local Contrast","Details" move_filter "Testing/Iain Fergusson/Turbulent Halftone","Patterns" move_filter "Testing/Joan Rake/Deformations/Ultrawarp++++","Degradations" move_filter "Testing/Naggobot/Blockism","Artistic" move_filter "Testing/Reptorian/Blur [Splinter]","Degradations" move_filter "Testing/Reptorian/Construction Material Texture","Rendering" move_filter "Testing/Reptorian/Emboss-Relief","Details" move_filter "Testing/Reptorian/Fragment Blur","Degradations" move_filter "Testing/Reptorian/Kaleidoscope [Reptorian-Polar]","Deformations" move_filter "Testing/Reptorian/Logarithmic Distortion","Deformations" move_filter "Testing/Reptorian/Nebulous","Rendering" move_filter "Testing/Reptorian/Pixel Push","Deformations" move_filter "Testing/Reptorian/Point Warp","Deformations" move_filter "Testing/Reptorian/Popcorn Fractal","Rendering" move_filter "Testing/Reptorian/Pseudorandom Noise","Rendering" move_filter "Testing/Reptorian/Sinusoidal Water Distortion","Deformations" move_filter "Testing/Samj/Arrays & Tiles/Annular Steiner Chain Round Tile","Arrays & Tiles" move_filter "Testing/Samj/Arrays & Tiles/Reptile","Patterns" move_filter "Testing/Samj/Artistic/Angoisse Anguish","Artistic" move_filter "Testing/Samj/Artistic/Chalk It Up [Fr]","Artistic" move_filter "Testing/Samj/Artistic/Barbouillage Paint Daub","Artistic" move_filter "Testing/Samj/Artistic/Skeletik","Artistic" move_filter "Testing/Samj/Patterns/Denim Texture","Patterns" move_filter "Testing/Samj/Patterns/Soft Random Shades","Patterns" move_filter "Testing/Samj/Rendering/Pythagoras Tree","Rendering" move_filter "Testing/Samj/Rendering/Snowflake 2","Rendering" move_filter "Testing/Samj/Rendering/Twisted Rays [Samj]","Rendering" move_filter "Testing/Souphead/Disco","Rendering" move_filter "Testing/Souphead/Moon2panorama","Deformations" move_filter "Testing/Souphead/Spiral RGB","Rendering" move_filter "Testing/Souphead/Kitaoka Spin Illusion","Rendering" move_filter "Testing/Zonderr/Spiral","Rendering" um move_filter upload_filters : e[^-1] "Upload filter definition file on G'MIC server.\n" rm # Be sure to get the latest version of filters. x "cd "$HOME"/work/src/gmic && git pull" x "cd "$HOME"/work/src/gmic-community && git pull" # Define the list of compatible versions. (335,336,$_version) sort. discard. store. compat # Import filters from stdlib and community. files $HOME/work/src/gmic-community/include/*.gmic files=${},$HOME/work/src/gmic/src/gmic_stdlib.gmic repeat narg($files) { l[] { file=${"arg0 $>,"$files} it[] $file basename $file basename=${} if ['$basename']=='sylvie_alexandre.gmic' s +,{'"#@gui "'} i[1--2:2] ('"#@gui ________Testing\n#@gui Samj\n"') y a y elif ['$basename']=='template.gmic' rm 0 fi } } i[1--2] ('"\n#@gui _________________\n"') y a y # Create update file. +l. { e "** Generate filter update file." m "parse_gui_trigger_update : _upload_filters_move" v + parse_gui. update v - um parse_gui_trigger_update # Upload filter files on G'MIC server. e "** Upload filter update." ot ${_path_rc}update$_version.gmic if "d = date(3); h = date(4); h>=7 && d>=1 && d<=5" url=http://bit.ly/2uaBRMB # url=http://bit.ly/2CmhX65 else url=http://bit.ly/2uaBRMB # https://bit.ly/33lRzX4 # https://bit.ly/2WeKVPv url=http://bit.ly/2uaBRMB # fi replace_str "David Tschumperlé","David Tschumperlé" o cimgz:/tmp/update$_version.gmic,uint8 $compat repeat h { v:=i[$>] _upload[] ${_path_rc}update$_version.gmic,plain_update$v.gmic,1 _upload[] /tmp/update$_version.gmic,update$v.gmic,1 } rm } # Create JSON file. +l. { e "** Generate JSON filters file." v + parse_gui. json v - # Upload filter files on G'MIC server. e "** Upload JSON filters." ot ${_path_rc}/update$_version.json $compat repeat h { v:=i[$>] _upload[] ${_path_rc}/update$_version.json,update$v.json,1 } rm } # Create filter listing. +l. { e "** Generate filter listing." v + parse_gui. list v - ot /tmp/gui_filters.txt rm } rm _upload : skip "${2=""}","${3=0}" if narg("$2") out="$2" else basename "$1" out=${} fi if !narg($GMIC_LOGIN) GMIC_LOGIN=${"gmic_ftp 0"} GMIC_PASSWD=${"gmic_ftp 1"} fi if narg($GMIC_LOGIN) x $3,"lftp sftp://"$GMIC_LOGIN":@ovh -e \"put -O /home/"$GMIC_LOGIN"/www/gmic/ "\ "\\\"$1\\\" -o \\\""$out"\\\"; quit\" >/dev/null" fi # Upload or list released binaries on the G'MIC web server. # $1=version number (e.g "1.6.7_pre") # $2={ 0:print list of URLs | 1:upload files } upload_binaries : check "isbool(${2=1})" is_pre=${"strcontains $1,_pre"} N=0 file$N=gmic_$1_debian11_bullseye_amd64.deb N+=1 file$N=gmic_$1_debian12_bookworm_amd64.deb N+=1 file$N=gmic_$1_ubuntu20-04_focal_amd64.deb N+=1 file$N=gmic_$1_ubuntu21-10_impish_amd64.deb N+=1 file$N=gmic_$1_ubuntu22-04_jammy_amd64.deb N+=1 file$N=gmic_$1_gimp2.10_debian11_bullseye_amd64.zip N+=1 file$N=gmic_$1_gimp2.10_debian12_bookworm_amd64.zip N+=1 file$N=gmic_$1_gimp2.10_ubuntu20-04_focal_amd64.zip N+=1 file$N=gmic_$1_gimp2.10_ubuntu21-10_impish_amd64.zip N+=1 file$N=gmic_$1_gimp2.10_ubuntu22-04_jammy_amd64.zip N+=1 file$N=gmic_$1_cli_win64.zip N+=1 file$N=gmic_$1_lib_win64.zip N+=1 file$N=gmic_$1_qt_win64.zip N+=1 file$N=gmic_$1_gimp2.10_win64.zip N+=1 file$N=gmic_$1_gimp2.10_win64.exe N+=1 if !$2 # Only get list of URLs e[0--3] "List URLs of released binaries ($1) from the G'MIC web server.\n" repeat $N { file=${file$>} is_win=${strcontains[]" "$file,win} if $is_win folder="windows" else folder="linux" fi e "http://gmic.eu/files/"$folder/$file } e "" else # Upload files e[0--3] "Upload released binaries ($1) on the G'MIC web server." t0=$| n=0 t=0 e "- Waiting for binary files to be build." do repeat $N { file=${file$>} if isfile(['{/$file}']) strreplace $file,_$1_,_ file_short=${} is_win=${strcontains[]" "$file,win} if $is_win folder="windows" else folder="linux" fi e "- Upload file '"$file"' to 'https://gmic.eu/files/prerelease/"$file_short"'." _upload[] $file,"files/prerelease/"$file_short if !$is_pre e "- Upload file '"$file"' to 'https://gmic.eu/files/"$folder/$file"'." _upload[] $file,"files/"$folder/$file fi file$>= n+=1 fi } if $n<$N if !($t%4) remaining= sep= repeat $N { if narg(${file$>}) remaining.=${sep}${file$>} sep=", " fi } e "- Waiting for files: "$remaining"." fi wait 30000 t+=1 fi while $n<$N" && "$|<$t0+60*60*6 if $n<$N e "- Partial uploads done (timeout reached)." else e "- All uploads done !" fi fi # Update version number in html header file. # (works both for CImg and G'MIC !) # $1 = filename # $2 = version number # $3 = is_prerelease = { 0 | 1 } _update_header_html : check "narg(${1=}) && ${2=0}>0 && isbool(${3=0})" filename="$1" l[] { it[] $filename s +,{'\n'} foreach { if h>=64 +autocrop {'" "'} autocrop. {'\t'} if "find(#0,['Latest stable version: '])>=0" is_gmic:="find(#-1,'gmic.eu')>=0" is_cimg:="find(#-1,'cimg.eu')>=0" if !$is_gmic" && "!$is_cimg error "Cannot determine CImg or G'MIC header file." fi # Retrieve latest stable version specified in the header file. +l. { s -,{'>'} if {2,"inrange(i[0],_'0',_'9') && i[1]==_'.' && inrange(i[2],_'0',_'9') && i[3]==_'.' && inrange(i[4],_'0',_'9')"} stable_ver={2,`crop(0,0,1,5)`} fi if $3 x "date +%Y%m%d >"${-path_tmp}/gmic_date it ${-path_tmp}/gmic_date discard. 10 date={t} rm. pre_ver=${strver\ $2} pre_date={`s=['$date'];string(s[0,4],'/',s[4,2],'/',s[6,2])`} else stable_ver=${strver\ $2} pre_ver,pre_date= fi rm } rm[0] if $is_gmic i[0] ('" Latest stable version: "\ ""$stable_ver""') if ['$pre_ver']!=0 i[1] ('"        "\ "Current pre-release: "$pre_ver" "\ "("$pre_date")"') fi else i[0] ('" Latest stable version: "\ ""$stable_ver""') if ['$pre_ver']!=0 i[1] ('"        "\ "Current pre-release: "$pre_ver" "\ "("$pre_date")"') fi fi y fi rm. fi } a y ot $filename rm } #@cli v : eq. to 'verbose'. : (+) #@cli verbose : level : { + | - } : (+) #@cli : Set or increment/decrement the verbosity level. Default level is 0. #@cli : (eq. to 'v').\n #@cli : When 'level>0', G'MIC log messages are displayed on the standard error (stderr). #@cli : Default value: 'level=1'. #@cli wait : delay : (no arg) : (+) #@cli : Wait for a given delay (in ms), optionally since the last call to 'wait'. #@cli : or wait for a user event occurring on the selected instant display windows. #@cli : 'delay' can be { <0:delay+flush events | 0:event | >0:delay }. #@cli : Command selection (if any) stands for instant display window indices instead of image indices. #@cli : If no window indices are specified and if 'delay' is positive, the command results #@cli : in a 'hard' sleep during specified delay. #@cli : Default value: 'delay=0'. #@cli warn : _force_visible={ 0 | 1 },_message : (+) #@cli : Print specified warning message, on the standard error (stderr). #@cli : Command selection (if any) stands for displayed call stack subset instead of image indices. #@cli w : eq. to 'window'. : (+) #@cli window : _width[%]>=-1,_height[%]>=-1,_normalization,_fullscreen,_pos_x[%],_pos_y[%],_title : (+) #@cli : Display selected images into an instant display window with specified size, normalization type, #@cli : fullscreen mode and title. #@cli : (eq. to 'w').\n #@cli : If 'width' or 'height' is set to -1, the corresponding dimension is adjusted to the window #@cli : or image size. #@cli : Specify 'pos_x' and 'pos_y' arguments only if the window has to be moved to the specified #@cli : coordinates. Otherwise, they can be avoided. #@cli : 'width'=0 or 'height'=0 closes the instant display window. #@cli : 'normalization' can be { -1:keep same | 0:none | 1:always | 2:1st-time | 3:auto }. #@cli : 'fullscreen' can be { -1:keep same | 0:no | 1:yes }. #@cli : You can manage up to 10 different instant display windows by using the numbered variants #@cli : 'w0' (default, eq. to 'w'),'w1',...,'w9' of the command 'w'. #@cli : Invoke 'window' with no selection to make the window visible, if it has been closed by the user. #@cli : Default values: 'width=height=normalization=fullscreen=-1' and 'title=(undefined)'. #--------------------------------- # #@cli :: List Manipulation # #--------------------------------- #@cli k : eq. to 'keep'. : (+) #@cli keep : (+) #@cli : Keep only selected images. #@cli : (eq. to 'k'). #@cli : $ image.jpg split x keep[0-50%:2] append x #@cli : $ image.jpg split x keep[^30%-70%] append x #@cli kn : eq. to 'keep_named'. kn : e[^-1] "Keep images named '$*'." nmd $"*" k[${}] #@cli keep_named : "name1","name2",... #@cli : Keep all images with specified names from the list of images. #@cli : Remove all images if no images with those names exist. #@cli : (eq. to 'kmn'). keep_named : e[^-1] "Keep images named '$*'." nmd $"*" k[${}] #@cli mv : eq. to 'move'. : (+) #@cli move : position[%] : (+) #@cli : Move selected images at specified position. #@cli : Images are actually inserted between current positions 'position-1' and 'position'. #@cli : (eq. to 'mv'). #@cli : $ image.jpg split x,3 move[1] 0 #@cli : $ image.jpg split x move[50%--1:2] 0 append x #@cli nm : eq. to 'name'. : (+) #@cli => : eq. to 'name'. : (+) #@cli name : "name1","name2",...,"nameN" : (+) #@cli : Set names of selected images. #@cli : - If no explicit image selection is given, image selection is assumed to be '[-N--1]', \ # where 'N' is the number of specified arguments. #@cli : - If 'N' is higher than the number of images in selection, an error is thrown. #@cli : - If 'N' is lower than the number of images in selection, image names are assigned in a periodic way, i.e. \ # `name(selection[k]) = arg[k%N]`. #@cli : (eq. to '=>' and 'nm'). #@cli : $ image.jpg name image blur[image] 2 #@cli : $$ #@cli rm : eq. to 'remove'. : (+) #@cli remove : (+) #@cli : Remove selected images. #@cli : (eq. to 'rm'). #@cli : $ image.jpg split x remove[30%-70%] append x #@cli : $ image.jpg split x remove[0-50%:2] append x #@cli remove_duplicates #@cli : Remove duplicates images in the selected images list. #@cli : $ (1,2,3,4,2,4,3,1,3,4,2,1) split x remove_duplicates append x remove_duplicates : e[^-1] "Remove duplicates images in selected list of image$?." repeat $! { base=$> off=0 repeat $!-$>-1 { comp:=$base+1+$>-$off if $comp>=$! break fi +-[$base,$comp] abs. is_duplicate:=!is rm. if $is_duplicate rm[$comp] off+=1 fi } } #@cli remove_empty #@cli : Remove empty images in the selected image list. remove_empty : e[^-1] "Remove empty images in selected list of image$?." $!,1,1,1,"!w#x?x:-1" discard. -1 if w rm[{^},-1] else rm. fi #@cli rmn : eq. to 'remove_named'. rmn : e[^-1] "Remove images named '$*'." nmd $"*" rm[${}] #@cli remove_named : "name1","name2",... #@cli : Remove all images with specified names from the list of images. #@cli : Does nothing if no images with those names exist. #@cli : (eq. to 'rmn'). remove_named : e[^-1] "Remove images named '$*'." nmd $"*" rm[${}] #@cli rv : eq. to 'reverse'. : (+) #@cli reverse : (+) #@cli : Reverse positions of selected images. #@cli : (eq. to 'rv'). #@cli : $ image.jpg split x,3 reverse[-2,-1] #@cli : $ image.jpg split x,-16 reverse[50%-100%] append x #@cli sort_list : _ordering={ + | - },_criterion #@cli : Sort list of selected images according to the specified image criterion. #@cli : Default values: 'ordering=+', 'criterion=i'. #@cli : $ (1;4;7;3;9;2;4;7;6;3;9;1;0;3;3;2) split y sort_list +,i append y sort_list : skip ${1=+},${2=i} s0="descending" s1="ascending" e[^-1] "Sort list of image$? in "${s{['$1']=='+'}}" order, according to the image criterion '$2'." if $! if isin('$2','n','N') # Special case : lexicographic order from image names (n: ignore case, N: consider case) op={`;['$1']=='-'?_'>':_'<'`} if ['$2']=='n' fn=lowercase else fn= fi $!,1,1,1,"n = name(#x,1024); find(n,0)%1025" slen:=iM rm. # Largest name length. eval " const lm1 = l - 1; const slen = "$slen"; strcmp(n0,n1) = (for (k = 0, k0, range = pop(); lo = range[0]; hi = range[1]; pivot = int((lo + hi)/2); ref(name(#pivot,slen),npivot); while (lo,$2}) } a[$i--1] y +f. y a[-2,-1] x sort. $1,y z. 1,1 repeat h { nm$>={$>,n} =>[$>] sortlist$> } repeat h { mv[sortlist{i(0,$>)}] -1 } repeat h { =>[$>] ${nm{i(0,$>)}} } rm. fi fi #--------------------------------- # #@cli :: Mathematical Operators # #--------------------------------- #@cli abs : (+) #@cli : Compute the pointwise absolute values of selected images. #@cli : $ image.jpg +sub {ia} abs[-1] #@cli : $ 300,1,1,1,'cos(20*x/w)' +abs display_graph 400,300 #@cli acos : (+) #@cli : Compute the pointwise arccosine of selected images. #@cli : $ image.jpg +normalize -1,1 acos[-1] #@cli : $ 300,1,1,1,'cut(x/w+0.1*u,0,1)' +acos display_graph 400,300 #@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands #@cli acosh : (+) #@cli : Compute the pointwise hyperbolic arccosine of selected images. #@cli + : eq. to 'add'. : (+) #@cli add : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Add specified value, image or mathematical expression to selected images, \ # or compute the pointwise sum of selected images. #@cli : (eq. to '+'). #@cli : $ image.jpg +add 30% cut 0,255 #@cli : $ image.jpg +blur 5 normalize 0,255 add[1] [0] #@cli : $ image.jpg add '80*cos(80*(x/w-0.5)*(y/w-0.5)+c)' cut 0,255 #@cli : $ image.jpg repeat 9 { +rotate[0] {$>*36},1,0,50%,50% } add div 10 #@cli & : eq. to 'and'. : (+) #@cli and : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the bitwise AND of selected images with specified value, image or mathematical \ # expression, or compute the pointwise sequential bitwise AND of selected images. #@cli : (eq. to '&'). #@cli : $ image.jpg and {128+64} #@cli : $ image.jpg +mirror x and #@cli argmax #@cli : Compute the argmax of selected images. Returns a single image #@cli : with each pixel value being the index of the input image with maximal value. #@cli : $ image.jpg sample lena,lion,square +argmax argmax : e[^-1] "Compute argmax of image$?." if !$! return fi 13,$! eval. "!x?copy(i(),[[',i#'],v2s(y,10,10)])" =. 0 discard. 0 str={t} rm. ${-max_whds},"argmax("$str")" k. => [argmax] #@cli argmaxabs #@cli : Compute the argmaxabs of selected images. Returns a single image #@cli : with each pixel value being the index of the input image with maxabs value. argmaxabs : e[^-1] "Compute argmaxabs of image$?." if !$! return fi 13,$! eval. "!x?copy(i(),[[',i#'],v2s(y,10,10)])" =. 0 discard. 0 str={t} rm. ${-max_whds},"argmaxabs("$str")" k. => [argmaxabs] #@cli argmin #@cli : Compute the argmin of selected images. Returns a single image #@cli : with each pixel value being the index of the input image with minimal value. #@cli : $ image.jpg sample lena,lion,square +argmin argmin : e[^-1] "Compute argmin of image$?." if !$! return fi 13,$! eval. "!x?copy(i(),[[',i#'],v2s(y,10,10)])" =. 0 discard. 0 str={t} rm. ${-max_whds},"argmin("$str")" k. => [argmin] #@cli argminabs #@cli : Compute the argminabs of selected images. Returns a single image #@cli : with each pixel value being the index of the input image with minabs value. argminabs : e[^-1] "Compute argminabs of image$?." if !$! return fi 13,$! eval. "!x?copy(i(),[[',i#'],v2s(y,10,10)])" =. 0 discard. 0 str={t} rm. ${-max_whds},"argminabs("$str")" k. => [argminabs] #@cli asin : (+) #@cli : Compute the pointwise arcsine of selected images. #@cli : $ image.jpg +normalize -1,1 asin[-1] #@cli : $ 300,1,1,1,'cut(x/w+0.1*u,0,1)' +asin display_graph 400,300 #@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands #@cli asinh : (+) #@cli : Compute the pointwise hyperbolic arcsine of selected images. #@cli atan : (+) #@cli : Compute the pointwise arctangent of selected images. #@cli : $ image.jpg +normalize 0,8 atan[-1] #@cli : $ 300,1,1,1,'4*x/w+u' +atan display_graph 400,300 #@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands #@cli atan2 : [x_argument] : (+) #@cli : Compute the pointwise oriented arctangent of selected images. #@cli : Each selected image is regarded as the y-argument of the arctangent function, while the #@cli : specified image gives the corresponding x-argument. #@cli : $ (-1,1) (-1;1) resize 400,400,1,1,3 atan2[1] [0] keep[1] mod {pi/8} #@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands #@cli atanh : (+) #@cli : Compute the pointwise hyperbolic arctangent of selected images. #@cli << : eq. to 'bsl'. : (+) #@cli bsl : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the bitwise left shift of selected images with specified value, image or \ # mathematical expression, or compute the pointwise sequential bitwise left shift of \ # selected images. #@cli : (eq. to '<<'). #@cli : $ image.jpg bsl 'round(3*x/w,0)' cut 0,255 #@cli >> : eq. to 'bsr'. : (+) #@cli bsr : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the bitwise right shift of selected images with specified value, image or \ # mathematical expression, or compute the pointwise sequential bitwise right shift of \ # selected images. #@cli : (eq. to '>>'). #@cli : $ image.jpg bsr 'round(3*x/w,0)' cut 0,255 #@cli cos : (+) #@cli : Compute the pointwise cosine of selected images. #@cli : $ image.jpg +normalize 0,{2*pi} cos[-1] #@cli : $ 300,1,1,1,'20*x/w+u' +cos display_graph 400,300 #@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands #@cli cosh : (+) #@cli : Compute the pointwise hyperbolic cosine of selected images. #@cli : $ image.jpg +normalize -3,3 cosh[-1] #@cli : $ 300,1,1,1,'4*x/w+u' +cosh display_graph 400,300 #@cli deg2rad #@cli : Convert pointwise angle values of selected images, from degrees to radians (apply 'i*pi/180'). deg2rad : e[^-1] "Convert pointwise angle values of image$?, from degrees to radians." * 0.017453292519943295 #@cli / : eq. to 'div'. : (+) #@cli div : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Divide selected images by specified value, image or mathematical expression, \ # or compute the pointwise quotient of selected images. #@cli : (eq. to '/'). #@cli : $ image.jpg div '1+abs(cos(x/10)*sin(y/10))' #@cli : $ image.jpg +norm add[-1] 1 +div #@cli div_complex : [divider_real,divider_imag],_epsilon>=0 #@cli : Perform division of the selected complex pairs (real1,imag1,...,realN,imagN) of images by #@cli : specified complex pair of images (divider_real,divider_imag). #@cli : In complex pairs, the real image must be always located before the imaginary image in the image list. #@cli : Default value: 'epsilon=1e-8'. div_complex : check ${3=1e-8}>=0 e[^-1] "Divide complex pair$? by complex pair "${"pass$1,$2 -1"}" (with epsilon $3)." repeat int($!/2) { pass${1,2} 0 l[$>,{$>+1},-2,-1] { +*[1,2] +*[0,3] -[-2,-1] # bc-ad *[0] [2] *[1] [3] +[0,1] # ac+bd sqr[1,2] +[1,2] +[1] $3 # c^2+d^2 /[2] [1] /[0,1] } } #@cli == : eq. to 'eq'. : (+) #@cli eq : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the boolean equality of selected images with specified value, image or \ # mathematical expression, or compute the boolean equality of selected images. #@cli : (eq. to '=='). #@cli : $ image.jpg round 40 eq {round(ia,40)} #@cli : $ image.jpg +mirror x eq #@cli erf : (+) #@cli : Compute the pointwise error function of selected images. #@cli : $ image.jpg +normalize 0,2 erf[-1] #@cli : $ 300,1,1,1,'7*x/w-3.5+u' +erf display_graph 400,300 #@cli exp : (+) #@cli : Compute the pointwise exponential of selected images. #@cli : $ image.jpg +normalize 0,2 exp[-1] #@cli : $ 300,1,1,1,'7*x/w+u' +exp display_graph 400,300 #@cli >= : eq. to 'ge'. : (+) #@cli ge : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the boolean 'greater or equal than' of selected images with specified value, image #@cli : or mathematical expression, or compute the boolean 'greater or equal than' of selected images. #@cli : (eq. to '>='). #@cli : $ image.jpg ge {ia} #@cli : $ image.jpg +mirror x ge #@cli > : eq. to 'gt'. : (+) #@cli gt : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the boolean 'greater than' of selected images with specified value, image or \ # mathematical expression, or compute the boolean 'greater than' of selected images. #@cli : (eq. to '>'). #@cli : $ image.jpg gt {ia} #@cli : $ image.jpg +mirror x gt #@cli <= : eq. to 'le'. : (+) #@cli le : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the boolean 'less or equal than' of selected images with specified value, image or \ # mathematical expression, or compute the boolean 'less or equal than' of selected images. #@cli : (eq. to '<='). #@cli : $ image.jpg le {ia} #@cli : $ image.jpg +mirror x le #@cli < : eq. to 'lt'. : (+) #@cli lt : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the boolean 'less than' of selected images with specified value, image or \ # mathematical expression, or compute the boolean 'less than' of selected images. #@cli : (eq. to '<'). #@cli : $ image.jpg lt {ia} #@cli : $ image.jpg +mirror x lt #@cli log : (+) #@cli : Compute the pointwise base-e logarithm of selected images. #@cli : $ image.jpg +add 1 log[-1] #@cli : $ 300,1,1,1,'7*x/w+u' +log display_graph 400,300 #@cli log10 : (+) #@cli : Compute the pointwise base-10 logarithm of selected images. #@cli : $ image.jpg +add 1 log10[-1] #@cli : $ 300,1,1,1,'7*x/w+u' +log10 display_graph 400,300 #@cli log2 : (+) #@cli : Compute the pointwise base-2 logarithm of selected images #@cli : $ image.jpg +add 1 log2[-1] #@cli : $ 300,1,1,1,'7*x/w+u' +log2 display_graph 400,300 #@cli max : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the maximum between selected images and specified value, image or \ # mathematical expression, or compute the pointwise maxima between selected images. #@cli : $ image.jpg +mirror x max #@cli : $ image.jpg max 'R=((x/w-0.5)^2+(y/h-0.5)^2)^0.5;255*R' #@cli maxabs : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the maxabs between selected images and specified value, image or \ # mathematical expression, or compute the pointwise maxabs between selected images. #@cli m/ : eq. to 'mdiv'. : (+) #@cli mdiv : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the matrix division of selected matrices/vectors by specified value, image or \ # mathematical expression, or compute the matrix division of selected images. #@cli : (eq. to 'm/'). #@cli med #@cli : Compute the median of selected images. #@cli : $ image.jpg sample lena,lion,square +med med : e[^-1] "Compute median of image$?." if !$! return fi 13,$! eval. "!x?copy(i(),[[',i#'],v2s(y,10,10)])" =. 0 discard. 0 str={t} rm. ${-max_whds},"med("$str")" k. => [med] #@cli min : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the minimum between selected images and specified value, image or \ # mathematical expression, or compute the pointwise minima between selected images. #@cli : $ image.jpg +mirror x min #@cli : $ image.jpg min 'R=((x/w-0.5)^2+(y/h-0.5)^2)^0.5;255*R' #@cli minabs : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the minabs between selected images and specified value, image or \ # mathematical expression, or compute the pointwise minabs between selected images. #@cli % : eq. to 'mod'. : (+) #@cli mod : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the modulo of selected images with specified value, image or mathematical \ # expression, or compute the pointwise sequential modulo of selected images. #@cli : (eq. to '%'). #@cli : $ image.jpg +mirror x n. 1,255 round. mod #@cli : $ image.jpg mod 'R=((x/w-0.5)^2+(y/h-0.5)^2)^0.5;255*R' #@cli m* : eq. to 'mmul'. : (+) #@cli mmul : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the matrix right multiplication of selected matrices/vectors by specified value, image or \ # mathematical expression, or compute the matrix right multiplication of selected images. #@cli : (eq. to 'm*'). #@cli : $ (0,1,0;0,0,1;1,0,0) (1;2;3) +mmul #@cli * : eq. to 'mul'. : (+) #@cli mul : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Multiply selected images by specified value, image or mathematical expression, \ # or compute the pointwise product of selected images. #@cli : (eq. to '*'). #@cli : See also: ''add'', ''sub'', ''div''. #@cli : $ image.jpg +mul 2 cut 0,255 #@cli : $ image.jpg (1,2,3,4,5,6,7,8) ri[-1] [0] mul[0] [-1] #@cli : $ image.jpg mul '1-3*abs(x/w-0.5)' cut 0,255 #@cli : $ image.jpg +luminance negate[-1] +mul #@cli mul_channels : value1,_value2,...,_valueN #@cli : Multiply channels of selected images by specified sequence of values. #@cli : $ image.jpg +mul_channels 1,0.5,0.8 mul_channels : e[^-1] "Multiply channels of image$? by value sequence ($*)." $=arg repeat $# { i=$> fact=${arg{1+($>%$#)}} repeat $! { if {$>,$i] $i *. $fact rm. fi } } #@cli mul_complex : [multiplier_real,multiplier_imag] #@cli : Perform multiplication of the selected complex pairs (real1,imag1,...,realN,imagN) of images by #@cli : specified complex pair of images (multiplier_real,multiplier_imag). #@cli : In complex pairs, the real image must be always located before the imaginary image in the image list. mul_complex : e[^-1] "Multiply complex pair$? by complex pair "${"pass$1,$2 -1"}"." repeat int($!/2) { pass${1,2} 0 l[$>,{$>+1},-2,-1] { +*[0,3] +*[1,2] +[-2,-1] # ad+bc *[0,2] *[1,2] -[0,1] # ac-bd } } #@cli != : eq. to 'neq'. : (+) #@cli neq : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the boolean inequality of selected images with specified value, image or \ # mathematical expression, or compute the boolean inequality of selected images. #@cli : (eq. to '!='). #@cli : $ image.jpg round 40 neq {round(ia,40)} #@cli | : eq. to 'or'. : (+) #@cli or : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the bitwise OR of selected images with specified value, image or mathematical \ # expression, or compute the pointwise sequential bitwise OR of selected images. #@cli : (eq. to '|'). #@cli : $ image.jpg or 128 #@cli : $ image.jpg +mirror x or #@cli ^ : eq. to 'pow'. : (+) #@cli pow : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Raise selected images to the power of specified value, image or mathematical \ # expression, or compute the pointwise sequential powers of selected images. #@cli : (eq. to '^'). #@cli : $ image.jpg div 255 +pow 0.5 mul 255 #@cli : $ image.jpg gradient pow 2 add pow 0.2 #@cli rad2deg #@cli : Convert pointwise angle values of selected images, from radians to degrees (apply 'i*180/pi'). rad2deg : e[^-1] "Convert pointwise angle values of image$?, from radians to degrees." * 57.295779513082323 #@cli rol : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the bitwise left rotation of selected images with specified value, image or \ # mathematical expression, or compute the pointwise sequential bitwise left rotation of \ # selected images. #@cli : $ image.jpg rol 'round(3*x/w,0)' cut 0,255 #@cli ror : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the bitwise right rotation of selected images with specified value, image or \ # mathematical expression, or compute the pointwise sequential bitwise right rotation of \ # selected images. #@cli : $ image.jpg ror 'round(3*x/w,0)' cut 0,255 #@cli sign : (+) #@cli : Compute the pointwise sign of selected images. #@cli : $ image.jpg +sub {ia} sign[-1] #@cli : $ 300,1,1,1,'cos(20*x/w+u)' +sign display_graph 400,300 #@cli sin : (+) #@cli : Compute the pointwise sine of selected images. #@cli : $ image.jpg +normalize 0,{2*pi} sin[-1] #@cli : $ 300,1,1,1,'20*x/w+u' +sin display_graph 400,300 #@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands #@cli sinc : (+) #@cli : Compute the pointwise sinc function of selected images. #@cli : $ image.jpg +normalize {-2*pi},{2*pi} sinc[-1] #@cli : $ 300,1,1,1,'20*x/w+u' +sinc display_graph 400,300 #@cli sinh : (+) #@cli : Compute the pointwise hyperbolic sine of selected images. #@cli : $ image.jpg +normalize -3,3 sinh[-1] #@cli : $ 300,1,1,1,'4*x/w+u' +sinh display_graph 400,300 #@cli sqr : (+) #@cli : Compute the pointwise square function of selected images. #@cli : $ image.jpg +sqr #@cli : $ 300,1,1,1,'40*x/w+u' +sqr display_graph 400,300 #@cli sqrt : (+) #@cli : Compute the pointwise square root of selected images. #@cli : $ image.jpg +sqrt #@cli : $ 300,1,1,1,'40*x/w+u' +sqrt display_graph 400,300 #@cli - : eq. to 'sub'. : (+) #@cli sub : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Subtract specified value, image or mathematical expression to selected images, \ # or compute the pointwise difference of selected images. #@cli : (eq. to '-'). #@cli : $ image.jpg +sub 30% cut 0,255 #@cli : $ image.jpg +mirror x sub[-1] [0] #@cli : $ image.jpg sub 'i(w/2+0.9*(x-w/2),y)' #@cli : $ image.jpg +mirror x sub #@cli tan : (+) #@cli : Compute the pointwise tangent of selected images. #@cli : $ image.jpg +normalize {-0.47*pi},{0.47*pi} tan[-1] #@cli : $ 300,1,1,1,'20*x/w+u' +tan display_graph 400,300 #@cli : $$ https://gmic.eu/oldtutorial/trigometric-and-inverse-trigometric-commands #@cli tanh : (+) #@cli : Compute the pointwise hyperbolic tangent of selected images. #@cli : $ image.jpg +normalize -3,3 tanh[-1] #@cli : $ 300,1,1,1,'4*x/w+u' +tanh display_graph 400,300 #@cli xor : value[%] : [image] : 'formula' : (no arg) : (+) #@cli : Compute the bitwise XOR of selected images with specified value, image or mathematical \ # expression, or compute the pointwise sequential bitwise XOR of selected images. #@cli : $ image.jpg xor 128 #@cli : $ image.jpg +mirror x xor #--------------------------------- # #@cli :: Values Manipulation # #--------------------------------- #@cli apply_curve : 0<=smoothness<=1,x0,y0,x1,y1,x2,y2,...,xN,yN #@cli : Apply curve transformation to image values. #@cli : Default values: 'smoothness=1', 'x0=0', 'y0=100'. #@cli : $ image.jpg +apply_curve 1,0,0,128,255,255,0 apply_curve : check "${1=1}>=0 && $1<=1" skip ${2=0},${3=100} e[^-1] "Apply intensity curve with smoothness $1 and keypoints (${2--1}) on image$?." # Determine value mapping. (${^0,1}) r. 2,{w/2},1,1,-1 +z. 0,0 vm,vM:=[im,iM] n. 0,8191 j.. . function1d[] $1,{-2,^} rm[-3,-2] # Apply value mapping. -[^-1] $vm *[^-1] {8191/($vM-$vm)} map[^-1] .,1 rm. #@cli apply_gamma : gamma>=0 #@cli : Apply gamma correction to selected images. #@cli : $ image.jpg +apply_gamma 2 apply_gamma : check $1>=0 e[^-1] "Apply Gamma-correction to image$?, with gamma $1." if $1==1 return fi foreach { mM:=[im,iM] n 0,1 ^ {1/$1} n $mM } #@cli balance_gamma : _ref_color1,... #@cli : Compute gamma-corrected color balance of selected image, with respect to specified reference color. #@cli : Default value: 'ref_color1=128'. #@cli : $ image.jpg +balance_gamma 128,64,64 balance_gamma : check "isnum(${1=128})" e[^-1] "Apply gamma-corrected color balance of image$?, with reference color ("${^0}")." foreach { (${^0}) r. {-2,s},1,1,1,0,1 s.. c /. 255 repeat $!-1 { /[$>] 255 ^[$>] {log({@$>})/log({$>,ia})} *[$>] 255 } rm. a c c 0,255 } #@cli cast : datatype_source,datatype_target #@cli : Cast datatype of image buffer from specified source type to specified target type. #@cli : 'datatype_source' and 'datatype_target' can be \ # { uint8 | int8 | uint16 | int16 | uint32 | int32 | uint64 | int64 | float32 | float64 }. cast : e[^-1] "Cast datatype of image buffer$? from '$1' to '$2'." ssize:=s=['"$1"'];s=='uint8'||s=='int8'?1:s=='uint16'||s=='int16'?2:s=='uint32'||s=='int32'||s=='float32'?4:8 dsize:=s=['"$2"'];s=='uint8'||s=='int8'?1:s=='uint16'||s=='int16'?2:s=='uint32'||s=='int32'||s=='float32'?4:8 foreach { w,h,d,s={w},{h},{d},{s} serialize $1,0,0 s -,{'\n$w\ $h\ $d\ $s\n'} i[1] ('\n1\ {int($w*$h*$d*$s*$ssize/$dsize)}\ 1\ 1\n') y[1] replace_str[0] "$1","$2" a y unserialize } #@cli complex2polar #@cli : Compute complex to polar transforms of selected images. #@cli : $ image.jpg +fft complex2polar[-2,-1] log[-2] shift[-2] 50%,50%,0,0,2 remove[-1] complex2polar : e[^-1] "Compute complex to polar transforms of image$?." repeat int($!/2) { l[{2*$>},{2*$>+1}] { ri[1] [0],3 +atan2[1] [0] => {1,n} sqr[-3,-2] +[-3,-2] sqrt.. } } #@cli compress_clut #@cli : Compress selected color LUTs as sequences of colored keypoints. compress_clut : e[^-1] "Compress color LUT$? as sets of colored keypoints." foreach { nm={b} if iM>1024 / 257 fi # When input is 16bits rs3d 33 round if d==1 S:=round(cbrt(wh)) r $S,$S,$S,100%,-1 fi # When input is a 2D haldclut image e "* Process CLUT '"$nm"' ("{w}"x"{h}"x"{d}").\n" # Detect B&W cluts. if "s==3 && crop(0,0,0,0,w,h,d,1)==crop(0,0,0,1,w,h,d,1) && crop(0,0,0,0,w,h,d,1)==crop(0,0,0,2,w,h,d,1)" channels 0 fi # Compress CLUT: try first with RBFs. if s>1 +compress_to_keypoints. 3,640,0.45,2.5,"deltaE. [0],1" # Color CLUT else +compress_to_keypoints. 3,640,2,10 # B&W CLUT fi +decompress_from_keypoints. deltaE. [0] err_avg_rbf,err_max_rbf:=ia,iM rm. if $err_avg_rbf>0.45" || "$err_max_rbf>2.5 # RBFs failed, try with PDEs store. rbf_keypoints if s>1 +compress_to_keypoints 2,2048,0.45,2.5,"deltaE. [0],1" # Color CLUT else +compress_to_keypoints 2,2048,2,10 # B&W CLUT fi +decompress_from_keypoints. deltaE. [0] err_avg_pde,err_max_pde:=ia,iM rm. if $err_avg_rbf<$err_avg_pde rm. $rbf_keypoints fi fi k. # Remove header and format keypoints. f. "begin(WHD = [ i0,i1,i2 ]); y<2?I:(P = I; [ round(P[0,3]*256/WHD),P[3,size(P)-3] ])" # Renormalize is_rbf:=i(0,1,0,2) rows 2,100% 1,100%,1,1,"P = I(#-1); P[0]*65536 + P[1]*256 + P[2]" rv a x sort {`$is_rbf?_'+':_'-'`},y z. 1,100% strclut $nm => ${} } # compress_cluts : {"file_pattern" | "file_list.txt"} # Batch compress CLUT files. compress_cluts : rm if isfile(['{/"$1"}']) it[] "$1" s -,{'\n'} else files "$1" ('${}') s -,{','} fi foreach { filename={t} 0 => $filename ext={x} rm basename $filename l[] { ('${}') replace_str .$ext,"" basename={t} rm } need_compression=1 cclut=cclut_$basename.gmz if isfile(['{/$cclut}']) i $cclut if "dat = (date(5) + 60*(date(4) + date(2)*24)); fdat = (date(5,'"{/$cclut}"') + 60*(date(4,'"{/$cclut}"') + date(2,'"{/$cclut}"')*24)); !h && dat-fdat<30" e "* Skip file '"$filename"' (CLUT already being compressed)." need_compression=0 elif h>0" && "h!=2048 e "* Skip file '"$filename"' (CLUT already compressed)." need_compression=0 fi rm. fi if $need_compression 0 o. $cclut rm. # Lock current file if lowercase(['$ext'])=='cube' input_cube $filename c. 0,255 elif lowercase(['$ext'])=='png' i $filename S:=round(cbrt(wh)) r $S,$S,$S,100%,-1 else e "* Skip file '"$filename"' (unknown CLUT format)." continue fi e "* Compress file '"$filename"'." if w>33 rs3d 33 fi to_rgb tic compress_clut toc o $cclut fi rm } #@cli compress_huffman : [huffman_tree],_max_leaf_value #@cli : Compress selected images with Huffman coding. #@cli : See also: ''decompress_huffman'', ''huffman_tree''. compress_huffman : check ${"is_image_arg $1"} skip "${2=}" e[^-1] "Compress image$? with Huffman coding, using tree $1." pass$1 # Transform Huffman tree to codebook to speed up encoding. if ['$2']==0 # Retrieve max integer value of all tree leafs eval. ">begin(Mlv = -inf); i2<0 && i3<0?Mlv = max(Mlv,i0); end(set('Mlv',Mlv)); I" else Mlv=$2 fi 1,{$Mlv+1},1,2 # codebook -> [ code,nb_bits ] for each tree leaf eval.. ":i2<0?( # Leaf ind = y; code = nb_bits = 0; while (ind>=0, # Iteratively go up to root node par = i(0,ind,0,1); par>=0?((code<<=1)|=(i(0,par,0,2)!=ind); ++nb_bits); ind = par; ); ncode = 0; repeat (nb_bits,(ncode<<=1)|=(code&1); code>>=1); # Reverse bit order I[#-1,i0] = [ ncode,nb_bits ]; ); I" rm.. # Encode input data. foreach[^-1] { nm={n} pass. 2 0 eval... ">begin(da_push(w,h,d,s); out_stream = nb_bits = 0); C = I[#-2,int(i),1]; (out_stream<<=C[1])|=C[0]; nb_bits+=C[1]; while (nb_bits>=8, # Write completed bytes da_push(out_stream>>(nb_bits-=8)); out_stream%=1< $nm } rm. #@cli huffman_tree #@cli : Generate Huffman coding tree from the statistics of all selected images. #@cli : Huffman tree is returned as a 1xN image inserted at the end of the image list, \ # representing the 'N' vector-valued leafs/nodes of the tree, encoded as `[ value,parent,child0,child1 ]`. #@cli : Last row of the returned image corresponds to the tree root. #@cli : Selected images must contain only positive integer values. #@cli : Return maximal value of the input data in the status. #@cli : See also: ''compress_huffman'', ''decompress_huffman''. +huffman_tree : e[^-1] "Generate Huffman coding tree from the statistic of image$?." Mlv:="val=-inf;for(k=0,k list of active leafs/nodes ( id,occurence,value,parent,child0,child1 ) eval " id = da_size(); active = 0; do ( # Find the two minimal occurrences in active nodes. omin1 = omin2 = inf; oind1 = oind2 = 0; for (k = da_size() - 1, k>=active, --k, occ = i(0,k,0,1); occ<=omin1?(oind2 = oind1; omin2 = omin1; oind1 = k; omin1 = occ): occ<=omin2?(oind2 = k; omin2 = occ); ); oind1>oind2?swap(oind1,oind2); # Merge them as a new active node. i(0,oind1,0,3) = i(0,oind2,0,3) = id; da_push([ id++,omin1 + omin2,-1,-1,i[oind1],i[oind2] ]); swap(I[oind1],I[active++]); # Move children in set of inactive nodes swap(I[oind2],I[active++]); ,_(while) active "[Huffman Tree]" u $Mlv #@cli compress_rle : _is_binary_data={ 0 | 1 },_maximum_sequence_length>=0 #@cli : Compress selected images as 2xN data matrices, using RLE algorithm. #@cli : Set 'maximum_sequence_length=0' to disable maximum length constraint. #@cli : Default values: 'is_binary_data=0' and 'maximum_sequence_length=0'. #@cli : $ image.jpg rescale2d ,100 quantize 4 round +compress_rle , +decompress_rle[-1] compress_rle : check "isbool(${1=0}) && isint(${2=0},0)" s0=" for binary data" s1="" if $2 s=", with maximal sequence length "$2 else s="" fi e[^-1] "Compress image$? using RLE algorithm"${s{!$1}}$s"." foreach { nm={n} im:=im header:=w,h,d,s,$im,$1!=0 - $im y x ({{0,@-1}+1}) a x r 100%,3 f ">!y?i:y==1?(i(x,0)==i(x+1,0)?-1:x):(i(x-1,1)==-1?i(x-1,2)+1:1)" if $2 # Constrain maximum sequence length. transpose mirror x f. ">x==2?i:!x?(j(0,-1)==$2?1:i!=1?j(0,-1)+1:1):(i==-1&&j(-1)==$2?y:i)" mirror x transpose fi z 0,{w-2} s y,3 y[1] discard[1] -1 warp[0,2] [1],0,0 rm[1] if $1 # Encode for binary data. !=[0] 0 *[0] 2 -[0] 1 * else # Encode for arbitrary data. *. -1 rv a x y discard -1 f ">i(0,y-1)<0 && !i && i(0,y+1)<0?-1:i" discard -1 fi i[0] ($header:y) a y => $nm } #@cli cumulate : { x | y | z | c }...{ x | y | z | c } : (no arg) : (+) #@cli : Compute the cumulative function of specified image data, optionally along the specified axes. #@cli : $ image.jpg +histogram 256 +cumulate[-1] display_graph[-2,-1] 400,300,3 #@cli c : eq. to 'cut'. : (+) #@cli cut : { value0[%] | [image0] },{ value1[%] | [image1] } : [image] : (+) #@cli : Cut values of selected images in specified range. #@cli : (eq. to 'c').\n #@cli : $ image.jpg +add 30% cut[-1] 0,255 #@cli : $ image.jpg +cut 25%,75% #@cli decompress_clut : _width>0,_height>0,_depth>0 #@cli : Decompress selected colored keypoints into 3D CLUTs, using a mixed RBF/PDE approach. #@cli : Default values: 'width=height=depth=33' and 'reconstruction_colorspace=0'. decompress_clut : check "isint(${1=33},1) && isint(${2=$1},1) && isint(${3=$1},1)" e[^-1] "Decompress colored keypoint$? into $1x$2x$3 CLUTs." foreach { +norm r. 1,2,1,1,2 is_rbf:=i[0]0,_height>0,_depth>0 : (no arg) #@cli : Decompress selected sets of keypoints as images (opt. of specified size). #@cli : A set of keypoints is defined as a vector-valued image, such that: #@cli : - The first pixel is a vector which encodes the `[ Width,Height,Depth ]` of the decompressed image. #@cli : - The second pixel is a vector which encodes `[ Min,Max,Use_RBF ]`, where 'Min' and 'Max' defines the \ # value range of the decompressed image, and 'Use_RBF' tells is the decompression scheme must use \ # RBFs ('Use_RBF=1') or Multiscale Diffusion PDE's ('Use_RBF=0'). #@cli : - The remaining pixels define the keypoint coordinates and values, as: #@cli : - `[ x_k,y_k,z_k, v1_k,...,vN_k ]` for a 3D target image of N-valued vectors. #@cli : - `[ x_k,y_k, v1_k,...,vN_k ]` for a 2D target image of N-valued vectors. #@cli : - `[ x_k, v1_k,...,vN_k ]` for a 1D target image of N-valued vectors. #@cli : where the coordinates 'x_k', 'y_k' and 'z_k' are defined respectively in ranges \ # '[0,Width-1]', '[0,Height-1]' and '[0,Depth-1]'. #@cli : If the 'width', 'height' and 'depth' arguments are provided, they define the size of the decompressed image, \ # : overriding then the original image size `[ Width,Height,Depth ]` defined in the keypoints header. decompress_from_keypoints : skip "${1=},${2=},${3=}" # Parse arguments. l[] { is_args:="isint($1,1) && (['$2']==0 || isint($2,1)) && (['$3']==0 || isint($3,1))" check $is_args onfail noarg is_args=0 } if $is_args w0,h0,d0:="[ narg($1)?$1:0, narg($2)?$2:1, narg($3)?$3:1 ]" e[0--3] "Decompress keypoint set$?, with size ("$w0,$h0,$d0")." else e[0--3] "Decompress keypoint set$?." fi pass[$[]] -1 sel=${} # Decompress from keypoints. foreach { nm={n} # Get image information from first pixel. r 1,{whd},1,100%,-1 w,h,d,s,im,iM,use_rbf:="P = I[0]; V = I[1]; W = P[0]; H = P[1]; D = s>2?P[2]:1; S = s - (D>1?3:H>1?2:1); use_rbf = s>2?V[2]:1; [ W,H,D,S,V[0],V[1],use_rbf ]" if $w<=0" || "$h<0" || "$d<0" || "$s<0" || "!isbool($use_rbf) error[0--4] "Command 'decompress_keypoints': Image \#"{arg0($>,$sel)}" = ("{[w,h,d,s]}") "\ "is a not a valid keypoint set." fi # Reconstruct 1D/2D or 3D signal from keypoints. rows 2,100% if $d>1 # 3D keypoints if $use_rbf rbf {$is_args?[$w0,$h0,$d0]:[$w,$h,$d]},0,0,0,{[$w,$h,$d]-1} else {$is_args?[$w0,$h0,$d0]:[$w,$h,$d]},$s 100%,100%,100%,100%,1 eval... "P = I; "$is_args"?( P[0] = round(P[0]*($w0 - 1)/($w - 1)); P[1] = round(P[1]*($h0 - 1)/($h - 1)); P[2] = round(P[2]*($d0 - 1)/($d - 1))); I(#-2,P[0],P[1],P[2]) = P[3,size(P) - 3]; i(#-1,P[0],P[1],P[2]) = 0" inpaint_pde.. .,-8,1,16 rm[-3,-1] fi elif $h>1 # 2D keypoints if $use_rbf rbf {$is_args?[$w0,$h0]:[$w,$h]},0,0,{[$w,$h]-1} else $w,$h,1,$s 100%,100%,1,100%,1 eval... "P = I; "$is_args"?( P[0] = round(P[0]*($w0 - 1)/($w - 1)); P[1] = round(P[1]*($h0 - 1)/($h - 1))); I(#-2,P[0],P[1]) = P[2,size(P) - 2]; i(#-1,P[0],P[1]) = 0" inpaint_pde.. .,-8,1,16 rm[-3,-1] fi else # 1D keypoints rbf {$is_args?$w0:$w},0,{$w-1} fi c $im,$iM => $nm } #@cli decompress_huffman : [huffman_tree] #@cli : Decompress selected images with Huffman decoding. #@cli : See also: ''compress_huffman'', ''huffman_tree''. #@cli : $ image.jpg huffman_tree compress_huffman.. . +decompress_huffman.. . decompress_huffman : check ${"is_image_arg $1"} e[^-1] "Decompress image$? with Huffman decoding, using tree $1." pass$1 foreach[^-1] { nm={n} pass. 2 # Decode compressed data. 0 eval " hvalues = crop(#-2,0,0,0,0,1,h#-2,1,1); hparent = crop(#-2,0,0,0,1,1,h#-2,1,1); hchild0 = crop(#-2,0,0,0,2,1,h#-2,1,1); hchild1 = crop(#-2,0,0,0,3,1,h#-2,1,1); const root = size(hvalues) - 1; siz = prod(crop(#-3,0,0,1,4)); off = 4; mask = byte = bytes_written = 0; ind = root; while (bytes_written>=1; child = bit?hchild1[ind]:hchild0[ind]; val = hvalues[child]; val>=0?( # Leaf da_push(val); ind = root; ++bytes_written; ):(ind = child); ); da_freeze()" r. {"crop(#-3,0,0,1,4)"},-1 k. => $nm } rm. #@cli decompress_rle #@cli : Decompress selected data vectors, using RLE algorithm. decompress_rle : e[^-1] "Decompress data vector$?, using RLE algorithm." foreach { # Retrieve original data dimension and min value. y whds={0,@0-3} im={0,@4} is_binary_data={0,@5} rows 6,100% # Transform RLE data to list of pairs {nb_occurrences,value}. if $is_binary_data # Decode for binary data. +>= 0 abs[0] a x else # Decode for arbitrary data +<. 0 (0;1;1) erode.. .,0 rm. -. 1 a x discard -1 # Get back compressed '0' (minimum) values. +< 0 (0;1;1) dilate.. . rm. *. -2 +. 1 # Get back singletons. rv abs. a x discard -1 r 2,{h/2},1,1,-1 fi # Decompress, using 3D objects. s y,-256 foreach { i[0] ('CImg3d') +[0] 0.5 i[1] ({2*h};{h}) s. x,2 cumulate.. siz={-2,@-1} +shift.. 0,1 -... 1 rv[-3,-1] z[-3,-1] 0,2 a[-3,-1] x i[3] (2,0,1;2,{2*h-2},{2*h-1}) r[3] 3,{h},1,1,3 round[3] r[4] 3 1,100%,1,1,1 y a y $siz j3d. ..,0,0,0,1,2,0,0 rm.. } a x r $whds,-1 + $im } #@cli discard : _value1,_value2,... : { x | y | z | c}...{ x | y | z | c},_value1,_value2,... : (no arg) : (+) #@cli : Discard specified values in selected images or discard neighboring duplicate values, #@cli : optionally only for the values along the first of a specified axis. #@cli : If no arguments are specified, neighboring duplicate values are discarded. #@cli : If all pixels of a selected image are discarded, an empty image is returned. #@cli : $ (1;2;3;4;3;2;1) +discard 2 #@cli : $ (1,2,2,3,3,3,4,4,4,4) +discard x #@cli eigen2tensor #@cli : Recompose selected pairs of eigenvalues/eigenvectors as 2x2 or 3x3 tensor fields. #@cli : $$ eigen2tensor : e[^-1] "Recompose pairs in eigen field$? as 2x2 or 3x3 tensor fields." repeat $!/2 { l[$>,{$>+1}] { nm={0,n} if s==2 # 2D tensors. s. c +sqr. *.. ... sqr... # u^2 uv v^2 sh. +*... -1 sh[-5] # v^2 -uv u^2 a[-3--1] c a[-4--2] c sh... 0 *[-3,-1] # l1*(u^2;uv;v^2) sh... 1 *[-2,-1] # l2*(v^2;-uv;u^2) rm... +[-2,-1] elif s==6 # 3D tensors. s. c l[-6--4] { +sqr.. +*[-2,-3] +sqr... *[-5] [-6] *[-4] [-6] sqr[-6] a c } l[-3--1] { +sqr.. +*[-2,-3] +sqr... *[-5] [-6] *[-4] [-6] sqr[-6] a c } s... c -[-5] ... -[-4] ... *.. [-5] *. [-4] (1^0^0^1^0^1) ri. ... *. [-4] rm[-6--4] +[-3--1] else error[0--3] "Command '$0': Invalid image ["{$!-$>-1}"] : Dimensions "{w}","{h}","{d}","{s}" does not represent a field of 2D or 3D eigenvectors." fi => $nm } } #@cli endian : _datatype : (+) #@cli : Reverse data endianness of selected images, eventually considering the pixel being of the specified datatype. #@cli : 'datatype' can be \ # { bool | uint8 | int8 | uint16 | int16 | uint32 | int32 | uint64 | int64 | float32 | float64 }. #@cli : This command does nothing for 'bool', 'uint8' and 'int8' datatypes. #@cli equalize : _nb_levels>0[%],_value_min[%],_value_max[%] : (no arg) : (+) #@cli : Equalize histograms of selected images. #@cli : If value range is specified, the equalization is done only for pixels in the specified #@cli : value range. #@cli : Default values: 'nb_levels=256', 'value_min=0%' and 'value_max=100%'. #@cli : $ image.jpg +equalize #@cli : $ image.jpg +equalize 4,0,128 #@cli f : eq. to 'fill'. : (+) #@cli fill : value1,_value2,... : [image] : 'formula' : (+) #@cli : Fill selected images with values read from the specified value list, existing image #@cli : or mathematical expression. Single quotes may be omitted in 'formula'. #@cli : (eq. to 'f'). #@cli : $ 4,4 fill 1,2,3,4,5,6,7 #@cli : $ 4,4 (1,2,3,4,5,6,7) fill[-2] [-1] #@cli : $ 400,400,1,3 fill "X=x-w/2; Y=y-h/2; R=sqrt(X^2+Y^2); a=atan2(Y,X); \ # R<=180?255*abs(cos(c+200*(x/w-0.5)*(y/h-0.5))):850*(a%(0.1*(c+1)))" #@cli : $$ #@cli index : { [palette] | palette_name },0<=_dithering<=1,_map_colors={ 0 | 1 } : (+) #@cli : Index selected vector-valued images by specified vector-valued palette. #@cli : 'palette_name' can be { default | hsv | lines | hot | cool | jet | flag | cube | rainbow | algae | amp |\ # balance | curl | deep | delta | dense | diff | haline | ice | matter | oxy | phase | rain | solar | speed | tarn |\ # tempo | thermal | topo | turbid | aurora | hocuspocus | srb2 | uzebox } #@cli : Default values: 'dithering=0' and 'map_colors=0'. #@cli : $ image.jpg +index 1,1,1 #@cli : $ image.jpg (0;255;255^0;128;255^0;0;255) +index[-2] [-1],1,1 #@cli : $$ https://gmic.eu/tutorial/gindex index : check "${2=0}>=0 && $2<=1 && isbool(${3=0})" if ${"is_image_arg $1"} pass$1 e[0--3] "Index values in image$? by LUT $1, with dithering level $2." else names=${-_palette_names} N:=narg($names) l[] { if isint("$1") name=${"arg0 ($1%"$N"),"$names} else name="$1" fi onfail name="$1" } e[0--3] "Index values in image$? by LUT '"$name"', with dithering level $2." palette $1 fi index[^-1] .,$2,$3 rm. #@cli ir : eq. to 'inrange'. ir : check "isbool(${3=1}) && isbool(${4=$3})" _gmic_s="$?" v + _inrange $* #@cli inrange : min[%],max[%],_include_min_boundary={ 0:no | 1:yes },_include_max_boundary={ 0:no | 1:yes } #@cli : Detect pixels whose values are in specified range `[min,max]`, in selected images. #@cli : (eq. to 'ir'). #@cli : Default value: 'include_min_boundary=include_max_boundary=1'. #@cli : $ image.jpg +inrange 25%,75% inrange : check "isbool(${3=1}) && isbool(${4=$3})" _gmic_s="$?" v + _$0 $* _inrange : skip "${3=1},${4=$3}" v:=$3+2*$4 s=${"arg0 "$v",out,\" min\",\" max\",\" min and max\""} if $v%3 b="y" else b="ies" fi e[0--3] "Extract pixel values in range [$1,$2] in image"$_gmic_s", with"$s" boundar"$b" included." foreach { m=$1 M=$2 if ispercentage($1) m:=im+(iM-im)*$1 fi if ispercentage($2) M:=im+(iM-im)*$2 fi f. inrange(i,$m,$M,$3) } #@cli map : [palette],_boundary_conditions : palette_name,_boundary_conditions : (+) #@cli : Map specified vector-valued palette to selected indexed images. #@cli : Each output image has 'M*N' channels, where 'M' and 'N' are the numbers of channels of, respectively, \ # the corresponding input image and the 'palette' image. #@cli : 'palette_name' can be { default | hsv | lines | hot | cool | jet | flag | cube | rainbow | algae | amp | \ # balance | curl | deep | delta | dense | diff | gray | haline | ice | matter | oxy | phase | rain | solar | speed | \ # tarn | tempo | thermal | topo | turbid | aurora | hocuspocus | srb2 | uzebox } #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default value: 'boundary_conditions=0'. #@cli : $ image.jpg +luminance map[-1] 3 #@cli : $ image.jpg +rgb2ycbcr split[-1] c (0,255,0) resize[-1] 256,1,1,1,3 map[-4] [-1] remove[-1] append[-3--1] c \ # ycbcr2rgb[-1] #@cli : $$ map : check "isint(${2=0},0,3)" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror boundary=${s$2} names=${-_palette_names} N:=narg($names) l[] { if isint("$1") name=${"arg0 ($1%"$N"),"$names} else name="$1" fi onfail name="$1" } e[^-1] "Map color LUT '"$name"' on image$?, with "$boundary" boundary conditions." palette $1 map[^-1] .,$2 rm. #@cli mix_channels : (a00,...,aMN) : [matrix] #@cli : Apply specified matrix to channels of selected images. #@cli : $ image.jpg +mix_channels (0,1,0;1,0,0;0,0,1) mix_channels : e[^-1] "Apply matrix $* to channels of image$?." if ${"is_image_arg $1"} pass$1 1 else i ${^0} fi foreach[^-1] { nm={n} whd:=[w,h,d] r. {[whd,s]},1,1,-1 pass. 0 mv. 0 m* r $whd,{h},-1 => $nm } rm. #@cli negate : base_value : (no arg) #@cli : Negate image values. #@cli : Default value: 'base_value=(undefined)'. #@cli : $ image.jpg +negate negate : skip "${1=,}" if isnum("$*") e[0--3] "Negate values of image$?, according to base value $*." - {"$*"} * -1 else e[0--3] "Negate values of image$?." repeat $! { -[$>] {$>,iM} } * -1 if ['"$1"']!=',' noarg fi fi #@cli noise : amplitude>=0[%],_noise_type : (+) #@cli : Add random noise to selected images. #@cli : 'noise_type' can be { 0:gaussian | 1:uniform | 2:salt&pepper | 3:poisson | 4:rice }. #@cli : Default value: 'noise_type=0'. #@cli : $ image.jpg +noise[0] 50,0 +noise[0] 50,1 +noise[0] 10,2 cut 0,255 #@cli : $ 300,300,1,3 [0] noise[0] 20,0 noise[1] 20,1 +histogram 100 display_graph[-2,-1] 400,300,3 #@cli noise_perlin : _scale_x[%]>0,_scale_y[%]>0,_scale_z[%]>0,_seed_x,_seed_y,_seed_z #@cli : Render 2D or 3D Perlin noise on selected images, from specified coordinates. #@cli : The Perlin noise is a specific type of smooth noise, #@cli : described here : . #@cli : Default values: 'scale_x=scale_y=scale_z=16' and 'seed_x=seed_y=seed_z=0'. #@cli : $ 500,500,1,3 noise_perlin , noise_perlin : check "${1=16}>0 && ${2=$1}>0 && ${3=$1}>0 && isnum(${4=0}) && isnum(${5=0}) && isnum(${6=0})" e[^-1] "Render Perlin noise on image$?, with scales (${1-3}) and seeds (${4-6})." init="permutation = [ 151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240, 21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88, 237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231, 83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1, 216,80,73,209,76,132,187,208,89,18,169,200,196,135,130,116,188,159,86,164,100,109,198, 173,186,3,64,52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47, 16,58,17,182,189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167, 43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,228,251, 34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,49,192,214,31, 181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,222,114,67,29, 24,72,243,141,128,195,78,66,215,61,156,180 ]; p = [ permutation,permutation ]; fade(t) = (t*t*t*(t*(t*6 - 15) + 10)); _lerp(t,a,b) = lerp(a,b,t); pcmod255 = vectors(); repeat (s,k,pcmod255[k] = p[k%255]); sx = ['$1']; is_px = sx[size(sx) - 1]==_'%'; sy = ['$2']; is_py = sy[size(sy) - 1]==_'%'; sz = ['$3']; is_pz = sz[size(sz) - 1]==_'%'; x0 = $4; y0 = $5; z0 = $6; x1 = x0 + (is_px?1/$1:w/$1); y1 = y0 + (is_py?1/$2:h/$2); z1 = z0 + (is_pz?1/$3:d/$3); fw = (x1 - x0)/max(w - 1,1); fh = (y1 - y0)/max(h - 1,1); fd = (z1 - z0)/max(d - 1,1);" repeat $! { if {$>,d>1} # 3D version f[$>] "*begin("$init" grad(hash,x,y,z) = ( gh = hash&15; gu = gh<8?x:y; gv = gh<4?y:gh==12 || gh==14?x:z; (!(gh&1)?gu:-gu) + (!(gh&2)?gv:-gv)) ); x = x0 + x*fw + pcmod255[c]; y = y0 + y*fh + pcmod255[c]; z = z0 + z*fd + pcmod255[c]; ix = floor(x); iy = floor(y); iz = floor(z); X = ix&255; Y = iy&255; Z = iz&255; fx = x - ix; fy = y - iy; fz = z - iz; u = fade(fx); v = fade(fy); w = fade(fz); A = p[X] + Y; AA = p[A] + Z; AB = p[A + 1] + Z; B = p[X + 1] + Y; BA = p[B] + Z; BB = p[B + 1] + Z; fx1 = fx - 1; fy1 = fy - 1; fz1 = fz - 1; lerp(lerp(lerp(grad(p[AA],fx,fy,fz), grad(p[BA],fx1,fy,fz),u), lerp(grad(p[AB],fx,fy1,fz), grad(p[BB],fx1,fy1,fz),u),v), lerp(lerp(grad(p[AA + 1],fx,fy,fz1), grad(p[BA + 1],fx1,fy,fz1,u), lerp(grad(p[AB + 1],fx,fy1,fz1), grad(p[BB+1],fx1,fy1,fz1),u),v),w)" else # 2D version f[$>] "*begin("$init" grad(hash,x,y) = ( gh = hash&15; gu = gh<8?x:y; gv = gh<4?y:gh==12 || gh==14?x:0; (!(gh&1)?gu:-gu) + (!(gh&2)?gv:-gv)) ); x = x0 + x*fw + pcmod255[c]; y = y0 + y*fh + pcmod255[c]; ix = floor(x); iy = floor(y); X = ix&255; Y = iy&255; fx = x - ix; fy = y - iy; u = fade(fx); v = fade(fy); A = p[X] + Y; B = p[X + 1] + Y; fx1 = fx - 1; fy1 = fy - 1; lerp(lerp(grad(p[A],fx,fy), grad(p[B],fx1,fy),u), lerp(grad(p[A + 1],fx,fy1), grad(p[B + 1],fx1,fy1),u),v)" fi } #@cli noise_poissondisk : _radius[%]>0,_max_sample_attempts>0,_p_norm>0 #@cli : Add poisson disk sampling noise to selected images. #@cli : Implements the algorithm from the article "Fast Poisson Disk Sampling in Arbitrary Dimensions", #@cli : by Robert Bridson (SIGGRAPH'2007). #@cli : Default values: 'radius=8', 'max_sample_attempts=30' and 'p_norm=2'. #@cli : $ 300,300 noise_poissondisk 8 noise_poissondisk : check "${1=8}>0 && isint(${2=30},1) && ${3=2}>0" e[^-1] "Add poisson disk sampling points to image$?, with radius $1, max sample attempts $2 and p-norm $3." R0,K,P=${1-3} foreach { W,H,D={w},{h},{d} value:=iM+(im==iM) R:=ispercentage($R0)?max(w,h,d)*$R0:$R0 C:=$R/sqrt(sum([$W,$H,$D]>1)) # Cell size # Determing optimal set of neighbors. _poissondisk[] $R,$P,$C f. "($W<2 && i0) || ($H<2 && i1) || ($D<2 && i2)?[8,8,8]:I" discard. 8 s. y,3 a[-3--1] x # Discard useless dims neighbors={^} rm. # Start point sampling. {ceil([$W,$H,$D]/$C)},1,-1 => grid # Grid used for quick testing neighboring points 1,32,1,3 => points # List of points 1,32,1,1 => active # List of active indices eval " insert_active_point(X) = ( i(#$grid,int(X/$C)) = new_ind = da_size(#$points); da_push(#$points,X); da_push(#$active,new_ind); I(#0,X) = $value; ); const eps = 1e-8; neighbors = ["$neighbors"]; # Initialize list of active points. X = int(u([$W,$H,$D] - eps)); insert_active_point(X); # Propagate new points. while (siz = da_size(#$active), indX = int(u(siz - eps)); X = I[#$points,i[#$active,indX]]; for (k = 0, k<$K, ++k, U = [ $W>1?u(-1,1):0, $H>1?u(-1,1):0, $D>1?u(-1,1):0 ]; U*=u($R,2*$R)/(norm(U) + eps); Y = round(X + U); min(inrange(Y,0,[$W,$H,$D] - 1))?( P = int(Y/$C); i(#$grid,P)<0?( is_sample_ok = 1; repeat(size(neighbors)/3,n, # Test neighboring points ind = i(#$grid,P + neighbors[3*n,3]); ind>=0?(nY = I[#$points,ind]; norm"$P"(nY - Y)<=$R?(is_sample_ok = 0; break())); # Invalid point ); is_sample_ok?(insert_active_point(Y); break()); # Add new point ); ); ); k>=$K?da_remove(#$active,indX); # Remove point from active list ); resize(#$points,1,da_size(#$points),1,3,0)" k[0] } # Determine optimal list of 3D neighbors to be tested. +_poissondisk : R,P,C=${1-3} 0 eval " dmin(dx,dy,dz,C) = ( _dx = dx*C; _dy = dy*C; _dz = dz*C; norm"$P"(min(abs(_dx),abs(_dx + C),abs(_dx - C)), min(abs(_dy),abs(_dy + C),abs(_dy - C)), min(abs(_dz),abs(_dz + C),abs(_dz - C))); ); for (n = 1, n<4, ++n, is_none_added = 1; for (k = -n, k<=n, ++k, for (j = -n, j<=n, ++j, for (i = -n, i<=n, ++i, abs(maxabs(i,j,k))==n && dmin(i,j,k,$C)<$R?(da_push([i,j,k]); is_none_added = 0) ) ) ); is_none_added?break(); ); resize(#-1,1,da_size(),1,3,0)" #@cli normp : p>=0 #@cli : Compute the pointwise Lp-norm norm of vector-valued pixels in selected images. #@cli : Default value: 'p=2'. #@cli : $ image.jpg +normp[0] 0 +normp[0] 1 +normp[0] 2 +normp[0] inf normp : check "isnum(${1==2}) && $1>=0" e[^-1] "Compute pointwise L"$1"-norm of vectors, in image$?." if !$1 != 0 compose_channels + elif $1==1 abs compose_channels + elif $1==2 norm elif $1==inf abs compose_channels max else ^ $1 compose_channels + ^ {1/$1} fi #@cli norm1 #@cli : Compute the pointwise L1-norm of vector-valued pixels in selected images. #@cli : $ image.jpg +norm1 #@cli : $$ norm1 : e[^-1] "Compute pointwise L1-norm of vectors, in image$?." abs compose_channels + #@cli norm : eq. to 'norm2'. norm : _gmic_s="$?" v + _norm2 #@cli norm2 #@cli : Compute the pointwise L2-norm (euclidean norm) of vector-valued pixels in selected images. #@cli : $ image.jpg +norm #@cli : $$ norm2 : _gmic_s="$?" v + _$0 _norm2 : e[^-1] "Compute pointwise euclidean norm of vectors, in image"$_gmic_s"." sqr compose_channels + sqrt #@cli n : eq. to 'normalize'. : (+) #@cli normalize : { value0[%] | [image0] },{ value1[%] | [image1] },_constant_case_ratio : [image] : (+) #@cli : Linearly normalize values of selected images in specified range. #@cli : (eq. to 'n'). #@cli : $ image.jpg split x,2 normalize[-1] 64,196 append x #@cli : $$ #@cli normalize_l2 #@cli : Normalize selected images such that they have a unit L2 norm. normalize_l2 : e[^-1] "Normalize image$?, s.t they have a unit L2 norm." repeat $! { /[$>] {norm={$>,in};norm!=0?norm:1} } #@cli normalize_sum #@cli : Normalize selected images such that they have a unit sum. #@cli : $ image.jpg +histogram 256 normalize_sum[-1] display_graph[-1] 400,300 normalize_sum : e[^-1] "Normalize image$?, s.t they have a unit sum." foreach { / {is!=0?is:1} } #@cli not #@cli : Apply boolean not operation on selected images. #@cli : $ image.jpg +ge 50% +not[-1] not : e[^-1] "Apply boolean not operation on image$?." == 0 #@cli orientation #@cli : Compute the pointwise orientation of vector-valued pixels in selected images. #@cli : $ image.jpg +orientation +norm[-2] negate[-1] mul[-2] [-1] reverse[-2,-1] #@cli : $$ orientation : e[^-1] "Compute pointwise orientation vectors, in image$?." repeat $! { +norm[$>] replace. 0,1 /[$>,-1] } #@cli oneminus #@cli : For each selected image, compute one minus image. #@cli : $ image.jpg normalize 0,1 +oneminus oneminus : e[^-1] "Compute one minus selected images$?." * -1 + 1 #@cli otsu : _nb_levels>0 #@cli : Hard-threshold selected images using Otsu's method. #@cli : The computed thresholds are returned as a list of values in the status. #@cli : Default value: 'nb_levels=256'. #@cli : $ image.jpg luminance +otsu , otsu : check "isint(${1=256},1)" e[^-1] "Hard-threshold image$? using Otsu\47s method, with $1 histogram levels." foreach { imM:=[im,iM] +histogram $1,$imM otsu:=" sum = sumB = wB = best_variance = best_t = 0; repeat (w,t,sum+=t*i[t]); repeat (w,t, wB+=i[t]; if (!wB, continue()); wF = whds#-2 - wB; if (!wF, break()); sumB+=t*i[t]; mB = sumB/wB; mF = (sum - sumB)/wF; variance = wB*wF*(mB - mF)^2; if (variance>best_variance, best_variance = variance; best_t = t); ); imM = ["$imM"]; imM[0] + best_t*(imM[1] - imM[0])/(w - 1)" rm. >=. $otsu if $> u ${},$otsu else u $otsu fi } #@cli polar2complex #@cli : Compute polar to complex transforms of selected images. polar2complex : e[^-1] "Compute polar to complex transforms of image$?." repeat int($!/2) { l[{2*$>},{2*$>+1}] { ri[1] [0],3 +sin. cos.. *. ... *[-3,-2] } } #@cli quantize : nb_levels>=1,_keep_values={ 0 | 1 },_quantization_type={ -1:median-cut | 0:k-means | 1:uniform } #@cli : Quantize selected images. #@cli : Default value: 'keep_values=1' and 'quantization_type=0'. #@cli : $ image.jpg luminance +quantize 3 #@cli : $ 200,200,1,1,'cos(x/10)*sin(y/10)' +quantize[0] 6 +quantize[0] 4 +quantize[0] 3 +quantize[0] 2 quantize : check "isint($1,1) && isbool(${2=1}) && isint(${3=0},-1,1)" e[^-1] "Quantize image$? using $1 levels, "${arg0\ !$2,with,without}" keeping value range." foreach { if $3==1 # Uniform quantization. if s==1 # Greyscale image. if $2 mM:=[im,iM] n 0,$1 round 1,-1 min {$1-1} n $mM else n 0,$1 round 1,-1 min {$1-1} fi else mM:=[im,iM] uniform_distribution $1,{s} n. $mM index.. .,0,$2 rm. fi else +colormap $1,{!$3},1 index.. .,0,$2 rm. # Non-uniform quantization. fi } #@cli quantize_area : _min_area>0 #@cli : Quantize selected images such that each flat region has an area greater or equal to 'min_area'. #@cli : Default value: 'min_area=10'. #@cli : $ image.jpg quantize 3 +blur 1 round[-1] +quantize_area[-1] 2 quantize_area : check "${1=10}>0" e[^-1] "Quantize image$? by regions of areas greater than $1." if $1==1 return fi foreach { if s>1 +f. "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm. round. 0.01 else [0] fi area. 0,0 <. $1 do [0] f.. "* begin( const boundary = 1; offx = [ -1,1,0,0,0,0 ]; offy = [ 0,0,-1,1,0,0 ]; offz = [ 0,0,0,0,-1,1 ]; nb_offs = d>1?6:h>1?4:2; ); is_neighbor = j(-1) && j(-1); if (h>1, is_neighbor&=j(1,0) && j(0,1)); if (d>1, is_neighbor&=j(0,0,-1) && j(0,0,1)); is_neighbor = !is_neighbor; i && is_neighbor?( col0 = I(#0); kmin = -1; distmin = inf; repeat (nb_offs,k, p = offx[k]; q = offy[k]; r = offz[k]; if (!j(p,q,r), col = J(#0,p,q,r); dist = norm(col-=col0); if (dist=0, I(#-1) = J(#0,offx[kmin],offy[kmin],offz[kmin]); 0, 1 ); ):i" rv[0,-1] rm. while iM rm. } #@cli rand : { value0[%] | [image0] },_{ value1[%] | [image1] },_[pdf],_precision : [image] : (+) #@cli : Fill selected images with random values in the specified range. #@cli : If no '[pdf]' (probability density function) is specified, random values follow a uniform distribution. #@cli : Argument 'precision' tells about the number of distinct values that can be generated when a '[pdf]' \ # is specified. #@cli : $ 400,400,1,3 rand -10,10 +blur 10 sign[-1] #@cli : $ (8,2,1) 50,50 rand[-1] 0,255,[-2] #@cli : $ 256 gaussian[-1] 30 line[-1] 47%,0,53%,0,1,0 500,500 rand[-1] 0,255,[-2] +histogram[-1] 256 \ # display_graph[0,2] 640,480,3,0 #@cli rand_sum : sum>0,_random_function #@cli : Fill selected images with strictly positive, random, integer values, that sums to 'sum'. #@cli : For each image, 'sum' must be greater or equal than 'width*height*depth*spectrum'. #@cli : Default value: 'random_function=u'. #@cli : $ 100 rand_sum 1000 rand_sum : check "isint($1,1) && isexpr(${3=u})" e[^-1] "Fill image$? with strictly positive, random, integer values that sums to $1, with random function '$3'." foreach { if whds>$1 error[0--4] "Command 'rand_sum': Image ["$>"] = ("{[w,h,d,s]}") has "{whds}" elements (should be <=$1)." fi f. "abs($3)" *. {($1-whds)/is} round. +. 1 eval " sum = is; while (sum!=$1, off = v(whds - 1); val = i[off]; delta = $1 - sum; dval = sign(delta)*v(abs(delta)); val + dval>0?(i[off]+=dval; sum+=dval); )" } #@cli replace : source,target #@cli : Replace pixel values in selected images. #@cli : $ (1;2;3;4) +replace 2,3 replace : e[^-1] "Replace pixel values $1 with $2 in image$?." f "i==$1?($2):i" #@cli replace_inf : _expression #@cli : Replace all infinite values in selected images by specified expression. #@cli : $ (0;1;2) log +replace_inf 2 replace_inf : e[^-1] "Replace all infinite values in image$? by expression '$1'." f "isinf(i)?($1):i" #@cli replace_infnan : _expression #@cli : Replace all NaN and infinite values in selected images by specified expression. replace_infnan : e[^-1] "Replace all +-inf and NaN values in images$? by expression '$1'." f "isinf(i) || isnan(i)?($1):i" #@cli replace_nan : _expression #@cli : Replace all NaN values in selected images by specified expression. #@cli : $ (-1;0;2) sqrt +replace_nan 2 replace_nan : e[^-1] "Replace all NaN values in images$? by expression '$1'." f "isnan(i)?($1):i" #@cli replace_seq : "search_seq","replace_seq" #@cli : Search and replace a sequence of values in selected images. #@cli : $ (1;2;3;4;5) +replace_seq "2,3,4","7,8" replace_seq : skip "${2=''}" e[^-1] "Replace value sequence '$1' by value sequence '${2--1}' in image$?." y foreach { nm={n} 1,100% eval "ref([ $1 ],str1); ref([ ${2--1} ],str2); copy_block(pd,src,len) = ( l = len; pd + l>=h#1?resize(#1,1,h(#1) + 3*l,1,1,0); copy(i[pd],src,l); pd+=l; ); for (ps = pd = 0, ps=0, qs>ps?copy_block(pd,i[#0,ps],qs - ps); copy_block(pd,str2,size(str2)); ps = qs + size(str1); ); ps $nm } #@cli replace_str : "search_str","replace_str" #@cli : Search and replace a string in selected images (viewed as strings, i.e. sequences of character codes). #@cli : $ ('"Hello there, how are you ?"') +replace_str "Hello there","Hi David" replace_str : skip "${2=}" e[^-1] "Replace string '"{/"$1"}"' by string '"{/"${2--1}"}"' in image$?." replace_seq {``{'"$1"'}},{'"${2--1}"'} #@cli round : rounding_value>=0,_rounding_type : (no arg) : (+) #@cli : Round values of selected images. #@cli : 'rounding_type' can be { -1:backward | 0:nearest | 1:forward }. #@cli : Default value: 'rounding_type=0'. #@cli : $ image.jpg +round 100 #@cli : $ image.jpg mul {pi/180} sin +round #@cli roundify : gamma>=0 #@cli : Apply roundify transformation on float-valued data, with specified gamma. #@cli : Default value: 'gamma=0'. #@cli : $ 1000 fill '4*x/w' repeat 5 { +roundify[0] {$>*0.2} } append c display_graph 400,300 roundify : check $1>=0 e[^-1] "Roundify image$?, with gamma $1." if $1==1 return fi foreach { +round 1 -.. . +*.. 2 abs. ^. $1 sign... *[-3,-1] *.. 0.5 + } #@cli = : eq. to 'set'. : (+) #@cli set : value,_x[%],_y[%],_z[%],_c[%] : (+) #@cli : Set pixel value in selected images, at specified coordinates. #@cli : (eq. to '=').\n #@cli : If specified coordinates are outside the image bounds, no action is performed. #@cli : Default values: 'x=y=z=c=0'. #@cli : $ 2,2 set 1,0,0 set 2,1,0 set 3,0,1 set 4,1,1 #@cli : $ image.jpg repeat 10000 { set 255,{u(100)}%,{u(100)}%,0,{u(100)}% } #@cli threshold : value[%],_is_soft={ 0 | 1 } : #@cli : Threshold values of selected images. #@cli : 'soft' can be { 0:hard-thresholding | 1:soft-thresholding }. #@cli : Default value: 'is_soft=0'. #@cli : $ image.jpg +threshold[0] 50% +threshold[0] 50%,1 #@cli : $$ threshold : check "isexpr($1) && isbool(${2=0})" e[^-1] ${"arg0 !$2,Soft,Hard"}"-threshold image$? by $1." if $2 # Soft thresholding f "begin( str = ['$1']; value = str[size(str)-1]==_'%'?im + (iM-im)*$1:$1 ); i>=value?i - value: i<=-value?i + value:0" else ge $1 fi #@cli vector2tensor #@cli : Convert selected vector fields to corresponding tensor fields. vector2tensor : e[^-1] "Convert vector field$? to tensor field$?." foreach { s c if $!==2 +sqr. *.. ... sqr... elif $!==3 +sqr.. +*... .. +sqr... *[-5,-4] [-6] sqr[-6] else error[0--4] "Command '$0': Invalid image ["{$!-$>-1}"] : Dimensions "{w}","{h}","{d}","{s}" does not represent a field of 2D or 3D vectors." fi a c } #--------------------------------- # #@cli :: Colors # #--------------------------------- #@cli adjust_colors : -100<=_brightness<=100,-100<=_contrast<=100,-100<=_gamma<=100,-100<=_hue_shift<=100,\ # -100<=_saturation<=100,_value_min,_value_max #@cli : Perform a global adjustment of colors on selected images. #@cli : Range of correct image values are considered to be in [value_min,value_max] (e.g. [0,255]). #@cli : If 'value_min==value_max==0', value range is estimated from min/max values of selected images. #@cli : Processed images have pixel values constrained in [value_min,value_max]. #@cli : Default values: 'brightness=0', 'contrast=0', 'gamma=0', 'hue_shift=0', 'saturation=0', 'value_min=value_max=0'. #@cli : $ image.jpg +adjust_colors 0,30,0,0,30 adjust_colors : check "${1=0}>=-100 && $1<=100 && ${2=0}>=-100 && $2<=100 && ${3=0}>=-100 && $3<=100 && ${4=0}>=-100 && $4<=100 && ${5=0}>=-100 && $5<=100" skip ${6=0},${7=0} e[^-1] "Adjust colors of image$?, with brightness $1, contrast $2, gamma $3, hue shift $4, saturation $5 and value range [$6,$7]." foreach { split_opacity l[0] { range:="$6==$7 && !$6?[im,iM]:[min($6,$7),max($6,$7)]" m:=arg(1,$range) M:=arg(2,$range) fact:=255/max(1e-5,$M-$m) - $m * $fact if $4" || "$5 # Adjust Hue/Saturation to_rgb[0] rgb2hsv[0] sh[0] 0 +. {$4*1.8} rm. sh[0] 1 +. {($5%)^(1+($5>0))} c. 0,1 rm. hsv2rgb[0] fi if $3 # Adjust Gamma /[0] 255 ^[0] {10^-($3%)} *[0] 255 fi if $2 # Adjust Contrast -[0] 128 *[0] {exp($2/64)} +[0] 128 fi +[0] {$1*2} # Adjust Brightness /[0] $fact +[0] $m c[0] $range # Renormalize values. a c } a c } #@cli ac : eq. to 'apply_channels'. ac : _gmic_s="$?" v + _apply_channels $"*" #@cli apply_channels : "command",color_channels,_value_action={ 0:none | 1:cut | 2:normalize } #@cli : Apply specified command on the chosen color channel(s) of each selected images. #@cli : (eq. to 'ac').\n #@cli : Argument 'color_channels' refers to a colorspace, and can be basically one of #@cli : { all | rgba | [s]rgb | ryb | lrgb | ycbcr | lab | lch | hsv | hsi | hsl | cmy | cmyk | yiq }. #@cli : You can also make the processing focus on a few particular channels of this colorspace, #@cli : by setting 'color_channels' as 'colorspace_channel' (e.g. 'hsv_h' for the hue). #@cli : All channel values are considered to be provided in the [0,255] range. #@cli : Default value: 'value_action=0'. #@cli : $ image.jpg +apply_channels "equalize blur 2",ycbcr_cbcr apply_channels : _gmic_s="$?" v + _$0 $"*" _apply_channels : check "isint(${3=0},0) && $3<=2" channels=${"_ac_list \"$2\""} e[^-1] "Apply command '$1' on channels '"$channels"' of image"$_gmic_s"." ('$/') id:="h=0;for(k=0,k0,0<=_dithering<=1,_method={ 0:median-cut | 1:k-means } #@cli : Index selected vector-valued images by adapted colormaps. #@cli : Default values: 'dithering=0' and 'method=1'. #@cli : $ image.jpg +autoindex[0] 4 +autoindex[0] 8 +autoindex[0] 16 autoindex : check "isint($1,1) && ${2=0}>=0" skip ${3=1} e[^-1] "Index colors in images$? by adapted colormap with $1 entries, dithering level $2 and "\ ${arg0\ !$3,k-means,median-cut}" method." foreach { if w>h if w>256 +rs 256 else [0] fi else if h>256 +rs ,256 else [0] fi fi colormap[1] $1,$3,0 index[0] [1],$2,1 rm[1] } #@cli bayer2rgb : _GM_smoothness,_RB_smoothness1,_RB_smoothness2 #@cli : Transform selected RGB-Bayer sampled images to color images. #@cli : Default values: 'GM_smoothness=RB_smoothness=1' and 'RB_smoothness2=0.5'. #@cli : $ image.jpg rgb2bayer 0 +bayer2rgb 1,1,0.5 bayer2rgb : skip ${1=1},${2=1},${3=0.5} e[^-1] "Transform RGB-Bayer image$? to color images, with smoothness ($1,$2,$3)." channels 0 foreach { # Expand image size to avoid problems with borders. expand xy,{"2 + 4*$1"} # Compute green-magenta chromaticity. (-1,1;1,-1) r. ..,..,1,1,0,2 +*.. . (0.25,0.5,0.25) convolve.. . transpose. convolve.. . rm. b. $1 *.. . -[-3,-2] # Compute red-blue chromaticity. (1,-1) r. ..,..,1,1,0,2 # Horizontal estimate *. ... (0.25,0.5,0.25) convolve.. . transpose. convolve.. . rm. b. y,$2 b. x,$3 (1;-1) r. ..,..,1,1,0,2 # Vertical estimate *. [-4] (0.25,0.5,0.25) convolve.. . transpose. convolve.. . rm. b. x,$2 b. y,$3 +[-2,-1] /. 2 # Luminance reconstruction. (2,0;0,-2) r. ..,..,1,1,0,2 *. .. -[-4,-1] # RGB reconstruction. a[-3--1] c mix_rgb. 1,-1,2,1,1,0,1,-1,-2 # Shrink to original image size. shrink xy,{"2 + 4*$1"} c 0,255 } #@cli clut : "clut_name",_resolution>0,_cut_and_round={ 0:no | 1:yes } #@cli : Insert one of the 1149 pre-defined CLUTs at the end of the image list.\n #@cli : 'clut_name' can be { 12_years_a_slave | 1917 | 2-strip-process | 60s | 60s_faded | 60s_faded_alt | 7drk_21 | \ # action_magenta_01 | action_red_01 | ad_astra | adventure_1453 | agfa_apx_100 | agfa_apx_25 | agfa_precisa_100 | \ # agfa_ultra_color_100 | agfa_vista_200 | agressive_highligjtes_recovery_5 | aladdin | alberto_street | alien_green | \ # ampio | amstragram | amstragram+ | analog_film_1 | analogfx_anno_1870_color | analogfx_old_style_i | \ # analogfx_old_style_ii | analogfx_old_style_iii | analogfx_sepia_color | analogfx_soft_sepia_i | \ # analogfx_soft_sepia_ii | anime | ant-man | apocalypse_this_very_moment | aqua | aqua_and_orange_dark | aquaman | \ # arabica_12 | asistas | atomic_pink | atusa | autumn | autumn_leaves | ava_614 | avalanche | avengers_endgame | \ # azrael_93 | baby_driver | bad_boys_for_life | basuco | bboyz_2 | bc_darkum | beach_aqua_orange | \ # beach_faded_analog | beati | beauty_and_the_beast | berlin_sky | bisogno | black_and_white | black_panther | \ # black_star | black_white_01 | black_white_02 | black_white_03 | black_white_04 | black_white_05 | black_white_06 | \ # blade_runner | bleach_bypass | bleachbypass_1 | bleachbypass_2 | bleachbypass_3 | bleachbypass_4 | \ # bleech_bypass_green | bleech_bypass_yellow_01 | blue_cold_fade | blue_dark | blue_house | blue_ice | blue_love_39 | \ # blue_mono | blue_shadows_01 | bluearchitecture | bluehour | blues | bob_ford | bohemian_rhapsody | bombshell | \ # bourbon_64 | boyado | bright_green_01 | bright_teal_orange | bright_warm | brightgreen | brown_mobster | brownbm | \ # brownish | bw_1 | bw_10 | bw_2 | bw_3 | bw_4 | bw_5 | bw_6 | bw_7 | bw_8 | bw_9 | bw_but_yellow | byers_11 | \ # calidum | candlelight | captain_marvel | caribe | chemical_168 | chrome_01 | cineblue | cinebm_4k | cinema | \ # cinema_2 | cinema_3 | cinema_4 | cinema_5 | cinema_noir | cinematic-1 | cinematic-10 | cinematic-2 | cinematic-3 | \ # cinematic-4 | cinematic-5 | cinematic-6 | cinematic-7 | cinematic-8 | cinematic-9 | cinematic_01 | cinematic_02 | \ # cinematic_03 | cinematic_04 | cinematic_05 | cinematic_06 | cinematic_07 | cinematic_for_flog | cinematic_forest | \ # cinematic_lady_bird | cinematic_mexico | city | city_7 | city_dust | city_of_god | classic_films_01 | \ # classic_films_02 | classic_films_03 | classic_films_04 | classic_films_05 | classic_teal_and_orange | clayton_33 | \ # clear | clear_teal_fade | clouseau_54 | cobi_3 | coffee_44 | cold_clear_blue | cold_clear_blue_1 | cold_ice | \ # cold_simplicity_2 | coldchrome | color_rich | colore | colorful_0209 | colornegative | conflict_01 | contrail_35 | \ # contrast_with_highlights_protection | contrasty_afternoon | contrasty_green | convold | cosa | creed_2 | \ # crispautumn | crispromance | crispwarm | crispwinter | cross_process_cp_130 | cross_process_cp_14 | \ # cross_process_cp_15 | cross_process_cp_16 | cross_process_cp_18 | cross_process_cp_3 | cross_process_cp_4 | \ # cross_process_cp_6 | crushin | cubicle_99 | culor | d_o_1 | dark_blues_in_sunlight | dark_green_02 | dark_green_1 | \ # dark_man_x | dark_orange_teal | dark_place_01 | darkandsomber | darkness | date_39 | day_4nite | day_for_night | \ # day_to_night_kings_blue | deep | deep_blue | deep_dark_warm | deep_high_contrast | deep_teal_fade | \ # deep_warm_fade | deepskintones_2 | deepskintones_3 | delicatessen | denoiser_simple_40 | desert_gold_37 | \ # dimension | dimmer | directions_23 | django_25 | doctor_strange | domingo_145 | dream_1 | dream_85 | \ # drop_green_tint_14 | dropblues | dunkirk | duotone_blue_red | earth_tone_boost | eda_0_2 | edgyember | \ # elegance_38 | enchanted | ensaya | eterna_for_flog | expired_69 | expired_fade | expired_polaroid | extreme | \ # fade | fade_to_green | faded | faded_47 | faded_alt | faded_analog | faded_extreme | faded_green | faded_pink-ish | \ # faded_print | faded_retro_01 | faded_retro_02 | faded_vivid | fadedlook | fallcolors | falua | farkling | fatos | \ # faux_infrared | faux_infrared_bw_1 | faux_infrared_color_p_2 | faux_infrared_color_p_3 | faux_infrared_color_r_0a | \ # faux_infrared_color_r_0b | faux_infrared_color_yp_1 | fezzle | fg_cinebasic | fg_cinebright | fg_cinecold | \ # fg_cinedrama | fg_cinetealorange_1 | fg_cinetealorange_2 | fg_cinevibrant | fg_cinewarm | fgcinebasic | \ # fgcinebright | fgcinecold | fgcinedrama | fgcinetealorange_1 | fgcinetealorange_2 | fgcinevibrant | fgcinewarm | \ # fight_club | film_0987 | film_9879 | film_gb-19 | film_high_contrast | film_print_01 | film_print_02 | filmic | \ # filo | flat_30 | flat_blue_moon | flavin | flog_to_rec_709 | foggynight | folger_50 | ford_v_ferrari | foresta | \ # formula_b | french_comedy | frosted | frostedbeachpicnic | fuji_160c | fuji_160c_+ | fuji_160c_++ | fuji_160c_- | \ # fuji_3510_constlclip | fuji_3510_constlmap | fuji_3510_cuspclip | fuji_3513_constlclip | fuji_3513_constlmap | \ # fuji_3513_cuspclip | fuji_400h | fuji_400h_+ | fuji_400h_++ | fuji_400h_- | fuji_800z | fuji_800z_+ | \ # fuji_800z_++ | fuji_800z_- | fuji_astia_100_generic | fuji_astia_100f | fuji_fp-100c | fuji_fp-100c_+ | \ # fuji_fp-100c_++ | fuji_fp-100c_+++ | fuji_fp-100c_++_alt | fuji_fp-100c_- | fuji_fp-100c_-- | fuji_fp-100c_alt | \ # fuji_fp-100c_cool | fuji_fp-100c_cool_+ | fuji_fp-100c_cool_++ | fuji_fp-100c_cool_- | fuji_fp-100c_cool_-- | \ # fuji_fp-100c_negative | fuji_fp-100c_negative_+ | fuji_fp-100c_negative_++ | fuji_fp-100c_negative_+++ | \ # fuji_fp-100c_negative_++_alt | fuji_fp-100c_negative_- | fuji_fp-100c_negative_-- | fuji_fp-3000b | \ # fuji_fp-3000b_+ | fuji_fp-3000b_++ | fuji_fp-3000b_+++ | fuji_fp-3000b_- | fuji_fp-3000b_-- | fuji_fp-3000b_hc | \ # fuji_fp-3000b_negative | fuji_fp-3000b_negative_+ | fuji_fp-3000b_negative_++ | fuji_fp-3000b_negative_+++ | \ # fuji_fp-3000b_negative_- | fuji_fp-3000b_negative_-- | fuji_fp-3000b_negative_early | fuji_fp_100c | fuji_hdr | \ # fuji_neopan_1600 | fuji_neopan_1600_+ | fuji_neopan_1600_++ | fuji_neopan_1600_- | fuji_neopan_acros_100 | \ # fuji_provia_100_generic | fuji_provia_100f | fuji_provia_400f | fuji_provia_400x | fuji_sensia_100 | \ # fuji_superia_100 | fuji_superia_100_+ | fuji_superia_100_++ | fuji_superia_100_- | fuji_superia_1600 | \ # fuji_superia_1600_+ | fuji_superia_1600_++ | fuji_superia_1600_- | fuji_superia_200 | fuji_superia_200_xpro | \ # fuji_superia_400 | fuji_superia_400_+ | fuji_superia_400_++ | fuji_superia_400_- | fuji_superia_800 | \ # fuji_superia_800_+ | fuji_superia_800_++ | fuji_superia_800_- | fuji_superia_hg_1600 | fuji_superia_reala_100 | \ # fuji_superia_x-tra_800 | fuji_velvia_100_generic | fuji_velvia_50 | fuji_xtrans_iii_acros | \ # fuji_xtrans_iii_acros+g | fuji_xtrans_iii_acros+r | fuji_xtrans_iii_acros+ye | fuji_xtrans_iii_astia | \ # fuji_xtrans_iii_classic_chrome | fuji_xtrans_iii_mono | fuji_xtrans_iii_mono+g | fuji_xtrans_iii_mono+r | \ # fuji_xtrans_iii_mono+ye | fuji_xtrans_iii_pro_neg_hi | fuji_xtrans_iii_pro_neg_std | fuji_xtrans_iii_provia | \ # fuji_xtrans_iii_sepia | fuji_xtrans_iii_velvia | fusion_88 | futuristicbleak_1 | futuristicbleak_2 | \ # futuristicbleak_3 | futuristicbleak_4 | going_for_a_walk | golden | golden_bright | golden_fade | golden_mono | \ # golden_night_softner_43 | golden_sony_37 | golden_vibrant | goldengate | goldentime | goldfx_bright_spring_breeze | \ # goldfx_bright_summer_heat | goldfx_hot_summer_heat | goldfx_perfect_sunset_01min | goldfx_perfect_sunset_05min | \ # goldfx_perfect_sunset_10min | goldfx_spring_breeze | goldfx_summer_heat | good_morning | green_15 | green_2025 | \ # green_action | green_afternoon | green_and_orange | green_blues | green_book | green_conflict | green_day_01 | \ # green_day_02 | green_g_09 | green_indoor | green_light | green_mono | green_yellow | greenish_contrasty | \ # greenish_fade | greenish_fade_1 | gremerta | greyhound | hackmanite | hallowen_dark | happyness_133 | \ # hard_teal_orange | hardboost | harsh_day | harsh_sunset | helios | herderite | heulandite | hiddenite | \ # highlights_protection | hilutite | hitman | hlg_1_1 | honey_light | hong_kong | horrorblue | howlite | huesio | \ # husmes | huyan | hydracore | hyla_68 | hypersthene | hypnosis | hypressen | i_tonya | ideo | ilford_delta_100 | \ # ilford_delta_3200 | ilford_delta_3200_+ | ilford_delta_3200_++ | ilford_delta_3200_- | ilford_delta_400 | \ # ilford_fp_4_plus_125 | ilford_hp_5 | ilford_hp_5_+ | ilford_hp_5_++ | ilford_hp_5_- | ilford_hp_5_plus_400 | \ # ilford_hps_800 | ilford_pan_f_plus_50 | ilford_xp_2 | inception | indoor_blue | industrial_33 | \ # infrared_-_dust_pink | instantc | j | jarklin | jojo_rabbit | joker | jumanji_the_next_level | \ # jurassic_world_fallen_kingdom | justice_league | justpeachy | jwick_21 | k_tone_vintage_kodachrome | kahve_3 | \ # kh_1 | kh_10 | kh_2 | kh_3 | kh_4 | kh_5 | kh_6 | kh_7 | kh_8 | kh_9 | killstreak | kingsman_the_golden_circle | \ # knives_out | kodak_2383_constlclip | kodak_2383_constlmap | kodak_2383_cuspclip | kodak_2393_constlclip | \ # kodak_2393_constlmap | kodak_2393_cuspclip | kodak_bw_400_cn | kodak_e-100_gx_ektachrome_100 | \ # kodak_ektachrome_100_vs | kodak_ektachrome_100_vs_generic | kodak_ektar_100 | kodak_elite_100_xpro | \ # kodak_elite_chrome_200 | kodak_elite_chrome_400 | kodak_elite_color_200 | kodak_elite_color_400 | \ # kodak_elite_extracolor_100 | kodak_hie_hs_infra | kodak_kodachrome_200 | kodak_kodachrome_25 | \ # kodak_kodachrome_64 | kodak_kodachrome_64_generic | kodak_portra_160 | kodak_portra_160_+ | kodak_portra_160_++ | \ # kodak_portra_160_- | kodak_portra_160_nc | kodak_portra_160_nc_+ | kodak_portra_160_nc_++ | kodak_portra_160_nc_- | \ # kodak_portra_160_vc | kodak_portra_160_vc_+ | kodak_portra_160_vc_++ | kodak_portra_160_vc_- | kodak_portra_400 | \ # kodak_portra_400_+ | kodak_portra_400_++ | kodak_portra_400_- | kodak_portra_400_nc | kodak_portra_400_nc_+ | \ # kodak_portra_400_nc_++ | kodak_portra_400_nc_- | kodak_portra_400_uc | kodak_portra_400_uc_+ | \ # kodak_portra_400_uc_++ | kodak_portra_400_uc_- | kodak_portra_400_vc | kodak_portra_400_vc_+ | \ # kodak_portra_400_vc_++ | kodak_portra_400_vc_- | kodak_portra_800 | kodak_portra_800_+ | kodak_portra_800_++ | \ # kodak_portra_800_- | kodak_portra_800_hc | kodak_t-max_100 | kodak_t-max_3200 | kodak_t-max_400 | kodak_tmax_3200 | \ # kodak_tmax_3200_+ | kodak_tmax_3200_++ | kodak_tmax_3200_- | kodak_tmax_3200_alt | kodak_tri-x_400 | \ # kodak_tri-x_400_+ | kodak_tri-x_400_++ | kodak_tri-x_400_- | kodak_tri-x_400_alt | korben_214 | la_la_land | \ # landscape | landscape_01 | landscape_02 | landscape_03 | landscape_04 | landscape_05 | landscape_1 | landscape_10 | \ # landscape_2 | landscape_3 | landscape_4 | landscape_5 | landscape_6 | landscape_7 | landscape_8 | landscape_9 | \ # lateafternoonwanderlust | latesunset | lavark | lc_1 | lc_10 | lc_2 | lc_3 | lc_4 | lc_5 | lc_6 | lc_7 | lc_8 | \ # lc_9 | lenox_340 | levex | life_giving_tree | light | light_blown | litore | little_women | logan | lomo | \ # lomography_redscale_100 | lomography_x-pro_slide_200 | london_nights | longbeachmorning | loro | lotta | louetta | \ # low_contrast_blue | low_key_01 | lucky_64 | lushgreen | lushgreensummer | mad_max_fury_road | maesky | \ # magenta_day | magenta_day_01 | magenta_dream | magenta_yellow | magentacoffee | magichour | marriage_story | \ # matrix | mckinnon_75 | memories | mercato | metropolis | milo_5 | minimalistcaffeination | modern_film | \ # modern_films_01 | modern_films_02 | modern_films_03 | modern_films_04 | modern_films_05 | modern_films_06 | \ # modern_films_07 | molti | mono_2 | mono_tinted | monochrome | monochrome_1 | monochrome_2 | moody_1 | moody_10 | \ # moody_2 | moody_3 | moody_4 | moody_5 | moody_6 | moody_7 | moody_8 | moody_9 | moonlight | moonlight_01 | \ # moonlight_2 | moonrise | morning_6 | morroco_16 | mostly_blue | mother! | motus | moviz_1 | moviz_10 | moviz_11 | \ # moviz_12 | moviz_13 | moviz_14 | moviz_15 | moviz_16 | moviz_17 | moviz_18 | moviz_19 | moviz_2 | moviz_20 | \ # moviz_21 | moviz_22 | moviz_23 | moviz_24 | moviz_25 | moviz_26 | moviz_27 | moviz_28 | moviz_29 | moviz_3 | \ # moviz_30 | moviz_31 | moviz_32 | moviz_33 | moviz_34 | moviz_35 | moviz_36 | moviz_37 | moviz_38 | moviz_39 | \ # moviz_4 | moviz_40 | moviz_41 | moviz_42 | moviz_43 | moviz_44 | moviz_45 | moviz_46 | moviz_47 | moviz_48 | \ # moviz_5 | moviz_6 | moviz_7 | moviz_8 | moviz_9 | mucca | mute_shift | muted_01 | muted_fade | mysticpurplesunset | \ # nah | natural_vivid | naturalboost | negative | nemesis | neon_770 | neutral | neutral_pump | neutral_teal_orange | \ # neutral_warm_fade | newspaper | night_01 | night_02 | night_03 | night_04 | night_05 | night_blade_4 | \ # night_king_141 | night_spy | night_view | nightfromday | nightlife | nigrum | no_time_to_die | nostalgiahoney | \ # nostalgic | nw-1 | nw-10 | nw-2 | nw-3 | nw-4 | nw-5 | nw-6 | nw-7 | nw-8 | nw-9 | old_west | once_upon_a_time | \ # once_upon_a_time_in_hollywood | onda | only_red | only_red_and_blue | operation_yellow | orange_dark_4 | \ # orange_dark_7 | orange_dark_look | orange_tone | orange_underexposed | orangeandblue | oranges | padre | paladin | \ # paladin_1875 | parasite | partia | pasadena_21 | passing_by | perso | picola | pink_fade | \ # pirates_of_the_caribbean | pitaya_15 | pmcinematic_01 | pmcinematic_02 | pmcinematic_03 | pmcinematic_04 | \ # pmcinematic_05 | pmcinematic_06 | pmcinematic_07 | pmnight_01 | pmnight_02 | pmnight_03 | pmnight_04 | pmnight_05 | \ # polaroid_664 | polaroid_665 | polaroid_665_+ | polaroid_665_++ | polaroid_665_- | polaroid_665_-- | \ # polaroid_665_negative | polaroid_665_negative_+ | polaroid_665_negative_- | polaroid_665_negative_hc | \ # polaroid_667 | polaroid_669 | polaroid_669_+ | polaroid_669_++ | polaroid_669_+++ | polaroid_669_- | \ # polaroid_669_-- | polaroid_669_cold | polaroid_669_cold_+ | polaroid_669_cold_- | polaroid_669_cold_-- | \ # polaroid_672 | polaroid_690 | polaroid_690_+ | polaroid_690_++ | polaroid_690_- | polaroid_690_-- | \ # polaroid_690_cold | polaroid_690_cold_+ | polaroid_690_cold_++ | polaroid_690_cold_- | polaroid_690_cold_-- | \ # polaroid_690_warm | polaroid_690_warm_+ | polaroid_690_warm_++ | polaroid_690_warm_- | polaroid_690_warm_-- | \ # polaroid_polachrome | polaroid_px-100uv+_cold | polaroid_px-100uv+_cold_+ | polaroid_px-100uv+_cold_++ | \ # polaroid_px-100uv+_cold_+++ | polaroid_px-100uv+_cold_- | polaroid_px-100uv+_cold_-- | polaroid_px-100uv+_warm | \ # polaroid_px-100uv+_warm_+ | polaroid_px-100uv+_warm_++ | polaroid_px-100uv+_warm_+++ | polaroid_px-100uv+_warm_- | \ # polaroid_px-100uv+_warm_-- | polaroid_px-680 | polaroid_px-680_+ | polaroid_px-680_++ | polaroid_px-680_- | \ # polaroid_px-680_-- | polaroid_px-680_cold | polaroid_px-680_cold_+ | polaroid_px-680_cold_++ | \ # polaroid_px-680_cold_++_alt | polaroid_px-680_cold_- | polaroid_px-680_cold_-- | polaroid_px-680_warm | \ # polaroid_px-680_warm_+ | polaroid_px-680_warm_++ | polaroid_px-680_warm_- | polaroid_px-680_warm_-- | \ # polaroid_px-70 | polaroid_px-70_+ | polaroid_px-70_++ | polaroid_px-70_+++ | polaroid_px-70_- | polaroid_px-70_-- | \ # polaroid_px-70_cold | polaroid_px-70_cold_+ | polaroid_px-70_cold_++ | polaroid_px-70_cold_- | \ # polaroid_px-70_cold_-- | polaroid_px-70_warm | polaroid_px-70_warm_+ | polaroid_px-70_warm_++ | \ # polaroid_px-70_warm_- | polaroid_px-70_warm_-- | polaroid_time_zero_expired | polaroid_time_zero_expired_+ | \ # polaroid_time_zero_expired_++ | polaroid_time_zero_expired_- | polaroid_time_zero_expired_-- | \ # polaroid_time_zero_expired_--- | polaroid_time_zero_expired_cold | polaroid_time_zero_expired_cold_- | \ # polaroid_time_zero_expired_cold_-- | polaroid_time_zero_expired_cold_--- | portrait | portrait_1 | portrait_10 | \ # portrait_2 | portrait_3 | portrait_4 | portrait_5 | portrait_6 | portrait_7 | portrait_8 | portrait_9 | \ # progressen | protect_highlights_01 | prussian_blue | pseudogrey | purple | purple_2 | quraqqq_12 | randas | \ # red_afternoon_01 | red_day_01 | red_dream_01 | redblueyellow | reds | reds_oranges_yellows | reeve_38 | remy_24 | \ # rest_33 | retro | retro_brown_01 | retro_magenta_01 | retro_summer_3 | retro_yellow_01 | rocketman | \ # rollei_ir_400 | rollei_ortho_25 | rollei_retro_100_tonal | rollei_retro_80s | rotate_muted | rotate_vibrant | \ # rotated | rotated_crush | satid | saturated_blue | saving_private_damon | scala | science_fiction | scrittle | \ # sea | seges | selor | sensum | separation | serenity | seringe_4 | serpent | seventies_magazine | sevsuz | \ # shade_kings_ink | shadow_king_39 | shine | sicario | sino | skin_tones | slog_to_rec709_basic | \ # slog_to_rec709_contrasty | slog_to_rec709_crush_shadows | slog_to_rec709_green_correction | smart_contrast | \ # smokey | smooth_clear | smooth_cromeish | smooth_fade | smooth_green_orange | smooth_sailing | smooth_teal_orange | \ # soft_fade | softblackandwhite | softwarming | solarized_color | solarized_color_2 | soldi | \ # spider-man_far_from_home | spotlight | springmorning | sprocket_231 | spy_29 | standard | \ # star_wars_the_rise_of_skywalker | strano | street | stringa | studio_skin_tone_shaper | subtle_blue | \ # subtle_green | subtle_yellow | sully | summer | summer_alt | sunlight_love_11 | sunlightlove | sunny | sunny_alt | \ # sunny_rich | sunny_warm | sunset | sunset_aqua_orange | sunset_intense_violet_blue | sunset_violet_mood | \ # super_warm | super_warm_rich | sutro_fx | sweet_bubblegum | sweet_gelatto | taşdemirrr_1 | taiga | tarraco | \ # teal-orange_for_flog | teal_fade | teal_moonlight | tealmagentagold | tealorange | tealorange_1 | tealorange_2 | \ # tealorange_3 | technicalfx_backlight_filter | teigen_28 | tenet | tensiongreen_1 | tensiongreen_2 | \ # tensiongreen_3 | tensiongreen_4 | terra_4 | the_dark_knight | the_darkest_hour | the_gentelmen | \ # the_grand_budapest_hotel | the_hurt_locker | the_irishman | the_lighthouse | the_lobster | the_martian | \ # the_matrices | the_revenant | the_shape_of_water | the_social_network | the_two_popes | the_way_back | \ # thor_ragnarok | thriller_2 | tirare | toastedgarden | top_gun_maverick | trent_18 | true_colors_8 | turkiest_42 | \ # tutto | tweed_71 | ultra_water | uncut_gems | undeniable | undeniable_2 | underwater | unknown | upglow | \ # urban_01 | urban_02 | urban_03 | urban_04 | urban_05 | urban_cowboy | uzbek_bukhara | uzbek_marriage | \ # uzbek_samarcande | valize | valsky | velvetia | venom | very_warm_greenish | vfb_21 | vibrant | vibrant_alien | \ # vibrant_contrast | vibrant_cromeish | victory | vintage | vintage_01 | vintage_02 | vintage_03 | vintage_04 | \ # vintage_05 | vintage_163 | vintage_alt | vintage_brighter | vintage_chrome | vintage_mob | vintage_warmth_1 | \ # violet_taste | vireo_37 | vita | vivid | vubes | war_for_the_planet_of_the_apes | warm | warm_dark_contrasty | \ # warm_fade | warm_fade_1 | warm_highlight | warm_neutral | warm_sunset_red | warm_teal | warm_vintage | \ # warm_yellow | wavefire | waves | well_see | western | western_6 | westernlut_2 | westernlut_2_13 | whiter_whites | \ # winterlighthouse | wipe | wolf_of_wall_street | wonder_woman | wooden_gold_20 | x-men_dark_phoenix | yangabuz_8 | \ # yellow_55b | yellow_film_01 | yellowstone | you_can_do_it | zed_32 | zeke_39 | zilverfx_bw_solarization | \ # zilverfx_infrared | zilverfx_vintage_bw | zombieland_double_tap } #@cli : Default values: 'resolution=33' and 'cut_and_round=1'. #@cli : $ clut summer clut alien_green,17 clut orange_dark4,48 +clut : check "isint(${2=33},1) && isbool(${3=1})" strclut "$1" name=${} l[] { e[^-1] "Input CLUT '"$name"' with resolution $2." path_clut=${-path_cache} if isfile(['{/${path_clut}clut_$name.cimgz}']) i ${path_clut}clut_$name.cimgz fi if $!"!=1 || w<$2 || h<$2 || d<$2" # Decompression needed rm input_cached gmic_cluts.gmz k[${"nmd 1,"$name}] if $!!=1 rm i https://gmic.eu/gmic_cluts.gmz o ${path_clut}gmic_cluts.gmz # Try getting newest version of the CLUTs file repeat $! { if ['{$>,n}']==['$name'] k[$>] break fi } if $!!=1 error[0--5] "Command '$0': Unknown CLUT name '"$name"'." fi fi decompress_clut $2,$2,$2 if $3 round c 0,255 to_rgb fi if $2>48 o. ${path_clut}clut_$name.cimgz fi elif "w>$2 || h>$2 || d>$2" r $2,$2,$2,3,2 # Downsize from higher resolution fi k. => $name } #@cli clut2hald #@cli : Convert selected 3D CLUTs to 2D HaldCLUTs. #@cli : $ clut summer +clut2hald clut2hald : e[^-1] "Convert 3D CLUT$? to 2D HaldCLUT format." foreach { n:=ceil(sqrt(max(w,h,d)))^2 r $n,$n,$n,100%,3 r {a=round(sqrt(w^3));[a,a]},1,100%,-1 } #@cli hald2clut #@cli : Convert selected 2D HaldCLUTs to 3D CLUTs. hald2clut : e[^-1] "Convert 2D HaldCLUT$? to 3D CLUT format." foreach { r {a=round(cbrt(whd));[a,a,a]},100%,-1 } #@cli cmy2rgb #@cli : Convert color representation of selected images from CMY to RGB. cmy2rgb : e[^-1] "Convert color representation of image$? from CMY to RGB." rgb2cmy #@cli cmyk2rgb #@cli : Convert color representation of selected images from CMYK to RGB. cmyk2rgb : e[^-1] "Convert color representation of image$? from CMYK to RGB." foreach { s c +/. -255 +. 1 *[0-2] . rm. +[0-2] . rm. a c cmy2rgb } #@cli colorblind : type={ 0:protanopia | 1:protanomaly | 2:deuteranopia | 3:deuteranomaly | \ # 4:tritanopia | 5:tritanomaly | 6:achromatopsia | 7:achromatomaly } #@cli : Simulate color blindness vision. #@cli : Simulation method of Vienot, Brettel & Mollon 1999, \ # "Digital video colourmaps for checking the legibility of displays by dichromats". #@cli : The dichromacy matrices of the paper were adapted to sRGB (RGB->XYZ). #@cli : Anomalous trichromacy simulated via linear interpolation with the identity and a factor of 0.6. #@cli : $ image.jpg +colorblind 0 colorblind : check "isint($1,0,7)" s0="protanopia" s1="protanomaly" s2="deuteranopia" s3="deuteranomaly" s4="tritanopia" s5="tritanomaly" s6="achromatopsia" s7="achromatomaly" e[^-1] "Simulate color blindness of type '"${s$1}"' on image$?." type0=(0.10889,0.89111,0;0.10889,0.89111,0;0.00447,-0.00447,1.0) type1=(0.46533,0.53467,0;0.06533,0.93467,0;0.00268,-0.00268,1) type2=(0.29031,0.70969,0;0.29031,0.70969,0;-0.02197,0.02197,1) type3=(0.57418,0.42582,0;0.17418,0.82582,0;-0.01318,0.01318,1) type4=(1,0.15236,-0.15236;0,0.86717,0.13283;0,0.86717,0.13283) type5=(1,0.09142,-0.09142;0,0.92030,0.07970;0,0.52030,0.47970) type6=(0.299,0.587,0.114;0.299,0.587,0.114;0.299,0.587,0.114) type7=(0.618,0.320,0.062;0.163,0.775,0.062;0.163,0.320,0.516) foreach { split_opacity l[0] { to_rgb srgb2rgb mix_channels ${type$1} rgb2srgb } a c } #@cli colormap : nb_levels>=0,_method={ 0:median-cut | 1:k-means },_sort_vectors #@cli : Estimate best-fitting colormap with 'nb_colors' entries, to index selected images. #@cli : Set 'nb_levels==0' to extract all existing colors of an image. #@cli : 'sort_vectors' can be { 0:unsorted | 1:by increasing norm | 2:by decreasing occurrence }. #@cli : Default value: 'method=1' and 'sort_vectors=1'. #@cli : $ image.jpg +colormap[0] 4 +colormap[0] 8 +colormap[0] 16 #@cli : $$ https://gmic.eu/oldtutorial/_colormap colormap : check "isint($1,0) && isbool(${2=1}) && isint(${3=1},0,2)" m0,m1="median-cut","k-means" s0,s1,s2="",", sorted by increasing norm",", sorted by decreasing occurence" if $1 e[0--3] "Estimate colormap with $1 entries for image$?, by "${m$2}" method"${s$3}"." else e[0--3] "Estimate full colormap for image$?"${s$3}"." fi foreach { nm={b} is_half=${-is_half} r {whd},1,1,100%,-1 if $1 # Estimate quantized colormap. if ${"count_colors. {$1+1}"}<=$1 +colormap 0,0,0 # Check that 'nb_levels' is lower than number of used colors else +_colormap $1 # Median-cut algorithm if $2 # Add k-means step max_diff:=(iM-im+1)/8192 do +index.. . # Find nearest cluster for each color if $is_half ..,1,1,{1,s} eval " csum = vector(#w*s#0); cocc = vector(#w,0); repeat (w#2,k, ind = i[#2,k]; repeat (s#0,c,csum[ind + c*w]+=i(#0,k,0,0,c)); ++cocc[ind]; ); off = 0; repeat (s#0,c, repeat (w,k,occ = cocc[k]; occ?(csum[off++]/=occ)); ); draw(#3,csum)" rm.. else ..,1,1,{1,s+1} f.. ">I[#3,i]+=[ I[#0,x],1 ]" rm.. f. "s = i(x,0,0,s-1); s?I/s:[ I[#1,x], 0 ]" fi +-.. . abs. diff:=iM/w rm. # Compute colormap difference j.. . rm. while $diff>$max_diff fi fi else # Extract full colormap. # Compute hashcode map. +n. 0,255 f. "ret = vectors(); H = 0; repeat (s,p,(H*=31)+=j(0,0,0,p)); ret[0] = H%2048; ret" channels. 0 equalize. 2048 n. 0,2047 round. 1 # Find single occurrence of each color/vector. 0x2048 eval[0] "> begin(sizes = vector2048(); ret = vectors()); col_s = I; H = i#1; indH = H + 2; sH = sizes[H]; !sH || find(#indH,col_s,s#0*(sH - 1),-s#0)<0?( # Color not found sH>=h(#indH)?resize(#indH,s#0,max(8,2*h#indH),1,1,0); copy(i(#indH,0,sH,0,0),col_s); ++sizes[H]; ); ret; end(fill(sizes,k,resize(#k + 2,s#0,sizes[k],1,1,0)))" a[2--1] y permute. yzcx rm.. fi # Sort colormap if requested. if $3==1 k. +norm rv a c sort +,x channels 1,100% # Sort by increasing norm. elif $3==2 index.. .,0,0 histogram.. {[w,0,w-1]} a y sort -,x rows 1 # Sort by decreasing occurrence else k. fi => "[colormap of "$nm"]" } _colormap : # Implementation of the median-cut algorithm. m "__colormap : repeat s { sh[$""1] $> =.. {iM-im},$""1,0,0,$> rm. }" 1,1,1,{s} __colormap 0 # Initialize image of box ranges repeat $1-1 { b,a:=xM,cM # b = box with highest range, a = axis with highest range l[$b] { shift 0,0,0,-$a,2 sort +,x shift 0,0,0,$a,2 xm:=int(w/2) } if {$b,w>1} +z[$b] $xm,100% z[$b] 0,{$xm-1} else 1,1,1,{$b,s} fi rv[-2,-1] r. {w+1},1,1,100%,0 __colormap $b __colormap {$!-2} # Update box ranges } rm. r 1,1,1,100%,2 a x # Average value in each box and append as final colormap um __colormap #@cli compose_channels #@cli : Compose all channels of each selected image, using specified arithmetic operator (+,-,or,min,...). #@cli : Default value: '1=+'. #@cli : $ image.jpg +compose_channels and #@cli : $$ compose_channels : skip ${1="+"} e[^-1] "Compose all channels of image$?, with operator '$1'." foreach { repeat s { sh[0] $> } $1[^0] r[0] 100%,100%,100%,1,-1 k[0] } #@cli count_colors : _count_until={ 0: none | >0: max number of counted colors } #@cli : Count number of distinct colors in selected images until it reaches the specified max number of counted colors. #@cli : Set 'count_until' to '0' to disable limit on counted colors. #@cli : This command returns the number of distinct colors for each image (separated by commas). count_colors : check "isint(${1=0},0)" if $1 e[0--3] "Count number of distinct colors in image$?, until it reaches $1." else e[0--3] "Count number of distinct colors in image$?." fi res,sep= foreach { # Compute hashcode map. +n. 0,255 f. "ret = vectors(); H = 0; repeat (s,p,(H*=31)+=j(0,0,0,p)); ret[0] = H%2048; ret" channels. 0 equalize. 2048 n. 0,2047 round. 1 # Find single occurrence of each color/vector. 0x2048 eval[0] "> const max_nb = $1; begin(sizes = vector2048(); nb = 0; ret = vectors()); !max_nb || nb=h(#indH)?resize(#indH,s#0,max(8,2*h#indH),1,1,0); copy(i(#indH,0,sH,0,0),col_s); ++sizes[H]; ++nb; ); ); ret; end(set('nb',sum(sizes)))" k[0] res.=$sep$nb sep=, } u $res #@cli deltaE : [image],_metric={ 0:deltaE_1976 | 1:deltaE_2000 },"_to_Lab_command" #@cli : Compute the CIE DeltaE color difference between selected images and specified [image]. #@cli : Argument 'to_Lab_command' is a command able to convert colors of [image] into a Lab representation. #@cli : Default values: 'metric=1' and 'to_Lab_command="srgb2lab"'. #@cli : $ image.jpg +blur 2 +deltaE[0] [1],1,srgb2lab deltaE : check ${"is_image_arg $1"}" && isbool(${2=1})" skip "${3=srgb2lab}" e[^-1] "Compute the CIE DeltaE_"${"s0,s1=1976,2000 u $s$2"}" color difference between image$? and image$1, "\ "with to_Lab command '$3'." pass$1 1 needs_to_lab:="s = ['$3']; s!=0 && s!=' '" if $needs_to_lab m "_deltaE_to_lab : $3" +_deltaE_to_lab. rm.. fi repeat $!-1 { l[$>,-1] { nm={0,n} if $needs_to_lab _deltaE_to_lab[0] fi if !$2 -.. . norm.. # DeltaE_1976 else 100%,100%,100%,1,${-math_lib}"deltaE00(I#0,I#1)" rv[0,-1] rm. # DeltaE_2000 fi =>[0] $nm } } rm. um _deltaE_to_lab #@cli direction2rgb #@cli : Compute RGB representation of selected 2D direction fields. #@cli : $ image.jpg luminance gradient append c blur 2 orientation +direction2rgb direction2rgb : e[^-1] "Compute RGB representation of 2D direction field$?." channels 0,1 foreach { nm={n} s c complex2polar round.. 0.001 *. {180/pi} %. 360 100%,100%,1,1,1 mv... $! if im!=iM n. 0,1 else f. 1 fi a c hsv2rgb => $nm } #@cli ditheredbw #@cli : Create dithered B&W version of selected images. #@cli : $ image.jpg +equalize ditheredbw[-1] ditheredbw : e[^-1] "Create dithered B&W version of image$?." foreach { split_opacity luminance[0] n[0] 0,255 (0,255) index[0] .,1,1 rm. a c } #@cli fc : eq. to 'fill_color'. fc : _gmic_s="$?" v + _fill_color $* #@cli fill_color : col1,...,colN #@cli : Fill selected images with specified color. #@cli : (eq. to 'fc'). #@cli : $ image.jpg +fill_color 255,0,255 #@cli : $$ https://gmic.eu/oldtutorial/_fill_color fill_color : _gmic_s="$?" v + _$0 $* _fill_color : e[0--3] "Fill image"$_gmic_s" with color (${^0})." foreach { repeat s { sh[0] $> f. {arg(1+$>,${^0})} } k[0] } #@cli gradient2rgb : _is_orientation={ 0 | 1 } #@cli : Compute RGB representation of 2D gradient of selected images. #@cli : Default value: 'is_orientation=0'. #@cli : $ image.jpg +gradient2rgb 0 equalize[-1] gradient2rgb : check "isbool(${1=0})" arg0 !$1,"orientation ","" e[^-1] "Compute RGB representation of 2D gradient "${}"of image$?." norm foreach { if $1 gradient_orientation 2 else g xy fi a c direction2rgb } #@cli hcy2rgb #@cli : Convert color representation of selected images from HCY to RGB. hcy2rgb : e[^-1] "Convert color representation of image$? from HCY to RGB." to_color f " H = (R/60)%6; X = G*(1 - abs(H%2 - 1)); RGB = arg0(int(H),[G,X,0],[X,G,0],[0,G,X],[0,X,G],[X,0,G],[G,0,X]); m = B - 0.3*RGB[0] - 0.59*RGB[1] - 0.11*RGB[2]; cut((RGB+=m)*=255,0,255)" #@cli hsi2rgb #@cli : Convert color representation of selected images from HSI to RGB. hsi2rgb : e[^-1] "Convert color representation of image$? from HSI to RGB." to_color f " H = (R/60)%6; S = G; I = B; Z = 1 - abs((H%2) - 1); C = I*S/(1 + Z); X = C*Z; m = I*(1 - S)/3; RGB = arg0(int(H),[C,X,0],[X,C,0],[0,C,X],[0,X,C],[X,0,C],[C,0,X]); (RGB+=m)*=3*255" #@cli hsi82rgb #@cli : Convert color representation of selected images from HSI8 to RGB. hsi82rgb : e[^-1] "Convert color representation of image$? from HSI8 to RGB." _hsx82rgb hsi2rgb #@cli hsl2rgb #@cli : Convert color representation of selected images from HSL to RGB. hsl2rgb : e[^-1] "Convert color representation of image$? from HSL to RGB." to_color f " H = (R/60)%6; S = G; L = B; C = (1 - abs(2*L - 1))*S; X = C*(1 - abs(H%2 - 1)); m = L - C/2; RGB = arg0(int(H),[C,X,0],[X,C,0],[0,C,X],[0,X,C],[X,0,C],[C,0,X]); (RGB+=m)*=255" #@cli hsl82rgb #@cli : Convert color representation of selected images from HSL8 to RGB. hsl82rgb : e[^-1] "Convert color representation of image$? from HSL8 to RGB." _hsx82rgb hsl2rgb #@cli hsv2rgb #@cli : Convert color representation of selected images from HSV to RGB. #@cli : $ (0,360;0,360^0,0;1,1^1,1;1,1) resize 400,400,1,3,3 hsv2rgb hsv2rgb : e[^-1] "Convert color representation of image$? from HSV to RGB." to_color f " H = (R/60)%6; S = G; V = B; C = V*S; X = C*(1 - abs(H%2 - 1)); m = V - C; RGB = arg0(int(H),[C,X,0],[X,C,0],[0,C,X],[0,X,C],[X,0,C],[C,0,X]); (RGB+=m)*=255" #@cli hsv82rgb #@cli : Convert color representation of selected images from HSV8 to RGB. hsv82rgb : e[^-1] "Convert color representation of image$? from HSV8 to RGB." _hsx82rgb hsv2rgb _hsx82rgb : foreach { sh[0] 0 /. 0.708333 rm. sh[0] 1,2 /. 255 rm. } #@cli int2rgb #@cli : Convert color representation of selected images from INT24 to RGB. int2rgb : e[^-1] "Convert color representation of image$? from INT24 scalars to RGB." round foreach { +>> 8 &[1] 255 +&[0] 255 >>[0] 16 a c } #@cli ipremula #@cli : Convert selected images with premultiplied alpha colors to normal colors. #@cli : See also: ''premula''. ipremula : e[^-1] "Convert image$? with premultiplied alpha colors to normal colors." to_a foreach { +channels. 100% /. 255 max. 1e-8 sh.. 0,{s#0-2} /. .. k[0] } #@cli jzazbz2rgb : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from RGB to Jzazbz. #@cli : Default value: 'illuminant=2'. jzazbz2rgb : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from Jzazbz to RGB, using the "${arg0\ $illu,D50,D65,E}" illuminant." jzazbz2xyz xyz2rgb $illu #@cli jzazbz2xyz #@cli : Convert color representation of selected images from RGB to XYZ. jzazbz2xyz : e[^-1] "Convert color representation of image$? from Jzazbz to XYZ." foreach { split_opacity f[0] ${-_jzazbz_const}" tmp = i0 + Jzazbz_d0; Iz = tmp/(1 + Jzazbz_d - Jzazbz_d*tmp); azz = i1; bzz = i2; Lp = Iz + 0.138605043271539*azz + 0.0580473161561189*bzz; Mp = Iz - 0.138605043271539*azz - 0.0580473161561189*bzz; Sp = Iz - 0.0960192420263189*azz - 0.811891896056039*bzz; tmp = Lp^(1/Jzazbz_p); L = peakLum*((Jzazbz_c1 - tmp)/(Jzazbz_c3*tmp-Jzazbz_c2))^(1/Jzazbz_n); tmp = Mp^(1/Jzazbz_p); M = peakLum*((Jzazbz_c1 - tmp)/(Jzazbz_c3*tmp-Jzazbz_c2))^(1/Jzazbz_n); tmp = Sp^(1/Jzazbz_p); S = peakLum*((Jzazbz_c1 - tmp)/(Jzazbz_c3*tmp-Jzazbz_c2))^(1/Jzazbz_n); Xp = 1.92422643578761*L - 1.00479231259537*M + 0.037651404030618*S; Yp = 0.350316762094999*L + 0.726481193931655*M - 0.065384422948085*S; Zp = -0.0909828109828476*L - 0.312728290523074*M + 1.52276656130526*S; X = (Xp + (Jzazbz_b - 1)*Zp)/Jzazbz_b; Y = (Yp + (Jzazbz_g - 1)*X)/Jzazbz_g; Z = Zp; [ X,Y,Z ]/255" a c } # The XYZ<->Jzazbz conversion code has been written by Alan Gibson, # and published on this page: _jzazbz_const : u "const Jzazbz_b = 1.15; const Jzazbz_g = 0.66; const Jzazbz_c1 = 3424/4096; const Jzazbz_c2 = 2413/128; const Jzazbz_c3 = 2392/128; const Jzazbz_n = 2610/16384; const Jzazbz_p = 1.7*2523/32; const Jzazbz_d = -0.56; const Jzazbz_d0 = 1.6295499532821566e-11; const peakLum = 10000;" #@cli lab2lch #@cli : Convert color representation of selected images from Lab to Lch. lab2lch : e[^-1] "Convert color representation of image$? from Lab to Lch." foreach { to_color s c complex2polar[1,2] a c } #@cli lab2rgb : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from Lab to RGB. #@cli : Default value: 'illuminant=2'. #@cli : $ (50,50;50,50^-3,3;-3,3^-3,-3;3,3) resize 400,400,1,3,3 lab2rgb lab2rgb : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from Lab to RGB, using the "${arg0\ $illu,D50,D65,E}" illuminant." lab2xyz $illu xyz2rgb $illu #@cli lab2srgb : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from Lab to sRGB. #@cli : Default value: 'illuminant=2'. #@cli : $ (50,50;50,50^-3,3;-3,3^-3,-3;3,3) resize 400,400,1,3,3 lab2rgb lab2srgb : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from Lab to sRGB, using the "${arg0\ $illu,D50,D65,E}" illuminant." lab2rgb $illu rgb2srgb #@cli lab82srgb : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from Lab8 to sRGB. #@cli : Default value: 'illuminant=2'. #@cli : $ (50,50;50,50^-3,3;-3,3^-3,-3;3,3) resize 400,400,1,3,3 lab2rgb lab82srgb : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from Lab8 to sRGB, using the "${arg0\ $illu,D50,D65,E}" illuminant." lab82rgb $illu rgb2srgb #@cli lab2xyz : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from Lab to XYZ. #@cli : Default value: 'illuminant=2'. lab2xyz : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from Lab to XYZ, using the "${arg0\ $illu,D50,D65,E}" illuminant." to_color f " begin( const epsilon = 216/24389; const kappa = 24389/27; D65 = [ 0.4124564, 0.3575761, 0.1804375, 0.2126729, 0.7151522, 0.0721750, 0.0193339, 0.1191920, 0.9503041 ]; D50 = [ 0.43603516, 0.38511658, 0.14305115, 0.22248840, 0.71690369, 0.06060791, 0.01391602, 0.09706116, 0.71392822 ]; E = [ 0.488718,0.3106803,0.2006017, 0.1762044,0.8129847,0.0108109, 0,0.0102048,0.9897952 ]; white = ("$illu"==2?E:"$illu"==1?D65:D50)*[ 1,1,1 ]; ); fy = (i0 + 16)/116; fz = fy - i2/200; fx = i1/500 + fy; fx3 = fx^3; fz3 = fz^3; XYZ = [ fx3>epsilon?fx3:(116*fx - 16)/kappa, i0>kappa*epsilon?((i0+16)/116)^3:i0/kappa, fz3>epsilon?fz3:(116*fz - 16)/kappa ]; XYZ*=white" #@cli lab82rgb : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from Lab8 to RGB. #@cli : Default value: 'illuminant=2'. lab82rgb : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from Lab8 to RGB, using the "${arg0\ $illu,D50,D65,E}" illuminant." foreach { sh[0] 0 /. 2.55 rm. sh[0] 1 /. 0.85 -. 127 rm. sh[0] 2 /. 0.836 -. 149 rm. } lab2rgb $illu c 0,255 #@cli lch2lab #@cli : Convert color representation of selected images from Lch to Lab. lch2lab : e[^-1] "Convert color representation of image$? from Lch to Lab." foreach { to_color s c polar2complex[1,2] a c } #@cli lch2rgb : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from Lch to RGB. #@cli : Default value: 'illuminant=2'. lch2rgb : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from Lch to RGB, using the "${arg0\ $illu,D50,D65,E}" illuminant." lch2lab lab2rgb $illu #@cli lch82rgb : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from Lch8 to RGB. #@cli : Default value: 'illuminant=2'. lch82rgb : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from Lch8 to RGB, using the "${arg0\ $illu,D50,D65,E}" illuminant." pi,facth:=[pi,255/(2*pi)] foreach { sh[0] 0 /. 2.55 rm. sh[0] 1 /. 1.1086 rm. sh[0] 2 /. $facth -. $pi rm. } lch2rgb $illu c 0,255 #@cli luminance #@cli : Compute luminance of selected sRGB images. #@cli : $ image.jpg +luminance #@cli : $$ luminance : e[^-1] "Compute luminance of image$?." remove_opacity srgb2rgb foreach { if s==3 sh 0 sh[0] 1 sh[0] 2 *[1] 0.22248840 *[2] 0.71690369 *[3] 0.06060791 +[1-3] rm[1] elif s!=1 norm n 0,255 fi } channels 0 rgb2srgb #@cli lightness #@cli : Compute lightness of selected sRGB images. #@cli : $ image.jpg +lightness lightness : e[^-1] "Compute lightness of image$?." remove_opacity srgb2rgb if s==3 srgb2lab channels 0 * {255/100} elif s!=1 norm n 0,255 rgb2srgb fi #@cli lut_contrast : _nb_colors>1,_min_rgb_value #@cli : Generate a RGB colormap where consecutive colors have high contrast. #@cli : This function performs a specific score maximization to generate the result, so #@cli : it may take some time when 'nb_colors' is high. #@cli : Default values: 'nb_colors=256' and 'min_rgb_value=64'. +lut_contrast : check "isint(${1=256},1) && isnum(${2=48})" e[^-1] "Generate high-contrast RGB colormap with $1 colors and min RGB value $2." l[] { # Initialization by farthest point sampling of the RGB cube. 64,64,64,1 eval "repeat (8,k, x = !!(k&1); y = !!(k&2); z = !!(k&4); i([x,y,z]*(w-1)) = 1)" N:=is e "" do +neq. 0 distance. 1 xyzM:=[xM,yM,zM] rm. col:=round([$xyzM]*255/(w-1)) if max($col)>=$2 =. 1,$xyzM N+=1 else =. -1,$xyzM fi e "\r [ Init ] > Colors \#"$N while $N<$1 >. 0 {is},1,1,3 eval.. ">begin(k = 0); i>0?(I[#-1,k++] = round([ x,y,z ]*255/63))" k. N0=5 # 5 first colors to be preserved. s x repeat $! { if {$>,I==[0,0,0]} rv[$>,0] fi if {$>,I==[255,255,255]} rv[$>,1] fi if {$>,I==[255,0,0]} rv[$>,2] fi if {$>,I==[0,255,0]} rv[$>,3] fi if {$>,I==[0,0,255]} rv[$>,4] fi } a x # Functional optimization. e "" +srgb2lab a c energy_max=${-_lut_contrast.} nb_attempts=1000 do e "\r [ Optim ] > Score = "{_$energy_max}", Attempts = "$nb_attempts" " . eval " do( k0 = v("$N0",w-1); k1 = v("$N0",w-1), k0==k1); tmp = I[k0]; I[k0] = I[k1]; I[k1] = tmp" energy=${-_lut_contrast.} if $energy>$energy_max energy_max=$energy k. nb_attempts=1000 else rm. nb_attempts-=1 fi while $nb_attempts>0 channels 0,2 } _lut_contrast : 100%,1,1,1,"> const N = 10; dist = 0; sumw = 0; RGB0 = (I[#0,x])[3,3]; kmin = max(x-N,0); kmax = min(x+N,w-1); for (k = kmin, k<=kmax, ++k, RGB = (I[#0,k])[3,3]; w = (1 + N - abs(k-x))^1.5; dist+= w*norm(RGB - RGB0); sumw+=w; ); dist/=sumw" u {is} rm. #@cli map_clut : [clut] | "clut_name" #@cli : Map specified RGB color LUT to selected images. #@cli : $ image.jpg uniform_distribution {2^6},3 mirror[-1] x +map_clut[0] [1] map_clut : e[^-1] "Map color LUT $1 on image$?." if !$! return fi to_color if ${"is_image_arg $1"} pass$1 0 to_rgb. else clut "$1" fi l:=round((w*h*d)^(1/3)) if w*h*d!=$l^3 error "Command '$0': Specified CLUT $1 has invalid dimensions "({w},{h},{d},{s}). fi r. $l,$l,$l,3,-1 repeat $!-1 { l[$>,-1] { nm={0,n} split_opacity[0] /[0] {256/$l} +warp. [0],0,1,1 rm[0] mv. 0 a[^-1] c =>[0] $nm } } rm. #@cli match_histogram : [reference_image],_nb_levels>0,_color_channels #@cli : Transfer histogram of the specified reference image to selected images. #@cli : Argument 'color channels' is the same as with command 'apply_channels'. #@cli : Default value: 'nb_levels=256' and 'color_channels=all'. #@cli : $ image.jpg 100,100,1,3,"u([256,200,100])" +match_histogram[0] [1] match_histogram : check ${"is_image_arg $1"}" && ${2=1024}>0" skip "${3=0}" channels=${"_ac_list \"$3\""} e[^-1] "Transfer histogram from image ["${"pass$1 -1"}"] to image$?, "\ "with $2 levels, for channels '"$channels"'." pass$1 1 sref:=s rm. to_colormode $sref if ['$channels']!='all' pass$1 {$sref==3?2:0} to_color ac. "+store _match_histogram_reference",$channels rm. ac "_match_histogram $2",$channels,1 else pass$1 store. _match_histogram_reference foreach { _match_histogram $2 } fi _match_histogram_reference= _match_histogram : $_match_histogram_reference repeat min(s#0,s#1) { sh $> +histogram[-2,-1] $1 cumulate[-2,-1] /.. {-2,i[-1,2]} /. {i[-1,2]} f.. "* const w1 = w -1; val = i; X = x; val=1, nX = max(0,X - step); val(val - vp)?--X); # Rounding ):( step = int((w1 - X)/2); while (X=1, nX = min(w1,X + step); val>i[#-1,nX]?(X = nX):(step = int(step/2))); X(vn - val)?++X); # Rounding ); im#-3 + (iM#-3 - im#-3)*X/w1" n[-4] 0,{w-1} round[-4] map[-4] .. k[0,1] } rm. #@cli match_icp : [reference_image],_precision>0,_transformation_variable #@cli : Transform selected set of d-dimensional vectors to match specified set of reference vectors, \ # using ICP (*Iterative Closest Point*) algorithm. #@cli : A description of ICP is available at . #@cli : Return the L2 alignment error. #@cli : Default values: 'precision=1e-2' and 'transformation_variable=(undefined)'. #@cli : sample lena,earth +match_icp[0] [1] match_icp : check ${"is_image_arg $1"}" && ${2=1e-2}>0" skip "${3=}" e[^-1] "Match vector set$? with reference vector set $1, using ICP with precision $2.\n" pass$1 foreach[^-1] { w,h,d,s={w},{h},{d},{s} pass. mv. 0 # [0]: reference set, [1]: current set if "['$3']!=0" +store. img fi match_pca. .. # Pre-normalize for better initial estimate # Iterate ICP steps. old_err=inf repeat inf { # For each vector, find nearest reference vector. 100%,100%,100%,4,"* P = I(#1); distmin = pmin = qmin = rmin = inf; repeat (d#0,r, repeat (h#0,q, repeat (w#0,p, dist = norm(I(#0,p,q,r) - P); dist Iteration "$>": Error = "{_$err} if !$err" || "abs($err-$old_err)<1e-3 rm. break fi old_err=$err +warp[0] .,0,0 rm.. _match_icp[-2,-1] mmul[-2,-1] r. $s,{h/$s},1,1,-1 permute. yzcx r. $w,$h,$d,$s,-1 } k. if "['$3']!=0" $img rv +_match_icp $3={^} rm[0,-2,-1] fi # Output linear transformation u $err } rm. # Compute linear transformation between two sets of paired vectors. # [-2] : Set of vectors. # [-1] : Corresponding set of reference vectors. _match_icp : N,s:=whd,s permute. cxyz y. # Unrolled (i0_ref_k,i1_ref_k,...) l.. { # Generate matrix for the linear system r 1,{whd},1,100%,-1 permute cyxz 1,100%,1,1,1 a x r 100%,{$s*$N},1,1 r {($s+1)^2},100%,1,1,0 r {$s*($s+1)},{($s+1)*$N},1,1,-1 s y rm[$s--1:{$s+1}] a y } solve. .. #@cli match_pca : [reference_image],_color_channels #@cli : Transfer mean and covariance matrix of specified vector-valued reference image to selected images. #@cli : Argument 'color channels' is the same as with command 'apply_channels'. #@cli : Default value: 'color_channels=all'. #@cli : $ sample lena,earth +match_pca[0] [1] match_pca : check ${"is_image_arg $1"} skip "${2=all}" channels=${"_ac_list \"$2\""} e[^-1] "Transfer mean vector and covariance matrix from image ["${"pass$1 -1"}"] to image$?, "\ "for channels '"$channels"'." pass$1 1 sref:=s rm. if $sref<=4 to_colormode[^-1] $sref fi if $sref==1 # Scalar case can to be solved more easily pass$1 var_ref,avg_ref:=[iv,ia] rm. foreach { - {ia} * {sqrt($var_ref/max(1e-8,iv))} + $avg_ref } elif ['$channels']!='all' pass$1 {$sref==3?2:0} to_color ac. "+store _match_pca_reference",$channels rm. ac "_match_pca",$channels,1 else pass$1 store. _match_pca_reference foreach { _match_pca } fi _match_pca_reference= _match_pca : $_match_pca_reference f.. "*begin( cov_ref = [ "${"covariance_vectors[1] _avg_ref"}" ]; cov = [ "${"covariance_vectors[0] _avg"}" ]; avg_ref = [ "$_avg_ref" ]; avg = [ "$_avg" ]; eig_ref = eig(cov_ref); eig = eig(cov); lambda_ref = sqrt(eig_ref[0,s]); lambda = 1/sqrt(1e-6 + eig[0,s]); repeat(s,k, # Align eigenvectors. sks = s + k*s; Uref = eig_ref[sks,s]; U = eig[sks,s]; dot(Uref,U)<0?copy(eig[sks],U*=-1); ); rot_ref = mul(transpose(eig_ref[s,s*s],s),diag(lambda_ref),s); rot = mul(diag(lambda),eig[s,s*s],s); M = mul(rot_ref,rot,s); ); avg_ref + M*(I - avg)" rm. #@cli match_rgb : [target],_gamma>=0,_regularization>=0,_luminosity_constraints>=0,_rgb_resolution>=0,\ # _is_constraints={ 0 | 1 } #@cli : Transfer colors from selected source images to selected reference image (given as argument). #@cli : 'gamma' determines the importance of color occurrences in the matching process (0:none to 1:huge). #@cli : 'regularization' determines the number of guided filter iterations to remove quantization effects. #@cli : 'luminosity_constraints' tells if luminosity constraints must be applied on non-confident matched colors. #@cli : 'is_constraints' tells if additional hard color constraints must be set (opens an interactive window). #@cli : Default values: 'gamma=0.3','regularization=8', 'luminosity_constraints=0.1', 'rgb_resolution=64' and \ # 'is_constraints=0'. #@cli : $ sample pencils,wall +match_rgb[0] [1],0,0.01 match_rgb : check "${2=0.3}>=0 && ${3=8}>=0 && ${4=0.15}>=0 && ${5=64}>=0 && isbool(${6=0})" e[^-1] "Transfer colors of image $1 to image$?." sigma=1.5 repeat $! { pass$1 0 l[$>,-1] { nm_source={0,b} nm_target={1,b} => source,target # Generate matching functions from 3D RGB features. +_match_rgb[source,target] $2,$sigma,$5 => fsource,ftarget n[fsource,ftarget] 0,255 # Manage color constraints. if $6 h0:=2*{*,v}/3 ws0={source,max(1,w*$h0/h)} wt0={target,max(1,w*$h0/h)} w1:=2*{*,u}/3 hs1={source,max(1,h*$w1/w)} ht1={target,max(1,h*$w1/w)} if abs($ws0+$wt0-$w1)$w1 rs[-2,-1] $w1 fi if h>$h0 rs[-2,-1] ,$h0 fi => visu,both w[visu] -1,-1 N=0 do w[] -1,-1,"[G'MIC] Add Color Guide (Constraint ""#"{1+$N}")" +select[$visu] 1 if i==-1 rm. break fi line[$visu] {i[0]},{i[1]},{i[3]},{i[4]},1,0xF0F0F0F0,0 line[$visu] {i[0]},{i[1]},{i[3]},{i[4]},1,0x0F0F0F0F,255 circle[$visu] {i[0]},{i[1]},5,1,0,0,0 circle[$visu] {i[0]},{i[1]},3,1,255,0,0 circle[$visu] {i[3]},{i[4]},5,1,0,0,0 circle[$visu] {i[3]},{i[4]},3,1,0,255,0 s. y,2 rows[-2,-1] 0,1 a[-2,-1] x permute. xczy +warp[$both] .,0,0,1 rm.. *. {($5-1)/255} s. x,2 -. .. *. -1 a[-2,-1] c N+=1 while {*} if $N a[-$N--1] x permute. xczy => constraints fi rm[$visu,$both] w 0 fi # Estimate warping field. if $constraints +pointcloud. 0 r. ...,...,...,3,0 +compose_channels. + a[-2,-1] c displacement[fsource] [ftarget],0.001,5,0,10000,1,. rm[ftarget,constraints,-1] else displacement[fsource] [ftarget],0.005 rm[ftarget] fi =>[fsource] displacement # Generate video of matching. # if videos # N=$! # [displacement],[displacement],[displacement],1,x +f. y +f. z a[-3--1] c *. {255/(w-1)} +. 1 # +_match_rgb[source] 0,$sigma,{w} *.. . distance. 1 *. -1 watershed.. . rm. # +_match_rgb[source] 0,$sigma,{w} a[-2,-1] c # [displacement],[displacement],[displacement],1,x +f. y +f. z a[-3--1] c *. {255/(w-1)} +. 1 # +_match_rgb[target] 0,$sigma,{w} *.. . distance. 1 *. -1 watershed.. . rm. # +_match_rgb[target] 0,$sigma,{w} a[-2,-1] c # warp. [displacement],1,1,1,48 # l[$N--1] { # f3d 1200 # ap "s. c,-3 view_func. 6,.. rm.." # ap "makefig" # append[^0] [0],x rm[0] # rs 1024 # [-1]x20 # o videos/video_${nm_source}_to_${nm_target}.mp4,10 # rm # } # fi # Estimate confidence of the color mapping. +_match_rgb[target] 0,$sigma,{displacement,w} warp. [displacement],1,1,1 c. 0,100% => fconfidence +map_clut[source] . => confidence # Generate transfer CLUT. [displacement],[displacement],[displacement],1,x +f. y +f. z a[-3--1] c *. {255/(w-1)} +. 1 +_match_rgb[target] 0,0,{w} *.. . warp[-2,-1] [displacement],1,0,1 distance. 1 *. -1 watershed.. . rm. -. 1 => clut b[clut] $sigma% # Enforce luminosity constraint for non-confident colors. if $4>0 ^[fconfidence] {$4/10} *[fconfidence] -1 +[fconfidence] 1 +f[fconfidence] x +f. y +f. z a[-3--1] c *. {255/(w-1)} rgb2hsv[clut,-1] channels. 100% j[clut] .,0,0,0,2,1,[fconfidence] rm. hsv2rgb[clut] fi # Map CLUT to get result. +map_clut[source] [clut] => res_noregul # Apply guided smoothing to remove quantization artifacts. if !$3 =>[res_noregul] res else l[source,res_noregul] { rgb2ycbcr # Perform regularization in YCbCr to avoid creation of false colors. +-[1] [0] repeat $3 { guided. [0],5,5 } +. [0] c. 0,255 ycbcr2rgb } => res j[res] [res_noregul],0,0,0,0,1,[confidence] rm[res_noregul] fi k[res] } } # _match_rgb : _gamma>=0,_smoothness>=0,_resolution>0 # Convert selected images into 3D volumetric scalar function for color matching. _match_rgb : l[] { check "${1=0}>=0 && ${2=1.5}>=0 && ${3=128}>0" gamma=$1 smoothness=$2 res=$3 onfail noarg gamma=0 smoothness=1.5 res=128 } e[^-1] "Convert image$? as 3D volumetric scalar functions for color matching, with gamma "$gamma", smoothness "$smoothness" and resolution "$res"." to_rgb foreach { b 0.3% r {w*h},3,1,1,-1 * {($res-1)/255} pointcloud 1,$res,$res,$res f i?i^$gamma:0 b $smoothness% n 0,1 } #@cli mix_rgb : a11,a12,a13,a21,a22,a23,a31,a32,a33 #@cli : Apply 3x3 specified matrix to RGB colors of selected images. #@cli : Default values: 'a11=1', 'a12=a13=a21=0', 'a22=1', 'a23=a31=a32=0' and 'a33=1'. #@cli : $ image.jpg +mix_rgb 0,1,0,1,0,0,0,0,1 #@cli : $$ mix_rgb : skip ${1=1},${2=0},${3=0},${4=0},${5=1},${6=0},${7=0},${8=0},${9=1} e[^-1] "Apply matrix [ $1 $2 $3 ; $4 $5 $6 ; $7 $8 $9 ] to RGB colors of image$?." to_color foreach { sh 0,2 mix_channels. (${1-3};${4-6};${7-9}) rm. } #@cli oklab2rgb #@cli : Convert color representation of selected images from OKlab to RGB. #@cli : (see colorspace definition at: ). #@cli : See also: ''rgb2oklab''. oklab2rgb : e[^-1] "Convert color representation of image$? from Oklab to RGB." foreach { split_opacity to_rgb[0] f[0] " l = (i0 + 0.3963377774*i1 + 0.2158037573*i2)^3; m = (i0 - 0.1055613458*i1 - 0.0638541728*i2)^3; s = (i0 - 0.0894841775*i1 - 1.2914855480*i2)^3; [ 4.0767245293*l - 3.3072168827*m + 0.2307590544*s, -1.2681437731*l + 2.6093323231*m - 0.3411344290*s, -0.0041119885*l - 0.7034763098*m + 1.7068625689*s ]" * 255 a c } #@cli palette : palette_name | palette_number #@cli : Input specified color palette at the end of the image list. #@cli : 'palette_name' can be { default | hsv | lines | hot | cool | jet | flag | cube | rainbow | \ # parula | spring | summer | autumn | winter | bone | copper | pink | vga | \ # algae | amp | balance | curl | deep | delta | dense | diff | gray | haline | ice | \ # matter | oxy | phase | rain | solar | speed | tarn | tempo | thermal | topo | turbid | aurora | hocuspocus | srb2 | \ # uzebox | amiga7800 | amiga7800mess | fornaxvoid1 } #@cli : $ palette hsv +palette : names=${-_palette_names} N:=narg($names) l[] { if isint("$1") name=${"arg0 ($1%"$N"),"$names} else name="$1" fi onfail name="$1" } e[^-1] "Input color palette '"$name"'." _palette_$name => $name _palette_names : u default,hsv,lines,hot,cool,jet,flag,cube,rainbow,\ parula,spring,summer,autumn,winter,bone,copper,pink,vga,\ algae,amp,balance,curl,deep,delta,dense,diff,gray,haline,ice,\ matter,oxy,phase,rain,solar,speed,tarn,tempo,thermal,topo,turbid,aurora,hocuspocus,srb2,uzebox,\ amiga7800,amiga7800mess,fornaxvoid1 _palette2code : # Convert a set of input palettes sort_list +,n foreach { r {whd},1,1,100%,-1 img2base64 0,0 e "_palette_"{n}" : " b64=\"${}\" l[] { ('$b64') s x,-119 ('\\\\\n') a[0--3] .,x rm. a x b64={t} rm } # No more than 120 char / lines e " base642img \\\n"$b64"\n" } # The color palettes below comes from the CImg library 'http://cimg.eu/'. _palette_default : 256,1,1,3,[16+32*int(x>>5),16+32*int((x>>2)&7),32+64*int(x&3)] _palette_hsv : 256,1,1,3,[x*359/(w-1),1,1] hsv2rgb. round. _palette_lines : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2MTcKeJwNkhGgu1AYxT8YDP4QDB4Eg+BKEATBYBAEQXDhQhAMHgweBBeuXB\ gMgmAwCIIgCIJBEAQPguBKEASDwWDwYDD4/tGx75zf+Q4gAuhCPFVzM0CgvUXFmr0plIpkB2KwW4bppr16aMgRzge16wbwTbxW0L6Y0IxsjiFyZyQvAISoD\ PFMYgWlnTUI/M4bsllO+I5M0GuoWm+LP6kImrtktjhu8sxjaJ9V6zwNDy0FijoRcloRzGvghbDTAiO+Z8/Hv9JoKRbRr79JjwXqpOfBGxRasEejCH2kHyhr\ uCHk2jXJ5xkgGFdjPgp/4IZ4HDTk4LTjXrAED56dzkIskRGG8Gwh9FdqTh8bkMgp0+JG9tb+xXpLf58ugHG/GoPG6ZmnSGh+Bn1OyCkX23dUAjon753DhyT\ JuhiCaxcy8acf79DuuwbQNo/NItIqXXWF491E8R0ECNOppKlesjlaK8wsIqpKwL3QJwnhCygGtbCWbgbvJ0W4GFMOSrgnpIQ9rZHDYMyVS261dE1U1B/hkU\ InqiU/44MDQ/m4BMtHV9Wq5bgqjtJ6laFWKUDtOwsRFvaMLvSAZbHj+u9o/u0itAN5xHX85KSu4GblHN0BJgEHfPGtksZjK4JkzFY1AvkIMTEJBX7tO4HCF\ 6Hov85VvUXaEWosOIqirYUDkWW/OH7FGP18B9h0jjs49tyukEYSKYiZTjCmPnkxI7neAWkOOTxrs4A3blzFEhvJsgMXqmsicxgRGMFil7o5wS3HuIZmAGcc\ g8wvlM8UfPWXpfJN5JTPj+YlnxB6QD6tvGmdD9oUxoBrWc3mf8h5fjM=" _palette_flag : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICMyNAp4nPv/n4Hh/wjGDCMeD4JIGEAMAH3RPtA=" _palette_cube : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjMxIDEgMSAzICMzMzgKeJxj5BCQkFfXN7Nz8wuLS80trWnpmTJn8erNuw6dunTr0cuPP39/e/f83v\ Xzxw/s2LBy4cyJnY1VRVnJ0cHezjYmOqqyoryszFxCUoqahhaOHgER8en55XVtfdPmLV27dc+RM1fuPH79+ffvL2+e3Ll69ui+beuWz5ve395QUZCRGBHo6\ WhlpKUsLczNwsIjIqOsbWzt7BUUlZhZWNnYMWHGguXrt+87fu7avadvv/759enVo1uXTx/es2XNkjlTe1vryvLS4sL83e0tDDQUJQQ5Wdj4xGRVdU1tXX1C\ YpKzi6ubuybNWrhy486DJy/cePD8/fd/DIQAE6eghKKGgYW9u39YXFpeWV1r79Q5S9Zs2XP49OVbj159+vWf9oCQN5kI+oIOgIVbWFpZy8jK0TMwIjGjoKK\ hvX/6vOXrtu07evbqnSdvvvymQ0ABAC/raR8=" r. 256,1,1,3,3 round. _palette_rainbow : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjI1IDEgMSAzICM1NjcKeJxj5ublE+DnF5KQUVBWU9fS0tHV0dXVAVL6RiZm5haWFpaWVpZWVtbW1i\ BsbWMDJq3BwMrK0sLMWF9LRYqLkYGRAQygFAYbG2DkVrH2iUjNq2zpnTp3+eadB09fvvXo+dvPP/7+//v375/fv37++PH9+7dvX799/fr1GwQAWV+/ACEUA\ Bmf3796+uDW1XMnD+3dum75wpkTOhvK81Ii/Z3NDbSUxNhZuPkEhISFhMWl5RRV1LV09AyMjE1MLWwcnJxd3T29vH18/fz9AwIDg4JDQCA0DAjCI0BkRGRk\ dExsQnJaZm5ReW1T54Tp85au3rBp8+ZNmzZt3Lhxw4YN69euXbd2zWpMABTfsHHTpq3bd+7ae+Dg4eMnTp+9cPnC2RNHDuzatnH10gWzJvW01JUXZCbHhvt\ 5OttbW5gYGepDgJ6enq6OjpaWpoa6mqqKorysnJy8goKCopKSopKigrycrLSkuIggHy+/kJCQAB8PFzOHiIyKnqm9R0BYbHJmXmlVY2tn38SpM2bPmTd/wc\ JFi5csWbZ82dJly5avWLFiJRCsAoPVYGL1GqBDN27eugPoyqMnz1+/++gxMCRPH9mzdd3KhbOn9rU3VBZmxEcEByGD4BAwPzgoMDAgwM/P19vT093d1dnJ3\ trK3NhIX0dDTVUJ5GCgi5VVVIFpSkdXT9/A0AAGQH7U1dXW1gJ5UA2E1NTUNdQ1NDTU1dXUVJQU5WQkxYT4efkFhIQE+Xm5mAHNkQH/" r. 256,1,1,3,3 round. # The color palettes below have been converted from # 'https://stackoverflow.com/questions/33273340/matlab-set-color-map-color-range'. _palette_parula : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjQgMSAxIDMgIzIwMwp4nAHAAD//NTY2NDAoHAwDAwUJDhETFBQSEAwJBwYGBggLEBYdJS44Qk1ZZX\ F7hpGbpK22v8fP2N/n7/b8//79+/j29fX2+C0zOT9FTVZeZGlucnZ6foKGio+UmZ2hpKeprK6xs7W3ubu8vb6/v7+/v76+vby8u7q5ubm7vsLHzNLX3OLp8\ PiMmKWyvsvX3uHh4N7c2tjW1NPS0tHPzcrGwr65tK+ppJ6Yko2HgXx4dHBsaGVhXlpXU09LRj85NDAsKCMfGhUQJK9mpg==" r. 256,1,1,3,3 round. _palette_jet : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNzYgMSAxIDMgIzEzMQp4nGNgwA24JFSN7H2i0ksa+2av3Hb4wr3XP/9jgt/vH145tnPN/ImtMH1MvN\ Iapk7+sVnlLRPnrdl57PLDd7+xaPz55t7Fw9tWzulrKkmP8rU3UpXgYoTbPWXh+j0nrz35+BeLxm8vb587sHnZzO76wpRwLxt9JVF2PL5gYAAAc+1twA==" r. 256,1,1,3,3 round. _palette_hot : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTI0IDEgMSAzICMxNDEKeJzjFRKXVdLQNTK3dXb3DY6IS87IK66oa+nsmzJz3pKV67fu2n/k1IWrt+\ 4/ff3+66//1AUMpABGdl5BcRlFdZBTndx9g0BOzS2urGvuADl18cr1W3buP3LyPMipr4BO/UdNu0kEjFxC0kpaRtbO3kHRydnFNS3dk2cvXr1lz5EzV+88e\ fP1LwA796yq" r. 256,1,1,3,3 round. _palette_cool : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMzIgMSAxIDMgIzc4CnicY+ERkVHRMbFx8QmJSckpqWnpmTJnyZote46cuXLnyZsvf/58efPkzpUzR/\ ZsWbNkzpSelpqSnJSYEB8XGxMdFRkRHpb/BAAAHuY/4Q==" r. 256,1,1,3,3 round. _palette_spring : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjggMSAxIDMgIzcxCnic+/8fN2DhFVPQNLJ28Q1LyCyqbumdtmDlpt1Hzl578PLT79+fXj64dvbI7k\ 0rF0zrbakuykwI83WxNtJUEONlAQDKiDfN" r. 256,1,1,3,3 round. _palette_summer : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjggMSAxIDMgIzcxCnicY+EVU9A0snbxDUvILKpu6Z22YOWm3UfOXnvw8tPvprbuCVNmzl20bPX6LT\ v2Hjx26vyVm3cfPXv94cvPv2l4AACR+i4M" r. 256,1,1,3,3 round. _palette_autumn : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjYgMSAxIDMgIzQyCnic+/8fF2Dhk1DSMXPwDI5NL6xu6ZuxaM22Aycv333+8TcDTgAAaHcm2w==" r. 256,1,1,3,3 round. _palette_winter : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMzEgMSAxIDMgIzc5CnicY2DAC1h4RGRUdU1t3fzC4tLzyxs6JsxYuHLjzkOnLt16/Prz778/v358+/\ Lpwzs3rlw4ffzw/t3bN69bvXzxvFnTJvV1tTUBACEGJp4=" r. 256,1,1,3,3 round. _palette_bone : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KOTkgMSAxIDMgIzI1Mgp4nGNkZuXg4uETFBaVkJKRU1BWVdfU0TMwNjG3tLZzcHJx9/T28w8KCY+Mjk\ tISk3PzM4rKCwpq6iqbWhqaevs7p0wccq0GbPnzl+4ZNnKtRu3bN+9/9Cxk2cuXLl+696jpy/ffPj8/dc/RmKsCI2IiU9KzcjJLyqtqK5rbGnv6ps4Zfqsu\ QsWAw3esHnbzr37Dx09cfrM+YtXrt24fef+w8fPnr968+7j56/ff/35x8TKwcMvJCopLa+koq6lZ2hibmXnCDTZNyA4PDI2ITktE2hwSVlldV1DU2t7Z3cf\ 2N2zwO5esXrtho1btu3YvXf/wSPHcFoBAJTokrQ=" r. 256,1,1,3,3 round. _palette_copper : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTE1IDEgMSAzICMzMzkKeJxj4+TmExQWk5SWU1RW09TRNzKxsLK1d3b18PINCA6NiI5LSE7LyM4rKC\ 6rqK5rbGnv7O2fNGX6rLnzFy1dvmrthk1bd+zee+DQ0ROnz164fO3mnXsPnzx7+eb9xy/ff/359x87YGFl5+Dm5RMQFBYVk5CSlpVXVFZRU9fU1tEzMDIxN\ be0trG1d3R2dfP08vH1DwwKCY2Iio6NT0hKTk3PyMrJKygsLi2rqKyurW9sam1r7+zu7eufOGXa9Jmz5sybv3DxkmUrVq1Zu37Dpi1bt+/avXffgUOHjx5j\ YgZZysnFzcPLxy8oJCwiIiYuISklLSMrp6CopKyiqqauoakFdIG+gaGRsYmpmbmFpZWNrZ29g6OTs4ubu4ent4+vn39AYFBwaFh4RGRUdExsXHxCYnJKalp\ 6RmZWdm5uXn5BUXFJaVl5RWVVTW1dPQCk5ZAs" r. 256,1,1,3,3 round. _palette_pink : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTIxIDEgMSAzICMzMDUKeJxT1DG1cfbwC46ISUzNzC0sraiub2rr7O6bOGX6zDnzFy5ZtnL1ug2bt2\ 7fuXvvgUOHDx85euz4iZOnTp85e+7c+QsXL12+cuXqtes3bt66ffvO3Xv37j94+PDR4ydPnj57/uLFy1evX799++79hw8fP33+8uXr12/ff/z49ev37z9//\ /37zyoso6SurW9sZmVr7+Tq7uXjHxQSFh4ZHRuflJyanpGVnZtfWFRSWl5RWV1b39Ta0d0PddLiZSvXIJx05NiJU2fOXbh89fqNW3fu3X/0BGgzFS1uaGxq\ aW1r7+zu6e2bMHHS5CnTps+YOWv2nHnzFy5avGTpsuUrVq5avWbtuvUbNm4COmn3vgOHj504fRbonhu3795/+OTZi1dv33/68u3Hrz//AH2168E=" r. 256,1,1,3,3 round. _palette_vga : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICMxNzgKeJz7/+fn10/vXz9/8uDOjasXzpw4sv/IiTMXrt648+DJ89fvP339+ec/Bv\ jx8dXj25dOHdi2ZsGU1tIEVwbCwDWhtHXKgjXbDpy6dPvxq48/MM2Q0zF3cPcLiYpPycgtLK2saSAMaipLC3MzUuKjQvzcHcx15IhwB4Yt/zH8v2vr+pWL5\ 07r72goz00KdTPEdDthQEYIYbgMw3c0CSEi/E9G7JIRQoT9T0YYEgYAJsphWQ==" # The color palettes below have been converted from 'https://matplotlib.org/cmocean/'. _palette_algae : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNTAgMSAxIDMgIzE2MQp4nAGWAGn/1M3Hwbq0raegmZKLhH11bWVcU0k+MygeFg8JBwYICg0PEhQVFx\ gZGRkZGRkYFxYVExL38u7p5ODc19PPy8fEwLy4tbGtqqainpqWko2JhH96dnFtaGRfW1ZRTUlEQDw3My4qJczFvbavqKGak42GgHp0bmhjX1pXVFJRUFBPT\ k1MS0lIRkRCPz07ODUyLywpJiMgHBkV1p88Lw==" r. 256,1,1,3,3 round. _palette_amp : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjEgMSAxIDMgIzE5NAp4nAG3AEj/8O3r6ejm5OLh397c29nY1tXU0tHPzszLycjGxMPBv728uri1s7\ CtqqainpqVkIuFgHp0b2lkXlhTTkhDPurm4dzX0s3Iw765tLCrpqGcmJOOiYSAe3ZxbGdiXVhTTklDPTgyLSgjHhkWEhAODg0ODg4ODg4NDQwLCgnq5N7Y0\ szGwLq0rqihnJWPiYN9eHJsZmBbVU9KRUA7NjIuKygmJCQkJCUmJicoKSkpKCcmJSMhHxwaGBUS64BWpQ==" r. 256,1,1,3,3 round. _palette_balance : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTIxIDEgMSAzICMzNzQKeJwBawGU/hgaHB4gIiMlJigoKSkoJyQgGhMNCgoMERYcIictMzg+Q0lOVV\ thaG51fIKJkJado6mwtrzDyc7V2+Dm7PDu7Oro5+Xj4t/e3dva2NfW1NLR0M7Ny8rIxsXDwcC+vLq4tbOwraqno56alZCLhYB6dG9pY15YU01IQj0dICImK\ CsuMDM2OTs/QUVJTFFVW19kaGxxdHh8gISHi46SlZmcoKOmqqyvs7a5vL/CxcnMz9PW2t3h5ens6OPe2dTPysXAu7axraijnpmVkIuGgXx3cm1pY15ZVE9K\ RD45My4oIx8aFhMQDw4NDg4ODg4ODQ0MCwoJREpRWF5lbHN7goqSmqGpsLa7vb69vby8u7u6urm5ubm5ubq6uru8vL2+v8HDxcbIys3P0dXX2t3f4ubp6+f\ h2tXPyMK9trCqpJ6YkYuGf3l0bWhiXFZQS0ZBPDczLisoJiQkJCQlJSYnKCkpKSgnJiQiIR4cGhcVEqaTs4c=" r. 256,1,1,3,3 round. _palette_curl : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTI0IDEgMSAzICMzODEKeJwTFROXkJCUkpKWlpaRARJSkuJiosJCggKCwmLSCup6Znbu/uHx6fnldW\ 29U2YtXLF+6+6DJ85duXn/6euP3/78+fn96+eP79++efXy+bOnjx89uH/3zu2b169duXT+7KkTRw/u271984Y1K5YsmDN9Ul9nS31VaUF2anxUqL+Xi72Vq\ ZySmpaekZmVraOrp29gSERMQnJGTkFxeXV9U3tX38SpM2bPW7h0xaq1GzZv27F738Ejx0+dvXDl+q27D5+8ePPhy+d3r57cv3X1wumjB/ds37hm+UKgHb0d\ TbUVxXmZKQnR4UF+Xq6OtlZmxvraGqpKCrJgn4kIg/wmwMfr4ubh7esfFBIWGR2bkJSSnpWTV1BUXFpeUVVdU1tXV9/Q2NTc2tbR1Tth8rRZcxcuXbVu846\ 9h0+cvQz2+cfXT+5eO3/i4O4ta5cvnD21v6u1obq8uCA3KyMtJTkpMSEhIT4+PgEG4uPjYqMjw0OD/L09nO2tzACwu7nF" r. 256,1,1,3,3 round. _palette_deep : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNDkgMSAxIDMgIzE1OAp4nAGTAGz/+e7j2M7Dt6yhlYqAdm5mYVxYVVJPTUtKSEZEQ0FAPz49PT0+P0\ BBQT8+Ozk2MzAsKfv38/Ds6OXh3dnV0czIw764s66po56Yk46Ig396dG9qZF5ZU05IQz46NjIvKycjHxvJw724s6+rqKWko6Kjo6Ojo6OjoqKhoJ+dnJuam\ ZeWlZSTkY+Mh4B4b2ZeVU1FPTYvM2JJnA==" r. 256,1,1,3,3 round. _palette_delta : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM3NzAKeJwTEBIWEROXkJSWkZWTV1BUUlZRVVUDAlVlJUV5OVlZGWlpKRCQlp\ aWASmBqFFT19DU0tHVNzA0NjWzsLKxc3Bycff08Q8Ki4xNSMnIKSipqGlo6ejpnzx91rxFS1euWb952869Bw4fP3X2wuVrN+8+ePzs5Zv3n7/9/Pvv7+9fP\ 79/+/r508f37968fvXi2dPHjx7cu3PrxrUrl86fPX3i6OEDe3dt37Jx3arlSxbMnTVtcn9PR2tjXVV5cUFORmpibFRYkL+3u4uDraWZkb62hqqSvIykuIiQ\ AB8vDzcXFzc3Dw8vH7+AINCbIqJiYuLiEhISkmAAZIiLi8srKCmrqqlramnr6ukbGBmbmJpbWFqDPeTm4e3rHxgcFhEVE5eQlJKWkZWTV1BUUlZRWV1b39j\ c2t7Z3ds3YdKUaTNmzZk7f+HipctXrFqzdv3GTVu2bt+xc/eeffsPHjpy9NiJk6fPnLtw8fKVa9dv3r5z78Gjx0+fv3z99t2HT1++/fj15+f3L58+vH398t\ mTR/fv3r55/eqlC+fOnDp5/Ojhgwf27dm9c8e2rZs3bVy/bu2aVStXLF+2ZPGihQvmzZ0ze+aM6dOmTp40sb+vt7urs72tpbmxoa62uqqirLS4sCAvJyszP\ TU5MT4uJioiLCQowM/H29Pd1dnR3tba0tzUxMhAT0dLQ01F2d7JFRJvMQnJ6dl5RWVVtY2tnb0TgL6aOWv27Dlz5gLBvHnz5s+fv2DBgoULFy5atHjxkiVL\ ly5dtmz58hUrVq5ctWr16jVr1q5dt27d+vUbNmzYuHHT5s1btm4DhsEuUCAcOHj4yNHjJ04BQ+H8xUtXrl6/cQsYDPcfPHz0+MnTp6ePH96/e9umdSuXLpw\ zY0p/d3tTXVVpYW5GSkJ0eDAwYp3trS1MDHS11JUVZKUlRIUF+Hi4ONjZWIGAjZ2Dk5uHj18QlIIlpYCpE5J+gYkTmDq1tLV14EBbW1tLS1NDQ11NVQWYsB\ Xk5WSkpSTERUWEAGwPYP8=" _palette_dense : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNzQgMSAxIDMgIzIyNAp4nHt878alM8cO7N66YfXSBbOnT+rram9uqKmqKCstKQaBktLSsvLyioqKys\ pKIFleDpIoLMjLyc5MS0mKj4kMC/L38XBxsDF//+bV8ycP792+ce3ShTMnjx3ev3fXts3r16xYsmDOjCkTejpaGmoqivOz05Pjo8NDAnw8XB3trMyNDfW0N\ FSVFGSlJcXFRISFhQQF+Pnev3398sWzp0+ePH4EA48fP34CBo8fP3p4/+7tm9euXDh78tihfbu2bly7YvG8mVP6u1rqK0vyMpJjwwN93BxtzI30NFUBgy55\ hg==" r. 256,1,1,3,3 round. _palette_diff : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTQzIDEgMSAzICM0NDAKeJwBrQFS/gcJCw0ODxESFBYWGBseIiYqLjI2Oj5CRUlNUVRYXF9jZmtvcn\ Z5fYCEiYyQk5ebn6Onq66yt7q/wsfLz9PX3N/k5+vu8fP19fX08e/s6ebi39zZ1dLOy8jEwr67uLWyr6yppqOgnZqYlZKPjIqHhIF+fHl2c3Bua2hkYF1ZV\ lJPS0hFQT47NzQwLSomIyAdIyYpLC8zNTk7P0JFSEtNUFNVWFteYGNmaGtucHN2eXx+gYSHioyPkpWYm56hpKeqrbC0trq+wMTHy8/R1djc3+Pm6ezu8PHx\ 8O/s6ufj4NzZ1dLOy8fDwLy5trKvq6mlop+bmJWSj4yJhoOAfXp3dHFubGhmY2BeW1hWU1FOTEpHRUI/PTs4NTIwLSooJSJAQ0dKTlFUWFteYWRmaGpsbnB\ yc3V4enx9gIKEhoiKjY+Rk5aYmpyfoaSnqauusLO2uLu+wcTGyszP0tXY29/i5efq7O7v8PDv7Onm4t3Z1dDLxsK9ubSvq6einZqVkY2IhIB8d3Nva2djX1\ tXU09LR0RAOzg0MS0qJyQiISAeHRsbGRgWFRQSEA4ODAoIBjEs26k=" r. 256,1,1,3,3 round. _palette_gray : 256,1,1,1,x r. 100%,1,1,3 _palette_haline : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNDYgMSAxIDMgIzE0OQp4nAGKAHX/KiwuLSgiGxQPDAwPEhcbICUpLTA0Nzo8QEJGSU5TWF9mb3mEkJ\ 6sucXR2+bw+RgaHB8mMDlBSE9VW19laW5zeHyBhouQlZqfpKmus7i9wsbLztLV2Nrd4OPm6e1xfoyZoaKfnJiVkpCOjIuKiYmIiIiHh4aFhIOBfnt4dHBrZ\ 2JeW1xgZW12gIqU7TdADQ==" r. 256,1,1,3,3 round. _palette_ice : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA5IDEgMSAzICMzMzgKeJwBRwG4/gQGBwoMDhETFRgaHB4gIiQmKCorLS8wMjQ1Nzg5Ojs8PD0+Pj\ 4+Pj4+Pj4+Pj4+Pj4+Pz9AQUJDREVGSEpLTE9QUlRWWFpcXmFjZWhqbXBzdnl8f4OGio6Slpqfo6essLS5vsLGy8/T2Nzg5OkGCAkMDhASFBYYGRwdHyEjJ\ CYoKSwtLzEzNDc4Ojw+QEJER0lLTlBSVVhaXWBiZWdqbW9ydXd6fX+ChIaJjI6Rk5WYmp2goqSnqayvsbS2uLu9v8LEx8rLzdDS1NfZ293f4eTm6evt8PL0\ 9/n7ExYaHSAkJyouMTU5PEBDR0pOUlZZXWFlaW1xdHh8gISIjI+Tlpmcn6KkpqiqrK6vsLKztLW2t7i5urq7vL2+v8DAwcLDxMXGx8jJysvMzc3P0NDS09P\ V1tfY2dvc3t/h4+Xn6evt7/L09fj6/ETzn2w=" r. 256,1,1,3,3 round. _palette_matter : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjQgMSAxIDMgIzIwMwp4nAHAAD///fz8+/v6+vn4+Pf29fTz8vHw7+3s6ujn5eLg3drX1NDNycXAvL\ ezrqmkn5qVkIqFgHt1cGplYFpVUEpFQDs2Mevl4NrUz8nEvrmzrqijnZiSjYiCfXdybGdiXVhTTkpGQj46NzMwLSsoJiQiIB8dHBsaGhkYGBcXFhYVFBMSE\ Q+uqKOemJOPioWBfHh0cGxpZWJfXFpYVlRTUlJSU1NUVVdYWVtcXV5fYGFiYmNjY2NiYWBfXVxaV1VST0xJRkM/PV5d6A==" r. 256,1,1,3,3 round. _palette_oxy : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2NTYKeJyzd3RydnP39PL18w8MDg2LiIqJjUtMSknNyMzOyS8oKi4rr6iqqa\ tvaG5pbevo7Oru6e0LDAgMDA4JCQ0Pj4iMigaqj09ISEpOTklLS8/IzMrOyc3LLygsKi4uLSsvr6yqrq6tq2tobGoCGgAzoX/CxEmTp0ydNn3mzFmz586bP\ 3/hosVLli5bvmLV6jVr163fsHHzlq3btu/YuXvP3n0HDh46dOToseMnT50+c+78hYuXr1y9duPmrdt3791/8Ojxk6fPX7x89ebt+w+fPn/5/u3L54/v3719\ /frVq1cvIeAFEDx//vzZs2dPnz558uTx40ePHj18+ODB/fv37t29e+cOKxCwQQE7DED5rGDAwgJSws7Bxc3HLygkKibh6x8QGBQcEhoWHhERFR0dEwcMgMS\ k5JTUtPT0zKys7Ny8vPxCYACUAgOgAhgANbV1DcAAaAGFIDAAenp7+4D+nwTy/wyg/+cAAwDo/8Vg/wMDAOT/TZu3QP2/dz8wAAj7/937T5+//vv75/fPH6\ BQ+PTh3ZvXL58/ffLowb27d25ev3b10sXz586cOnni6JFDB/fv3bN7x/ZtWzdt3LAO4n92dg4OTk4uLi5ubh4eHl4g4AMCfhAAMYB8Hm5uLk5OTg4OdnZvs\ P+DQ8LC4AkgPjEpKTk1FSUBFJWUlpVVVFZW1dTW1jc0NgJTUGt7R2cnSgIABsCcuXPnLYAmgJXAAAAlgE2bNoMSwK7de/bsAwbAYWAAnAAGwNlz585fvHT5\ yrXrN27evnP3HigAngED4DUwAD58fJSRlpIYExUe4uft4epgZ2NlYWZqYmRooK+ro6OtqamhrqaqqqKspKSoIC8vJysrIyMlJSkJADQjlG8=" _palette_phase : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA2IDEgMSAzICMzMjkKeJwBPgHB/qissLO2uby/wsTHyczO0dPU1tjZ29zd3d7e3t7d3NvZ19XSz8\ zJxcG9ubWxrKeinpiTjYeBe3VuaGFbVE1HQTs1MCsnIyEeHBkXFRMRDw0MCwsOEhgeJS01PkdQWWJqcXh/hIqPlJidoqZ2dHJwbmtoZWNhXVtYVVJPTElFQ\ j47NzQwLSsoJiUlJicpLC8zNjo/Q0dLT1NXW19iZmltcHN2eXx+gYOGiIqLjY6PkJGSk5OUlZWWlpeXmJiZmZmampmZmZiXlpSSkI6MioiGhIF/fXp4DhIX\ Gx4iJiouMjY6PkNHTFFXXGJobnV8g4qSmaGor7e+xMvQ1tvf4+fq7O/w8vPz9PPz8vHv7ero5eHd2dTPysbBu7axrKejnpqVkIyHgnx3cWtlXlhRSUI5Mio\ jHRcTEA8ODQ0NDQ0NDPDwmdk=" r. 256,1,1,3,3 round. _palette_rain : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTE1IDEgMSAzICMzNDIKeJx7++bVy2dPHz96eP/e3Tu3b928cf3q5Yvnz5w8dmjf7u2b169evmTBnB\ lTJvZ2tjbWVpYU5makxEeHBfq4O9taGOtpqirIiIsI8nJxsLGyMDMzs7CwcXBy8woIiYhJSEnLyikoKCopq6ioqIIAkFZRVlZSUnzz6vnTRw/u3bl57cqlC\ 2dPnzh25NCBfXt37dyxfduWzZs2rl+3ds2qlSuWL12yeOGC+fPmzJ41Y/q0qZMnTZzQ19Pd1dHW0txYX1tdWVFaXJifm52ZnpqcGB8bFREWEhTg5+Pl7urs\ aG9rbWluYmSgr6OloaaiKC8r/fHdq+ePH9y9efXimZNHDgA9tm7V0oVzZ06bPLG/p6sTZGZTQ31dbU1VZUV5aWlJcVFhQX5+Xl4uDORBAYidk5OTnZWVmZm\ enpaanJSYEBcTHRkRGgy23RNovZMD0H4rSwDxo6Ac" r. 256,1,1,3,3 round. _palette_solar : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMzUgMSAxIDMgIzExNgp4nAFpAJb/N0BJUltkbXZ/h46VnKGnrLG1ur7BxcjMztHU1tja3N3f3+AVGB\ odHyIlJysvNDpARk5VXGRrc3uDi5OcpK22v8jR2+Xu+RkcHyEjJCQkIiAeHBoYFhQTExITFBUXGh0gJCgsMDU6PkNIm9UqlA==" r. 256,1,1,3,3 round. _palette_speed : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNDkgMSAxIDMgIzE1OAp4nAGTAGz//fn28+/r5+Pf2tXOyMG5samgl46FfHJpYFZNQzoxKCAZEg4LCg\ wOEBMVFxgZGRkYF/rz7efh29bQy8bCvbm1sq6rqKWinpuYlZKOi4eDf3t3c25pZWBbVlFMSEI9ODMvKSTIvbKnnJCFeW5jWE1COS8nHxgRDAcFBQgLDxQYG\ x8iJScpKywsLCwrKiknJCIfGxgUF7w7kA==" r. 256,1,1,3,3 round. _palette_tarn : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA1IDEgMSAzICMzMjYKeJwBOwHE/hcaHiEjJyktMDU8Q0lQV11kanF4f4aNk5uiqbG4v8XKzc/S1N\ bY2t3f4uPm6evu8PT2+vv8+/j08Ovn4tzWzsa+t6+ooJmSioR9dm5nX1dPRz84MSolIh8eHRwaGRcUExAOCwoICQsODyQoLTE1OT5CRklMT1JVWFpdYGNlZ\ 2psb3F0dnl7fYCEiY6Ump+lq7C2vMHHzdLY3uTq7/T39vPu6eTf2tXRzcrGwr66trOvq6ekoJyZlpKOi4eDf3t3cm5qZGFcV1NOSkVBPTgzLikkIA0ODw8Q\ Dw8ODQwNEBETFhgbHR8hIyUnKisuMTQ3O0FIUlpia3N8hI2Vnqavt8DJ0dri6/H18uzj29HJwLewq6ikop+dm5mXlJKPjYuJh4WEgoF/fnx7enh2c3JvbWt\ paGZkYmFfXVpVUfhNmgM=" r. 256,1,1,3,3 round. _palette_tempo : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTEyIDEgMSAzICMzNDcKeJwBUAGv/v369/Tx7uvn5OHe29fU0c3Kx8PAvLm1sq+rp6OgnJiVkI2JhY\ B9eHRwa2hjX1pVUUxIQz86NTEtKSUhHhsYFhMSEREQEBEREhMUFBUWFxcYGRkaGhsbGxscHBwcHBsbGxsbGhoaGRkZGBgXFxYWFRX08vDu7Oro5eTi4N7c2\ tjW1dPRz87MysnHxcPCwL+9u7q4t7W0srGvrqyqqaempKKhn52cmpiWlZKQj42KiYeEgoB+fHp4dXNxb21raWZkYmBeXFpXVVNRT01LSEZFQ0A+PDo4NjQx\ Ly4rKSclIiAe8+/s6eXi3tvY1dLOy8jFwr+9ure0sa+sqqelo6CenJqYlpSSkY+OjIuKiIeGhYSDg4KBgYGAgH9/fn5+fXx8e3t6eXh4d3Z1dHNycXBvbm1\ samloZmVkY2FgX11cW1pYV1ZUU1JQT05MS0pIR0ZFRA7PoM4=" r. 256,1,1,3,3 round. _palette_thermal : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTA3IDEgMSAzICMzMjUKeJxjYWFhZWVj5+Ti4RcUkZBRUNUyMLWyd/HwCQiJiI5PSsvMyS8qq6ypb2\ rr7J0wefqseQuXrlizYfP2XfsOHj155vzla7fuPnj07MXrt+8/fv7y7fuPn79+/YaAX79+/vzx/dvXL58/fXz/7s2rF8qq6hpa2jp6+gaGRkbGYACkTMzML\ aysbe0dnJxd3T28vH39/AODgkNCwyMio6JjY+MTEpOSU1LTMjKzcnLzC4pKyiqraxua2zp7JkyeNmveoqUr127cumPPgSMnzly4cuPOgycv3nz8+tPE0tbJ\ 3ScwPCYpI6+0urGjb8qM2fPmz58/b+6c2bNmzpg2dcrkSRMn9Pf39fb2dHd1drS3tbY0NzXU11RXlpcWFeRlZ6QmJcREhgUH+nl7uLk6Odrb2dqAgK2tHdS\ lQIcGBoeGRwIArFub1Q==" r. 256,1,1,3,3 round. _palette_topo : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2OTEKeJxT19TS1tXTNzQyNjE1s7C0sraxtbWzt3dwcAQBBwcHe3s7OztbCA\ Cy7MFyTk7Ozi6urm7u7h6enl7e3j6+fn7+AYFBwSGhYRGR0TFxCUkp6Zk5+UVllbWNrZ29E6fOmrd4+eoNW3bsPXj05LlL127de/Ts9fvP33/z8vELCAoJC\ 4uIiolLSEpKScvIysopKCqpqGvpGhibWVrbOTq7eXr7BQaHRUbHJSSnZWTnFRSXVVTXNTS3dXT3TZg8bebseQuXLF+5Zv2mrTt27zt46PDRY8dPnDx1+szZ\ c+cvXLp85eq16zdu3rp95979Bw8fPX767PmLV6/fvHv/8dPnr99/SEmDrFNWUVMHB4SBobGJmbmllY2tvaOzq5uHt69/YHBoRBTYR2mZ2bn5hcWl5ZXVtfW\ NTS1tHV09fRMmTZk2Y9ac+QsXL12+cvXa9Rs3b92+c/fe/QcPHz1+8tSZcxcuXr567cZNkPVg21+8fP3m7fsPQMu//fj5+4+qmoaWDjD8jYGhD7HVxc3DC2\ htQCAwMMMjIqNiYuPiE5OSU1LTMzKzcnLz8guLikvKyisqq2tq6xoam5pb29o7urp7+vonTJw0ZSrIMfMWLFoCdcy2Hbv27AO5BeSUy1ev37pz78Hjp89fv\ Xn/8fO3H7/+6ugZmphb2tg7uXp4+wWFRkTHJ6UCI664rLKmvqm1vbO7F2zupMmTp0yZOnXatGnTp8+YMXPmzFmzZs+eM2fu3Lnz5s2fv2DBgoULFwHBYgwA\ FVyyZOmy5StWrlq9dt0GRAgdO3EKGPHAmAdHvZS0tLQMMPqBCQAM5MHxoqEJS59m5uagBGptYwNLiSjADgIcXDx8/IPDomITUzKywQmwoaW9u2/S1JlzFix\ evmrdxq079xw4fPzUuUtXb9558AQAVgJv5Q==" _palette_turbid : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMTI2IDEgMSAzICMzNzAKeJx78fzpk8ePHj64f+/undu3bt64fu3qlcuXLl28cP7c2TOnT506cfzY0S\ OHDx08sH/f3j27d+7Yvm3L5k0b1q9ds2rl8mVLFi2YN2fWzOlTJ0/s7+3p6mhraWqoq6mqKC0uzM/NzkxPTU6Ii4kMDw0O9Pf19nR3dXa0t7W2NDc1NtTX1\ dZUV1X+8unD29cvnz15dP/u7RvXrlw6f/b0yWNHDh3Yu3vn9q2bNqxbvXI5xI4Z06ZMmtAHsaKxvra6sqKsBGRJTlZmelpKcmJCXGxMVGREWGhIcGCAv5+P\ t5enh7uri5Ojg72djbWVpYW5qYmxkaG+nq6OlqaGuqqKsqKC/KrlSxbOmz1j6qT+3s72lsa6msryksL8nCyguxPjY6IiQkOCAvx8vb083N1cXZyBZgENs7O\ 1tbWxsbEGASsYsIQACyAwNzc3MzMzNTUxMQbaZ2hgoK8HtFNHW0sTaK2aqoqKshLQZjlZGWkAeF+paA==" r. 256,1,1,3,3 round. # The color palettes below comes from the LoSpec palette list 'https://lospec.com/palette-list'. _palette_aurora : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2ODYKeJxt0kFo01AYB/B3qYwwaDbCaEG6aS4T3ME4qJrgVpCiLUqolEgYbI\ QirIce9kQLknXbZSplWaGMSaeUYWU0g21QD0XbFLaasimMQqmUOiyBHiyOemgZZfpMql7Ew/v43vtOv+//QN/5yze89x89eZ58++HzMQJjQJnXizmktH4gh\ JSQN6SgbqO/bgHgfnfwejYwG88d1hqtdivEwSVZ1U60PQnSDiitv1Er31R5Cd5zWkmapTnok9ZrKXmRNdtI2ifJalbekDiONg/Stzj4UJIPNVmCHOOckWS5\ gbRaWS3suqnhcSFRKaEWaqNyJRWfvCuEN3fqCBViK9DPDA/1EhghuoHpbH//GTDC+CNesQc3DZgBBSzktGuZzwZdAoMDspedWlzLpMvlkzqqZDJh3uegvPE\ CQp3TemY1Av7xG9iuXz/zOaMjxkLGo/KnilvJeHQhMB18FkumCzVAO7xQkovqS8iN/PVralYSWRvL+fRRTv10cqylxGl9rG/qe1FNG2Cr4Q/o/opmrIOkxj\ kOblbVdFwS+eEBzGIhhjwML2QSsQDvplhhLrpTKn1FzWphIzDqnFgp11EznwpfWd07avxEjXr5aI2/aHf5PP7gi1fb+XSAdzBncfwcjlMOIZLYfQ89Qxdww\ kIAzGoFrD9+zYaPEv/zG0xWzx8ddP3GJhS9GPnrAeSeLkcXpyZnHojh2FYR0Yzhr8iQowdJmvPqMaqy5OWuU2Y7zeoRL0jrnf380qSTdHb9Ta2qqhIc0b+G\ lNzXvw4yrr/zr7W0YjYdj9hJWx/AcBdGCZcYMwZMBAZ4gPG4y+G0mkyEqafXhGGQJ9g71FzEv5H4iFCzdDThEddW2pnqrk7oNFG7VclEb24fFg1S5xShLyi\ ffewTnPaFoJJPh4NXb9t/Aejmgpc=" _palette_hocuspocus : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM2MzQKeJxjk3x5buu8lopLu5f0lsV5mHk7WxlpKkrINhWlhnla6iotmtpakR\ Ht7b5oUlNRUpCT+anN87qK4jzteqtz4/wdjVRfXNy1pK880UdUiJ+Xi52VyUJTXpSPg1ns57Vt08sjLGTeHp5bFW2vovrr3qFVfWVRLk9Ob53TmhMW8OT0p\ tlNOUF2DASAqIiwkKAAPx8vDzcXFycH+9PrZw5uW71g+uJpnQ1lWQkRMGW/Hp3aNKspK1wUAjR/3Tu2blpdWsDzFy9fv3n3/uOHG4fXz2orjGfklTN0DEwq\ bZ26bOfJm6/+MIg83t2fZq1amRUb6GiqIQu0go2NlUVgamtpWpibuUZ+Upifq6WBfn1xerSfk5n29LaytDB3c8OOyowoL0sdhRktxUlBrqYWx3fPbczwMZD\ ePK0ywdtcWevjkRnFweYy3Dc29GT7mcrIf9ndGW+rxM0Mda/AgcWdBVHuRqpk+n9qX3NFYWZ8eKCosCAfDxcHO5Dm5+XmZP9258jKSeUJEcBwnt9ZHOt5cP\ Oymf1NpSncvHwCgkIiouj+F5adluOmIchflwfyhqbslb3LJlQm+bismNJSkOBjo1eQHO4P9L/pjiVTmvPj/ewXT2goiPGxNN+zfEp9TpiLcU9lZoyPvZGqi\ CAo+pkZfz29sHvZxJriO1vA/uZ+cnhZZ7afoQjMP0yioiIiIsLCwkJCQoKCggLK3z69evbo3q1rl86dOnZo365tm9YuWzRv1rRJfV1tTXVVZc115cX52enJ\ 8dHhwf7e7s6/P715/vDOtQtH9+3avnn9mhVPz+9cPLE61R/mf3h6gMY/AyMzCxs7hxyu+AcA2aYUlw==" _palette_srb2 : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM0OTcKeJz7//398/vXzx/fv3398vnT+9vry/PT48P93e3N9dXlxfnZGcAS0/\ ra6kpzU2NDfJz3b169eDZIXXZ8sLe9sfZ/GIAYdBrG/fz0xul9G5dMh8ufX9+f76/P///19cPrZ7cXw4Tf3z+/f/38/vr8eH97fXn+/5+f3z69f/NyqIWh5\ QWZiVFB3s42prpqckD33Du+dmpNipcRAxT8/39///z6ePuXR+aWhzjqSV/dv2pqQ1Yo2Pkg+efH1vYUBxlKMKCB89vnt+eH26v/J+T/5QtmTOhsrCzKSooK\ BjrDuLo4Pzs5PjzY390ZqEZb+fXj25eB2neDgmVyd3P9uzund66Y3gZ2vK+rrRmG/2/vnl0d7awNcbY8uru2L+6vzo72t9dTV5AU4mZl+P/hwYUDGxZMaCh\ ICHAwUOD//+TE2knlUfZQb8vnQ/XB/P/x9eO7186f2D+3vzx+P1ABDv//h4c3If/3dzRUFILiwMvJ2lhbRdob6G9rc2N9bXVleWlxYf77l4/v3rx89uTm6u\ Ls5Ohg/zs7p5eGmknCbXoP9TeMXxzurC8vzM5AwP8wvr+3m6ONuZGuhrKcpAg/F8uKKQ3ZES4Gsujx//nhud3Lm2NMYKENEgX5Hx78z89tndOSHeRvb64uz\ 8/+Hxb/ADJVV38=" _palette_uzebox : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM1Mwp4nGNQ8ciduO3Wf4aRSkOBChR4QEEuFEyEgm1QcAsK/kPBcNFPLgilEK\ yiEPynEAAA16V90A==" _palette_amiga7800 : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM1MzMKeJxjEFQydglNK++YuWr3mbvv/v////3Z1SObF01syI/ztdGWMnQKTiltm7\ 5i56nbb/7+h4CfL64f27pkclNhgr+dfUBiUfOUpduO33j5Cyr9/93dM7tXzewoTwt1MZbVcwhMKm6Zumz7iZsvfwMlP9w/t3fN7K7KjHA3UxVhJgYGDkkta\ 5/YvPoJCzcdvvIUTRIM2MQ1LL2ic2r75m84eGD9vN6a7ChPC3UxVqg0g4CikXNIaln7jJW7Tj+8sH/d3J7qrEgPczVRFqAkn7yBY1BySeu05TtO3nr95///\ b0+vHN60cEJ9XqyPtZYkA5r/v399cvnQxgX9dbkx3laaEuwoll96/AVFMwfYen4FpEC6cXzb0inNRYkB9nqyPJjOu4MUdLoy3EBZLmkdW7/4gsZJi7ccvfb\ 8x/8fz68d3bJ4UmNBvJ+tjjSXlLaNb1x+w8RFm49cffYdHLwoIahu4RmVXdM7b/2Bi48+Q4L//b2ze1bP6qxID3M1UUZ1PVAW3X9o/p/eDIl3TgZkAHVk65\ xNt46sm9FcnBTooC/HC5Jh5peGu/DEvfdfPr97+QRug4+jaUxGCSTyX71+/eb1+y8/voPBJ0g81RZm1EDi/dt/ZAB1ZGWyr5pNUAay9/5+fApPnhZKAtw8g\ mLS8BDcvP/04undVZkR7maqIsJAIMDFzgEGvHL6oHTY1DetCdV/ADmMewY=" _palette_amiga7800mess : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM3MTQKeJxj1LONLG9ZsP3sjbsPXn349v//PyAEgxu7Z5aHOLoXLdh84smf/2jg7+\ fnV/ZvmFsV712/+cT9t7//QDVBNP9+/+T6qd2rpjame3nGpFZNmrts454j567euvfw2dkDG+bPmNBQmBrl76LKycbKo2DmFp6YW905ce7yTbsPX75weP3sy\ b0FYb6exrKycrKyTEyMTEDMKG2XWtY1e+PxCye2LWqrLQ7yM9eQ5GdhYBCTUdbQs3IPjC1s7Z2+dMfOTy9unzu0Y/W87sqcBA9XSx03n9DcCWsOXbz//N33\ vyD3AcGfn28fXti/dXVvYYQjI7r////88uzGhaOrJzZk+JtrKEjKqVoGZjX1zFx35Oixa0+e37p4aNOSGV2VeUl+ZvraKvLiUrKKqjZBCQX1E+at2nni1I7\ 1S+f21hWlR/na6kowMzIys/ApW7v6R6XmljVPmLNq2+XT+zatWjCpuTQjysfWQE2OQ8bQKTg2s7y1d8aS9TsOXnz78sn9OzeObVs5q60wxtNC0ye2oGXm6j\ 3n7zx8/urth88gB/5/8/jm2V1LJteXZod4OASnVfTMWLp+9/nHr7+CJP+8f3jtzP5N83vrC8OCXEyt7N0iynoWbth37OytZ59+/rxy+tjB7WsXTG4tTwm0U\ pWXQvf/mpkVYYG+NsCwF5cQYGNkYBGUU1RSkFfVtQiJzO1YtPrikc3zumszQ73ttVX4BXj5WHjFJWUsQxOT6/rnr9137t6bVy+ePbp17jDQA50lCd7muR0T\ l+07eenuiw8/QYGPlITe3rt6amlzZe/izUfO33rx/R807YAiCMh6c/P88TWTy2Oi8lvmbDt19ws0Xf6Dxb+vuTo4/hkYGBkZeCSV9Z3D4zOnTZ++ujInJNj\ TRl+GixEkycDJxcnJwa2gb+vqm1q75OiVGc2FCTGRfi5WBurywhyszABUd6ys" _palette_fornaxvoid1 : base642img \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMjU2IDEgMSAzICM1MjkKeJyrKi3ISU+OiwwN8HF3srM01Te3d/MLi03NLalp6Z48exEDO6+IlIK6ro\ mVg7tvSFTChwcXDmxYMKGhICHAwUBBgOH/p2e3zh3aunLuxNaKnIQQj8/Pb184snPtwmld9cXpMQGu/6HgwYEFDQlAHTeObJjbWZrkZ6UuzAAC/9/dPbN71\ cyO8rRQF2MlQQYGVk5eITEpOWV1bX0TCxsHRkYGFIChXkBez9YnOquqc8bybUevPP7/6enNM0AXNoDcp6EgsGzT3hOX77388v/Wtom5HioMjy8f2bpsekdl\ ZpS3ja4cP4Oqlr6xhY2jm5d/cERsYlrWvWvnju3btm7ZvGl9bXVleWlnDu/auGLB9L7WmpLspMgAjw8vH925eu7EwV1b1i5bMHNSD7r/+zqaasoKslLiIoJ\ 83BysTG8cWjMN6HG4+9H9r62hpqKkICcjKS4qLMjPy43u//8fHl46vGXptI7KrGgfWz2FBXNnz5w+dfLEvp6ujraWpgYGTiFpFV0ze4/AqOSc0rq2A2tmth\ UnGDDgAuj+/48GXn/6icI/c+Hy9Vt3Hz558frdp68///zH8D+RABb//79/ePHw5qVTh3dvWTuxLNrh/7d3T+9eOXN496ZVC2dO6GhgEFQydglNK++YuWr3m\ bvv/qP7n6B6KHhx4QA4Cfy/v39+fby9PAOXsIyqnjlGeAIAmOWLbw==" # Command that tries to compress a smooth-enough palette. compress_palette : N:=w do N-=1 +r. $N,1,1,3,2 round. ri. ..,3 round. deltaE. .. dEavg,dEmax="[ia,iM]" rm. e "N = "$N", dEavg = "$dEavg", dEmax = "$dEmax while $dEavg<0.75" && "$dEmax<1.25 N+=1 r $N,1,1,3,2 round. e "" img2base64 0,0 rm str=\"${}\" ('$str') s. x,-119 ('\\\n') if $!>2 a[0--3] .,x fi rm. i[0] ('" base642img \\\n"') ('"\n r. 256,1,1,3,3 round.\n"') a x ot pal.txt #@cli premula #@cli : Convert selected images with normal colors to premultiplied alpha colors. #@cli : After conversion, alpha channel of resulting images has value in [0,1] range. #@cli : See also: ''ipremula''. premula : e[^-1] "Convert image$? with normal colors to premultiplied alpha colors." to_a foreach { +channels. 100% /. 255 sh.. 0,{s#0-2} *. .. k[0] } #@cli pseudogray : _max_increment>=0,_JND_threshold>=0,_bits_depth>0 #@cli : Generate pseudogray colormap with specified increment and perceptual threshold. #@cli : If 'JND_threshold' is 0, no perceptual constraints are applied. #@cli : Default values: 'max_increment=5', 'JND_threshold=2.3' and 'bits_depth=8'. #@cli : $ pseudogray 5 pseudogray : check "isint(${1=5},0) && ${2=2.3}>=0 && isint(${3=8},1)" e[^-1] "Generate pseudogray colormap with increment $1, JND threshold $2 and $3 bits depth." # Generate all possible sRGB colors with given increments. {round(2^$3)},1,1,3,'x' if !$1 n. 0,255 return fi {$1+1},{$1+1},{$1+1},1,"x" +f. "y" +f. "z" a[-3--1] c r. {w*h*d},1,1,3,-1 f. "R = i(x,0,0,0); G = i(x,0,0,1); B = i(x,0,0,2); min(R,G,B)?-1:i" permute. cxyz discard. -1 r. 3,{h/3},1,1,-1 permute. yzcx r.. {w*100}% ri. ..,0,2 +[-2,-1] f. "R = i(x,0,0,0); G = i(x,0,0,1); B = i(x,0,0,2); max(R,G,B)>2^$3-1?-1:i" permute. cxyz discard. -1 r. 3,{h/3},1,1,-1 permute. yzcx n. 0,255 +srgb2lab. rv[-2,-1] a[-2,-1] y sort. +,x # Sort by increasing lightness. if !$2 rows. 1 else # Add perceptual constraint if requested. # Constraint 1 : keep colors close enough to equivalent 'pure' grays. s. y rv[-2,-1] . sh. 1,2 f. 0 rm. -[-2,-1] norm. <=. $2 *. 'x+1' discard. 0 -. 1 map. .. rm.. # Constraint 2 : remove neighboring colors that are above the JND. repeat 10000 { +srgb2lab. +shift. 0,{1-2*($>%2)},0,0,1 -[-2,-1] norm. <=. $2 if im rm. break fi *. 'y+1' discard. 0 -. 1 map. .. rm.. } transpose. fi => pseudogray$1 #@cli random_clut #@cli : Generate a 33x33x33 random 3D color LUT. #@cli : $ image.jpg random_clut +map_clut.. . #@cli random_clut : _seed = { >=0 | -1 } #@cli : Generate a 33x33x33 random 3D color LUT. #@cli : If specified 'seed' is positive, it is used as a seed for the random number generator \ #@cli : (so that using the same seed will return the same CLUT). #@cli : $ image.jpg random_clut +map_clut.. . +random_clut : l[] { check "isint(${1=-1},-1)" seed=$1 onfail seed=-1 noarg } e[^-1] "Generate a random 3D color LUT." if $seed>=0 srand $seed fi # Determine a set of random color keypoint. 1,1,1,3 1,8,1,1,"u<0.75 || !y?da_push(255*[ y&1, (y>>1)&1, (y>>2)&1 ])" rm. # Add some of the cube corners 1,{v(2,12)},1,1," do ( RGB = u([255,255,255]); dmin = inf; for (p = 0, p70?(da_push(RGB); break()); 1)" rm. da_freeze. # Randomly modify keypoint colors. rgb2hsl. mirror. c sort. +,y mirror. c +f. "begin(Lmax = 0); H = i0; S = i1; L = i2; u<0.5?( do ( nH = (H + 30*g)%360; nS = cut(S + g/4,0,1); nL = cut(L + g/8,0,1), _(while) nL=0,smoothness[%]>=0,src1,src2,...,dest1,dest2,... #@cli : Replace pixels from/to specified colors in selected images. #@cli : $ image.jpg +replace_color 40,3,204,153,110,255,0,0 replace_color : check "$1>=0 && $2>=0" l[] { (${3--1}) y c s c,2 col1={0,^} col2={1,^} rm } e[^-1] "Replace color ("$col1") by color ("$col2") in image$?, with tolerance $1 and smoothness $2." foreach { 1,1,1,100%,$col1 ri[1] [0] if $1 -[1] [0] norm[1] <=[1] $1 else ==[1] [0] l[1] { s c & } fi b[1] $2 1,1,1,{0,s},$col2 ri[2] [0] j[0] [2],0,0,0,0,1,[1] k[0] } #@cli retinex : _value_offset>0,_colorspace={ hsi | hsv | lab | lrgb | rgb | ycbcr },\ # 0<=_min_cut<=100,0<=_max_cut<=100,_sigma_low>0,_sigma_mid>0,_sigma_high>0 #@cli : Apply multi-scale retinex algorithm on selected images to improve color consistency. #@cli : (as described in the page ). #@cli : Default values: 'offset=1', 'colorspace=hsv', 'min_cut=1', 'max_cut=1', 'sigma_low=15','sigma_mid=80' \ # and 'sigma_high=250'. retinex : check "${1=5}>0 && ${3=1}>=0 && $3<=100 && ${4=1}>=0 && $4<=100 && ${5=15}>0 && ${6=80}>0 && ${7=250}>0" skip "${2=hsv}" e[^-1] "Apply Retinex color consistency algorithm on image$?, with value offset $1, colorspace '$2', cuts ($3,$4) and sigmas (${5-7})." if ['$2']=='hsi' mode=hsi_i elif ['$2']=='hsv' mode=hsv_v elif ['$2']=='lab' mode=lab_l elif ['$2']=='rgb' mode=rgb elif ['$2']=='lrgb' mode=lrgb elif ['$2']=='ycbcr' mode=ycbcr_y else error[0--2] "Command '$0': Invalid colorspace argument '$2'." fi ac "_retinex $1,${3--1}",$mode _retinex : - {im-$1} {[w,h,d,s]},1 repeat 3 { +b[0] {arg(1+$>,${4-6})} +/[0,-1] rm.. *[1,-1] } rm[0] log c $2%,{100-$3}% n 0,255 #@cli rgb2bayer : _start_pattern=0,_color_grid=0 #@cli : Transform selected color images to RGB-Bayer sampled images. #@cli : Default values: 'start_pattern=0' and 'color_grid=0'. #@cli : $ image.jpg +rgb2bayer 0 rgb2bayer : skip ${1=0},${2=0} e[^-1] "Transform image$? to a RGB-Bayer "${arg0\ !$2,color,monochrome}" grid, starting from pattern '$1'." to_rgb foreach { _rgb2bayer$1 ri[1] [0],0,2 * if !$2 s c + fi } _rgb2bayer0 : (1,0;0,0^0,1;1,0^0,0;0,1) _rgb2bayer1 : (0,0;0,1^0,1;1,0^1,0;0,0) _rgb2bayer2 : (0,1;0,0^1,0;0,1^0,0;1,0) _rgb2bayer3 : (0,0;1,0^1,0;0,1^0,1;0,0) #@cli rgb2cmy #@cli : Convert color representation of selected images from RGB to CMY. #@cli : $ image.jpg rgb2cmy split c rgb2cmy : e[^-1] "Convert color representation of image$? from RGB to CMY." to_rgb * -1 + 255 c 0,255 #@cli rgb2cmyk #@cli : Convert color representation of selected images from RGB to CMYK. #@cli : $ image.jpg rgb2cmyk split c #@cli : $ image.jpg rgb2cmyk split c fill[3] 0 append c cmyk2rgb rgb2cmyk : e[^-1] "Convert color representation of image$? from RGB to CMYK." rgb2cmy foreach { s c +min -[0-2] . +/. 255 -. 1 *. -1 +==. 0 +[-2,-1] /[0-2] . rm. a c } #@cli rgb2hcy #@cli : Convert color representation of selected images from RGB to HCY. #@cli : $ image.jpg rgb2hcy split c rgb2hcy : e[^-1] "Convert color representation of image$? from RGB to HCY." to_color f " M = max(R,G,B); C = M - min(R,G,B); H = 60*(!C?0:M==R?((G-B)/C)%6:M==G?(B-R)/C+2:(R-G)/C+4); Y = 0.299*R + 0.587*G + 0.114*B; [ H,C/255,Y/255 ]" #@cli rgb2hsi #@cli : Convert color representation of selected images from RGB to HSI. #@cli : $ image.jpg rgb2hsi split c rgb2hsi : e[^-1] "Convert color representation of image$? from RGB to HSI." to_color f " m = min(R,G,B); M = max(R,G,B); C = M - m; sum = R + G + B; H = 60*(!C?0:M==R?((G - B)/C)%6:M==G?(B - R)/C + 2:(R - G)/C + 4); S = sum<=0?0:1 - 3*m/sum; I = sum/(3*255); [ H, S, I ]" #@cli rgb2hsi8 #@cli : Convert color representation of selected images from RGB to HSI8. #@cli : $ image.jpg rgb2hsi8 split c rgb2hsi8 : e[^-1] "Convert color representation of image$? from RGB to HSI8." rgb2hsi _rgb2hsx8 #@cli rgb2hsl #@cli : Convert color representation of selected images from RGB to HSL. #@cli : $ image.jpg rgb2hsl split c #@cli : $ image.jpg rgb2hsl +split c add[-3] 100 mod[-3] 360 append[-3--1] c hsl2rgb rgb2hsl : e[^-1] "Convert color representation of image$? from RGB to HSL." to_color f " m = min(R,G,B); M = max(R,G,B); C = M - m; H = 60*(!C?0:M==R?((G - B)/C)%6:M==G?(B - R)/C + 2:(R - G)/C + 4); L = 0.5*(m + M)/255; S = L==1 || !L?0:C/(1 - abs(2*L - 1))/255; [ H, S, L ]" #@cli rgb2hsl8 #@cli : Convert color representation of selected images from RGB to HSL8. #@cli : $ image.jpg rgb2hsl8 split c rgb2hsl8 : e[^-1] "Convert color representation of image$? from RGB to HSL8." rgb2hsl _rgb2hsx8 #@cli rgb2hsv #@cli : Convert color representation of selected images from RGB to HSV. #@cli : $ image.jpg rgb2hsv split c #@cli : $ image.jpg rgb2hsv +split c add[-2] 0.3 cut[-2] 0,1 append[-3--1] c hsv2rgb rgb2hsv : e[^-1] "Convert color representation of image$? from RGB to HSV." to_color f " M = max(R,G,B); C = M - min(R,G,B); H = 60*(!C?0:M==R?((G - B)/C)%6:M==G?(B - R)/C + 2:(R - G)/C + 4); S = M<=0?0:C/M; [ H, S, M/255 ]" #@cli rgb2hsv8 #@cli : Convert color representation of selected images from RGB to HSV8. #@cli : $ image.jpg rgb2hsv8 split c rgb2hsv8 : e[^-1] "Convert color representation of image$? from RGB to HSV8." rgb2hsv _rgb2hsx8 _rgb2hsx8 : foreach { sh[0] 0 *. 0.708333 rm. sh[0] 1,2 *. 255 rm. } #@cli rgb2int #@cli : Convert color representation of selected images from RGB to INT24 scalars. #@cli : $ image.jpg rgb2int rgb2int : e[^-1] "Convert color representation of image$? from RGB to INT24 scalars." to_rgb round foreach { s c <<[0] 16 <<[1] 8 + } #@cli rgb2jzazbz : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from RGB to Jzazbz. #@cli : Default value: 'illuminant=2'. rgb2jzazbz : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from RGB to Jzazbz, using the "${arg0\ $illu,D50,D65,E}" illuminant." rgb2xyz $illu xyz2jzazbz #@cli rgb2lab : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from RGB to Lab. #@cli : Default value: 'illuminant=2'. rgb2lab : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from RGB to Lab, using the "${arg0\ $illu,D50,D65,E}" illuminant." rgb2xyz $illu xyz2lab $illu #@cli rgb2lab8 : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from RGB to Lab8. #@cli : Default value: 'illuminant=2'. #@cli : $ image.jpg rgb2lab8 split c rgb2lab8 : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from RGB to Lab8, using the "${arg0\ $illu,D50,D65,E}" illuminant." rgb2lab $illu foreach { sh[0] 0 *. 2.55 rm. sh[0] 1 +. 127 *. 0.85 rm. sh[0] 2 +. 149 *. 0.836 rm. } c 0,255 #@cli rgb2lch : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from RGB to Lch. #@cli : Default value: 'illuminant=2'. #@cli : $ image.jpg rgb2lch split c rgb2lch : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from RGB to Lch, using the "${arg0\ $illu,D50,D65,E}" illuminant." rgb2lab $illu lab2lch #@cli rgb2lch8 : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from RGB to Lch8. #@cli : Default value: 'illuminant=2'. #@cli : $ image.jpg rgb2lch8 split c rgb2lch8 : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from RGB to Lch8, using the "${arg0\ $illu,D50,D65,E}" illuminant." pi,facth:=[pi,255/(2*pi)] rgb2lch $illu foreach { sh[0] 0 *. 2.55 rm. sh[0] 1 *. 1.1086 rm. sh[0] 2 +. $pi *. $facth rm. } c 0,255 #@cli rgb2luv #@cli : Convert color representation of selected images from RGB to LUV. #@cli : $ image.jpg rgb2luv split c rgb2luv : e[^-1] "Convert color representation of image$? from RGB to LUV." foreach { +rgb2xyz rgb2lab.. channels.. 0 s. c *. 3 +*.. 15 +[-2,-1] +. ... +. 1e-8 # Z <- X+15Y+3Z *... 4 *.. 9 /[-3,-2] . rm. -.. 0.2009 -. 0.4610 +*... 13 *... . *[-2,-1] a c } #@cli rgb2oklab #@cli : Convert color representation of selected images from RGB to Oklab. #@cli : (see colorspace definition at: ). #@cli : See also: ''oklab2rgb''. rgb2oklab : e[^-1] "Convert color representation of image$? from RGB to Oklab." foreach { split_opacity to_rgb[0] /[0] 255 f[0] " l = cbrt(0.4121656120*R + 0.5362752080*G + 0.0514575653*B); m = cbrt(0.2118591070*R + 0.6807189584*G + 0.1074065790*B); s = cbrt(0.0883097947*R + 0.2818474174*G + 0.6302613616*B); [ 0.2104542553*l + 0.7936177850*m - 0.0040720468*s, 1.9779984951*l - 2.4285922050*m + 0.4505937099*s, 0.0259040371*l + 0.7827717662*m - 0.8086757660*s ]" a c } #@cli rgb2ryb #@cli : Convert color representation of selected images from RGB to RYB. #@cli : $ image.jpg rgb2ryb split c rgb2ryb : e[^-1] "Convert color representation of image$? from RGB to RYB." to_color f "red = R; green = G; blue = B; white = min(red,green,blue); red-=white; green-=white; blue-=white; maxgreen = max(red,green,blue); yellow = min(red,green); red-=yellow; green-=yellow; blue>0 && green>0?(blue/=2; green/=2); yellow+=green; blue+=green; maxyellow = max(red,yellow,blue); maxyellow>0?( N = maxgreen/maxyellow; red*=N; yellow*=N; blue*=N; ); red+=white; yellow+=white; blue+=white; [ red,yellow,blue ]" #@cli rgb2srgb #@cli : Convert color representation of selected images from linear RGB to sRGB. rgb2srgb : e[^-1] "Convert color representation of image$? from linear RGB to sRGB." f "val = i/255; sval = val<=0.0031308?val*12.92:1.055*val^0.416667 - 0.055; cut(255*sval,0,255)" #@cli rgb2xyz : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from RGB to XYZ. #@cli : Default value: 'illuminant=2'. #@cli : $ image.jpg rgb2xyz split c rgb2xyz : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from RGB to XYZ, using the "${arg0\ $illu,D50,D65,E}" illuminant." if $illu==2 # E mix_rgb {[0.488718,0.3106803,0.2006017,\ 0.1762044,0.8129847,0.0108109,\ 0,0.0102048,0.9897952]/255} elif $illu==1 # D65 mix_rgb {[0.4124564,0.3575761,0.1804375,\ 0.2126729,0.7151522,0.0721750,\ 0.0193339,0.1191920,0.9503041]/255} else # D50 mix_rgb {[0.43603516,0.38511658,0.14305115,\ 0.22248840,0.71690369,0.06060791,\ 0.01391602,0.09706116,0.71392822]/255} fi #@cli rgb2xyz8 : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from RGB to XYZ8. #@cli : Default value: 'illuminant=2'. #@cli : $ image.jpg rgb2xyz8 split c rgb2xyz8 : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from RGB to XYZ8, using the "${arg0\ $illu,D50,D65,E}" illuminant." rgb2xyz $illu foreach { sh[0] 0 *. 255 rm. sh[0] 1 *. 255 rm. sh[0] 2 *. 231.8182 rm. } #@cli rgb2yiq #@cli : Convert color representation of selected images from RGB to YIQ. #@cli : $ image.jpg rgb2yiq split c rgb2yiq : e[^-1] "Convert color representation of image$? from RGB to YIQ." mix_rgb 0.299,0.587,0.114,\ 0.595716,-0.274453,-0.321263,\ 0.211456,-0.522591,0.311135 #@cli rgb2yiq8 #@cli : Convert color representation of selected images from RGB to YIQ8. #@cli : $ image.jpg rgb2yiq8 split c rgb2yiq8 : e[^-1] "Convert color representation of image$? from RGB to YIQ." rgb2yiq foreach { sh[0] 1 +. 151.908 *. 0.8393238012481239 rm. sh[0] 2 +. 133.261 *. 0.9567690472081104 rm. } #@cli rgb2ycbcr #@cli : Convert color representation of selected images from RGB to YCbCr. #@cli : $ image.jpg rgb2ycbcr split c rgb2ycbcr : e[^-1] "Convert color representation of image$? from RGB to YCbCr." mix_rgb 66,129,25,-38,-74,112,112,-94,-18 foreach { sh[0] 0,2 +. 128 /. 256 rm. sh[0] 0 +. 16 rm. sh[0] 1,2 +. 128 rm. } #@cli rgb2yuv #@cli : Convert color representation of selected images from RGB to YUV. #@cli : $ image.jpg rgb2yuv split c rgb2yuv : e[^-1] "Convert color representation of image$? from RGB to YUV." mix_rgb {[0.299,0.587,0.114,\ -0.14713,-0.28886,0.436,\ 0.615,-0.51498,-0.10001]/255} #@cli rgb2yuv8 #@cli : Convert color representation of selected images from RGB to YUV8. #@cli : $ image.jpg rgb2yuv8 split c rgb2yuv8 : e[^-1] "Convert color representation of image$? from RGB to YUV8." rgb2yuv foreach { sh[0] 0 *. 255 rm. sh[0] 1 +. 0.44 *. 289.773 rm. sh[0] 2 +. 0.62 *. 205.645 rm. } #@cli remove_opacity #@cli : Remove opacity channel of selected images. remove_opacity : e[^-1] "Remove opacity channel of image$?." foreach { if !s||s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)." elif s==2 channels 0 elif s==4 channels 0,2 fi } #@cli ryb2rgb #@cli : Convert color representation of selected images from RYB to RGB. ryb2rgb : e[^-1] "Convert color representation of image$? from RYB to RGB." to_color f "red = R; yellow = G; blue = B; white = min(red,yellow,blue); red-=white; yellow-=white; blue-=white; maxyellow = max(red,yellow,blue); green = min(yellow,blue); yellow-=green; blue-=green; blue>0 && green>0?(blue*=2; green*=2); red+=yellow; green+=yellow; maxgreen = max(red,green,blue); maxgreen>0?( N = maxyellow/maxgreen; red*=N; green*=N; blue*=N; ); red+=white; green+=white; blue+=white; [ red,green,blue ]" #@cli select_color : tolerance[%]>=0,col1,...,colN #@cli : Select pixels with specified color in selected images. #@cli : $ image.jpg +select_color 40,204,153,110 #@cli : $$ https://gmic.eu/oldtutorial/_select_color select_color : skip ${1=0} e[^-1] "Select color (${2--1}) in image$?, with tolerance $1." foreach { +fc ${2--1} - norm <= $1 } #@cli sepia #@cli : Apply sepia tones effect on selected images. #@cli : $ image.jpg sepia sepia : e[^-1] "Apply sepia tones effect on image$?." (0,44,115,143,196,244^0,20,84,119,184,235^0,5,44,73,144,200) r. 256,1,1,3,3 foreach[^-1] { pass. split_opacity[0] luminance[0] map[0] . rm. a c } rm. #@cli solarize #@cli : Solarize selected images. #@cli : $ image.jpg solarize solarize : e[^-1] "Solarize image$?." luminance n 0,128 map 1 #@cli split_colors : _tolerance>=0,_max_nb_outputs>0,_min_area>0 #@cli : Split selected images as several image containing a single color. #@cli : One selected image can be split as at most 'max_nb_outputs' images. #@cli : Output images are sorted by decreasing area of extracted color regions and have an additional alpha-channel. #@cli : Default values: 'tolerance=0', 'max_nb_outputs=256' and 'min_area=8'. #@cli : $ image.jpg quantize 5 +split_colors , display_rgba split_colors : check "${1=0}>=0 && isint(${2=256},1) && ${3=8}>=1" e[^-1] "Split image$? as single color outputs, with tolerance $1, $2 maximal outputs and minimal color area $3." foreach { +label 0,1 norm. area. 0,1 # Loop over biggest color regions. repeat $2-1 { coordsM={1,[xM,yM,zM,cM]} if {1,i($coordsM)<$3} break fi color={0,I($coordsM)} +select_color[0] $1,$color ==. 0 *[1] . ==. 0 } if iM#1 !=[1] 0 mv[1] $! # Residual mask, if any. else rm[1] fi r[^0] [0],[0],[0],{0,s+1} N:=$!-1 sh[^0] 0,{s-2} *[-$N--1] [0] rm[-$N--1] sh[^0] 100% *[-$N--1] 255 rm[0,-$N--1] } #@cli split_opacity #@cli : Split color and opacity parts of selected images. #@cli : This command returns 1 or 2 images for each selected image, whether it has an opacity channel or not. split_opacity : e[^-1] "Split color and opacity parts of image$?." foreach { s c,{s==4?-3:s==2?-1:-s} } #@cli split_vector : keep_splitting_values={ + | - },value1,_value2,... #@cli : Split selected images into multiple parts, where specified vector '[value1,_value2,...]' is the separator. split_vector : e[^-1] "Split image$?, according to vector [${2--1}]." foreach { s:=s permute cxyz s $1,${2--1} $!,1,1,1,"resize(#x,$s,h#x/$s,1,1,-1)" rm. permute cyzx } #@cli srgb2lab : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from sRGB to Lab. #@cli : Default value: 'illuminant=2'. #@cli : $ image.jpg srgb2lab split c #@cli : $ image.jpg srgb2lab +split c mul[-2,-1] 2.5 append[-3--1] c lab2srgb srgb2lab : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from sRGB to Lab, using the "${arg0\ $illu,D50,D65,E}" illuminant." srgb2rgb rgb2lab $illu #@cli srgb2lab8 : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from sRGB to Lab8. #@cli : Default value: 'illuminant=2'. srgb2lab8 : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from sRGB to Lab8, using the "${arg0\ $illu,D50,D65,E}" illuminant." srgb2rgb rgb2lab8 $illu #@cli srgb2rgb #@cli : Convert color representation of selected images from sRGB to linear RGB. srgb2rgb : e[^-1] "Convert color representation of image$? from sRGB to linear RGB." f "sval = i/255; val = sval<=0.04045?sval/12.92:((sval + 0.055)/(1.055))^2.4; cut(255*val,0,255)" #@cli to_a #@cli : Force selected images to have an alpha channel. to_a : e[^-1] "Force image$? to have an alpha channel." foreach { if !s||s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)." elif s==1||s==3 channels 0,{s} sh. {s-1} f. 255 rm. fi } #@cli to_color #@cli : Force selected images to be in color mode (RGB or RGBA). to_color : e[^-1] "Force image$? to be in color mode." foreach { if !s||s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)." elif s==2 r 100%,100%,1,4,0,1,0,0,0,1 elif s==1 r 100%,100%,1,3,1 fi } #@cli to_colormode : mode={ 0:adaptive | 1:G | 2:GA | 3:RGB | 4:RGBA } #@cli : Force selected images to be in a given color mode. #@cli : Default value: 'mode=0'. to_colormode : check "isint(${1=0},0,4)" s,err:="$1?[ $1,$1 ]:(rgb = a = e = 0; repeat (l,k,rgb|=s#k>=3; a|=!(s#k%2); e|=!s#k||s#k>4); [ (rgb?3:1) + a,e ])" if $err foreach { if !s||s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)." fi } fi ss=${"arg "$s",gray,graya,rgb,rgba"} e[^-1] "Force image$? to be in "{`uppercase(['$ss'])`}" mode." to_$ss #@cli to_gray #@cli : Force selected images to be in GRAY mode. #@cli : $ image.jpg +to_gray to_gray : e[^-1] "Force image$? to be in GRAY mode." foreach { if !s||s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)." elif s>=3 luminance elif s==2 channels 0 fi } #@cli to_graya #@cli : Force selected images to be in GRAYA mode. to_graya : e[^-1] "Force image$? to be in GRAYA mode." foreach { if !s||s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)." elif s==4 s c,-3 luminance.. a c elif s==3 luminance channels 0,1 sh. 1 f. 255 rm. elif s==1 channels 0,1 sh. 1 f. 255 rm. fi } #@cli to_pseudogray : _max_step>=0,_is_perceptual_constraint={ 0 | 1 },_bits_depth>0 #@cli : Convert selected scalar images ([0-255]-valued) to pseudo-gray color images. #@cli : Default values: 'max_step=5', 'is_perceptual_constraint=1' and 'bits_depth=8'. #@cli : The original pseudo-gray technique has been introduced by Rich Franzen . #@cli : Extension of this technique to arbitrary increments for more tones, has been done by David Tschumperlé. to_pseudogray : check "isint(${1=5},0) && isint(${3=8},1)" skip ${2=1} e[^-1] "Convert scalar image$? to pseudo-gray color images, with steps $1." channels 0 srgb2rgb pseudogray $1,{2.3*$2},$3 # Compute colormap with 65336 entries, to have match corresponding lightness. +srgb2lab. channels. 0 *. {65535/100} round. rows. 0,2 rv[-2,-1] permute. xcyz +. 1 a[-2,-1] y pointcloud. 0 +norm. !=. 0 distance. 1 *. -1 watershed.. . rm. -. 1 # Map colormap to images, with lightness preservation. repeat $!-1 { to_rgb[$>] rgb2lab[$>] channels[$>] 0 *[$>] {65535/100} round[$>] c[$>] 0,65535 map[$>] . } rm. #@cli to_rgb #@cli : Force selected images to be in RGB mode. to_rgb : e[^-1] "Force image$? to be in RGB mode." foreach { if !s||s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)." elif s==4 channels 0,2 elif s==2 channels 0 r 100%,100%,100%,3 elif s==1 r 100%,100%,100%,3 fi } #@cli to_rgba #@cli : Force selected images to be in RGBA mode. to_rgba : e[^-1] "Force image$? to be in RGBA mode." foreach { if !s||s>4 error[0--5] "Command '$0': Image ["$>"] is not a G,GA,RGB or RGBA image ("{s}" channels)." elif s==3 channels 0,3 sh. 3 f. 255 rm. elif s==2 r 100%,100%,100%,4,0,1,0,0,0,1 elif s==1 r 100%,100%,100%,4 sh. 3 f. 255 rm. fi } #@cli to_automode #@cli : Force selected images to be in the most significant color mode. #@cli : This commands checks for useless alpha channel (all values equal to 255), as well as #@cli : detects grayscale images encoded as color images. to_automode : e[^-1] "Force image$? to be in most significant color mode." foreach { # Check for useless alpha channel. if s==2||s==4 # GREYA or RGBA sh 100% if im==iM" && "im==255 rm. channels 0,{s-2} else rm. fi fi # Check for useless color. if s==3||s==4 # RGB or RGBA eval. "begin(is_gray = 1); is_gray && (i0!=i1 || i0!=i2)?(is_gray = 0); end(merge(is_gray,&&); set('is_gray',is_gray))" if $is_gray s c,-3 channels[0] 0 a c fi fi } #@cli xyz2jzazbz #@cli : Convert color representation of selected images from XYZ to RGB. xyz2jzazbz : e[^-1] "Convert color representation of image$? from XYZ to Jzazbz." foreach { split_opacity f[0] ${-_jzazbz_const}" X = i0*255; Y = i1*255; Z = i2*255; Xp = Jzazbz_b*X - (Jzazbz_b - 1)*Z; Yp = Jzazbz_g*Y - (Jzazbz_g - 1)*X; Zp = Z; L = 0.41478972*Xp + 0.579999*Yp + 0.0146480*Zp; M = -0.2015100*Xp + 1.120649*Yp + 0.0531008*Zp; S = -0.0166008*Xp + 0.264800*Yp + 0.6684799*Zp; tmp = (L/peakLum)^Jzazbz_n; Lp = ((Jzazbz_c1 + Jzazbz_c2*tmp)/(1 + Jzazbz_c3*tmp))^Jzazbz_p; tmp = (M/peakLum)^Jzazbz_n; Mp = ((Jzazbz_c1 + Jzazbz_c2*tmp)/(1 + Jzazbz_c3*tmp))^Jzazbz_p; tmp = (S/peakLum)^Jzazbz_n; Sp = ((Jzazbz_c1 + Jzazbz_c2*tmp)/(1 + Jzazbz_c3*tmp))^Jzazbz_p; Iz = 0.5*Lp + 0.5*Mp; az = 3.52400*Lp - 4.066708*Mp + 0.542708*Sp; bz = 0.199076*Lp + 1.096799*Mp - 1.295875*Sp; Jz = (1 + Jzazbz_d)*Iz/(1 + Jzazbz_d*Iz) - Jzazbz_d0; [ Jz,az,bz ]" a c } #@cli xyz2lab : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from XYZ to Lab. #@cli : Default value: 'illuminant=2'. xyz2lab : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from XYZ to Lab, using the "${arg0\ $illu,D50,D65,E}" illuminant." to_color f " begin( const epsilon = 216/24389; const kappa = 24389/27; lab(x) = (x>epsilon?cbrt(x):(x*kappa + 16)/116); D65 = [ 0.4124564, 0.3575761, 0.1804375, 0.2126729, 0.7151522, 0.0721750, 0.0193339, 0.1191920, 0.9503041 ]; D50 = [ 0.43603516, 0.38511658, 0.14305115, 0.22248840, 0.71690369, 0.06060791, 0.01391602, 0.09706116, 0.71392822 ]; E = [ 0.488718,0.3106803,0.2006017, 0.1762044,0.8129847,0.0108109, 0,0.0102048,0.9897952 ]; white = ("$illu"==2?E:"$illu"==1?D65:D50)*[ 1,1,1 ]; ); xr = i0/white[0]; yr = i1/white[1]; zr = i2/white[2]; fx = lab(xr); fy = lab(yr); fz = lab(zr); [ cut(116*fy - 16,0,100), 500*(fx - fy), 200*(fy - fz) ]" #@cli xyz2rgb : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from XYZ to RGB. #@cli : Default value: 'illuminant=2'. xyz2rgb : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from XYZ to RGB, using the "${arg0\ $illu,D50,D65,E}" illuminant." if $illu==2 # E mix_rgb {[2.3706743,-0.9000405,-0.4706338,\ -0.513885,1.4253036,0.0885814,\ 0.0052982,-0.0146949,1.0093968]*255} elif $illu # D65 mix_rgb {[3.2404542,-1.5371385,-0.4985314,\ -0.9692660,1.8760108,0.0415560,\ 0.0556434,-0.2040259,1.0572252]*255} else # D50 mix_rgb {[3.134274799724,-1.617275708956,-0.490724283042,\ -0.978795575994,1.916161689117,0.033453331711,\ 0.071976988401,-0.228984974402,1.405718224383]*255} fi c 0,255 #@cli xyz82rgb : illuminant={ 0:D50 | 1:D65 | 2:E } : (no arg) #@cli : Convert color representation of selected images from XYZ8 to RGB. #@cli : Default value: 'illuminant=2'. xyz82rgb : skip "${1=,}" l[] { if isnum("$1") illu:="$1>1?2:$1>0" else if ["'$1'"]!=',' noarg fi illu=2 fi onfail noarg illu=2 } e[^-1] "Convert color representation of image$? from XYZ8 to RGB, using the "${arg0\ $illu,D50,D65,E}" illuminant." foreach { sh[0] 0 /. 255 rm. sh[0] 1 /. 255 rm. sh[0] 2 /. 231.8182 rm. } xyz2rgb $illu #@cli ycbcr2rgb #@cli : Convert color representation of selected images from YCbCr to RGB. ycbcr2rgb : e[^-1] "Convert color representation of image$? from YCbCr to RGB." foreach { sh[0] 0 -. 16 rm. sh[0] 1,2 -. 128 rm. sh[0] 0,2 mix_rgb. 298,0,409,\ 298,-100,-208,\ 298,516,0 +. 128 /. 256 c. 0,255 rm. } #@cli yiq2rgb #@cli : Convert color representation of selected images from YIQ to RGB. yiq2rgb : e[^-1] "Convert color representation of image$? from YIQ to RGB." mix_rgb 1,0.9563,0.6210,\ 1,-0.2721,-0.6474,\ 1,-1.1070,1.7046 c 0,255 #@cli yiq82rgb #@cli : Convert color representation of selected images from YIQ8 to RGB. yiq82rgb : e[^-1] "Convert color representation of image$? from YIQ8 to RGB." foreach { sh[0] 1 /. 0.8393238012481239 -. 151.908 rm. sh[0] 2 /. 0.9567690472081104 -. 133.261 rm. } mix_rgb 1,0.9563,0.6210,\ 1,-0.2721,-0.6474,\ 1,-1.1070,1.7046 c 0,255 #@cli yuv2rgb #@cli : Convert color representation of selected images from YUV to RGB. yuv2rgb : e[^-1] "Convert color representation of image$? from YUV to RGB." mix_rgb {[1,0,1.13983,\ 1,-0.39465,-0.5806,\ 1,2.03211,0]*255} c 0,255 #@cli yuv82rgb #@cli : Convert selected images from YUV8 to RGB color bases. yuv82rgb : e[^-1] "Convert color representation of image$? from YUV8 to RGB." foreach { sh[0] 0 /. 255 rm. sh[0] 1 /. 289.773 -. 0.44 rm. sh[0] 2 /. 205.645 -. 0.62 rm. } yuv2rgb #--------------------------------- # #@cli :: Geometry Manipulation # #--------------------------------- #@cli a : eq. to 'append' : (+) #@cli append : [image],axis,_centering : axis,_centering : (+) #@cli : Append specified image to selected images, or all selected images together, along specified axis. #@cli : (eq. to 'a').\n #@cli : 'axis' can be { x | y | z | c }. #@cli : Usual 'centering' values are { 0:left-justified | 0.5:centered | 1:right-justified }. #@cli : Default value: 'centering=0'. #@cli : $ image.jpg split y,10 reverse append y #@cli : $ image.jpg repeat 5 { +rows[0] 0,{10+18*$>}% } remove[0] append x,0.5 #@cli : $ image.jpg append[0] [0],y #@cli append_tiles : _M>=0,_N>=0,0<=_centering_x<=1,0<=_centering_y<=1 #@cli : Append MxN selected tiles as new images. #@cli : If 'N' is set to 0, number of rows is estimated automatically. #@cli : If 'M' is set to 0, number of columns is estimated automatically. #@cli : If 'M' and 'N' are both set to '0', auto-mode is used. #@cli : If 'M' or 'N' is set to 0, only a single image is produced. #@cli : 'centering_x' and 'centering_y' tells about the centering of tiles when they have different sizes. #@cli : Default values: 'M=0', 'N=0', 'centering_x=centering_y=0.5'. #@cli : $ image.jpg split xy,4 append_tiles , append_tiles : check "isint(${1=0},0) && isint(${2=0},0) && inrange(${3=0},0,1) && inrange(${4=$3},0,1)" if !$! e[0--3] "Append image$? as a 0x0-tiled image." return elif !$1" && "!$2 # Auto-mode N:=int(sqrt($!)) M:=ceil($!/$N) e[0--3] "Append image$? as a "${M}x${N}"-tiled image (auto-mode)." elif !$2 # Auto-rows M=$1 N:=round($!/$1,1,1) e[0--3] "Append image$? as a "${M}x${N}"-tiled image." elif !$1 # Auto-columns M:=round($!/$2,1,1) N=$2 e[0--3] "Append image$? as a "${M}x${N}"-tiled image." else e[0--3] "Append image$?, as $1x$2-tiled images." M=$1 N=$2 fi W,H=${-max_wh} rs $W,$H,2,1 MN:=$M*$N if $!%$MN 0x{$MN-($!%$MN)} fi repeat $!/$MN { l[$>-{$>+$MN-1}] { repeat $!/$M { a[$>-{$>+$M-1}] x,$3 } a y,$4 } } #@cli apply_scales : "command",number_of_scales>0,_min_scale[%]>=0,_max_scale[%]>=0,_scale_gamma>0,_interpolation #@cli : Apply specified command on different scales of selected images. #@cli : 'interpolation' can be { 0:none | 1:nearest | 2:average | 3:linear | 4:grid | 5:bicubic | 6:lanczos }. #@cli : Default value: 'min_scale=25%', 'max_scale=100%' and 'interpolation=3'. #@cli : $ image.jpg apply_scales "blur 5 sharpen 1000",4 apply_scales : check "isint($2,1) && ${3=25%}>=0 && ${4=100%}>=0 && ${5=1}>0 && isint(${6=3},0)" skip "${1=}" s0="no" s1="nearest-neighbor" s2="average" s3="linear" s4="grid" s5="bicubic" s6="lanczos" e[^-1] "Apply command '$1' on image$? for $2 scales ($3 -> $4) and "${s{min(6,$6)}}" interpolation." foreach { nm={n} scale0:=ispercentage($3)?$3*max(w,h,d):$3 scale1:=ispercentage($4)?$4*max(w,h,d):$4 repeat $2 { scale:=$scale0+($scale1-$scale0)*($>/max(1,$2-1))^$5 w={0,w==1?1:max(1,round($scale*w/max(w,h,d)))} h={0,h==1?1:max(1,round($scale*h/max(w,h,d)))} d={0,d==1?1:max(1,round($scale*d/max(w,h,d)))} +r[0] $w,$h,$d,100%,$6 if narg("$1") l. { $1 } fi } rm[0] =>[^] $nm } #@cli autocrop : value1,value2,... : (no arg) : (+) #@cli : Autocrop selected images by specified vector-valued intensity. #@cli : If no arguments are provided, cropping value is guessed. #@cli : $ 400,400,1,3 fill_color 64,128,255 ellipse 50%,50%,120,120,0,1,255 +autocrop #@cli autocrop_components : _threshold[%],_min_area[%]>=0,_is_high_connectivity={ 0 | 1 },\ # _output_type={ 0:crop | 1:segmentation | 2:coordinates } #@cli : Autocrop and extract connected components in selected images, according to a mask given as the last channel of #@cli : each of the selected image (e.g. alpha-channel). #@cli : Default values: 'threshold=0%', 'min_area=0.1%', 'is_high_connectivity=0' and 'output_type=1'. #@cli : $ 256,256 noise 0.1,2 eq 1 dilate_circ 20 label_fg 0,1 normalize 0,255 +neq 0 *[-1] 255 append c \ # +autocrop_components , autocrop_components : skip ${1=0%} check "${2=0.1%}>=0 && isbool(${3=0}) && isint(${4=1},0,2)" e[^-1] "Autocrop connected components from image$?, with threshold $1, minimal area $2, "\ ${arg0\ $3,low,high}" connectivity "\ "and output type set to '"${arg0\ $4,crop,segmentation,coordinates}"'.\n" foreach { min_area:=max(1,round(ispercentage($2)?$2*w*h:$2)) +channels 100% >. $1 area_fg. 0,$3 >=. $min_area # Discard background and small objects. +area. 0,1 <. $min_area -|[-2,-1] label_fg. 0,1 # Fill small holes in objects. # Extract detected objects. N:=iM repeat iM { n:=1+$> e "\r > "$n/$N rprogress {100*$n/$N} +==[1] $n +*[0,-1] rm.. if !$4 coords=${autocrop_coords.\ auto} rm. +z[0] $coords elif $4==1 autocrop. else coords=${autocrop_coords.\ auto} rm. ($coords) y. fi } rm[0,1] if !$! 0 fi if $4==2 a x fi } #@cli autocrop_seq : value1,value2,... | auto #@cli : Autocrop selected images using the crop geometry of the last one by specified vector-valued intensity, #@cli : or by automatic guessing the cropping value. #@cli : Default value: auto mode. #@cli : $ image.jpg +fill[-1] 0 ellipse[-1] 50%,50%,30%,20%,0,1,1 autocrop_seq 0 autocrop_seq : skip ${1=auto} e[^-1] "Auto-crop image$? using crop geometry of last image by vector '$*'." if !$! return fi is_auto:=['"$1"']=='auto' if $!==1 _autocrop$is_auto ${1--1} return fi coords=${autocrop_coords.\ ${1--1}} x0,y0,z0,x1,y1,z1:=[$coords][0,6] if $x0>$x1" || "$y0>$y1" || "$z0>$z1 i[0--2] 0 rm[1--1:2] else z $x0,$y0,$z0,$x1,$y1,$z1 fi #@cli channels : c0[%],_c1[%] #@cli : Keep only specified channels of selected images. #@cli : Dirichlet boundary is used when specified channels are out of range. #@cli : Default value: 'c1=c0'. #@cli : $ image.jpg channels 0,1 #@cli : $ image.jpg luminance channels 0,2 channels : skip ${2=$1} e[^-1] "Keep channels $1...$2 of image$?." z 0,0,0,$1,100%,100%,100%,$2 #@cli columns : x0[%],_x1[%] #@cli : Keep only specified columns of selected images. #@cli : Dirichlet boundary is used when specified columns are out of range. #@cli : Default value: 'x1=x0'. #@cli : $ image.jpg columns -25%,50% columns : skip ${2=$1} e[^-1] "Keep columns $1...$2 of image?." z $1,$2 #@cli z : eq. to 'crop'. : (+) #@cli crop : x0[%],x1[%],_boundary_conditions : x0[%],y0[%],x1[%],y1[%],_boundary_conditions : \ # x0[%],y0[%],z0[%],x1[%],y1[%],z1[%],_boundary_conditions : \ # x0[%],y0[%],z0[%],c0[%],x1[%],y1[%],z1[%],c1[%],_boundary_conditions : (+) #@cli : Crop selected images with specified region coordinates. #@cli : (eq. to 'z').\n #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default value: 'boundary_conditions=0'. #@cli : $ image.jpg +crop -230,-230,280,280,1 crop[0] -230,-230,280,280,0 #@cli : $ image.jpg crop 25%,25%,75%,75% #@cli diagonal #@cli : Transform selected vectors as diagonal matrices. #@cli : $ 1,10,1,1,'y' +diagonal diagonal : e[^-1] "Transform vector$? as diagonal matrix." y foreach { r {h+1},100%,1,1,0 r {h},100%,1,1,-1 } # downsize_aliased : 100<=factor<=0 # Downsize selected images with a specific algorithm to better render anti-aliased rendering # over transparent background (from aliased transparent images, manage transparent border pixels with more care). downsize_aliased : check "${1=50}>=0 && $1<=100" if $1==100 return elif !$1 r 1,1,1,100%,2 return fi N:=ceil(1+100/$1) foreach { split_opacity if $!==1 continue fi +dilate.. $N +==.. 0 j[0] ..,0,0,0,0,1,. rm[-2,-1] a c r $1%,$1%,$1%,100%,2 } #@cli edgels : x0,y0,_n0,_is_high_connectivity={ 0 | 1 } #@cli : Extract one or several lists of edgels (and their normals) that defines a 2D binary silhouette. #@cli : When specified (i.e. '!=-1'), arguments 'x0,y0,n0' are the coordinates of the starting edgel, \ # which must be located on an edge of the binary silhouette. #@cli : - If 'x0,y0' and 'n0' are specified, only a single list of edgels is returned. #@cli : - If only 'x0,y0' are specified (meaning 'n0=-1'), up to 4 lists of edgels can be returned, \ # all starting from the same point (x0,y0). #@cli : - If no arguments are specified (meaning 'x0=y0=n0=-1'), all possible lists of edgels are returned. #@cli : A list of edgels is returned as an image with 3 channels '[x,y,n]' where 'x' and 'y' are the 2D coordinates \ # of the edgel pixel, and 'n' is the orientation of its associated canonical normal \ # (which can be { 0:[1,0] | 1:[0,1] | 2:[-1,0] | 3:[0,-1] }. #@cli : Default values: 'x0=y0=n0=-1' and 'is_high_connectivity=1'. edgels : check "isint(${1=-1}) && $1>=(${2=-1}>=0?0:-1) && isint($2) && $2>=($1>=0?0:-1) && "\ "isint(${3=-1}) && ($1>=0?inrange($3,-1,3):($3==-1)) && isbool(${4=1})" is_xy0,is_xyn0:="a=min($1,$2)>=0;[a,a && $3>=0]" if $is_xyn0 s= opt=", starting from edgels ($1,$2,$3)." elif $is_xy0 s=s opt=", starting from pixel ($1,$2)." else s=s opt="." fi e[^-1] "Extract list"$s" of edgels in image$?"$opt pass[$[]] -1 sel=${} code=" P0 = P = [ x,y,n ]; do ( da_push(P); !$is_xyn0?(i(#1,P[0],P[1],0,0)|=(1<,$sel)}"]." fi 1,1,1,3 eval ${-math_lib}"const x = $1; const y = $2; const n = $3; "$code elif $is_xy0 # Pixel coordinates specified if "!i($1,$2)" error[0--5] "Command 'edgels': Point ($1,$2) is in background, in image ["{arg0($>,$sel)}"]." fi 100%,100% # Already visited edgels (bitwise storage) 1,1,1,3 eval ${-math_lib}" const x = $1; const y = $2; sep = [ -1,-1,-1 ]; repeat (4,n, !(i(#1,x,y)&(1<${-math_lib}"begin(sep = [ -1,-1,-1 ]); i(x,y)?repeat (4,n, !(i(#1,x,y)&(1<0 $!,1,1,1,"resize(#x,3,h#x/3,1,1,-1)" rm. else 0 fi permute zycx fi } #@cli elevate : _depth,_is_plain={ 0 | 1 },_is_colored={ 0 | 1 } #@cli : Elevate selected 2D images into 3D volumes. #@cli : Default values: 'depth=64', 'is_plain=1' and 'is_colored=1'. elevate : check "${1=64}>0" skip ${2=1},${3=1} e[^-1] "Elevate 2D image$? into $1-slices volume(s)." r 100%,100%,1,100% foreach { nm={n} +norm 100%,100%,$1,{$3?{0,s}:1} m={-2,im} d={-2,iM-$m} repeat $1 { if $2 +>=[1] {$m+$d*($>+1)/$1} else +ir[1] {$m+$d*$>/$1},{$m+$d*($>+1)/$1} fi r. 100%,100%,1,.. if $3 *. [0] fi j.. .,0,0,$> rm. } rm[0,1] => $nm } #@cli expand : axes,size[%],_boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Expand selected images along the specified axes. #@cli : 'axes' can be { x | y | z | c | xy | xz | xc | yz | yc | zc | xyz | xyc | xzc | yzc | xyzc }. #@cli : Default value: 'boundary_conditions=0'. #@cli : $ image.jpg expand xy,30 expand : check "isint(${3=0},0,3)" e[^-1] "Expand image$? along ax"{`size(['$1'])>1?_'e':'i'`}"s '$1', with size $2 and "\ ${"arg0 $3,dirichlet,neumann,periodic,mirror"}" boundary conditions." no_x,no_y,no_z,no_c:="arg = ['$1']; [ find(arg,_'x')<0, find(arg,_'y')<0, find(arg,_'z')<0, find(arg,_'c')<0 ]" $!,1,1,1," const ipc = ispercentage($2); const siz2 = 2*$2; nw = w#x + ($no_x?0:ipc?floor(w#x*siz2):siz2); nh = h#x + ($no_y?0:ipc?floor(h#x*siz2):siz2); nd = d#x + ($no_z?0:ipc?floor(d#x*siz2):siz2); ns = s#x + ($no_c?0:ipc?floor(s#x*siz2):siz2); nw>0 && nh>0 && nd>0 && nd>0?resize(#x,nw,nh,nd,ns,0,$3,0.5,0.5,0.5,0.5):resize(#x,0); " rm. #@cli extract : "condition",_output_type={ 0:xyzc-coordinates | 1:xyz-coordinates | 2:scalar-values | 3:vector-values } #@cli : Extract a list of coordinates or values from selected image, where #@cli : specified mathematical condition holds. #@cli : For N coordinates matching, result is a 1xNx1x4 image. #@cli : Default values: 'output_type=0'. #@cli : $ sp lena +extract "norm(I)>128",3 extract : check "isin(${2=0},0,1,2,3)" s0,s1,s2,s3=xyzc-coordinates,xyz-coordinates,scalar-values,vector-values e[^-1] "Extract "$s$2" from image$? verifying condition '$1'." str=">begin(run('1,32,1,',arg(1+$2,4,3,1,s)));($1)?(" if !$2 str.="da_push([x,y,z,c]));i" elif $2==1 str.="da_push([x,y,z]));I" elif $2==2 str.="da_push(i));i" else str.="da_push(I));I" fi str.=";end(resize(#-1,1,da_size(),1,s(#-1),0))" repeat $! { nm={$>,n} eval[$>] $str => $nm rv[$>,-1] rm. } #@cli extract_region : [label_image],_extract_xyz_coordinates={ 0 | 1 },_label_1,...,_label_M #@cli : Extract all pixels of selected images whose corresponding label in '[label_image]' is equal to 'label_m', #@cli : and output them as M column images. #@cli : Default value: 'extract_xyz_coordinates=0'. #@cli : $ image.jpg +blur 3 quantize. 4,0 +extract_region[0] [1],0,1,3 extract_region : check ${"is_image_arg $1"}" && isnum(${2=0})" if $#<3 e[0--3] "Extract pixels of image$? for labels [] in image $1, with"${"arg0 !!$2,out"}" coordinates -> no labels provided, ignoring." return fi e[^-1] "Extract pixels of image$? for labels {${3--1}} in image $1, with"${"arg0 !!$2,out"}" coordinates." pass$1 mv. 0 repeat $!-1 { l[0,{1+$<}] { nm={n} 1,16,1,{s+($2?3:0)} if $#>3 .x{$#-3} fi f[0] "> begin( const N = iM + 1; # Number of labels R = [ ${3--1} ]; # Requested labels hash = vectorN(0); # Correspondence table repeat (size(R),k, hash[R[k]] = k + 2); ); (ind = hash[i])>0?( $2?da_push(#ind,[ I(#1),x,y,z ]):da_push(#ind,I(#1)); ); i; end( repeat (l - 2,k, resize(#k + 2,1,da_size(#k + 2),1,-100,0)) )" rm[1] =>[^] $nm } } rm[0] #@cli montage : "_layout_code",_montage_mode={ 0<=centering<=1 | 2<=scale+2<=3 },\ # _output_mode={ 0:single layer | 1:multiple layers },"_processing_command" #@cli : Create a single image montage from selected images, according to specified layout code : #@cli : - 'X' to assemble all images using an automatically estimated layout. #@cli : - 'H' to assemble all images horizontally. #@cli : - 'V' to assemble all images vertically. #@cli : - 'A' to assemble all images as an horizontal array. #@cli : - 'B' to assemble all images as a vertical array. #@cli : - 'Ha:b' to assemble two blocks 'a' and 'b' horizontally. #@cli : - 'Va:b' to assemble two blocks 'a' and 'b' vertically. #@cli : - 'Ra' to rotate a block 'a' by 90 deg. ('RRa' for 180 deg. and 'RRRa' for 270 deg.). #@cli : - 'Ma' to mirror a block 'a' along the X-axis ('MRRa' for the Y-axis). #@cli : A block 'a' can be an image index (treated periodically) or a nested layout expression 'Hb:c','Vb:c','Rb' or #@cli : 'Mb' itself. #@cli : For example, layout code 'H0:V1:2' creates an image where image [0] is on the left, and images [1] and [2] #@cli : vertically packed on the right. #@cli : Default values: 'layout_code=X', 'montage_mode=2', output_mode='0' and 'processing_command=""'. #@cli : $ image.jpg sample ? +plasma[0] shape_cupid 256 normalize 0,255 frame xy,3,0 frame xy,10,255 to_rgb \ # +montage A +montage[^-1] H1:V0:VH2:1H0:3 montage : check "isnum(${2=2}) && $2>=0 && $2<=3" skip "${1=X}",${3=0},"${4=}" if $2<=1 e[0--3] "Create aligned montage from image$?, with layout code '$1' and centering $2." else e[0--3] "Create scaled montage from image$?, with layout code '$1' and scale "{$2-2}"." fi to_colormode 0 # Manage automatic layout. if lowercase(['"$1"'])=='x' +l { foreach { => $> } repeat $!-1 { if {-2,w>h}" && "w>h mode=V # Both landscape. elif {-2,h>w}" && "h>w mode=H # Both portrait. elif {-2,w>h}" && "h>w # Landscape - portrait. if {-2,h/w}<(w/h) mode=V else mode=H fi else # Portrait - landscape. if {-2,w/h}<(h/w) mode=H else mode=V fi fi name=$mode{-2,n}:{n} montage[-2,-1] $mode,$2 mv. 0 =>[0] $name } layout={0,n} rm } montage $layout,$2,$3,"$4" else # Non-automatic layout. # Format and check validity of layout code. N=$! l[] { _scode="$1" _mode=$2 if lowercase(['"$1"'])=='h' if $N>1 {$N-1},1,1,1,-1 $N,1,1,1,x a x y else return fi # Simple horizontal montage. elif lowercase(['"$1"'])=='v' if $N>1 {$N-1},1,1,1,-2 $N,1,1,1,x a x y else return fi # Simple vertical montage. elif isin(lowercase(['"$1"']),'a','b') # Montage as an array. if $N<2 return fi nr:=round(sqrt($N)) nc:=round($N/$nr,1,1) # Horizontal array. if lowercase(['"$1"'])=='b' n=$nr nr=$nc nc=$n fi # Vertical array. $N,1,1,1,x s x,-{round(w/$nr,1,1)} foreach { if w>1 i[0] {w-1},1,1,1,-1 a x fi } a x if $nr>1 i[0] {$nr-1},1,1,1,-2 a x fi y else # Other complex montage. ('"$1"') f "i==72 || i==104?-1: i==86 || i==118?-2: i==82 || i==114?-3: i==77 || i==109?-4: i>=48 && i<=57?i-48:-5" s +,-1 s +,-2 s +,-3 s +,-4 s +,-5 foreach { if im>=0 ++. 48 =.. {t} rm. rows 0 fi } a y discard -5 fi f "i<0?i:i%$N" } if $!==$N rm return fi # Empty layout code. =>[^-1] 0 repeat h { c:=i[$>] # Duplicate multiple references. if $c>=0 if {$c,n} i.. [$c] i:=$!-2 =. $i,0,$> ref$i=$c else =>[$c] 1 ref$c=$c fi fi } _code={^} _lcode:=narg($_code) rm. # Determine image positions and sizes. N=$! repeat $N { ($>,0,0,{$>,w},{$>,h},0,0,0) } l[$N--1] { _p=1 k[${-_montage}] w:=i[3] h:=i[4] f i(0,y)<0?-1:i discard -1 y r 8,{h/8},1,1,-1 onfail error[0--3] "Too many input images." } # Render montage. if narg("$4") m "__montage : $4 k[0]" else m "__montage : if $""7%2 mirror x fi if $""8%2 mirror y fi rotate {90*$""6} r {max(1,round($""4,1,1))},{max(1,round($""5,1,1))},1,100%,3" fi s=${max_s[^-1]} repeat h { i,xi,yi,wi,hi,ai,mxi,myi:=crop(0,$>,w,1) if $3||!$> i.. $w,$h,1,$s fi __montage[$i] ${ref$i},$xi,$yi,{max(1,$wi)},{max(1,$hi)},$ai,$mxi,$myi j.. [$i],$xi,$yi } um __montage rm[0-{$N-1},-1] fi => "[Montage '$1']" _montage : if $_p>$_lcode error "Command 'montage': Incomplete layout code '"$_scode"'." fi c:=arg($_p,$_code) if $c>=0 _p+=1 u $c # Single index. elif $c==-4 # Mirror. _p+=1 l=${-_montage} f[$l] "a=i(5,y)%2;(x==7&&a)||(x==6&&!a)?!i:x==1?i(3,0)-i(3,y)-i:i" u $l elif $c==-3 # Rotation. _p+=1 l=${-_montage} l[$l] { s x +[2] [4] rv[1,2] *[1] -1 +[1] {4,@0} rv[3,4] +[5] 1 a x } u $l else # Merge. _p+=1 l=${-_montage} lw={$l,@3} lh={$l,@4} r=${-_montage} rw={$r,@3} rh={$r,@4} if $c==-1 # Horizontal merge. if $_mode<2 h:=max($lh,$rh) +[$l] '0,0,{($h-$lh)*min(1,$_mode)},0,0,0,0,0' +[$r] '0,$lw,{($h-$rh)*min(1,$_mode)},0,0,0,0,0' else h:=($_mode-2)*max($lh,$rh)+(3-$_mode)*min($lh,$rh) lf:=$h/$lh rf:=$h/$rh lw:=$lw*$lf rw:=$rw*$rf *[$l] '1,$lf,$lf,$lf,$lf,1,1,1' *[$r] '1,$rf,$rf,$rf,$rf,1,1,1' +[$r] '0,$lw,0,0,0,0,0,0' fi i[$l] (-1,0,0,{$lw+$rw},$h,0,0,0) a[$l,{$l+1}] y a[$l] [$r],y r[$r] 1,1,1,1,0 else # Vertical merge. if $_mode<2 w:=max($lw,$rw) +[$l] '0,{($w-$lw)*min(1,$_mode)},0,0,0,0,0,0' +[$r] '0,{($w-$rw)*min(1,$_mode)},$lh,0,0,0,0,0' else w:=($_mode-2)*max($lw,$rw)+(3-$_mode)*min($lw,$rw) lf:=$w/$lw rf:=$w/$rw lh:=$lh*$lf rh:=$rh*$rf *[$l] '1,$lf,$lf,$lf,$lf,1,1,1' *[$r] '1,$rf,$rf,$rf,$rf,1,1,1' +[$r] '0,0,$lh,0,0,0,0,0' fi i[$l] (-1,0,0,$w,{$lh+$rh},0,0,0) a[$l,{$l+1}] y a[$l] [$r],y r[$r] 1,1,1,1,0 fi u $l fi #@cli mirror : { x | y | z }...{ x | y | z } : (+) #@cli : Mirror selected images along specified axes. #@cli : $ image.jpg +mirror y +mirror[0] c #@cli : $ image.jpg +mirror x +mirror y append_tiles 2,2 #@cli permute : permutation_string : (+) #@cli : Permute selected image axes by specified permutation. #@cli : 'permutation' is a combination of the character set {x|y|z|c}, #@cli : e.g. 'xycz', 'cxyz', ... #@cli : $ image.jpg permute yxzc #@cli rs : eq. to 'rescale2d'. rs : _gmic_s="$?" v + _rescale2d $* #@cli rescale2d : _width[%]={ 0:any | >0 },_height[%]={ 0:any | >0 },-1=<_interpolation<=6,\ # _mode={ 0:inside | 1:padded-inside | 2:outside | 3:cropped-outside } #@cli : Resize selected 2D images while preserving aspect ratio. #@cli : 'interpolation' can be { -1:status only | 0:none | 1:nearest | 2:average | 3:linear | \ # 4=grid | 5=bicubic | 6=lanczos }. #@cli : When 'interpolation==-1', image size is actually not modified, but the size that would have been used \ # for the last selected image is returned in the status value. #@cli : Each resized image size is computed according to the specified 'mode': #@cli : - If 'mode==0', image size is __at most__ '(width,height)'. #@cli : - If 'mode==1' or 'mode==3', image size is __exactly__ '(width,height)'. #@cli : - If 'mode==2', image size is __at least__ '(width,height)'. #@cli : (eq. to 'rs'). #@cli : Default values: 'width=height=0', 'interpolation=2' and 'mode=0'. rescale2d : _gmic_s="$?" v + _$0 $* _rescale2d : check "${1=0}>=0 && ${2=0}>=0 && isin(${3=2},-1,0,1,2,3,4,5,6) && isin(${4=0},0,1,2,3)" e[0--3] "Resize 2D image"$_gmic_s" to ("${"arg0 !!$1,any,$1"},${"arg0 !!$2,any,$2"}"), "\ "with "${"arg0 \"max(0,$3)\",no,nearest,average,linear,grid,bicubic,lanczos"}" interpolation "\ "and '"${"arg0 $4,inside,padded-inside,outside,cropped-outside"}"' mode." 1,$!,1,1,"w = max(w#y,1); h = max(h#y,1); d = max(d#y,1); s = max(s#y,1); aw = round(ispercentage($1)?max(1,w*$1):$1); ah = round(ispercentage($2)?max(1,h*$2):$2); aw || ah?( ratio = aw && ah?($4<2?min(aw/w,ah/h):max(aw/w,ah/h)): # W&H constraints aw?aw/w: # W constraint ah/h; # H constraint bw = max(1,round(ratio*w)); bh = max(1,round(ratio*h)); $4==1?(cw = max(aw,bw); ch = max(ah,bh)): $4==3?(cw = min(aw,bw); ch = min(ah,bh)): (cw = bw; ch = bh); $3>=0?(resize(#y,bw,bh,d,s,$3); isin($4,1,3)?resize(#y,cw,ch,d,s,0,0,0.5,0.5)); ); end($3<0?set('{}',string(cw,',',ch)))" rm. #@cli rs3d : eq. to 'rescale3d'. rs3d : _gmic_s="$?" v + _rescale3d $* #@cli rescale3d : _width[%]={ 0:any | >0 },_height[%]={ 0:any | >0 },_depth[%]={ 0:any | >0 },-1=<_interpolation<=6,\ # _mode={ 0:inside | 1:padded-inside | 2:outside | 3: cropped-outside } #@cli : Resize selected 3D images while preserving aspect ratio. #@cli : 'interpolation' can be { -1:status only | 0:none | 1:nearest | 2:average | 3:linear | \ # 4=grid | 5=bicubic | 6=lanczos }. #@cli : When 'interpolation==-1', image size is actually not modified, but the size that would have been used \ # for the last selected image is returned in the status value. #@cli : Each resized image size is computed according to the specified 'mode': #@cli : - If 'mode==0', image size is __at most__ '(width,height)'. #@cli : - If 'mode==1' or 'mode==3', image size is __exactly__ '(width,height)'. #@cli : - If 'mode==2', image size is __at least__ '(width,height)'. #@cli : (eq. to 'rs3d'). #@cli : Default values: 'width=height=depth=0', 'interpolation=2' and 'mode=0'. rescale3d : _gmic_s="$?" v + _$0 $* _rescale3d : check "${1=0}>=0 && ${2=0}>=0 && ${3=0}>=0 && isin(${4=2},-1,0,1,2,3,4,5,6) && isin(${5=0},0,1,2,3)" e[0--3] "Resize 3D image"$_gmic_s" to ("${"arg0 !!$1,any,$1"},${"arg0 !!$2,any,$2"},${"arg0 !!$3,any,$3"}"), "\ "with "${"arg0 \"max(0,$4)\",no,nearest,average,linear,grid,bicubic,lanczos"}" interpolation "\ "and '"${"arg0 $5,inside,padded-inside,outside,cropped-outside"}"' mode." 1,$!,1,1,"w = max(w#y,1); h = max(h#y,1); d = max(d#y,1); s = max(s#y,1); aw = round(ispercentage($1)?max(1,w*$1):$1); ah = round(ispercentage($2)?max(1,h*$2):$2); ad = round(ispercentage($3)?max(1,d*$3):$3); aw || ah || ad?( ratio = aw && ah && ad?($5<2?min(aw/w,ah/h,ad/d):max(aw/w,ah/h,ad/d)): # W&H&D constraints aw && ah && !ad?($5<2?min(aw/w,ah/h):max(aw/w,ah/h)): # W&H constraints aw && !ah && ad?($5<2?min(aw/w,ad/d):max(aw/w,ad/d)): # W&D constraints !aw && ah && ad?($5<2?min(ah/h,ad/d):max(ah/h,ad/d)): # H&D constraints aw?aw/w: # W constraint ah?ah/h: # H constraint ad/d; # D constraint bw = max(1,round(ratio*w)); bh = max(1,round(ratio*h)); bd = max(1,round(ratio*d)); $5==1?(cw = max(aw,bw); ch = max(ah,bh); cd = max(ad,bd)): $5==3?(cw = min(aw,bw); ch = min(ah,bh); cd = min(ad,bd)): (cw = bw; ch = bh; cd = bd); $4>=0?(resize(#y,bw,bh,bd,s,$4); isin($5,1,3)?resize(#y,cw,ch,cd,s,0,0,0.5,0.5,0.5)); ); end($4<0?set('{}',string(cw,',',ch,',',cd)))" rm. #@cli r : eq. to 'resize'. : (+) #@cli resize : {[image_w] | width>0[%]},_{[image_h] | height>0[%]},_{[image_d] | depth>0[%]},\ # _{[image_s] | spectrum>0[%]},_interpolation,_boundary_conditions,_ax,_ay,_az,_ac : (+) #@cli : Resize selected images with specified geometry. #@cli : (eq. to 'r').\n #@cli : 'interpolation' can be { -1:none (memory content) | 0:none | 1:nearest | 2:average | 3:linear | \ # 4=grid | 5=bicubic | 6=lanczos }. #@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode : #@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless. #@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0:none | 1:neumann }. #@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0 or 4' #@cli : (set to '0' by default, must be defined in range [0,1]). #@cli : Default values: 'interpolation=1', 'boundary_conditions=0' and 'ax=ay=az=ac=0'. #@cli : $ image.jpg +resize[-1] 256,128,1,3,2 +resize[-1] 120%,120%,1,3,0,1,0.5,0.5 \ # +resize[-1] 120%,120%,1,3,0,0,0.2,0.2 +resize[-1] [0],[0],1,3,4 #@cli ri : eq. to 'resize_as_image'. ri : skip "${2=1},${3=0},${4=0},${5=0},${6=0},${7=0}" # Faster than the non-shortcut version of the command pass$1 r[^-1] .,.,.,.,${2--1} rm. #@cli resize_as_image : [reference],_interpolation,_boundary_conditions,_ax,_ay,_az,_ac #@cli : Resize selected images to the geometry of specified [reference] image. #@cli : (eq. to 'ri'). #@cli : Default values: 'interpolation=1', 'boundary_conditions=0' and 'ax=ay=az=ac=0'. #@cli : $ image.jpg sample duck +resize_as_image[-1] [-2] resize_as_image : check ${"is_image_arg $1"}" && isint(${2=1},-1,6) && "\ "isint(${3=0},0,3) && isnum(${4=0}) && isnum(${5=0}) && isnum(${6=0}) && isnum(${7=0})" pass$1 r[^-1] .,.,.,.,${2--1} rm. #@cli resize_mn : width[%]>=0,_height[%]>=0,_depth[%]>=0,_B_value,_C_value #@cli : Resize selected images with Mitchell-Netravali filter (cubic). #@cli : For details about the method, see: . #@cli : Default values: 'height=100%', 'depth=100%', 'B=0.3333' and 'C=0.3333'. #@cli : $ image.jpg rescale2d 32 resize_mn 800%,800% resize_mn : check "${2=100%}>=0 && ${3=100%}>=0" skip "${4=0.333},${5=0.333}" e[^-1] "Resize image$? to $1x$2x$3 using Mitchell-Netravali filter (B=$4, C=$5)." lib="const B = $4; const C = $5; const boundary = 1; const interp = 0; mn(P0,P1,P2,P3,d) = ( ( (-B/6-C)*P0 + (-3*B/2-C+2)*P1 + (3*B/2+C-2)*P2 + (B/6+C)*P3 )*d^3 + ( (B/2+2*C)*P0 + (2*B+C-3)*P1 + (-5*B/2-2*C+3)*P2 -C*P3)*d^2 + ( (-B/2-C)*P0 + (B/2+C)*P2)*d + B/6*P0 + (-B/3+1)*P1 + B/6*P2);" foreach { nm={n} nw:=ispercentage($1)?max(1,round($1*w)):round($1) nh:=ispercentage($2)?max(1,round($2*h)):round($2) nd:=ispercentage($3)?max(1,round($3*d)):round($3) if !$nw" || "!$nh" || "!$nd rm 0 elif !w rm $nw,$nh,$nd,1 else if w==1||$nww $nw,100%,100%,100%,${lib}"X = x*(w#-1-1)/(w-1); d = X - int(X); P0 = I(#-1,X-1); P1 = I(#-1,X); P2 = I(#-1,X+1); P3 = I(#-1,X+2); mn(P0,P1,P2,P3,d);" k. fi if h==1||$nh $nm } #@cli resize_pow2 : _interpolation,_boundary_conditions,_ax,_ay,_az,_ac #@cli : Resize selected images so that each dimension is a power of 2. #@cli : 'interpolation' can be { -1:none (memory content) | 0:none | 1:nearest | 2:average | 3:linear | \ # 4:grid | 5:bicubic | 6:lanczos }. #@cli : 'boundary_conditions' has different meanings, according to the chosen 'interpolation' mode : #@cli : . When 'interpolation=={ -1 | 1 | 2 | 4 }', 'boundary_conditions' is meaningless. #@cli : . When 'interpolation==0', 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : . When 'interpolation=={ 3 | 5 | 6 }', 'boundary_conditions' can be { 0:none | 1:neumann }. #@cli : 'ax,ay,az,ac' set the centering along each axis when 'interpolation=0' #@cli : (set to '0' by default, must be defined in range [0,1]). #@cli : Default values: 'interpolation=0', 'boundary_conditions=0' and 'ax=ay=az=ac=0'. #@cli : $ image.jpg +resize_pow2[-1] 0 resize_pow2 : check "isint(${1=0},-1,6)" skip ${2=0},${3=0},${4=0},${5=0},${6=0} e[^-1] "Resize image$? so that each dimension is a power of 2." foreach { r {2^(ceil(log2([w,h,d])))},100%,${1-6} } #@cli rotate : angle,_interpolation,_boundary_conditions,_center_x[%],_center_y[%] : \ # u,v,w,angle,interpolation,boundary_conditions,_center_x[%],_center_y[%],_center_z[%] : (+) #@cli : Rotate selected images with specified angle (in deg.), and optionally 3D axis (u,v,w). #@cli : 'interpolation' can be { 0:none | 1:linear | 2:bicubic }. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : When a rotation center (cx,cy,_cz) is specified, the size of the image is preserved. #@cli : Default values: 'interpolation=1', 'boundary_conditions=0' and 'center_x=center_y=(undefined)'. #@cli : $ image.jpg +rotate -25,1,2,50%,50% rotate[0] 25 #@cli rotate_tileable : angle,_max_size_factor>=0 #@cli : Rotate selected images by specified angle and make them tileable. #@cli : If resulting size of an image is too big, the image is replaced by a 1x1 image. #@cli : Default values: 'max_size_factor=8'. rotate_tileable : check ${2=8}>=0 e[^-1] "Rotate image$? with angle $1 deg. and make them tileable." # Reduce angle to known fraction. angle:=$1%360 if $angle>=270 rotate 270 angle-=270 elif $angle>=180 rotate 180 angle-=180 elif $angle>=90 rotate 90 angle-=90 fi # List of known fractions. (0,1;1,8;1,7;1,6;1,5;1,4;1,5;1,3;2,5;1,2;2,5;3,5;2,3;3,4;4,5;1,1;5,4;7,5;3,2;8,5;9,5;2,1;3,1;4,1;5,1;6,1;7,1;8,1) s. x,2 +/[-2,-1] atan. *. {180/pi} # Compute corresponding angles. ($angle) index. .. rm.. p={-3,@{^}} q={-2,@{^}} rm[-3--1] # Find nearest fraction p/q to atan(angle). if !$p||!$q return fi foreach { # Compute width and height of tile. theta:=atan2($p,$q) gcd:=gcd(h*$q,w*$p) pw:=h*$q/$gcd nw:=round($pw*w/cos($theta)) gcd!=gcd(h*$p,w*$q) qh:=w*$q/$gcd nh:=round($qh*h/cos($theta)) # Rotate and make tileable (may result in very large images!). if !$2" || "($nw<$2*w" && "$nh<$2*h) r {1.5*$nw},{1.5*$nh},1,100%,0,2 rotate {$theta*180/pi},1,2,50%,50% r $nw,$nh,1,100%,0,2,0.5,0.5 else error[0--4] "Command '$0': Invalid image dimension "({w},{h},{d},{s}). fi } #@cli rows : y0[%],_y1[%] #@cli : Keep only specified rows of selected images. #@cli : Dirichlet boundary conditions are used when specified rows are out of range. #@cli : Default value: 'y1=y0'. #@cli : $ image.jpg rows -25%,50% rows : skip ${2=$1} e[^-1] "Keep rows $1...$2 of image$?." z 0,$1,100%,$2 #@cli scale2x #@cli : Resize selected images using the Scale2x algorithm. #@cli : $ image.jpg threshold 50% resize 50%,50% +scale2x scale2x : e[^-1] "Double xy-dimensions of image$?, using Scale2x algorithm." foreach { r 200%,200% f "dx=x&1;dy=y&1;A=j(0,-2,0,0,0,1);B=j(2,0,0,0,0,1);C=j(-2,0,0,0,0,1);D=j(0,2,0,0,0,1); !dy*(!dx*(C==A&&C!=D&&A!=B?A:i) + dx*(A==B&&A!=C&&B!=D?B:i)) + dy*(dx*(B==D&&B!=A&&D!=C?D:i) + !dx*(D==C&&D!=B&&C!=A?C:i))" } #@cli scale3x #@cli : Resize selected images using the Scale3x algorithm. #@cli : $ image.jpg threshold 50% resize 33%,33% +scale3x scale3x : e[^-1] "Triple xy-dimensions of image$?, using Scale3x algorithm." foreach { r 300%,300% f "dx=x%3;dy=y%3;c0=!dx;c1=(dx==1);c2=(dx==2); A=j(-3,-3,0,0,0,1);B=j(0,-3,0,0,0,1);C=j(3,-3,0,0,0,1); D=j(-3,0,0,0,0,1);F=j(3,0,0,0,0,1); G=j(-3,3,0,0,0,1);H=j(0,3,0,0,0,1);I=j(3,3,0,0,0,1); !dy*(c0*(D==B&&D!=H&&B!=F?D:i) + c1*((D==B&&D!=H&&B!=F&&i!=C)||(B==F&&B!=D&&F!=H&&i!=A)?B:i) + c2*(B==F&&B!=D&&F!=H?F:i)) + (dy==1)*(c0*((H==D&&H!=F&&D!=B&&i!=A)||(D==B&&D!=H&&B!=F&&i!=G)?D:i) + c1*i + c2*((B==F&&B!=D&&F!=H&&i!=I)||(F==H&&F!=B&&H!=D&&i!=C)?F:i)) + (dy==2)*(c0*(H==D&&H!=F&&D!=B?D:i) + c1*((F==H&&F!=B&&H!=D&&i!=G)||(H==D&&H!=F&&D!=B&&i!=I)?H:i) + c2*(F==H&&F!=B&&H!=D?F:i))" } #@cli scale_dcci2x : _edge_threshold>=0,_exponent>0,_extend_1px={ 0:false | 1:true } #@cli : Double image size using directional cubic convolution interpolation, #@cli : as described in . #@cli : Default values: 'edge_threshold=1.15', 'exponent=5' and 'extend_1px=0'. #@cli : $ image.jpg +scale_dcci2x , scale_dcci2x : check "${1=1.15}>=0 && ${2=5}>=0" skip ${3=0} e[^-1] "Double xy-dimensions of image$?, using DCCI2x algorithm." foreach { r {2*w-(!$3)},{2*h-(!$3)},1,100%,4 # Estimate diagonal values. f "begin( const threshold = $1; const exponent = $2; interpolation = 0; boundary = 1; j1(x,y) = P[7*y + x + 24]; # eq. to 'j(x,y)', but faster j2(x,y) = P[7*x - y + 24]; # eq. to 'j(-y,x)', but faster interp(k) = -k#(-3,-3) + 9*k#(-1,-1) + 9*k#(1,1) - k#(3,3); d(k) = sum(abs([ k#(-1,-3) - k#(-3,-1), k#(1,-3) - k#(-1,-1), k#(3,-3) - k#(1,-1), k#(-1,-1) - k#(-3,1), k#(1,-1) - k#(-1,1), k#(3,-1) - k#(1,1), k#(-1,1) - k#(-3,3), k#(1,1) - k#(-1,3), k#(3,1) - k#(1,3) ])); ); if (!((x*y)%2),i, ref(crop(x - 3,y - 3,0,c,7,7,1,1),P); d1 = d(j1); d2 = d(j2); ratio = (1 + d1)/(1 + d2); value = ratio>threshold ? interp(j1): # Up-right edge ratio<(1/threshold) ? interp(j2): # Down-right edge (w1 = 1/(1 + d1^exponent); w2 = 1/(1 + d2^exponent); (interp(j1)*w1 + interp(j2)*w2)/(w1 + w2)); # Smooth area value/=16)" # Estimate remaining values. f "begin( const threshold = $1; const exponent = $2; interpolation = 0; boundary = 1; j1(x,y) = P[7*y + x + 24]; # eq. to 'j(x,y)', but faster j2(x,y) = P[7*x - y + 24]; # eq. to 'j(-y,x)', but faster interp(k) = -k#(0,-3) + 9*k#(0,-1) + 9*k#(0,1) - k#(0,3); d(k) = sum(abs([ k#(-1,-2) - k#(1,-2), k#(-2,-1) - k#(0,-1), k#(0,-1) - k#(2,-1), k#(-3,0) - k#(-1,0), k#(-1,0) - k#(1,0), k#(1,0) - k#(3,0), k#(-2,1) - k#(0,1), k#(0,1) - k#(2,1), k#(-1,2) - k#(1,2) ])); ); if ((x%2) + (y%2)!=1,i, ref(crop(x - 3,y - 3,0,c,7,7,1,1),P); d1 = d(j1); d2 = d(j2); ratio = (1 + d1)/(1 + d2); value = ratio>threshold ? interp(j1) : # Horizontal edge ratio<(1/threshold) ? interp(j2) : # Vertical edge (w1 = 1/(1 + d1^exponent); w2 = 1/(1 + d2^exponent); (interp(j1)*w1 + interp(j2)*w2)/(w1 + w2)); # Smooth area value/=16)" } #@cli seamcarve : _width[%]>=0,_height[%]>=0,_is_priority_channel={ 0 | 1 },_is_antialiasing={ 0 | 1 },\ # _maximum_seams[%]>=0 #@cli : Resize selected images with specified 2D geometry, using the seam-carving algorithm. #@cli : Default values: 'height=100%', 'is_priority_channel=0', 'is_antialiasing=1' and 'maximum_seams=25%'. #@cli : $ image.jpg seamcarve 60% # The main code of this algorithm has been done by Andy (Garagecoder). seamcarve : check "${2=100%}>=0 && ${5=25%}>=0" skip ${3=0},${4=1} e[^-1] "Resize image$? to $1x$2 using seam-carving algorithm, "${arg0\ !$3,with,without}" priority channel, "\ ${arg0\ !$4,with,without}" anti-aliasing and maximum seams $5." foreach { nw:=max(1,round(ispercentage($1)?$1*w:$1)) nh:=max(1,round(ispercentage($2)?$2*h:$2)) if $nw!=w _seamcarve $nw,$3,$4,$5 fi if $nh!=h transpose _seamcarve $nh,$3,$4,$5 transpose fi } # Subroutine to remove/add vertical seams/ # $1 = desired width. # $2 = is_priority_channel={ 0 | 1 } # $3 = is_antialiasing={ 0 | 1 } # $4 = max number of seams added/removed at once. _seamcarve : do max_seams:=max(1,round(ispercentage($4)?$4*w:$4)) ssms:=max(min(round($1-w),w),1-w) sms:=min($max_seams,abs($ssms)) # Compute potential map. if $2 s[0] c,{1-s} /. 256 fi +gradient[0] a[-2,-1] c abs. compose_channels. + n. 0,1 if $2 +. .. a[0,1] c fi # Add x-coordinates channel for anti-aliasing. if $3 100%,1,1,1,x r. [0],[0] a[0,-1] c fi # Calculate low matrix (backwards propagation). . repeat h { +rows. {$<+1} erode. 3 j.. .,0,$<,0,0,-1 rm. } # Initialise seams, top matrix. 100%,100% +rows[1] 0 => grad,low,seam,top repeat h#0-1 { nr:=$>+1 +rows[low] $nr # Find optimum matches between two 1D matrices. +*[4,5] +shift[4] 1 *. [5] +shift[5] 1 *. [4] +[-2,-1] j[5] [4] a[-3--1] c f. ">c?i:max(j(-1)+j(0,0,0,1),j(-2)+j(0,0,0,2))" s. c shift... 1 +.. ... shift... 1 +[-3,-1] >[-2,-1] f. " # Distribute matched pixels in top matrix. a[-2,-1] c f. "j(i,0,0,-1)" channels. 1 # Add next energy row to top matrix. +rows[grad] $nr +[top,-1] } # Add / remove seams. max:=iM*2 repeat $sms { =. $max,{xm} } j[grad] .,0,100% rm[low,top] a[-2,-1] c f. "0 # Add seams. -. 2 s[0] c repeat $s { if $><($s-1) . fi a[$>,-1] c } permute cxyz a c discard -1 f "i<0?j(0,-1):i" r {$w+$sms},$h,1,$s,-1 fi # Perform anti-aliasing step. if $3 s c,{1-s} g. x,1 round !=. 1 (0.5,0.5) +convolve[0] . rm.. j[0] .,0,0,0,0,1,[1] rm[-2,-1] fi rprogress {a=w/$1;a<1?a*100:100/a} while w!=$1 #@cli shift : vx[%],_vy[%],_vz[%],_vc[%],_boundary_conditions,_interpolation={ 0:nearest_neighbor | 1:linear } : (+) #@cli : Shift selected images by specified displacement vector. #@cli : Displacement vector can be non-integer in which case linear interpolation should be chosen. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default value: 'boundary_conditions=0' and 'interpolation=0'. #@cli : $ image.jpg +shift[0] 50%,50%,0,0,0 +shift[0] 50%,50%,0,0,1 +shift[0] 50%,50%,0,0,2 #@cli shrink : axes,size[%],_boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Shrink selected images along the specified axes. #@cli : 'axes' can be { x | y | z | c | xy | xz | xc | yz | yc | zc | xyz | xyc | xzc | yzc | xyzc }. #@cli : Default value: 'boundary_conditions=0'. #@cli : $ image.jpg shrink xy,100 shrink : check "isint(${3=0},0,3)" e[^-1] "Shrink image$? along ax"{`size(['$1'])>1?_'e':'i'`}"s '$1', with size $2 and "\ ${"arg0 $3,dirichlet,neumann,periodic,mirror"}" boundary conditions." expand "$1",{-$2},$3 #@cli slices : z0[%],_z1[%] #@cli : Keep only specified slices of selected images. #@cli : Dirichlet boundary conditions are used when specified slices are out of range. #@cli : Default value: 'z1=z0'. slices : skip ${2=$1} e[^-1] "Keep slices $1...$2 of image$?." z 0,0,$1,100%,100%,$2 #@cli sort : _ordering={ + | - },_axis={ x | y | z | c } : (+) #@cli : Sort pixel values of selected images. #@cli : If 'axis' is specified, the sorting is done according to the data of the first column/row/slice/channel #@cli : of selected images. #@cli : Default values: 'ordering=+' and 'axis=(undefined)'. #@cli : $ 64 rand 0,100 +sort display_graph 400,300,3 #@cli s : eq. to 'split'. : (+) #@cli split : { x | y | z | c }...{ x | y | z | c },_split_mode : \ # keep_splitting_values={ + | - },_{ x | y | z | c }...{ x | y | z | c },value1,_value2,... : (no arg) : (+) #@cli : Split selected images along specified axes, or regarding to a sequence of scalar values #@cli : (optionally along specified axes too). #@cli : (eq. to 's').\n #@cli : 'split_mode' can be { 0:split according to constant values | >0:split in N parts | \ # <0:split in parts of size -N }. #@cli : Default value: 'split_mode=-1'. #@cli : $ image.jpg split c #@cli : $ image.jpg split y,3 #@cli : $ image.jpg split x,-128 #@cli : $ 1,20,1,1,"1,2,3,4" +split -,2,3 append[1--1] y #@cli : $ (1,2,2,3,3,3,4,4,4,4) +split x,0 append[1--1] y #@cli split_tiles : M!=0,_N!=0,_is_homogeneous={ 0 | 1 } #@cli : Split selected images as a MxN array of tiles. #@cli : If M or N is negative, it stands for the tile size instead. #@cli : Default values: 'N=M' and 'is_homogeneous=0'. #@cli : $ image.jpg +local split_tiles 5,4 blur 3,0 sharpen 700 append_tiles 4,5 done split_tiles : skip ${2=$1},${3=0} if $3 e[^-1] "Split image$? as a $1x$2 array of homogeneous tiles." else e[^-1] "Split image$? as a $1x$2 array of tiles." fi foreach { nm={n} s y,$2 s x,$1 if $3 r [0],[0],100%,100%,0 fi =>[^] $nm } #@cli undistort : -1<=_amplitude<=1,_aspect_ratio,_zoom,_center_x[%],_center_y[%],_boundary_conditions #@cli : Correct barrel/pincushion distortions occurring with wide-angle lens. #@cli : References: #@cli : [1] Zhang Z. (1999). Flexible camera calibration by viewing a plane from unknown orientation. #@cli : [2] Andrew W. Fitzgibbon (2001). Simultaneous linear estimation of multiple view geometry and lens distortion. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'amplitude=0.25', 'aspect_ratio=0', 'zoom=0', 'center_x=center_y=50%' \ # and 'boundary_conditions=0'. undistort : check "${1=0.1}>=-1 && $1<=1 && ${6=0}>=0 && $6<=3" skip ${2=0},${3=0},${4=50%},${5=50%} e[^-1] "Undistort barrel/pincushion effect in image$?, with amplitude $1, aspect ratio $2, zoom factor $3, center ($4,$5) and "${"arg0 $6,dirichlet,neumann,periodic,mirror"}" boundary conditions." foreach { center_x:=ispercentage($4)?w*$4:$4 center_y:=ispercentage($5)?h*$5:$5 f " const interpolation = 1; const boundary = $6; const center_x = "$center_x"; const center_y = "$center_y"; const alpha = cut($1,-0.999,0.999); const ratio = $2>=0?1+$2:1/(1-$2); const zoom = $3>=0?1+$3:1/(1-$3); const M = max(w,h); x = 2*(x - center_x)/(zoom*ratio*M); y = 2*(y - center_y)/(zoom*M); r = norm(x,y); nr = r/(1 - alpha*r^2); if (r>0, nx = nr/r*x; ny = nr/r*y, nx = x; ny = y ); x = 0.5*nx*ratio*M + center_x; y = 0.5*ny*M + center_y; I(x,y)" } #@cli y : eq. to 'unroll'. : (+) #@cli unroll : _axis={ x | y | z | c } : (+) #@cli : Unroll selected images along specified axis. #@cli : (eq. to 'y'). #@cli : Default value: 'axis=y'. #@cli : $ (1,2,3;4,5,6;7,8,9) +unroll y #@cli upscale_smart : width[%],_height[%],_depth,_smoothness>=0,_anisotropy=[0,1],sharpening>=0 #@cli : Upscale selected images with an edge-preserving algorithm. #@cli : Default values: 'height=100%', 'depth=100%', 'smoothness=2', 'anisotropy=0.4' and 'sharpening=10'. #@cli : $ image.jpg rescale2d ,100 +upscale_smart 500%,500% append x upscale_smart : skip ${2=100%},${3=100%} check "${4=2}>=0 && ${5=0.4}>=0 && $5<=1 && ${6=10}>=0" e[^-1] "Upscale image$? to $1x$2x$3, with smoothness $4, anisotropy $5 and sharpening $6." foreach { w,h={w},{h} +r. $1,$2,$3,1,0 # Compute desired dimensions. if w<$w" && "h<$h # Test for downscaling rm. r. $1,$2,$3,100%,2 else rm. +diffusiontensors 0,$5,1.2,1.2 r[-2,-1] $1,$2,$3,100%,5 smooth.. .,$4 rm. ac "sharpen. $6,10",ycbcr_y fi } #@cli volumetric2d : _x[%],_y[%],_z[%],_separator_size>=0 #@cli : Convert selected 3D volumetric images into a 2D representation. #@cli : Default values: 'x=y=z=50%' and 'separator_size=0'. #@cli : $ image.jpg rescale2d 64 animate noise,0,100,50 cut 0,255 append z volumetric2d 50%,50%,50%,1 volumetric2d : skip ${1=50%},${2=50%},${3=50%} check "isint(${4=0},0)" e[^-1] "Convert 3D volumetric image$? into a 2D representation, with coordinates (${1-3}) and separator size $4." foreach { nm={n} {[w+d+$4,h+d+$4]},1,100%,{im} +slices[0] $3 j.. . rm. +rows[0] $2 permute. xzyc j.. .,0,{0,h+$4} rm. +columns[0] $1 permute. zyxc j.. .,{0,w+$4},0 k.. => $nm } #--------------------------------- # #@cli :: Filtering # #--------------------------------- #@cli bandpass : _min_freq[%],_max_freq[%] #@cli : Apply bandpass filter to selected images. #@cli : Default values: 'min_freq=0' and 'max_freq=20%'. #@cli : $ image.jpg bandpass 1%,3% #@cli : $$ https://gmic.eu/oldtutorial/_bandpass bandpass : skip ${1=0},${2=20%} e[^-1] "Apply bandpass filter [$1,$2] to image$?." foreach { 100%,100%,100% f. "sqrt((x/w-0.5)^2 + (y/h-0.5)^2 + (z/d-0.5)^2)" n. 0,1 ir. $1,$2 shift. {int(w/2)},{int(h/2)},{int(d/2)},0,2 fft.. *... . *[-2,-1] ifft rm. } #@cli bilateral : [guide],std_deviation_s[%]>=0,std_deviation_r[%]>=0,_sampling_s>=0,_sampling_r>=0 : \ # std_deviation_s[%]>=0,std_deviation_r[%]>=0,_sampling_s>=0,_sampling_r>=0 : (+) #@cli : Blur selected images by anisotropic (eventually joint/cross) bilateral filtering. #@cli : If a guide image is provided, it is used for drive the smoothing filter. #@cli : A guide image must be of the same xyz-size as the selected images. #@cli : Set 'sampling' arguments to '0' for automatic adjustment. #@cli : $ image.jpg repeat 5 { bilateral 10,10 } #@cli b : eq. to 'blur'. : (+) #@cli blur : std_deviation>=0[%],_boundary_conditions,_kernel : \ # axes,std_deviation>=0[%],_boundary_conditions,_kernel : (+) #@cli : Blur selected images by a deriche or gaussian filter (recursive implementation). #@cli : (eq. to 'b').\n #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'kernel' can be { 0:deriche | 1:gaussian }. #@cli : When specified, argument 'axes' is a sequence of { x | y | z | c }. #@cli : Specifying one axis multiple times apply also the blur multiple times. #@cli : Default values: 'boundary_conditions=1' and 'kernel=1'. #@cli : $ image.jpg +blur 5,0 +blur[0] 5,1 #@cli : $ image.jpg +blur y,10% #@cli : $$ https://gmic.eu/oldtutorial/_blur #@cli blur_angular : amplitude[%],_center_x[%],_center_y[%] #@cli : Apply angular blur on selected images. #@cli : Default values: 'center_x=center_y=50%'. #@cli : $ image.jpg blur_angular 2% #@cli : $$ https://gmic.eu/oldtutorial/_blur_angular blur_angular : skip ${2=50%},${3=50%} e[^-1] "Apply angular blur on image$?, with amplitude $1 and center point ($2,$3)." euclidean2polar $2,$3,1.3,1 foreach { 1,100% =. 1,50%,50% b. y,$1 convolve_fft.. . rm. } polar2euclidean $2,$3,1.3,1 #@cli blur_bloom : _amplitude>=0,_ratio>=0,_nb_iter>=0,_blend_operator={ + | max | min },\ # _kernel={ 0:deriche | 1:gaussian | 2:box | 3:triangle | 4:quadratic },\ # _normalize_scales={ 0 | 1 },_axes #@cli : Apply a bloom filter that blend multiple blur filters of different radii, #@cli : resulting in a larger but sharper glare than a simple blur. #@cli : When specified, argument 'axes' is a sequence of { x | y | z | c }. #@cli : Specifying one axis multiple times apply also the blur multiple times. #@cli : Reference: Masaki Kawase, "Practical Implementation of High Dynamic Range Rendering", GDC 2004. #@cli : Default values: 'amplitude=1', 'ratio=2', 'nb_iter=5', 'blend_operator=+', 'kernel=1', 'normalize_scales=0' \ # and 'axes=(all)' #@cli : $ image.jpg blur_bloom , blur_bloom : check "${1=1}>=0 && ${2=2}>=0 && isint(${3=5},0) && isint(${5=1},0,4) && isnum(${6=0})" skip "${4=+},${7=}" e[^-1] "Apply bloom effect on image$?, with amplitude $1, ratio $2, $3 iterations, blend operator '$4' and "\ ${"arg0 !$6,\"\",\"no \""}"scale normalization." if narg("$7") axes=$7, fi m "_bloom0 : b "$axes"$""1" m "_bloom1 : b "$axes"$""1,1,1" m "_bloom2 : boxfilter "$axes"{1+2*$""1},0,1" m "_bloom3 : boxfilter "$axes"{1+2*$""1},0,1,2" m "_bloom4 : boxfilter "$axes"{1+2*$""1},0,1,3" foreach { nm={n} mM:=[im,iM] [0] repeat $3 { sigma:=$1*($2^$>) +_bloom$5[0] $sigma if $6 n. $mM fi -$4[1,-1] } n. $mM k. => $nm } um _bloom0,_bloom1,_bloom2,_bloom3,_bloom4 #@cli blur_linear : amplitude1[%],_amplitude2[%],_angle,_boundary_conditions={ 0:dirichlet | 1:neumann } #@cli : Apply linear blur on selected images, with specified angle and amplitudes. #@cli : Default values: 'amplitude2=0', 'angle=0' and 'boundary_conditions=1'. #@cli : $ image.jpg blur_linear 10,0,45 #@cli : $$ https://gmic.eu/oldtutorial/_blur_linear blur_linear : skip ${2=0},${3=0},${4=1} e[^-1] "Apply linear blur on image$?, with angle $3 deg. and amplitudes ($1,$2)." std1:=ispercentage($1)?$1*max(w,h):$1 std2:=ispercentage($2)?$2*max(w,h):$2 stdM:=round(1.25*max($std1,$std2)) if $stdM<=0 return fi foreach { expand xy,$stdM,{$4!=0} {s=2*$stdM;[s,s]} gaussian. $1,$2,$3 normalize_sum. convolve_fft[0] [1] rm. shrink xy,$stdM } #@cli blur_radial : amplitude[%],_center_x[%],_center_y[%] #@cli : Apply radial blur on selected images. #@cli : Default values: 'center_x=center_y=50%'. #@cli : $ image.jpg blur_radial 2% #@cli : $$ https://gmic.eu/oldtutorial/_blur_radial blur_radial : skip ${2=50%},${3=50%} e[^-1] "Apply radial blur on image$?, with amplitude $1 and center point ($2,$3)." euclidean2polar $2,$3,5,1 b x,$1 polar2euclidean $2,$3,5,1 #@cli blur_selective : sigma>=0,_edges>0,_nb_scales>0 #@cli : Blur selected images using selective gaussian scales. #@cli : Default values: 'sigma=5', 'edges=0.5' and 'nb_scales=5'. #@cli : $ image.jpg noise 20 cut 0,255 +local[-1] repeat 4 { blur_selective , } done #@cli : $$ https://gmic.eu/oldtutorial/_blur_selective blur_selective : check "${1=5}>=0 && ${2=0.5}>=0 && isint(${3=5},1)" e[^-1] "Blur image$? using $3 selective gaussian scales, with sigma $1 and edges $2." foreach { nm={n} +gradient_norm +. 1 ^. {-max(0.01,$2)} quantize. {$3+1},0,1 min. {$3-1} ri. .. repeat $3 { +==. $> *. ... +[-2,-1] b.. {$1/($3+1)} } rm.. => $nm } #@cli boxfilter : size>=0[%],_order,_boundary_conditions,_nb_iter>=0 : \ # axes,size>=0[%],_order,_boundary_conditions,_nb_iter>=0 : (+) #@cli : Blur selected images by a box filter of specified size (fast recursive implementation). #@cli : 'order' can be { 0:smooth | 1:1st-derivative | 2:2nd-derivative }. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : When specified, argument 'axes' is a sequence of { x | y | z | c }. #@cli : Specifying one axis multiple times apply also the blur multiple times. #@cli : Default values: 'order=0', 'boundary_conditions=1' and 'nb_iter=1'. #@cli : $ image.jpg +boxfilter 5% #@cli : $ image.jpg +boxfilter y,3,1 #@cli bump2normal #@cli : Convert selected bumpmaps to normalmaps. #@cli : $ 300,300 circle 50%,50%,128,1,1 blur 5% bump2normal bump2normal : e[^-1] "Convert bumpmap$? to normalmap." foreach { channels 0 g xy,1 +f. 1 a c orientation * 127 + 128 round c 0,255 } #@cli closing : size>=0 : size_x>=0,size_y>=0,_size_z>=0 : \ # [kernel],_boundary_conditions,_is_real={ 0:binary-mode | 1:real-mode } #@cli : Apply morphological closing to selected images. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'size_z=1', 'boundary_conditions=1' and 'is_real=0'. #@cli : $ image.jpg +closing 10 closing : skip "${2=},${3=}" boundary,is_kernel,sel=1,0,^ if "isnum($1)" # Full square kernel if "['$2']==0 && ['$3']==0" sx,sy,sz=$1 e[0--4] "Apply morphological closing to image$?, with kernel of size "$sx"." else sx=$1 if ['$2']==0 sy=1 else sy=$2 fi if ['$3']==0 sz=1 else sz=$3 fi e[0--4] "Apply morphological closing to image$?, with "${sx}"x"${sy}"x"${sz}" kernel." fi elif ${"is_image_arg $1"}" && narg("${"pass$1 -1"}")==1" # Custom kernel pass$1 sx,sy,sz={w},{h},{d} is_kernel,sel,r0,r1,b0,b1,b2,b3=1,^-1,binary,real,dirichlet,neumann,periodic,mirror is_real=0 if ['$2']!=0 boundary:=cut($2,0,3) fi if ['$3']!=0 is_real=$3 fi e[0--3] "Apply morphological closing to image$? with kernel ["${"pass$1 -1"}"] and "${b$boundary}" boundary "\ "conditions, in "${r$is_real}" mode." else error[0--3] "Command 'closing': Invalid arguments '$*'." fi if $sx>0||$sy>0||$sz>0 sx1,sx2:="s=int(($sx-1)/2);[s,$sx-s-1]" sy1,sy2:="s=int(($sy-1)/2);[s,$sy-s-1]" sz1,sz2:="s=int(($sz-1)/2);[s,$sz-s-1]" foreach[$sel] { nm={n} if d>1 # 3D r {[w+$sx+1,h+$sy+1,d+$sz+1]},100%,0,$boundary,0.5,0.5,0.5 if $is_kernel pass. dilate.. .,0,$is_real erode.. .,0,$is_real rm. else dilate $sx,$sy,$sz erode $sx,$sy,$sz fi z {$sx1+1},{$sy1+1},{$sz1+1},{w-$sx2-2},{h-$sy2-2},{d-$sz2-2} => $nm elif h>1 # 2D r {[w+$sx+1,h+$sy+1]},1,100%,0,$boundary,0.5,0.5 if $is_kernel pass. dilate.. .,0,$is_real erode.. .,0,$is_real rm. else dilate $sx,$sy erode $sx,$sy fi z {$sx1+1},{$sy1+1},{w-$sx2-2},{h-$sy2-2} => $nm else # 1D r {w+$sx+1},1,1,100%,0,$boundary,0.5 if $is_kernel pass. dilate.. .,0,$is_real erode.. .,0,$is_real rm. else dilate $sx,1 erode $sx,1 fi z {$sx1+1},{w-$sx2-2} => $nm fi } fi if $is_kernel rm. fi #@cli closing_circ : _size>=0,_is_real={ 0 | 1 } #@cli : Apply circular dilation of selected images by specified size. #@cli : Default values: 'boundary_conditions=1' and 'is_real=0'. #@cli : $ image.jpg +closing_circ 7 closing_circ : check "$1>=0 && isbool(${2=0})" r0,r1=binary,real e[^-1] "Apply morphologicel closing of image$? by circular kernel of size $1, in "${r$2}" mode." if $1<2 return fi shape_circle $1 closing[^-1] .,$2 rm. #@cli compose_freq #@cli : Compose selected low and high frequency parts into new images. #@cli : $ image.jpg split_freq 2% mirror[-1] x compose_freq compose_freq : e[^-1] "Compose low and high frequency part$? into new images." repeat int($!/2) { +[$>,{$>+1}] } #@cli convolve : [mask],_boundary_conditions,_is_normalized={ 0 | 1 },_channel_mode,\ # _xcenter,_ycenter,_zcenter,_xstart,_ystart,_zstart,_xend,_yend,_zend,_xstride,_ystride,_zstride,\ # _xdilation,_ydilation,_zdilation,interpolation_type : (+) #@cli : Convolve selected images by specified mask. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'channel_mode' can be { 0:all | 1:one-for-one | 2:partial sum | 3:full sum }. #@cli : 'interpolation_type' can be { 0:nearest-neighbor | 1:linear }. #@cli : Default values: 'boundary_conditions=1', 'is_normalized=0', 'channel_mode=1', \ # 'xcenter=ycenter=zcenter=(undefined)', 'xstart=ystart=zstart=0', 'xend=yend=zend=(max-coordinates)', \ # 'xstride=ystride=zstride=1', 'xdilation=ydilation=zdilation=1' and 'interpolation_type=0'. #@cli : $ image.jpg (0,1,0;1,-4,1;0,1,0) convolve[-2] [-1] keep[-2] #@cli : $ image.jpg (0,1,0) resize[-1] 130,1,1,1,3 +convolve[0] [1] #@cli : $$ https://gmic.eu/oldtutorial/_convolve #@cli convolve_fft : [mask],_boundary_conditions #@cli : Convolve selected images with specified mask, in the fourier domain. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : $ image.jpg 100%,100% gaussian[-1] 20,1,45 +convolve_fft[0] [1] convolve_fft : check ${is_image_arg\ $1}" && isin(${2=2},0,1,2,3)" e[^-1] "Convolve image$? with mask $1, in the fourier domain." pass$1 store. kernel foreach { if w w0,h0,d0={w},{h},{d} $kernel if $2!=2 r[0] {[w#0,h#0,d#0]+2*round([w#1>1?w#1:0,h#1>1?h#1:0,d#1>1?d#1:0]/2)},100%,0,$2,0.5,0.5 fi r ${-max_whd},100%,0,0,0.5,0.5 r 100%,100%,100%,${-max_s} fft[1] fft[0] +*[1,2] +*[0,3] +[-2,-1] *[1,3] *[0,2] -[0,1] ifft rm. shift {-int(([w,h,d]-1)/2)},0,2 r $w0,$h0,$d0,100%,0,0,0.5,0.5 fi } #@cli correlate : [mask],_boundary_conditions,_is_normalized={ 0 | 1 },_channel_mode,\ # _xcenter,_ycenter,_zcenter,_xstart,_ystart,_zstart,_xend,_yend,_zend,_xstride,_ystride,_zstride,\ # _xdilation,_ydilation,_zdilation,interpolation_type : (+) #@cli : Correlate selected images by specified mask. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'channel_mode' can be { 0:all | 1:one-for-one | 2:partial sum | 3:full sum }. #@cli : 'interpolation_type' can be { 0:nearest-neighbor | 1:linear }. #@cli : Default values: 'boundary_conditions=1', 'is_normalized=0', 'channel_mode=1', \ # 'xcenter=ycenter=zcenter=-1', 'xstart=ystart=zstart=0', 'xend=yend=zend=(max-coordinates)', \ # 'xstride=ystride=zstride=1', 'xdilation=ydilation=zdilation=1' and 'interpolation_type=0'. #@cli : $ image.jpg (0,1,0;1,-4,1;0,1,0) correlate[-2] [-1] keep[-2] #@cli : $ image.jpg +crop 40%,40%,60%,60% +correlate[0] [-1],0,1 #@cli cross_correlation : [mask] #@cli : Compute cross-correlation of selected images with specified mask. #@cli : $ image.jpg +shift -30,-20 +cross_correlation[0] [1] cross_correlation : check ${is_image_arg\ $1} e[^-1] "Compute cross-correlation of image$? with mask $1." repeat $! { pass$1 0 l[$>,-1] { norm fft.. fft. [-2,-1] *.. [-5] *. [-6] -[-2,-1] *[-5,-3] *[-3,-2] +[-3,-2] ifft rm. } } #@cli curvature #@cli : Compute isophote curvatures on selected images. #@cli : $ image.jpg blur 10 curvature curvature : e[^-1] "Compute isophote curvatures on image$?." foreach { if d==1 +g xy,0 hessian... xxxyyy # ixx ixy iyy ix iy *... .. *[-4] . *[-4] -2 # ixx -2iyixy ixiyy ix iy +[-4,-3] *... .. # ixx -2ixiyixy+ix^2iyy ix iy sqr[-2,-1] *[-4] . +[-4,-3] # iy^2ixx-2ixiyixy+ix^2iyy ix^2 iy^2 +[-2,-1] +. 0.1 ^. 1.5 / # (iy^2ixx+2ixiyixy+ix^2iyy)/(ix^2+iy^2) else +inn +gradient_norm.. laplacian... # inn+iee inn in -[-3,-2] +. 0.1 /[-2,-1] # iee in +inn. laplacian.. - # iee/in fi } #@cli dct : _{ x | y | z }...{ x | y | z } : (no arg) #@cli : Compute the discrete cosine transform of selected images, optionally along the specified axes only. #@cli : Output images are always evenly sized, so this command may change the size of the selected images. #@cli : Default values: (no arg) #@cli : See also: ''idct''. #@cli : $ image.jpg +dct +idct[-1] abs[-2] +[-2] 1 log[-2] #@cli : $$ https://gmic.eu/oldtutorial/_dct-and-idct dct : skip ${1=0} ('"$1"') is_axes:="im>=_'x' && iM<=_'z'" if $is_axes e[0--3] "Compute discrete cosine transform of image$? along axes '$1'." repeat w { axis:=i[$>] if $axis==_'x' foreach[^-1] { if w>1 _dct fi } elif $axis==_'y' foreach[^-1] { if h>1 permute yxzc _dct permute yxzc fi } elif $axis==_'z' foreach[^-1] { if d>1 permute zxyc _dct permute yzxc fi } fi } rm. else rm. e[0--3] "Compute discrete cosine transform of image$?." noarg foreach { if w>1 _dct fi if h>1 permute yxzc _dct permute yxzc fi if d>1 permute zxyc _dct permute yzxc fi } fi # 1D direct transform (DCT-II) along the x-axis, for a single image. _dct : if w%2 r {w+1},100%,100%,100%,0,1 fi s x l[1--1:2] { a x mirror x } mv[1] $! a x fft x 100%,1,1,1,2*cos(-x*pi/(2*w)) *[0,2] 100%,1,1,1,2*sin(-x*pi/(2*w)) *[1,2] - +z[0] 0,0 /. {sqrt(2)} j.. .,0,0,0 rm. * {sqrt(2/w)} # Make the transform orthogonal. #@cli deblur : amplitude[%]>=0,_nb_iter>=0,_dt>=0,_regul>=0,_regul_type={ 0:Tikhonov | 1:meancurv. | 2:TV } #@cli : Deblur image using a regularized Jansson-Van Cittert algorithm. #@cli : Default values: 'nb_iter=10', 'dt=20', 'regul=0.7' and 'regul_type=1'. #@cli : $ image.jpg blur 3 +deblur 3,40,20,0.01 deblur : check "${2=10}>=0 && ${3=20}>=0 && ${4=0.7}>=0" skip ${5=1} e[^-1] "Deblur image$? with a regularized Jansson-Van Cittert algorithm, with sigma $1, $2 iterations, time step $3 and regularization $4." foreach { nm={n} [0] repeat $2 { if $5>=2 +curvature. # TV regularization. elif $5>=1 +iee. # Meancurv. regularization. else +laplacian. # Tikhonov regularization. fi *. $4 +b.. $1 -. [-4] # Data fidelity term. -[-2,-1] *. {$3/(0.0001+max(abs(im),abs(iM)))} # Adaptive time step. +[-2,-1] # Update image. } rm.. => $nm } #@cli deblur_goldmeinel : sigma>=0,_nb_iter>=0,_acceleration>=0,_kernel_type={ 0:deriche | 1:gaussian }. #@cli : Deblur selected images using Gold-Meinel algorithm #@cli : Default values: 'nb_iter=8', 'acceleration=1' and 'kernel_type=1'. #@cli : $ image.jpg +blur 1 +deblur_goldmeinel[-1] 1 ###### : (contribution from Jérôme Boulanger). deblur_goldmeinel : check "$1>=0 && ${2=8}>=0 && ${3=1}>=0" skip ${4=1} e[^-1] "Deblur image$? using Gold-Meinel algorithm, with sigma $1, $2 iterations, acceleration $3 and "\ ${arg0\ !$4,"",quasi-}"gaussian kernel." foreach { [0] repeat $2 { +b. $1,1,$4 +/[0,-1] rm.. ^. $3 *[-1,-2] # u *= f / Hu } rm[0] } #@cli deblur_richardsonlucy : sigma>=0, nb_iter>=0, _kernel_type={ 0:deriche | 1:gaussian }. #@cli : Deblur selected images using Richardson-Lucy algorithm. #@cli : Default values: 'nb_iter=50' and 'kernel_type=1'. #@cli : $ image.jpg +blur 1 +deblur_richardsonlucy[-1] 1 ###### : (contribution from Jérôme Boulanger). deblur_richardsonlucy : check "$1>=0 && ${2=50}>=0" skip ${3=1} e[^-1] "Deblur image$? using Richardson-Lucy algorithm, with sigma $1, $2 iterations and "\ ${arg0\ !$3,"",quasi-}"gaussian kernel." foreach { [0] repeat $2 { +b. $1,1,{$3!=0} max. 1e-6 +/[0,-1] rm.. b. $1,1,{$3!=0} *[-1,-2] # u *= H ( f / Hu ) } rm[0] } #@cli deconvolve_fft : [kernel],_regularization>=0 #@cli : Deconvolve selected images by specified mask in the fourier space. #@cli : Default value: 'regularization>=0'. #@cli : $ image.jpg +gaussian 5 +convolve_fft[0] [1] +deconvolve_fft[-1] [1] deconvolve_fft : check ${is_image_arg\ $1}" && ${2=.001}>=0" e[^-1] "Deconvolve image$? with mask $1 and regularization $2, in the fourier domain." repeat $! { pass$1 0 l[$>,-1] { w2,h2,d2={0,int([w,h,d]/2)} r[1] [0],[0],[0],1,0,0,0.5,0.5,0.5,0.5 shift[1] -$w2,-$h2,-$d2,0,2 fft[0] fft[2] # a b a' b' +l[-1,-2] { sqr + + $2 } # a b a' b' (a'^2+b'^2+eps) +*[-4] ... # a b a' b' (a'^2+b'^2) ba' +*[-6] ... # a b a' b' (a'^2+b'^2) ba' ab' -[-2,-1] # a b a' b' (a'^2+b'^2) ba'-ab' *[-6,-4] # aa' b b' (a'^2+b'^2) ba'-ab' *[-4,-3] # aa' bb' (a'^2+b'^2) ba'-ab' +[-4,-3] # aa'+bb' (a'^2+b'^2) ba'-ab' /. .. /[-3,-2] # divide (aa'+bb') and (ba'-ab') by (a'^2+b'^2) ifft rm. } } #@cli deinterlace : _method={ 0 | 1 } #@cli : Deinterlace selected images ('method' can be { 0:standard or 1:motion-compensated }). #@cli : Default value: 'method=0'. #@cli : $ image.jpg +rotate 3,1,1,50%,50% resize 100%,50% resize 100%,200%,1,3,4 shift[-1] 0,1 add +deinterlace 1 deinterlace : skip ${1=0} e[^-1] "Deinterlace image$? with "${arg0\ !$1,motion-compensated,standard}" method." foreach { wh:=[w,h] s y a[0--1:2] y a[^0] y ri.. .,0 r 100%,200%,1,100%,5 if $1!=0 +displacement. ..,0.05 warp... .,1,1,1 rm. fi + / 2 c 0,255 r $wh } #@cli denoise : [guide],std_deviation_s[%]>=0,std_deviation_r[%]>=0,_patch_size>0,_lookup_size>0,_smoothness,\ # _fast_approx={ 0 | 1 } : \ # std_deviation_s[%]>=0,std_deviation_r[%]>=0,_patch_size>0,_lookup_size>0,_smoothness,\ # _fast_approx={ 0 | 1 } : (+) #@cli : Denoise selected images by non-local patch averaging. #@cli : Default values: 'patch_size=5', 'lookup_size=6' and 'smoothness=1'. #@cli : $ image.jpg +denoise 5,5,8 #@cli denoise_haar : _threshold>=0,_nb_scales>=0,_cycle_spinning>0 #@cli : Denoise selected images using haar-wavelet thresholding with cycle spinning. #@cli : Set 'nb_scales==0' to automatically determine the optimal number of scales. #@cli : Default values: 'threshold=1.4', 'nb_scale=0' and 'cycle_spinning=10'. #@cli : $ image.jpg noise 20 cut 0,255 +denoise_haar[-1] 0.8 denoise_haar : check "${1=1.4}>=0 && isint(${2=0},0) && isint(${3=10},1)" e[^-1] "Denoise image$? using haar-wavelet thresholding, with threshold $1, "\ ${arg0\ ($2>0),auto,$2}" scales and $3 spinning cycles." foreach { nm={n} nb_scales:=min($2?$2:32,int(log2(min(w,h))-1)) w,h,d={w},{h},{d} sigma=${-std_noise} r {round(w,2^($nb_scales+1),1)},{round(h,2^($nb_scales+1),1)},{d==1?1:round(d,2^($nb_scales+1),1)},100%,0,0 +f 0 repeat $3 { dx:=u(4*$nb_scales) dy:=u(4*$nb_scales) dz:=$d==1?0:v(4*$nb_scales) +shift[0] $dx,$dy,$dz,0,2 haar. $nb_scales threshold. {$1*$sigma},1 ihaar. $nb_scales shift. {-$dx},{-$dy},{-$dz},0,2 +[-2,-1] } rm[0] / $3 r $w,$h,$d,100%,0 => $nm } #@cli denoise_cnn : _noise_type={ 0:soft | 1:heavy | 2:heavy (faster) | 3:poisson+gaussian | \ # 4:poisson+gaussian2 },_patch_size>0 #@cli : Denoise selected images using a convolutional neural network (CNN). #@cli : Input value range should be [0,255]. Output value range is [0,255]. #@cli : Default value: 'patch_size=64'. #@cli : $ image.jpg noise 20 cut 0,255 +denoise_cnn denoise_cnn : skip "${1=0},${2=64}" N=$! if isint($1,0,4) check "isint($2,1)" type=$1 else type=0 noarg fi s0,s1,s2,s3,s4=soft,heavy,heavy,poisson+gaussian,poisson+gaussian2 e[^-1] "Denoise image$? using a convolutional neural network (for "${s$type}"-noise), with patch size $2." # Load denoising network. if 0${_is_denoise_cnn_$type} ${_denoise_cnn_$type} else l[] { l[] { input_cached gmic_denoise_cnn.gmz onfail error[0--5] "Command 'denoise_cnn': Unable to load network file 'gmic_denoise_cnn.gmz'." } k[$type] unserialize +store _denoise_cnn_$type _is_denoise_cnn_$type=1 } fi ps=$2 replace_str. "nn_layer_","nn_" # Upward compatibility replace_str. "nn_input X,32,32,1,3","nn_input X,"$ps","$ps",1,3" # Set patch size # Preserve upward compatibility. if !$1 replace_str. "nn_crop OUT,OUT0,2,2,0,29,29,0","nn_crop OUT,OUT0,2,2,0,0,29,29,0,2" elif inrange($1,1,4) replace_str. "nn_crop OUT,OUT0,2,2,0,29,29,0","nn_crop OUT,OUT0,2,2,0,0,29,29,0,2" replace_str. ",1,1,0 ",",1,1,0,1,0 " replace_str. ",1,1,1 ",",1,1,0,1,1 " fi run {t} rm[-2,-1] # Process images. repeat $N { l[$>,$N--1] { w,h:=w#0,h#0 to_color[0] if $type==3 srgb2rgb[0] elif $type==4 srgb2rgb[0] /[0] 255 fi # Denoise tiles. img2patches[0] $ps,6,3 1,1,{0,d},1,*${-nn_lib}"X = crop(#0,0,0,z,0,$ps,$ps,1,3);"${_nn_forward}"draw(#0,OUT0,0,0,z,0,$ps,$ps,1,3)" rm. patches2img[0] $w,$h,6,30% c[0] 0,255 if $type==3 rgb2srgb[0] elif $type==4 *[0] 255 rgb2srgb[0] fi } } rm[$N--1] #@cli denoise_patchpca : _strength>=0,_patch_size>0,_lookup_size>0,_spatial_sampling>0 #@cli : Denoise selected images using the patch-pca algorithm. #@cli : Default values: 'patch_size=7', 'lookup_size=11', 'details=1.8' and 'spatial_sampling=5'. #@cli : $ image.jpg +noise 20 cut[-1] 0,255 +denoise_patchpca[-1] , denoise_patchpca : check "${1=1.8} && $1>=0 && isint(${2=7},1) && isint(${3=11},1) && isint(${4=5},1)" e[^-1] "Denoise image$? using patch-pca, with strength $1, patch size $2, lookup size $3 and spatial sampling $4." foreach { nm={n} N2:=$2^2 M2:=$3^2 stdnoise=${-std_noise} 100%,100%,1,100% => aggreg 100%,100% => weights f[0] "* begin( n1 = int($2/2); n2 = $2 - n1 - 1; m1 = int($3/2); m2 = $3 - m1 - 1; patch(x,y) = crop(x - n1,y - n1,0,c,$2,$2,1,1,1); ngauss(x) = exp(-x*x/(2*n1*n1)); ref(vector"$N2"(0),zero); ref(vector"$N2"(0),mask); for (l = 0; q = -n1, q<=n2, ++q, for (p = -n1, p<=n2, ++p, mask[l++] = ngauss(p)*ngauss(q) ) ) ); if (!(x%$4) && !(y%$4), # Sub-sampling ref(patch(x,y),X); # Build correlation matrix for PCA. ref(vector"{$N2*$N2}"(0),M); for (q = -m1, q<=m2, ++q, for (p = -m1, p<=m2, ++p, ref(patch(x + p,y + q) - X,Xk); M += mul(Xk,Xk,"$N2"); ) ); M/="$M2"; eig = eig(M); lambda = sqrt(abs(eig[0,"$N2"])); # Determine number of lambdas to keep and project neighboring patches. for (k = 0, k=$1*"$stdnoise", ++k); Qt = eig["$N2","{$N2*$N2}"]; Q = transpose(Qt,"$N2"); for (q = -m1, q<=m2, ++q, for (p = -m1, p<=m2, ++p, pY = Qt*(patch(x + p,y + q) - X); copy(pY[k],zero[0],size(pY) - k); (Y = Q*pY)+=X; draw(#"$aggreg",Y,x + p - n1,y + q - n1,0,c,$2,$2,1,1,-1,mask); draw(#"$weights",mask,x + p - n1,y + q - n1,0,c,$2,$2,1,1,-1); ) ); 0);0" max[weights] 0.01 /[aggreg,weights] k[aggreg] => $nm } #@cli deriche : std_deviation>=0[%],order={ 0 | 1 | 2 },axis={ x | y | z | c },_boundary_conditions : (+) #@cli : Apply Deriche recursive filter on selected images, along specified axis and with #@cli : specified standard deviation, order and boundary conditions. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default value: 'boundary_conditions=1'. #@cli : $ image.jpg deriche 3,1,x #@cli : $ image.jpg +deriche 30,0,x deriche[-2] 30,0,y add #@cli : $$ https://gmic.eu/oldtutorial/_deriche #@cli dilate : size>=0 : size_x>=0,size_y>=0,size_z>=0 : \ # [kernel],_boundary_conditions,_is_real={ 0:binary-mode | 1:real-mode } : (+) #@cli : Dilate selected images by a rectangular or the specified structuring element. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'size_z=1', 'boundary_conditions=1' and 'is_real=0'. #@cli : $ image.jpg +dilate 10 #@cli dilate_circ : _size>=0,_boundary_conditions,_is_real={ 0 | 1 } #@cli : Apply circular dilation of selected images by specified size. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'boundary_conditions=1' and 'is_real=0'. #@cli : $ image.jpg +dilate_circ 7 dilate_circ : check "$1>=0 && isint(${2=1},0,3) && isbool(${3=0})" r0,r1=binary,real e[^-1] "Apply circular dilation of image$? by size $1, boundary conditions $2, in "${r$3}" mode." if $1<2 return fi shape_circle $1 dilate[^-1] .,$2,$3 rm. #@cli dilate_oct : _size>=0,_boundary_conditions,_is_real={ 0 | 1 } #@cli : Apply octagonal dilation of selected images by specified size. #@cli : Default values: 'boundary_conditions=1' and 'is_real=0'. #@cli : $ image.jpg +dilate_oct 7 dilate_oct : check "$1>=0 && isint(${2=1},0,3) && isbool(${3=0})" r0,r1=binary,real e[^-1] "Apply octagonal dilation of image$? by size $1, boundary conditions $2, in "${r$3}" mode." if $1<2 return fi if $1&1 ss=$1 else ss:=$1+1 fi i[0] (0,1,0;1,1,1;0,1,0) i[1] (1,1,1;1,1,1;1,1,1) repeat $!-2 { r:=round(($ss-1)*sqrt(2)/(1+sqrt(2))/2) q:=round(($ss-1)/(1+sqrt(2))/2) if $r>0 repeat $r { dilate. [0],$2,$3 } fi if $q>0 repeat $q { dilate. [1],$2,$3 } fi mv. 2 } rm[0,1] _kr_circle : if !($1%2) 2,2,1,1,1 else 1,1,1,1,1 fi r. $1,$1,1,1,0,0,0.5,0.5 distance. 1 round. 0.5 ir. 0,{$1/2} _jf_circle : {round($1)},{round($1)} center:=0.5*(w-1) f. "sqrt((x - $center)^2 + (y - $center)^2)" if !(w%2) round. 0.0001,-1 t1:=sqrt(((round($1)-1)/2)^2+0.25) t2:=sqrt(((round($1)+1)/2)^2+0.25) k:=$1-round($1)+0.5 t:=$t1+($t2-$t1)*$k ir. 0,$t else ir. 0,{$1/2-0.25} fi #@cli dilate_threshold : size_x>=1,size_y>=1,size_z>=1,_threshold>=0,_boundary_conditions #@cli : Dilate selected images in the (X,Y,Z,I) space. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'size_y=size_x', 'size_z=1', 'threshold=255' and 'boundary_conditions=1'. dilate_threshold : check "isint($1,1) && isint(${2=$1},1) && isint(${3=1},1) && ${4=255}>=0 && isbool(${5=1})" e[^-1] "Dilate image$? with mask $1x$2x$3, threshold $4 and "${arg0\ $5,dirichlet,neumann}" boundary conditions." l[] { dx1:=int($1/2) dx2:=$1-$dx1-1 dy1:=int($2/2) dy2:=$2-$dy1-1 dz1:=int($3/2) dz2:=$3-$dz1-1 (-$dx1,$dx1) (-$dy1;$dy1) (-$dz1/$dz1) r $1,$2,$3,1,3 a c round r {w*h*d},3,1,1,-1 transpose. i.. 1,100%,1,1,254 1,100%,1,1,255 a x ('{^}') rm.. replace_str "254,","(v=j(" replace_str ",255",",0,0,$5);abs(v-i)<=$4?v:-1e20)" list={t} rm } f max($list) #@cli divergence #@cli : Compute divergence of selected vector fields. #@cli : $ image.jpg luminance +gradient append[-2,-1] c divergence[-1] divergence : e[^-1] "Compute divergence of vector field$?." foreach { if s==1 g x,0 elif s==2 s c g.. x,0 g. y,0 + elif s==3 s c g... x,0 g.. y,0 g. z,0 + else error[] "Command '$0': Cannot compute divergence of image ["$>"] (has "{s}">3 channels)." fi } #@cli dog : _sigma1>=0[%],_sigma2>=0[%] #@cli : Compute difference of gaussian on selected images. #@cli : Default values: 'sigma1=2%' and 'sigma2=3%'. #@cli : $ image.jpg dog 2,3 dog : check "${1=2%}>=0 && ${2=3%}>=0" e[^-1] "Compute difference of gaussian on image$?, with standard deviations $1 and $2." foreach { [0] parallel "b[0] $1","b[1] $2" - abs } #@cli diffusiontensors : _sharpness>=0,0<=_anisotropy<=1,_alpha[%],_sigma[%],is_sqrt={ 0 | 1 } #@cli : Compute the diffusion tensors of selected images for edge-preserving smoothing algorithms. #@cli : Default values: 'sharpness=0.7', 'anisotropy=0.3', 'alpha=0.6', 'sigma=1.1' and 'is_sqrt=0'. #@cli : $ image.jpg diffusiontensors 0.8 abs pow 0.2 #@cli : $$ https://gmic.eu/oldtutorial/_diffusiontensors diffusiontensors : check "${1=0.7}>=0 && ${2=0.3}>=0 && $2<=1" skip ${3=0.6},${4=1.1},${5=0} e[^-1] "Compute diffusion tensors for image$?, with sharpness $1, anisotropy $2, alpha $3 and sigma $4." p1:=($5?0.5:1)*max($1,1e-2) p2:=$p1/(1e-7+1-$2) b $3 n 0,255 structuretensors 0 b $4 foreach { eigen max.. 0 if s==2 s.. c +[-3,-2] +.. 1 +^.. -$p1 ^... -$p2 a[-3,-1] c # 2D else s.. c +[-4--2] +.. 1 +^.. -$p1 r. 100%,100%,100%,2 ^... -$p2 a[-3,-1] c # 3D fi eigen2tensor } #@cli edges : _threshold[%]>=0 #@cli : Estimate contours of selected images. #@cli : Default value: 'edges=15%' #@cli : $ image.jpg +edges 15% edges : skip ${1=15%} e[^-1] "Estimate image contours of image$?, with threshold $1." gradient_norm b 0.5 >= $1 distance 0 equalize negate c 30%,70% n 0,1 #@cli erode : size>=0 : size_x>=0,size_y>=0,_size_z>=0 : \ # [kernel],_boundary_conditions,_is_real={ 0:binary-mode | 1:real-mode } : (+) #@cli : Erode selected images by a rectangular or the specified structuring element. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'size_z=1', 'boundary_conditions=1' and 'is_real=0'. #@cli : $ image.jpg +erode 10 #@cli erode_circ : _size>=0,_boundary_conditions,_is_real={ 0 | 1 } #@cli : Apply circular erosion of selected images by specified size. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'boundary_conditions=1' and 'is_real=0'. #@cli : $ image.jpg +erode_circ 7 erode_circ : check "$1>=0 && isint(${2=1},0,3) && isbool(${3=0})" r0,r1=binary,real e[^-1] "Apply circular erosion of image$? by size $1, boundary conditions $2, in "${r$3}" mode." if $1<2 return fi shape_circle $1 erode[^-1] .,$2,$3 rm. #@cli erode_oct : _size>=0,_boundary_conditions,_is_real={ 0 | 1 } #@cli : Apply octagonal erosion of selected images by specified size. #@cli : Default values: 'boundary_conditions=1' and 'is_real=0'. #@cli : $ image.jpg +erode_oct 7 erode_oct : check "$1>=0 && isint(${2=1},0,3) && isbool(${3=0})" r0,r1=binary,real e[^-1] "Apply octagonal erosion of image$? by size $1, boundary conditions $2, in "${r$3}" mode." if $1<2 return fi if $1&1 ss=$1 else ss:=$1+1 fi i[0] (0,1,0;1,1,1;0,1,0) i[1] (1,1,1;1,1,1;1,1,1) repeat $!-2 { r:=round(($ss-1)*sqrt(2)/(1+sqrt(2))/2) q:=round(($ss-1)/(1+sqrt(2))/2) if $r>0 repeat $r { erode. [0],$2,$3 } fi if $q>0 repeat $q { erode. [1],$2,$3 } fi mv. 2 } rm[0,1] #@cli erode_threshold : size_x>=1,size_y>=1,size_z>=1,_threshold>=0,_boundary_conditions #@cli : Erode selected images in the (X,Y,Z,I) space. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'size_y=size_x', 'size_z=1', 'threshold=255' and 'boundary_conditions=1'. erode_threshold : check "isint($1,1) && isint(${2=$1},1) && isint(${3=1},1) && ${4=255}>=0 && isbool(${5=1})" e[^-1] "Erode image$? with mask $1x$2x$3, threshold $4 and "${arg0\ $5,dirichlet,neumann}" boundary conditions." l[] { dx1:=int($1/2) dx2:=$1-$dx1-1 dy1:=int($2/2) dy2:=$2-$dy1-1 dz1:=int($3/2) dz2:=$3-$dz1-1 (-$dx1,$dx1) (-$dy1;$dy1) (-$dz1/$dz1) r $1,$2,$3,1,3 a c round r {w*h*d},3,1,1,-1 transpose. i.. 1,100%,1,1,254 1,100%,1,1,255 a x ('{^}') rm.. replace_str "254,","(v=j(" replace_str ",255",",0,0,$5);abs(v-i)<=$4?v:1e20)" list={t} rm } f min($list) #@cli fft : _{ x | y | z }...{ x | y | z } : (+) #@cli : Compute the direct fourier transform (real and imaginary parts) of selected images, #@cli : optionally along the specified axes only. #@cli : See also: ''ifft''. #@cli : $ image.jpg luminance +fft append[-2,-1] c norm[-1] log[-1] shift[-1] 50%,50%,0,0,2 #@cli : $ image.jpg w2:=int(w/2) h2:=int(h/2) fft shift $w2,$h2,0,0,2 ellipse $w2,$h2,30,30,0,1,0 \ # shift -$w2,-$h2,0,0,2 ifft remove[-1] #@cli : $$ https://gmic.eu/oldtutorial/_fft #@cli g : eq. to 'gradient'. : (+) g : _gmic_s="$?" axes,scheme,boundary,is_noarg=${"_gradient_get_args $*"} v + _gradient $axes,$scheme,$boundary v - if $is_noarg noarg fi #@cli gradient : { x | y | z | c }...{ x | y | z | c },_scheme,_boundary_conditions : (no arg) #@cli : Compute the gradient components (first derivatives) of selected images, along specified axes. #@cli : (eq. to 'g').\n #@cli : 'scheme' can be { -1:backward | 0:centered | 1:forward | 2:sobel | 3:rotation-invariant (default) | \ # 4:deriche | 5:vanvliet }. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : (no arg) compute all significant components. #@cli : Default values: 'scheme=0' and 'boundary_conditions=1'. #@cli : $ image.jpg gradient #@cli : $$ https://gmic.eu/oldtutorial/_gradient gradient : _gmic_s="$?" axes,scheme,boundary,is_noarg=${"_gradient_get_args $*"} v + _gradient $axes,$scheme,$boundary v - if $is_noarg noarg fi # Parse and check arguments. _gradient_get_args : skip "${1=},${2=},${3=}" is_noarg=0 l[] { if ['$1']==0 axes,scheme,boundary=,0,1 is_noarg=1 # No 1st arg else is_axes:="s=['$1'];fill(s,k,isin(s[k],_'x',_'y',_'z',_'c'));min(s)" if !$is_axes axes,scheme,boundary=,0,1 is_noarg=1 # Invalid 1st arg elif ['$2']==0 axes,scheme,boundary=$1,0,1 # No 2nd arg else is_scheme:="isint($2,-1,5)" if !$is_scheme axes,scheme,boundary=,0,1 is_noarg=1 # Invalid 2nd arg elif ['$3']==0 axes,scheme,boundary=$1,$2,1 # No 3rd arg else is_boundary:="isint($3,0,3)" if !$is_boundary axes,scheme,boundary=,0,1 is_noarg=1 # Invalid 3rd arg else axes,scheme,boundary=${1-3} fi fi fi fi onfail axes,scheme,boundary=,0,1 is_noarg=1 # Invalid arguments } u $axes,$scheme,$boundary,$is_noarg # Main command. _gradient : axes,scheme,boundary=${1=},${2=},${3=} # Display log message. if ['$axes']!=0 s_axes=" along axes '"$axes"'" fi s0,s1,s2,s3,s4,s5,s6=backward,centered,forward,sobel,rotation-invariant,deriche,vanvliet s_scheme=${s{$scheme+1}} s0,s1,s2,s3=dirichlet,neumann,periodic,mirror s_boundary=${s$boundary} e[0--3] "Compute gradient of image"$_gmic_s$s_axes", with "$s_scheme" scheme and "$s_boundary" boundary conditions." sx,sy,sz,sc=",",;,/,^ # Process images. foreach { nm={n} laxes=$axes if ['$laxes']==0 if d>1 laxes=xyz else laxes=xy fi fi ('$laxes') repeat w { a={`i[#1,$>]`} s=${s$a} if 0${gradient_$a}>0 [gradient_$a] elif $scheme==-1 # Backward (-1${s}1${s}0) +correlate[0] .,$boundary rm.. elif !$scheme # Centered (-0.5${s}0${s}0.5) +correlate[0] .,$boundary rm.. elif $scheme==1 # Forward (0${s}-1${s}1) +correlate[0] .,$boundary rm.. elif $scheme==2 # Sobel if _'$a'==_'x' (-1,0,1;-2,0,2;-1,0,1) elif _'$a'==_'y' (-1,-2,-1;0,0,0;1,2,1) else (-0.5${s}0${s}0.5) fi +correlate[0] .,$boundary rm.. elif $scheme==3 # Rotation-invariant A,B:=[0.25*(2-sqrt(2)),0.5*(sqrt(2)-1)] if _'$a'==_'x' (-$A,0,$A;-$B,0,$B;-$A,0,$A) elif _'$a'==_'y' (-$A,-$B,-$A;0,0,0;$A,$B,$A) else (-0.5${s}0${s}0.5) fi +correlate[0] .,$boundary rm.. else # Deriche/Vanvliet s4,s5=deriche,vanvliet com=${s$scheme} if $boundary<2 +$com[0] 0,1,$a,$boundary else if _'$a'==_'x' +r[0] {0,[w+2,h,d,s]},0,$boundary,0.5 $com. 0,1,$a shrink. x,1 elif _'$a'==_'y' +r[0] {0,[w,h+2,d,s]},0,$boundary,0,0.5 $com. 0,1,$a shrink. y,1 elif _'$a'==_'z' +r[0] {0,[w,h,d+2,s]},0,$boundary,0,0,0.5 $com. 0,1,$a shrink. z,1 fi fi fi => gradient_$a } rm[0,1] =>[^] $nm } u $is_noaarg #@cli gradient_norm #@cli : Compute gradient norm of selected images. #@cli : $ image.jpg gradient_norm equalize #@cli : $$ https://gmic.eu/oldtutorial/_gradient_norm gradient_norm : e[^-1] "Compute gradient norm of image$?." foreach { g sqr s c + sqrt } #@cli gradient_orientation : _dimension={ 1 | 2 | 3 } #@cli : Compute N-d gradient orientation of selected images. #@cli : Default value: 'dimension=3'. #@cli : $ image.jpg +gradient_orientation 2 gradient_orientation : check "isint(${1=3},1,3)" e[^-1] "Compute $1-d gradient orientation of image$?." foreach { if $1==1 g x +abs. +. 1e-8 -/ elif $1==2 g xy +sqr +[-2,-1] +. 1e-8 sqrt. /... . /[-2,-1] else g xyz +sqr +[-3--1] +. 1e-8 sqrt. /[-4,-3] . /[-2,-1] fi } #@cli guided : [guide],radius[%]>=0,regularization[%]>=0 : radius[%]>=0,regularization[%]>=0 : (+) #@cli : Blur selected images by guided image filtering. #@cli : If a guide image is provided, it is used to drive the smoothing process. #@cli : A guide image must be of the same xyz-size as the selected images. #@cli : This command implements the filtering algorithm described in: #@cli : He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering", #@cli : IEEE Transactions on Pattern Analysis and Machine Intelligence, vol.35, no.6, pp.1397,1409, June 2013 #@cli : $ image.jpg +guided 5,400 #@cli haar : scale>0 #@cli : Compute the direct haar multiscale wavelet transform of selected images. #@cli : See also: ''ihaar''. #@cli : $$ https://gmic.eu/oldtutorial/_haar haar : check "isint(${1=1},0)" e[^-1] "Compute haar transform of image$? with $1 scales." foreach { _haar repeat $1-1 { w,h,d:="vmax(0,round([w,h,d]/2^(1+"$>"))-1)" +z 0,0,0,$w,$h,$d _haar. j.. . rm. } } _haar : # Mono-scale direct haar transform. _haar_x _haar_y _haar_z _haar_x : # Direct haar transform along the x-axis. if w<=1 return fi if w%2 error[0--6] "Command 'haar': Invalid image width="{w}" (is not even)." fi +shift -1 r 50% +-[1] [0] +[0,1] / {sqrt(2)} a x _haar_y : # Direct haar transform along the y-axis. if h<=1 return fi if h%2 error[0--6] "Command 'haar': Invalid image height="{h}" (is not even)." fi +shift 0,-1 r 100%,50% +-[1] [0] +[0,1] / {sqrt(2)} a y _haar_z : # Direct haar transform along the z-axis. if d<=1 return fi if d%2 error[0--6] "Command 'haar': Invalid image depth="{h}" (is not even)." fi +shift 0,0,-1 r 100%,100%,50% +-[1] [0] +[0,1] / {sqrt(2)} a z #@cli heat_flow : _nb_iter>=0,_dt,_keep_sequence={ 0 | 1 } #@cli : Apply iterations of the heat flow on selected images. #@cli : Default values: 'nb_iter=10', 'dt=30' and 'keep_sequence=0'. #@cli : $ image.jpg +heat_flow 20 heat_flow : skip ${1=10},${2=30},${3=0} e[^-1] "Apply $1 iterations of the heat flow on image$?, with time step $2." pde_flow $1,$2,laplacian,$3 #@cli hessian : { xx | xy | xz | yy | yz | zz }...{ xx | xy | xz | yy | yz | zz },_boundary_conditions : (no arg) : #@cli : Compute the hessian components (second derivatives) of selected images along specified axes. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : (no arg) compute all significant components. #@cli : Default value: 'boundary_conditions=1'. #@cli : $ image.jpg hessian hessian : skip "${1=},${2=}" # Parse arguments. l[] { if ['$1']==0 axes,boundary=,1 noarg # No 1st arg else is_axes:="s=['$1'];fill(s,k,isin(s[k],_'x',_'y',_'z'));min(s)" if !$is_axes axes,boundary=,1 noarg # Invalid 1st arg elif ['$2']==0 axes,boundary=$1,1 # No 2nd arg else is_boundary:="isint($2,0,3)" if !$is_boundary axes,boundary=,1 noarg # Invalid 2nd arg else axes,boundary=$1,$2 fi fi fi onfail axes,boundary=,1 noarg # Invalid arguments } # Display log message. if ['$axes']!=0 s_axes=" along axes '"$axes"'" fi s0,s1,s2,s3=dirichlet,neumann,periodic,mirror s_boundary=${s$boundary} e[^-1] "Compute hessian of image$?"$s_axes", with "$s_boundary" boundary conditions." # Process images. foreach { nm={n} laxes=$axes if ['$laxes']==0 if d>1 laxes=xxxyxzyyyzzz else laxes=xxxyyy fi fi ('$laxes') repeat int(w/2) { a1,a2={`i[#1,2*$>]`},{`i[#1,2*$>+1]`} if _'$a1'>_'$a2' a1,a2=$a2,$a1 fi if 0${hessian_$a1$a2}>0 [hessian_$a1$a2] elif _'$a1'==_'x'" && "_'$a2'==_'x' (1,-2,1) +correlate[0] .,$boundary rm.. elif _'$a1'==_'x'" && "_'$a2'==_'y' (0.25,0,-0.25;0,0,0;-0.25,0,0.25) +correlate[0] .,$boundary rm.. elif _'$a1'==_'x'" && "_'$a2'==_'z' (0.25,0,-0.25/0,0,0/-0.25,0,0.25) +correlate[0] .,$boundary rm.. elif _'$a1'==_'y'" && "_'$a2'==_'y' (1;-2;1) +correlate[0] .,$boundary rm.. elif _'$a1'==_'y'" && "_'$a2'==_'z' (0.25;0;-0.25/0;0;0/-0.25;0;0.25) +correlate[0] .,$boundary rm.. elif _'$a1'==_'z'" && "_'$a2'==_'z' (1/-2/1) +correlate[0] .,$boundary rm.. fi => hessian_$a1$a2 } rm[0,1] =>[^] $nm } u $is_noaarg #@cli idct : _{ x | y | z }...{ x | y | z } : (no arg) #@cli : Compute the inverse discrete cosine transform of selected images, optionally along the specified axes only. #@cli : Output images are always evenly sized, so this command may change the size of the selected images. #@cli : (dct images obtained with the 'dct' command are evenly sized anyway). #@cli : Default values: (no arg) #@cli : See also: ''dct''. #@cli : $$ https://gmic.eu/oldtutorial/_dct-and-idct idct : skip ${1=0} ('"$1"') is_axes:="im>=_'x' && iM<=_'z'" if $is_axes e[0--3] "Compute inverse discrete cosine transform of image$? along axes '$1'." repeat w { axis:=i[$>] if $axis==_'x' foreach[^-1] { if w>1 _idct fi } elif $axis==_'y' foreach[^-1] { if h>1 permute yxzc _idct permute yxzc fi } elif $axis==_'z' foreach[^-1] { if d>1 permute zxyc _idct permute yzxc fi } fi } rm. else rm. e[0--3] "Compute inverse discrete cosine transform of image$?." noarg foreach { if w>1 _idct fi if h>1 permute yxzc _idct permute yxzc fi if d>1 permute zxyc _idct permute yzxc fi } fi # 1D inverse transform (DCT-III) along the x-axis, for a single image. _idct : if w%2 r {w+1},100%,100%,100%,0,0 fi / {sqrt(2/w)} +z[0] 0,0 *. {sqrt(2)} j.. .,0,0,0 rm. # Make the transform orthogonal. +mirror x shift. 1 *. -1 100%,1,1,1,cos(x*pi/(2*w)) 100%,1,1,1,sin(x*pi/(2*w)) +*[0,3] +*[1,2] +[-2,-1] *[0,2] *[1,2] -[0,1] ifft x k[0] / 2 s x,2 mirror. x r[0] 200%,100%,100%,100%,4,0,0 r[1] 200%,100%,100%,100%,4,0,1 + #@cli iee #@cli : Compute gradient-orthogonal-directed 2nd derivative of image(s). #@cli : $ image.jpg iee iee : e[^-1] "Compute gradient-orthogonal-directed 2nd derivative of image$?." foreach { if d==1 +g xy,0 hessian... xxxyyy # ixx ixy iyy ix iy *... .. *[-4] . *[-4] -2 # ixx -2iyixy ixiyy ix iy +[-4,-3] *... .. # ixx -2ixiyixy+ix^2iyy ix iy sqr[-2,-1] *[-4] . +[-4,-3] # iy^2ixx-2ixiyixy+ix^2iyy ix^2 iy^2 +[-2,-1] +. 1e-8 / # (iy^2ixx+2ixiyixy+ix^2iyy)/(ix^2+iy^2) else +inn laplacian.. - fi } #@cli ifft : _{ x | y | z }...{ x | y | z } : (+) #@cli : Compute the inverse fourier transform (real and imaginary parts) of selected images. #@cli : optionally along the specified axes only. #@cli : See also: ''fft''. #@cli : $$ https://gmic.eu/oldtutorial/_fft #@cli ihaar : scale>0 #@cli : Compute the inverse haar multiscale wavelet transform of selected images. #@cli : See also: ''haar''. #@cli : $$ https://gmic.eu/oldtutorial/_haar ihaar : check "isint(${1=1},0)" e[^-1] "Compute inverse haar transform of image$? with $1 scales." foreach { repeat $1-1 { w,h,d:="vmax(0,round([w,h,d]/2^(1+"$<"))-1)" +z 0,0,0,$w,$h,$d _ihaar. j.. . rm. } _ihaar } _ihaar : # Mono-scale inverse haar transform. _ihaar_x _ihaar_y _ihaar_z _ihaar_x : # Inverse haar transform along the x-axis. if w<=1 return fi if w%2 error[0--6] "Command 'ihaar': Invalid image width="{w}" (is not even)." fi s x,2 r 200% (-1,1) *[-2,-1] + / {sqrt(2)} _ihaar_y : # Inverse haar transform along the y-axis. if h<=1 return fi if h%2 error "Command 'ihaar': Invalid image height="{h}" (is not even)." fi s y,2 r 100%,200% (-1;1) r. {-2,w} *[-2,-1] + / {sqrt(2)} _ihaar_z : # Inverse haar transform along the z-axis. if d<=1 return fi if d%2 error "Command 'ihaar': Invalid image depth="{h}" (is not even)." fi s z,2 r 100%,100%,200% (-1/1) r. {-2,w},{-2,h} *[-2,-1] + / {sqrt(2)} #@cli ilaplacian : { nb_iterations>0 | 0 },_[initial_estimate] #@cli : Invert selected Laplacian images. #@cli : If given 'nb_iterations' is '0', inversion is done in Fourier space (single iteration), #@cli : otherwise, by applying 'nb_iterations' of a Laplacian-inversion PDE flow. #@cli : Note that the resulting inversions are just estimation of possible/approximated solutions. #@cli : Default values: 'nb_iterations=0' and '[initial_estimated]=(undefined)'. #@cli : $ image.jpg +laplacian +ilaplacian[-1] 0 ilaplacian : check "${1=0}>=0" skip "${2=}" is_estimate=${"is_image_arg $2"} nb_iter:=round($1) if !$nb_iter # Inversion in Fourier space if $is_estimate e[0--4] "Invert Laplacian image$? in Fourier space, with initial estimate $2." pass$2 1 ia=${-average_vectors} rm. else e[0--4] "Invert Laplacian image$? in Fourier space." ia=0 fi foreach { fft 100%,100%,1,1,"2*(cos(x*2*pi/w) + cos(y*2*pi/h)) - 4" =. 1 /[-3,-2] . rm. = 0 ifft rm. } + '"begin(S = resize(["$ia"],s,0)); S"' else # Inversion with PDE-flow if $is_estimate e[0--4] "Invert Laplacian image$? using $1 iterations of PDE flow and initial estimate $2." repeat $! { pass$2 0 l[$>,-1] { *[0] 5 i[1] (0,5,0;5,0,5;0,5,0) repeat $1 { +convolve. [1] -. [0] +[-2,-1] /. 21 } k. # Optimized semi-implicit scheme, with dt=5 } } else e[0--4] "Invert Laplacian image$? using $1 iterations of PDE flow." foreach { * 5 (0,5,0;5,0,5;0,5,0) +f.. 0 repeat $1 { +convolve. [1] -. [0] +[-2,-1] /. 21 } k. # Optimized semi-implicit scheme, with dt=5 } fi fi #@cli inn #@cli : Compute gradient-directed 2nd derivative of image(s). #@cli : $ image.jpg inn inn : e[^-1] "Compute gradient-directed 2nd derivative of image$?." foreach { if d==1 +g xy,0 hessian... xxxyyy # ixx ixy iyy ix iy *[-5] .. *[-4] . *[-4] 2 # ixixx 2iyixy iyy ix iy +[-5,-4] *[-4] .. # ix^2ixx+2ixiyixy iyy ix iy sqr[-2,-1] *... . +[-4,-3] # ix^2ixx+2ixiyixy+iy^2iyy ix^2 iy^2 +[-2,-1] +. 1e-8 / # (ix^2ixx+2ixiyixy+iy^2iyy)/(ix^2+iy^2) else +g xyz,0 hessian[-4] xxxyxzyyyzzz # ixx ixy ixz iyy iyz izz ix iy iz *[-9] ... *[-8] .. *[-8] 2 *[-7] . *[-7] 2 # ixixx 2iyixy 2izixz iyy iyz izz ix iy iz +[-9--7] *[-7] ... # ix^2ixx+2ixiyixy+2ixizixy iyy iyz izz ix iy iz *[-6] .. *[-5] . *[-5] 2 # ix^2ixx+2ixiyixy+2ixizixy iyiyy 2iziyz izz ix iy iz +[-6,-5] *[-5] .. +[-6,-5] # ix^2ixx+2ixiyixy+2ixizixy+iy^2iyy+2iyiziyz izz ix iy iz sqr[-3--1] *[-4] . +[-5,-4] # ix^2ixx+2ixiyixy+2ixizixy+iy^2iyy+2iyiziyz+iz^2izz ix^2 iy^2 iz^2 +[-3--1] +. 1e-8 / # (ix^2ixx+2ixiyixy+2ixizixy+iy^2iyy+2iyiziyz+iz^2izz)/(ix^2+iy^2+iz^2) fi } #@cli inpaint : [mask] : [mask],0,_fast_method : \ # [mask],_patch_size>=1,_lookup_size>=1,_lookup_factor>=0,_lookup_increment!=0,_blend_size>=0,\ # 0<=_blend_threshold<=1,_blend_decay>=0,_blend_scales>=1,_is_blend_outer={ 0 | 1 } : (+) #@cli : Inpaint selected images by specified mask. #@cli : If no patch size (or 0) is specified, inpainting is done using a fast average or median algorithm. #@cli : Otherwise, it used a patch-based reconstruction method, that can be very time consuming. #@cli : 'fast_method' can be { 0:low-connectivity average | 1:high-connectivity average | 2:low-connectivity median | \ # 3:high-connectivity median }. #@cli : Default values: 'patch_size=0', 'fast_method=1', 'lookup_size=22', 'lookup_factor=0.5', 'lookup_increment=1', \ # 'blend_size=0', 'blend_threshold=0', 'blend_decay=0.05', 'blend_scales=10' and 'is_blend_outer=1'. #@cli : $ image.jpg 100%,100% ellipse 50%,50%,30,30,0,1,255 ellipse 20%,20%,30,10,0,1,255 +inpaint[-2] [-1] remove[-2] #@cli : $ image.jpg 100%,100% circle 30%,30%,30,1,255,0,255 circle 70%,70%,50,1,255,0,255 \ # +inpaint[0] [1],5,15,0.5,1,9,0 remove[1] #@cli inpaint_pde : [mask],_nb_scales[%],_diffusion_type={ 0:isotropic | 1:Delaunay-guided | \ # 2:edge-guided | 3:mask-guided },_diffusion_iter>=0 #@cli : Inpaint selected images by specified mask using a multiscale transport-diffusion algorithm. #@cli : Argument 'nb_scales' sets the number of scales used in the multi-scale resolution scheme. #@cli : - When the '%' qualifier is used for 'nb_scales', the number of used scales is relative \ # to `nb_scales_max = ceil(log2(max(w,h,d)))`. #@cli : - When 'nb_scales<0', it determines the minimum image size encountered at the lowest scale. #@cli : If 'diffusion_type==3', non-zero values of the mask (e.g. a distance function) are used #@cli : to guide the diffusion process. #@cli : Default values: 'nb_scales=-9', 'diffusion_type=1' and 'diffusion_iter=20'. #@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 +inpaint_pde[0] [1] inpaint_pde : check ${is_image_arg\ $1}" && isexpr(${2=-9}) && isint(${3=1},0,3) && isint(${4=20},0)" s0="isotropic" s1="Delaunay-guided" s2="edge-guided" s3="mask-guided" e[^-1] "Inpaint image$? by mask $1, using a multiscale diffusion algorithm with $2 scales"\ "and $4 iterations of "${s$3}" diffusion." repeat $! { nm={n} pass$1 l[$>,-1] { nb_scales:="const nb_scales_max = ceil(log2(max(w,h,d))); nb_scales = ispercentage($2)?nb_scales_max*$2: $2<0?1 + log2((d>1?min(w,h,d):min(w,h))/(-$2)): $2; floor(cut(nb_scales,1,nb_scales_max))" nb_iter:=max(0,$4) repeat $nb_scales { {0,"S = 2^"$<"; round([ max(1,w/S), max(1,h/S), max(1,d/S), s ])"} 100%,100%,100% eval[1] "const wl1 = w#-1 - 1; const hl1 = h#-1 - 1; const dl1 = d#-1 - 1; const w1 = max(1,w - 1); const h1 = max(1,h - 1); const d1 = max(1,d - 1); !i?( X = round(x*wl1/w1); Y = round(y*hl1/h1); Z = round(z*dl1/d1); I(#-2,X,Y,Z) += I(#0,x,y,z); ++i(#-1,X,Y,Z) );I" +max. 1 /[-3,-1] !=. 0 if !$> # First scale: Initialize by value propagation im={-2,im} +-.. {$im-1} *. .. +distance.. 1 *. -1 watershed.. . rm. +. {$im-1} mv. -3 fi if $>>0" || "$nb_scales==1 # Apply diffusion iterations. ri... ..,3 if !$3 # Isotropic diffusion repeat $nb_iter { j... ..,0,0,0,0,1,. b... 0.5 } elif $3==1 # Delaunay-guided +distance. 1 100%,100%,100%,{d==1?2:3} eval.. "* # Apply specific gradient scheme for distance function const boundary = 1; maxabs(a,b) = (abs(a)>abs(b)?a:b); ix = maxabs(j(1) - i,i - j(-1)); iy = maxabs(j(0,1) - i,i - j(0,-1)); d>1?( iz = maxabs(j(0,0,1) - i,i - j(0,0,-1)); copy(I(#-1),[ ix,iy,iz ],3,whd); ):copy(I(#-1),[ ix,iy ],2,whd)" rm.. orientation. repeat $nb_iter { j[-4] ...,0,0,0,0,1,.. +warp[-4] .,1,2,1 *.. -1 warp[-5] ..,1,2,1 +[-5,-1] /[-4] 2 } rm. elif $3==2 # Edge-guided repeat $nb_iter { +diffusiontensors... 0,1,1.5,0.5 j[-4] ...,0,0,0,0,1,.. smooth[-4] .,1,10,0 rm. } else # Mask-guided +r[1] .,2 g. a[-{d==1?2:3}--1] c orientation. repeat $nb_iter { j[-4] ...,0,0,0,0,1,.. +warp[-4] .,1,2,1 *.. -1 warp[-5] ..,1,2,1 +[-5,-1] /[-4] 2 } rm. fi j... ..,0,0,0,0,1,. fi rm[-2,-1] } => $nm rv[0,-1] rm. } rm. } #@cli inpaint_flow : [mask],_nb_global_iter>=0,_nb_local_iter>=0,_dt>0,_alpha>=0,_sigma>=0 #@cli : Apply iteration of the inpainting flow on selected images. #@cli : Default values: 'nb_global_iter=10', 'nb_local_iter=100', 'dt=5', 'alpha=1' and 'sigma=3'. #@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 inpaint_flow[0] [1] inpaint_flow : check ${is_image_arg\ $1}" && ${2=10}>=0 && ${3=100}>=0 && ${4=5}>0 && ${5=1}>=0 && ${6=3}>=0" e[^-1] "Apply $2x$3 iterations of the inpainting flow on image$?, with mask $1, time step $4, alpha $5 and sigma $6." repeat $! { pass$1 0 l[$>,-1] { r. [0],[0],[0],1,0 inpaint.. [1] repeat $2 { progress {100*$>/($2-1)} +diffusiontensors.. 0,1,$5,$6,0 *. .. smooth... .,$3,$4,0 rm. } progress 100 } rm. } #@cli inpaint_holes : maximal_area[%]>=0,_tolerance>=0,_is_high_connectivity={ 0 | 1 } #@cli : Inpaint all connected regions having an area less than specified value. #@cli : Default values: 'maximal_area=4', 'tolerance=0' and 'is_high_connectivity=0'. #@cli : $ image.jpg noise 5%,2 +inpaint_holes 8,40 inpaint_holes : check "${1=4}>=0 && ${2=0}>=0" skip ${3=0} e[^-1] "Inpaint holes with area less than $1 pixels in image$?, with tolerance $2 and "\ ${arg0\ !$3,high,low}" connectivity." foreach { 100%,100%,100% area:=ispercentage($1)?$1*w*h*d:$1 repeat s#0 { sh[0] $> +area. $2,$3 <=. $1 -|[1,-1] rm. } if im k[0] whd={w},{h},{d} r 1,1,1,100%,2 r $whd,100% else inpaint[0] [1],0,{2*!$2+!!$3} k[0] fi } #@cli inpaint_morpho : [mask] #@cli : Inpaint selected images by specified mask using morphological operators. #@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 +inpaint_morpho[0] [1] inpaint_morpho : check ${is_image_arg\ $1} e[^-1] "Inpaint image$? by mask $1, using morphological operators." repeat $! { pass$1 0 l[$>,-1] { nm={0,n} im,iM={0,[im,iM]} im1,iM1:=$im-1,$iM+1 channels. 0 ==. 0 +f[0] $im1 j. [0],0,0,0,0,1,.. do +dilate. 3 replace.. $im1,$iM1 erode.. 3 replace.. $iM1,$im1 +[-2,-1] /. 2 j. ...,0,0,0,0,1,.. while im==$im1 k. => $nm } } #@cli inpaint_matchpatch : [mask],_nb_scales={ 0:auto | >0 },_patch_size>0,_nb_iterations_per_scale>0,\ # _blend_size>=0,_allow_outer_blending={ 0 | 1 },_is_already_initialized={ 0 | 1 } #@cli : Inpaint selected images by specified binary mask, using a multi-scale matchpatch algorithm. #@cli : Default values: 'nb_scales=0', 'patch_size=9', 'nb_iterations_per_scale=10', 'blend_size=5',\ # 'allow_outer_blending=1' and 'is_already_initialized=0'. #@cli : $ image.jpg 100%,100% ellipse[-1] 30%,30%,40,30,0,1,255 +inpaint_matchpatch[0] [1] inpaint_matchpatch : check ${is_image_arg\ $1}"&& ${2=0}>=0 && isint(${3=9},1) && isint(${4=10},1) && isint(${5=5},0)" skip ${6=1},${7=0} e[^-1] "Inpaint image$? with mask $1, using a multiscale patch-matching algorithm with "\ ${"if $2 u \"$2 \" else u auto- fi"}\ "scales, $3x$3 patches, $4 iterations per scale and blending size $5." repeat $! { pass$1 0 l[$>,-1] { nm={0,n} # Init variables and images. => img,mask nb_scales:=max(1,round($2?$2:log2(min(w,h)/16),1,1)) visu_size=${fitscreen[]" "{0,[w,h,1]},25%,50%} slices[img] 0 r[mask] [img],[img],1,1,0 !=[mask] 0 if !$7 inpaint_pde[img] [mask],75% fi # Quick first estimate. im={img,im} -[img] $im first_iter=1 iter=0 repeat $nb_scales { scale:=100*(0.5^$<) e "> Process scale "{1+$>}"/"$nb_scales" -> "$scale% progress {100*$>/max(1,$nb_scales-1)} # Compute image and mask at current scales. +r[img,mask] $scale%,$scale%,1,100%,2 => scaled_img,scaled_mask >=[scaled_mask] 0.95 if {scaled_mask,!iM} rm[scaled_img,scaled_mask] continue fi # Skip scale (if too low). +f[scaled_img] -4096 +j[scaled_img] .,0,0,0,0,1,[scaled_mask] rm.. => scaled_reference coef:=0.5^($nb_scales-$iter) patch_size:="v=round(max(min($3,5),$3*$coef));v+(1-(v%2))" patch_size:=min(w,h,$patch_size) blend_size:="v=$5?round(max(3,$5*$coef)):0;v+(1-(v%2))" iter+=1 ==[scaled_mask] 0 if $first_iter # First iteration. # Estimate initial correspondence map. 100%,100%,1,1,x +f. y mv[scaled_mask] $! a[-3--1] c matchpatch[scaled_img] [scaled_reference],$patch_size,$patch_size,1,4,4,0,0,. rm[scaled_reference,-1] => correspondence first_iter=0 else # Standard iteration. # Upscale correspondence map from previous scale. *[correspondence] 2 r[correspondence] 200%,200%,1,2 r[correspondence] [scaled_img],[scaled_img],1,2,0,1 100%,100%,1,1,x +f. y a[-2,-1] c f[scaled_mask] "*i?1:( upc = i(#"$correspondence",x-1,y,0,0); vpc = i(#"$correspondence",x-1,y,0,1); ucp = i(#"$correspondence",x,y-1,0,0); vcp = i(#"$correspondence",x,y-1,0,1); ucc = i(#"$correspondence",x,y,0,0); vcc = i(#"$correspondence",x,y,0,1); i(#-1,x,y,0,0) = (ucc==upc && vcc==vpc)?upc + 1:ucc; i(#-1,x,y,0,1) = (ucc==ucp && vcc==vcp)?vcp + 1:vcc; 0)" rm[correspondence] => correspondence a[correspondence] [scaled_mask],c # Refine correspondence map iteratively with matchpatch. nbs1:=max(1,$nb_scales-1) nb_iter:=round(max(1,$4*(($<+1)/$nbs1)^2)) repeat $nb_iter { _inpaint_matchpatch[scaled_img] [correspondence],[scaled_mask],$blend_size,$6 +matchpatch[scaled_img] [scaled_reference],$patch_size,$patch_size,1,4,4,0,0,[correspondence] j[correspondence] . rm. if {*1} w1[scaled_img] $visu_size,0 fi if {*2} w2[correspondence] $visu_size,1 fi } rm[scaled_img,scaled_mask,scaled_reference] channels[correspondence] 0,1 fi } progress 100 # Generate final result. if $correspondence ==[mask] 0 _inpaint_matchpatch[img] [correspondence],[mask],$5,$6 rm[correspondence] fi +[img] $im =>[0] $nm } rm[mask] } # _inpaint_matchpatch : [correspondence_map],[mask],blend_size>0,_allow_outer_blending={ 0 | 1 } _inpaint_matchpatch : pass$1 1 pass$2 {!$3" || "!$4} if !$3 warp[0] [1],0,0,1 else if $4 erode. $3 fi f[0] "*begin( boundary = 1; const patch_size = $3; const p2 = int(patch_size/2); const p1 = patch_size - p2 - 1; avg = resize([0],s#0); # Pre-compute gaussian kernel for patch blending. wpq = resize([0],patch_size^2); g = 0; for (q = -p1, q<=p2, ++q, for (p = -p1, p<=p2, ++p, wpq[g++] = exp(-(p^2 + q^2)/(2*(0.3*patch_size)^2)); ); ); ); if (i#2,I, g = 0; avg = 0; norm = 0; for (q = -p1, q<=p2, ++q, for (p = -p1, p<=p2, ++p, U = I(#1,x + p,y + q); w = wpq[g++]; norm+=w; avg+=w*I(#0,U[0,2] - [p,q]); ); ); avg/norm)" fi k[0] # _inpaint_warping2d : fill-in zero-valued vectors in absolute 2D warping field (works even when 'spectrum>2'). _inpaint_warping2d : foreach { 100%,100%,100%,2,"> begin(const S = s#0; zero0 = vectorS(); zero1 = [0,0]; N = 0); I(#-1)==zero0?zero1:[++N,1]" s. c distance. 1 *. -1 watershed.. . rm. # Propagate offsets in each distinct voronoi cell. repeat 2 { f.. ">i?I:( # Forward propagation nP = vectors(); const sP = size(nP); r = i(#-1); (P = J(-1,-1))[0] && j(#-1,-1,-1)==r?(nP[0] = ++P[0]; nP[1] = ++P[1]; sP>2?copy(nP[2],P[2],sP-2)): (P = J(0,-1))[0] && j(#-1,0,-1)==r ?(nP[0] = P[0]; nP[1] = ++P[1]; sP>2?copy(nP[2],P[2],sP-2)): (P = J(1,-1))[0] && j(#-1,1,-1)==r ?(nP[0] = --P[0]; nP[1] = ++P[1]; sP>2?copy(nP[2],P[2],sP-2)): (P = J(-1,0))[0] && j(#-1,-1,0)==r ?(nP[0] = ++P[0]; nP[1] = P[1]; sP>2?copy(nP[2],P[2],sP-2)); nP)" f.. "2?copy(nP[2],P[2],sP-2)): (P = J(0,1))[0] && j(#-1,0,1)==r ?(nP[0] = P[0]; nP[1] = --P[1]; sP>2?copy(nP[2],P[2],sP-2)): (P = J(-1,1))[0] && j(#-1,-1,1)==r?(nP[0] = ++P[0]; nP[1] = --P[1]; sP>2?copy(nP[2],P[2],sP-2)): (P = J(1,0))[0] && j(#-1,1,0)==r ?(nP[0] = --P[0]; nP[1] = P[1]; sP>2?copy(nP[2],P[2],sP-2)); nP)" } rm. } #@cli kuwahara : size>0 #@cli : Apply Kuwahara filter of specified size on selected images. #@cli : $ image.jpg kuwahara 9 kuwahara : check $1>0 e[^-1] "Apply Kuwahara filter of size $1 on image$?." foreach { s:=s +dilate $1 compose_channels. min +erode[0] $1 compose_channels. max -[-2,-1] $1,1,1,1,{1/$1} convolve[0] . transpose. convolve[0] . rm. p:=int($1/2) a[-2,-1] c f "v1=i(x-"$p",y-"$p",0,"$s",0,1); \ v2=i(x+"$p",y-"$p",0,"$s",0,1); \ v3=i(x-"$p",y+"$p",0,"$s",0,1); \ v4=i(x+"$p",y+"$p",0,"$s",0,1); \ vm=min(v1,v2,v3,v4); \ c>="$s"?i: \ vm==v1?i(x-"$p",y-"$p",0,c,0,1): vm==v2?i(x+"$p",y-"$p",0,c,0,1): vm==v3?i(x-"$p",y+"$p",0,c,0,1): i(x+"$p",y+"$p",0,c,0,1)" channels 0,{s-2} } #@cli laplacian #@cli : Compute Laplacian of selected images. #@cli : $ image.jpg laplacian laplacian : e[^-1] "Compute Laplacian of image$?." foreach { hessian ${arg0\ (d==1),xxyyzz,xxyy} + } #@cli lic : _amplitude>0,_channels>0 #@cli : Render LIC representation of selected vector fields. #@cli : Default values: 'amplitude=30' and 'channels=1'. #@cli : $ 400,400,1,2,'!c?x-w/2:y-h/2' +lic 200,3 quiver[-2] [-2],10,1,1,1,255 lic : skip ${1=30},${2=1} e[^-1] "Render LIC representation of 2D vector field$?, with amplitude $1 and $2 channel(s)." foreach { nm={n} channels 0,1 / {max(abs(im),abs(iM))} vector2tensor 100%,100%,100%,$2 rand. 0,255 smooth. ..,$1 rm.. equalize => $nm } #@cli map_tones : _threshold>=0,_gamma>=0,_smoothness>=0,nb_iter>=0 #@cli : Apply tone mapping operator on selected images, based on Poisson equation. #@cli : Default values: 'threshold=0.1', 'gamma=0.8', 'smoothness=0.5' and 'nb_iter=30'. #@cli : $ image.jpg +map_tones , map_tones : skip ${1=0.1},${2=0.8},${3=0.5},${4=30} e[^-1] "Apply tone mapping operator on image$?, with threshold $1, gamma $2, smoothness $3 and $4 iterations." foreach { # Estimate target divergence for each channel. +l { s c foreach { g xy,1 a c +norm orientation.. m,M:=[im,iM] b. $3 n. $m,$M *. 'alpha=$1*iM;(alpha/(1e-10+i))*(i/(1e-10+alpha))^$2' * s c g.. x,-1 g. y,-1 + } a c * 0.25 } # Start Poisson-PDE iterations repeat $4 { +laplacian.. *. 0.25 +. ... -. .. *. 800 +[-3,-1] /.. 801 c.. 0,255 } rm. } #@cli map_tones_fast : _radius[%]>=0,_power>=0 #@cli : Apply fast tone mapping operator on selected images. #@cli : Default values: 'radius=3%' and 'power=0.3'. #@cli : $ image.jpg +map_tones_fast , map_tones_fast : check "${1=3%}>=0 && ${2=0.3}>=0" e[^-1] "Apply fast tone mapping operator on image$?, with radius $1 and power $2." foreach { +luminance b. $1 n 0,1 +*. 2 -. 1 abs. *. {$2*log(10)} exp. <=.. 0.5 ri. ... +*... -1 +. 1 ^. .. *. -1 +. 1 *. ... ^[-4,-2] ==.. 0 *[-3,-2] + } n 0,255 #@cli meancurvature_flow : _nb_iter>=0,_dt,_keep_sequence={ 0 | 1 } #@cli : Apply iterations of the mean curvature flow on selected images. #@cli : Default values: 'nb_iter=10', 'dt=30' and 'keep_sequence=0'. #@cli : $ image.jpg +meancurvature_flow 20 meancurvature_flow : skip ${1=10},${2=30},${3=0} e[^-1] "Apply $1 iterations of the mean curvature flow on image$?, with time step $2." pde_flow $1,$2,iee,$3 #@cli median : size>=0,_threshold>0 : (+) #@cli : Apply (opt. thresholded) median filter on selected images with structuring element size x size. #@cli : $ image.jpg +median 5 #@cli merge_alpha #@cli : Merge selected alpha detail scales into a single image. #@cli : Alpha detail scales have been obtained with command ''split_alpha''. merge_alpha : e[^-1] "Merge alpha detail$? into a single image." repeat $!-1 { r[0] [1],[1],1,100%,5 c[0] 0,255 blend[0,1] alpha } if s=['{b}'];find(s,'_0')==size(s)-2 ext={x} if ['$ext']!=0 ext..=. fi ({'{f}{b}'}) z. 0,{w-3} bm={t} rm. => $bm$ext fi #@cli nlmeans : [guide],_patch_radius>0,_spatial_bandwidth>0,_tonal_bandwidth>0,_patch_measure_command : \ # _patch_radius>0,_spatial_bandwidth>0,_tonal_bandwidth>0,_patch_measure_command #@cli : Apply non local means denoising of Buades et al, 2005. on selected images. #@cli : The patch is a gaussian function of 'std_patch_radius'. #@cli : The spatial kernel is a rectangle of radius 'spatial_bandwidth'. #@cli : The tonal kernel is exponential (`exp(-d^2/_tonal_bandwidth^2)`) #@cli : with `d` the euclidean distance between image patches. #@cli : Default values: 'patch_radius=4', 'spatial_bandwidth=4', 'tonal_bandwidth=10' and 'patch_measure_command=-norm'. #@cli : $ image.jpg +noise 10 nlmeans[-1] 4,4,{0.6*${-std_noise}} nlmeans: if ${"is_image_arg $1"} # Guided-filtering check "${2=4}>0 && ${3=4}>0 && ${4=10}>0" skip "${5=-norm}" e[^-1] "Apply non-local means denoising on image$?, with guide $1, patch size $2, spatial bandwidth $3, tonal bandwidth $4 and patch measure command '$5'." pass$1 0 l. { $5 k[0] } # [1] preprocessed image used to compute weights. repeat $!-1 { l[$>,-1] { 100%,100%,100%,100%,{-1.0/($4*$4)} # [2] compute a scaling. nlmeans_core[0] [1],[2],$2,$3 rm. # Apply the NLM denoising with image 1 and 2 as parameter. } } rm. else # Non-guided filtering check "${1=4}>0 && ${2=4}>0 && ${3=10}>0" skip "${4=-norm}" e[^-1] "Apply non-local means denoising on image$?, with patch size $1, spatial bandwidth $2, tonal bandwidth $3 and patch measure command '$4'." foreach { +l { $4 k[0] } # [1] preprocessed image used to compute weights. 100%,100%,100%,100%,{-1.0/($3*$3)} # [2] compute a scaling. nlmeans_core[0] [1],[2],$1,$2 k[0] # Apply the NLM denoising with image 1 and 2 as parameter. } fi #@cli nlmeans_core: _reference_image,_scaling_map,_patch_radius>0,_spatial_bandwidth>0 #@cli : Apply non local means denoising using a image for weight and a map for scaling nlmeans_core : check ${is_image_arg\ $1}" && "${is_image_arg\ $2}" && $3>0 && $4>0" e[^-1] "Apply non-local means denoising using weight images $1, scaling map $2, patch size $3 and spatial bandwidth $4." pass$1 0 pass$2 0 repeat $!-2 { l[$>,-1,-2] { # [0] original, [1] weights, [2] scaling [3] sum(weights * patch), [4] sum(weights), [5] max(weights) 100%,100%,100%,{0,s},0 100%,100%,100%,{1,s},0 100%,100%,100%,{1,s},1e-6 if d#0==1 repeat 2*$4+1 { j:=$>-$4 repeat 2*$4+1 { i:=$>-$4 if $i!=0||$j!=0 # Compute shifted images [6] and weight [7] +shift[0,1] $i,$j,0,0,2 -[7] [1] sqr[7] b[7] $3 *[7] [2] exp[7] # Accumulate weights *[6] [7] max[5] [7] +[4,7] +[3,6] fi } } else repeat 2*$4+1 { k:=$>-$4 repeat 2*$4+1 { j:=$>-$4 repeat 2*$4+1 { i:=$>-$4 if $i!=0||$j!=0||$k!=0 # Compute shifted images [6] and weight [7] +shift[0,1] $i,$j,0,0,2 -[7] [1] sqr[7] b[7] $3 *[7] [2] exp[7] # Accumulate weights *[6] [7] max[5] [7] +[4,7] +[3,6] fi } } } fi rm[1,2] *[0] [3] +[1,0] +[1,2] # Add central patch / # Normalize } } #@cli normalize_local : _amplitude>=0,_radius>0,_n_smooth>=0[%],_a_smooth>=0[%],_is_cut={ 0 | 1 },_min=0,_max=255 #@cli : Normalize selected images locally. #@cli : Default values: 'amplitude=3', 'radius=16', 'n_smooth=4%', 'a_smooth=2%', 'is_cut=1', 'min=0' and 'max=255'. #@cli : $ image.jpg normalize_local 8,10 normalize_local : check "${1=3}>=0 && ${2=16}>0 && isbool(${5=1})" skip ${3=4%},${4=2%},${6=0},${7=255} e[^-1] "Normalize image$? locally, with amplitude $1, radius $2, neighborhood smoothness $3 and average smoothness $4." foreach { +l { erode {2*$2+1} s c min } +l.. { dilate {2*$2+1} s c max } +b... $4 b[-3,-2] $3 +-.. ... +. 0.01 -[-5] [-4] /[-5,-1] *[-3,-2] {$1+1} *. -$1 +... . +[-2,-1] if $5 max.. $6 min. $7 fi -. .. *[-3,-1] + if $5 c $6,$7 fi } #@cli normalized_cross_correlation : [mask] #@cli : Compute normalized cross-correlation of selected images with specified mask. #@cli : $ image.jpg +shift -30,-20 +normalized_cross_correlation[0] [1] normalized_cross_correlation : check ${is_image_arg\ $1} e[^-1] "Compute normalized cross-correlation of image$? with mask $1." pass$1 0 norm repeat $!-1 { . l[$>,-1] { fft.. fft. [-2,-1] *.. [-5] *. [-6] -[-2,-1] *[-5,-3] *[-3,-2] +[-3,-2] [-2,-1] a[-2,-1] c norm. /... . /[-2,-1] ifft rm. } } rm. #@cli opening : size>=0 : size_x>=0,size_y>=0,_size_z>=0 : \ # [kernel],_boundary_conditions,_is_real={ 0:binary-mode | 1:real-mode } #@cli : Apply morphological opening to selected images. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'size_z=1', 'boundary_conditions=1' and 'is_real=0'. #@cli : $ image.jpg +opening 10 opening : skip "${2=},${3=}" boundary,is_kernel,sel=1,0,^ if "isnum($1)" # Full square kernel if "['$2']==0 && ['$3']==0" sx,sy,sz=$1 e[0--4] "Apply morphological opening to image$?, with kernel of size "$sx"." else sx=$1 if ['$2']==0 sy=1 else sy=$2 fi if ['$3']==0 sz=1 else sz=$3 fi e[0--4] "Apply morphological opening to image$?, with "${sx}"x"${sy}"x"${sz}" kernel." fi elif ${"is_image_arg $1"}" && narg("${"pass$1 -1"}")==1" # Custom kernel pass$1 sx,sy,sz={w},{h},{d} is_kernel,sel,r0,r1,b0,b1,b2,b3=1,^-1,binary,real,dirichlet,neumann,periodic,mirror is_real=0 if ['$2']!=0 boundary:=cut($2,0,3) fi if ['$3']!=0 is_real=$3 fi e[0--3] "Apply morphological opening to image$? with kernel ["${"pass$1 -1"}"] and "${b$boundary}" boundary "\ "conditions, in "${r$is_real}" mode." else error[0--3] "Command 'opening': Invalid arguments '$*'." fi if $sx>0||$sy>=0||$sz>=0 sx1,sx2:="s=int(($sx-1)/2);[s,$sx-s-1]" sy1,sy2:="s=int(($sy-1)/2);[s,$sy-s-1]" sz1,sz2:="s=int(($sz-1)/2);[s,$sz-s-1]" foreach[$sel] { nm={n} if d>1 # 3D r {[w+$sx+1,h+$sy+1,d+$sz+1]},100%,0,$boundary,0.5,0.5,0.5 if $is_kernel pass. erode.. .,0,$is_real dilate.. .,0,$is_real rm. else erode $sx,$sy,$sz dilate $sx,$sy,$sz fi z {$sx1+1},{$sy1+1},{$sz1+1},{w-$sx2-2},{h-$sy2-2},{d-$sz2-2} => $nm elif h>1 # 2D r {[w+$sx+1,h+$sy+1]},1,100%,0,$boundary,0.5,0.5 if $is_kernel pass. erode.. .,0,$is_real dilate.. .,0,$is_real rm. else erode $sx,$sy dilate $sx,$sy fi z {$sx1+1},{$sy1+1},{w-$sx2-2},{h-$sy2-2} => $nm else # 1D r {w+$sx+1},1,1,100%,0,$boundary,0.5 if $is_kernel pass. erode.. .,0,$is_real dilate.. .,0,$is_real rm. else erode $sx,1 dilate $sx,1 fi z {$sx1+1},{w-$sx2-2} => $nm fi } fi if $is_kernel rm. fi #@cli opening_circ : _size>=0,_is_real={ 0 | 1 } #@cli : Apply circular opening of selected images by specified size. #@cli : Default values: 'boundary_conditions=1' and 'is_real=0'. #@cli : $ image.jpg +opening_circ 7 opening_circ : check "$1>=0 && isbool(${2=0})" r0,r1=binary,real e[^-1] "Apply morphological opening of image$? with circular kernel of size $1, in "${r$2}" mode." if $1<2 return fi shape_circle $1 opening[^-1] .,$2 rm. #@cli percentile : [mask],0<=_min_percentile[%]<=100,0<=_max_percentile[%]<=100. #@cli : Apply percentile averaging filter to selected images. #@cli : Default values: 'min_percentile=0' and 'max_percentile=100'. #@cli : $ image.jpg shape_circle 11,11 +percentile[0] [1],25,75 percentile : check ${"is_image_arg $1"}" && inrange(${2=0},0,100) && inrange(${3=100},0,100) && $2<=$3" vmin,vmax:=_[ispercentage($2)?100*$2:$2,ispercentage($3)?100*$3:$3] e[^-1] "Apply percentile averaging filter to image$?, with mask $1, "\ "min percentile "$vmin"% and max percentile "$vmax"%." # Generate code for masking. pass$1 0 !=. 0 N:=is if !$N rm. return fi 128,$N eval.. "> begin( p = 0; const w2 = int(w/2); const h2 = int(h/2); ); i?( out = string('N[',p,']=j(',x - w2,',',y - h2,');'); copy(i(#-1,0,p++),out,size(out)); )" discard. 0 code={t} rm[-2,-1] # Apply filter. f " begin( N = vector"$N"() ); const boundary = 1; const sS = size(N) - 1; const s0 = round(sS*"$vmin"%); const s1 = round(sS*"$vmax"%); const ds = 1 + s1 - s0; "$code" S = sort(N); res = 0; for (s = s0, s<=s1, ++s, res+=S[s]); res/=ds" #@cli peronamalik_flow : K_factor>0,_nb_iter>=0,_dt,_keep_sequence={ 0 | 1 } #@cli : Apply iterations of the Perona-Malik flow on selected images. #@cli : Default values: 'K_factor=20', 'nb_iter=5', 'dt=5' and 'keep_sequence=0'. #@cli : $ image.jpg +heat_flow 20 peronamalik_flow : check "${1=20}>0 && ${2=5}>=0" skip ${3=5},${4=0} e[^-1] "Apply $2 iterations of the Perona-Malik flow on image$?, with K factor $1 and time step $3." m "_peronamalik_flow : +gradient xy,0 a[-2,-1] c norm. b. 0.8 /. $1 sqr. *. -1 exp. a[-2,-1] c f. \"s1 = s-1; C = i(x,y,z,s-1); c>=s1?0: (C+i(x+1,y,z,s-1,0,1))*(j(1,0,0,0,0,1)-i) - (C+i(x-1,y,z,s-1,0,1))*(i-j(-1,0,0,0,0,1)) + (C+i(x,y+1,z,s-1,0,1))*(j(0,1,0,0,0,1)-i) - (C+i(x,y-1,z,s-1,0,1))*(i-j(0,-1,0,0,0,1))\"" pde_flow $2,$3,_peronamalik_flow,$4 um _peronamalik_flow #@cli phase_correlation : [destination] #@cli : Estimate translation vector between selected source images and specified destination. #@cli : $ image.jpg +shift -30,-20 +phase_correlation[0] [1] unroll[-1] y phase_correlation : check ${"is_image_arg $1"} e[^-1] "Estimate shift between source image$? and destination $1." pass$1 repeat $!-1 { normalized_cross_correlation[$>] . l[$>] { eval " store('res', [xM>=w/2?xM - w:xM, yM>=h/2?yM - h:yM, zM>=d/2?zM - d:zM]*=-1,1,1,1,3)" } $res => "[phase correlation]" rv[$>,-1] rm. } rm. #@cli pde_flow : _nb_iter>=0,_dt,_velocity_command,_keep_sequence={ 0 | 1 } #@cli : Apply iterations of a generic PDE flow on selected images. #@cli : Default values: 'nb_iter=10', 'dt=30', 'velocity_command=laplacian' and 'keep_sequence=0'. #@cli : $ image.jpg +pde_flow 20 pde_flow : skip ${1=10},${2=30},${3=laplacian},${4=0} e[^-1] "Apply $1 iterations of the velocity flow '$3' on image$?, with time step $2." foreach { repeat $1 { +$3. *. {$2/(0.01+max(abs(im),abs(iM)))} if $4 +. .. else +[-2,-1] fi } if $4 rm[0] fi a x } if $4 s x,$1 fi #@cli periodize_poisson #@cli : Periodize selected images using a Poisson solver in Fourier space. #@cli : $ image.jpg +periodize_poisson array 2,2,2 periodize_poisson : e[^-1] "Periodize image$? using Poisson solver in Fourier space." foreach { s c foreach { mM:=[im,iM] sum={0,ia} laplacian ilaplacian 0 + $sum c $mM } a c } #@cli rbf : dx,_x0,_x1,_phi(r) : dx,dy,_x0,_y0,_x1,_y1,_phi(r) : dx,dy,dz,x0,y0,z0,x1,y1,z1,phi(r) #@cli : Reconstruct 1D/2D or 3D image from selected sets of keypoints, by RBF-interpolation. #@cli : A set of keypoints is represented by a vector-valued image, where each pixel represents a single keypoint. #@cli : Vector components of a keypoint have the following meaning: #@cli : - For 1D reconstruction: [ x_k, f1(k),...fN(k) ]. #@cli : - For 2D reconstruction: [ x_k,y_k, f1(k),...,fN(k) ]. #@cli : - For 3D reconstruction: [ x_k,y_k,z_k, f1(k),...,fN(k) ]. #@cli : Values 'x_k','y_k' and 'z_k' are the spatial coordinates of keypoint 'k'. #@cli : Values 'f1(k),..,fN(k)' are the 'N' components of the vector value of keypoint 'k'. #@cli : The command reconstructs an image with specified size 'dx'x'dy'x'dz', with 'N' channels. #@cli : Default values: 'x0=y0=z0=0', 'x1=dx-1', 'y1=dy-1', 'z1=dz-1', 'phi(r)=r^2*log(1e-5+r)'. #@cli : $ sample colorful,400 100%,100% noise_poissondisk. 10 1,{is},1,5 \ # eval[-2] "begin(p=0);i?(I[#-1,p++]=[x,y,I(#0)])" to_rgb[1] mul[0,1] dilate_circ[0] 5 +rbf[-1] {0,[w,h]} c[-1] 0,255 #@cli : $ 32,1,1,5,u([400,400,255,255,255]) rbf 400,400 c 0,255 rbf : $=a default_phi_r="r^2*log(1 + r)" if isin($#,1,3,4) # 1D reconstruction dx,x0,x1=$a1,{$#>1?[$a2,$a3]:[0,$a1-1]} phi_r={`$#>3?['$a4']:'$default_phi_r'`} check $dx>0 e[^-1] "Reconstruct 1D image from keypoint set$?, with size "$dx", "\ "from ("$x0") to ("$x1") and phi(r) = "$phi_r. foreach { nm={n} if !w $dx elif whd==1 channels. 1,100% r. $dx,1,1,100% else r 1,{whd},1,100%,-1 permute. cyzx $dx,1,1,{w-1},"* begin( phi(r) = ("$phi_r"); ref(crop(#0,0,0,0,0,1,h#0,1,1),X); ref(crop(#0,1,0,0,0,s,h#0,1,1,1),F); ref(vector(#h#0^2),M); repeat (h#0,k, for (l = 0, l<=k, ++l, r = abs(X[k] - X[l]); M[k*h#0 + l] = M[l*h#0 + k] = phi(r); ) ); ref(solve(M,F,s,1),W); const fx = ("$x1-$x0")/(w-1); ); ref(vectors(),res); x = (x - "$x0")*fx; repeat (h#0,k, r = abs(x - X[k]); res+=W[s*k,s]*phi(r)); res" k. => $nm fi } elif isin($#,2,6,7) # 2D reconstruction dx,dy,x0,y0,x1,y1=$a1,$a2,{$#>2?[$a3,$a4,$a5,$a6]:[0,0,[$a1,$a2]-1]} phi_r={`$#>6?['$a7']:'$default_phi_r'`} check $dx>0" && "$dy>0 e[^-1] "Reconstruct 2D image from keypoint set$?, with size "$dx,$dy", "\ "from ("$x0,$y0") to ("$x1,$y1") and phi(r) = "$phi_r. foreach { nm={n} if !w $dx,$dy elif whd==1 channels 2,100% r. $dx,$dy,1,100% else r 1,{whd},1,100%,-1 permute. cyzx $dx,$dy,1,{w-2},"* begin( phi(r) = ("$phi_r"); ref(crop(#0,0,0,0,0,1,h#0,1,1),X); ref(crop(#0,1,0,0,0,1,h#0,1,1),Y); ref(crop(#0,2,0,0,0,s,h#0,1,1,1),F); ref(vector(#h#0^2),M); repeat (h#0,k, for (l = 0, l<=k, ++l, r = norm(X[k] - X[l],Y[k] - Y[l]); M[k*h#0 + l] = M[l*h#0 + k] = phi(r); ) ); ref(solve(M,F,s,1),W); const fx = ("$x1-$x0")/(w-1); const fy = ("$y1-$y0")/(h-1); ); ref(vectors(),res); x = (x - "$x0")*fx; y = (y - "$y0")*fy; repeat (h#0,k, r = norm(x - X[k], y - Y[k]); res+=W[s*k,s]*phi(r)); res" k. => $nm fi } elif isin($#,3,9,10) # 3D reconstruction dx,dy,dz,x0,y0,z0,x1,y1,z1=$a1,$a2,$a3,{$#>3?[$a4,$a5,$a6,$a7,$a8,$a9]:[0,0,0,[$a1,$a2,$a3]-1]} phi_r={`$#>9?['$arg10']:'$default_phi_r'`} check $dx>0" && "$dy>0" && "$dz>0 e[^-1] "Reconstruct 3D image from keypoint set$?, with size "$dx,$dy,$dz", "\ "from ("$x0,$y0,$z0") to ("$x1,$y1,$z1") and phi(r) = "$phi_r. foreach { nm={n} if !w $dx,$dy,$dz elif whd==1 channels 3,100% r. $dx,$dy,$dz,100% else r 1,{whd},1,100%,-1 permute. cyzx $dx,$dy,$dz,{w-3},"* begin( phi(r) = ("$phi_r"); ref(crop(#0,0,0,0,0,1,h#0,1,1),X); ref(crop(#0,1,0,0,0,1,h#0,1,1),Y); ref(crop(#0,2,0,0,0,1,h#0,1,1),Z); ref(crop(#0,3,0,0,0,s,h#0,1,1,1),F); ref(vector(#h#0^2),M); repeat (h#0,k, for (l = 0, l<=k, ++l, r = norm(X[k] - X[l],Y[k] - Y[l],Z[k] - Z[l]); M[k*h#0 + l] = M[l*h#0 + k] = phi(r); ) ); ref(solve(M,F,s,1),W); const fx = ("$x1-$x0")/(w - 1); const fy = ("$y1-$y0")/(h - 1); const fz = ("$z1-$z0")/(d - 1); ); ref(vectors(),res); x = (x - "$x0")*fx; y = (y - "$y0")*fy; z = (z - "$z0")*fz; repeat (h#0,k, r = norm(x - X[k], y - Y[k], z - Z[k]); res+=W[s*k,s]*phi(r)); res" k. => $nm fi } else error[0--2] "Command 'rbf': invalid arguments '$*'." fi #@cli red_eye : 0<=_threshold<=100,_smoothness>=0,0<=attenuation<=1 #@cli : Attenuate red-eye effect in selected images. #@cli : Default values: 'threshold=75', 'smoothness=3.5' and 'attenuation=0.1'. #@cli : $ image.jpg +red_eye , red_eye : skip ${1=75},${2=3.5},${3=0.1} e[^-1] "Attenuate red-eye effect in image$?, with threshold $1, smoothness $2 and attenuation $3." to_rgb rgb2ycbcr foreach { s c -. 128 +>=. $1% b. $2 sqrt. *. -1 +. 1 n. $3,1 *[-2,-1] +. 128 a c ycbcr2rgb } #@cli remove_hotpixels : _mask_size>0, _threshold[%]>0 #@cli : Remove hot pixels in selected images. #@cli : Default values: 'mask_size=3' and 'threshold=10%'. #@cli : $ image.jpg noise 10,2 +remove_hotpixels , remove_hotpixels : check ${1=3}>0 skip ${2=10%} e[^-1] "Remove hot pixels in image$?, with mask size $1 and threshold $2." foreach { +median $1 +- abs. >=. $2 *.. . ==. 0 *[-3,-1] + } #@cli remove_pixels : number_of_pixels[%]>=0 #@cli : Remove specified number of pixels (i.e. set them to 0) from the set of non-zero pixels in selected images. #@cli : $ image.jpg +remove_pixels 50% remove_pixels : check "$1>=0" e[^-1] "Remove $1 of the non-zero pixels in image$?." foreach { +norm !=. 0 N:=is # Number of non-zero pixels. n:=round(ispercentage($1)?$N*$1:$1) # Number of pixels to remove. if $n<=0 rm. # No pixels to remove. elif $n>=$N rm. f 0 # All pixels to remove. elif $n>int($N/2) # More pixels to remove than to keep. remove_pixels. {$N-$n} ==. 0 * else # Less pixels to remove than to keep. d:=d r 100%,{d*h},1,100%,-1 # Force image to be in 2D. # Retrieve coordinates of all non-zero pixels. 100%,1,1,1,x 1,{-2,h},1,1,y +[-2,-1] 1 r[-2,-1] ..,. *[-2,-1] ... rm... y[-2,-1] a[-2,-1] x discard. y,0 # Generate a 1xN vector with at least n non-zero pixels. do 1,100%,1,1 rand. 0,{h} <=. {$n*1.25} if is>=$n break else rm. fi while 1 # Generate a 1xn vector of coordinates to 'remove'. r. 2 *[-2,-1] discard. y,0 i.. 1,100% rand.. 0,1 a[-2,-1] x sort. +,y rows. 0,{$n-1} -. 1 z. 1,3 # Set those pixels to 0 using a 3D object. i.. ({'CImg3d'},{h},{h}) 1,100%,1,1,1 1,100%,1,1,y a[-2,-1] x 3,100% 1,100%,1,1,1 y[-5--1] a[-5--1] y if s#0<=3 j3d.. .,0,0,0,1,0,0,0,0 else [0],[0],1,1,1 j3d. ..,0,0,0,1,0,0,0,0 *[0,-1] fi rm. r 100%,{h/$d},$d,100%,-1 # Resize to original dimension (eventually 3D). fi } #@cli rolling_guidance : std_deviation_s[%]>=0,std_deviation_r[%]>=0,_precision>=0 #@cli : Apply the rolling guidance filter on selected image. #@cli : Rolling guidance filter is a fast image abstraction filter, described in: #@cli : "Rolling Guidance Filter", Qi Zhang Xiaoyong, Shen Li, Xu Jiaya Jia, ECCV'2014. #@cli : Default values: 'std_deviation_s=4', 'std_deviation_r=10' and 'precision=0.5'. #@cli : $ image.jpg +rolling_guidance , +- rolling_guidance : check "${1=4}>=0 && ${2=10}>=0 && ${3=0.5}>=0" e[^-1] "Apply rolling guidance filter on image$?, with standard deviations ($1,$2) and precision $3." precision:=2^-$3 foreach { nm={n} +b $1 repeat 100 { if c>1 +norm. +bilateral... .,$1,$2 rm.. else +bilateral.. .,$1,$2 fi -.. . std={-2,id} rm.. if $std<$precision break fi } k. => $nm } #@cli sharpen : amplitude>=0 : amplitude>=0,edge>=0,_alpha[%],_sigma[%] #@cli : Sharpen selected images by inverse diffusion or shock filters methods. #@cli : 'edge' must be specified to enable shock-filter method. #@cli : Default values: 'edge=0', 'alpha=0' and 'sigma=0'. #@cli : $ image.jpg sharpen 300 #@cli : $ image.jpg blur 5 sharpen 300,1 sharpen : check "$1>=0 && ${2=0}>=0 && ${3=0}>=0 && ${4=0}>=0" if $2>0 # Shock filters e[0--3] "Sharpen image$? with shock filters, amplitude $1, edge $2, alpha $3 and sigma $4." foreach { im,iM:=[im,iM] +b $3 structuretensors. 0 b. $4 eigen. l.. { max 0 s c + + 1 ^ {-0.5*$2} *. -1 +. 1 } if {0,d>1} # 3D +f[0] "const boundary = 1; minmod(a,b) = (a*b<=0?0:minabs(a,b)); u = i(#-1,x,y,z,0); v = i(#-1,x,y,z,1); w = i(#-1,x,y,z,2); amp = i(#-2,x,y,z,0); Ippp = j(-1,-1,-1); Icpp = j(0,-1,-1); Inpp = j(1,-1,-1); Ipcp = j(-1,0,-1); Iccp = j(0,0,-1); Incp = j(1,0,-1); Ipnp = j(-1,1,-1); Icnp = j(0,1,-1); Innp = j(1,1,-1); Ippc = j(-1,-1,0); Icpc = j(0,-1,0); Inpc = j(1,-1,0); Ipcc = j(-1,0,0); Iccc = i; Incc = j(1,0,0); Ipnc = j(-1,1,0); Icnc = j(0,1,0); Innc = j(1,1,0); Ippn = j(-1,-1,1); Icpn = j(0,-1,1); Inpn = j(1,-1,1); Ipcn = j(-1,0,1); Iccn = j(0,0,1); Incn = j(1,0,1); Ipnn = j(-1,1,1); Icnn = j(0,1,1); Innn = j(1,1,1); ixx = Incc + Ipcc - 2*Iccc; ixy = 0.25*(Innc + Ippc - Inpc - Ipnc); ixz = 0.25*(Incn + Ipcp - Incp - Ipcn); iyy = Icnc + Icpc - 2*Iccc; iyz = 0.25*(Icnn + Icpp - Icnp - Icpn); izz = Iccn + Iccp - 2*Iccc; ixf = Incc - Iccc; ixb = Iccc - Ipcc; iyf = Icnc - Iccc; iyb = Iccc - Icpc; izf = Iccn - Iccc; izb = Iccc - Iccp; itt = u^2*ixx + v^2*iyy + w^2*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz; it = u*minmod(ixf,ixb) + v*minmod(iyf,iyb) + w*minmod(izf,izb); amp*sign(itt)*abs(it)" else # 2D +f[0] "const boundary = 1; minmod(a,b) = (a*b<=0?0:minabs(a,b)); u = i(#-1,x,y,z,0); v = i(#-1,x,y,z,1); amp = i(#-2,x,y,z,0); Ipp = j(-1,-1); Icp = j(0,-1); Inp = j(1,-1); Ipc = j(-1,0); Icc = i; Inc = j(1,0); Ipn = j(-1,1); Icn = j(0,1); Inn = j(1,1); ixx = Inc + Ipc - 2*Icc; ixy = 0.25*(Ipp + Inn - Ipn - Inp); iyy = Icn + Icp - 2*Icc; ixf = Inc - Icc; iyf = Icn - Icc; ixb = Icc - Ipc; iyb = Icc - Icp; itt = u^2*ixx + v^2*iyy + 2*u*v*ixy; it = y*minmod(ixf,ixb) + v*minmod(iyf,iyb); amp*sign(itt)*abs(it)" fi vM:=abs(maxabs(im,iM)) if $vM>1e-8 *. {$1/$vM} rm[-3,-2] - else rm[-3--1] fi c $im,$iM } else # Inverse diffusion e[0--3] "Sharpen image$? with inverse diffusion and amplitude $1." foreach { im,iM:=[im,iM] +laplacian vM:=abs(maxabs(im,iM)) if $vM>1e-8 *. {$1/abs(maxabs(im,iM))} - else rm. fi c $im,$iM } fi #@cli sharpen_alpha : _amplitude[%]>=0,_nb_scales>0,0<=_anisotropy<=1,0<=_minimize_alpha<=1 #@cli : Sharpen selected images using a multi-scale and alpha boosting algorithm. #@cli : Default values: 'amplitude=1', 'nb_scales=5', 'anisotropy=0' and 'minimize_alpha=1'. sharpen_alpha : check "${1=1}>=0 && isint(${2=5},1) && inrange(${3=0},0,1) && inrange(${4=1},0,1)" e[^-1] "Sharpen image$? with a multi-scale and alpha boosting algorithm, with amplitude $1, $2 scales, "\ "anisotropy $3 and alpha minimization factor $4." foreach { split_opacity l[0] { split_alpha $2,0,$3,$4 foreach[^0] { sh 100% *. {$/($>+$<)):(1+$1)} c. 0,255 rm. } merge_alpha } a c } #@cli smooth : amplitude[%]>=0,_sharpness>=0,0<=_anisotropy<=1,_alpha[%],_sigma[%],_dl>0,_da>0,\ # _precision>0,_interpolation,_fast_approx={ 0 | 1 } : \ # nb_iterations>=0,_sharpness>=0,_anisotropy,_alpha,_sigma,_dt>0,0 : [tensor_field],_amplitude>=0,_dl>0,_da>0,\ # _precision>0,_interpolation,_fast_approx={ 0 | 1 } : \ # [tensor_field],_nb_iters>=0,_dt>0,0 : (+) #@cli : Smooth selected images anisotropically using diffusion PDE's, with specified field of #@cli : diffusion tensors. #@cli : 'interpolation' can be { 0:nearest | 1:linear | 2:runge-kutta }. #@cli : Default values: 'sharpness=0.7', 'anisotropy=0.3', 'alpha=0.6', 'sigma=1.1', 'dl=0.8', 'da=30', \ # 'precision=2', 'interpolation=0' and 'fast_approx=1'. #@cli : $ image.jpg repeat 3 smooth 40,0,1,1,2 done #@cli : $ image.jpg 100%,100%,1,2 rand[-1] -100,100 repeat 2 smooth[-1] 100,0.2,1,4,4 done warp[0] [-1],1,1,1 #@cli : $$ https://gmic.eu/oldtutorial/_smooth #@cli split_freq : smoothness>0[%] #@cli : Split selected images into low and high frequency parts. #@cli : $ image.jpg split_freq 2% split_freq : e[^-1] "Split image$? into low and high frequency parts, with smoothness $1." foreach { +b $1 -[0] [1] rv } #@cli solve_poisson : "laplacian_command",_nb_iterations>=0,_time_step>0,_nb_scales>=0 #@cli : Solve Poisson equation so that applying 'laplacian[n]' is close to the result of 'laplacian_command[n]'. #@cli : Solving is performed using a multi-scale gradient descent algorithm. #@cli : If 'nb_scales=0', the number of scales is automatically determined. #@cli : Default values: 'nb_iterations=60', 'dt=5' and 'nb_scales=0'. #@cli : $ image.jpg command "foo : gradient x" +solve_poisson foo +foo[0] +laplacian[1] solve_poisson : check "${2=60}>=0 && ${3=5}>0 && ${4=0}>=0" e[^-1] "Solve Poisson equation for image$?, for laplacian command '$1', with $2 iterations, time step $3 and "\ ${arg0\ !$4,$4,auto}" scales." foreach { [0] repeat $4?$4:int(max(log2(max(w,h))-1,1)) { f:=2^$< r[1] {0,max(1,w/$f)},{0,max(1,h/$f)},1,100%,3 +ri[0] [1],2 l. { $1 k[0] } repeat $2 { +laplacian.. -. .. *. {$3/max(1e-8,abs(im),abs(iM))} +[-3,-1] } rm. } rm[0] } #@cli split_alpha : _nb_scales[%]={ 0:auto | -S<0 | N>0 },_subsample={ 0:no | 1:yes },0<=_anisotropy<=1,\ # 0<=_minimize_alpha<=1 #@cli : Split selected images into alpha detail scales. #@cli : If 'nb_scales==-S', the lowest scale has a size of at least 'SxS'. #@cli : Parameter 'anisotropy' is only considered when 'subsample=0'. #@cli : Image reconstruction is done with command ''merge_alpha''. #@cli : Default values: 'nb_scales=0', 'subsample=0', 'anisotropy=0' and 'minimize_alpha=1'. split_alpha : check "(isint(${1=0}) || ispercentage($1)) && isbool(${2=0}) && inrange(${3=0},0,1) && "\ "inrange(${4=1},0,1)" s= if $1 s="$1 " fi s0,s1="out", e[^-1] "Split image$? into "${s}"alpha detail scales, with"$s$2" subsampling, anisotropy $3 "\ "and alpha minimization factor $4." foreach { bm={f}{b} ext={x} if ['$ext']!=0 ext..=. fi nb_scales_max:=int(log2(min(w,h))) nb_scales:=cut(ispercentage($1)?round($nb_scales_max*$1):\ $1<0?int(log2(min(w,h)/(-$1)))+1:\ $1?$1:$nb_scales_max,\ 1,$nb_scales_max) if $nb_scales>1 repeat $nb_scales-1 { if $2 # With cubic subsampling +r. {int([w,h]/2)},1,100%,2 +r. ..,..,1,100%,5 c. 0,255 sub_alpha... .,$4 rm. else # Without subsampling if $3 +bilateral. {0.25+2^$>},{(1-$3)*100} b. {0.5+$3} else +b. {0.25+2^$>} fi sub_alpha.. .,$4 fi } rv foreach { => ${bm}_$>$ext } fi } #@cli split_details : _nb_scales[%]={ 0:auto | -S<0 | N>0 },_base_scale[%]>=0,_detail_scale[%]>=0 #@cli : Split selected images into 'nb_scales' detail scales. #@cli : If 'base_scale' = 'detail_scale' = 0, the image decomposition is done with 'a trous' wavelets. #@cli : Otherwise, it uses laplacian pyramids with linear standard deviations. #@cli : Default values: 'nb_scales=0', 'base_scale=0' and 'detail_scale=0'. #@cli : $ image.jpg split_details , split_details : check "(isint(${1=0}) || ispercentage($1)) && ${2=0}>=0 && ${3=0}>=0" if !max($2,$3) e[0--3] "Split image$? using $1 scales and 'a trous' wavelets." else e[0--3] "Split image$? using $1 scales, with base scale $2 and detail scale $3." fi foreach { bm={f}{b} ext={x} if ['$ext']!=0 ext..=. fi nb_scales_max:=int(log2(min(w,h))) nb_scales:=cut(ispercentage($1)?round($nb_scales_max*$1):\ $1<0?int(log2(min(w,h)/(-$1)))+1:\ $1?$1:$nb_scales_max,\ 1,$nb_scales_max) if !max($2,$3) repeat $nb_scales-1 { +f. "begin(interpolation = 0; boundary = 1; d = 2^"$>"; d2 = d*2); i(x - d2) + i(x + d2) + 4*i(x - d) + 4*i(x + d) + 6*i;" /. 16 if h>1 f. "begin(interpolation = 0; boundary = 1; d = 2^"$>"; d2 = d*2); i(x,y - d2) + i(x,y + d2) + 4*i(x,y - d) + 4*i(x,y + d) + 6*i;" /. 16 fi if d>1 f. "begin(interpolation = 0; boundary = 1; d = 2^"$>"; d2 = d*2); i(x,y,z - d2) + i(x,y,z + d2) + 4*i(x,y,z - d) + 4*i(x,y,z + d) + 6*i;" /. 16 fi -.. . } else ss:=max(0.25,ispercentage($3)?$3*max(w,h):$3) se:=max(0.25,ispercentage($2)?$2*max(w,h):$2) repeat $nb_scales-1 { +b. {lerp($ss,$se,$>/($>+$<))} -.. . } fi rv foreach { => ${bm}_$>$ext } } #@cli structuretensors : _scheme={ 0:centered | 1:forward/backward } #@cli : Compute the structure tensor field of selected images. #@cli : Default value: 'scheme=0'. #@cli : $ image.jpg structuretensors abs pow 0.2 #@cli : $$ https://gmic.eu/oldtutorial/_structuretensors structuretensors : skip "${1=}" l[] { if "isbool($1)" scheme=$1 else scheme=0 noarg fi onfail scheme=0 noarg } s0,s1=centered,forward-backward e[^-1] "Compute structure tensor field of image$?, with "${s$scheme}" scheme." foreach { if d>1 # 3D if $scheme # Fwd/bwd +g xyz,-1 +g[0] xyz,1 sqr[^0] +[1,4] +[2,4] +[3,4] /[^0] 2 g[0] xyz,0 [2] *. [1] *[2] [0] *[0,1] compose_channels + mv[2] 0 mv. -2 a c else # Centered g xyz,0 +*[0,1] +*[0,2] +*[1,2] sqr[0-2] compose_channels + mv[3,4] 1 rv[4,5] a c fi else # 2D if $scheme # Fwd/bwd +g xy,-1 +g[0] xy,1 sqr[^0] +[1,3] +[2,3] /[^0] 2 g[0] xy,0 *[0,1] compose_channels + mv[0] 2 a c else # Centered g xy,0 +* sqr[0,1] compose_channels + rv[1,2] a c fi fi } #@cli solidify : _smoothness[%]>=0,_diffusion_type={ 0:isotropic | 1:Delaunay-guided | 2:edge-oriented },\ # _diffusion_iter>=0 #@cli : Solidify selected transparent images. #@cli : Default values: 'smoothness=75%', 'diffusion_type=1' and 'diffusion_iter=20'. #@cli : $ image.jpg 100%,100% circle[-1] 50%,50%,25%,1,255 append c +solidify , display_rgba solidify : check "${1=75%}>=0 && isint(${2=1},0,2) && ${3=20}>=0" s0="isotropic" s1="Delaunay-guided" s2="edge-oriented" e[^-1] "Solidify transparent image$? with smoothness $1 and $3 iterations of "${s$2}" diffusion." foreach { split_opacity if $!>1 <=. 128 inpaint_pde.. [1],${1-3} rm. c 0,255 fi } #@cli syntexturize : _width[%]>0,_height[%]>0 #@cli : Resynthetize 'width'x'height' versions of selected micro-textures by phase randomization. #@cli : The texture synthesis algorithm is a straightforward implementation of the method described in : #@cli : . #@cli : Default values: 'width=height=100%'. #@cli : $ image.jpg crop 2,282,50,328 +syntexturize 320,320 syntexturize : check "${1=100%}>0 && ${2=$1}>0" e[^-1] "Resynthetize $1x$2 versions of texture$? by phase randomization." foreach { # Prepare input image data. mM:=im,iM repeat s { sh. $> sum$>:=is var$>:=iv rm. } # Retrieve some stats for post-normalization. nw:=ispercentage($1)?$1*w:$1 nh:=ispercentage($2)?$2*h:$2 repeat s { sum$>*={$nw*$nh/(w*h)} } # Re-estimate output (0,0) frequency. if $nw>w||$nh>h # Spot extension required when rendering on bigger image. periodize_poisson 100%,100% rectangle. 5,5,{w-6},{h-6},1,1 b. 2 n. 0,1 $nw,$nh,1,{-2,s} fc. ${average_vectors...} j. ...,{(w-{-2,w})/2},{(h-{-2,h})/2},0,0,1,.. rm[-3,-2] else r $nw,$nh,1,100%,0,0,0.5,0.5 periodize_poisson fi fft # Compute coherent random phase. 100%,100% rand. {[-pi,pi]} =. 0 if !(w%2) =. {(u<0.5)*pi},{int(w/2)} fi if !(h%2) =. {(u<0.5)*pi},0,{int(h/2)} fi if !(h%2)&&!(h%2) =. {(u<0.5)*pi},{int(w/2)},{int(h/2)} fi # Add random phase to fft of input image. +sin. cos.. +*[-4,-1] +*[-4,-3] +[-2,-1] *[-5,-3] *[-3,-2] -[-3,-2] # Get synthetised result and normalize it. repeat s { =.. ${sum$>},0,0,0,$> =. 0,0,0,0,$> } ifft rm. repeat s { sh. $> avg:=ia -. $avg *. {sqrt(${var$>}/(iv?iv:1))} +. $avg rm. } c $mM } #@cli syntexturize_matchpatch : _width[%]>0,_height[%]>0,_nb_scales>=0,_patch_size>0,_blending_size>=0,_precision>=0 #@cli : Resynthetize 'width'x'height' versions of selected micro-textures using a patch-matching algorithm. #@cli : If 'nbscales==0', the number of scales used is estimated from the image size. #@cli : Default values: 'width=height=100%', 'nb_scales=0', 'patch_size=7', 'blending_size=5' and 'precision=1'. #@cli : $ image.jpg crop 25%,25%,75%,75% syntexturize_matchpatch 512,512 syntexturize_matchpatch : check "${1=100%}>0 && ${2=$1}>0 && isint(${3=0},0) && isint(${4=7},1) && "\ "${5=5}>=0 && ${6=1}>=0" e[^-1] "Resynthetize $1x$2 version(s) of texture$? using a patch-matching algorithm with "\ ${"if $3 u \"$3 \" else u auto- fi"}"scales, $4x$4 patches, blending size $5 and precision $6." foreach { nb_scales:=round($3?$3:log2(min(w,h)/16),1,1) width:=ispercentage($1)?round(w*$1,1,1):$1 height:=ispercentage($2)?round(h*$2,1,1):$2 repeat $nb_scales { scale:=100*(0.5^$<) +r[0] $scale%,$scale%,1,3,2 if !$> # Initialization. {1+round(w*$width/{0,w},1,1)},{1+round(h*$height/{0,h},1,1)},1,1 noise. 0.2,2 ==. 1 +distance. 1 *. -1 label_fg.. 0 watershed.. . rm. 100%,100%,1,1,x +f. y a[-2,-1] c channels. 0,2 +blend. ..,shapeaverage -.. . rm. channels. 0,1 {-2,iM+1} rand. 0,{-4,w} +rand. 0,{-4,h} a[-2,-1] c map... . rm. +[-2,-1] round. s. c %.. {-3,w} %. {-3,h} a[-2,-1] c else # Upscale. rv[-2,-1] channels. 0,1 *. 2 r. 200%,200%,1,2,1 f. "*upc = i(x - 1,y,0,0); vpc = i(x - 1,y,0,1); ucp = i(x,y - 1,0,0); vcp = i(x,y - 1,0,1); ucc = i(x,y,0,0); vcc = i(x,y,0,1); if (ucc==upc && vcc==vpc && !c, upc + 1, if (ucc==ucp && vcc==vcp && c==1, vcp + 1,i))" fi # Synthesis. psize={-2,min(w,h,$4)} repeat 1+$6*$< { psynth:=int(max(3,$5*$scale%)) +warp_patch.. .,$psynth matchpatch. ...,$psize,$psize,1,4,4,0,0,.. rm.. } rm.. } warp_patch.. .,$5 rm. r $width,$height,1,100%,0,0,0.5,0.5 } # _syntexturize_matchpatch : [correspondence_map],blend_size>0 _syntexturize_matchpatch : check ${is_image_arg\ $1}" && isint(${2=3},0)" if $2<=1 pass$1 warp[^-1] .,0 rm. else repeat $! { pass$1 l[$>,-1] { [1],[1],1,[0] f. "*begin( boundary = 1; const patch_size = $2; const p2 = int(patch_size/2); const p1 = patch_size - p2 - 1; avg = resize([0],s#0); # Pre-compute gaussian kernel. wpq = resize([0],patch_size^2); g = 0; for (q = -p1, q<=p2, ++q, for (p = -p1, p<=p2, ++p, wpq[g++] = exp(-(p^2 + q^2)/(2*(0.3*patch_size)^2)); ); ); ); g = 0; avg = 0; norm = 0; for (q = -p1, q<=p2, ++q, for (p = -p1, p<=p2, ++p, U = I(#1,x + p,y + q); w = wpq[g++]; avg+=w*I(#0,U - [p,q]); norm+=w; ); ); avg/norm" k. } } fi #@cli tv_flow : _nb_iter>=0,_dt,_keep_sequence={ 0 | 1 } #@cli : Apply iterations of the total variation flow on selected images. #@cli : Default values: 'nb_iter=10', 'dt=30' and 'keep_sequence=0'. #@cli : $ image.jpg +tv_flow 40 tv_flow : skip ${1=10},${2=30},${3=0} e[^-1] "Apply $1 iterations of the total variation flow on image$?, with time step $2." pde_flow $1,$2,curvature,$3 #@cli unsharp : radius[%]>=0,_amount>=0,_threshold[%]>=0 #@cli : Apply unsharp mask on selected images. #@cli : Default values: 'amount=2' and 'threshold=0'. #@cli : $ image.jpg blur 3 +unsharp 1.5,15 cut 0,255 unsharp : check "${2=2}>=0" skip ${3=0} e[^-1] "Apply unsharp mask on image$?, with radius $1, amount $2 and threshold $3." foreach { +b $1 -. .. if $3 +norm. >=. $3 *[-2,-1] fi *. $2 - } #@cli unsharp_octave : _nb_scales>0,_radius[%]>=0,_amount>=0,threshold[%]>=0 #@cli : Apply octave sharpening on selected images. #@cli : Default values: 'nb_scales=4', 'radius=1', 'amount=2' and 'threshold=0'. #@cli : $ image.jpg blur 3 +unsharp_octave 4,5,15 cut 0,255 unsharp_octave : check "${1=4}>0 && ${3=2}>=0" skip ${2=1},${4=0} e[^-1] "Apply octave sharpening on image$?, with $1 scales, radius $2, amount $3 and threshold $4." foreach { nm={n} +f 0 weight=0 repeat $1 { +unsharp[0] {$2*2^-$<},$3,$4 *. {2^-$>} weight+=2^-$> +[1,-1] } rm[0] / $weight => $nm } #@cli vanvliet : std_deviation>=0[%],order={ 0 | 1 | 2 | 3 },axis={ x | y | z | c },_boundary_conditions : (+) #@cli : Apply Vanvliet recursive filter on selected images, along specified axis and with #@cli : specified standard deviation, order and boundary conditions. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default value: 'boundary_conditions=1'. #@cli : $ image.jpg +vanvliet 3,1,x #@cli : $ image.jpg +vanvliet 30,0,x vanvliet[-2] 30,0,y add #@cli voronoi #@cli : Compute the discrete Voronoi diagram of non-zero pixels in selected images. #@cli : $ 400,400 noise 0.2,2 eq 1 +label_fg 0 voronoi[-1] +gradient[-1] xy,1 append[-2,-1] c \ # norm[-1] ==[-1] 0 map[-2] 2,2 mul[-2,-1] normalize[-2] 0,255 dilate_circ[-2] 4 reverse max voronoi : e[^-1] "Compute the discrete Voronoi diagram of non-zero pixels in image$?." foreach { s c foreach { +!=. 0 distance. 1 *. -1 watershed.. . rm. } a c } #@cli watermark_fourier : text,_size>0 #@cli : Add a textual watermark in the frequency domain of selected images. #@cli : Default value: 'size=33'. #@cli : $ image.jpg +watermark_fourier "Watermarked!" +display_fft remove[-3,-1] normalize 0,255 \ # append[-4,-2] y append[-2,-1] y watermark_fourier : check ${2=33}>0 e[^-1] "Add textual watermark '$1' with size $2 in the frequency domain of image$?." i[0] 0 t[0] "$1",0,0,$2,1,1 >=[0] 0.5 autocrop[0] 0 repeat $!-1 { w2,h2:=int([w,h]/2) fft. shift[-2,-1] $w2,$h2,0,0,2 [0],[0],1,{s} x0,y0,x1,y1:=" const x = round(w#-2/3); const y = round(h#-2/3); const w2 = int(w/2); const h2 = int(h/2); x0 = x - w2; y0 = y - h2; x1 = w#-2 - 1 - x - w2; y1 = h#-2 - 1 - y - h2; [ x0,y0,x1,y1 ]" j[-3,-2] .,$x0,$y0,0,0,1,[0] mirror[0] x j[-3,-2] .,$x1,$y0,0,0,1,[0] mirror[0] y j[-3,-2] .,$x1,$y1,0,0,1,[0] mirror[0] x j[-3,-2] .,$x0,$y1,0,0,1,[0] mirror[0] y rm. shift[-2,-1] -$w2,-$h2,0,0,2 ifft[-2,-1] rm. mv. 1 } rm[0] #@cli watershed : [priority_image],_is_high_connectivity={ 0 | 1 } : (+) #@cli : Compute the watershed transform of selected images. #@cli : Default value: 'is_high_connectivity=1'. #@cli : $ 400,400 noise 0.2,2 eq 1 +distance 1 mul[-1] -1 label[-2] watershed[-2] [-1] mod[-2] 256 map[-2] 0 reverse #--------------------------------- # #@cli :: Features Extraction # #--------------------------------- #@cli area : tolerance>=0,is_high_connectivity={ 0 | 1 } #@cli : Compute area of connected components in selected images. #@cli : Default values: 'is_high_connectivity=0'. #@cli : $ image.jpg luminance stencil[-1] 1 +area 0 #@cli : $$ https://gmic.eu/oldtutorial/_area area : check "$1>=0" skip ${2=0} e[^-1] "Compute area of connected components in image$?, with tolerance $1 and "\ ${arg0\ !$2,high,low}" connectivity." foreach { s c foreach { label $1,$2 nb:=iM+1 +histogram $nb,0,{$nb-1} map.. . rm. } a c } #@cli area_fg : tolerance>=0,is_high_connectivity={ 0 | 1 } #@cli : Compute area of connected components for non-zero values in selected images. #@cli : Similar to 'area' except that 0-valued pixels are not considered. #@cli : Default values: 'is_high_connectivity=0'. #@cli : $ image.jpg luminance stencil[-1] 1 +area_fg 0 area_fg : check "$1>=0" skip ${2=0} e[^-1] "Compute area of foreground connected components in image$?, with tolerance $1 and "\ ${arg0\ !$2,high,low}" connectivity." foreach { s c foreach { label_fg $1,$2 nb:=1+iM +histogram $nb,0,{$nb-1} =. 0 map.. . rm. } a c } #@cli at_line : x0[%],y0[%],z0[%],x1[%],y1[%],z1[%] #@cli : Retrieve pixels of the selected images belonging to the specified line (x0,y0,z0)-(x1,y1,z1). #@cli : $ image.jpg +at_line 0,0,0,100%,100%,0 line[0] 0,0,100%,100%,1,0xFF00FF00,255,0,0 at_line : check ${7=100%}>=0 e[^-1] "Retrieve pixels of image$?, belonging to line ($1,$2,$3)-($4,$5,$6)." foreach { x0:=ispercentage($1)?(w-1)*$1:$1 y0:=ispercentage($2)?(h-1)*$2:$2 z0:=ispercentage($3)?(d-1)*$3:$3 x1:=ispercentage($4)?(w-1)*$4:$4 y1:=ispercentage($5)?(h-1)*$5:$5 z1:=ispercentage($6)?(d-1)*$6:$6 ($x0,$x1^$y0,$y1^$z0,$z1) r. {1+max(abs($x1-$x0),abs($y1-$y0),abs($z1-$z0))},1,1,3,3 round. 1 warp[0] .,0,0,0 rm. } #@cli at_quadrangle : x0[%],y0[%],x1[%],y1[%],x2[%],y2[%],x3[%],y3[%],_interpolation,_boundary_conditions : \ # x0[%],y0[%],z0[%],x1[%],y1[%],z1[%],x2[%],y2[%],z2[%],x3[%],y3[%],z3[%],_interpolation,_boundary_conditions #@cli : Retrieve pixels of the selected images belonging to the specified 2D or 3D quadrangle. #@cli : 'interpolation' can be { 0:nearest-neighbor | 1:linear | 2:cubic }. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : $ image.jpg params=5%,5%,95%,5%,60%,95%,40%,95% +at_quadrangle $params polygon.. 4,$params,0.5,255 at_quadrangle : check "$#>=8 && $#<=14 && $#!=11" _at_quadrangle{$#<12?2:3} $* _at_quadrangle2 : check "${9=1}>=0 && $9<=2 && ${10=0}>=0 && $10<=3" foreach { x0:=round(ispercentage($1)?(w-1)*$1:$1) y0:=round(ispercentage($2)?(h-1)*$2:$2) x1:=round(ispercentage($3)?(w-1)*$3:$3) y1:=round(ispercentage($4)?(h-1)*$4:$4) x2:=round(ispercentage($5)?(w-1)*$5:$5) y2:=round(ispercentage($6)?(h-1)*$6:$6) x3:=round(ispercentage($7)?(w-1)*$7:$7) y3:=round(ispercentage($8)?(h-1)*$8:$8) ($x0,$x1;$x3,$x2^$y0,$y1;$y3,$y2) r. {P0=[$x0,$y0];P1=[$x1,$y1];P2=[$x2,$y2];P3=[$x3,$y3];\ 1+round([max(norm(P1-P0),norm(P3-P2)),max(norm(P3-P0),norm(P2-P1))])},1,2,3 warp.. .,0,$9,$10 rm. } _at_quadrangle3 : check "${13=1}>=0 && $13<=2 && ${14=0}>=0 && $14<=3" foreach { x0:=round(ispercentage($1)?(w-1)*$1:$1) y0:=round(ispercentage($2)?(h-1)*$2:$2) z0:=round(ispercentage($3)?(h-1)*$3:$3) x1:=round(ispercentage($4)?(w-1)*$4:$4) y1:=round(ispercentage($5)?(h-1)*$5:$5) z1:=round(ispercentage($6)?(h-1)*$6:$6) x2:=round(ispercentage($7)?(w-1)*$7:$7) y2:=round(ispercentage($8)?(h-1)*$8:$8) z2:=round(ispercentage($9)?(h-1)*$9:$9) x3:=round(ispercentage($10)?(w-1)*$10:$10) y3:=round(ispercentage($11)?(h-1)*$11:$11) z3:=round(ispercentage($12)?(h-1)*$12:$12) ($x0,$x1;$x3,$x2^$y0,$y1;$y3,$y2^$z0,$z1;$z3,$z2) r. {P0=[$x0,$y0,$z0];P1=[$x1,$y1,$z1];P2=[$x2,$y2,$z2];P3=[$x3,$y3,$z2];\ 1+round([max(norm(P1-P0),norm(P3-P2)),max(norm(P3-P0),norm(P2-P1))])},1,3,3 warp.. .,0,$13,$14 rm. } #@cli barycenter #@cli : Compute the barycenter vector of pixel values. #@cli : $ 256,256 ellipse 50%,50%,20%,20%,0,1,1 deform 20 +barycenter +ellipse[-2] {@0,1},5,5,0,10 barycenter : e[^-1] "Compute the barycenter vector of pixel values of image$?." norm foreach { nm={0,b} sum:=is if $sum>0 if d>1 +* 'z' z:=is rm. else z=0 fi if h>1 +* 'y' y:=is rm. else y=0 fi * 'x' x:=is rm. ({$x/$sum};{$y/$sum};{$z/$sum}) else ({w/2},{h/2},{d/2}) rm.. fi => "[barycenter of '"$nm"']" } #@cli betti #@cli : Compute Betti numbers B0,B1 and B2 from selected 3D binary shapes. #@cli : Values B0,B1 and B2 are returned in the status. \ # When multiple images are selected, the B0,B1,B2 of each image are concatenated in the status. #@cli : (see 'https://en.wikipedia.org/wiki/Betti_number' for details about Betti numbers). betti : e[^-1] "Compute Betti numbers B0,B1 and B2 from binary shape$?." res,c= foreach { # Compute Euler characteristic. {[2*w+1,2*h+1,2*d+1]} eval.. "*i?( # Compute volume of 0D,1D,2D and 3D primitives. X = 2*x; Y = 2*y; Z = 2*z; X1 = X + 1; Y1 = Y + 1; Z1 = Z + 1; X2 = X + 2; Y2 = Y + 2; Z2 = Z + 2; # Set 8 points i(#-1,X,Y,Z) = i(#-1,X2,Y,Z) = i(#-1,X2,Y2,Z) = i(#-1,X,Y2,Z) = i(#-1,X,Y,Z2) = i(#-1,X2,Y,Z2) = i(#-1,X2,Y2,Z2) = i(#-1,X,Y2,Z2) = 1; # Set 12 segments. i(#-1,X1,Y,Z) = i(#-1,X2,Y1,Z) = i(#-1,X1,Y2,Z) = i(#-1,X,Y1,Z) = i(#-1,X1,Y,Z2) = i(#-1,X2,Y1,Z2) = i(#-1,X1,Y2,Z2) = i(#-1,X,Y1,Z2) = i(#-1,X,Y,Z1) = i(#-1,X2,Y,Z1) = i(#-1,X,Y2,Z1) = i(#-1,X2,Y2,Z1) = 2; # Set 6 surfaces. i(#-1,X1,Y1,Z) = i(#-1,X1,Y1,Z2) = i(#-1,X1,Y,Z1) = i(#-1,X1,Y2,Z1) = i(#-1,X,Y1,Z1) = i(#-1,X2,Y1,Z1) = 3; # Set 1 volume. i(#-1,X1,Y1,Z1) = 4; )" histogram. 4,1,4 Xi:=i[0]-i[1]+i[2]-i[3] rm. # Euler characteristic # Compute Betti numbers B0, B1 and B2. expand. xyz,1 # Make sure borders are 0 +label. 0,1 {iM+1} eval.. "*i(#-3)?(i[#-1,i] = 1)" B0:=is rm[-2,-1] # Number of connected components +label. 0,0 {iM+1} eval.. "*!i(#-3) && i?(i[#-1,i] = 1)" B2:=is rm[-2,-1] # Number of cavities B1:=$B0+$B2-$Xi shrink. xyz,1 res.=$c$B0,$B1,$B2 c=, } u $res #@cli canny : _sigma[%]>=0,_low_threshold>=0,_high_threshold>=0 #@cli : Locate image edges using Canny edge detector. #@cli : Default values: 'sigma=1', 'low_threshold=0.05', 'high_threshold=0.15'. #@cli : $ image.jpg canny 1 canny : check "${1=1}>=0 && ${2=0.05}>=0 && ${3=0.15}>=0 && isint(${4=0},0,2)" e[^-1] "Apply Canny edge detection on image$?, with sigma $1, low threshold $2 and high threshold $3." low,high:=$2*$3,$3 foreach { nm={n} b $1 (1,0,-1;2,0,-2;1,0,-1^1,2,1;0,0,0;-1,-2,-1) convolve.. .,1,0,0 rm. s. c,2 # Gaussian + Sobel +atan2.. . mul. {4/pi} round. 1 mod. 4 # Quantized gradient orientation sqr[0,1] +[0,1] sqrt.. # Gradient norm f.. " # Suppress non-maximum gradients A = i#1; Y = A<1?max(j(0,-1),j(0,1)): A<2?max(j(-1,-1),j(1,1)): A<3?max(j(-1),j(1)): max(j(-1,1),j(1,-1)); i>=Y?i:0" rm. div {iM} +ge $high ge.. $low # Double thresholding # Hysteresis. f. ">max(crop(x-1,y-1,z,c,3,3,1,1)) && i#0?1:i" f. " $nm } #@cli delaunay : _output_type={ 0:image | 1:coordinates/triangles } #@cli : Generate discrete 2D Delaunay triangulation of non-zero pixels in selected images. #@cli : Input images must be scalar. #@cli : Each pixel of the output image is a triplet (a,b,c) meaning the pixel belongs to #@cli : the Delaunay triangle 'ABC' where 'a','b','c' are the labels of the pixels 'A','B','C'. #@cli : $ 400,400 rand 32,255 100%,100% noise. 0.4,2 eq. 1 mul +delaunay #@cli : $ image.jpg 100%,100% noise. 2,2 eq. 1 delaunay. +blend shapeaverage0 delaunay : skip "${1=0}" mode=0 if isin(['"$1"'],'0','1') mode=$1 else mode=0 noarg fi s0,s1=image,coordinates e[^-1] "Generate discrete 2D Delaunay triangulation of non-zero pixels in image$?, in "${s$mode}" mode." foreach { bnm={b} nm={n} # Extract and label non-zero pixels. 1,64,1,2 =. 1,0,100% f.. "begin(N = 0); I!=0?(da_push([x,y]);++N):0; end(resize(#-1,1,da_size(),1,2,0))" # Construct discrete voronoi diagram. +neq.. 0 distance. 1 *. -1 watershed... . rm. # Extract Delaunay triangles from discrete voronoi. 1,64,1,3 eval... " V = crop(x,y,2,2); min(V)?( V[1]==V[0] && V[2]!=V[0] && V[3]!=V[0] && V[3]!=V[2]?( # [ a,a,b,c ] da_push([ V[0],V[2],V[3] ]); ):(V[2]==V[0] && V[1]!=V[0] && V[3]!=V[0] && V[3]!=V[1]) || # [ a,b,a,c ] (V[1]==V[2] && V[0]!=V[1] && V[3]!=V[0] && V[3]!=V[1])?( # [ a,b,b,c ] da_push([ V[0],V[1],V[3] ]); ):(V[3]==V[0] && V[1]!=V[0] && V[2]!=V[0] && V[2]!=V[1]) || # [ a,b,c,a ] (V[1]==V[3] && V[0]!=V[1] && V[2]!=V[0] && V[2]!=V[1]) || # [ a,b,c,b ] (V[2]==V[3] && V[0]!=V[1] && V[0]!=V[2] && V[1]!=V[2]) ?( # [ a,b,c,c ] da_push([ V[0],V[1],V[2] ]); ):V[0]!=V[1] && V[0]!=V[2] && V[0]!=V[3] && V[1]!=V[2] && V[1]!=V[3] && V[2]!=V[3]?( # [ a,b,c,d ] da_push([ V[0],V[1],V[2] ]); da_push([ V[1],V[3],V[2] ]); ); end(resize(#-1,1,da_size(),1,3,0)); )" # Return output in expected mode. if $mode k[-2,-1] => ${bnm}_points,${bnm}_faces else {0,[w,h,1,3]} eval.. "polygon(#-1,3,I[#1,i0],I[#1,i1],I[#1,i2],1,i0,i1,i2)" k. => $nm fi } #@cli detect_skin : 0<=tolerance<=1,_skin_x,_skin_y,_skin_radius>=0 #@cli : Detect skin in selected color images and output an appartenance probability map. #@cli : Detection is performed using CbCr chromaticity data of skin pixels. #@cli : If arguments 'skin_x', 'skin_y' and 'skin_radius' are provided, skin pixels are learnt #@cli : from the sample pixels inside the circle located at ('skin_x','skin_y') with radius 'skin_radius'. #@cli : Default value: 'tolerance=0.5' and 'skin_x=skiny=radius=-1'. detect_skin : check "${1=0.5}>=0 && $1<=1" skip ${2=-1},${3=-1},${4=-1} if $2<0||$3<=0||$4<=0 e[0--3] "Detect skin in image$?, using tolerance $1." m0=120.9292108800069 m1=142.5745272918084 A=0.09749985486268997 B=0.06388871371746063 C=0.05250053107738495 to_rgb srgb2rgb rgb2ycbcr channels 1,2 foreach { whd={w},{h},{d} r {whd},2,1,1,-1 s y -[0] $m0 -[1] $m1 a y i[0] ($A,$B;$B,$C) +m* rm[0] * s y + *. {$1-1} exp. r $whd,1,-1 } else e[0--3] "Detect skin in image$?, using tolerance $1 and target circle at ($2,$3) with radius $4." to_rgb srgb2rgb rgb2ycbcr channels 1,2 foreach { 100%,100% circle[1] $2,$3,$4,1,1 +f[1] "i?y:-1" f[1] "i?x:-1" discard[1,2] -1 a[1,2] c +warp[0] [1],0,0,1 rm[1] s[1] c m0={1,ia} -[1] $m0 m1={2,ia} -[2] $m1 M:=h a[1,2] x +transpose[1] rv[1,2] m*[1,2] /[1] $M invert[1] rv whd={w},{h},{d} r[1] {whd},2,1,1,-1 s[1] y -[1] $m0 -[2] $m1 a[1,2] y +m* rm[0] * s y + *. {$1-1} exp. r $whd,1,-1 } fi #@cli displacement : [source_image],_smoothness,_precision>=0,_nb_scales>=0,_iteration_max>=0,is_backward={ 0 | 1 },\ # _[guide] : (+) #@cli : Estimate displacement field between specified source and selected target images. #@cli : If 'smoothness>=0', regularization type is set to isotropic, else to anisotropic. #@cli : If 'nbscales==0', the number of scales used is estimated from the image size. #@cli : Default values: 'smoothness=0.1', 'precision=5', 'nb_scales=0', 'iteration_max=10000', 'is_backward=1' \ # and '[guide]=(unused)'. #@cli : $ image.jpg +rotate 3,1,0,50%,50% +displacement[-1] [-2] quiver[-1] [-1],15,1,1,1,{1.5*iM} #@cli distance : isovalue[%],_metric : isovalue[%],[metric],_method : (+) #@cli : Compute the unsigned distance function to specified isovalue, opt. according to a custom metric. #@cli : 'metric' can be { 0:chebyshev | 1:manhattan | 2:euclidean | 3:squared-euclidean }. #@cli : 'method' can be { 0:fast-marching | 1:low-connectivity dijkstra | 2:high-connectivity dijkstra | \ # 3:1+return path | 4:2+return path }. #@cli : Default value: 'metric=2' and 'method=0'. #@cli : $ image.jpg threshold 20% distance 0 pow 0.3 #@cli : $ 400,400 set 1,50%,50% +distance[0] 1,2 +distance[0] 1,1 distance[0] 1,0 mod 32 threshold 16 append c #@cli : $$ https://gmic.eu/oldtutorial/_distance #@cli fftpolar #@cli : Compute fourier transform of selected images, as centered magnitude/phase images. #@cli : $ image.jpg fftpolar ellipse 50%,50%,10,10,0,1,0 ifftpolar fftpolar : e[^-1] "Compute fourier transform of image$?, as centered magnitude/phase images." foreach { fft complex2polar shift {-round(w/2)},{-round(h/2)},{-round(d/2)},0,2 } #@cli histogram : nb_levels>0[%],_min_value[%],_max_value[%] : (+) #@cli : Compute the histogram of selected images. #@cli : If value range is set, the histogram is estimated only for pixels in the specified #@cli : value range. Argument 'max_value' must be specified if 'min_value' is set. #@cli : Default values: 'min_value=0%' and 'max_value=100%'. #@cli : $ image.jpg +histogram 64 display_graph[-1] 400,300,3 #@cli histogram_masked : [mask],nb_levels>0[%],_min_value[%],_max_value[%] #@cli : Compute the masked histogram of selected images. #@cli : Default values: 'min_value=0%' and 'max_value=100%'. histogram_masked : check ${"is_image_arg $1"}" && isint($2,1)" skip ${3=0%},${4=100%} e[^-1] "Compute histogram of image$?, with mask $1, $2 levels in range [$3,$4]." pass$1 foreach[^-1] { vmin,vmax:="1 + (ispercentage($3)?$3:($3 - im)/(iM - im)),"\ "1 + (ispercentage($4)?$4:($4 - im)/(iM - im))" n 1,2 pass$1 !=. 0 * discard 0 histogram $2,$vmin,$vmax } rm. #@cli histogram_nd : nb_levels>0[%],_value0[%],_value1[%] #@cli : Compute the 1D,2D or 3D histogram of selected multi-channels images (having 1,2 or 3 channels). #@cli : If value range is set, the histogram is estimated only for pixels in the specified #@cli : value range. #@cli : Default values: 'value0=0%' and 'value1=100%'. #@cli : $ image.jpg channels 0,1 +histogram_nd 256 histogram_nd : check $1>0 skip ${2=0%},${3=100%} e[^-1] "Compute histogram of multi-channels image$?, using $1 levels in range [$1,$2]." foreach { nm={n} vmin,vmax:="const a = ispercentage($2)?lerp(im,iM,$2):$2; const b = ispercentage($3)?lerp(im,iM,$3):$3; [ min(a,b), max(a,b) ]" nb_levels:=max(1,round(ispercentage($1)?(1+$vmax-$vmin)*$1:$1)) r {whd},1,1,{min(3,s)},-1 if s==1 # 1d histogram $nb_levels,$vmin,$vmax elif s==2 # 2d $nb_levels,$nb_levels eval.. "ind(p) = cut(int($nb_levels*(p - $vmin)/($vmax - $vmin)),0,$nb_levels - 1); inrange(i0,$vmin,$vmax) && inrange(i1,$vmin,$vmax)? ++i(#-1,ind(i0),ind(i1),0,0); I" else # 3d $nb_levels,$nb_levels,$nb_levels eval.. "ind(p) = cut(int($nb_levels*(p - $vmin)/($vmax - $vmin)),0,$nb_levels - 1); inrange(i0,$vmin,$vmax) && inrange(i1,$vmin,$vmax) && inrange(i2,$vmin,$vmax)? ++i(#-1,ind(i0),ind(i1),ind(i2),0); I" fi k. => $nm } #@cli histogram_cumul : _nb_levels>0,_is_normalized={ 0 | 1 },_val0[%],_val1[%] #@cli : Compute cumulative histogram of selected images. #@cli : Default values: 'nb_levels=256', 'is_normalized=0', 'val0=0%' and 'val1=100%'. #@cli : $ image.jpg +histogram_cumul 256 histogram[0] 256 display_graph 400,300,3 histogram_cumul : check ${1=256}>0 skip ${2=0},${3=0%},${4=100%} arg0 !$2,"normalized ","" e[^-1] "Compute "${}"cumulative histogram of image$?, using $1 levels." histogram $1,$3,$4 cumulate if $2 foreach { / {iM} } fi #@cli histogram_pointwise : nb_levels>0[%],_value0[%],_value1[%] #@cli : Compute the histogram of each vector-valued point of selected images. #@cli : If value range is set, the histogram is estimated only for values in the specified #@cli : value range. #@cli : Default values: 'value0=0%' and 'value1=100%'. histogram_pointwise : skip ${2=0%},${3=100%} e[^-1] "Compute the pointwise histogram of vector-valued points in image$?, with $1 levels." foreach { nm={n} nb_levels:=round(ispercentage($1)?(iM-im)*$1:$1) value0:=ispercentage($2)?im+(iM-im)*$2:$2 value1:=ispercentage($3)?im+(iM-im)*$3:$3 - $value0 * {$nb_levels/max(1,abs($value1-$value0))} c 0,{$nb_levels-1} round w,h,d={w},{h},{d} r {whd},{s},1,1,-1 i.. (0,{w-1}) r.. .,.,1,1,3 round.. r[-2,-1] 300%,100%,1,1,4 shift. 1 +[-2,-1] y. i.. ({'CImg3d'},{h/3},{h/3}) (1,0;1,{h/3-1}) r. 2,{-2,h/3},1,1,3 round. 3,100%,1,1,1 1,100%,1,1,-1 y[-5,-3,-2] a[-5--1] y {$w*$h*$d},$nb_levels j3d. ..,0,0,0,1,0,0,0 rm.. r $w,$h,$d,$nb_levels,-1 => $nm } #@cli hough : _width>0,_height>0,gradient_norm_voting={ 0 | 1 } #@cli : Compute hough transform (theta,rho) of selected images. #@cli : Default values: 'width=512', 'height=width' and 'gradient_norm_voting=1'. #@cli : $ image.jpg +blur 1.5 hough[-1] 400,400 blur[-1] 0.5 add[-1] 1 log[-1] hough : check "${1=512}>0 && ${2=$1}>0" skip ${3=1} e[^-1] "Compute $1x$2 hough transform of image$?, "${arg0\ !$3,with,without}" gradient norm voting." slices 50% luminance foreach { nm={n} rhomax:=sqrt(w^2+h^2)/2 g (0,{w-1}) (0;{{-2,h}-1}) r[-2,-1] {-3,w},{-3,h},1,1,3 -.. {w/2} -. {h/2} complex2polar[-4--1] -. ... polar2complex[-2,-1] rm. +<. 0 *. {pi} +[-3,-1] abs. %.. {2*pi} *. {$2/$rhomax} *.. {0.5*$1/pi} y[-3--1] x {w} mv[-4] $! if !$3 f. 1 fi a y pointcloud 1 r $1,$2,1,1,0 => $nm } #@cli ifftpolar #@cli : Compute inverse fourier transform of selected images, from centered magnitude/phase images. ifftpolar : e[^-1] "Compute inverse fourier transform of image$?, from centered magnitude/phase images." repeat int($!/2) { l[$>,{$>+1}] { shift {round(w/2)},{round(h/2)},{round(d/2)},0,2 polar2complex ifft rm. } } #@cli img2patches : patch_size>0,_overlap[%]>0,_boundary_conditions #@cli : Decompose selected 2D images into (possibly overlapping) patches and stack them along the z-axis. #@cli : 'overlap' must be in range '[0,patch_size-1]'. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'overlap=0' and 'boundary_conditions=0'. #@cli : See also: ''patches2img''. #@cli : $ image.jpg img2patches 64 img2patches : check "isint($1,1) && ${2=0}>=0 && isint(${3=0},0,3)" e[^-1] "Decompose 2D images$? as $1x$1 patch-image, with overlap $2." foreach { if d>1 error[0--4] "Command 'img2patches': Image ["$>"] is not a 2D image (size: "{[w,h,d,s]}")." fi nm={n} psiz=$1 if ispercentage($2) overlap:=round(lerp(0,$psiz-1,$2)) else overlap:=int($2) fi if $overlap<0" || "$overlap>=$psiz error[0--4] "Command 'img2patches': Specified overlap $2 (value: "$overlap") is larger than "\ "patch size (value: "$psiz")." fi step:=$psiz-$overlap Nw,Nh:=round([w,h]/$step,1,1) # Extract patches. $1,$1,{$Nw*$Nh},100% eval.. "* begin(ret = vector(#s); const boundary = $3); (x%$step) + (y%$step)==0?( P = crop(x,y,$psiz,$psiz); ind = (y*$Nw + x)/$step; draw(#1,P,0,0,ind,0,$psiz,$psiz,1,s); ); ret" k. => $nm } #@cli isophotes : _nb_levels>0 #@cli : Render isophotes of selected images on a transparent background. #@cli : Default value: 'nb_levels=64' #@cli : $ image.jpg blur 2 isophotes 6 dilate_circ 5 display_rgba isophotes : skip ${1=64} e[^-1] "Render isophote maps from images$?, with $1 levels." to_rgba foreach { +luminance repeat $1 { +isoline3d[1] {$>*255/($1-1)} } rm[1] +3d[^0] col3d. 1 [0],[0] j3d. ..,0,0,0,1,0,0,0 rm.. * } #@cli label : _tolerance>=0,is_high_connectivity={ 0 | 1 },_is_L2_norm={ 0 | 1 } : (+) #@cli : Label connected components in selected images. #@cli : If 'is_L2_norm=1', tolerances are compared against L2-norm, otherwise L1-norm is used. #@cli : Default values: 'tolerance=0', 'is_high_connectivity=0' and 'is_L2_norm=1'. #@cli : $ image.jpg luminance threshold 60% label normalize 0,255 map 0 #@cli : $ 400,400 set 1,50%,50% distance 1 mod 16 threshold 8 label mod 255 map 2 #@cli : $$ https://gmic.eu/oldtutorial/_label #@cli label_fg : tolerance>=0,is_high_connectivity={ 0 | 1 },_is_L2_norm={ 0 | 1 } #@cli : Label connected components for non-zero values (foreground) in selected images. #@cli : Similar to 'label' except that 0-valued pixels are not labeled. #@cli : If 'is_L2_norm=1', tolerances are compared against L2-norm, otherwise L1-norm is used. #@cli : Default value: 'is_high_connectivity=0'. label_fg : check "$1>=0 && isbool(${2=0}) && isbool(${3=1})" e[^-1] "Label foreground connected components on image [1], with tolerance $1 (L"{$3+1}"-norm) and "\ ${arg0\ !$2,high,low}" connectivity." foreach { if d>1 +z -1,-1,-1,100%,100%,100% label. $1,$2,$3 z. 1,1,1,100%,100%,100% else +z -1,-1,100%,100% label. $1,$2,$3 z. 1,1,100%,100% fi norm.. !=.. 0 * +histogram {1+iM} =. 0 >. 0 cumulate. map.. . rm. } #@cli laar #@cli : Extract the largest axis-aligned rectangle in non-zero areas of selected images. #@cli : Rectangle coordinates are returned in status, as a sequence of numbers x0,y0,x1,y1. #@cli : $ shape_cupid 256 coords=${-laar} normalize 0,255 to_rgb rectangle $coords,0.5,0,128,0 laar : e[^-1] "Extract the largest axis-aligned rectangle in non-zero areas of image$?." res= sep= foreach { +channels 0 gt. 0 => shape +cumulate[shape] xy => cumul val:=i[-1,2] if !$val res.=-1,-1,-1,-1 # All black elif $val==wh res.=0,0,{[w,h]-1} # All white else # Step 1: Compute largest square. Rin,Rout=0,{min(w,h)+1} P0,P= do Rmid:=int(($Rin+$Rout)/2) Q=${_laar\ $Rmid,$Rmid,$P} if narg($Q) Rin=$Rmid P0=$Q P=$Q else Rout=$Rmid P= fi while $Rin!=$Rout-1 if $Rin==1 P=${_laar\ 1,1} fi # Step 2: Compute largest rectangle, for W>H. maxA,maxW,maxH=0 maxcoords= P=$P0 W,H=$Rin for $H>0 { A:=$W*$H if $A>$maxA maxA,maxW,maxH=$A,$W,$H maxP:=[$P][0,2] fi nW:=$W+1 Q=${_laar\ $nW,$H,$P} if narg($Q) W=$nW P=$Q elif $H>1 pH:=$H-1 Q=${_laar\ $nW,$pH} if narg($Q) W,H=$nW,$pH P=$Q else H-=2 P= fi else break fi } # Step 3: Compute largest rectangle, for H>W. P=$P0 W,H=$Rin for $W>0 { A:=$W*$H if $A>$maxA maxA,maxW,maxH=$A,$W,$H maxP:=[$P][0,2] fi nH:=$H+1 Q=${_laar\ $W,$nH,$P} if narg($Q) H=$nH P=$Q elif $W>1 pW:=$W-1 Q=${_laar\ $pW,$nH} if narg($Q) W,H=$pW,$nH P=$Q else W-=2 P= fi else break fi } res.=$sep$maxP,{[$maxP]+[$maxW,$maxH]-1} sep=, fi rm[shape,cumul] } u $res # $1,$2: Rectangle width and height. # ${3--1}: Coordinates of candidate locations. If empty, full-search is done. # Return the list of upper-left rectangle coordinates that fit. # Images [shape] and [cumul] must be defined. _laar : skip "${3=}" fn="cumul(x0,y0,x1,y1) = ( _px0 = x0 - 1; _py0 = y0 - 1; i(#"$cumul,"x1,y1) + i(#"$cumul",_px0,_py0) - i(#"$cumul",x1,_py0) - i(#"$cumul",_px0,y1); );" res= if narg($3) # Test suggested locations (${3--1}) r. 2,{w/2},1,1,-1 s. x a[-2,-1] c f. ${fn}"x1 = i0 + $1 - 1; y1 = i1 + $2 - 1; i(#"$shape",i0,i1) && x1=1 #@cli : Return locations of maximal values in local patch-based neighborhood of given size for selected images. #@cli : Default value: 'patch_size=16'. #@cli : $ image.jpg norm +max_patch 16 max_patch : check "isint(${1=16},1)" e[^-1] "Return locations of maximal values in local patch neighborhood of size $1, in image$?." foreach { +dilate $1 == } #@cli min_patch : _patch_size>=1 #@cli : Return locations of minimal values in local patch-based neighborhood of given size for selected images. #@cli : Default value: 'patch_size=16'. #@cli : $ image.jpg norm +min_patch 16 min_patch : check "isint(${1=16},1)" e[^-1] "Return locations of minimal values in local patch neighborhood of size $1, in image$?." foreach { +erode $1 == } #@cli minimal_path : x0[%]>=0,y0[%]>=0,z0[%]>=0,x1[%]>=0,y1[%]>=0,z1[%]>=0,_is_high_connectivity={ 0 | 1 } #@cli : Compute minimal path between two points on selected potential maps. #@cli : Default value: 'is_high_connectivity=0'. #@cli : $ image.jpg +gradient_norm fill[-1] 1/(1+i) minimal_path[-1] 0,0,0,100%,100%,0 pointcloud[-1] 0 *[-1] 280 \ # to_rgb[-1] ri[-1] [-2],0 or minimal_path : check "$1>=0 && $2>=0 && $3>=0" skip ${7=0} e[^-1] "Compute minimal path between points ($1,$2,$3) and ($4,$5,$6) for potential map$?, with "\ ${arg0\ $7,low,high}" connectivity." foreach { nm={n} - {im} + {iM/100} 100%,100% = 1,${4-6} distance. 1,[0],{$7?4:3} k. channels. 1 i[0] 0 eval " x = round(ispercentage($1)?$1*(w - 1):$1); y = round(ispercentage($2)?$2*(h - 1):$2); z = round(ispercentage($3)?$3*(d - 1):$3); da_push(#0,[x,y,z]); do ( p = i(x,y,z); p&1?--x:p&2?++x; p&4?--y:p&8?++y; p&16?--z:p&32?++z; da_push(#0,[x,y,z]), _(while) p); resize(#0,1,da_size(#0),1,3,0)" rm. => $nm } #@cli mse : [reference] #@cli : Return the MSE (Mean-Squared Error) between selected images and specified reference image. #@cli : This command does not modify the images. It returns a value or a list of values in the status. mse : e[^-1] "Compute MSE between image$? and reference image $1." pass$1 1 +_mse[^-1] . rm. +mse : e[^-1] "Compute MSE between image$? and reference image $1." pass$1 1 +_mse[^-1] . rm. +_mse : check ${"is_image_arg $1"} rep,sep= pass$1 1 +foreach[^-1] { # Check image dimensions. pass. 1 if [w#0,h#0,d#0,s#0]!=[w#1,h#1,d#1,s#1] error[0--5] "Command 'mse': Image dimensions ("{0,[w,h,d,s]}") and ("{1,[w,h,d,s]}") do not match." fi # Compute MSE. - sqr res.=$sep{ia} sep=, rm } rm. u $res #@cli mse_matrix #@cli : Compute MSE (Mean-Squared Error) matrix between selected images. #@cli : $ image.jpg +noise 30 +noise[0] 35 +noise[0] 38 cut. 0,255 +mse_matrix mse_matrix : e[^-1] "Compute the "$!x$!" matrix of MSE values, for image$?." +mse_matrix k. +mse_matrix : e[^-1] "Compute the "$!x$!" matrix of MSE values, for images$?." N=$! $N,$N repeat $N { i=$> j:=$i+1 for $j<$N { +mse[$i] [$j] =. ${},$i,$j =. ${},$j,$i j+=1 } } #@cli patches2img : width>0,height>0,_overlap[%]>0,_overlap_std[%] #@cli : Recompose 2D images from their selected patch representations. #@cli : 'overlap' must be in range '[0,patch_size-1]' \ # where 'patch_size' is the width/height of the selected image. #@cli : 'overlap_std' is the standard deviation of the gaussian weights used for reconstructing overlapping patches. #@cli : If 'overlap_std' is set to '-1', uniform weights are used rather than gaussian. #@cli : Default value: 'overlap=0' and 'overlap_std=-1'. #@cli : See also: ''img2patches''. #@cli : $ image.jpg +img2patches 32,0,3 mirror[-1] xy patches2img[-1] {0,[w,h]} patches2img : check "isint($1,1) && isint($2,1) && ${3=0}>=0 && (${4=-1}==-1 || $4>=0)" e[^-1] "Recompose 2D images from patch-image$?, with size ($1,$2), overlap $3 and overlap_std $4." foreach { if w!=h error[0--4] "Command 'patches2img': Image "[$>]" is not a patch-image (size: "{[w,h,d,s]}")." fi nm={n} psiz:=w W,H:=${1,2} if ispercentage($3) overlap:=round(lerp(0,$psiz-1,$3)) else overlap:=int($3) fi if $overlap<0" || "$overlap>=$psiz error[0--4] "Command 'patches2img': Specified overlap $3 (value: "$overlap") is larger than "\ "patch size (value: "$psiz")." fi step:=$psiz-$overlap Nw,Nh:=round([$W,$H]/$step,1,1) # Draw patches. $W,$H,1,100% if $overlap # Overlapping patches $psiz,$psiz if $4==-1 f. 1 else gaussian. $4 n. 1e-8,1 fi store. weights # Create weights for overlapping patches $W,$H,1,1,1e-5 1,1,[0],1,"> begin(M = get('weights',$psiz*$psiz)); P = crop(#0,0,0,z,$psiz,$psiz,1); X = $step*(z%$Nw); Y = $step*int(z/$Nw); draw(#1,P,X,Y,0,0,$psiz,$psiz,1,s#0,-1,M); draw(#2,M,X,Y,0,0,$psiz,$psiz,1,1,-1)" /[-3,-2] else # Distinct patches 1,1,[0],1,"* P = crop(#0,0,0,z,w#0,h#0,1); X = $step*(z%$Nw); Y = $step*int(z/$Nw); draw(#1,P,X,Y,0,0,w#0,h#0,1,s#0)" fi k.. => $nm } #@cli patches : patch_width>0,patch_height>0,patch_depth>0,x0,y0,z0,_x1,_y1,_z1,...,_xN,_yN,_zN #@cli : Extract N+1 patches from selected images, centered at specified locations. #@cli : $ image.jpg +patches 64,64,1,153,124,0,184,240,0,217,126,0,275,38,0 patches : check "isint($1,1) && isint($2,1) && isint($3,1)" e[^-1] "Extract $1x$2x$3 patches from image$?, at locations (${4--1})." (${4--1}) r. 3,{w/3},1,1,-1 permute. yzcx N:=w H:=int(sqrt(w)) W:=round(w/$H,1,1) r. {$W*$H},1,1,3,0 r. $W,$H,1,3,-1 r. {w*$1},{h*$2},{d*$3} $1,$2,$3,1,x-{int($1/2)} +f. y-{int($2/2)} +f. z-{int($3/2)} a[-3--1] c ri. ..,0,2 +[-2,-1] repeat $!-1 { warp[$>] .,0,0,0 } rm. foreach { s y,$H s x,$W k[0-{$N-1}] } #@cli matchpatch : [patch_image],patch_width>=1,_patch_height>=1,_patch_depth>=1,_nb_iterations>=0,\ # _nb_randoms>=0,_patch_penalization,_output_score={ 0 | 1 },_[guide] : (+) #@cli : Estimate correspondence map between selected images and specified patch image, using #@cli : a patch-matching algorithm. #@cli : Each pixel of the returned correspondence map gives the location (p,q) of the closest patch in #@cli : the specified patch image. If 'output_score=1', the third channel also gives the corresponding #@cli : matching score for each patch as well. #@cli : If 'patch_penalization' is >=0, SSD is penalized with patch occurrences. #@cli : If 'patch_penalization' is <0, SSD is inf-penalized when distance between patches are less \ # than '-patch_penalization'. #@cli : Default values: 'patch_height=patch_width', 'patch_depth=1', 'nb_iterations=5', 'nb_randoms=5', \ # 'patch_penalization=0', 'output_score=0' and 'guide=(undefined)'. #@cli : $ image.jpg sample colorful +matchpatch[0] [1],3 +warp[-2] [-1],0 #@cli plot2value #@cli : Retrieve values from selected 2D graph plots. #@cli : $ 400,300,1,1,'y>300*abs(cos(x/10+2*u))' +plot2value +display_graph[-1] 400,300 plot2value : e[^-1] "Retrieve values from 2D graph plot$?." foreach { s c >= 50% foreach { (1,{w}) ri[1] [0],3 * histogram {w},1,{w} } a c } #@cli pointcloud : _type = { -X:-X-opacity | 0:binary | 1:cumulative | 2:label | 3:retrieve coordinates },\ # _width,_height>0,_depth>0 #@cli : Render a set of point coordinates, as a point cloud in a 1D/2D or 3D binary image #@cli : (or do the reverse, i.e. retrieve coordinates of non-zero points from a rendered point cloud). #@cli : Input point coordinates can be a NxMx1x1, Nx1x1xM or 1xNx1xM image, where 'N' is the number of points, #@cli : and M the point coordinates. #@cli : If 'M'>3, the 3-to-M components sets the (M-3)-dimensional color at each point. #@cli : Parameters 'width','height' and 'depth' are related to the size of the final image : #@cli : - If set to 0, the size is automatically set along the specified axis. #@cli : - If set to N>0, the size along the specified axis is N. #@cli : - If set to N<0, the size along the specified axis is at most N. #@cli : Points with coordinates that are negative or higher than specified ('width','height','depth') #@cli : are not plotted. #@cli : Default values: 'type=0' and 'max_width=max_height=max_depth=0'. #@cli : $ 3000,2 rand 0,400 +pointcloud 0 dilate[-1] 3 #@cli : $ 3000,2 rand 0,400 {w} {w},3 rand[-1] 0,255 append y +pointcloud 0 dilate[-1] 3 pointcloud : check "${1=0}<=3 && ${2=0}>=0 && ${3=0}>=0 && ${4=0}>=0" e[^-1] "Convert image$? to point clouds, in "${arg\ 2+($1>=0)*$1-($1<0),{-$1}-opacity,binary,cumulative,labeling}\ " mode, with dimensions $2x$3x$4." foreach { nm={n} if $1!=3 # Render point cloud image from set of point coordinates # Force input data to be of size Nx1x1xM. if "d>1 || (w>1 && h>1 && s>1)" error "Command '$0': Invalid input image "{w}x{h}x{d}x{s}". Should be NxMx1x1, Nx1x1xM or 1xNx1xM." fi if "w>1 && h>1 && s==1" r 100%,1,1,{h},-1 # NxMx1x1 -> Nx1x1xM elif "w==1 && h>1 && s>1" r {h},1,1,{s},-1 # 1xNx1xM -> Nx1x1xM fi # Retrieve coordinates and color info. if s<3 channels 0,2 fi if s<4 100%,1,1,1,1 a[-2,-1] c fi sh. 0 round. siz_x:=!$2?iM+1:$2 sh.. 1 round. siz_y:=!$3?iM+1:$3 sh... 2 round. siz_z:=!$4?iM+1:$4 rm[-3--1] # Draw point cloud. $siz_x,$siz_y,$siz_z,{$1!=2?s-3:1} if $1<0 # -X-opacity f.. ">V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) = (1+$1)*I(#-1,P) - $1*C; V" elif !$1 # Binary f.. ">V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) = C; V" elif $1==1 # Cumulative f.. ">V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) += C; V" else # Label f.. ">begin(l = 0); V = I; P = V[0,3]; C = V[3,size(V) - 3]; I(#-1,P) = ++l; V" fi else # Retrieve set of point coordinates from rendered point cloud image 16,1,1,{s+3} f.. "> begin(N = 0; zero = vectors(0)); I!=zero?I[#-1,N++] = [ x,y,z,I ]; N>=w(#-1)?resize(#-1,1.5*w(#-1),1,1,s#-1,0); end(resize(#-1,N,1,1,s#-1,0)); I" fi k. => $nm } #@cli psnr : [reference],_max_value>0 #@cli : Return PSNR (Peak Signal-to-Noise Ratio) between selected images and specified reference image. #@cli : This command does not modify the images. It returns a value or a list of values in the status. #@cli : Default value: 'max_value=255'. psnr : check ${"is_image_arg $1"}" && ${2=255}>0" e[^-1] "Compute PSNR between image$? and reference image $1, with max value $2." pass$1 1 +_psnr[^-1] .,$2 rm. +psnr : check ${"is_image_arg $1"}" && ${2=255}>0" e[^-1] "Compute PSNR between image$? and reference image $1, with max value $2." pass$1 1 +_psnr[^-1] .,$2 rm. +_psnr : check ${"is_image_arg $1"}" && ${2=255}>0" rep,sep= pass$1 1 +foreach[^-1] { # Check image dimensions. pass. 1 if [w#0,h#0,d#0,s#0]!=[w#1,h#1,d#1,s#1] error[0--5] "Command 'psnr': Image dimensions ("{0,[w,h,d,s]}") and ("{1,[w,h,d,s]}") do not match." fi # Compute PSNR. - sqr res.=$sep{"const m2 = ($2)^2; 10*log10(m2/ia)"} sep=, rm } rm. u $res #@cli psnr_matrix : _max_value>0 #@cli : Compute PSNR (Peak Signal-to-Noise Ratio) matrix between selected images. #@cli : Default value: 'max_value=255'. #@cli : $ image.jpg +noise 30 +noise[0] 35 +noise[0] 38 cut. 0,255 +psnr_matrix psnr_matrix : check "${1=255}>0" e[^-1] "Compute the "$!x$!" matrix of PSNR values, for image$?, with max value $1." +psnr_matrix $1 k. +psnr_matrix : check "${1=255}>0" e[^-1] "Compute the "$!x$!" matrix of PSNR values, for images$?, with max value $1." N=$! $N,$N repeat $N { i=$> j:=$i+1 for $j<$N { +psnr[$i] [$j],$1 =. ${},$i,$j =. ${},$j,$i j+=1 } =. inf,$i,$i } #@cli segment_watershed : _threshold>=0 #@cli : Apply watershed segmentation on selected images. #@cli : Default values: 'threshold=2'. #@cli : $ image.jpg segment_watershed 2 segment_watershed : check "${1=2}>=0" e[^-1] "Apply watershed segmentation on image$?, with edge threshold $1." foreach { min:=im + {1+$min} +gradient_norm +f. "i<$1 && i=0,0<=_weight_std_max_avg<=1,_dilation,_smoothness>=0 #@cli : Estimate bumpmap from binary shape in selected images. #@cli : Default value: 'resolution=256', 'weight_std_max=0.75', 'dilation=0' and 'smoothness=100'. shape2bump : check "isint(${1=256},0) && ${2=0.75}>=0 && $2<=1 && isnum(${3=0}) && ${4=100}>=0" e[^-1] "Estimate bumpmap from binary shape in image$?, using "\ ${"if $1 u \"resolution $1\" else u \"full resolution\" fi"}", avg/max weight $2, dilation $3 and smoothness $4." foreach { slices 0 channels 100% > 0 siz:=w,h # Generate skeleton (fast method). distance 0 + $3 +f. "const boundary = 1; (i>j(-1)&&i>j(1)) || (i>j(0,-1)&&i>j(0,1)) || (i>j(-1,-1)&&i>j(1,1)) || (i>j(-1,1)&&i>j(1,-1))" # Downsize for faster computation. is_resized=0 if $1" && "max(w,h)>$1 rs $1,$1,2 gt. 0 thinning. 1 *.. {$1/max([$siz])} is_resized=1 fi rmax={0,iM} # Generate z-map (max method). [0],[0] if $2>0 eval[1] ":i?( r = i(#0,x,y); ir = floor(r); r2 = r^2; for (q = -ir, q<=ir, ++q, Y = y + q; for (p = -ir, p<=ir, ++p, X = x + p; dist = norm(p,q); dist=0 && $1<=1)" if isnum($1) bc=$1 else bc=1 noarg fi e[^-1] "Compute skeleton of binary image$? with "${"arg0 "$bc",dirichlet,neumann"}" boundary conditions." foreach { s c foreach { # [0] = 2D binary shape 1,16,1,2 # [1] = List of boundary pixels 1,16,1,2 # [2] = List of boundary pixels (next iteration) # [3] = Pixels on median axis. +distance[0] 0 f. "const boundary = 1; (i>j(-1)&&i>j(1)) || (i>j(0,-1)&&i>j(0,1)) || (i>j(-1,-1)&&i>j(1,1)) || (i>j(-1,1)&&i>j(1,-1))" # Extract boundary pixels. f[0] ">const boundary = "$bc"; i && (!j(-1) || !j(1) || !j(0,-1) || !j(0,1))?da_push(#1,[x,y]); i;" # Run thinning algorithm. eval " const boundary = "$bc"; # Lookup tables for detecting the simple points. is_removable = [ 0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1,1,1,0,1,1,1,0, 1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,1,1,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,1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1,1,1,0,1,1, 1,0,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,1,0,0,1,1,1,1,0,0,0,0, 0,0,0,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,1,1,0,0,1,1,0, 1,1,1,0,1,1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,0 ]; dotm = [ 128,64,32,16,0,8,4,2,1 ]; is_removed = 1; # Start thinning iterations. while (is_removed, is_removed = 0; N = da_size(#1); repeat (N,n, xc = i(#1,0,n,0,0); yc = i(#1,0,n,0,1); icc = i(#0,xc,yc); (icc && !i(#3,xc,yc))?( xp = xc - 1; yp = yc - 1; xn = xc + 1; yn = yc + 1; ref(crop(#0,xp,yp,3,3),V); val = dot(dotm,V>0); is_removable[val]?( i(#0,xc,yc) = 0; is_removed = 1; V[3]==1?(da_push(#2,[xp,yc]); i(#0,xp,yc) = 2); V[5]==1?(da_push(#2,[xn,yc]); i(#0,xn,yc) = 2); V[1]==1?(da_push(#2,[xc,yp]); i(#0,xc,yp) = 2); V[7]==1?(da_push(#2,[xc,yn]); i(#0,xc,yn) = 2); ):(da_push(#2,[xc,yc]); i(#0,xc,yc) = 2); ) ); resize(#1,1,h(#2),1,2,0); copy(i(#1),i(#2),2*h(#2)); i[#2,h(#2)-1] = 0; );" k[0] > 0 thinning } a c } #@cli slic : size>0,_regularity>=0,_nb_iterations>0 #@cli : Segment selected 2D images with superpixels, using the SLIC algorithm (Simple Linear Iterative Clustering). #@cli : Scalar images of increasingly labeled pixels are returned. #@cli : Reference paper: Achanta, R., Shaji, A., Smith, K., Lucchi, A., Fua, P., & Susstrunk, S. (2010). \ # SLIC Superpixels (No. EPFL-REPORT-149300). #@cli : Default values: 'size=16', 'regularity=10' and 'nb_iterations=10'. #@cli : $ image.jpg +srgb2lab slic[-1] 16 +blend shapeaverage f[-2] "j(1,0)==i && j(0,1)==i" *[-1] [-2] slic : check "${1=16}>0 && ${2=10}>=0 && ${3=10}>0" e[^-1] "Segment image$? using SLIC superpixels, with size $1, regularity $2 and $3 iterations." S,m,nb_iter=${1-3} foreach { slices 50% # Initialize superpixel centers Ck. {[max(1,round(w/$S)),max(1,round(h/$S))]},1,2,"round(([x,y]+=0.5)*="$S")" # Perturb the Ck towards low gradient positions. if $S>=3 +b[0] 0.7 g. xy,1 a[-2,-1] c norm. # Gradient norm with forward differences f.. " const n = round("$S"/3); const n1 = int(n/2); pos = argmin(crop(#-1,i0 - n1,i1 - n1,n,n,1)); dxy = [pos%n,int(pos/n)] - n1; [ cut(i0 + dxy[0],0,w#0-1), cut(i1 + dxy[1],0,h#0-1) ]" rm. fi r. {wh},1,1,2,-1 100%,1,1,{0,s},"I(#0,I#1)" a[-2,-1] c # Add superpixels colors [0],[0],1,2 eval.. "I(#-1,i0,i1) = [ x + 1,1 ]; I" s. c distance. 1 *. -1 watershed.. . rm. channels. 0,1 # Start iteration loop. repeat $nb_iter { # Assign best superpixel to each pixel. sh[2] 1 f. inf rm. eval[1] " const m = "$m"; const S = "$S"; k = x; xk = i0; yk = i1; Ik = (I)[2,s#0]; x0 = max(xk - S,0); x1 = min(xk + S,w#0 - 1); y0 = max(yk - S,0); y1 = min(yk + S,h#0 - 1); for (y = y0, y<=y1, ++y, for (x = x0, x<=x1, ++x, delta_c = norm(I(#0,x,y) - Ik); delta_s = norm([x - xk, y - yk]); delta = delta_c + m/S*delta_s; if (delta const boundary = 1; if (i[#-1,i]>=0, N = [ j(-1,0),j(0,-1),j(1,0),j(0,1) ]; repeat (size(N),k, if (N[k]!=i, i[#-1,i] = i[#-1,i]==i || i[#-1,i]==N[k]?N[k]:-1 ); ); ); i" f. "i>=0" +map[0] . rm.. or[-2,-1] +[0] 1 100%,100% j[0] .,0,0,0,0,1,.. rm. distance. 0 *. -1 watershed.. . rm. label. 0,0 } #@cli ssd_patch : [patch],_use_fourier={ 0 | 1 },_boundary_conditions #@cli : Compute fields of SSD between selected images and specified patch. #@cli : Argument 'boundary_conditions' is valid only when 'use_fourier=0'. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default value: 'use_fourier=0' and 'boundary_conditions=0'. #@cli : $ image.jpg +crop 20%,20%,35%,35% +ssd_patch[0] [1],0,0 ssd_patch : check ${is_image_arg\ $1} skip ${2=0},${3=0} e[^-1] "Compute field of SSD between image$? and patch $1 using "${arg0\ !$2,fourier,spatial}" mode." repeat $! { pass$1 0 l[$>,-1] { r 100%,100%,100%,${-max_s} s c repeat $!/2 { l[$>,{-1-$<}] { +sqr[1] val:=is rm. # Sum J(p,q)^2 +sqr[0] +f[1] 1 if $2 convolve_fft.. . rm. # Sum I(x+p,y+q)^2 mirror[1] xyz convolve_fft[0] [1] rm[1] # Sum I(x+p,y+q).J(p,q) else correlate.. .,$3 rm. # Sum I(x+p,y+q)^2 correlate[0] [1],$3 rm[1] # Sum I(x+p,y+q).J(p,q) fi *[0] -2 +[0,1] + $val } } + } } #@cli ssim : [reference],_patch_size>0,_max_value>0 #@cli : Compute the Structural Similarity Index Measure (SSIM) between selected images and specified reference image. #@cli : This command does not modify the images, it just returns a value or a list of values in the status. #@cli : When 'downsampling_factor' is specified with a ending '%', its value is equal to \ # '1+(patch_size-1)*spatial_factor%'. #@cli : \nSSIM is a measure introduced int the following paper: #@cli : *Wang, Zhou, et al.*, "Image quality assessment: from error visibility to structural similarity.", #@cli : in IEEE transactions on image processing 13.4 (2004): 600-612. #@cli : \nThe implementation of this command is a direct translation of the reference code (in Matlab), found at : #@cli : https://ece.uwaterloo.ca/~z70wang/research/ssim/ #@cli : Default values: 'patch_size=11', and 'max_value=255'. +ssim : check ${"is_image_arg $1"}" && isint(${2=11},1) && ${3=255}>0" e[^-1] "Compute the SSIM between image$? and reference image $1, with patch size $2 and max_value $3." C1,C2:=([0.01,0.03]*$3)^2 $2,$2 gaussian. 1.5 /. {is} store. gaussian_weights res,sep= pass$1 +foreach[^-1] { # Check image dimensions. pass. 0 if [w#0,h#0,d#0,s#0]!=[w#1,h#1,d#1,s#1] error[0--4] "Command 'ssim': Image dimensions ("{0,[w,h,d,s]}") and ("{1,[w,h,d,s]}") do not match." fi # Automatic downsampling. f:=max(1,round(min(w,h)/256)); if $f>1 r {round([w,h,d]/$f,1,1)},100%,2 fi => A,B # Compute SSIM map. $gaussian_weights => W p0,p1:="p0 = round(w/2,1,-1); [ p0,w-1-p0 ]" +correlate[A,B] [W],0,0,1,$p0,$p0,0,$p0,$p0,0,{A,[w,h]-1-$p1},0 +mul[-2,-1] sqr[-3,-2] => muA2,muB2,muAB +mul[A,B] sqr[A,B] =>[A,B,-1] A2,B2,AB correlate[A2,B2,AB] [W],0,0,1,$p0,$p0,0,$p0,$p0,0,{A2,[w,h]-1-$p1},0 -[A2] [muA2] -[B2] [muB2] -[AB] [muAB] =>[A2,B2,AB] sigA2,sigB2,sigAB *[muAB] 2 +[muAB] $C1 *[sigAB] 2 +[sigAB] $C2 *[muAB,sigAB] =>[muAB] num +[muA2,muB2] +[muA2] $C1 +[sigA2,sigB2] +[sigA2] $C2 *[sigA2,muA2] =>[sigA2] den /[num] [den] # Return average value of SSIM map. res.=$sep{ia} sep=, rm } rm. u $res #@cli ssim_matrix : _patch_size>0,_max_value>0 #@cli : Compute SSIM (Structural Similarity Index Measure) matrix between selected images. #@cli : Default values: 'patch_size=11', and 'max_value=255'. #@cli : $ image.jpg +noise 30 +noise[0] 35 +noise[0] 38 cut. 0,255 +ssim_matrix ssim_matrix : check "isint(${1=11},1) && ${2=255}>0" e[^-1] "Compute the "$!x$!" matrix of SSIM values, for image$?, with patch size $1 and max value $2." +ssim_matrix ${1,2} k. +ssim_matrix : check "isint(${1=11},1) && ${2=255}>0" e[^-1] "Compute the "$!x$!" matrix of SSIM values, for image$?, with patch size $1 and max value $2." N=$! $N,$N repeat $N { i=$> j:=$i+1 for $j<$N { +ssim[$i] [$j],${1,2} =. ${},$i,$j =. ${},$j,$i j+=1 } =. 1,$i,$i } #@cli thinning : _boundary_conditions={ 0:dirichlet | 1:neumann } #@cli : Compute skeleton of binary shapes using morphological thinning #@cli : (beware, this is a quite slow iterative process) #@cli : Default value: 'boundary_conditions=1'. #@cli : $ shape_cupid 320 +thinning thinning : check "!isnum(${1=1}) || ($1>=0 && $1<=1)" if isnum($1) bc=$1 else bc=1 noarg fi e[^-1] "Apply morphological thinning to binary image$? with "\ ${"arg0 "$bc",dirichlet,neumann"}" boundary conditions." foreach { s c foreach { # [0] = 2D binary shape (current iteration) 1,16,1,2 # [1] = List of boundary pixels (current iteration) 1,16,1,2 # [2] = List of pixels to remove (current iteration) 1,16,1,2 # [3] = List of boundary pixels (for next iteration) # Extract boundary pixels. f[0] ">const boundary = "$bc"; i && (!j(-1) || !j(1) || !j(0,-1) || !j(0,1))?da_push(#1,[x,y]); i;" # Run thinning algorithm. eval " const boundary = "$bc"; # Lookup tables for detecting the 8 3x3 hit&miss. hm_and = [ 231,189,231,189,122,91,94,218 ]; hm_eq = [ 7,148,224,41,18,80,72,10 ]; # Start thinning iterations. dotm = [ 128,64,32,16,0,8,4,2,1 ]; is_removed = vector8(0); ind = 1; nind = 3; it = 0; do ( N = da_size(#ind); it8 = it%8; is_removed[it8] = 0; # Find removable contour points. repeat (N,n, xc = i(#ind,0,n,0,0); yc = i(#ind,0,n,0,1); icc = i(#0,xc,yc); icc?( xp = xc - 1; yp = yc - 1; xn = xc + 1; yn = yc + 1; ref(crop(#0,xp,yp,3,3),V); val = dot(dotm,V>0); (val & hm_and[it8])==hm_eq[it8]?( da_push(#2,[xc,yc]); is_removed[it8] = 1; V[3]==1?(da_push(#nind,[xp,yc]); i(#0,xp,yc) = 2); V[5]==1?(da_push(#nind,[xn,yc]); i(#0,xn,yc) = 2); V[1]==1?(da_push(#nind,[xc,yp]); i(#0,xc,yp) = 2); V[7]==1?(da_push(#nind,[xc,yn]); i(#0,xc,yn) = 2); ):(da_push(#nind,[xc,yc]); i(#0,xc,yc) = 2); ); ); # Re-assign value '1' to contour points. N = da_size(#nind); repeat (N,n, i(#0,i[#nind,n],i[#nind,n + h(#nind)]) = 1); _tmp = ind; ind = nind; nind = _tmp; i[#nind,h(#nind)-1] = 0; # Remove matching contour points. N = da_size(#2); repeat (N,n, i(#0,I[#2,n]) = 0); i[#2,h(#2)-1] = 0; ++it; _(while), max(is_removed) );" k[0] } a c } #@cli tones : N>0 #@cli : Get N tones masks from selected images. #@cli : $ image.jpg +tones 3 tones : check $1>0 e[^-1] "Get $1 tones masks from image$?." norm n 0,{$1-1} round 1 foreach { repeat $1-1 { +==[0] {1+$>} } ==[0] 0 } #@cli topographic_map : _nb_levels>0,_smoothness #@cli : Render selected images as topographic maps. #@cli : Default values: 'nb_levels=16' and 'smoothness=2'. #@cli : $ image.jpg topographic_map 10 topographic_map : check "isint(${1=16},1)" skip ${2=2} e[^-1] "Render topographic maps from image$?, with $1 levels and smoothness $2." foreach { +b $2 isophotes. $1 compose_channels. + ==. 0 blend shapeaverage0 } #@cli tsp : _precision>=0 #@cli : Try to solve the 'travelling salesman' problem, using a combination of greedy search and 2-opt algorithms. #@cli : Selected images must have dimensions Nx1x1xC to represent N cities each with C-dimensional coordinates. #@cli : This command re-order the selected data along the x-axis so that the point sequence becomes a shortest path. #@cli : Default values: 'precision=256'. #@cli : $ 256,1,1,2 rand 0,512 tsp , 512,512,1,3 repeat w#0 circle[-1] {0,I[$>]},2,1,255,255,255 \ # line[-1] {0,boundary=2;[I[$>],I[$>+1]]},1,255,128,0 done keep[-1] tsp : check "${1=256}>=0" e[^-1] "Try to solve the 'travelling salesman' problem for pointcloud$?, with precision $1." foreach { nm={n} if h>1" || "d>1 error[0--4] "Selected image '"{n}"' has invalid dimensions ("{[w,h,d,s]}")." fi # Find initial estimate, using greedy nearest neighbor algorithm. eval " is_used = vectorw(0); next = vectorw(-1); n_initial = n_current = v(w - 1); do ( is_used[n_current] = 1; P_current = I[n_current]; n_next = -1; dmin = inf; repeat (w,n, if (!is_used[n], d = norm(I[n] - P_current); if (dbegin(ind = 0); val = I[#0,ind]; ind = val[s]; val" rm.. # Improving initial estimate iteratively by 2-opt algorithm. eval " is_improved = 1; while (is_improved, is_improved = 0; nb_try = $1*w; repeat (nb_try,try, r = round(max(8,0.5*w*(try/nb_try)^0.25)); i = v(w - 1); ni = (i+1)%w; pi = (i-1)%w; do (j = (i + v(-r,r))%w; _(while), j==i || j==ni || j==pi); nj = (j+1)%w; P_i = I[i]; P_ni = I[ni]; P_j = I[j]; P_nj = I[nj]; dist_ini = norm(P_ni - P_i); dist_jnj = norm(P_nj - P_j); dist_ij = norm(P_j - P_i); dist_ninj = norm(P_nj - P_ni); if (dist_ij + dist_ninj $nm } #@cli variance_patch : _patch_size>=1 #@cli : Compute variance of each images patch centered at (x,y), in selected images. #@cli : Default value: 'patch_size=16' #@cli : $ image.jpg +variance_patch variance_patch : check "isint(${1=16},1)" e[^-1] "Compute variance of image patches in image$?, with patch size $1." $1,$1,1,1,1 normalize_sum. repeat $!-1 { l[$>,-1] { +sqr[0] convolve[0,2] [1] sqr[0] rv[0,2] -[0,2] max[0] 0 } } rm. #--------------------------------- # #@cli :: Image Drawing # #--------------------------------- #@cli arrow : x0[%],y0[%],x1[%],y1[%],_thickness[%]>=0,_head_length[%]>=0,_head_thickness[%]>=0,_opacity,\ # _pattern,_color1,... #@cli : Draw specified arrow on selected images. #@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted #@cli : even if a color is specified. If a pattern is specified, the arrow is #@cli : drawn outlined instead of filled. #@cli : Default values: 'thickness=1%', 'head_length=10%', 'head_thickness=3%', 'opacity=1', 'pattern=(undefined)' \ # and 'color1=0'. #@cli : $ 400,400,1,3 repeat 100 arrow 50%,50%,{u(100)}%,{u(100)}%,3,20,10,0.3,${-rgb} done arrow : check "${5=1%}>=0 && ${6=10%}>=0 && ${7=3%}" skip ${8=1} e[^-1] "Draw arrow in image$?, from ($1,$2) to ($3,$4), with thickness $5, head length $6, head_thickness $7 and opacity $8." foreach { polygon. 7,{" x0 = ispercentage($1)?(w-1)*$1:$1; y0 = ispercentage($2)?(h-1)*$2:$2; x1 = ispercentage($3)?(w-1)*$3:$3; y1 = ispercentage($4)?(h-1)*$4:$4; p0 = [x0,y0]; dp = [x1,y1]-=p0; l = norm(dp); # arrow length t = ispercentage($5)?l*$5:$5; # thickness hl = ispercentage($6)?l*$6:$6; # head length ht = ispercentage($7)?l*$7:$7; # head thickness lmhl = l - hl; X = mul([0,-t,lmhl,-t,lmhl,-ht,l,0,lmhl,ht,lmhl,t,0,t],rot(-atan2(dp[1],dp[0])),2); X+=[p0,p0,p0,p0,p0,p0,p0]"},${8--1} } #@cli axes : x0,x1,y0,y1,_font_height>=0,_opacity,_pattern,_color1,... #@cli : Draw xy-axes on selected images. #@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted #@cli : even if a color is specified. #@cli : To draw only one x-axis at row Y, set both 'y0' and 'y1' to Y. #@cli : To draw only one y-axis at column X, set both 'x0' and 'x1' to X. #@cli : Default values: 'font_height=14', 'opacity=1', 'pattern=(undefined)' and 'color1=0'. #@cli : $ 400,400,1,3,255 axes -1,1,1,-1 axes : check "isint(${5=14},0) && ${6=1}>=0" skip ${7=0},${8=0} if ${"is_pattern \"$7\""} e[0--3] "Draw xy-axes on image$?, with x-range ($1,$2), y-range ($3,$4), font height $5, opacity $6, pattern $7 and color (${8--1})." pattern=$7 color=${8--1} else e[0--3] "Draw xy-axes on image$?, with x-range ($1,$2), y-range ($3,$4), font height $5, opacity $6 and color (${7--1})." pattern=0xFFFFFFFF color=${7--1} fi if !$5" || "!$6 return fi mx:=min($1,$2) Mx:=max($1,$2) my:=min($3,$4) My:=max($3,$4) # Start drawing axes on selected images. foreach { w1,h1={0,[w,h]-1} # Determine number of axes tick marks. if $1!=$2 u=${"_axes[] $1,$2,{0.3*w/$5}"} offx:=arg(1,$u) deltax:=arg(2,$u) fi if $3!=$4 u=${"_axes[] $3,$4,{0.3*h/$5}"} offy:=arg(1,$u) deltay:=arg(2,$u) fi # Draw x-axis. is_0x=0 if $3==$4 y0=$3 else y0:=v=-($my)*$h1/($My-$my);$4>=$3?v:$h1-v fi sty:=$y0>$h1-$5?-1:1 if $1!=$2" && "$y0>=0" && "$y0<=$h1 line 0,$y0,$w1,$y0,$6,$pattern,$color 4,4,1,1,x<=y +mirror. y rows. 1,3 a[-2,-1] y .,.,1,[0] fc. $color if $2>=$1 j[0] .,{$w1-3},{$y0-3},0,0,$6,.. else mirror.. x j[0] .,0,{$y0-3},0,0,$6,.. fi rm[-2,-1] i=0 do val:=_$offx+$i*$deltax i+=1 if $val>=$mx" && "$val<=$Mx x:=v=($val-$mx)*$w1/($Mx-$mx);$2>=$1?v:$w1-v line $x,{$y0-1},$x,{$y0+1},$6,$pattern,$color if $val 0 t. $val,0,0,$5,1,1 100%,100%,1,[0] fc. $color j[0] .,{max(0,min($w1-w,$x-w/2))},{$sty>0?$y0+3:$y0-h-3},0,0,$6,.. rm[-2,-1] else is_0x=1 fi fi while $val<$Mx fi # Draw y-axis. is_0y=0 if $1==$2 x0=$1 else x0:=v=-($mx)*$w1/($Mx-$mx);$2>=$1?v:$w1-v fi stx:=$x0>$w1-$5?-1:1 if $3!=$4" && "$x0>=0" && "$x0<=$w1 line $x0,0,$x0,$h1,$6,$pattern,$color 4,4,1,1,x>=y +mirror. x z. 1,3 a[-2,-1] x .,.,1,[0] fc. $color if $4>=$3 j[0] .,{$x0-3},{$h1-3},0,0,$6,.. else mirror.. y j[0] .,{$x0-3},0,0,0,$6,.. fi rm[-2,-1] i=0 do val:=_$offy+$i*$deltay i+=1 if $val>=$my" && "$val<=$My y:=v=($val-$my)*$h1/($My-$my);$4>=$3?v:$h1-v line {$x0-1},$y,{$x0+1},$y,$6,$pattern,$color if $val 0 t. $val,0,0,$5,1,1 100%,100%,1,[0] fc. $color j[0] .,{$stx>0?$x0+6:$x0-w-6},{max(0,min($h1-h,$y-h/2))},0,0,$6,.. rm[-2,-1] else is_0y=1 fi fi while $val<$My fi # Draw origin, if necessary. if $is_0x" || "$is_0y 0 t. 0,0,0,$5,1,1 100%,100%,1,[0] fc. $color j[0] .,{$stx>0?$x0+6:$x0-w-6},{$sty>0?$y0+3:$y0-h-3},0,0,$6,.. rm[-2,-1] fi } # Return optimal (offset0,scale) to display input (min,max) values. # $1 = min value, $2 = max value, $3 = max number of tags(>=1). _axes : n:=max(1,round($3)) d:=abs($2-$1)/($n-1) s:=10^round(log10($d)) m:=round(min($1,$2),$s,-1) M:=round(max($1,$2),$s,1) do N:=1+round(($M-$m)/$s,1,1) s:=2*$s while $N>$n u $m,{$s/2} #@cli ball : _size>0, _R,_G,_B,0<=_specular_light<=8,0<=_specular_size<=8,_shadow>=0 #@cli : Input a 2D RGBA colored ball sprite. #@cli : Default values: 'size=64', 'R=255', 'G=R', 'B=R', 'specular_light=0.8', 'specular_size=1' and 'shading=1.5'. #@cli : $ repeat 9 { ball {1.5^($>+2)},${-rgb} } append x +ball : check "${1=64}>0 && ${5=0.8}>=0 && $5<=8 && ${6=1}>=0 && $6<=8 && ${7=1.5}>=0" skip ${2=255},${3=$2},${4=$3} e[^-1] "Input $1x$1 ball with color (${2-4}), specular light $5, specular size $6 and shadow factor $7." l[] { {2*$1},{2*$1} = 1,65%,30% distance 1 * -1 +n 0,1 ^[1] $7 *[1] 1.4 +*[1] $3 +*[1] $4 *[1] $2 a[^0] c >=[0] {100-10*$6}% b[0] {3*$6}% n[0] 0,{$5*255} rv + c 0,255 100%,100% circle[1] 50%,50%,34%,1,1 *[0] [1] *. 255 a c r $1,$1,1,4,2 } # chromeball64x64 : Output a 64x64 GreyA chrome ball sprite. # ${1-3} = R,G,B chromeball64x64 : base642img[] \ "MiB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KNjQgNjQgMSAyICMzMzY0CnicxZl5VJNnvsfT0mM7ntPp9N7paefe6ZzOmWlnemfGmTpzer0dO1W"\ "RnQCBQPZ9TwhhDQkYwyKQAAlbEjZBthgRRBahFamAggp1KS7QIgKyk7DIpoCQd54XtComyPLH/f7xvn/k/Xyf3/N7n+X35EUgLOqN9z/DHcqtaW69cq"\ "EmK8z7s3csP2ZZO/7ILGzpHhwaHBzoHxgA9/vt55M8fvPaxuh37NOu9AyNjBpNY+PjE+OmkYGuOzfb73XURu1589X0zx2PXu8ZMo0Dbnh41DQxMT5mH"\ "O7vam/v7O5sUH35Cgebvyiaf+g3jRuHB/oGjeMPpqamHkyMDvb3g3iG+7tvnjm8a71e/Af+5PXOYdPo4MCwaWr24cO5ubnZ2ekHY6PDgwNDRtNQT9u3"\ "mRirqXztt5Laa10jo0N9fcaZ+UWghflHwGBudnpywjTUNzjcd/faN3kBH1vGX98VWdd6b2B4oKt3fH7JbF5+vDC/ADQPm0xPTRr779/vuNZUdUzyT0t"\ "9eON/5Webb3f8eOdqW9/Eo2XI/Gh6auYxbAMMHsIO48OD/TcvnCvNltjbvIz/I/RMXVPTlaa6xrauwbG55eU5wAObpZmJialpoJnpSZCZ63WV+rRgxz"\ "fW9n0Xr/RsZVX1mdOV9a237o3OLT+amJw3Q9DS3IRxdNQ4NmYcn4YN7l8+U5ybIDq4pgsf4fPrjhcWFhScqL54vWt0Zn5++sHcotkMLc1OjptGR4yjw"\ "6MPZqbBu+yoLyvURfjueQF/z1V98YRGo8vWV128PTTzeGlpeWkZtA5B5sVHczNTD4CmZ+Zmp8b6Oy7XFB9NDOV+8hz+1j7Jt9+kKVIyC0433DItmiHz"\ "CvtEZvPSit/j+bnpsYGbF6tL8tIiRfRn4+C1PzGKWwuVCRm5xWevDS5CVmRemp0Y6m1rqCrOT1eKBS4/peA/XZKu1mkVKVn5ZQ0/TJmt8dDSeO8PNy6"\ "fK9fnZqllIu4fnuA2u4VnO0rU8WmZheUN9x5ZxaHFwatN9bVnSgtzstKOBAkIO1b595007W25CQkpmUUwb7V981T3pXM1VWXFBUcztcpQIWfX6rj9G6"\ "nqXotGmZCUDvjO2cWFhWXL/OTdK9/WlJ8yFORkalXhIt5qAL/4l/hSz8UUhVKlKyiv7xibmRibsxzDo77vL56tKDXk52TqUuQiAetTmP/USXm1pzFZo"\ "VADvuHOkGlocGzJIr881tlaV1kC+KwMXZS/gI0Er+DNL7zUV+9d1CgUqvTC8gsdIyO9vabHljMwP3S7sbr0eN7R7KN5sf4CLu2XIHv7MXFX7rZmg/jT"\ "CysutBuNvT2j81Yy+OBea22ZIe9YblFpPOAZuxGIXQd9ws/fbTMoFIm6/NPfXu8Z6e8bsZIAEMCtxqoSvf54WbUK5pEIm332nsJTP7RXqRSJaTkl3zS"\ "13e3pG5mxnABoebKrpe5MWXllXXUc4JnEn73tZo+kpH9/u+FofGJSpr6ytvlGR49VHloytrfW19bVXy6RifgcJuXXv8E6OKIP1d9oOa1JUGtyT5yuvX"\ "yza8gqDz0e6/zuUvOVCxkhfjwOk/znv1Id7d0Z+ZcvNeiTEsEELK6qb7kzMG2Vh5ZMP95ovXIqOsCXx2aQ93zFdbZz8ZKX19bX5CSqtbmG01+f/+6u0"\ "eokBKNo4PZ3X6cG+/G5LDrZ1t3fzdbBjZqgLz97KkOdmlVUUl7deKN7fNHqNFgauX0+O9zfl8dh0chIfCjazs4ZxY07WlxWlKXRZBcYSmsab9wzPlxc"\ "smzxeKhFHx0oFHDZTBoJxZARHA46ItG86IxCg/5Yhi47/3hpdX1L293eQdP0/Mse5ocdlUlhAQKQPTqV6MmNYSDt7J3d0dzIjAKDoSA3J6/wRGlV9df"\ "nGi5dvdU1MDa9ZjbO99bnxAGey2bQyHiUUCVEO9k5unp4sw9r8/XHiwoLi/T6E8XFJWUV1bUXWm/c6TbNLS4/DWN5prvpZDrMc1gMKgmHZGskZHdHey"\ "ekhxctVJVTdBzIcBy20RtOlFR8Xdf43Z2eQePk9KPHy0vzk73f158u0MaGBwhA78lErAMxK5qHQdo7OiM90ARfeVoewE+A5oEMer3hZFnVuebrt37s6"\ "ukb6Ou80VRbebIgKzkmPIDPolNJeMy/XHJVEgba2cEJGHhhqP6RqcdAwydLTpVXlJWcMBiKSyrONjRdam48X1dzSl+QBy8d8VHSAB6LRiHhvP/+xVHN"\ "kQCKm5PjioEPjuYXrtTmFukNJWWnSk8CA8PJU2WlhsLcrAxtilqVmJAQHxd9ONSfx6BRyVj0xx+mZKkihBgXJydnFzcPT28snswSSaMTkjOOFR4HPSg"\ "qKsjNTE2Ii4mOlB+WyQ7LwfWQNNiPS6fRKViv99+JyNaqZCwvV8C7url7on2weBKFwRUGS+VH4hRKpTI2SiYVBwXCCgoODgkJDg4KFAk4DCabhkHtQH"\ "DS0zPigyhebq6urkg3d5SXNwZHIJKpNPAAl8fj8/krFz5fIPAV+MISCAQ8LpvN51N99iMQX4J9QxslJPt4uiNh3tMLhIDDE0kUKpVGpzOYLBaLvSIOE"\ "HxnsZhMFs8vwI/s80cE4r9CFGk6VbiAjPP2RHl4oFBeXmhvDHAgEElkMmxCg22eiQ7E5PmFSPwIaHgPJUmOJCVGifl08Dp90F5eIABvHwwWi3tqQQEm"\ "K6I9EZ3FFYllMi7uILz+/40TEBkXJfXns8GIIOJxGB9v72cGRBKRBExgARtgRaMzuQJ/SUS0mIb9aGXzxzOE4TIJWE/4YErCrxW4AAEcTyAQSLBWcAq"\ "VQgX54Pj6B4VHxcoFBORqFfQ5nswPDPL35cLpFfBXXOg0Gtzac4JRFofL9w2UhMuiFTEhVPyTQvAtNAbDEAj5bI6vn5/IPyAwQOQH+/B4XM5q5tnwKw"\ "Ao+DU4TB4dHaOIk7II7k+LsD97uLmTWWwGnSMQ+geGhIaKxWCYgMESFBTgD8tPKBSKwA8SqVQep1QqYqOlXCLuw6f1h42DowMSR6WQaWy+UBQUKpWAB"\ "8ElFHYSiyVhEnCXyCIjo6JiVMlqRbRczCUTv3pWP/3C1fagiw/INoXJEwiD4KclUglMAwupTA4GvTwyVhGfqErVpSnB7GGTSd4/e64A+/jg/v0O7j44"\ "YMDhCQNDACgGAx0oRCyVRUYdiYlVqlN1uvRMTZxUxKURiPj/fqEA3PPVflsndx8Chc7h8EXB0jAgqXjFAPBH4hLUKdrMLF1aYmQwj0HCk6l/ebH+tLH"\ "be8DBHUOiMUCeRSHhh+UR0dERMqBDkXHxquRUjS5dl5oYLRawqAQsjbMXsUav79t7wBlNoIM3xfUNORyjUCYmJ6mTU5ISkmBUk5qckhgVyqdRiBgMU2"\ "T7cgX/+sEvv3JGE+nwdBNKY1O06ZnpWm16SoIqJS01Me5IpCyYSyHgfbywfMna6nlV/2e31xlLptEoZDpfHJWoyy3ILyjIzdKoosOCgvxYVCyYFyhPi"\ "lj+hSUa6BOXvUgKk4rxROMYAWFH1FpNSnJ8ZFiwL5OC9/ZCodAoT4JYEfuJFRyBeNfDwZnEpHq7ubhj6Twhn8nkMYk+aA83Dx8Sm0OlsMK12ohPreJA"\ "n5FcPMg0vJu9vbMXDuPh5unuam9r6+BJF/n5R6Tk5aei3loPRyB22vliUHgqztV23wEHuwOObo62SAyJzpfEZxflpBI/WJ+G9ZZdmIxL9EG5OhzYb4c"\ "kEJnCUHmMUp2uVXj/6tX0iv6Hmp6nUYaHHkrUKmLjlOq0rOwU6u4dG6RX9ME+ukydW1FZXl6kCafs+91mWKCdu7Hqijvj8JY9dadajf3w1cgzvUutWV"\ "hbdnQl/XOD9J9y5iwXTp2itzdAF1s+PKxoTPoKh7eTXwr8RZlo6+H7e9anYTX82hptE7FO6M80ibKM7yzfCA1kllvC32veIA5U+PL5/722jeMWDHY2b"\ "gaHoGNr+JLN4RAU/gIeulkcMiOfwz9/xaixJNOzcfDmzc3jEHTmJ/7QVnAI8n6CfzC7Nb7vyZ9pmVvDIUi4OnK2kLwnAaysiJFbxSEInsw2A1vnLwF+"\ "39ZxCALLcvp2eAkCsY3wIagJ8fvt4NDCTuy2eGifent8YMX2eN2mlq2XdXZwe/w2m4d6/r/5DW1Z6/DbDKClZXt8RfH2eHXE9nim2/b43e9tC596HbG"\ "lveepKhGIbSWACgq2beAL74IF+PrW+TJ4/2BtnbeH+R19W8VbVvfPgK3yzk/Kh+6t4c1P6wfUlvCF3T8VIKVb4ROe1U+/7N88fu3549CXm64hJv+AeF"\ "6CTeILTmsK0NhN4WYmYq02Y2DmvIQjEHLrH37WBk+1gCMQRCtfHdZq0s4ijkD8tXMj+P2PrODg+JXx6u3kzLpH8C+urU93e69HA9ng2q3TfbwNfIFGO"\ "Fda7sUF0kZP8L8S1K05Ay80hP52g/CqdnzOTa1o6QFqqdb57dlp7bl/A5UFagkxIDQ1IDEgMSAjNTIKeJxz941i0M/Iz03VTykpTs4ozS3Qd0ktzi7J"\ "L9BPzigCiscXF2SkFqXqFeSlMwAAcSYQbQ==" s. c +apply_gamma.. 0.05 b. 3 n. 0,150 n... 0,1 i[-4] 100%,100%,1,3 fc[-4] ${1-3} *[-4,-3] +[-3,-1] c.. 0,255 a[-2,-1] c #@cli chessboard : size1>0,_size2>0,_offset1,_offset2,_angle,_opacity,_color1,...,_color2,... #@cli : Draw chessboard on selected images. #@cli : Default values: 'size2=size1', 'offset1=offset2=0', 'angle=0', 'opacity=1', 'color1=0' and 'color2=255'. #@cli : $ image.jpg chessboard 32,32,0,0,25,0.3,255,128,0,0,128,255 chessboard : check "$1>0 && ${2=$1}>0" skip ${3=0},${4=0},${5=0},${6=1},${7=0},${8=255} e[^-1] "Draw chessboard on image$?, with sizes ($1,$2), offsets ($3,$4), angle $5 deg., opacity $6 and colors (${7--1})." i[0] (${7--1}) r[0] {{0,w}/2},1,1,2,-1 permute[0] cyzx repeat $!-1 { w,h={w},{h} theta:=$5*pi/180 ($3,{$3+$w-1};$3,{$3+$w-1}^$4,$4;{$4+$h-1},{$4+$h-1}) r. $w,$h,1,2,3 r. {$w*$h},2,1,1,-1 i.. ({cos($theta)},{-sin($theta)};{sin($theta)},{cos($theta)}) m*[-2,-1] r. $w,$h,1,2,-1 %. {$1+$2} >=. $1 s. c xor[-2,-1] map. [0] r. 100%,100%,1,.. j.. .,0,0,0,0,$6 rm. mv. 1 } rm[0] #@cli cie1931 #@cli : Draw CIE-1931 chromaticity diagram on selected images. #@cli : $ 500,400,1,3 cie1931 cie1931 : e[^-1] "Draw CIE-1931 chromaticity diagram on image$?." # Generate convex hull of visible colors, as a 3D object. (67.5;73.5;109.5;103.5;51.5;100.5;37;36) # Header (280,420,0;171,829,0;158,820,0;153,816,0;147,811,0;140,804,0;132,794,0;121,776,0;106,747,0;88,701,0;\ # Vertices. 65,633,0;42,539,0;20,421,0;5,295,0;0,179,0;4,115,0;10,83,0;16,61,0;25,38,0;35,21,0;47,10,0;58,3,0;\ 71,0,0;92,1,0;111,7,0;151,28,0;189,52,0;226,79,0;262,109,0;298,141,0;334,175,0;370,209,0;405,244,0;\ 441,279,0;475,313,0;509,347,0;731,568,0) xM=731 yM=829 2,{h-1},1,1,3,0 1,{h},1,1,'y' ++. 1 %. {h} +[-2,-1] 1 a[-3--1] x # Primitives. 3,{h},1,1,160 1,{h},1,1,1 # Colors + opacities. y[-4--2] a[-5--1] y mv. 0 # Generate RGB triangle of displayable colors. xR=636 yR=504 xG=297 yG=234 xB=147 yB=774 512,512,1,3 triangle_shade. 0,0,{w-1},0,0,{h-1},""255,0,0,""0,255,0,""0,0,255 rgb2srgb. +compose_channels. max +. 1e-8 /[-2,-1] *. 255 i.. (67.5;73.5;109.5;103.5;51.5;100.5;3;1;$xR;$yR;-0.01;$xG;$yG;-0.01;$xB;$yB;-0.01;\ 9;0;1;2;0;0;511;0;0;511;-128;512;512;3) y. (1) a[-3--1] y mv. 1 # Draw chroma diagram. repeat $!-2 { to_rgb. fc. 255,255,255 grid. 10%,10%,0,0,0.3,0xCCCCCCCC,1,0 100%,100%,1,3 +*3d[0,1] {(w-8)/$xM},{(h-32)/$yM} j3d... .,2,30,0,1,2 +!=... 0 distance. 1 *. -1 watershed[-4] . rm. /... 1.5 j3d... .,2,30,0,1,2 p3d. 1 p3d. 2 col3d. 128 j3d... .,2,30,0,1,1 rm. {-2,w},{-2,h} j3d. ..,2,30,0,1,2 rm.. +erode. 4 -. .. ==. 0 *[-3,-1] a[-2,-1] c blend[-2,-1] alpha 100%,100%,1,1,255 axes. 0,0.75,0.85,0,14,1 +erode. 3 negate. to_rgb.. j... ..,0,0,0,0,1,.,400 rm[-2,-1] mv. 2 } rm[0,1] #@cli circle : x[%],y[%],R[%],_opacity,_pattern,_color1,... #@cli : Draw specified colored circle on selected images. #@cli : A radius of '100%' stands for 'sqrt(width^2+height^2)'. #@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted #@cli : even if a color is specified. If a pattern is specified, the circle is #@cli : drawn outlined instead of filled. #@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'. #@cli : $ image.jpg repeat 300 circle {u(100)}%,{u(100)}%,{u(30)},0.3,${-rgb} done circle 50%,50%,100,0.7,255 circle : skip ${4=1},${5=0},${6=0} if ${"is_pattern \"$5\""} e[0--3] "Draw outlined circle at ($1,$2) with radius $3 on image$?, with opacity $4, pattern $5 and color (${6--1})." else e[0--3] "Draw filled circle at ($1,$2) with radius $3 on image$?, with opacity $4 and color (${5--1})." fi ellipse $1,$2,$3,$3,0,${4--1} #@cli close_binary : 0<=_endpoint_rate<=100,_endpoint_connectivity>=0,_spline_distmax>=0,_segment_distmax>=0,\ # 0<=_spline_anglemax<=180,_spline_roundness>=0,_area_min>=0,_allow_self_intersection={ 0 | 1 } #@cli : Automatically close open shapes in binary images (defining white strokes on black background). #@cli : Default values: 'endpoint_rate=75', 'endpoint_connectivity=2', 'spline_distmax=80', 'segment_distmax=20', \ # 'spline_anglemax=90', 'spline_roundness=1','area_min=100', 'allow_self_intersection=1'. close_binary : check "${1=75}>=0 && $1<=100 && ${2=2}>=0 && ${3=80}>=0 && ${4=20}>=0 && ${5=90}>=0 && $5<=180 && ${6=1}>=0 && ${7=100}>=0 && isnum(${8=1})" e[^-1] "Close open shapes in binary image$?, with endpoint rate $1, endpoint connectivity $2, spline max distance $3, segment max distance $4, spline max angle $5, spline roundness $6, area min $7 and self intersections "\ ${"arg0 !$8,allowed,\"not allowed\""}"." # Set algorithm parameters. endpoint_threshold:=100-$1 # in [0,100] endpoint_connectivity:=round($2) spline_distmax=$3 # in px segment_distmax=$4 # in px spline_anglemax=$5 # in deg spline_roundness=$6 area_min=$7 # in px allow_self_intersections:=!!$8 # 0 or 1 # Define useful functions to navigate through edgels. _edgel_lib=" begin( pn = [ 0,1,1,1,-1,0,-1,1,0,-1,-1,-1,1,0,1,-1 ]; pp = [ 0,-1,1,-1,1,0,1,1,0,1,-1,1,-1,0,-1,-1 ]; ); next(img,p) = ( # Used when shape is made of '!=0' (in 8-connexity) p0 = p#[0,2] + pn[4*p#[2],2]; p1 = p#[0,2] + pn[4*p#[2] + 2,2]; case = !!i(#img,p0[0],p0[1],0,0) + 2*!!i(#img,p1[0],p1[1],0,0); !case?[ p[0],p[1],(p[2]+1)%4 ]:case==1?[ p0[0],p0[1],p[2] ]:[ p1[0],p1[1],(p[2]-1)%4 ]; ); previous(img,p) = ( # Used when shape is made of '!=0' (in 8-connexity) p0 = p#[0,2] + pp[4*p#[2],2]; p1 = p#[0,2] + pp[4*p#[2] + 2,2]; case = !!i(#img,p0[0],p0[1],0,0) + 2*!!i(#img,p1[0],p1[1],0,0); !case?[ p[0],p[1],(p[2]-1)%4 ]:case==1?[ p0[0],p0[1],p[2] ]:[ p1[0],p1[1],(p[2]+1)%4 ]; ); next2(img,p) = ( # Used when shape is made of '{ 0 | 4 }' (in 4-connexity) p0 = p#[0,2] + pn[4*p#[2],2]; p1 = p#[0,2] + pn[4*p#[2] + 2,2]; case = !(i(#img,p0[0],p0[1],0,0)&3) + 2*!(i(#img,p1[0],p1[1],0,0)&3); case==1?[ p0[0],p0[1],p[2] ]:case==3?[ p1[0],p1[1],(p[2]-1)%4 ]:[ p[0],p[1],(p[2]+1)%4 ]; ); next3(img,p) = ( # Used when shape is made of '{ 1 | 2 }' (in 4-connexity) p0 = p#[0,2] + pn[4*p#[2],2]; p1 = p#[0,2] + pn[4*p#[2] + 2,2]; val1 = i(#img,p0[0],p0[1],0,0); val2 = i(#img,p1[0],p1[1],0,0); case = (val1==1 || val1==2) + 2*(val2==1 || val2==2); case==1?[ p0[0],p0[1],p[2] ]:case==3?[ p1[0],p1[1],(p[2]-1)%4 ]:[ p[0],p[1],(p[2]+1)%4 ]; ); vec(p) = ( ang = i(p[0],p[1],0,p[2])*pi/180; [ cos(ang), sin(ang) ]; );" foreach { nm={n} # Ensure input is a 2D binary image. slices 50% channels 0 > 0 => strokes # Extract primary edgel normals. 100%,100%,1,4," ref(crop(#"$strokes",x - 1,y - 1,0,0,3,3,1,1,1),N); N[4]?[ N[5]?-2:0, N[7]?-2:90, N[3]?-2:180, N[1]?-2:270 ]:[-2,-2,-2,-2]; " => e_normals # Smooth edgel normals. f[e_normals] $_edgel_lib" const boundary = 1; i<-1?-2:( ppos = npos = pos = [ x,y,c ]; u = vec(pos); for (t = 1, t<=5, ++t, ppos = previous(#"$strokes",ppos); npos = next(#"$strokes",npos); if (ppos==npos, break()); w = exp(-t^2/30); u+=w*vec(ppos); u+=w*vec(npos); ); ang = (atan2(u[1],u[0])*180/pi)%360; )" # Estimate edgel curvatures. +f[e_normals] $_edgel_lib" const boundary = 1; i<-1?-2:( pos = [ x,y,c ]; ppos = previous(#"$strokes",pos); npos = next(#"$strokes",pos); pu = vec(ppos); nu = vec(npos); du = (nu - pu)/2; cr = cross([vec(pos),0],[du,0]); norm(du)*sign(cr[2]); )" => e_curvatures # Smooth edgel curvatures. +f[e_curvatures] $_edgel_lib" const boundary = 1; i<-1?-2:( ppos = npos = pos = [ x,y,c ]; val = i; sumw = 1; for (t = 1, t<=5, ++t, ppos = previous(#"$strokes",ppos); npos = next(#"$strokes",npos); if (ppos==npos, break()); w = exp(-t^2/30); val+=w*i(ppos[0],ppos[1],0,ppos[2]); val+=w*i(npos[0],npos[1],0,npos[2]); sumw+=2*w; ); val/sumw; )" => e_smooth_curvatures # Estimate stroke radius. +distance[strokes] 0 f. " i<1-0.01||i>1+0.01?0:( p = x; q = y; d = 1; for (is_better = 1, is_better && d<32, next_p = p; next_q = q; is_better = 0; pp = p - 1; np = p + 1; pq = q - 1; nq = q + 1; (nd = i(pp,pq))>d?(d = nd; next_p = pp; next_q = pq; is_better = 1); (nd = i(p, pq))>d?(d = nd; next_p = p; next_q = pq; is_better = 1); (nd = i(np,pq))>d?(d = nd; next_p = np; next_q = pq; is_better = 1); (nd = i(pp,q))>d?(d = nd; next_p = pp; next_q = q; is_better = 1); (nd = i(np,q))>d?(d = nd; next_p = np; next_q = q; is_better = 1); (nd = i(pp,nq))>d?(d = nd; next_p = pp; next_q = nq; is_better = 1); (nd = i(p, nq))>d?(d = nd; next_p = p; next_q = nq; is_better = 1); (nd = i(np,nq))>d?(d = nd; next_p = np; next_q = nq; is_better = 1); p = next_p; q = next_q; ); d)" => stroke_radii # Detect end points. compose_channels[e_smooth_curvatures] max =>[e_smooth_curvatures] smooth_curvatures +compose_channels[e_curvatures] max => curvatures f. "i(#"$smooth_curvatures")>=("$endpoint_threshold"%)/(max(1,i(#"$stroke_radii"))) || i>=max(0.25,"$endpoint_threshold"%)" label_fg. 0,1 => keypoints if iM>0 {iM},1,1,3,-1 => keycoords f[keypoints] "> # Keep only a single point on connected regions of high curvatures. ret = 0; if (i, j = i - 1; old_max = I[#"$keycoords",j]; kappa = i(#"$smooth_curvatures"); if (kappa>old_max[2], I[#"$keycoords",j] = [ x,y,kappa ]; i(#"$keypoints",old_max[0],old_max[1]) = 0; ret = 1; ); ); ret" channels[keycoords] 0,3 # Channels 2,3 = number of connexions and normal angle for each keypoint. fi rm[smooth_curvatures,stroke_radii,keypoints] # Estimate point normals. if !narg($keycoords) rm[e_normals,e_curvatures] [strokes] => new_strokes else f[keycoords] " P = (I)[0,2]; angles = I(#"$e_normals",P); U = [ 0,0 ]; repeat (size(angles),k, if (angles[k]>=0, w = max(1e-8,i(#"$e_curvatures",P[0],P[1],0,k))^2; ang = angles[k]*pi/180; U += w*[ cos(ang),sin(ang) ]; ); ); [ P,0,(atan2(U[1],U[0])*180/pi)%360 ]" rm[e_normals,e_curvatures] # Detect pairs of keypoints preserving the spline constraints. 256,1,1,3 => keypairs f[keycoords] "> begin(ind = 0); for (nx = x + 1, nx0, if (ind>=w(#"$keypairs"), resize(#"$keypairs",2*ind,1,1,3,0)); I[#"$keypairs",ind++] = [ quality,x,nx ]; ); ); end(resize(#"$keypairs",ind,1,1,3,0)); I" if {keypairs,w} sort[keypairs] -,x else rm[keypairs] fi # Find and connect splines. [strokes] => new_strokes ind_strokes:=$allow_self_intersections?$strokes:$new_strokes if narg($keypairs) f[keypairs] ">"$_edgel_lib" for_spline(code) = for (t = 0, t<=1, t+=dt, t3 = t*(t2 = t*t); P = round(mul([t3,t2,t,1],C,2)); code#; dP = abs(mul([3*t2,2*t,1,0],C,2)) + 1e-8; dt = min(dtmin,0.75/max(dP)); ); Ic = I; indS = Ic[1]; indT = Ic[2]; if (i(#"$keycoords",indS,0,0,2)<"$endpoint_connectivity" && i(#"$keycoords",indT,0,0,2)<"$endpoint_connectivity", S = I(#"$keycoords",indS)[0,2]; T = I(#"$keycoords",indT)[0,2]; angS = i(#"$keycoords",indS,0,0,3)*pi/180; angT = i(#"$keycoords",indT,0,0,3)*pi/180; ST = T - S; dist = "$spline_roundness"*norm(ST); NS = [ cos(angS),sin(angS) ]; NT = [ cos(angT),sin(angT) ]; tmax = max(abs(ST)); is_cond = 1; C = mul([ 2,-2,1,1,-3,3,-2,-1,0,0,1,0,1,0,0,0 ],[ S,T,dist*NS,-dist*NT ],2); dt = dtmin = 1/max(abs(T - S)); current_val = 1; nb_switches = 0; for_spline( if (i(#"$ind_strokes",P,0,0)!=current_val, ++nb_switches; current_val=!current_val); if (nb_switches>2, is_cond = 0; break()) ); if (is_cond, # Check that the new spline did not create small closed regions. const area_max_threshold = "$area_min"; const area_min_threshold = 5; const max_edgels = 2*(area_max_threshold + 1); if (area_max_threshold<=0, for_spline(i(#"$new_strokes",P)=1); , #else for_spline(i(#"$new_strokes",P)|=2); for_spline( if (!i(#"$new_strokes",P[0] + 1,P[1]), edgels = area = 0; Q0 = Q = [ P[0] + 1,P[1],2 ]; do ( i(#"$new_strokes",Q[0],Q[1]) = 4; area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]); Q = next2(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels); if (edgels<=max_edgels && area>=area_min_threshold && area=area_min_threshold && area=area_min_threshold && area=area_min_threshold && area"$_edgel_lib" for_segment(code) = for (t = 0, t<=tmax, ++t, P = round(S + t*ST); code#; ); if (i(#"$keycoords",x,0,0,2)<"$endpoint_connectivity", S = I(#"$keycoords",x)[0,2]; angS = i(#"$keycoords",x,0,0,3)*pi/180; NS = [ cos(angS),sin(angS) ]; ST = round(NS*"$segment_distmax"); tmax = max(abs(ST)); ST/=tmax; is_cond = 0; current_val = 0; nb_switches = 0; for_segment( if (i(#"$new_strokes",P,0,0)!=current_val, ++nb_switches; current_val=1 - current_val); if (nb_switches==2,is_cond = 1; break();); ); tmax = t; if (is_cond, # Check that the new segment did not create small closed regions. const area_max_threshold = "$area_min"; const area_min_threshold = 5; const max_edgels = 2*(area_max_threshold + 1); if (area_max_threshold<=0, for_segment(i(#"$new_strokes",P)=0); , #else for_segment(i(#"$new_strokes",P)|=4); for_segment( if (i(#"$new_strokes",P[0] + 1,P[1])==1, edgels = area = 0; Q0 = Q = [ P[0] + 1,P[1],2 ]; do ( i(#"$new_strokes",Q[0],Q[1]) = 2; area+=(Q[2]&1?0:1-Q[2])*(Q[0]+!Q[2]); Q = next3(#"$new_strokes",Q), Q!=Q0 && ++edgels<=max_edgels); if (edgels<=max_edgels && area>=area_min_threshold && area=area_min_threshold && area=area_min_threshold && area=area_min_threshold && area[new_strokes] $nm } #@cli curve : [xy_coordinates],_thickness>0,_tilt,_tilt_strength[%],_is_closed={ 0:no | 1:yes },_opacity,_color1,... #@cli : Draw specified parameterized curve on selected images. #@cli : Arguments are: #@cli : * '[xy_coordinates]' is the set of XY-coordinates of the curve, specified as a 2-channels image. #@cli : * 'thickness' is the thickness of the drawing, specified in pixels. #@cli : * 'tilt' is an angle, specified in degrees. #@cli : * 'tilt_strength' must be a float value in [0,1] (or in [0,100] if specified as a percentage). #@cli : * 'is_closed' is a boolean which tells if the curve is closed or not. #@cli : Default values: 'thickness=0', 'tilt=45' #@cli : $ image.jpg srand 3 16,1,1,4,u s. c,2 rbf[-2,-1] 1000,0,1 n[-2] 10,{w#0-10} n[-1] 10,{h#0-10} a[-2,-1] c \ # curve[-2] [-1],6,0,0,0,1,0,128,0 curve : check ${"is_image_arg $1"}" && ${2=0}>=0 && isnum(${3=45}) && inrange(${4=0},0,1) && isbool(${5=0}) && "\ "isnum(${6=1}) && isnum(${7=0})" s0,s1=open,closed e[^-1] "Draw parameterized "${s$5}" curve $1 on image$?, with thickness $2, tilt $3, tilt strength $4, "\ "opacity $6 and color (${7--1})." pass$1 0 if !w rm. return fi r. 1,{whds/2},1,2,-1 if $2>=1 # Estimate pointwise (tilted) normals for thick curves # Ensure consecutive curve points are at a minimal distance from each others. 1,1,1,2 eval.. "> begin(ret = [ 0,0 ]; pP = I); P = I; norm(P - pP)>1e-3?(da_push(pP); pP = P); end( da_size()?(norm(P - da_back())>1e-3?da_push(P)):da_push(I[0]); da_freeze(); ); ret" rm.. # Estimate (tilted) normals. +f. " begin( const boundary = $5?2:1; const thickness = $2/2; U = cexp([ 0,$3° ]); ); T = unitnorm(J[1] - J[-1]); N = [ -T[1],T[0] ]; nU = dot(N,U)<0?-U:U; thickness*abs(dot(N,lerp(N,nU,$4)))*N" fi # Draw curve on selected images. if $2<1 repeat $!-1 { # W/o thickness 1,{h-1+$5},1,1,"const boundary = 2; P0 = I(#-1); P1 = J[#-1,1]; polygon(#"$>",2,P0[0],P0[1],P1[0],P1[1],${6--1})" rm. } rm. else repeat $!-2 { # W/ thickness 1,{h-1+$5},1,1,"const boundary = 2; P0 = I(#-2); N0 = I(#-1); P1 = J[#-2,1]; N1 = J[#-1,1]; A = round(P0 - N0); B = round(P0 + N0); C = round(P1 - N1); D = round(P1 + N1); polygon(#"$>",3,A[0],A[1],B[0],B[1],C[0],C[1],${6--1}); polygon(#"$>",3,D[0],D[1],B[0],B[1],C[0],C[1],${6--1})" rm. } rm[-2,-1] fi #@cli ellipse : x[%],y[%],R[%],r[%],_angle,_opacity,_pattern,_color1,... : (+) #@cli : Draw specified colored ellipse on selected images. #@cli : A radius of '100%' stands for 'sqrt(width^2+height^2)'. #@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted #@cli : even if a color is specified. If a pattern is specified, the ellipse is #@cli : drawn outlined instead of filled. #@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'. #@cli : $ image.jpg repeat 300 ellipse {u(100)}%,{u(100)}%,{u(30)},{u(30)},{u(180)},0.3,${-rgb} done \ # ellipse 50%,50%,100,100,0,0.7,255 #@cli flood : x[%],_y[%],_z[%],_tolerance>=0,_is_high_connectivity={ 0 | 1 },_opacity,_color1,... : (+) #@cli : Flood-fill selected images using specified value and tolerance. #@cli : Default values: 'y=z=0', 'tolerance=0', 'is_high_connectivity=0', 'opacity=1' and 'color1=0'. #@cli : $ image.jpg repeat 1000 flood {u(100)}%,{u(100)}%,0,20,0,1,${-rgb} done #@cli gaussian : _sigma1[%],_sigma2[%],_angle #@cli : Draw a centered gaussian on selected images, with specified standard deviations and orientation. #@cli : Default values: 'sigma1=3', 'sigma2=sigma1' and 'angle=0'. #@cli : $ 400,400 gaussian 100,30,45 #@cli : $$ https://gmic.eu/oldtutorial/_gaussian gaussian : skip ${1=15%},${2=$1},${3=0} e[^-1] "Draw centered gaussian on image$? with standard deviations ($1,$2) and angle $3 deg." u:=cos($3*pi/180) v:=sin($3*pi/180) dmax:=max(w,h) if isnum($1) l1=$1 else l1:=${1}10000*$dmax/100 fi if isnum($2) l2=$2 else l2:=${2}10000*$dmax/100 fi l1:=1/(2*max(1/3,$l1)^2) l2:=1/(2*max(1/3,$l2)^2) A:=$l1*$u*$u+$l2*$v*$v B:=($l1-$l2)*$u*$v C:=$l1*$v*$v+$l2*$u*$u foreach { nm={n} w,h={w},{h} ds={d},{s} rm $w,$h,1,1,'X=x-{($w-1)/2};Y=y-{($h-1)/2};$A*X*X+2*$B*X*Y+$C*Y*Y' * -1 exp r $w,$h,$ds => $nm } #@cli graph : [function_image],_plot_type,_vertex_type,_ymin,_ymax,_opacity,_pattern,_color1,... : \ # 'formula',_resolution>=0,_plot_type,_vertex_type,_xmin,xmax,_ymin,_ymax,_opacity,_pattern,_color1,... : (+) #@cli : Draw specified function graph on selected images. #@cli : 'plot_type' can be { 0:none | 1:lines | 2:splines | 3:bar }. #@cli : 'vertex_type' can be { 0:none | 1:points | 2,3:crosses | 4,5:circles | 6,7:squares }. #@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted #@cli : even if a color is specified. #@cli : Default values: 'plot_type=1', 'vertex_type=1', 'ymin=ymax=0 (auto)', 'opacity=1', 'pattern=(undefined)' #@cli : and 'color1=0'. #@cli : $ image.jpg +rows 50% blur[-1] 3 split[-1] c div[0] 1.5 graph[0] [1],2,0,0,0,1,255,0,0 \ # graph[0] [2],2,0,0,0,1,0,255,0 graph[0] [3],2,0,0,0,1,0,0,255 keep[0] #@cli grid : size_x[%]>=0,size_y[%]>=0,_offset_x[%],_offset_y[%],_opacity,_pattern,_color1,... #@cli : Draw xy-grid on selected images. #@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted #@cli : even if a color is specified. #@cli : Default values: 'offset_x=offset_y=0', 'opacity=1', 'pattern=(undefined)' and 'color1=0'. #@cli : $ image.jpg grid 10%,10%,0,0,0.5,255 #@cli : $ 400,400,1,3,255 grid 10%,10%,0,0,0.3,0xCCCCCCCC,128,32,16 grid : check "$1>=0 && $2>=0" skip ${3=0},${4=0},${5=1},${6=0},${7=$6} if ${"is_pattern \"$6\""} e[0--3] "Draw xy-grid on image$?, with sizes ($1,$2), offsets ($3,$4), opacity $5, pattern $6 and color (${7--1})." pattern=$6 color=${7--1} else e[0--3] "Draw xy-grid on image$?, with sizes ($1,$2), offsets ($3,$4), opacity $5, and color (${6--1})." pattern=0xFFFFFFFF color=${6--1} fi eval " repeat (l,k, # Horizontal lines. size = ispercentage($1)?max(1,w#k*$1):$1; size>=1?( off = (ispercentage($3)?size*$3:$3)%size; for (x = off, x=1?( off = (ispercentage($4)?size*$4:$4)%size; for (y = off, y] ..,$2,$3,$4,$5,$6,.,255 } rm[-3--1] else s. c,{1-s} repeat $!-2 { j[$>] ..,$2,$3,$4,$5,$6,.,255 } rm[-2,-1] fi #@cli line : x0[%],y0[%],x1[%],y1[%],_opacity,_pattern,_color1,... : (+) #@cli : Draw specified colored line on selected images. #@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted #@cli : even if a color is specified. #@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'. #@cli : $ image.jpg repeat 500 line 50%,50%,{u(w)},{u(h)},0.5,${-rgb} done line 0,0,100%,100%,1,0xCCCCCCCC,255 \ # line 100%,0,0,100%,1,0xCCCCCCCC,255 #@cli line_aa : x0[%],y0[%],x1[%],y1[%],_opacity,_color1,... #@cli : Draw specified antialiased colored line on selected images. #@cli : Default values: 'opacity=1' and 'color1=0'. #@cli : $ 512,512,1,3 repeat 100 line_aa {v([w,h,w,h])-1},1,${-RGB} done line_aa : check "isnum(${5=1}) && isnum(${6=0})" e[^-1] "Draw antialiased line ($1,$2) - ($3,$4) on image$?, with opacity $5 and color (${6--1})." if !$5 return fi foreach { x0:=ispercentage($1)?(w-1)*$1:$1 y0:=ispercentage($2)?(h-1)*$2:$2 x1:=ispercentage($3)?(w-1)*$3:$3 y1:=ispercentage($4)?(h-1)*$4:$4 if $#>6 eval ${-math_lib}"line_aa($x0,$y0,$x1,$y1,$5,[${6--1}])" else eval ${-math_lib}"line_aa($x0,$y0,$x1,$y1,$5,$6)" fi } #@cli spline : x0[%],y0[%],u0[%],v0[%],x1[%],y1[%],u1[%],v1[%],_opacity,_color1,... #@cli : Draw specified colored spline curve on selected images (cubic hermite spline). #@cli : Default values: 'opacity=1' and 'color1=0'. #@cli : $ image.jpg repeat 30 { spline {u(100)}%,{u(100)}%,{u(-600,600)},{u(-600,600)},{u(100)}%,{u(100)}%,\ # {u(-600,600)},{u(-600,600)},1,${-RGB} } spline : skip ${9=1},${10=0} e[^-1] "Draw spline from ($1,$2) [$3,$4] to ($5,$6) [$7,$8] on image$?, with opacity $9 and color (${10--1})." foreach { x0:=ispercentage($1)?$1*(w-1):$1 y0:=ispercentage($2)?$2*(h-1):$2 u0:=ispercentage($3)?$3*(w-1):$3 v0:=ispercentage($4)?$4*(h-1):$4 x1:=ispercentage($5)?$5*(w-1):$5 y1:=ispercentage($6)?$6*(h-1):$6 u1:=ispercentage($7)?$7*(w-1):$7 v1:=ispercentage($8)?$8*(h-1):$8 eval ${-math_lib}"spline(#0,["$x0","$y0"],["$u0","$v0"],["$x1","$y1"],["$u1","$v1"],$9,[${10--1}])" } #@cli thickline : x0[%],y0[%],x1[%],y1[%],_thickness,_opacity,_color1 #@cli : Draw specified colored thick line on selected images. #@cli : Default values: 'thickness=2', 'opacity=1' and 'color1=0'. #@cli : $ 400,400,1,3 repeat 100 thickline {u([w,h,w,h,5])},0.5,${-rgb} done thickline : check "${5=2}>=0 && isnum(${6=1}) && isnum(${7=0})" e[^-1] "Draw thick line ($1,$2) - ($3,$4) on image$?, with thickness $5, opacity $6 and color (${7--1})." if !$5 line ${1-4},${6--1} else foreach { x0:=ispercentage($1)?(w-1)*$1:$1 y0:=ispercentage($2)?(h-1)*$2:$2 x1:=ispercentage($3)?(w-1)*$3:$3 y1:=ispercentage($4)?(h-1)*$4:$4 coords:=" const th = "$5"; P0 = [ "$x0","$y0" ]; P1 = [ "$x1","$y1" ]; dP = P1 - P0; n = [ -dP[1],dP[0] ]/max(1e-8,norm(dP))*th/2; round([ P0 - n, P0 + n, P1 + n, P1 - n ]); " polygon 4,$coords,${6--1} } fi #@cli thickspline : x0[%],y0[%],u0[%],v0[%],x1[%],y1[%],u1[%],v1[%],_thickness,_opacity,_color1,... #@cli : Draw specified colored thick spline curve on selected images (cubic hermite spline). #@cli : Default values: 'thickness=3', 'opacity=1' and 'color1=0'. #@cli : $ image.jpg repeat 30 { thickspline {u(100)}%,{u(100)}%,{u(-600,600)},{u(-600,600)},{u(100)}%,{u(100)}%,\ # {u(-600,600)},{u(-600,600)},3,1,${-RGB} } thickspline : skip ${9=3},${10=1},${11=0} e[^-1] "Draw spline from ($1,$2) [$3,$4] to ($5,$6) [$7,$8] on image$?, "\ "with thickness $9, opacity $10 and color (${11--1})." foreach { x0:=ispercentage($1)?$1*(w-1):$1 y0:=ispercentage($2)?$2*(h-1):$2 u0:=ispercentage($3)?$3*(w-1):$3 v0:=ispercentage($4)?$4*(h-1):$4 x1:=ispercentage($5)?$5*(w-1):$5 y1:=ispercentage($6)?$6*(h-1):$6 u1:=ispercentage($7)?$7*(w-1):$7 v1:=ispercentage($8)?$8*(h-1):$8 eval ${-math_lib}"thickspline(#0,["$x0","$y0"],["$u0","$v0"],["$x1","$y1"],["$u1","$v1"],$9,$10,[${11--1}])" } #@cli mandelbrot : z0r,z0i,z1r,z1i,_iteration_max>=0,_is_julia={ 0 | 1 },_c0r,_c0i,_opacity : (+) #@cli : Draw mandelbrot/julia fractal on selected images. #@cli : Default values: 'iteration_max=100', 'is_julia=0', 'c0r=c0i=0' and 'opacity=1'. #@cli : $ 400,400 mandelbrot -2.5,-2,2,2,1024 map 0 +blur 2 elevation3d[-1] -0.2 #@cli marble : _image_weight,_pattern_weight,_angle,_amplitude,_sharpness>=0,_anisotropy>=0,_alpha,_sigma,\ # _cut_low>=0,_cut_high>=0 #@cli : Render marble like pattern on selected images. #@cli : Default values: 'image_weight=0.2', 'pattern_weight=0.1', 'angle=45', 'amplitude=0', 'sharpness=0.4' \ # and 'anisotropy=0.8', #@cli : 'alpha=0.6', 'sigma=1.1' and 'cut_low=cut_high=0'. #@cli : $ image.jpg +marble , marble : skip ${1=0.2},${2=0.1},${3=45},${4=0},${5=0.4},${6=0.8},${7=0.6},${8=1.1},${9=0%},${10=100%} e[^-1] "Render marble like pattern on image$?, with image weight $1, pattern weight $2, angle $3 deg., amplitude $4, sharpness $5, anisotropy $6, alpha $7, sigma $8, and cut ($9,$10)." sx:=$2*sin($3*pi/180) sy:=$2*cos($3*pi/180) f sin(x*$sx+y*$sy+i*$1) if $4 smooth $4,$5,$6,$7,$8 fi c $9,$10 n 0,255 #@cli maze : _width>0,_height>0,_cell_size>0 #@cli : Input maze with specified size. #@cli : $ maze 30,20 negate normalize 0,255 +maze : check "isint(${1=15},1) && isint(${2=$1},1) && isint(${3=24},1)" e[^-1] "Input $1x$2 maze." ({v($1-1)},{v($2-1)}) # Starting cell. $1,$2,1,1,15 +f. 0 a[-2,-1] c # Starting maze data. _generate_maze $1,$2 _render_maze. $3 => [maze] _generate_maze : # Start opening walls. do x={-2,@-2} y={-2,@-1} # Coords of the current cell. =. 1,$x,$y,0,1 # Mark current cell as visited. # Check for neighboring cells that are candidate for opening wall, and select one random. is_candidate=0 up=-1 if i($x,$y)&8" && "$y>0" && "!i($x,$y-1,0,1) up=$x,{$y-1},8 is_candidate=1 fi # Up. down=-1 if i($x,$y)&4" && "$y<$2-1" && "!i($x,$y+1,0,1) down=$x,{$y+1},4 is_candidate=1 fi # Down. left=-1 if i($x,$y)&2" && "$x>0" && "!i($x-1,$y,0,1) left={$x-1},$y,2 is_candidate=1 fi # Left. right=-1 if i($x,$y)&1" && "$x<$1-1" && "!i($x+1,$y,0,1) right={$x+1},$y,1 is_candidate=1 fi # Right. if $is_candidate ($up,$down,$left,$right) y. discard. -1 r. 3,{h/3},1,1,-1 shift. 0,{v(4)},0,0,2 rows. 0,0 mv. -2 fi # Remove wall between the current and chosen neighboring cells. if $is_candidate if {-2,@-1}==8 =. {i($x,$y)&7},$x,$y =. {i($x,$y-1)&11},$x,{$y-1} # Remove up wall. elif {-2,@-1}==4 =. {i($x,$y)&11},$x,$y =. {i($x,$y+1)&7},$x,{$y+1} # Remove down wall. elif {-2,@-1}==2 =. {i($x,$y)&13},$x,$y =. {i($x-1,$y)&14},{$x-1},$y # Remove left wall. else =. {i($x,$y)&14},$x,$y =. {i($x+1,$y)&13},{$x+1},$y # Remove right wall. fi z.. 0,1 a[-3,-2] y # Add neighboring cell to stack of cells to explore. else # No candidate : remove current cell from cells to explore. if h#-2==1 break fi rows.. 0,{{-2,h}-2} fi while 1 rm.. channels. 0 _render_maze : # Create the 16 configurations of walls. i[0] $1,$1 i[1] [0]x15 line[8-15] 0,0,100%,0,1,1 line[4-7,12-15] 0,100%,100%,100%,1,1 line[2-3,6-7,10-11,14-15] 0,0,0,100%,1,1 line[1-15:2] 100%,0,100%,100%,1,1 # Map the wall data with them. a[0-15] x r. {w*$1},{h*$1} *. $1 channels. 0,1 $1,$1,1,1,x $1,$1,1,1,y a[-2,-1] c r. ..,..,1,2,0,2 +[-2,-1] warp.. .,0,0,0 rm. #@cli maze_mask : _cellsize>0 #@cli : Input maze according to size and shape of selected mask images. #@cli : Mask may contain disconnected shapes. #@cli : $ 0 text "G'MIC",0,0,53,1,1 dilate 3 autocrop 0 frame xy,1,0 maze_mask 8 dilate 3 negate mul 255 maze_mask : check "isint(${1=24},1)" e[^-1] "Input masked maze from image$? with cell size $1." compose_channels + >= 50% foreach { do +rand[0] 0,1 *. [0] ({[xM,yM]}) rm.. # Select one starting point in the mask. +flood[0] {^},0,0,0,1,2 >=. 2 +negate. *.. 15 a[-2,-1] c flood[0] {-2,^},0,0,0,1,0 _generate_maze {w},{h} while iM#0 rm[0] + _render_maze. $1 => [maze] } #@cli newton_fractal : z0r,z0i,z1r,z1i,_angle,0<=_descent_method<=2,_iteration_max>=0,_convergence_precision>0,\ # _expr_p(z),_expr_dp(z),_expr_d2p(z) #@cli : Draw newton fractal on selected images, for complex numbers in range (z0r,z0i) - (z1r,z1i). #@cli : Resulting images have 3 channels whose meaning is [ last_zr, last_zi, nb_iter_used_for_convergence ]. #@cli : 'descent_method' can be { 0:secant | 1:newton | 2:householder }. #@cli : Default values: 'angle=0', 'descent_method=1', 'iteration_max=200', 'convergence_precision=0.01', \ # 'expr_p(z)=z^^3-1', 'expr_dp(z)=3*z^^2' and 'expr_d2z(z)=6*z'. #@cli : $ 400,400 newton_fractal -1.5,-1.5,1.5,1.5,0,2,200,0.01,"z^^6 + z^^3 - 1","6*z^^5 + 3*z^^2","30*z^^4 + 6*z" \ # f "[ atan2(i1,i0)*90+20,1,cut(i2/30,0.2,0.7) ]" hsl2rgb newton_fractal : check "isin(${6=1},0,1,2) && ${7=200}>=0 && ${8=0.01}>0" skip "${4=0},${9=z^^3-1},${10=3*z^^2},${11=6*z}" m0,m1,m2=secant,newton,householder e[^-1] "Draw newton fractal on image$?, for complex range ($1,$2)-($3,$4), with angle $5, $7 max "${m$6}" "\ "iterations, precision $8, and expressions 'p(z)=$9', 'dp(z)=$10' and 'd2p(z)=$11'." channels 0,2 f "* begin( const dx = abs($3 - $1); const dy = abs($4 - $2); const angle = $5; const method = $6; const itermax = $7; const precision = $8; zc = [ $1 + $3, $2 + $4 ]/2; R = rot(-angle°); ); p(z) = ($9); dp(z) = ($10); d2p(z) = ($11); zn = [ $1 + x*dx/(w-1), $2 + y*dy/(h-1) ]; angle?(zn = (R*(zn-=zc)+=zc)); !method?(znm1 = zn + [ precision,0 ]); repeat (itermax,iter, pzn = p(zn); !method?( znp1 = zn - pzn**(zn - znm1)//(pzn - p(znm1)); # secant znm1 = zn; ):method==1?( dpzn = dp(zn); znp1 = zn - pzn//dpzn; # newton ):( # householder dpzn = dp(zn); d2pzn = d2p(zn); hn = (pzn**d2pzn)//(2*dpzn^^2); znp1 = zn - pzn//dpzn**([1,0] + hn); ); norm(znp1 - zn)=0,0<=_min_scale<=100,_allow_rotation={ 0:0 deg. | 1:180 deg. | 2:90 deg. | 3:any },\ # _spacing,_precision>=0,max_iterations>=0 #@cli : Try to randomly pack as many sprites as possible onto the 'empty' areas of an image. #@cli : Sprites can be eventually rotated and scaled during the packing process. #@cli : First selected image is the canvas that will be filled with the sprites. #@cli : Its last channel must be a binary mask whose zero values represent potential locations for drawing the sprites. #@cli : All other selected images represent the sprites considered for packing. #@cli : Their last channel must be a binary mask that represents the sprite shape (i.e. a 8-connected component). #@cli : The order of sprite packing follows the order of specified sprites in the image list. #@cli : Sprite packing is done on random locations and iteratively with decreasing scales. #@cli : 'nb_scales' sets the number of decreasing scales considered for all specified sprites to be packed. #@cli : 'min_scale' (in %) sets the minimal size considered for packing (specified as a percentage of the #@cli : original sprite size). #@cli : 'spacing' can be positive or negative. #@cli : 'precision' tells about the desired number of failed trials before ending the filling process. #@cli : Default values: 'nb_scales=5', 'min_scale=25', 'allow_rotation=3', 'spacing=1', 'precision=7' \ # and 'max_iterations=256'. #@cli : $ 512,512,1,3,"min(255,y*c/2)" 100%,100% circle 50%,50%,100,1,255 append c image.jpg rescale2d[-1] ,24 \ # to_rgba pack_sprites 3,25 pack_sprites : check "isint(${1=5},0) && ${2=25}>=0 && $2<=100 && isint(${3=3},0,3) && isint(${4=1}) && isint(${5=7},0) && isint(${6=256},0)" e[^-1] "Randomly pack image$? with $1 scales, minimum scale $2%, "${arg0\ $3,no,180\"\ \"deg.,90\"\ \"deg.,any}\ " rotation, spacing $4, precision $5 and $6 maximum iterations." N:=$!-1 is_first_time=1 foreach { r 100%,100%,1,{max(2,s)} } # Ensure all images have a binary shape mask. # Start iterations over scales. repeat $1 { rprogress {$>*100/$1} nb_attempts=0 # Generate all sprites for current scale. ratio={$1>1?$2+(100-$2)*$}] { w,h:=[w,h]*$ratio if $w<1||$h<1 rm else r $w,$h,1,100%,2 sh. 100% !=. 0 area{1+$>}:=is rm. fi } } # Pack rescaled sprites together. l[0,{$N+1}--1] { repeat $6 { # Compute reference sprite. ind:=1+($>%$N) area=${area$ind} if !$3 [$ind] elif $3==1 +rotate[$ind] {v()*180} elif $3==2 +rotate[$ind] {v(3)*90} else +rotate[$ind] {u*360} sh. 100% !=. 0 area:=is rm. fi # Get binary map of possible locations. +channels[0] 100% ==. 0 if $4>1 erode. {2*$4-1} elif $4<1 dilate. {-2*$4+3} fi # Generate random skeleton-oriented point cloud. +rectangle. 0,0,100%,100%,1,0xFFFFFFFF,0 if $is_first_time noise. 0.1,2 ==. 1 fi distance. 0 noise. 1,1 max_patch. {$ind,round(1.5*max(w,h))} *. .. pointcloud3d. # Subdivide point cloud if multiple sprites. if $N>1 l. { s3d /[1] $N round[1] max[1] 1 n={1,@0} r[2] 3,{{2,h}/3},1,1,-1 i[2] 1,{2,h} rand[2] 0,1 a[2,3] x sort[2] +,y z[2] 1,3 r[2] 3,$n,1,1 y[2] r[3] 1,{2*$n},1,1,0 r[4] 1,{3*$n},1,1,0 r[5] 1,$n,1,1,0 a y } fi # Create 3D cloud of sprites. n={@7} if $n s3d. rm[-2,-1] if !$3 # No rotation allowed. [-6] i.. (-128;{w};{h};{s}) if $n>1 4,{$n-1},1,1,-128,0,0,0 fi +channels.. 100% i.. (-128;{w};{h};{s}) if $n>1 ... fi elif $3==1 # 180 deg. rotation allowed. +rotate[-6] {v()*180} i.. (-128;{w};{h};{s}) if $n>1 +rotate. 180 i.. (-128;{w};{h};{s}) fi if $n>2 4,{$n-2},1,1,-128,0,0,0 1,100% rand. 0,1 round. 1 j.. .,1 rm. fi +channels[-4] 100% i.. (-128;{w};{h};{s}) if $n>1 +channels[-4] 100% i.. (-128;{w};{h};{s}) fi if $n>2 [-5] fi else # 90 deg. rotation (or more) allowed. +rotate[-6] {v(3)*90} i.. (-128;{w};{h};{s}) if $n>1 +rotate. 90 i.. (-128;{w};{h};{s}) fi if $n>2 +rotate. 90 i.. (-128;{w};{h};{s}) fi if $n>3 +rotate. 90 i.. (-128;{w};{h};{s}) fi if $n>4 4,{$n-4},1,1,-128,0,0,0 1,100% rand. 0,3 round. 1 j.. .,1 rm. fi +channels[-8] 100% i.. (-128;{w};{h};{s}) if $n>1 +channels[-8] 100% i.. (-128;{w};{h};{s}) fi if $n>2 +channels[-8] 100% i.. (-128;{w};{h};{s}) fi if $n>3 +channels[-8] 100% i.. (-128;{w};{h};{s}) fi if $n>4 [-9] fi fi y[{$N+3}--1] a[{$N+3}--1] y fi rm... # Delete reference sprite. # Draw cloud and detect non-intersecting sprites. [0] sh. 100% f. 1 -. [-4] j3d.. ...,0,0,0,1,2,0,0 rm[-3,-1] sh. 100% area_fg. 0,1 ==. $area *. ... rm... sh.. 0,{-2,s-2} *. .. rm. # Draw selected sprites on rendering image. if iM j[0] ..,0,0,0,0,1,. rm[-2,-1] else rm[-2,-1] nb_attempts+=1 if $nb_attempts>$5 break else continue fi fi } k[0] } } k[0] #@cli piechart : label_height>=0,label_R,label_G,label_B,"label1",value1,R1,G1,B1,...,"labelN",valueN,RN,GN,BN #@cli : Draw pie chart on selected (RGB) images. #@cli : $ image.jpg piechart 25,0,0,0,"Red",55,255,0,0,"Green",40,0,255,0,"Blue",30,128,128,255,"Other",5,128,128,128 piechart : check $1>=0 e[^-1] "Draw pie chart on image$?, with label height $1 and color ($2,$3,$4)." $=arg foreach { ellipse 50%,50%,{w/2-1},{h/2-1},0,1,1 ellipse 50%,50%,{w/2-1},{h/2-1},0,1,0xFFFFFFFF (${6--1:5}) normalize_sum. theta=0 if w>1 repeat w { xe:=0.5*{-2,w}*(1+cos($theta)) ye:=0.5*{-2,h}*(1+sin($theta)) line.. 50%,50%,$xe,$ye theta-=2*pi*i($>) } fi theta=0 repeat w { if i($>) ntheta:=$theta-2*pi*i($>) xc:=0.5*{-2,w}*(1+0.5*cos(0.5*($ntheta+$theta))) yc:=0.5*{-2,h}*(1+0.5*sin(0.5*($ntheta+$theta))) xf:=0.5*{-2,w}*(1+0.8*cos(0.5*($ntheta+$theta))) yf:=0.5*{-2,h}*(1+0.8*sin(0.5*($ntheta+$theta))) flood.. $xf,$yf,0,0,0,1,${arg{7+5*$>}},${arg{8+5*$>}},${arg{9+5*$>}} if abs($ntheta-$theta)>0.1 0 t. ${arg{5+5*$>}},0,0,$1,1,1 ($2^$3^$4) r. ..,..,1,3 *. .. j[-4] .,{$xc-w/2},{$yc-h/2},0,0,1,.. rm[-2,-1] fi theta=$ntheta fi } rm. } #@cli plasma : _alpha,_beta,_scale>=0 : (+) #@cli : Draw a random colored plasma fractal on selected images. #@cli : This command implements the so-called 'Diamond-Square' algorithm. #@cli : Default values: 'alpha=1', 'beta=1' and 'scale=8'. #@cli : $ 400,400,1,3 plasma #@cli : $$ https://gmic.eu/oldtutorial/_plasma #@cli point : x[%],_y[%],_z[%],_opacity,_color1,... : (+) #@cli : Set specified colored pixel on selected images. #@cli : Default values: 'z=0', 'opacity=1' and 'color1=0'. #@cli : $ image.jpg repeat 10000 point {u(100)}%,{u(100)}%,0,1,${-rgb} done #@cli polka_dots : diameter>=0,_density,_offset1,_offset2,_angle,_aliasing,_shading,_opacity,_color,... #@cli : Draw dots pattern on selected images. #@cli : Default values: 'density=20', 'offset1=offset2=50', 'angle=0', 'aliasing=10', 'shading=1', 'opacity=1' \ # and 'color=255'. #@cli : $ image.jpg polka_dots 10,15,0,0,20,10,1,0.5,0,128,255 polka_dots : check $1>=0 skip ${2=20},${3=50},${4=50},${5=0},${6=10},${7=1},${8=1},${9=255} e[^-1] "Draw polka dots on image$?, with diameter $1, density $2, angle $3 deg., shift ($4,$5), aliasing $6 and shading $7." theta:=$5*pi/180 ct:=cos($theta) st:=sin($theta) mid1:=$1/2 mid2:=$2/2 i[0] (${9--1}) y[0] c repeat $!-1 { WH:=max(w,h) 100%,100%,100%,1,"xn = 100*x/"$WH"-$3; yn = 100*y/"$WH"-$4; \ xr = xn*"$ct"-yn*"$st"; yr = xn*"$st"+yn*"$ct"; \ xc = xr%$2-"$mid2"; yc = yr%$2-"$mid2"; \ "$mid1"-sqrt(xc*xc+yc*yc)" *. $6 c. 0,$7 n. 0,$8 (${9--1}) y. c r. ..,..,.. j... .,0,0,0,0,1,.. rm[-2,-1] mv. 1 } rm[0] #@cli polygon : N>=1,x1[%],y1[%],...,xN[%],yN[%],_opacity,_pattern,_color1,... : (+) #@cli : Draw specified colored N-vertices polygon on selected images. #@cli : 'pattern' is an hexadecimal number starting with '0x' which can be omitted #@cli : even if a color is specified. If a pattern is specified, the polygon is #@cli : drawn outlined instead of filled. #@cli : Default values: 'opacity=1', 'pattern=(undefined)' and 'color1=0'. #@cli : $ image.jpg polygon 4,20%,20%,80%,30%,80%,70%,20%,80%,0.3,0,255,0 \ # polygon 4,20%,20%,80%,30%,80%,70%,20%,80%,1,0xCCCCCCCC,255 #@cli : $ image.jpg 2,16,1,1,'u(x?{h}:{w})' polygon[-2] {h},{^},0.6,255,0,255 remove[-1] #@cli quiver : [function_image],_sampling[%]>0,_factor>=0,_is_arrow={ 0 | 1 },_opacity,_color1,... #@cli : Draw specified 2D vector/orientation field on selected images. #@cli : Default values: 'sampling=5%', 'factor=1', 'is_arrow=1', 'opacity=1', 'pattern=(undefined)' #@cli : and 'color1=0'. #@cli : $ 100,100,1,2,'!c?x-w/2:y-h/2' 500,500,1,3,255 quiver[-1] [-2],10 #@cli : $ image.jpg +rescale2d ,600 luminance[0] gradient[0] mul[1] -1 reverse[0,1] append[0,1] c \ # blur[0] 8 orientation[0] quiver[1] [0],20,1,1,0.8,255 quiver : check ${"is_image_arg $1"}" && ${2=5%}>0 && ${3=1}>=0 && isbool(${4=1})" skip "${5=1},${6=0}" e[^-1] "Draw 2D vector field $1 on image$?, with sampling $2, factor $3, arrows "${"arg0 $4,disabled,enabled"}", opacity $5 and color (${6--1})." pass$1 repeat $!-1 { l[$>,-1] { eval ${-math_lib}" s_sampling = ['$2']; sampling = s_sampling[size(s_sampling) - 1 ]==_'%'?min(w#0,h#0)*$2:$2; vmax = max(abs(im),abs(iM)); vmax = vmax?vmax:1; fact = $3*sampling/vmax; for (y = sampling/2, y=0','mirroring={ 0:none | 1:x | 2:y | 3:xy } #@cli : Render rorschach-like inkblots on selected images. #@cli : Default values: 'smoothness=5%' and 'mirroring=1'. #@cli : $ 400,400 rorschach 3% rorschach : check "${1=5%}>=0 && isint(${2=1},0,3)" e[^-1] "Render rorschach-like inkblots on image$?, with smoothness $1 and "${arg0\ $2,no,x,y,xy}"-mirroring." if !$2 # No mirroring. rand -1,1 b $1 >= 0 elif $2==1 # X-mirroring. foreach { w:=w columns 0,{w/2-1} rand -1,1 b $1 >= 0 +mirror x if $w%2 columns. 1,100% fi a x } elif $2==2 # Y-mirroring. foreach { h:=h rows 0,{h/2-1} rand -1,1 b $1 >= 0 +mirror y if $h%2 rows. 1,100% fi a y } elif $2==3 # XY-mirroring. foreach { w,h={w},{h} z 0,0,{w/2-1},{h/2-1} rand -1,1 b $1 >= 0 +mirror x if $w%2 columns. 1,100% fi a x +mirror y if $h%2 rows. 1,100% fi a y } fi #@cli sierpinski : recursion_level>=0 #@cli : Draw Sierpinski triangle on selected images. #@cli : Default value: 'recursion_level=7'. #@cli : $ image.jpg sierpinski 7 sierpinski : check ${1=7}>=0 skip ${2=50},${3=0},${4=0},${5=100},${6=100},${7=100} e[^-1] "Draw Sierpinski triangle of degree $1 on image$?." _sierpinski ${2-7},$1 _sierpinski : if $7<=0 polygon 3,$1%,$2%,$3%,$4%,$5%,$6%,1,255 return fi _sierpinski $1,$2,{($1+$3)/2},{($2+$4)/2},{($1+$5)/2},{($2+$6)/2},{$7-1} _sierpinski {($1+$3)/2},{($2+$4)/2},$3,$4,{($3+$5)/2},{($4+$6)/2},{$7-1} _sierpinski {($1+$5)/2},{($2+$6)/2},$5,$6,{($3+$5)/2},{($4+$6)/2},{$7-1} #@cli spiralbw : width>0,_height>0,_is_2dcoords={ 0 | 1 } #@cli : Input a 2D rectangular spiral image with specified size. #@cli : Default values: 'height=width' and 'is_2dcoords=0'. #@cli : $ spiralbw 16 #@cli : $ image.jpg spiralbw {[w,h]},1 +warp[0] [1],0,1,1 +warp[2] [1],2,1,1 +spiralbw : check "$1>=1 && ${2=$1}>=1 && isbool(${3=0})" e[^-1] "Input 2D rectangular spiral image of size $1x$2." main="alpha = min(x,y,w - 1 - x,h - 1 - y); t0 = alpha*2*(w + h) - 4*alpha^2; X = x - alpha; Y = y - alpha; W = w - 2*alpha; H = h - 2*alpha; t = t0 + (!Y?X: X==W - 1?W - 1 + Y: Y==H - 1?2*W + H - 3 - X: 2*(W + H - 2) - Y);" if $3 $1,$2,1,2,$main"[ t%w, int(t/w) ]" else $1,$2,1,1,$main fi #@cli tetraedron_shade : x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3,R0,G0,B0,...,R1,G1,B1,...,R2,G2,B2,...,R3,G3,B3,... #@cli : Draw tetraedron with interpolated colors on selected (volumetric) images. tetraedron_shade : e[^-1] "Draw tetraderon ($1,$2,$3)-($4,$5,$6)-($7,$8,$9)-($10,$11,$12) with interpolated colors in image$?." # Find bounding box. xm:=round(min($1,$4,$7,$10),1,-1) xM:=round(max($1,$4,$7,$10),1,1) ym:=round(min($2,$5,$8,$11),1,-1) yM:=round(max($2,$5,$8,$11),1,1) zm:=round(min($3,$6,$9,$12),1,-1) zM:=round(max($3,$6,$9,$12),1,1) # Find color mapping coefficients for each vertex. l[] { (${1-3},1;${4-6},1;${7-9},1;${10-12},1) (${13--1}) r. {w/4},4,1,1,-1 s. x solve[^0] [0] rm[0] a c } # Draw tetraedron on selected images. f[^-1] "* begin( x0 = $1; y0 = $2; z0 = $3; x1 = $4; y1 = $5; z1 = $6; x2 = $7; y2 = $8; z2 = $9; x3 = $10; y3 = $11; z3 = $12; u01 = x1 - x0; v01 = y1 - y0; w01 = z1 - z0; u02 = x2 - x0; v02 = y2 - y0; w02 = z2 - z0; u03 = x3 - x0; v03 = y3 - y0; w03 = z3 - z0; u12 = x2 - x1; v12 = y2 - y1; w12 = z2 - z1; u13 = x3 - x1; v13 = y3 - y1; w13 = z3 - z1; u23 = x3 - x2; v23 = y3 - y2; w23 = z3 - z2; nx012 = v01*w02 - w01*v02; ny012 = w01*u02 - u01*w02; nz012 = u01*v02 - v01*u02; if (nx012*u03 + ny012*v03 + nz012*w03<0, nx012*=-1; ny012*=-1; nz012*=-1); nx013 = v01*w03 - w01*v03; ny013 = w01*u03 - u01*w03; nz013 = u01*v03 - v01*u03; if (nx013*u02 + ny013*v02 + nz013*w02<0, nx013*=-1; ny013*=-1; nz013*=-1); nx023 = v02*w03 - w02*v03; ny023 = w02*u03 - u02*w03; nz023 = u02*v03 - v02*u03; if (nx023*u01 + ny023*v01 + nz023*w01<0, nx023*=-1; ny023*=-1; nz023*=-1); nx123 = v12*w13 - w12*v13; ny123 = w12*u13 - u12*w13; nz123 = u12*v13 - v12*u13; if (-nx123*u01 - ny123*v01 - nz123*w01<0, nx123*=-1; ny123*=-1; nz123*=-1); ); if (x<"$xm" || x>"$xM" || y<"$ym" || y>"$yM" || z<"$zm" || z>"$zM",i, dx0 = x - x0; dy0 = y - y0; dz0 = z - z0; dx1 = x - x1; dy1 = y - y1; dz1 = z - z1; is_in = dx0*nx012 + dy0*ny012 + dz0*nz012>=0 && dx0*nx013 + dy0*ny013 + dz0*nz013>=0 && dx0*nx023 + dy0*ny023 + dz0*nz023>=0 && dx1*nx123 + dy1*ny123 + dz1*nz123>=0; is_in? i(#-1,0,0,0)*x + i(#-1,0,1,0)*y + i(#-1,0,2,0)*z + i(#-1,0,3,0) :i ) " rm. #@cli t : eq. to 'text'. : (+) #@cli text : text,_x[%|~],_y[%|~],_{ font_height[%]>=0 | custom_font },_opacity,_color1,... : (+) #@cli : Draw specified colored text string on selected images. #@cli : (eq. to 't').\n #@cli : If one of the x or y argument ends with a '~', its value is expected to be \ # a centering ratio (in [0,1]) rather than a position. #@cli : Usual centering ratio are { 0:left-justified | 0.5:centered | 1:right-justified }. #@cli : Sizes '13' and '128' are special and correspond to binary fonts (no-antialiasing). \ # Any other font size is rendered with anti-aliasing. #@cli : Specifying an empty target image resizes it to new dimensions such that the image contains \ # the entire text string. #@cli : A custom font can be specified as a variable name that stores an image list of 256 or 512 items \ # (512 for 256 character sprites + 256 associated opacities), or as an image selection that is a serialized \ # version of such an image list. #@cli : Default values: 'x=y=0.01~', 'font_height=16', 'opacity=1' and 'color1=0'. #@cli : $ image.jpg rescale2d ,600 div 2 y=0 repeat 30 { text {2*$>}" : This is a nice text!",10,$y,{2*$>},0.9,255 \ # y+={2*$>} } #@cli : $ 0 text "G'MIC",0,0,23,1,255 #@cli to : eq. to 'text_outline'. to : skip "${1=}",${2=0.01~},${3=0.01~},${4=7.5%} check "${5=2}>=0 && isnum(${6=1}) && isnum(${7=255}) && "\ "isnum(${8=$7}) && isnum(${9=$7}) && isnum(${10=255})" _text_outline $"*" #@cli text_outline : text,_x[%|~],_y[%|~],{ _font_height[%]>0 | custom_font },_outline>=0,_opacity,_color1,... #@cli : Draw specified colored and outlined text string on selected images. #@cli : If one of the x or y argument ends with a '~', its value is expected to be #@cli : a centering ratio (in [0,1]) rather than a position. #@cli : Usual centering ratio are { 0:left-justified | 0.5:centered | 1:right-justified }. #@cli : Default values: 'x=y=0.01~', 'font_height=7.5%', 'outline=2', 'opacity=1', 'color1=color2=color3=255' \ # and 'color4=255'. #@cli : $ image.jpg text_outline "Hi there!",10,10,63,3 text_outline : skip "${1=}",${2=0.01~},${3=0.01~},${4=7.5%} check "${5=2}>=0 && isnum(${6=1}) && isnum(${7=255}) && "\ "isnum(${8=$7}) && isnum(${9=$7}) && isnum(${10=255})" _text_outline $"*" _text_outline : skip "${1=}" if "isexpr($4)" str="height $4" is_custom_font=0 else str="'$4'" is_custom_font=1 fi e[0--3] "Draw outlined text '$1' at position ($2,$3) on image$?, with font "$str", outline $5, opacity $6 and color ${7--1}." if ['"$1"']==0 return fi sepx,sepy:="sx=['$2']; sy=['$3']; [sx[size(sx)-1], sy[size(sy)-1]]" if !$is_custom_font is_fontpercent:=ispercentage($4) fi xpos={`s=['"$2"'];$sepx==_'~'||$sepx==_'%'?s[0,size(s)-1]:s`} ypos={`s=['"$3"'];$sepy==_'~'||$sepy==_'%'?s[0,size(s)-1]:s`} foreach { 0 if $is_custom_font t. "$1",0,0,$4,1,1 else t. "$1",0,0,{-2,$is_fontpercent?h*$4:$4},1,1 fi expand. xy,{1+$5} +dilate. {2*$5+1} i[-3] (${7--1}) r... {s#0},1,1,1,0,2 y... c r... .,.,1,100% if $5 *[-3,-2] else rm.. fi if w#0 j... ..,{[($sepx==_'~'?(w#0-1-w):$sepx==_'%'?(w#0-1)%:1)*$xpos,\ ($sepy==_'~'?(h#0-1-h):$sepy==_'%'?(h#0-1)%:1)*$ypos]},0,0,$6,. k[0] else k[1] fi } #@cli triangle_shade : x0,y0,x1,y1,x2,y2,R0,G0,B0,...,R1,G1,B1,...,R2,G2,B2,... #@cli : Draw triangle with interpolated colors on selected images. #@cli : $ image.jpg triangle_shade 20,20,400,100,120,200,255,0,0,0,255,0,0,0,255 triangle_shade : e[^-1] "Draw triangle ($1,$2)-($3,$4)-($5,$6) with interpolated colors on image$?." # Find color mapping coefficients for each vertex. l[] { ($1,$2,1;$3,$4,1;$5,$6,1) (${7--1}) r. {w/3},3,1,1,-1 s. x solve[^0] [0] rm[0] a c } # Pre-compute coefs to test point inside triangle. invarea:=(-$4*$5+$2*(-$3+$5)+$1*($2-$6)+$3*$6)^-1 s1:=$2*$5-$1*$6 s2:=$6-$2 s3:=$1-$5 t1:=$1*$4-$2*$3 t2:=$2-$4 t3:=$3-$1 # Begin drawing on each selected image. repeat $!-1 { l[$>,-1] { repeat s#0 { a:=i(0,0,0,$>) b:=i(0,1,0,$>) c:=i(0,2,0,$>) sh[0] $> f. "s = "$invarea"*("$s1" + "$s2"*x + "$s3"*y); t = "$invarea"*("$t1" + "$t2"*x + "$t3"*y); s>=0 && t>=0 && t+s<=1 ? "$a"*x+"$b"*y+"$c":i" rm. } } } rm. #@cli truchet : _scale>0,_radius>=0,_pattern_type={ 0:straight | 1:curved } #@cli : Fill selected images with random truchet patterns. #@cli : Default values: 'scale=32', 'radius=5' and 'pattern_type=1'. #@cli : $ 400,300 truchet , truchet : check "isint(${1=32},1) && ${2=3}>=0" skip ${3=1} e[^-1] "Render "${arg0\ !$3,curved,straight}" truchet patterns in image$?, with scale $1 and radius $2." foreach { nm={n} w,h,s={w},{h},{s} rm $1,$1 = 1,0,0 = 1,100%,100% distance 1,{1+$3} M:=int(iM/2) # Generate truchet pattern and its mirrored version. ir {$M-$2/2-($1%2)},{$M+$2/2} +mirror y a x {round($w/$1,1,1)},{round($h/$1,1,1)} rand. 0,1 >=. 50% r. {w*$1},{h*$1} *. $1 channels. 0,1 (0,{$1-1}) r. $1,$1,1,1,3 +transpose. a[-2,-1] c ri. ..,0,2 +[-2,-1] warp.. . rm. >= 50% r $w,$h,1,1,0 r 100%,100%,1,$s => $nm } #@cli turbulence : _radius>0,_octaves={1,2,3...,12},_alpha>0,_difference={-10,10},_mode={0,1,2,3} #@cli : Render fractal noise or turbulence on selected images. #@cli : Default values: 'radius=32', 'octaves=6', 'alpha=3', 'difference=0' and 'mode=0'. #@cli : $ 400,400,1,3 turbulence 16 #@cli : $$ https://gmic.eu/oldtutorial/_turbulence turbulence : check "${1=32}>0 && ${2=6}>0" skip ${3=3},${4=0},${5=0} e[^-1] "Render fractal noise or turbulence on image$?, with radius $1, octaves $2, damping per octave $3, difference $4 and mode $5." foreach { nm={n} if $4 . fi f. 0 +noise. 10,0 b. $1,0 if isbool($5) -. {ia} abs. elif $5==3||$5==4 ^. 2 elif $5==5 ^. 3 fi repeat $2-1 { +noise.. 10,0 b. {$1/2^$>},0 if !$5 -. {ia} abs. elif $5==4 ^. 2 elif $5==5 ^. 3 fi *.. $3 +[-2--1] } n. 0,255 rm.. if $4 *. $4 mv.. 2 - n. 0,255 fi => $nm } #@cli yinyang #@cli : Draw a yin-yang symbol on selected images. #@cli : $ 400,400 yinyang yinyang : e[^-1] "Draw yin-yang symbol on image$?." f 0 foreach { s:=s channels 0 r:=round(0.95*min(w,h)/4) +line 50%,0,50%,50%,1,2 ellipse. 50%,{h/2-$r},$r,$r,0,1,2 line. 50%,50%,50%,100%,1,1 ellipse. 50%,{h/2+$r},$r,$r,0,1,1 flood. {w/2-$r},50%,0,0,0,1,2 flood. {w/2+$r},50%,0,0,0,1,1 ellipse.. 50%,50%,{2*$r},{2*$r},0,1,1 * ellipse. 50%,{h/2-$r},{$r/3},{$r/3},0,1,1 ellipse. 50%,{h/2+$r},{$r/3},{$r/3},0,1,2 r 100%,100%,1,$s } #--------------------------------- # #@cli :: Matrix Computation # #--------------------------------- #@cli dijkstra : starting_vertex>=0,_ending_vertex={ -1:none | >=0 } #@cli : Compute minimal distances/paths in selected graphs, from specified 'starting_vertex' to all other vertices \ # (opt. only until 'ending_vertex' has been reached). #@cli : A graph of 'N' vertices is specified as a 'NxN' adjacency matrix giving the weights of all edges \ # connecting vertices (set to 'inf' when two vertices are not connected). #@cli : This command return a '1xNx1x2' image containing the '[distance,parent]' information : #@cli : - 'distance' is the minimal distance from vertex '#y' to the 'starting_vertex' (i.e. the sum \ # of edge weights composing the minimal path between these two vertices). #@cli : - 'parent' is the index of the next vertex that must be followed to reaches the 'starting_vertex' \ # through the minimal path. #@cli : Default value: 'ending_vertex=-1' +dijkstra : check "isint($1,0) && isint(${2=-1},-1)" s0,s1=x,ces t0,t1=," to vertex \#$2" e[^-1] "Compute minimal distances/path from vertex \#$1"${t{$2>=0}}", for adjacency matri"${s{$!>1}}" [$[]]." if $2!=-1 str_check_end_vertex="C[1]==$2?break();" fi foreach { nm={n} if !w" || "w!=h" || "d>1" || "s>1 error[0--4] "Command 'dijkstra': Image ["{arg0($>,$[])}"]: '"$nm"', of size ("{[w,h,d,s]}") "\ "is not an adjacency matrix." fi N:=w # Number of nodes 1,$N,1,2,[inf,-1] =. 0,0,$1 # [1]: Resulting image [ dist,parent ] 1,{$N+1},1,2,[inf,y] eval "swap(I[0],I[$1]); i[0] = 0; I[h - 1] = [$N,0]" # [2]: Heap [ dist,index ] eval " while (da_size(), C = da_pop_heap(); "$str_check_end_vertex" repeat (da_size(),q, N = I[q]; edge = i(#0,C[1],N[1]); !isinf(edge)?( # Neighbor found dist = C[0] + edge; dist0, # Update heap parent = (index - 1)>>1; N[0] $nm } mv[1--1:2] $! add_copymark[50%--1] dijkstra : foreach { nm={n} +dijkstra $* k. => $nm } #@cli eigen : (+) #@cli : Compute the eigenvalues and eigenvectors of selected symmetric matrices or matrix fields. #@cli : If one selected image has 3 or 6 channels, it is regarded as a field of 2x2 or 3x3 symmetric matrices, #@cli : whose eigen elements are computed at each point of the field. #@cli : $ (1,0,0;0,2,0;0,0,3) +eigen #@cli : $ image.jpg structuretensors blur 2 eigen split[0] c #@cli : $$ https://gmic.eu/oldtutorial/_eigen #@cli eye : _size>0 #@cli : Insert an identity matrix of given size at the end of the image list. #@cli : $ eye 3 eye 7 eye 10 +eye : check "isint($1,0)" e[^-1] "Input $1x$1 identity matrix." if $1 $1,$1,1,1,x==y else 0 fi #@cli fitsamples : nb_samples>0,_relevant_dimension[%]>0,\ # _average_vector_varname,_dilation_vector_varname,_orientation_matrix_varname #@cli : Generate 'nb_samples' vectors having the same multivariate gaussian distribution as the vectors of the \ # selected images. #@cli : Each input represents a set of 'M' vectors of dimension 'N' (with M>1) (specified as an image with size \ # 'MxNx1x1', 'Mx1xNx1', 'Mx1x1xN', '1xMxNx1', '1xMx1xN' or '1x1xMxN'). #@cli : The command returns a new set of random vectors with similar geometry. #@cli : Default values: 'relevant_dimension=100%', and \ # 'average_vector_varname=orientation_matrix_varname=dilation_matrix_varname=(undefined)'. fitsamples : check "isint($1,1) && ${2=100%}>0" skip "${3=},${4=},${5=}" e[^-1] "Generate $1 new vectors for input vector set$? with $2 relevant dimensions." fwd0,fwd1,fwd2,fwd3,fwd4,fwd5=xzcy,xycz,,yxcz,yxzc,zxyc bwd0,bwd1,bwd2,bwd3,bwd4,bwd5=xcyz,xycz,,yxcz,yxzc,yzxc foreach { nm={n} # Determine input format: 'MxNx1x1', 'Mx1xNx1', 'Mx1x1xN', '1xMxNx1', '1xMx1xN' or '1x1xMxN'. M,N,type:=w>1?[w,h>1?[h,0]:d>1?[d,1]:[s,2]]:h>1?[h,d>1?[d,3]:[s,4]]:d>1?[d,s>1?[s,5]:[-1,-1]] if $M*$N!=whds error[0--4] "Command 'fitsamples': Selected image ("{[w,h,d,s]}") does not represent a set of vectors." fi permute ${fwd$type} # Force it to be a 'Mx1x1xN' matrix. # Compute average vector. if !narg("$3") +l { s x + / $M } else $$3 fi => avg # Compute dilation vector and orientation matrix. if !narg("$4")" || "!narg("$5") $N,$N eval... "* begin(avg = crop(#$avg); C = vector(#$N^2,0)); mI = I - avg; C+=mul(mI,mI,s); end(merge(C,+); C/=$M; draw(#-1,C,0,0,0,0,$N,$N,1,1))" P:=round(ispercentage($2)?$N*$2:$2) poweriteration. $P sqrt.. else $$4 $$5 P:=w fi => dilation,orientation # Generate new random samples with same gaussian distribution. $1,1,1,$N,"* begin(avg = crop(#$avg); L = crop(#$dilation); R = crop(#$orientation)); U = vector(#$P); fill(U,k,g*L[k]); mul(R,U)+=avg" k. permute ${bwd$type} => $nm } #@cli invert : _use_LU={ 0:SVD | 1:LU },_lambda>=0 : (+) #@cli : Inverse selected matrices (or compute Moore-Penrose pseudoinverse for non-square matrices). #@cli : SVD solver is slower but more precise than LU. #@cli : 'lambda' is used only in the Moore-Penrose pseudoinverse, by estimating A^t.(A^t.A + lambda.Id)^-1. #@cli : Default value: 'use_LU=0' and 'lambda=0'. #@cli : $ (0,1,0;0,0,1;1,0,0) +invert #@cli meigen : m>=1 #@cli : Compute an approximation of the 'm' largest eigenvalues and eigenvectors of selected symmetric matrices, #@cli : using the Arnoldi iteration method (https://en.wikipedia.org/wiki/Arnoldi_iteration). #@cli : A larger 'm' goes with better numerical precision. #@cli : $ (1,0,0;0,2,0;0,0,3) +meigen 3 meigen : check "isint($1,1)" if $!!=1 s="ce" else s="x" fi e[^-1] "Compute $1 largest eigen-values of matri"$s"$?." foreach { nm={n} if w!=h" || "d!=1" || "s!=1 v 1 error[0--5] "Command 'meigen': Image '"$nm"' is not a square matrix." fi eval ${-math_lib}" store('val',meig(crop(),$1,h),1,min($1,h))" $val k. => $nm } #@cli mproj : [dictionary],_method,_max_iter={ 0:auto | >0 },_max_residual>=0 : (+) #@cli : Find best matching projection of selected matrices onto the span of an over-complete #@cli : dictionary D, using the orthogonal projection or Matching Pursuit algorithm. #@cli : Selected images are 2D-matrices in which each column represent a signal to project. #@cli : '[dictionary]' is a matrix in which each column is an element of the dictionary D. #@cli : 'method' tells what projection algorithm must be applied. It can be: #@cli : \ - 0 = orthogonal projection (least-squares solution using LU-based solver). #@cli : \ - 1 = matching pursuit. #@cli : \ - 2 = matching pursuit, with a single orthogonal projection step at the end. #@cli : \ - >=3 = orthogonal matching pursuit where an orthogonal projection step is performed #@cli : \ every 'method-2' iterations. #@cli : 'max_iter' sets the max number of iterations processed for each signal. #@cli : If set to '0' (default), 'max_iter' is equal to the number of columns in D. #@cli : (only meaningful for matching pursuit and its variants). #@cli : 'max_residual' gives a stopping criterion on signal reconstruction accuracy. #@cli : (only meaningful for matching pursuit and its variants). #@cli : For each selected image, the result is returned as a matrix W #@cli : whose columns correspond to the weights associated to each column of D, #@cli : such that the matrix product D*W is an approximation of the input matrix. #@cli : Default values: 'method=0', 'max_iter=0' and 'max_residual=1e-6'. #@cli orthogonalize : _mode = { 0:orthogonalize | 1:orthonormalize } #@cli : Orthogonalize or orthonormalize selected matrices, using Modified Gram-Schmidt process. #@cli : Default value: 'mode=0'. orthogonalize : if isbool($1) mode=$1 else mode=0 noarg fi u0,u1,v0,v1=Orthogonalize,Orthonormalize,x,ce e[^-1] ${u$mode}" matri"${v{$!!=1}}"$?, using Modified Gram-Schmidt process." foreach { eval "> proj(u,v) = (dot(u,v)/dot(u,u)*u); for (p = 1, p0,_epsilon>0,_max_iter>0 #@cli : Compute the 'nb_eigenvectors' largest eigenvectors of the selected symmetric matrices, #@cli : using the power iteration algorithm. #@cli : Default values: 'nb_eigenvectors=1', 'epsilon=1e-5' and 'max_iter=100'. poweriteration : check "isint(${1=1},1) && ${2=1e-6}>0 && isint(${3=1000},1)" s0,s1=,s if $!>1 c=ces else c=x fi e[^-1] "Compute the $1 first eigenvector"${s{$1>1}}" of matri"$c" [$[]] with epsilon $2." foreach { nm={n} if !w rm 0 0 => $nm[val] $nm[vec] continue fi if w!=h" || "d>1" || "s>1 error[0--3] "Command '$0': Image "[{[$[]][$>]}]" ("{[w,h,d,s]}") is not a square matrix." fi 1,$1 $1,.. eval " const N = w#0; A = crop(#0); b = expr('u(-1,1)',N); b/=norm(b); repeat ($1,k, p_lambda = inf; repeat ($3, c = mul(A,b); lambda = norm(c); c/=lambda; delta = abs(lambda - p_lambda); b = c; p_lambda = lambda; delta<$2?break(); ); lambda = mul(b,mul(A,b,1))[0]; # Get eigenvalue with correct sign i[#-2,k] = lambda; draw(#-1,b,k,1); A-=(mul(b,b,N)*=lambda); )" rm[0] } #@cli solve : [image],_use_LU={ 0:SVD | 1:LU } : (+) #@cli : Solve linear system AX = B for selected B-matrices and specified A-matrix. #@cli : If the system is under- or over-determined, the least squares solution is returned. #@cli : Default value: 'use_LU=0'. #@cli : $ (0,1,0;1,0,0;0,0,1) (1;2;3) +solve[-1] [-2] #@cli svd : (+) #@cli : Compute SVD decomposition of selected matrices. #@cli : $ 10,10,1,1,'x==y?x+u(-0.2,0.2):0' +svd #@cli transpose #@cli : Transpose selected matrices. #@cli : $ image.jpg +transpose transpose : e[^-1] "Transpose image$?." permute yxzc #@cli trisolve : [image] : (+) #@cli : Solve tridiagonal system AX = B for selected B-vectors and specified tridiagonal A-matrix. #@cli : Tridiagonal matrix must be stored as a 3 column vector, where 2nd column contains the #@cli : diagonal coefficients, while 1st and 3rd columns contain the left and right coefficients. #@cli : $ (0,0,1;1,0,0;0,1,0) (1;2;3) +trisolve[-1] [-2] #--------------------------------- # #@cli :: 3D Meshes # #--------------------------------- #@cli +3d : eq. to 'add3d'. : (+) #@cli add3d : tx,_ty,_tz : [object3d] : (no arg) : (+) #@cli : Shift selected 3D objects with specified displacement vector, or merge them with specified #@cli : 3D object, or merge all selected 3D objects together. #@cli : (eq. to '+3d'). #@cli : Default values: 'ty=tz=0'. #@cli : $ sphere3d 10 repeat 5 { +add3d[-1] 10,{u(-10,10)},0 color3d[-1] ${-rgb} } add3d #@cli : $ repeat 20 { torus3d 15,2 color3d[-1] ${-rgb} mul3d[-1] 0.5,1 if $>%2 rotate3d[-1] 0,1,0,90 fi add3d[-1] 70 \ # add3d rotate3d[-1] 0,0,1,18 } double3d 0 #@cli animate3d : nb_frames>0,_step_angle_x,_step_angle_y,_step_angle_z,_zoom_factor,0<=_fake_shadow_level<=100,\ # _[background] #@cli : Generate 3D animation frames of rotating 3D objects. #@cli : Frames are stacked along the z-axis (volumetric image). #@cli : Frame size is the same as the size of the '[background]' image (or 800x800 if no background specified). #@cli : Default values: 'step_angle_x=0', 'step_angle_y=5', 'step_angle_z=0', 'zoom_factor=1', 'fake_shadow_level=50' \ # and 'background=(undefined)'. animate3d : check "isint($1,1) && isnum(${2=0}) && isnum(${3=5}) && isnum(${4=0}) && ${5=1}>0 && "\ "inrange(${6=50},0,100)" skip "${7=}" e[^-1] "Generate 3D animation frames from 3D object$?, with $1 frames, angle steps (${2-4}), zoom factor $5 "\ "and $6% fake shadow." if ${"is_image_arg $7"} pass$7 else 3,2,1,1,"32,32,64,64,116,96" permute. cyzx r. 800,800,1,3,3 round. fi => anim3d_bg repeat {$!-1} { l[$>,-1] { check ${-is_mesh3d..} nm={0,n} bn={0,b} e " * Object '"$bn"': 0/$1" c3d[0] n3d[0] *3d[0] {1,$5*min(w,h)/2} repeat $1 { e "\r * Object '"$bn"': "{1+$>}"/$1" +r3d[0] 0,0,1,{$>*$4} r3d. 0,1,0,{$>*$3} r3d. 1,0,0,{$>*$2} {1,[w,h,d,4]},-1 j3d. ..,50%,50%,0,1 sh. 0,{s-2} +l.. { s c max } !=. -1 mul.. . mul. 255 j... .,0,0,0,{-2,s} rm[-2,-1] if $6 sh. 100% +b. 2% shift. {m=min(w,h)*2%;[m,m]} -. {255-$6*255%} c. 0,255 max[-2,-1] rm. fi # Add fake shadow +blend[1,-1] alpha rm[-3,-2] if {*} w. -1,-1,0 fi } a[2--1] z rv[0,-1] rm. } =>[$>] $nm } rm. #@cli apply_camera3d : pos_x,pos_y,pos_z,target_x,target_y,target_z,up_x,up_y,up_z #@cli : Apply 3D camera matrix to selected 3D objects. #@cli : Default values: 'target_x=0', 'target_y=0', 'target_z=0', 'up_x=0', 'up_y=-1' and 'up_z=0'. apply_camera3d : skip ${4=0},${5=0},${6=0},${7=0},${8=-1},${9=0} e[^-1] "Apply 3D camera matrix to 3D object$?, with camera position ($1,$2,$3), target position ($4,$5,$6) and up-vector ($7,$8,$9)." ({$4-$1}^{$5-$2}^{$6-$3}) # f. ($7^$8^$9) # up. orientation[-2,-1] # f/|f| and up/|up|. _cross3d {-2,^},{^} # s = f x up _cross3d {^},{-3,^} # u = s x f rm... y[-3--1] x mv[-2,-1] -3 a[-3--1] y z. 0,3 # Rotation matrix R. -3d[^-1] $1,$2,$3 pose3d[^-1] {^} rm. -3d 0,0,800 _cross3d : ({$2*$6-$3*$5}^{$3*$4-$1*$6}^{$1*$5-$2*$4}) orientation. y. #@cli apply_matrix3d : a11,a12,a13,...,a31,a32,a33 #@cli : Apply specified 3D rotation matrix to selected 3D objects. #@cli : $ torus3d 10,1 +apply_matrix3d {mul(rot(1,0,1,-15°),[1,0,0,0,2,0,0,0,8],3)} double3d 0 apply_matrix3d : e[^-1] "Apply 3x3 matrix (${1-3};${4-6};${7-9}) to 3D object$?." foreach { nbp:=i[6] sh 8,{8+3*$nbp-1},0,0 r. 3,$nbp,1,1,-1 3,3,1,1,$* transpose. m*[-2,-1] rm. } #@cli array3d : size_x>=1,_size_y>=1,_size_z>=1,_offset_x[%],_offset_y[%],_offset_y[%] #@cli : Duplicate a 3D object along the X,Y and Z axes. #@cli : Default values: 'size_y=1', 'size_z=1' and 'offset_x=offset_y=offset_z=100%'. #@cli : $ torus3d 10,1 +array3d 5,5,5,110%,110%,300% array3d : check "isint($1,1) && isint(${2=1},1) && isint(${3=1},1)" skip ${4=100%},${5=100%},${6=100%} e[^-1] "Duplicate 3D object$? along X,Y,Z axes with factors ($1,$2,$3) and offsets ($4,$5,$6)." foreach { # Retrieve object dimensions. +rows 8,{8+3*i[6]} r. 3,{h/3},1,1,-1 s. x,3 dx={-3,ispercentage($4)?$4*(iM-im):$4} dy={-2,ispercentage($5)?$5*(iM-im):$5} dz={-1,ispercentage($6)?$6*(iM-im):$6} rm[-3--1] # Duplicate along X. off=0 repeat int(log2($1)) { ++3d. {2^$>*$dx} +3d. .. if !($1&(2^$>)) rm.. else +3d.. $off off+=2^$>*$dx fi } +3d. $off +3d # Duplicate along Y. off=0 repeat int(log2($2)) { ++3d. 0,{2^$>*$dy} +3d. .. if !($2&(2^$>)) rm.. else +3d.. 0,$off off+=2^$>*$dy fi } +3d. 0,$off +3d # Duplicate along Z. off=0 repeat int(log2($3)) { ++3d. 0,0,{2^$>*$dz} +3d. .. if !($3&(2^$>)) rm.. else +3d.. 0,0,$off off+=2^$>*$dz fi } +3d. 0,0,$off +3d } #@cli arrow3d : x0,y0,z0,x1,y1,z1,_radius[%]>=0,_head_length[%]>=0,_head_radius[%]>=0 #@cli : Input 3D arrow with specified starting and ending 3D points. #@cli : Default values: 'radius=5%', 'head_length=25%' and 'head_radius=15%'. #@cli : $ repeat 10 { a:=$>*2*pi/10 arrow3d 0,0,0,{cos($a)},{sin($a)},-0.5 } +3d +arrow3d : check "${7=5%}>=0 && ${8=25%}>=0 && ${9=15%}>=0" e[^-1] "Input 3D arrow, from (${1-3}) to (${4-6}), with radius $7, head length $8 and head radius $9." # Create 3D object. L:=sqrt(($4-$1)^2+($5-$2)^2+($6-$3)^2) R:=ispercentage($7)?$7*$L:$7 l:=ispercentage($8)?$8*$L:$8 r:=ispercentage($9)?$9*$L:$9 L-=$l cylinder3d $R,$L cone3d $r,$l +3d. 0,0,$L +3d[-2,-1] # Compute rotation matrix for arrow orientation. ({$4-$1}^{$5-$2}^{$6-$3}) (0.01^-0.02^0.03) orientation[-2,-1] _cross3d {-2,^},{^} _cross3d {^},{-3,^} rm... y[-3--1] x mv[-2,-1] -3 a[-3--1] y # Rotate and translate the arrow at specified coordinates. s3d.. r[-5] 3,{-5,h/3},1,1,-1 m*[-5,-1] y[-4] a[-6--1] y +3d. ${1-3} rv3d. #@cli axes3d : _size_x,_size_y,_size_z,_font_size>0,_label_x,_label_y,_label_z,_is_origin={ 0:no | 1:yes } #@cli : Input 3D axes with specified sizes along the x,y and z orientations. #@cli : Default values: 'size_x=size_y=size_z=1', 'font_size=23', 'label_x=X', 'label_y=Y', 'label_z=Z' and \ # 'is_origin=1' #@cli : $ axes3d , +axes3d : check "${4=23}>0 && isbool(${8=1})" skip ${1=1},${2=$1},${3=$2},"${5=X},${6=Y},${7=Z}" e[^-1] "Input 3D axes with sizes ($1,$2,$3)." l[] { m:=max(abs($1),abs($2),abs($3))/40 m2:=2*$m m3:=1.2*$m2 if $1 line3d 0,0,0,$1,0,0 fi if $2 line3d 0,0,0,0,$2,0 fi if $3 line3d 0,0,0,0,0,$3 fi if $1 cone3d $m,{2*$m},16 r3d. 0,1,0,90 +3d. {$1-$m2},0,0 _axes3d "$5",$4 +3d. {$1+$m3},0,0 fi if $2 cone3d $m,{2*$m},16 r3d. 1,0,0,-90 +3d. 0,{$2-$m2},0 _axes3d "$6",$4 +3d. 0,{$2+$m3},0 fi if $3 cone3d $m,{2*$m},16 +3d. 0,0,{$3-$m2} _axes3d "$7",$4 +3d. 0,0,{$3+$m3} fi if $8 _axes3d "O",$4 -3d. $m3,$m3,$m3 fi +3d => [3d\ axes] } _axes3d : 0 t. "$1",2,0,$2,1,1 +dilate. 3 *.. 255 r.. 100%,100%,1,3 i... (67.5;73.5;109.5;103.5;51.5;100.5;1;1;0;0;0;1;0;-128;{w};{h};3) i.. (-128;{w};{h};1) y[-3,-1] a[-4--1] y #@cli boundingbox3d #@cli : Replace selected 3D objects by their 3D bounding boxes. #@cli : $ torus3d 100,30 +boundingbox3d +3d[-1] [-2] boundingbox3d : e[^-1] "Replace 3D object$? by their 3D bounding boxes." foreach { s3d nbv,nbp={1,f2ui([i[0],i[1]])} k[2,3] => pts,prims r[pts] 3,{pts,h/3},1,1,-1 permute[pts] zycx ($nbv) a[pts,-1] y # Convert vertices to dynamic array $nbp eval. ">begin(p = 0); # Manage 3D spheres by adding virtual vertices N = i[#$prims,p++]; N==5?( i0 = i[#$prims,p++]; i1 = i[#$prims,p++]; p+=3; P0 = I[#$pts,i0]; P1 = I[#$pts,i1]; Pc = (P0 + P1)/2; U = (P1 - P0)/2; V = rot(-U[1],U[0],U[2],pi/2)*U; W = cross(U,V)/norm(U); da_push(#$pts,Pc + V,Pc - V,Pc + W,Pc - W); ):(p+=N); end(da_freeze(#$pts))" rm[prims,-1] s c xm,dx={0,[im,iM-im]} ym,dy={1,[im,iM-im]} zm,dz={2,[im,iM-im]} rm box3d $dx,$dy,$dz +3d $xm,$ym,$zm p3d 1 } #@cli box3d : _size_x,_size_y,_size_z #@cli : Input 3D box at (0,0,0), with specified geometry. #@cli : Default values: 'size_x=1' and 'size_z=size_y=size_x'. #@cli : $ box3d 100,40,30 +primitives3d 1 color3d[-2] ${-rgb} +box3d : skip ${1=1},${2=$1},${3=$2} e[^-1] "Input 3D box, with size ($1,$2,$3)." 1,86,1,1,\ 67.5,73.5,109.5,103.5,51.5,100.5,8,6,\ 0,0,0,$1,0,0,$1,$2,0,0,$2,0,\ 0,0,$3,$1,0,$3,$1,$2,$3,0,$2,$3,\ 4,0,3,2,1,4,4,5,6,7,4,0,1,5,4,4,3,7,6,2,4,0,4,7,3,4,1,2,6,5,\ 200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,200,\ 1,1,1,1,1,1 => [3D\ box] #@cli c3d : eq. to 'center3d'. c3d : _center3d #@cli center3d #@cli : Center selected 3D objects at (0,0,0). #@cli : (eq. to 'c3d'). #@cli : $ repeat 100 { circle3d {u(100)},{u(100)},{u(100)},2 } add3d color3d[-1] 255,0,0 +center3d \ # color3d[-1] 0,255,0 add3d center3d : _$0 _center3d : e[0--3] "Center 3D object$?." check3d foreach { if i[6] s3d r[2] 3,{2,h/3},1,1,-1 s[2] x -[2] {2,(iM+im)/2} -[3] {3,(iM+im)/2} -[4] {4,(iM+im)/2} a[2-4] x y[2] a y fi } #@cli chainring3d : _nb_links>=3,_x_scale>0,_y_scale>0,_z_scale>0 #@cli : Input 3D chain ring with specified geometry. #@cli : 'nb_links' should be preferably even. #@cli : Default values: 'nb_links=16', 'x_scale=0.5', 'y_scale=1' and 'z_scale=1'. #@cli : $ chainring3d chainring3d : check "isint(${1=16},3) && ${2=0.5}>0 && ${3=1}>0 && ${4=1}>0" e[^-1] "Input 3D chain ring, with $1 links and scales (${2-4})." l[] { torus3d 1,0.1 *3d ${2-4} shift:=0.75*$1/pi repeat $1 { ang:=$>*360/$1 ($ang^1^0.784) hsv2rgb. +col3d[0] {^} rm.. if $>%2 r3d. 0,1,0,90 fi +3d. $shift r3d. 0,0,1,$ang } rm[0] +3d /3d $shift => [3D\ chainring] } #@cli circle3d : _x0,_y0,_z0,_radius>=0 #@cli : Input 3D circle at specified coordinates. #@cli : Default values: 'x0=y0=z0=0' and 'radius=1'. #@cli : $ repeat 500 { a:=$>*pi/250 circle3d {cos(3*$a)},{sin(2*$a)},0,{$a/50} color3d[-1] ${-rgb},0.4 } add3d +circle3d : skip ${1=0},${2=0},${3=0},${4=1} e[^-1] "Input 3D circle at position ($1,$2,$3) with radius $4." r:=$4/sqrt(3) 1,24,1,1,\ 67.5,73.5,109.5,103.5,51.5,100.5,2,1,\ {$1-$r},{$2-$r},{$3-$r},\ {$1+$r},{$2+$r},{$3+$r},\ 5,0,1,0,0,0,200,200,200,1 => [3D\ circle] #@cli circles3d : _radius>=0,_is_outlined={ 0 | 1 } #@cli : Convert specified 3D objects to sets of 3D circles with specified radius. #@cli : Default values: 'radius=1' and 'is_outlined=1'. #@cli : $ image.jpg luminance rescale2d ,40 threshold 50% * 255 pointcloud3d color3d[-1] 255,255,255 circles3d 0.7 circles3d : check "${1=1}>=0 && isbool(${2=0})" e[^-1] "Convert 3D object$? to sets of 3D "${arg0\ $2,filled,outlined}" circles with radius $1." p3d 0 foreach { -3d {$1/2},0,0 ++3d $1,0,0 +3d[1] [0] s3d # Two 3D objects decomposed here! rows[7] 0 j[1] [7] # Number of points rv[2,8] # Points rv[3,9] l[3] { r 2,{h/2},1,1,-1 z 1,1 s y,2 i[0] 1,100%,1,1,5 1,100%,1,1,$2 2,100% a x y } # Primitives k[0-5] a y } #@cli col3d : eq. to 'color3d'. col3d : _gmic_s="$?" v + _color3d $* #@cli color3d : R,_G,_B,_opacity #@cli : Set color (and optionally opacity) of selected 3D objects. #@cli : (eq. to 'col3d'). #@cli : Default value: 'B=G=R' and 'opacity=(undefined)'. #@cli : $ torus3d 100,10 double3d 0 repeat 7 { +rotate3d[-1] 1,0,0,20 color3d[-1] ${-rgb} } add3d color3d : _gmic_s="$?" v + _$0 $* _color3d : $=arg col:=$#==1?[$arg1,$arg1,$arg1]:$#==2?[$arg1,$arg2,0]:[$arg1,$arg2,$arg3] if $#<4 e[0--4] "Set color of 3D object"$_gmic_s" to ("$col")." else e[0--4] "Set color of 3D object"$_gmic_s" to ("$col"), with opacity "$arg4"." fi foreach { nbv,nbp:=f2ui([i[6],i[7]]) $nbp eval. ">begin(p = 8 + 3*$nbv); p+=i[#0,p] + 1; end(set('p',p))" # Find beginning of color data eval. ">begin(p = $p; col = ["$col"]); i[#0,p]==-128?(p+=prod(crop(#0,0,++p,1,3))):copy(i[#0,p],col); p+=3; end(set('p',p))" if $#>3 eval. ">begin(p = $p; const o = $arg4); i[#0,p]==-128?(p+=3 + prod(crop(#0,0,++p,1,3))):(i[#0,p++] = o)" fi k[0] } #@cli colorcube3d : _is_wireframe={ 0 | 1 } #@cli : Input 3D color cube. #@cli : Default value: 'is_wireframe=0'. #@cli : $ colorcube3d mode3d 2 +primitives3d 1 +colorcube3d : l[] { check "isbool(${1=0})" is_wireframe=$1 onfail is_wireframe=1 noarg } s0,s1="face","wireframe" e[^-1] "Input 3D RGB-color "$s$1" cube." if $is_wireframe l[] { # Wireframe version ({'CImg3d'},8,12) # Header (0,0,0;255,0,0;255,255,0;0,255,0;0,0,255;255,0,255;255,255,255;0,255,255) # Vertices (6,0,1,0,0,63,0;6,1,2,0,1,63,1;6,2,3,0,2,63,2;6,3,0,0,3,63,3;\ # Primitives 6,0,4,0,4,63,4;6,1,5,0,5,63,5;6,2,6,0,6,63,6;6,3,7,0,7,63,7;\ 6,4,5,0,8,63,8;6,5,6,0,9,63,9;6,6,7,0,10,63,10;6,7,4,0,11,63,11) (-128,64,12,3) (0,0,0;255,0,0/255,0,0;255,255,0/255,255,0;0,255,0/0,255,0;0,0,0/\ # First texture 0,0,0;0,0,255/255,0,0;255,0,255/255,255,0;255,255,255/0,255,0;0,255,255/\ 0,0,255;255,0,255/255,0,255;255,255,255/255,255,255;0,255,255/0,255,255;0,0,255) permute. yzcx r. 64,100%,1,3,3 (-128,0,0,0)x11 # 11 other shared textures 1,12,1,1,1 # Opacities y a y } else l[] { # Face version ({'CImg3d'},8,6) # Header (0,0,0;255,0,0;255,255,0;0,255,0;0,0,255;255,0,255;255,255,255;0,255,255) # Vertices (12,0,3,2,1,0,0,0,63,63,63,63,0;12,1,2,6,5,0,0,0,63,63,63,63,0;\ # Primitives 12,0,4,7,3,0,0,63,0,63,63,0,63;12,4,5,6,7,0,0,63,0,63,63,0,63;\ 12,0,1,5,4,0,0,63,0,63,63,0,63;12,3,7,6,2,0,0,0,63,63,63,63,0) (0,255;0,255^0,0;255,255^0,0;0,0) # Generate 6 textures (255,255;255,255^0,0;255,255^0,255;0,255) (0,0;0,0^0,0;255,255^0,255;0,255) (0,255;0,255^0,0;255,255^255,255;255,255) (0,255;0,255^0,0;0,0^0,0;255,255) (0,255;0,255^255,255;255,255^0,0;255,255) r[-6--1] 64,64,1,3,3 round[-6--1] y[-6--1] i[-7--2] (-128;64;64;3) 1,6,1,1,1 # Opacities y a y } fi => [3D\ colorcube] #@cli colorize3d : _color_function,_passed_images_for_color_function #@cli : Colorize primitives of selected 3D objects, according to a specified function. #@cli : - 'color_function' returns a G,GA,RGB or RGBA vector that can depend on variables 'x','y' and 'z', which are \ # defined as the barycenter coordinates for each primitive. #@cli : - 'passed_images_for_color_function' can be specified as a selection (e.g. '[0,2]') of images that will be \ # inserted at the end of the image list while modifying 3D objects, so that the 'color_function' can have access to \ # their content. #@cli : Default values: 'color_function=[x,y,z]' and 'passed_images_for_color_function='. #@cli : $ torus3d 100,40,640,100 c3d n3d mul3d 256 +3d 128,128,128 sample colorful,257 colorize3d[0] "I(#-1,x,y,0)",[1] colorize3d : skip "${1=[x,y,z]},${2=}" e[^-1] "Colorize 3D objects$?, according to color function '$1'." check3d 0 p3d 2 N=$! if narg($2) is_passed=1 pass$2 1 fi foreach[0-{$N-1}] { s3d nbp={1,@1} if $is_passed pass[$N--1] 1 fi eval " begin( fn() = ($1); x = y = z = 0; col = fn(); P = [ 0,0,0 ]; ); for (p = off = 0, off=3); size(col)==2 || size(col)>3?(i[#5,p] = col[size(col)-1]/255); )" k[0-5] a y } if $is_passed rm[$N--1] fi #@cli cone3d : _radius,_height,_nb_subdivisions>0 #@cli : Input 3D cone at (0,0,0), with specified geometry. #@cli : Default value: 'radius=1','height=1' and 'nb_subdivisions=24'. #@cli : $ cone3d 10,40 +primitives3d 1 color3d[-2] ${-rgb} +cone3d : check ${3=24}>0 skip ${1=1},${2=1} e[^-1] "Input 3D cone, with radius $1, height $2 and $3 subdivisions." # Header. (67.5;73.5;109.5;103.5;51.5;100.5) ({$3+2};{2*$3}) # Vertices. (0,0,0;0,0,$2) (0;{2*pi}) r. 1,{$3+1},1,1,3 rows. 0,{$3-1} +sin. cos.. *[-2,-1] $1 a[-2,-1] x z. 0,2 a[-2,-1] y # Primitives. 1,$3,1,1,'y' +shift. 0,-1 +[-2,-1] 2 2,$3,1,1,3,0 .. [-4] a[-3--1] x i[-4] 2,$3,1,1,3,1 a[-4--2] x a[-2,-1] y # Colors / opacities. 3,{h},1,1,200 1,{h},1,1,1 y[-4--2] a[-6--1] y => [3D\ cone] #@cli cubes3d : _size>=0 #@cli : Convert specified 3D objects to sets of 3D cubes with specified size. #@cli : Default value: 'size=1'. #@cli : $ image.jpg luminance rescale2d ,40 threshold 50% * 255 pointcloud3d color3d[-1] 255,255,255 cubes3d 1 cubes3d : check ${1=1}>=0 e[^-1] "Convert 3D object$? to sets of 3D cubes with size $1." p3d 0 foreach { nbv={@6} nbp={@7} if $nbv&&$nbp s3d l[1] { = {8*i[0]} = {6*i[1]},0,1 } # Header l[2] { # Vertices r 3,{h/3},1,1,-1 half:=$1/2 - '$half,0,0' ++ '$1,0,0' a x - '0,$half,0' ++ '0,$1,0' a x - '0,0,$half' ++ '0,0,$1' a x } l[3] { # Primitives r 2,{h/2},1,1,-1 z 1,1 * 8 r 4,100% i[0] 1,100%,1,1,4 a x [-1]x5 a x + '"0,0,2,3,1, 0,4,5,7,6, 0,0,1,5,4, 0,2,6,7,3, 0,0,4,6,2, 0,1,3,7,5"' } l[4] { r 3,{h/3},1,1,-1 r 18,100%,1,1,0,2 } r[5] 6,100% # Colors & opacities. y a y fi } #@cli cup3d : _resolution>0 #@cli : Input 3D cup object. #@cli : Default value: 'resolution=128'. #@cli : $ cup3d , +cup3d : check ${1=128}>0 e[^-1] "Input 3D cup, with resolution $1." 100,200 ellipse. 0%,0%,40%,40%,0,1,1 ellipse. 0,0,35%,35%,0,1,0 polygon. 4,0,45%,8%,45%,20%,90%,0,90%,1,1 ellipse. 0%,100%,30%,10%,0,1,1 b. 0.1% lathe3d. $1,2 => [3D\ cup] #@cli curve3d : _"x(t)",_"y(t)",_"z(t)",_"r(t)",_resolution>1,_tmin,_tmax,_nb_sides>=0,_is_closed_curve={ 0 | 1 } #@cli : Input 3D curve with specified parameterization. #@cli : If 'r(t)==0' or 'nb_sides<3', the generated 3D object is composed of segments only. #@cli : Default values: 'x(t)=cos(2*pi*t)', 'y(t)=sin(2*pi*t)', 'z(t)=t', 'r(t)=0.025', 'resolution=128', \ # 'tmin=0', 'tmax=1', 'nb_sides=16' and 'is_closed_curve=0'. #@cli : $ curve3d , +curve3d : check "isint(${5=128},2) && isbool(${9=0}) && isint(${8=16},0)" skip "${1=0.25*cos(4*pi*t)},${2=0.25*sin(4*pi*t)},${3=t},${4=0.025},${6=0},${7=1}" xt,yt,zt,rt,resolution,tmin,tmax,nb_sides,is_closed=$"*" nb_sides:="$nb_sides<3 || ['"$rt"']=='0'?1:$nb_sides" s0,s1="open","closed" e[^-1] "Input 3D "${s$is_closed}" curve with parameterization ("$xt,$yt,$zt"), radius '"$rt"', "\ "resolution "$resolution", t-range ["$tmin,$tmax"], radius and "$nb_sides" sides." ('CImg3d') (0,0) 1,$resolution,1,4,"t = lerp($tmin,$tmax,y/(h - !$is_closed)); [ ("$xt"),("$yt"),("$zt"),("$rt") ]" s. c,-3 if $nb_sides==1 # Curve made of segments rm. 1,{$resolution-1+$is_closed},1,3,"[ 2,y,(y + 1)%$resolution ]" nbv=$resolution nbp:=h permute[-2,-1] cyzx else # Curve made of quadrangles +g.. y,0,{$is_closed?2:1} orientation. # Unit tangent vector 0 $nb_sides,$resolution,1,1," !x?( P = I(#-4,0,y); R = i(#-3,0,y); W = I(#-2,0,y); !norm(W)?(W = [ 0,0,1 ]); !y?( P0 = P; do (ref = unitnorm([ u([-1,-1,-1],[1,1,1]) ]); crossWref = cross(W,ref), norm(crossWref)<1e-5); ); U = cross(W,ref); U/=norm(U); V = cross(W,U); V/=norm(V); M = transpose([ U,V,W ],3); ref = -V; ); ang = x*2*pi/w; Q = P + R*mul(M,[ cos(ang),sin(ang),0 ]); da_push(Q[0],Q[1],Q[2]); end( !$is_closed?da_push(P0[0],P0[1],P0[2],P[0],P[1],P[2]); da_freeze(); )" rm[-5--3,-1] nbv:=h/3 0 $nb_sides,{$resolution-1+$is_closed},1,1,"begin(nbp = 0); b0 = y*$nb_sides; b1 = ((y + 1)%$resolution)*$nb_sides; nx = (x+1)%$nb_sides; da_push(4,b0 + x,b0 + nx,b1 + nx,b1 + x); ++nbp; end( const indmin = $nb_sides*$resolution; const indmax = indmin + 1; const last = indmin - $nb_sides; !$is_closed?( repeat($nb_sides,k,da_push(3,indmin,(k + 1)%$nb_sides,k); ++nbp); repeat($nb_sides,k,da_push(3,indmax,last + k,last + ((k + 1)%$nb_sides)); ++nbp); ); da_freeze(); set('nbp',nbp); )" rm. fi eval "i[#-3,0] = ui2f($nbv); i[#-3,1] = ui2f($nbp)" 3,$nbp,1,1,200 1,$nbp,1,1,1 y[-6--1] a[-6--1] y => [3D\ Curve] #@cli cylinder3d : _radius,_height,_nb_subdivisions>0 #@cli : Input 3D cylinder at (0,0,0), with specified geometry. #@cli : Default value: 'radius=1','height=1' and 'nb_subdivisions=24'. #@cli : $ cylinder3d 10,40 +primitives3d 1 color3d[-2] ${-rgb} +cylinder3d : check ${3=24}>0 skip ${1=1},${2=1} e[^-1] "Input 3D cylinder, with radius $1, height $2 and $3 subdivisions." l[] { N:=round($3) nbv,nbp:=2*$N+2,3*$N ({0.5+[{'CImg3d'}]}) ($nbv,$nbp) 1,$nbv,1,3,"Z = (y<="$N"?0:$2); ang = ((y%("$N"+1))-1)*2*pi/"$N"; !(y%("$N"+1))?[0,0,Z]:[$1*cos(ang),$1*sin(ang),Z]" 1,$N,1,13,"i1 = 1 + y; i2 = 1 + (i1%"$N"); const j0 = "$N" + 1; j1 = j0 + i1; j2 = j0 + i2; [ 3,0,i2,i1, 3,j0,j1,j2, 4,i1,i2,j2,j1 ]" permute[^0,1] "cyzx" 1,$nbp,1,3,200 1,$nbp,1,1,1 y a y => [3D\ cylinder] } #@cli delaunay3d #@cli : Generate 3D Delaunay triangulations from selected images. #@cli : One assumes that the selected input images are binary images containing the set of points to mesh. #@cli : The output 3D object is a mesh composed of non-oriented triangles. #@cli : $ 500,500 noise 0.05,2 eq 1 * 255 +delaunay3d color3d[1] 255,128,0 dilate_circ[0] 5 to_rgb[0] \ # +object3d[0] [1],0,0,0,1,1 max[-1] [0] delaunay3d : e[^-1] "Generate 3D Delaunay triangulation from image$?." foreach { channels 0 != 0 # Label each point separately whd={w},{h},{d} +r 1,{whd},1,1,-1 cumulate. *. .. r. $whd,1,-1 # Compute voronoi diagram of point cloud. +distance[0] 1 *[2] -1 watershed[1] [2] rm[2] # Get redondant set of Delaunay triangles from the voronoi diagram. r[1] 100%,100%,100%,3 if d>1 # Add detection cases for 3D images. +_delaunay3d[1] 1,0,0,0,0,1 +_delaunay3d[1] -1,0,0,0,0,-1 +_delaunay3d[1] 0,1,0,0,0,1 +_delaunay3d[1] 0,-1,0,0,0,-1 fi +_delaunay3d[1] 1,0,0,0,1,0 _delaunay3d[1] -1,0,0,0,-1,0 # 2D detection. a[^0] x transpose. -. 1 # Build 3D mesh. pointcloud3d[0] s3d[0] rm[3-5] i.. 1,100%,1,1,3 a[-2,-1] x 3,100%,1,1,200 1,100%,1,1,1 =[1] {h},0,1 y a y } _delaunay3d : f. "A=j($1,$2,$3,0,0,1); B=j($4,$5,$6,0,0,1); i!=A && i!=B && A!=B?kth(1+c,i,A,B):0" discard. 0 r. {h/3},3,1,1,-1 #@cli distribution3d #@cli : Get 3D color distribution of selected images. #@cli : $ image.jpg distribution3d colorcube3d primitives3d[-1] 1 add3d distribution3d : e[^-1] "Get 3D color distribution of image$?." to_rgb permute "cxyz" y foreach { nbp:=round(h/3) i.. (67.5;73.5;109.5;103.5;51.5;100.5;\ # Magick number for CImg3d. $nbp;$nbp) # Number of vertices and primitives. 1,$nbp,1,1,1 +f. y a[-2,-1] x y. # Primitives. .. # Colors. 1,$nbp,1,1,1 # Opacities. a y => [3D\ distribution] # Build 3D object. } #@cli /3d : eq. to 'div3d'. : (+) #@cli div3d : factor : factor_x,factor_y,_factor_z : (+) #@cli : Scale selected 3D objects isotropically or anisotropically, with the inverse of specified #@cli : factors. #@cli : (eq. to '/3d'). #@cli : Default value: 'factor_z=1'. #@cli : $ torus3d 5,2 repeat 5 { +add3d[-1] 12,0,0 div3d[-1] 1.2 color3d[-1] ${-rgb} } add3d #@cli db3d : eq. to 'double3d'. db3d : l[] { check "isbool(${1=1})" mode=$1 onfail noarg mode=1 } v + _double3d $mode #@cli double3d : _is_double_sided={ 0 | 1 } #@cli : Enable/disable double-sided mode for 3D rendering. #@cli : (eq. to 'db3d'). #@cli : Default value: 'is_double_sided=1'. #@cli : $ mode3d 1 repeat 2 { torus3d 100,30 rotate3d[-1] 1,1,0,60 double3d $> snapshot3d[-1] 400 } double3d : l[] { check "isbool(${1=1})" mode=$1 onfail noarg mode=1 } v + _$0 $mode _double3d : if $^>=0 s0,s1=Disable,Enable e[0--4] ${s$1}" double-sided mode for 3D rendering." fi _double3d=$1 #@cli elevation3d : { z-factor | [elevation_map] | 'formula' },base_height={ -1 | >=0 } : (no arg) #@cli : Generate 3D elevation of selected images, opt. with a specified elevation map. #@cli : When invoked with (no arg) or 'z-factor', the elevation map is computed as the pointwise L2 norm of the #@cli : pixel values. Otherwise, the elevation map is taken from the specified image or formula. #@cli : $ image.jpg +blur 5 elevation3d. 0.75 #@cli : $ 128,128,1,3,u(255) plasma 10,3 blur 4 sharpen 10000 n 0,255 \ # elevation3d[-1] 'X=(x-64)/6;Y=(y-64)/6;-100*exp(-(X^2+Y^2)/30)*abs(cos(X)*sin(Y))' elevation3d : skip "${1=_noarg}" check "${2=-1}==-1 || $2>=0" if $2>=0 base_str=" and base height $2" else base_str= fi if isnum($1) e[^-1] "Generate 3D elevation of image$?, with z-factor $1"$base_str. argtype,zfactor=0,$1 elif ${"is_image_arg $1"} e[^-1] "Generate 3D elevation of image$?, from elevation $1"$base_str. argtype=2 pass$1 0 if s>1 norm. fi store. elevation_img elif isexpr($1) e[^-1] "Generate 3D elevation of image$?, with formula '$1'"$base_str. argtype=1 else e[^-1] "Generate 3D elevation of image$?." argtype,zfactor=0,1 fi is_base:=$2>=0 foreach { nm={n} M,N:=w,h to_rgb # Generate vertices. 100%,100%,1,2,[x,y] if !$argtype +norm[0] *. $zfactor # with z-factor elif $argtype==1 [0],[0],1,1,"$1" # with formula else $elevation_img # from [image] fi a[-2,-1] c if $is_base . sh. 100% f. {-sign($1)*$2} rm. a[-2,-1] y fi r. {wh},1,1,3,-1 permute. cxyz nbv:=h # Generate primitives. header="const M = "$M"; const N = "$N"; const MN = M*N" {[$M,$N]-1},1,5,$header"; # Rear i0 = M*y + x; i1 = i0 + MN; [ 4,ui2f(i0),ui2f(i0 + M),ui2f(i0 + 1 + M),ui2f(i0 + 1) ]" r. 1,{wh},1,100%,-1 nbp:=h if $is_base # Add base primitives if necessary {[$M,$N]-1},1,5,$header"; # Front i0 = M*y + x; i1 = i0 + MN; [ 4,ui2f(i1),ui2f(i1 + 1),ui2f(i1 + 1 + M),ui2f(i1 + M) ]" r. 1,{wh},1,100%,-1 nbp+=h {$M-1},1,1,10,$header" ; # Top and bottom i0 = x; i1 = i0 + MN; i2 = i0 + M*(N - 1); i3 = i2 + MN; [ 4,ui2f(i0),ui2f(i0 + 1),ui2f(i1 + 1),ui2f(i1), 4,ui2f(i2),ui2f(i3),ui2f(i3 + 1),ui2f(i2 + 1) ]" r. 1,{wh},1,100%,-1 s. c,2 nbp+=2*h {$N-1},1,1,10,$header"; # Left and right i0 = M*x; i1 = i0 + MN; i2 = i0 + M - 1; i3 = i2 + MN; [ 4,ui2f(i0),ui2f(i1),ui2f(i1 + M),ui2f(i0 + M), 4,ui2f(i2),ui2f(i2 + M),ui2f(i3 + M),ui2f(i3) ]" r. 1,{wh},1,100%,-1 s. c,2 nbp+=2*h permute[-6--1] cyxz -a[-6--1] y else permute. cyxz fi # Generate colors / opacities. mv[0] $! r. {[w,h]-1},1,3,0 r. {wh},1,1,3,-1 permute. cxyz if $is_base 3,{$nbp-h},1,1,200 fi 1,$nbp,1,1,1 # Add header and merge object. i[0] ('CImg3d':y) +[0] 0.5 i[1] ({ui2f([$nbv,$nbp]):;}) y a y => $nm } #@cli empty3d #@cli : Input empty 3D object. #@cli : $ empty3d +empty3d : e[^-1] "Input empty 3D object." (67.5;73.5;109.5;103.5;51.5;100.5;0;0) => [3D\ empty] #@cli extract_textures3d #@cli : Extract texture data from selected 3D objects. #@cli : $ image.jpg imagesphere3d 10,10 +extract_textures3d extract_textures3d : e[^-1] "Extract textures from 3D object$?." check3d foreach { ext={x} if ['$ext']!=0 ext..=. fi bn={`"s = ['"{b}'"]; e = ['"$ext"']; p = find(s,e,size(s) - size(e),-1); p>0?s[p] = 0; s"`} s3d nbv,nbp={1,f2ui(crop())} tmp=$! $nbp eval. ">begin(p = N = 0); i[#4,p++]==-128?( W = i[#4,p++]; H = i[#4,p++]; S = i[#4,p++]; WHS = W*H*S; WHS?(run('+z[4] 0,',p,',0,',p + WHS - 1,' r. ',W,',',H,',1,',S,',-1 => tx',N); p+=WHS); ):(p+=2); ++N" rm[0-$tmp] foreach { => ${bn}_texture$> } } #@cli extrude3d : _depth>0,_resolution>0,_smoothness[%]>=0 #@cli : Generate extruded 3D object from selected binary XY-profiles. #@cli : Default values: 'depth=16', 'resolution=1024' and 'smoothness=0.5%'. #@cli : $ image.jpg threshold 50% extrude3d 16 extrude3d : check "${1=16}>0 && ${2=1024}>0 && ${3=0.5%}>=0" e[^-1] "Generate extruded 3D object from XY-profile$?, with depth $1, resolution $2 and smoothness $3." norm n 0,1 autocrop 0 foreach { nm={n} wr:=round(max(1,w>h?min($2,w):min($2,h)*w/h)) hr:=round(max(1,w>h?min($2,w)*h/w:min($2,h))) fact:=$1/max(w/$wr,h/$hr) b $3,0 r $wr,$hr,1,1,2 expand xyz,1 isosurface3d 50% *3d 1,1,$fact rv3d => $nm } #@cli f3d : eq. to 'focale3d'. f3d : l[] { check "isnum(${1=700})" focale=$1 onfail noarg focale=700 } v + _focale3d $focale #@cli focale3d : focale #@cli : Set 3D focale. #@cli : (eq. to 'f3d').\n #@cli : Set 'focale' to 0 to enable parallel projection (instead of perspective). #@cli : Set negative 'focale' will disable 3D sprite zooming. #@cli : Default value: 'focale=700'. #@cli : $ repeat 5 { torus3d 100,30 rotate3d[-1] 1,1,0,60 focale3d {$<*90} snapshot3d[-1] 400 } remove[0] focale3d : l[] { check "isnum(${1=700})" focale=$1 onfail noarg focale=700 } v + _$0 $focale _focale3d : e[0--3] "Set 3D focale to $1." _focale3d=$1 #@cli fov3d : fov_angle>=0,_image_resolution>0 #@cli : Set 3D focale to match specified field of vision angle (in degree) for rendering a 3D object in an image \ # with specified resolution. #@cli : Return corresponding value of the focale in status. #@cli : Default value: 'fov_angle=45' and 'image_size=max(w,h)' (max size of the latest image). fov3d : check "${1=45}>=0 && isint(${2=max(w,h)},1)" e[^-1] "Set 3D focale to have FOV angle of $1 for image size $2." if !$1 f3d=0 else f3d:=($2)/2/tan(($1)°/2) fi f3d $f3d u $f3d #@cli gaussians3d : _size>0,_opacity #@cli : Convert selected 3D objects into set of 3D gaussian-shaped sprites. #@cli : $ image.jpg rescale2d ,32 distribution3d gaussians3d 20 colorcube3d primitives3d[-1] 1 +3d gaussians3d : check "${1=32}>0" skip ${2=0.3} e[^-1] "Convert 3D object$? into sets of gaussian-shaped 3D sprites, with size $1 and opacity $2." p3d 2 p3d 0 foreach { nm={n} s3d nbv:=h rm. (-128;$1;$1;1) $1,$1 gaussian. 35%,35%,0 c. 30%,100% n. 0,$2 y. a[-2,-1] y # First opacity is generated. if $nbv>1 4,{$nbv-1},1,1,-128,0,0,0 y[-2,-1] a[-2,-1] y fi # Other ones are shared copies of the first one. a y => $nm } #@cli gmic3d #@cli : Input a 3D G'MIC logo. #@cli : $ gmic3d +primitives3d 1 +gmic3d : e[^-1] "Input 3D G\47MIC logo." text3d G,60,20,2 col3d. 16,64,255 text3d \',60,20,2 +3d. 40 col3d. 64,128,255 text3d M,60,20,2 +3d. 50 col3d. 96,196,255 text3d I,60,20,2 +3d. 90 col3d. 64,128,255 text3d C,60,20,2 +3d. 100 col3d. 16,64,255 sphere3d 8,-3 +3d. 102,-3,20 col3d. 192,128,255 +3d[-6--1] c3d. repeat 30 { box3d {min(3+$,20+80*$>,10*$>],0,255)},0.5 r3d. 1,1,1,{$>*12} +3d. {80*cos(0.5+1.02*$>*12*pi/180)},{30*sin(0.8+$>*12*pi/180)},{2*$>-60} } +3d[-30--1] +3d. 0,5,30 +3d[-2--1] => [3d\ gmic] #@cli gyroid3d : _resolution>0,_zoom #@cli : Input 3D gyroid at (0,0,0), with specified resolution. #@cli : Default values: 'resolution=32' and 'zoom=5'. #@cli : $ gyroid3d 48 +primitives3d 1 +gyroid3d : check ${1=32}>0 skip ${2=5} e[^-1] "Input 3D gyroid, with resolution $1 and range $2." isosurface3d "'0.49*(\ cos( 2*x + y + z - pi) + cos( 2*x - y + z - pi)\ + cos(- 2*x + y - z - pi) + cos(- 2*x - y - z - pi)\ + cos( x + 2*y + z - pi) + cos( x + 2*y - z - pi)\ + cos(- x - 2*y + z - pi) + cos(- x - 2*y - z - pi)\ + cos( x + y + 2*z - pi) + cos(- x + y + 2*z - pi)\ + cos( x - y - 2*z - pi) + cos(- x - y - 2*z - pi)\ + cos(- 2*x + y + z) + cos( 2*x + y - z)\ + cos(- 2*x - y + z) + cos( 2*x - y - z)\ + cos(- x + 2*y + z) + cos( x - 2*y + z)\ + cos(- x + 2*y - z) + cos( x - 2*y - z)\ + cos( x - y + 2*z) + cos( x + y - 2*z)\ + cos(- x - y + 2*z) + cos(- x + y - 2*z)\ ) + 0.27*( \ cos(- 2*x + 2*y - pi) + cos( 2*x - 2*y - pi)\ + cos( 2*x + 2*y - pi) + cos(- 2*x - 2*y - pi)\ + cos(- 2*y + 2*z - pi) + cos( 2*y - 2*z - pi)\ + cos( 2*y + 2*z - pi) + cos(- 2*y - 2*z - pi)\ + cos(- 2*z + 2*x - pi) + cos( 2*z - 2*x - pi)\ + cos( 2*z + 2*x - pi) + cos(- 2*z - 2*x - pi)\ ) - 0.69'",0,{-$2},{-$2},{-$2},$2,$2,$2,$1,$1,$1 c3d. n3d. => [3D\ gyroid] #@cli histogram3d #@cli : Get 3D color histogram of selected images. #@cli : $ image.jpg rescale2d 64 histogram3d circles3d 3 opacity3d. 0.75 colorcube3d primitives3d[-1] 1 add3d histogram3d : e[^-1] "Get 3D color histogram of image$?." to_rgb foreach { r {wh},3,1,1,-1 pointcloud 1 n 0,255 palette hot point. 0,0,0,1,0 map.. . rm. pointcloud3d => "[3D histogram]" } #@cli image6cube3d #@cli : Generate 3D mapped cubes from 6-sets of selected images. #@cli : $ image.jpg animate flower,"30,0","30,5",6 image6cube3d image6cube3d : e[^-1] "Generate 3D mapped cubes from image$?." M:=max(${-max_wh}) r $M,$M,1,3 imageplane3d n3d c3d repeat int($!/6) { l[$>-{$>+5}] { +3d[0] 0,0,-0.5 r3d[1] 0,1,0,90 +3d[1] -0.5,0,0 r3d[2] 0,1,0,180 +3d[2] 0,0,0.5 r3d[3] 0,1,0,270 +3d[3] 0.5,0,0 r3d[4] 1,0,0,90 +3d[4] 0,0.5,0 r3d[5] 1,0,0,270 +3d[5] 0,-0.5,0 +3d => "[3D image cube]" } } #@cli imageblocks3d : _maximum_elevation,_smoothness[%]>=0 #@cli : Generate 3D blocks from selected images. #@cli : Transparency of selected images is taken into account. #@cli : Default values: 'maximum_elevation=10' and 'smoothness=0'. #@cli : $ image.jpg rescale2d ,32 imageblocks3d -20 mode3d 3 imageblocks3d : check ${2=0}>=0 skip ${1=10},${3=0} e[^-1] "Generate 3D blocks from image$?, with maximum elevation $1 and smoothness $2." foreach { w,h={w},{h} split_opacity to_rgb[0] is_opacity:=$!==2 # Create 3D object template. l[] { box3d 1,1,0 repeat $w-1 { ++3d. 1,0,0 } +3d repeat $h-1 { ++3d. 0,1,0 } +3d } s3d. # Set vertex altitudes. +norm[0] b. $2 y. n. 0,$1 r[-5] 24,{-5,round(w*h/24)},1,1,-1 if $1<0 j[-5] .,2 j[-5] .,5 j[-5] .,8 j[-5] .,11 else j[-5] .,14 j[-5] .,17 j[-5] .,20 j[-5] .,23 fi rm. y[-4] # Set primitive colors. rm.. r[0] {0,wh},1,1,100%,-1 permute[0] cxyz r[0] 600%,100%,1,1,0,2 y[0] mv[0] -1 # Set primitive opacities. if $is_opacity rm. mv[0] $! /. 255 y. r. 6,100%,1,1 y. fi a y } #@cli imagecube3d #@cli : Generate 3D mapped cubes from selected images. #@cli : $ image.jpg imagecube3d imagecube3d : e[^-1] "Generate 3D mapped cubes from image$?." slices 50% to_rgb foreach { nm={n} i.. (67.5;73.5;109.5;103.5;51.5;100.5;\ # Magick number for CImg3d. 8;6;\ # Number of vertices and primitives. -0.5;-0.5;-0.5;\ # Vertex coordinates. 0.5;-0.5;-0.5;\ 0.5;0.5;-0.5;\ -0.5;0.5;-0.5;\ -0.5;-0.5;0.5;\ 0.5;-0.5;0.5;\ 0.5;0.5;0.5;\ -0.5;0.5;0.5;\ 12;0;3;2;1;0;0;0;{h};{w};{h};{w};0;\ # Primitives description. 12;1;2;6;5;0;0;0;{h};{w};{h};{w};0;\ 12;5;6;7;4;0;0;0;{h};{w};{h};{w};0;\ 12;4;7;3;0;0;0;0;{h};{w};{h};{w};0;\ 12;4;0;1;5;0;0;0;{h};{w};{h};{w};0;\ 12;3;7;6;2;0;0;0;{h};{w};{h};{w};0;\ -128;{w};{h};{s}) # Texture map for the first face. y. (-128;0;0;0;-128;0;0;0;-128;0;0;0;-128;0;0;0;-128;0;0;0;1;1;1;1;1;1) # Other faces and opacities. a y => $nm } #@cli imageplane3d #@cli : Generate 3D mapped planes from selected images. #@cli : $ image.jpg imageplane3d imageplane3d : e[^-1] "Generate 3D mapped planes from image$?." slices 50% to_color foreach { w,h,s,w1,h1={w},{h},{s},{w-1},{h-1} nm={n} i.. (67.5;73.5;109.5;103.5;51.5;100.5;\ # Magick number for CImg3d. 4;1;\ # Number of vertices and primitives. 0;0;0;\ # Vertex coordinates. $w;0;0;\ $w;$h;0;\ 0;$h;0;\ 12;0;3;2;1;0;0;0;$h1;$w1;$h1;$w1;0;\ # Primitives description. -128;$w;$h;$s) # Texture map. y. (1) # Opacity. a y => $nm } #@cli imagepyramid3d #@cli : Generate 3D mapped pyramids from selected images. #@cli : $ image.jpg imagepyramid3d imagepyramid3d : e[^-1] "Generate 3D mapped pyramids from image$?." to_rgb foreach { nm={n} w2:=w/2 i.. (67.5;73.5;109.5;103.5;51.5;100.5;\ # Magick number for CImg3d. 5;5;\ # Number of vertices and primitives. -0.5;-0.5;-0.5;\ # Vertex coordinates. 0.5;-0.5;-0.5;\ 0.5;0.5;-0.5;\ -0.5;0.5;-0.5;\ 0;0;0.5;\ 12;0;3;2;1;0;0;0;{h};{w};{h};{w};0;\ # Primitives description. 9;0;4;3;0;{h};$w2;0;{w};{h};\ 9;1;4;0;0;{h};$w2;0;{w};{h};\ 9;2;4;1;0;{h};$w2;0;{w};{h};\ 9;3;4;2;0;{h};$w2;0;{w};{h};\ -128;{w};{h};{s}) # Texture map for the first face. y. (-128;0;0;0;-128;0;0;0;-128;0;0;0;-128;0;0;0;1;1;1;1;1) # Other faces and opacities. a y => $nm } #@cli imagerubik3d : _xy_tiles>=1,0<=xy_shift<=100,0<=z_shift<=100 #@cli : Generate 3D mapped rubik's cubes from selected images. #@cli : Default values: 'xy_tiles=3', 'xy_shift=5' and 'z_shift=5'. #@cli : $ image.jpg imagerubik3d , imagerubik3d : check "${1=3}>=1 && ${2=5}>=0 && $2<=100 && ${3=5}>=0 && $3<=100" e[^-1] "Generate 3D mapped rubik\47s cubes from image$? with $1 xy-tiles, xy-shift $2 and z-shift $3." foreach { nm={n} # Generate primary 3D side. ('CImg3d') +. 0.5 (8,5) (0,0,0;\ 100,0,0;\ 100,100,0;\ 0,100,0;\ $2,$2,{-$3};\ {100-$2},$2,{-$3};\ {100-$2},{100-$2},{-$3};\ $2,{100-$2},{-$3}) (4,4,7,6,5;\ 4,0,4,5,1;\ 4,3,2,6,7;\ 4,0,3,7,4;\ 4,1,5,6,2) 3,5,1,1,200 1,5,1,1,1 y[-6--1] a[-6--1] y repeat $1-1 { ++3d. 100 } +3d[-$1--1] # Duplicate along X repeat $1-1 { ++3d. 0,100 } +3d[-$1--1] # Duplicate along Y t3d. .. rm.. /3d. $1 -3d. 50,50,50 +r3d. 0,1,0,-90 +r3d. 0,1,0,-90 +r3d. 0,1,0,-90 # Generate the 5 other sides. +r3d. 0,0,1,-90 +r3d. 0,0,1,180 +3d => $nm } #@cli imagesphere3d : _resolution1>=3,_resolution2>=3 #@cli : Generate 3D mapped sphere from selected images. #@cli : Default values: 'resolution1=32' and 'resolutions2=16'. #@cli : $ image.jpg imagesphere3d 32,16 imagesphere3d : check "${1=32}>=3 && ${2=16}>=3" e[^-1] "Generate 3D mapped sphere from image$?, with resolutions ($1,$2)." to_rgb foreach { nm={n} # Generate object header. tw,th:=[w,h]-1 # Maximum texture xy-coordinates. nbv:=2+$1*($2-2) # Number of vertices. nbp:=$1*($2-1) # Number of primitives. (67.5;73.5;109.5;103.5;51.5;100.5;\ # Magick number for CImg3d. $nbv;$nbp) # Number of vertices and primitives. # Define sphere vertices. (0;0;1) (0;0;-1) (0,{2*pi};0,{2*pi}^0,0;{pi},{pi}) r. {$1+1},$2,1,2,3 z. 0,1,{w-2},{h-2} s. c +sin. +sin... *[-2,-1] +cos.. sin... cos[-4] *[-4,-3] a[-3--1] c permute. cxyz y. a[-3--1] y # Define sphere primitives (triangles and quadrangles). repeat $1 { v=$> tx0:=$v*$tw/$1 tx1:=($v+1)*$tw/$1 ty1:=$th/($2-1) (9;0;{2+$v};{2+($v+1)%$1};{$tw/2};0;$tx0;$ty1;$tx1;$ty1) # Textured triangle from 1st pole. repeat $2-3 { u=$> ty0=$ty1 ty1:=($u+2)*$th/($2-1) i0:=2+$u*$1+$v i1:=2+$u*$1+($v+1)%$1 (12;$i0;{$i0+$1};{$i1+$1};$i1;$tx0;$ty0;$tx0;$ty1;$tx1;$ty1;$tx1;$ty0) # Textured quadrangle. } (9;1;{2+$1*($2-3)+($v+1)%$1};{2+$1*($2-3)+$v};{$tw/2};$th;$tx1;$ty1;$tx0;$ty1) # Textured triangle from 2nd pole. } a[-$nbp--1] y # Define sphere textures, opacities and generate object. mv[-4] $! i.. (-128;{w};{h};3) y. 1,{4*($nbp-1)},1,1,-128,0,0,0 1,$nbp,1,1,1 a y => $nm } #@cli isoline3d : isovalue[%] : 'formula',value,_x0,_y0,_x1,_y1,_size_x>0[%],_size_y>0[%] : (+) #@cli : Extract 3D isolines with specified value from selected images or from specified formula. #@cli : Default values: 'x0=y0=-3', 'x1=y1=3' and 'size_x=size_y=256'. #@cli : $ image.jpg blur 1 isoline3d 50% #@cli : $ isoline3d 'X=x-w/2;Y=y-h/2;(X^2+Y^2)%20',10,-10,-10,10,10 #@cli isosurface3d : isovalue[%] : 'formula',value,_x0,_y0,_z0,_x1,_y1,_z1,_size_x>0[%],_size_y>0[%],_size_z>0[%] : (+) #@cli : Extract 3D isosurfaces with specified value from selected images or from specified formula. #@cli : Default values: 'x0=y0=z0=-3', 'x1=y1=z1=3' and 'size_x=size_y=size_z=32'. #@cli : $ image.jpg rescale2d ,128 luminance threshold 50% expand z,2 blur 1 isosurface3d 50% mul3d 1,1,30 #@cli : $ isosurface3d 'x^2+y^2+abs(z)^abs(4*cos(x*y*z*3))',3 #@cli label3d : "text",font_height>=0,_opacity,_color1,... #@cli : Generate 3D text label. #@cli : Default values: 'font_height=13', 'opacity=1' and 'color=255,255,255'. +label3d : check ${2=13}>=0 skip ${3=1},${4=255},${5=$4},${6=$5} e[^-1] "Generate 3D label '$1' with font height $2, opacity $3 and color (${4--1})." l[] { 0 t "$1",0,0,$2,1,${4--1},255 sprite3d } #@cli label_points3d : _label_size>0,_opacity #@cli : Add a numbered label to all vertices of selected 3D objects. #@cli : Default values: 'label_size=13' and 'opacity=0.8'. #@cli : $ torus3d 100,40,6,6 label_points3d 23,1 mode3d 1 label_points3d : check ${1=13}>0 skip ${2=0.8} e[^-1] "Label vertices of 3D object$?." repeat $! { +p3d[$>] 0 l. { s3d rm[-3--1] nbp={-2,@0} =.. $nbp,0,1 # Set correct number of primitives (1,0;1,{$nbp-1}) r. 2,$nbp,1,1,3 r. 1,{2*h},1,1,-1 # Create new primitive data repeat $nbp { # Create texture labels as primitive colors. 0 t. $>,0,0,$1,1,255,255,255 autocrop. 0 i.. (-128;{w};{h};3) y. } repeat $nbp { # Create texture masks as primitive opacities. 0 t. $>,0,0,$1,1,$2 autocrop. 0 i.. (-128;{w};{h};1) y. } a y # Merge final object data. } +3d[$>,-1] } #@cli lathe3d : _resolution>0,_smoothness[%]>=0,_max_angle>=0 #@cli : Generate 3D object from selected binary XY-profiles. #@cli : Default values: 'resolution=128', 'smoothness=0.5%' and 'max_angle=361'. #@cli : $ 300,300 rand -1,1 blur 40 sign normalize 0,255 lathe3d , lathe3d : check "${1=128}>0 && ${2=0.5%}>=0 && ${3=361}>=0" e[^-1] "Generate lathed 3D object from XY-profile$?, with resolution $1, smoothness $2 and maximum angle $3 deg." tmax:=($3-180)*pi/180 round norm n 0,1 autocrop 0 foreach { wr:=max(1,w2=2*w;w2>h?min($1,w2):min($1,h)*w2/h) hr:=max(1,w2=2*w;w2>h?min($1,w2)*h/w2:min($1,h)) rmax:=sqrt(($wr)^2+($hr)^2)/2 $wr,1,$wr,1,"xc = x - w/2; zc = z - d/2; t = atan2(zc,xc); t>"$tmax"?"$rmax":sqrt(xc*xc+zc*zc)" *. {2*({-2,w}-1)/(w-1)} r. $wr,$hr,$wr (0;{{-2,h}-1}) r. $wr,$hr,$wr,1,3 a[-2--1] c warp.. .,0,1,0 rm. expand xyz,10 b $2 isosurface3d 50% rv3d } #@cli l3d : eq. to 'light3d'. : (+) #@cli light3d : position_x,position_y,position_z : [texture] : (no arg) : (+) #@cli : Set the light coordinates or the light texture for 3D rendering. #@cli : (eq. to 'l3d').\n #@cli : (no arg) resets the 3D light to default. #@cli : $ torus3d 100,30 double3d 0 specs3d 1.2 repeat 5 { light3d {$>*100},0,-300 +snapshot3d[0] 400 } remove[0] #@cli line3d : x0,y0,z0,x1,y1,z1 #@cli : Input 3D line at specified coordinates. #@cli : $ repeat 100 { a:=$>*pi/50 line3d 0,0,0,{cos(3*$a)},{sin(2*$a)},0 color3d. ${-rgb} } add3d +line3d : e[^-1] "Input 3D line (${1-3})-(${4-6})." 1,21,1,1,67.5,73.5,109.5,103.5,51.5,100.5,2,1,${1-6},2,0,1,200,200,200,1 => [3D\ line] #@cli lines3d : _length>=0 #@cli : Convert specified 3D objects to sets of 3D horizontal segments with specified length. #@cli : Default values: 'length=1'. #@cli : $ torus3d 100,40 +lines3d 20 lines3d : check "${1=1}>=0" e[^-1] "Convert 3D object$? to sets of 3D horizontal segments with length $1." p3d 0 foreach { -3d {$1/2},0,0 ++3d $1,0,0 s3d # Two 3D objects decomposed here! r[2,8] 3,{2,h/3},1,1,-1 a[2,8] x rm[3,6--1] i[3] 3,{2,h},1,1,"y2 = 2*y; x?(y2 + (x==2)):2" =[1] {1,2*i} y a y } #@cli lissajous3d : resolution>1,a,A,b,B,c,C #@cli : Input 3D lissajous curves `x(t)=sin(a*t+A*2*pi)`, `y(t)=sin(b*t+B*2*pi)`, `z(t)=sin(c*t+C*2*pi)`. #@cli : Default values: 'resolution=1024', 'a=2', 'A=0', 'b=1', 'B=0', 'c=0' and 'C=0'. #@cli : $ lissajous3d , +lissajous3d : check "isint(${1=1024},2)" skip ${2=2},${3=0},${4=1},${5=0},${6=0},${7=0} e[^-1] "Input 3D lissajous curve, with resolution $1, (a,A)=($2,$3), (b,B)=($4,$5) and (c,C)=($6,$7)." curve3d[] "const pi2 = 2*pi; sin($2*t + $3*pi2)","sin($4*t + $5*pi2)","sin($6*t + $7*pi2)",0,$1,0,{2*pi} => [3D\ lissajou] #@cli m3d : eq. to 'mode3d'. m3d : l[] { check "isint(${1=4},-1,5)" mode=$1 onfail noarg mode=4 } v + _mode3d $mode #@cli mode3d : _mode #@cli : Set static 3D rendering mode. #@cli : (eq. to 'm3d').\n #@cli : 'mode' can be { -1:bounding-box | 0:dots | 1:wireframe | 2:flat | 3:flat-shaded | 4:gouraud-shaded | \ # 5:phong-shaded }. #@cli : Bounding-box mode ('mode==-1') is active only for the interactive 3D viewer. #@cli : Default value: 'mode=4'. #@cli : $ (0,1,2,3,4,5) double3d 0 repeat w { torus3d 100,30 rotate3d[-1] 1,1,0,60 mode3d {0,@$>} \ # snapshot3d[-1] 300 } remove[0] mode3d : l[] { check "isint(${1=4},-1,5)" mode=$1 onfail noarg mode=4 } v + _$0 $mode _mode3d : if $^>=0 s0,s1,s2,s3,s4,s5,s6=bounding-box,dots,wireframe,flat,flat-shaded,gouraud-shaded,phong-shaded e[0--4] "Set static 3D rendering mode to "${s{$1+1}}"." fi _mode3d=$1 #@cli md3d : eq. to 'moded3d'. md3d : l[] { check "isint(${1=-1},-1,5)" mode=$1 onfail noarg mode=-1 } v + _moded3d $mode #@cli moded3d : _mode #@cli : Set dynamic 3D rendering mode for interactive 3D viewer. #@cli : (eq. to 'md3d').\n #@cli : 'mode' can be { -1:bounding-box | 0:dots | 1:wireframe | 2:flat | 3:flat-shaded | 4:gouraud-shaded | \ # 5:phong-shaded }. #@cli : Default value: 'mode=-1'. moded3d : l[] { check "isint(${1=-1},-1,5)" mode=$1 onfail noarg mode=-1 } v + _$0 $mode _moded3d : if $^>=0 s0,s1,s2,s3,s4,s5,s6=bounding-box,dots,wireframe,flat,flat-shaded,gouraud-shaded,phong-shaded e[0--4] "Set dynamic 3D rendering mode to "${s{$1+1}}"." fi _moded3d=$1 #@cli *3d : eq. to 'mul3d'. : (+) #@cli mul3d : factor : factor_x,factor_y,_factor_z : (+) #@cli : Scale selected 3D objects isotropically or anisotropically, with specified factors. #@cli : (eq. to '*3d'). #@cli : Default value: 'factor_z=1'. #@cli : $ torus3d 5,2 repeat 5 { +add3d[-1] 10,0,0 mul3d[-1] 1.2 color3d[-1] ${-rgb} } add3d #@cli n3d : eq. to 'normalize3d'. n3d : v + _normalize3d #@cli normalize3d #@cli : Normalize selected 3D objects to unit size. #@cli : (eq. to 'n3d'). #@cli : $ repeat 100 { circle3d {u(3)},{u(3)},{u(3)},0.1 } add3d color3d[-1] 255,0,0 +normalize3d[-1] \ # color3d[-1] 0,255,0 add3d normalize3d : v + _$0 _normalize3d : e[0--3] "Normalize size of 3D object$?." check3d foreach { if i[6] s3d r[2] 3,{2,h/3},1,1,-1 s[2] x factor:=v=max({2,iM-im},{3,iM-im},{4,iM-im});v?v:1 a[2-4] x /[2] $factor y[2] a y fi } #@cli o3d : eq. to 'opacity3d'. o3d : _gmic_s="$?" v + _opacity3d $* #@cli opacity3d : opacity #@cli : Set opacity of selected 3D objects. #@cli : (eq. to 'o3d'). #@cli : $ torus3d 100,10 double3d 0 repeat 7 { +rotate3d[-1] 1,0,0,20 opacity3d[-1] {u} } add3d opacity3d : _gmic_s="$?" v + _$0 $* _opacity3d : e[0--3] "Set opacity of 3D object"$_gmic_s" to $1." foreach { nbv,nbp:=f2ui([i[6],i[7]]) $nbp eval. ">begin(p = 8 + 3*$nbv); p+=i[#0,p] + 1; end(set('p',p))" # Find beginning of color data eval. ">begin(p = $p); i[#0,p]==-128?(p+=prod(crop(#0,0,++p,1,3))); p+=3; end(set('p',p))" # Skip color data eval. ">begin(p = $p; const o = $1); i[#0,p]==-128?(p+=3 + prod(crop(#0,0,++p,1,3))):(i[#0,p++] = o)" k[0] } #@cli parametric3d : _x(a,b),_y(a,b),_z(a,b),_amin,_amax,_bmin,_bmax,_res_a>0,_res_b>0,_res_x>0,_res_y>0,_res_z>0,\ # _smoothness>=0,_isovalue>=0 #@cli : Input 3D object from specified parametric surface `(a,b) ⟶ (x(a,b),y(a,b),z(a,b))`. #@cli : Default values: 'x=(2+cos(b))*sin(a)', 'y=(2+cos(b))*cos(a)', 'c=sin(b)', 'amin=-pi', 'amax=pi', \ # 'bmin=-pi', 'bmax=pi', \ # 'res_a=512', 'res_b=res_a', 'res_x=64', 'res_y=res_x', 'res_z=res_y', 'smoothness=2%' and 'isovalue=10%'. #@cli : $ parametric3d , +parametric3d : skip "${1=(2+cos(b))*sin(a)}","${2=(2+cos(b))*cos(a)}","${3=sin(b)}" skip ${4={-pi}},${5={pi}},${6={-pi}},${7={pi}} check "${8=512}>0 && ${9=$8}>0 && ${10=64}>0 && ${11=$10}>0 && ${12=$11}>0 && \ ${13=2%}>=0 && ${14=10%}>=0" e[^-1] "Input 3D object from parametric surface ($1,$2,$3)." # Compute (x(a,b),y(a,b),z(a,b)) and normalize it. ($4,$5;$4,$5^$6,$6;$7,$7) r. $8,$9,1,2,3 channels. 0,2 f. "a=i(x,y,0,0);b=i(x,y,0,1);!c?$1:c==1?$2:$3" sh. 0 xmin,xmax:=im,iM n. 16,{$10-17} rm. sh. 1 ymin,ymax:=im,iM n. 16,{$11-17} rm. sh. 2 zmin,zmax:=im,iM n. 16,{$12-17} rm. r. {w*h},3,1,1,-1 # Extract 3D surface. pointcloud. 1 r. $10,$11,$12,1,0 b. $13,0 isosurface3d. $14 c3d. n3d. *3d. {$xmax-$xmin},{$ymax-$ymin},{$zmax-$zmin} => [3D\ parametric] #@cli pca_patch3d : _patch_size>0,_M>0,_N>0,_normalize_input={ 0 | 1 },_normalize_output={ 0 | 1 },_lambda_xy #@cli : Get 3D patch-pca representation of selected images. #@cli : The 3D patch-pca is estimated from M patches on the input image, and displayed as a cloud of N 3D points. #@cli : Default values: 'patch_size=7', 'M=1000', 'N=3000', 'normalize_input=1', 'normalize_output=0', \ # and 'lambda_xy=0'. #@cli : $ image.jpg pca_patch3d 7 pca_patch3d : check "isint(${1=7},1) && isint(${2=1000},1) && isint(${3=3000},1)" skip ${4=1},${5=0},${6=0} e[^-1] "Get 3D patch-pca representation"${arg0\ ($!>1),s,""}" of image$?, from $2 $1x$1 input patches, with $3 output patches, input normalization "${arg0\ !$4,enabled,disabled}", output normalization "\ ${arg0\ !$5,enabled,disabled}" and lambda_xy $6." P1:=int($1/2) # Backward half-patch size P2:=$1-$P1-1 # Forward half-patch size n 0,255 round 1 foreach { nm={n} s:=s # Pick set of M random located patches. 1,$2 rand. 0,{0,w-1} +rand. 0,{0,h-1} +f. 0 a[-3--1] x round. 1 +patches[0] $1,$1,1,{^} y[2--1] a[2--1] x z[1] 0,1 transpose[1] *[1] $6 a[1,2] y s[^0] x # Normalize patch coordinates by using average and standard deviation. ++[^0] /. $2 -[1--2] . rm. a[^0] x if $4 l. { s y / 'sqrt(1e-8+iv)' a y } fi # Do PCA for dimension reduction. +transpose. m*[-2,-1] eigen. rows.. 0,2 columns. 0,2 transpose. if $5 sqrt.. /.. {-2,iM} ri.. . /. .. fi rm.. # Pick set of N random located patches. repeat $3 { x:=v(w#0-1) y:=v(h#0-1) ({$6*$x};{$6*$y}) +z[0] {$x-$P1},{$y-$P1},{$x+$P2},{$y+$P2},1 y. a[-2,-1] y } # Generate 3D representation of the projected patch set. +a[2--1] x m*[1,-1] transpose[1] # Vertex coordinates. rows[2--1] 2,100% # Colors if $s!=3 r[2--1] $1,$1,1,{min(3,$s)},-1 r[2--1] $1,$1,1,3,{$s==1} y[2--1] fi i[2--2] (-128;$1;$1;3) a[2--1] y rm[0] # Remove input image (now useless). i[0] ('CImg3d') # Header. i[1] ($3;$3) # Geometry. i[3] 2,$3,1,1,x?y:1 # Primitives. 1,$3,1,1,1 # Opacities. y a[-6--1] y # Merge as a 3D object. => $nm } #@cli plane3d : _size_x,_size_y,_nb_subdivisions_x>0,_nb_subdisivions_y>0 #@cli : Input 3D plane at (0,0,0), with specified geometry. #@cli : Default values: 'size_x=1', 'size_y=size_x' and 'nb_subdivisions_x=nb_subdivisions_y=24'. #@cli : $ plane3d 50,30 +primitives3d 1 color3d[-2] ${-rgb} +plane3d : check "${3=24}>0 && ${4=24}>0" skip ${1=1},${2=$1} e[^-1] "Input 3D plane, with size (${1,2}) and subdivisions (${3,4})." {$3+1},{$4+1} elevation3d. 0 *3d. {$1/$3},{$2/$4} col3d. 200 => [3D\ plane] #@cli point3d : x0,y0,z0 #@cli : Input 3D point at specified coordinates. #@cli : $ repeat 1000 { a:=$>*pi/500 point3d {cos(3*$a)},{sin(2*$a)},0 color3d[-1] ${-rgb} } add3d +point3d : e[^-1] "Input 3D point ($1,$2,$3)." 1,17,1,1,67.5,73.5,109.5,103.5,51.5,100.5,1,1,${1-3},1,0,200,200,200,1 => [3D\ point] #@cli pointcloud3d #@cli : Convert selected planar or volumetric images to 3D point clouds. #@cli : $ image.jpg luminance rescale2d ,100 threshold 50% mul 255 pointcloud3d color3d[-1] 255,255,255 pointcloud3d : e[^-1] "Convert image$? to 3D point cloud." foreach { nm={n} s z foreach { +norm !=. 0 i.. (1,{w};1,{w}^1,1;{h},{h}) r.. .,.,1,2,3 *[-2,-1] round. permute. cxyz l. { s -,0 a y is_points=$! } if $is_points -. 1 r. 2,{h/2},1,1,-1 permute. cyzx +warp.. .,0,0,1 rm... permute.. cyzx i.. 1,{h},1,1,$> a[-3,-2] x # Coordinates. i... ('CImg3d') i... ({h},{h}) # Header and size. i.. 1,{h},1,1,1 i.. 1,{h},1,1,y a[-3,-2] x # Primitives. permute. cyzx # Colors. if w==1 r. 3,{h},1,1 elif w>3 i.. 4,{h},1,1,-128,1,1,{w} a[-2,-1] x else r. 3,{h},1,1,0 fi 1,{h},1,1,1 # Opacities. y[-6--1] a[-6--1] y else rm empty3d fi } +3d => $nm } #@cli pose3d : p1,...,p12 #@cli : Apply 3D pose matrix to selected 3D objects. #@cli : $ torus3d 100,20 pose3d 0.152437,1.20666,-0.546366,0,-0.535962,0.559129,1.08531,0,1.21132,0.0955431,\ # 0.548966,0,0,0,-206,1 snapshot3d 400 pose3d : e[^-1] "Apply 3D pose matrix [ $1,$2,$3,$4; $5,$6,$7,$8; $9,$10,$11,$12 ] to 3D object$?." foreach { if ${-_is_mesh3d} if "i[6] && i[7]" s3d r[2] 3,{2,h/3},1,1,-1 i[3] 1,{2,h},1,1,1 a[2,3] x i[3] ($1,$5,$9;$2,$6,$10;$3,$7,$11;$4,$8,$12) m*[2,3] r[2] 1,{2,3*h},1,1,-1 a y fi else error "Command '$0': Image ["{$!-$>-1}"] does not represent a 3D object." fi } #@cli p3d : eq. to 'primitives3d'. p3d : check "isint($1,0,2)" _gmic_s="$?" v + _primitives3d $* #@cli primitives3d : mode #@cli : Convert primitives of selected 3D objects. #@cli : (eq. to 'p3d').\n #@cli : 'mode' can be { 0:points | 1:outlines | 2:non-textured }. #@cli : $ sphere3d 30 primitives3d 1 torus3d 50,10 color3d[-1] ${-rgb} add3d primitives3d : check "isint($1,0,2)" _gmic_s="$?" v + _$0 $* _primitives3d : s0,s1,s2=points,segments,non-textured e[0--3] "Convert primitives of 3D object"$_gmic_s" to "$s$1"." check3d foreach { s3d nbv,nbp={1,f2ui(crop())} r[2] 3,{2,h/3},1,1,-1 permute[2] zycx ($nbv) a[2,-1] y # Vertices as dynamic array of 3D coordinates # Extract textures. tmp=$! $nbp eval. ">begin(p = N = 0); i[#4,p++]==-128?( W = i[#4,p++]; H = i[#4,p++]; S = i[#4,p++]; WHS = W*H*S; WHS?(run('+z[4] 0,',p,',0,',p + WHS - 1,' r. ',W,',',H,',1,',S,',-1 => tx',N); p+=WHS); ):(p+=2); ++N" rm[$tmp] # Convert object to new points/primitives/colors/opacities. 1x3 => prims,cols,opacs $nbp eval. "> begin( const mode = $1; pp = pc = po = 0; # Offset values for primitives, M = N = 0; # Index of input / output primitives. RGB0 = RGB1 = RGB2 = RGB3 = [ 0,0,0 ]; refc = vector(#$nbp); # Correspondence old -> new indices for shared textures add_colored_point(i,R,G,B,O) = ( # Add colored point to object primitives vname = string(#24,'pt',i); isnan(get(vname))?( # If point does not already exist da_push(#$prims,1,ui2f(i)); da_push(#$cols,R,G,B); da_push(#$opacs,O); set(vname,1); ++N; ); ); add_colored_segment(i0,i1,R,G,B,O) = ( # Add colored segment to object primitives vname = string(#24,'seg',min(i0,i1),'_',max(i0,i1)); isnan(get(vname))?( # If segment does not already exist da_push(#$prims,2,ui2f(i0),ui2f(i1)); da_push(#$cols,R,G,B); da_push(#$opacs,O); set(vname,1); ++N; ); ); add_textured_segment(i0,i1,tx0,ty0,tx1,ty1) = ( # Add textured segment to object primitives vname = string(#24,'seg',min(i0,i1),'_',max(i0,i1)); isnan(get(vname))?( # If segment does not already exist da_push(#$prims,6,ui2f(i0),ui2f(i1),tx0,ty0,tx1,ty1); !is_shared_texture?( # Non-shared texture da_push(#$cols,-128,wc,hc,sc); off = da_size(#$cols); resize(#$cols,1,h(#$cols) + whsc,1,1,0); copy(i[#$cols,off],i[#ind_texture,0],whsc); i[#$cols,h(#$cols) - 1] = off + whsc; is_shared_texture = 1; refc[M] = N; wc = M; ):da_push(#$cols,-128,ui2f(refc[wc]),0,0); # Shared texture da_push(#$opacs,O); set(vname,1); ++N; ); ); ); # Read primitive material. n = i[#3,pp++]; # Type of primitive ind_texture = -1; R = i[#4,pc++]; R==-128?( wc = i[#4,pc++]; hc = i[#4,pc++]; sc = i[#4,pc++]; whsc = wc*hc*sc; is_shared_texture = !whsc; vname = string(#24,'tx',whsc?M:wc); ind_texture = get(vname); pc+=whsc; wt1 = w#ind_texture - 1; ht1 = h#ind_texture - 1; R = G = B = 200; ):(G = i[#4,pc++]; B = i[#4,pc++]); O = i[#5,po++]; O==-128?( # Ignore opacity mask wo = i[#5,po++]; ho = i[#5,po++]; so = i[#5,po++]; whso = wo*ho*so; po+=whso; O = 1; ); # Convert primitives. n==1?( # Colored point or sprite i0 = f2ui(i[#3,pp++]); add_colored_point(i0,R,G,B,O); ):n==2?( # Colored segment i0 = f2ui(i[#3,pp++]); i1 = f2ui(i[#3,pp++]); !mode?(add_colored_point(i0,R,G,B,O); add_colored_point(i1,R,G,B,O)): add_colored_segment(i0,i1,R,G,B,O); ):n==3?( # Colored triangle i0 = f2ui(i[#3,pp++]); i1 = f2ui(i[#3,pp++]); i2 = f2ui(i[#3,pp++]); !mode?(add_colored_point(i0,R,G,B,O); add_colored_point(i1,R,G,B,O); add_colored_point(i2,R,G,B,O)): mode==1?(add_colored_segment(i0,i1,R,G,B,O); add_colored_segment(i1,i2,R,G,B,O); add_colored_segment(i2,i0,R,G,B,O)): (da_push(#$prims,3,ui2f(i0),ui2f(i1),ui2f(i2)); da_push(#$cols,R,G,B); da_push(#$opacs,O); ++N); ):n==4?( # Colored quadrangle i0 = f2ui(i[#3,pp++]); i1 = f2ui(i[#3,pp++]); i2 = f2ui(i[#3,pp++]); i3 = f2ui(i[#3,pp++]); !mode?(add_colored_point(i0,R,G,B,O); add_colored_point(i1,R,G,B,O); add_colored_point(i2,R,G,B,O); add_colored_point(i3,R,G,B,O)): mode==1?(add_colored_segment(i0,i1,R,G,B,O); add_colored_segment(i1,i2,R,G,B,O); add_colored_segment(i2,i3,R,G,B,O); add_colored_segment(i3,i0,R,G,B,O)): (da_push(#$prims,4,ui2f(i0),ui2f(i1),ui2f(i2),ui2f(i3)); da_push(#$cols,R,G,B); da_push(#$opacs,O); ++N); ):n==5?( # Colored sphere i0 = f2ui(i[#3,pp++]); i1 = f2ui(i[#3,pp++]); is_outlined = i[#3,pp]; pp+=3; !mode?( P0 = I[#2,i0]; P1 = I[#2,i1]; nind = da_size(#2); da_push(#2,(P0 + P1)/2); add_colored_point(nind,R,G,B,O); ):( da_push(#$prims,5,ui2f(i0),ui2f(i1),mode==1?1:is_outlined,0,0); da_push(#$cols,R,G,B); da_push(#$opacs,O); ++N; ); ):n==6?( # Textured segment i0 = f2ui(i[#3,pp++]); i1 = f2ui(i[#3,pp++]); tx0 = cut(i[#3,pp++],0,wt1); ty0 = cut(i[#3,pp++],0,ht1); tx1 = cut(i[#3,pp++],0,wt1); ty1 = cut(i[#3,pp++],0,ht1); !mode?( copy(RGB0,i(#ind_texture,tx0,ty0,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB1,i(#ind_texture,tx1,ty1,0,0),min(3,s#ind_texture),1,wh#ind_texture); add_colored_point(i0,RGB0[0],RGB0[1],RGB0[2],O); add_colored_point(i1,RGB1[0],RGB1[1],RGB1[2],O); ):mode==1?add_textured_segment(i0,i1,tx0,ty0,tx1,ty1):( da_push(#$prims,2,ui2f(i0),ui2f(i1)); copy(RGB0,i(#ind_texture,tx0,ty0,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB1,i(#ind_texture,tx1,ty1,0,0),min(3,s#ind_texture),1,wh#ind_texture); (RGB0+=RGB1)/=2; da_push(#$cols,RGB0[0],RGB0[1],RGB0[2]); da_push(#$opacs,O); ++N; ); ):n==9?( # Textured triangle i0 = f2ui(i[#3,pp++]); i1 = f2ui(i[#3,pp++]); i2 = f2ui(i[#3,pp++]); tx0 = cut(i[#3,pp++],0,wt1); ty0 = cut(i[#3,pp++],0,ht1); tx1 = cut(i[#3,pp++],0,wt1); ty1 = cut(i[#3,pp++],0,ht1); tx2 = cut(i[#3,pp++],0,wt1); ty2 = cut(i[#3,pp++],0,ht1); !mode?( copy(RGB0,i(#ind_texture,tx0,ty0,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB1,i(#ind_texture,tx1,ty1,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB2,i(#ind_texture,tx2,ty2,0,0),min(3,s#ind_texture),1,wh#ind_texture); add_colored_point(i0,RGB0[0],RGB0[1],RGB0[2],O); add_colored_point(i1,RGB1[0],RGB1[1],RGB1[2],O); add_colored_point(i2,RGB2[0],RGB2[1],RGB2[2],O); ):mode==1?( add_textured_segment(i0,i1,tx0,ty0,tx1,ty1); add_textured_segment(i1,i2,tx0,ty0,tx2,ty2); add_textured_segment(i2,i0,tx2,ty2,tx0,ty0); ):( da_push(#$prims,3,ui2f(i0),ui2f(i1),ui2f(i2)); copy(RGB0,i(#ind_texture,tx0,ty0,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB1,i(#ind_texture,tx1,ty1,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB2,i(#ind_texture,tx2,ty2,0,0),min(3,s#ind_texture),1,wh#ind_texture); (RGB0+=RGB1+=RGB2)/=3; da_push(#$cols,RGB0[0],RGB0[1],RGB0[2]); da_push(#$opacs,O); ++N; ); ):n==12?( # Textured quadrangle i0 = f2ui(i[#3,pp++]); i1 = f2ui(i[#3,pp++]); i2 = f2ui(i[#3,pp++]); i3 = f2ui(i[#3,pp++]); tx0 = cut(i[#3,pp++],0,wt1); ty0 = cut(i[#3,pp++],0,ht1); tx1 = cut(i[#3,pp++],0,wt1); ty1 = cut(i[#3,pp++],0,ht1); tx2 = cut(i[#3,pp++],0,wt1); ty2 = cut(i[#3,pp++],0,ht1); tx3 = cut(i[#3,pp++],0,wt1); ty3 = cut(i[#3,pp++],0,ht1); !mode?( copy(RGB0,i(#ind_texture,tx0,ty0,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB1,i(#ind_texture,tx1,ty1,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB2,i(#ind_texture,tx2,ty2,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB3,i(#ind_texture,tx3,ty3,0,0),min(3,s#ind_texture),1,wh#ind_texture); add_colored_point(i0,RGB0[0],RGB0[1],RGB0[2],O); add_colored_point(i1,RGB1[0],RGB1[1],RGB1[2],O); add_colored_point(i2,RGB2[0],RGB2[1],RGB2[2],O); add_colored_point(i3,RGB3[0],RGB3[1],RGB3[2],O) ):mode==1?( add_textured_segment(i0,i1,tx0,ty0,tx1,ty1); add_textured_segment(i1,i2,tx1,ty1,tx2,ty2); add_textured_segment(i2,i3,tx2,ty2,tx3,ty3); add_textured_segment(i3,i0,tx3,ty3,tx0,ty0); ):( da_push(#$prims,4,ui2f(i0),ui2f(i1),ui2f(i2),ui2f(i3)); copy(RGB0,i(#ind_texture,tx0,ty0,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB1,i(#ind_texture,tx1,ty1,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB2,i(#ind_texture,tx2,ty2,0,0),min(3,s#ind_texture),1,wh#ind_texture); copy(RGB3,i(#ind_texture,tx3,ty3,0,0),min(3,s#ind_texture),1,wh#ind_texture); (RGB0+=RGB1+=RGB2+=RGB3)/=4; da_push(#$cols,RGB0[0],RGB0[1],RGB0[2]); da_push(#$opacs,O); ++N; ); ):(pp+=n); ++M; end(set('N',N)); " rm. da_freeze[2,prims,cols,opacs] f[1] {2,h},$N permute[2] cyzx y[2] rv[3,prims] rv[4,cols] rv[5,opacs] k[0-5] a y } #@cli projections3d : _x[%],_y[%],_z[%],_is_bounding_box={ 0 | 1 },nb_subdivisions>0 #@cli : Generate 3D xy,xz,yz projection planes from specified volumetric images. #@cli : Default values: 'x=y=z=50%', 'is_bounding_box=1' and 'nb_subdividions=5' projections3d : skip "${1=50%},${2=50%},${3=50%}" check "isbool(${4=1}) && isint(${5=5},1)" e[^-1] "Generate 3D projection planes (XY,XZ,ZY) from image$?, from point ($1,$2,$3)." foreach { +projections3d ${1-4} k. } # Fast version that avoid image copy. +projections3d : skip "${1=50%},${2=50%},${3=50%}" check "isbool(${4=1}) && isint(${5=5},1)" e[^-1] "Generate 3D projection planes (XY,XZ,ZY) from image$?, from point ($1,$2,$3)." foreach { w,h,d,x,y,z:="[ w,h,d, round([ cut(ispercentage($1)?$1*w:$1,0,w-1), cut(ispercentage($2)?$2*h:$2,0,h-1), cut(ispercentage($3)?$3*d:$3,0,d-1) ]) ]" +z. 0,0,$z,100%,100%,$z # XY +z.. 0,$y,100%,$y permute. xzyc # XZ +z... $x,$x permute. zyxc # ZY if s==1 to_rgb[-3--1] else channels[-3--1] 0,2 fi n[-3--1] 0,255 r[-3--1] 100%,100%,1,3 foreach[-3--1] { plane3d {[w,h]},$5,$5 t3d. .. rm.. } +3d... 0,0,$z r3d.. 1,0,0,90 +3d.. 0,$y,0 r3d. 0,1,0,-90 +3d. $x,0,0 o3d[^0] 0.8 if $4 box3d $w,$h,$d p3d. 1 o3d. 0.5 fi +3d[^0] } mv[1--1:2] $! #@cli pyramid3d : width,height #@cli : Input 3D pyramid at (0,0,0), with specified geometry. #@cli : $ pyramid3d 100,-100 +primitives3d 1 color3d[-2] ${-rgb} +pyramid3d : e[^-1] "Input new 3D pyramid, with width $1 and height $2." (67.5;73.5;109.5;103.5;51.5;100.5;\ # Magick number for CImg3d. 5;5;\ # Number of vertices and primitives. {-$1/2};{-$1/2};{-$2/2};\ # Vertex coordinates. {$1/2};{-$1/2};{-$2/2};\ {$1/2};{$1/2};{-$2/2};\ {-$1/2};{$1/2};{-$2/2};\ 0;0;{$2/2};\ 4;0;3;2;1;\ # Primitives description. 3;0;4;3;\ 3;1;4;0;\ 3;2;4;1;\ 3;3;4;2) 1,15,1,1,200 1,5,1,1,1 a[-3--1] y => [3D\ pyramid] #@cli quadrangle3d : x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3 #@cli : Input 3D quadrangle at specified coordinates. #@cli : $ quadrangle3d -10,-10,10,10,-10,10,10,10,10,-10,10,10 repeat 10 { +rotate3d[-1] 0,1,0,30 \ # color3d[-1] ${-rgb},0.6 } add3d mode3d 2 +quadrangle3d : e[^-1] "Input 3D quadrangle ($1,$2,$3)-($4,$5,$6)-($7,$8,$9)-($10,$11,$12)." 1,29,1,1,67.5,73.5,109.5,103.5,51.5,100.5,4,1,${1-12},4,0,1,2,3,200,200,200,1 => [3D\ quadrangle] #@cli random3d : nb_points>=0 #@cli : Input random 3D point cloud in [0,1]^3. #@cli : $ random3d 100 circles3d 0.1 opacity3d 0.5 +random3d : check "$1>=0" e[^-1] "Input random 3D point cloud, with $1 points." if $1<0.5 empty3d else l[] { N:=round($1) ({'CImg3d'},$N,$N) 3,$N rand. 0,1 1,$N,1,1,1 1,$N,1,1,y a[-2,-1] x 3,$N,1,1,200 1,$N,1,1,1 y a y } fi => [3D\ random\ pointcloud] #@cli rv3d : eq. to 'reverse3d'. rv3d : _gmic_s="$?" v + _reverse3d #@cli reverse3d #@cli : Reverse primitive orientations of selected 3D objects. #@cli : (eq. to 'rv3d'). #@cli : $ torus3d 100,40 double3d 0 +reverse3d reverse3d : _gmic_s="$?" v + _$0 _reverse3d : e[0--3] "Reverse primitives orientation of 3D object"$_gmic_s"." check3d foreach { {f2ui(i[7])} eval. ">begin(p = 8 + 3*f2ui(i[#0,6])); n = i[#0,p++]; n==2?swap(i[#0,p],i[#0,p + 1]): n==3?swap(i[#0,p + 1],i[#0,p + 2]): n==4?swap(i[#0,p + 1],i[#0,p + 3]): n==6?(swap(i[#0,p],i[#0,p + 1]); swap(i[#0,p + 2],i[#0,p + 4]); swap(i[#0,p + 3],i[#0,p + 5])): n==9?(swap(i[#0,p + 1],i[#0,p + 2]); swap(i[#0,p + 5],i[#0,p + 7]); swap(i[#0,p + 6],i[#0,p + 8])): n==12?(swap(i[#0,p + 1],i[#0,p + 3]); swap(i[#0,p + 6],i[#0,p + 10]); swap(i[#0,p + 7],i[#0,p + 11])); p+=n" rm. } #@cli r3d : eq. to 'rotate3d'. : (+) #@cli rotate3d : u,v,w,angle : (+) #@cli : Rotate selected 3D objects around specified axis with specified angle (in deg.). #@cli : (eq. to 'r3d'). #@cli : $ torus3d 100,10 double3d 0 repeat 7 { +rotate3d[-1] 1,0,0,20 } add3d #@cli rotation3d : u,v,w,angle #@cli : Input 3x3 rotation matrix with specified axis and angle (in deg). #@cli : $ rotation3d 1,0,0,0 rotation3d 1,0,0,90 rotation3d 1,0,0,180 rotation3d : e[^-1] "Input 3D rotation matrix around axis ($1,$2,$3) with angle $4 deg." 3,3,1,1,{"rot(${1-4}°)"} => [3D\ rotation] #@cli sierpinski3d : _recursion_level>=0,_width,_height #@cli : Input 3D Sierpinski pyramid. #@cli : $ sierpinski3d 3,100,-100 +primitives3d 1 color3d[-2] ${-rgb} +sierpinski3d : check ${1=4}>=0 skip ${2=1},${3=1} -e[^-1] "Input 3D Sierpinski pyramid of degree $1, with width $2 and height $3." l[] { _sierpinski3d {-$2/2},{-$2/2},{-$3/2},{$2/2},{-$2/2},{-$3/2},\ {$2/2},{$2/2},{-$3/2},{-$2/2},{$2/2},{-$3/2},\ 0,0,{$3/2},$1 +3d } => [3D\ sierpinski] _sierpinski3d : if $16<=0 (67.5;73.5;109.5;103.5;51.5;100.5;\ 5;5;\ $1;$2;$3;\ $4;$5;$6;\ $7;$8;$9;\ $10;$11;$12;\ $13;$14;$15;\ 4;0;3;2;1;\ 3;0;4;3;\ 3;1;4;0;\ 3;2;4;1;\ 3;3;4;2) 1,15,1,1,200 1,5,1,1,1 a[-3--1] y return fi _sierpinski3d $1,$2,$3,\ {($1+$4)/2},{($2+$5)/2},{($3+$6)/2},\ {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\ {($1+$10)/2},{($2+$11)/2},{($3+$12)/2},\ {($1+$13)/2},{($2+$14)/2},{($3+$15)/2},\ {$16-1} _sierpinski3d {($1+$4)/2},{($2+$5)/2},{($3+$6)/2},\ $4,$5,$6,\ {($4+$7)/2},{($5+$8)/2},{($6+$9)/2},\ {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\ {($4+$13)/2},{($5+$14)/2},{($6+$15)/2},\ {$16-1} _sierpinski3d {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\ {($4+$7)/2},{($5+$8)/2},{($6+$9)/2},\ $7,$8,$9,\ {($7+$10)/2},{($8+$11)/2},{($9+$12)/2},\ {($7+$13)/2},{($8+$14)/2},{($9+$15)/2},\ {$16-1} _sierpinski3d {($1+$10)/2},{($2+$11)/2},{($3+$12)/2},\ {($1+$4+$7+$10)/4},{($2+$5+$8+$11)/4},{($3+$6+$9+$12)/4},\ {($7+$10)/2},{($8+$11)/2},{($9+$12)/2},\ $10,$11,$12,\ {($10+$13)/2},{($11+$14)/2},{($12+$15)/2},\ {$16-1} _sierpinski3d {($1+$13)/2},{($2+$14)/2},{($3+$15)/2},\ {($4+$13)/2},{($5+$14)/2},{($6+$15)/2},\ {($7+$13)/2},{($8+$14)/2},{($9+$15)/2},\ {($10+$13)/2},{($11+$14)/2},{($12+$15)/2},\ $13,$14,$15,\ {$16-1} #@cli size3d #@cli : Return bounding box size of the last selected 3D object. size3d : +rows. 8,{8+3*i[6]} r. 3,{h/3},1,1,-1 s. x,3 u {-3,iM-im},{-2,iM-im},{iM-im} rm[-3--1] #@cli skeleton3d : _metric,_frame_type={ 0:squares | 1:diamonds | 2:circles | 3:auto },_skeleton_opacity,\ # _frame_opacity,_is_frame_wireframe={ 0 | 1 } #@cli : Build 3D skeletal structure object from 2d binary shapes located in selected images. #@cli : 'metric' can be { 0:chebyshev | 1:manhattan | 2:euclidean }. #@cli : Default values: 'metric=2', 'bones_type=3', 'skeleton_opacity=1' and 'frame_opacity=0.1'. #@cli : $ shape_cupid 480 +skeleton3d , skeleton3d : check "isint(${1=2},0,2) && isint(${2=3},0,3)" skip ${3=1},${4=0.1},${5=1} e[^-1] "Build 3D skeletal structure object from image$?, with "${arg0\ $1,chebyshev,manhattan,euclidean}" metric, "\ ${arg0\ $2,squares,diamonds,circles,auto}" bones, skeleton opacity $3 and frame opacity $4 ." foreach { channels 0 # Construct skeleton representation. +distance 0,$1 +f. "(i>j(-1)&&i>j(1)) || (i>j(0,-1)&&i>j(0,1)) || (i>j(-1,-1)&&i>j(1,1)) || (i>j(-1,1)&&i>j(1,-1))" if $3 +slices. -1,0 isosurface3d. 0.5 o3d. $3 col3d. 255,0,0 fi *[0-2] pointcloud3d[0] # Construct bones from skeleton. if $4 l[0] { s3d n={1,@0} if $n r[2] 3,$n,1,1,-1 r[3] 2,$n,1,1,-1 r[4] 3,$n,1,1,-1 if !$2" || "($2==3" && "!$1) # Frame with squares =[1] {4*$n} i[3] [2]x3 +z.. 0,1 z. 0,2 -[2] . +[4] . s. x *.. -1 a[-3--1] x +[3] . -[5,-1] a[2-5] x rm[3] 1,$n,1,1,4 +f. 4*y ++. 1 ++. 1 ++. 1 rv[-3,-1] a[-5--1] x mv. 3 elif $2==1" || "($2==3" && "$1==1) # Frame with diamonds =[1] {4*$n} i[3] [2]x3 +z.. 0,0 z. 0,2 -[2] . +[4] . shift. 1,0 -[3] . +[5,-1] a[2-5] x rm[3] 1,$n,1,1,4 +f. 4*y ++. 1 ++. 1 ++. 1 rv[-3,-1] a[-5--1] x mv. 3 elif $2==2" || "($2==3" && "$1==2) # Frame with circles =[1] {2*$n} +z[4] 0,0 z. 0,2 ++[2,-1] -[2,-2] a[2,-1] x rm[3] 1,$n,1,1,5 +f. 2*y ++. 1 3,100% a[-4--1] x mv. 3 fi y a y o3d $4 if $5 p3d 1 fi col3d 200 else rm empty3d fi } else rm[0] fi +3d } #@cli snapshot3d : _size>0,_zoom>=0,_backgroundR,_backgroundG,_backgroundB,_backgroundA,_fov_angle>=0 : \ # [background_image],zoom>=0,_fov_angle>=0 #@cli : Take 2D snapshots of selected 3D objects. #@cli : Set 'zoom' to 0 to disable object auto-scaling. #@cli : Default values: 'size=1024', 'zoom=1', '[background_image]=(default)' and 'fov_angle=45'. #@cli : $ torus3d 100,20 rotate3d 1,1,0,60 snapshot3d 400,1.2,128,64,32 #@cli : $ torus3d 100,20 rotate3d 1,1,0,60 sample ? +snapshot3d[0] [1],1.2 snapshot3d : check "("${"is_image_arg $1"}" || isint(${1=1024},1)) && ${2=1}>=0 && ${7=45}>=0" \ skip ${3=""},${4=$3},${5=$4},${6=255} fov=$7 if ${"is_image_arg $1"} # Background image specified pass$1 0 to_color. if isnum($3) fov=$3 fi e[0--3] "Take "{w}"x"{h}" snapshot of 3D object$?, with background image $1, zoom factor $2 and FOV "$fov"." elif isnum($3) # Background color specified e[0--3] "Take $1x$1 snapshot of 3D object$?, with zoom factor $2, background color (${3-6}) "\ "and FOV "$fov"." if $6>=255 ($3^$4^$5) else ($3^$4^$5^$6) fi r. $1,$1 else # Default background color e[0--3] "Take $1x$1 snapshot of 3D object$?, with zoom factor $2, default background and FOV "$fov"." 3,3,1,1,"0,0,0,0,0,100,100,0,50" permute. cyzx r. $1,$1,1,3,3 24,24,1,1,48 +f. 64 a[-2,-1] x +mirror. x a[-2,-1] y r. {-2,[w,h]},1,3,0,2 +[-2,-1] /. 2 round. fi ofocale3d=$_focale3d fov3d $fov foreach[^-1] { pass. 0 if $2!=0 c3d[0] n3d[0] *3d[0] {3*min(w,h)*$2/4} fi # Apply zoom factor if s>3 # RGBA rendering o3d[0] 1 100%,100%,1,3,-1 j3d. [0],50%,50%,0,1 to_rgba. replace_color. 0,0,-1,-1,-1,255,0,0,0,0 blend[-2,-1] alpha else # RGB rendering j3d[1] [0],50%,50%,0,1 fi =>[1] {0,n} k. } rm. _focale3d=$ofocale3d #@cli sl3d : eq. to 'specl3d'. sl3d : l[] { check "${1=0.15}>=0" value=$1 onfail noarg value=0.15 } v + _specl3d $value #@cli specl3d : value>=0 #@cli : Set lightness of 3D specular light. #@cli : (eq. to 'sl3d'). #@cli : Default value: 'value=0.15'. #@cli : $ (0,0.3,0.6,0.9,1.2) repeat w { torus3d 100,30 rotate3d[-1] 1,1,0,60 color3d[-1] 255,0,0 specl3d {0,@$>} \ # snapshot3d[-1] 400 } remove[0] specl3d : l[] { check "${1=0.15}>=0" value=$1 onfail noarg value=0.15 } v + _$0 $value _specl3d : e[0--3] "Set lightness of 3D specular light to $1." _specl3d=$1 #@cli ss3d : eq. to 'specs3d'. ss3d : l[] { check "${1=0.8}>=0" value=$1 onfail noarg value=0.8 } v + _specs3d $value #@cli specs3d : value>=0 #@cli : Set shininess of 3D specular light. #@cli : (eq. to 'ss3d'). #@cli : Default value: 'value=0.8'. #@cli : $ (0,0.3,0.6,0.9,1.2) repeat w { torus3d 100,30 rotate3d[-1] 1,1,0,60 color3d[-1] 255,0,0 specs3d {0,@$>} \ # snapshot3d[-1] 400 } remove[0] specs3d : l[] { check "${1=0.8}>=0" value=$1 onfail noarg value=0.8 } v + _$0 $value _specs3d : e[0--3] "Set shininess of 3D specular light to $1." _specs3d=$1 #@cli sphere3d : radius,_nb_recursions!=0 : radius,_nb_phi>=3,_nb_theta>=3 #@cli : Input 3D sphere at (0,0,0), with specified geometry. #@cli : - If 2 arguments are specified: #@cli : - If 'nb_recursions>0', the sphere is generated using recursive subdivisions of an **icosahedron**. #@cli : - If 'nb_recursions<0', the sphere is generated using recursive subdividions of a **cube**. #@cli : - If 3 arguments are specified, the sphere is generated using spherical coordinates discretization. #@cli : Default value: 'nb_recursions=3'. #@cli : $ sphere3d 100 +primitives3d 1 color3d[-2] ${-rgb} +sphere3d : check "isnum(${1=1})" skip "${2=3},${3=}" if !narg($3) # Generate 3D sphere with recursive subdivisions e[0--3] "Input 3D sphere, with radius $1 and $2 subdivision iterations." l[] { if $2>=0 # Subdivide icosahedron # Generate initial icosahedron. (1;3;1;3;2;2;2;2;4;0;4;0^2;2;2;2;4;4;0;0;3;3;1;1^4;4;0;0;3;1;3;1;2;2;2;2) # List of vertices (-0.850650808352039932,-0.525731112119133606,0,0.525731112119133606,0.850650808352039932) map.. . rm. (12) a[-2,-1] y (0;0;9;4;4;8;8;5;5;2;7;7;7;11;0;6;9;9;9;7^\ # List of triangles 1;4;4;8;1;1;10;8;3;3;3;10;6;6;6;10;11;2;5;11^\ 4;9;5;5;8;10;3;3;2;7;10;6;11;0;1;1;0;11;2;2) (20) a[-2,-1] y # Subdivide recursively. repeat $2 { 0 # Next primitives 1,{1,@{1,h-1}},1,1," unit(V) = (ref(V,_V); _V/norm(_V)); p = I[#1,y]; P0 = I[#0,p[0]]; P1 = I[#0,p[1]]; P2 = I[#0,p[2]]; ref(string(#48,'p',min(p[0],p[1]),'_',max(p[0],p[1])),varname); p01 = get(varname); isnan(p01)?(set(varname,p01 = da_size(#0)); da_push(#0,unit((P0 + P1)/2))); ref(string(#48,'p',min(p[0],p[2]),'_',max(p[0],p[2])),varname); p02 = get(varname); isnan(p02)?(set(varname,p02 = da_size(#0)); da_push(#0,unit((P0 + P2)/2))); ref(string(#48,'p',min(p[1],p[2]),'_',max(p[1],p[2])),varname); p12 = get(varname); isnan(p12)?(set(varname,p12 = da_size(#0)); da_push(#0,unit((P1 + P2)/2))); da_push([p[0],p01,p02],[p01,p[1],p12],[p02,p12,p[2]],[p01,p12,p02])" rm[1,-1] } else # Subdivide cube # Generate initial cube. (-1;1;1;-1;-1;1;1;-1^-1;-1;1;1;-1;-1;1;1^-1;-1;-1;-1;1;1;1;1) /. {sqrt(3)} (8) a[-2,-1] y # List of vertices (0;4;0;3;0;1^3;5;1;7;4;2^2;6;5;6;7;6^1;7;4;2;3;5) (6) a[-2,-1] y # List of triangles # Subdivide recursively. repeat -$2 { 0 # Next primitives 1,{1,@{1,h-1}},1,1," unit(V) = (ref(V,_V); _V/norm(_V)); p = I[#1,y]; P0 = I[#0,p[0]]; P1 = I[#0,p[1]]; P2 = I[#0,p[2]]; P3 = I[#0,p[3]]; ref(string(#48,'p',min(p[0],p[1]),'_',max(p[0],p[1])),varname); p01 = get(varname); isnan(p01)?(set(varname,p01 = da_size(#0)); da_push(#0,unit((P0 + P1)/2))); ref(string(#48,'p',min(p[1],p[2]),'_',max(p[1],p[2])),varname); p12 = get(varname); isnan(p12)?(set(varname,p12 = da_size(#0)); da_push(#0,unit((P1 + P2)/2))); ref(string(#48,'p',min(p[2],p[3]),'_',max(p[2],p[3])),varname); p23 = get(varname); isnan(p23)?(set(varname,p23 = da_size(#0)); da_push(#0,unit((P2 + P3)/2))); ref(string(#48,'p',min(p[0],p[3]),'_',max(p[0],p[3])),varname); p03 = get(varname); isnan(p03)?(set(varname,p03 = da_size(#0)); da_push(#0,unit((P0 + P3)/2))); pc = da_size(#0); da_push(#0,unit((P0 + P1 + P2 + P3)/4)); da_push([p[0],p01,pc,p03],[p01,p[1],p12,pc],[pc,p12,p[2],p23],[p03,pc,p23,p[3]])" rm[1,-1] } fi # Convert coordinates/primitives to a 3D object. da_freeze nbp={1,h} *[0] $1 permute cyzx i.. 1,100%,1,1,{w} a[-2,-1] x i[0] ({'CImg3d'},{0,h},$nbp) 3,$nbp,1,1,200 1,$nbp,1,1,1 y a y } else # Generate 3D sphere with discretization of spherical coordinates e[0--3] "Input 3D sphere, with radius $1 and angular discretizations ($2,$3)." l[] { nb_phi,nb_theta:=max(3,$2),max(3,$3) # Generate vertices. 1 $nb_phi,{$nb_theta-2},1,1," begin(da_push(#0,0,0,-1)); theta = lerp(0,pi,(y + 1)/(h + 1)); phi = lerp(0,2*pi,x/w); st = sin(theta); da_push(#0,cos(phi)*st,sin(phi)*st,-cos(theta)); end(da_push(#0,0,0,1); da_freeze())" rm. # Generate primitives. 1 $nb_phi,{$nb_theta-1},1,1," const p = w; const ptm2p1 = p*(h - 1) + 1; xp1 = x + 1; xp1mpp1 = xp1%p + 1; yp = (y - 1)*p; yp1p = yp + p; !y?da_push(3,0,xp1mpp1,xp1): y "[3D sphere]" #@cli spherical3d : "radius_function(phi,theta)",_nb_recursions!=0 : \ # "radius_function(phi,theta)",_nb_phi>=3,_nb_theta>=3 #@cli : Input 3D spherical object at (0,0,0), with specified geometry. #@cli : Second and third arguments are the same as in command ''sphere3d''. #@cli : Default values: 'nb_recursions=5'. #@cli : $ spherical3d "abs(1+0.5*cos(3*phi)*sin(4*theta))" +primitives3d 1 +spherical3d : check "${2=5}!=0" skip "${3=}" e[^-1] "Input 3D spherical object, with radius function '$1'." l[] { sphere3d 1,${2--1} s3d r[2] 3,{2,h/3},1,1,-1 permute[2] zycx f[2] " V = [ i0,i1,i2 ]; V/=norm(V); theta = -acos(V[2]); phi = atan2(V[1],V[0]); R = ($1); st = sin(theta); [ R*cos(phi)*st,R*sin(phi)*st,R*cos(theta) ]" permute[2] cyzx y[2] a y => "[3D spherical surface '$1']" } #@cli spline3d : x0[%],y0[%],z0[%],u0[%],v0[%],w0[%],x1[%],y1[%],z1[%],u1[%],v1[%],w1[%],_nb_vertices>=2 #@cli : Input 3D spline with specified geometry. #@cli : Default values: 'nb_vertices=128'. #@cli : $ repeat 100 { spline3d {u},{u},{u},{u},{u},{u},{u},{u},{u},{u},{u},{u},128 color3d[-1] ${-rgb} } \ # box3d 1 primitives3d[-1] 1 add3d +spline3d : check ${13=128}>=2 e[^-1] "Input new 3D spline from (${1-3}) [${4-6}] to (${7-9}) [${10-12}] with $13 vertices." ('CImg3d') +. 0.5 # Header. ($13;{$13-1}) # Nb vertices / primitives. # Define vertices. 1,$13,1,1,1 (0;1) r. 1,$13,1,1,3 +sqr. +*[-2,-1] a[-4--1] x +*. '$2,$5,{3*(($8)-($2))-2*($5)-($11)},{($5)+($11)+2*(($2)-($8))}' l. { s x + } +*.. '$3,$6,{3*(($9)-($3))-2*($6)-($12)},{($6)+($12)+2*(($3)-($9))}' l. { s x + } *... '$1,$4,{3*(($7)-($1))-2*($4)-($10)},{($4)+($10)+2*(($1)-($7))}' l... { s x + } a[-3--1] x 1,{$13-1},1,1,2 (0,1;{$13-2},{$13-1}) r. 2,..,1,1,3 round. a[-2,-1] x # Primitives. 1,{3*($13-1)},1,1,200 1,{$13-1},1,1,1 # Colors / opacities. y[-3,-4,-6] a[-6--1] y #@cli s3d : eq. to 'split3d'. s3d : _gmic_s="$?" v + _split3d #@cli split3d #@cli : Split selected 3D objects into feature vectors : #@cli : { header, sizes, vertices, primitives, colors, opacities }. #@cli : (eq. to 's3d').\n #@cli : To recreate the 3D object, append all produced images along the y-axis (with command `append y`). #@cli : $ box3d 100 +split3d split3d : _gmic_s="$?" v + _$0 _split3d : e[0--3] "Split 3D object$? into feature vectors." check3d foreach { nm={n} +z. 0,0,0,5 +z.. 0,6,0,7 nbv,nbp:=f2ui(crop()) indp:=8+3*$nbv if $nbv +z... 0,8,0,{$indp-1} else 0 fi # Vertices if $nbp $nbp eval. ">begin(p = $indp); p+=i[#0,p] + 1; end(set('indc',p))" rm. +z[0] 0,$indp,0,{$indc-1} $nbp eval. ">begin(p = $indc); i[#0,p]==-128?(p+=prod(crop(#0,0,++p,1,3))); p+=3; end(set('indo',p))" rm. +z[0] 0,$indc,0,{$indo-1} +z[0] 0,$indo,0,100% else 0 0 0 fi rm[0] => $nm,${nm}_c1,${nm}_c2,${nm}_c3,${nm}_c4,${nm}_c5 } #@cli sprite3d #@cli : Convert selected images as 3D sprites. #@cli : Selected images with alpha channels are managed. #@cli : $ image.jpg sprite3d sprite3d : e[^-1] "Convert image$? as 3D sprites." foreach { nm={n} split_opacity i[0] (67.5;73.5;109.5;103.5;51.5;100.5;1;1;0;0;0;1;0;-128;{w};{h};{0,s}) y[1] if $!==2 (1) a y else /. 255 i.. (-128;{w};{h};{s}) y. fi a y => $nm } #@cli sprites3d : [sprite],_sprite_has_alpha_channel={ 0 | 1 } #@cli : Convert selected 3D objects as a sprite cloud. #@cli : Set 'sprite_has_alpha_channel' to 1 to make the last channel of the selected sprite be a transparency mask. #@cli : Default value: 'mask_has_alpha_channel=0'. #@cli : $ torus3d 100,20 image.jpg rescale2d[-1] ,64 100%,100% gaussian[-1] 30%,30% *[-1] 255 append[-2,-1] c \ # +sprites3d[0] [1],1 display_rgba[-2] sprites3d : check ${is_image_arg\ $1} skip ${2=0} e[^-1] "Convert image$? as 3D sprites clouds, using sprite $1 ("${"arg {1+!$2},with,without"}" alpha-channel)." repeat $! { if !{$>,i(0,7)} continue fi # Do nothing if 3D object is empty. pass$1 0 if !w empty3d rv[$>,-1] =>[$>] {n} rm. continue fi l[$>,-1] { s3d[0] N={1,@0} =[1] $N,0,1 rm[3-5] i[3] (1,0;1,{$N-1}) r[3] 2,$N,1,1,3 round[3] if $2 # With alpha-channel. if s==1 # Only alpha-channel. i.. 3,$N,1,1,200 /. 255 i.. (-128;{w};{h};1) if $N>1 1,{4*($N-1)},1,1,-128,0,0,0 fi else # Image + alpha. s. c,-{s-1} /. 255 i... (-128;{w};{h};{-2,s}) if $N>1 i.. 1,{4*($N-1)},1,1,-128,0,0,0 fi i.. (-128;{w};{h};1) if $N>1 1,{4*($N-1)},1,1,-128,0,0,0 fi fi else # Without alpha-channel. i.. (-128;{w};{h};{s}) y[-3,-1] if $N>1 1,{4*($N-1)},1,1,-128,0,0,0 fi 1,$N,1,1,1 fi y a y } } #@cli star3d : _nb_branches>0,0<=_thickness<=1 #@cli : Input 3D star at position `(0,0,0)`, with specified geometry. #@cli : Default values: 'nb_branches=5' and 'thickness=0.38'. #@cli : $ star3d , +primitives3d 1 color3d[-2] ${-rgb} +star3d : check "${1=5}>0 && ${2=0.38}>=0 && $2<=1" e[^-1] "Input 3D star, with $1 branches and thickness $2." N:=2*$1 ('CImg3d') +. 0.5 ({$N+1};$N) ({-pi/2};{3*pi/2}) r. 1,{$N+1},1,1,3 rows. 0,{h-2} +sin. cos.. a[-2,-1] x (1,1;$2,$2) *[-2,-1] z. 0,2 r. 3,{h+1},1,1,0 (3,$N,1,0;3,$N,$N,{$N-1}) r. 4,$N,1,1,3 round. =. 0,2,100% 3,$N,1,1,200 1,$N,1,1,1 y[-6,-4--2] a[-6--1] y => [3D\ star] #@cli streamline3d : x[%],y[%],z[%],_L>=0,_dl>0,_interpolation,_is_backward={ 0 | 1 },_is_oriented={ 0 | 1 } : \ # 'formula',x,y,z,_L>=0,_dl>0,_interpolation,_is_backward={ 0 | 1 },_is_oriented={ 0 | 1 } : (+) #@cli : Extract 3D streamlines from selected vector fields or from specified formula. #@cli : 'interpolation' can be { 0:nearest integer | 1:1st-order | 2:2nd-order | 3:4th-order }. #@cli : Default values: 'dl=0.1', 'interpolation=2', 'is_backward=0' and 'is_oriented=0'. #@cli : $ 100,100,100,3 rand -10,10 blur 3 repeat 300 { +streamline3d[0] {u(100)},{u(100)},{u(100)},1000,1,1 \ # color3d[-1] ${-rgb} } remove[0] box3d 100 primitives3d[-1] 1 add3d #@cli -3d : eq. to 'sub3d'. : (+) #@cli sub3d : tx,_ty,_tz : (+) #@cli : Shift selected 3D objects with the opposite of specified displacement vector. #@cli : (eq. to '3d'). #@cli : Default values: 'ty=tz=0'. #@cli : $ sphere3d 10 repeat 5 { +sub3d[-1] 10,{u(-10,10)},0 color3d[-1] ${-rgb} } add3d #@cli subdivide3d #@cli : Subdivide primitives of selected 3D objects. subdivide3d : e[^-1] "Subdivide primitives of selected 3D objects." foreach { s3d nbv,nbp={1,f2ui(crop())} r[2] 3,{2,h/3},1,1,-1 permute[2] zycx foreach[2--1] { ({h}) a y } # Convert features to dynamic arrays $nbp eval. ">begin(pp = pc = po = M = N = 0); # Read primitive material. n = i[#3,pp++]; # Type of primitive R = i[#4,pc++]; R==-128?( wc = f2ui(i[#4,pc++]); hc = i[#4,pc++]; sc = i[#4,pc++]; whsc = wc*hc*sc; ind_texture = whsc?wc:M; pc+=whsc; R = G = B = 200; ):(G = i[#4,pc++]; B = i[#4,pc++]); O = i[#5,po++]; O==-128?( # Ignore opacity mask wo = f2ui(i[#5,po++]); ho = i[#5,po++]; so = i[#5,po++]; whso = wo*ho*so; po+=whso; O = 1; ); # Subdivide primitives. n==2 || n==6?( # Colored/textured segment opp = pp; i0 = f2ui(i[#3,pp++]); i1 = f2ui(i[#3,pp++]); P0 = I[#2,i0]; P1 = I[#2,i1]; P01 = (P0 + P1)/2; i01 = da_size(#2); da_push(#2,P01); i0 = ui2f(i0); i1 = ui2f(i1); i01 = ui2f(i01); n==2?( # Colored segment copy(i[#3,opp],[ i0,i01 ]); da_push(#3,2,i01,i1); da_push(#4,R,G,B); ):( # Textured segment tx0 = i[#3,pp++]; ty0 = i[#3,pp++]; tx1 = i[#3,pp++]; ty1 = i[#3,pp++]; tx01 = round((tx0 + tx1)/2); ty01 = round((ty0 + ty1)/2); copy(i[#3,opp],[ i0,i01,tx0,ty0,tx01,ty01 ]); da_push(#3,6,i01,i1,tx01,ty01,tx1,ty1); whsc?(ui2fM = ui2f(M); da_push(#4,-128,ui2fM,0,0)): (ui2fwc = ui2f(wc); da_push(#4,-128,ui2fwc,0,0)); ); da_push(#5,O); N+=2): n==3 || n==9?( # Colored/textured triangle opp = pp; i0 = f2ui(i[#3,pp++]); i1 = f2ui(i[#3,pp++]); i2 = f2ui(i[#3,pp++]); P0 = I[#2,i0]; P1 = I[#2,i1]; P2 = I[#2,i2]; P01 = (P0 + P1)/2; P12 = (P1 + P2)/2; P20 = (P2 + P0)/2; i01 = da_size(#2); i12 = i01 + 1; i20 = i12 + 1; da_push(#2,P01,P12,P20); i0 = ui2f(i0); i1 = ui2f(i1); i2 = ui2f(i2); i01 = ui2f(i01); i12 = ui2f(i12); i20 = ui2f(i20); n==3?( # Colored triangle copy(i[#3,opp],[ i01,i12,i20 ]); da_push(#3,3,i0,i01,i20, 3,i01,i1,i12, 3,i20,i12,i2); da_push(#4,R,G,B, R,G,B, R,G,B); ):( # Textured triangle tx0 = i[#3,pp++]; ty0 = i[#3,pp++]; tx1 = i[#3,pp++]; ty1 = i[#3,pp++]; tx2 = i[#3,pp++]; ty2 = i[#3,pp++]; tx01 = round((tx0 + tx1)/2); ty01 = round((ty0 + ty1)/2); tx12 = round((tx1 + tx2)/2); ty12 = round((ty1 + ty2)/2); tx20 = round((tx2 + tx0)/2); ty20 = round((ty2 + ty0)/2); copy(i[#3,opp],[ i01,i12,i20,tx01,ty01,tx12,ty12,tx20,ty20 ]); da_push(#3,9,i0,i01,i20,tx0,ty0,tx01,ty01,tx20,ty20, 9,i01,i1,i12,tx01,ty01,tx1,ty1,tx12,ty12, 9,i20,i12,i2,tx20,ty20,tx12,ty12,tx2,ty2); whsc?(ui2fM = ui2f(M); da_push(#4,-128,ui2fM,0,0, -128,ui2fM,0,0, -128,ui2fM,0,0)): (ui2fwc = ui2f(wc); da_push(#4,-128,ui2fwc,0,0, -128,ui2fwc,0,0, -128,ui2fwc,0,0)); ); da_push(#5,O,O,O); N+=4): n==4 || n==12?( # Colored/textured quadrangle opp = pp; i0 = f2ui(i[#3,pp++]); i1 = f2ui(i[#3,pp++]); i2 = f2ui(i[#3,pp++]); i3 = f2ui(i[#3,pp++]); P0 = I[#2,i0]; P1 = I[#2,i1]; P2 = I[#2,i2]; P3 = I[#2,i3]; P01 = (P0 + P1)/2; P12 = (P1 + P2)/2; P23 = (P2 + P3)/2; P30 = (P3 + P0)/2; Pc = (P0 + P1 + P2 + P3)/4; i01 = da_size(#2); i12 = i01 + 1; i23 = i12 + 1; i30 = i23 + 1; ic = i30 + 1; da_push(#2,P01,P12,P23,P30,Pc); i0 = ui2f(i0); i1 = ui2f(i1); i2 = ui2f(i2); i3 = ui2f(i3); i01 = ui2f(i01); i12 = ui2f(i12); i23 = ui2f(i23); i30 = ui2f(i30); ic = ui2f(ic); n==4?( # Colored quadrangle copy(i[#3,opp],[ i0,i01,ic,i30 ]); da_push(#3,4,i01,i1,i12,ic, 4,ic,i12,i2,i23, 4,i30,ic,i23,i3); da_push(#4,R,G,B, R,G,B, R,G,B); ):( # Textured quadrangle tx0 = i[#3,pp++]; ty0 = i[#3,pp++]; tx1 = i[#3,pp++]; ty1 = i[#3,pp++]; tx2 = i[#3,pp++]; ty2 = i[#3,pp++]; tx3 = i[#3,pp++]; ty3 = i[#3,pp++]; tx01 = round((tx0 + tx1)/2); ty01 = round((ty0 + ty1)/2); tx12 = round((tx1 + tx2)/2); ty12 = round((ty1 + ty2)/2); tx23 = round((tx2 + tx3)/2); ty23 = round((ty2 + ty3)/2); tx30 = round((tx3 + tx0)/2); ty30 = round((ty3 + ty0)/2); txc = (tx0 + tx1 + tx2 + tx3)/4; tyc = (ty0 + ty1 + ty2 + ty3)/4; copy(i[#3,opp],[ i0,i01,ic,i30,tx0,ty0,tx01,ty01,txc,tyc,tx30,ty30 ]); da_push(#3,12,i01,i1,i12,ic,tx01,ty01,tx1,ty1,tx12,ty12,txc,tyc, 12,ic,i12,i2,i23,txc,tyc,tx12,ty12,tx2,ty2,tx23,ty23, 12,i30,ic,i23,i3,tx30,ty30,txc,tyc,tx23,ty23,tx3,ty3); whsc?(ui2fM = ui2f(M); da_push(#4,-128,ui2fM,0,0, -128,ui2fM,0,0, -128,ui2fM,0,0)): (ui2fwc = ui2f(wc); da_push(#4,-128,ui2fwc,0,0, -128,ui2fwc,0,0, -128,ui2fwc,0,0)); ); da_push(#5,O,O,O); N+=4): (pp+=n; ++N); ++M; end(i[#1,0] = ui2f(da_size(#2)); i[#1,1] = ui2f(N))" rm. da_freeze[2--1] nbv={2,h} permute[2] cyzx y[2] a y } #@cli superformula3d : resolution>1,m>=1,n1,n2,n3 #@cli : Input 2D superformula curve as a 3D object. #@cli : Default values: 'resolution=1024', 'm=8', 'n1=1', 'n2=5' and 'n3=8'. #@cli : $ superformula3d , +superformula3d : check "${1=1024}>1 && ${2=8}>=1" skip ${3=1},${4=5},${5=8} e[^-1] "Input 2D superformula curve, with resolution $1, m=$2 and (n1,n2,n3)=($3,$4,$5)." res:=round($1) # Define object header and vertices. (67.5;73.5;109.5;103.5;51.5;100.5;$res;{$res-1}) (0,{2*pi}) r. $res,1,1,1,3 . *. {$2/4} +sin. cos.. abs[-2,-1] ^.. $4 ^. $5 +[-2,-1] ^. {-1/$3} +sin.. cos... *. .. *[-3,-2] n[-2,-1] -1,1 a[-2,-1] y rows. 0,2 transpose. r. 1,{w*h},1,1,-1 # Define object primitives, colors and opacities. 1,{$res-1},1,1,2 (0;{$res-2}) r. 1,{$res-1},1,1,3 ++. 1 a[-3--1] x round. 1 r. 1,{w*h},1,1,-1 1,{3*($res-1)},1,1,200 1,{$res-1},1,1,1 a[-5--1] y => [3D\ superformula] #@cli surfels3d : 0<=_left_right_attenuation<=1,0<=_top_bottom_attenuation<=1,0<=_closer_further_attenuation<=1 #@cli : Convert selected images to 3D objects composed of 3D surfels (or 2D edgels for 2D images). #@cli : The binary shape is composed of all non-zero voxels. #@cli : The resulting 3D object is colored according to the color of non zero voxels. #@cli : Default values: 'left_right_attenuation=1', 'top_bottom_attenuation=1' and 'closer_further_attenuation=1'. #@cli : $ 100,100,100 = 1,40%,40%,40% = 1,60%,60%,60% distance 1 lt 30% blur 3 gt 50% surfels3d 0.5,0.75,1 surfels3d : check "${1=1}>0 && ${2=1}>0 && ${3=1}>0" e[^-1] "Generate 3D surfel object from binary shape$? with color attenuations (${1-3})." foreach { nm={n} if d==1 # 2D {[w,h]+1},1,1,-1 1,1,1,2x2 1,1,1,{0,s} => ind,pts,prims,cols eval[0] ">val = I; norm(val)?( const ind = $ind; const pts = $pts; const prims = $prims; const cols = $cols; nx = x + 1; ny = y + 1; !j(-1)?( # Left i0 = i(#ind,x,y); i0<0?(i0 = i(#ind,x,y) = da_size(#pts); da_push(#pts,[x - 0.5, y - 0.5])); i1 = i(#ind,x,ny); i1<0?(i1 = i(#ind,x,ny) = da_size(#pts); da_push(#pts,[x - 0.5, y + 0.5])); da_push(#prims,[i1,i0]); da_push(#cols,val*$1); ); !j(1)?( # Right i0 = i(#ind,nx,y); i0<0?(i0 = i(#ind,nx,y) = da_size(#pts); da_push(#pts,[x + 0.5, y - 0.5])); i1 = i(#ind,nx,ny); i1<0?(i1 = i(#ind,nx,ny) = da_size(#pts); da_push(#pts,[x + 0.5, y + 0.5])); da_push(#prims,[i0,i1]); da_push(#cols,val*$1); ); !j(0,-1)?( # Top i0 = i(#ind,x,y); i0<0?(i0 = i(#ind,x,y) = da_size(#pts); da_push(#pts,[x - 0.5, y - 0.5])); i1 = i(#ind,nx,y); i1<0?(i1 = i(#ind,nx,y) = da_size(#pts); da_push(#pts,[x + 0.5, y - 0.5])); da_push(#prims,[i0,i1]); da_push(#cols,val*$2); ); !j(0,1)?( # Bottom i0 = i(#ind,x,ny); i0<0?(i0 = i(#ind,x,ny) = da_size(#pts); da_push(#pts,[x - 0.5, y + 0.5])); i1 = i(#ind,nx,ny); i1<0?(i1 = i(#ind,nx,ny) = da_size(#pts); da_push(#pts,[x + 0.5, y + 0.5])); da_push(#prims,[i1,i0]); da_push(#cols,val*$2); ); ); I; end( resize(#pts,1,da_size(#pts),1,s#pts,0); resize(#prims,1,da_size(#prims),1,s#prims,0); resize(#cols,1,da_size(#cols),1,s#cols,0); )" if !w rm # Empty object else c[cols] 0,255 to_rgb[cols] k[pts,prims,cols] permute cyzx i[1] 1,{0,h},1,1,0 a[0,1] x i[1] 1,{1,h},1,1,2 a[1,2] x =>.. prims fi else # 3D {[w,h,d]+1},1,-1 1,1,1,3 1,1,1,4 1,1,1,{0,s} => ind,pts,prims,cols eval[0] ">val = I; norm(val)?( const ind = $ind; const pts = $pts; const prims = $prims; const cols = $cols; nx = x + 1; ny = y + 1; nz = z + 1; !j(-1)?( # Left i0 = i(#ind,x,y,z); i0<0?(i0 = i(#ind,x,y,z) = da_size(#pts); da_push(#pts,[x-0.5, y-0.5, z-0.5])); i1 = i(#ind,x,ny,z); i1<0?(i1 = i(#ind,x,ny,z) = da_size(#pts); da_push(#pts,[x-0.5, y+0.5, z-0.5])); i2 = i(#ind,x,ny,nz); i2<0?(i2 = i(#ind,x,ny,nz) = da_size(#pts); da_push(#pts,[x-0.5, y+0.5, z+0.5])); i3 = i(#ind,x,y,nz); i3<0?(i3 = i(#ind,x,y,nz) = da_size(#pts); da_push(#pts,[x-0.5, y-0.5, z+0.5])); da_push(#prims,[i0,i3,i2,i1]); da_push(#cols,val*$1); ); !j(1)?( # Right i0 = i(#ind,nx,y,z); i0<0?(i0 = i(#ind,nx,y,z) = da_size(#pts); da_push(#pts,[x+0.5, y-0.5, z-0.5])); i1 = i(#ind,nx,ny,z); i1<0?(i1 = i(#ind,nx,ny,z) = da_size(#pts); da_push(#pts,[x+0.5, y+0.5, z-0.5])); i2 = i(#ind,nx,ny,nz); i2<0?(i2 = i(#ind,nx,ny,nz) = da_size(#pts); da_push(#pts,[x+0.5, y+0.5, z+0.5])); i3 = i(#ind,nx,y,nz); i3<0?(i3 = i(#ind,nx,y,nz) = da_size(#pts); da_push(#pts,[x+0.5, y-0.5, z+0.5])); da_push(#prims,[i0,i1,i2,i3]); da_push(#cols,val*$1); ); !j(0,-1)?( # Top i0 = i(#ind,x,y,z); i0<0?(i0 = i(#ind,x,y,z) = da_size(#pts); da_push(#pts,[x-0.5, y-0.5, z-0.5])); i1 = i(#ind,nx,y,z); i1<0?(i1 = i(#ind,nx,y,z) = da_size(#pts); da_push(#pts,[x+0.5, y-0.5, z-0.5])); i2 = i(#ind,nx,y,nz); i2<0?(i2 = i(#ind,nx,y,nz) = da_size(#pts); da_push(#pts,[x+0.5, y-0.5, z+0.5])); i3 = i(#ind,x,y,nz); i3<0?(i3 = i(#ind,x,y,nz) = da_size(#pts); da_push(#pts,[x-0.5, y-0.5, z+0.5])); da_push(#prims,[i0,i1,i2,i3]); da_push(#cols,val*$2); ); !j(0,1)?( # Bottom i0 = i(#ind,x,ny,z); i0<0?(i0 = i(#ind,x,ny,z) = da_size(#pts); da_push(#pts,[x-0.5, y+0.5, z-0.5])); i1 = i(#ind,nx,ny,z); i1<0?(i1 = i(#ind,nx,ny,z) = da_size(#pts); da_push(#pts,[x+0.5, y+0.5, z-0.5])); i2 = i(#ind,nx,ny,nz); i2<0?(i2 = i(#ind,nx,ny,nz) = da_size(#pts); da_push(#pts,[x+0.5, y+0.5, z+0.5])); i3 = i(#ind,x,ny,nz); i3<0?(i3 = i(#ind,x,ny,nz) = da_size(#pts); da_push(#pts,[x-0.5, y+0.5, z+0.5])); da_push(#prims,[i0,i3,i2,i1]); da_push(#cols,val*$2); ); !j(0,0,-1)?( # Closer i0 = i(#ind,x,y,z); i0<0?(i0 = i(#ind,x,y,z) = da_size(#pts); da_push(#pts,[x-0.5, y-0.5, z-0.5])); i1 = i(#ind,nx,y,z); i1<0?(i1 = i(#ind,nx,y,z) = da_size(#pts); da_push(#pts,[x+0.5, y-0.5, z-0.5])); i2 = i(#ind,nx,ny,z); i2<0?(i2 = i(#ind,nx,ny,z) = da_size(#pts); da_push(#pts,[x+0.5, y+0.5, z-0.5])); i3 = i(#ind,x,ny,z); i3<0?(i3 = i(#ind,x,ny,z) = da_size(#pts); da_push(#pts,[x-0.5, y+0.5, z-0.5])); da_push(#prims,[i0,i3,i2,i1]); da_push(#cols,val*$3); ); !j(0,0,1)?( # Further i0 = i(#ind,x,y,nz); i0<0?(i0 = i(#ind,x,y,nz) = da_size(#pts); da_push(#pts,[x-0.5, y-0.5, z+0.5])); i1 = i(#ind,nx,y,nz); i1<0?(i1 = i(#ind,nx,y,nz) = da_size(#pts); da_push(#pts,[x+0.5, y-0.5, z+0.5])); i2 = i(#ind,nx,ny,nz); i2<0?(i2 = i(#ind,nx,ny,nz) = da_size(#pts); da_push(#pts,[x+0.5, y+0.5, z+0.5])); i3 = i(#ind,x,ny,nz); i3<0?(i3 = i(#ind,x,ny,nz) = da_size(#pts); da_push(#pts,[x-0.5, y+0.5, z+0.5])); da_push(#prims,[i0,i1,i2,i3]); da_push(#cols,val*$3); ); ); I; end( resize(#pts,1,da_size(#pts),1,s#pts,0); resize(#prims,1,da_size(#prims),1,s#prims,0); resize(#cols,1,da_size(#cols),1,s#cols,0); )" if !w rm # Empty object else c[cols] 0,255 to_rgb[cols] k[pts,prims,cols] permute cyzx i[1] 1,{1,h},1,1,4 a[1,2] x =>.. prims fi fi if !$! empty3d else i[0] ({'CImg3d'},{pts,h},{prims,h}:y) 1,100%,1,1,1 y a y fi => $nm } #@cli tensors3d : _radius_factor>=0,_shape={ 0:box | >=N:ellipsoid },_radius_min>=0 #@cli : Generate 3D tensor fields from selected images. #@cli : when 'shape'>0, it gives the ellipsoid shape precision. #@cli : Default values: 'radius_factor=1', 'shape=2' and 'radius_min=0.05'. #@cli : $ 6,6,6,9,"U = [x,y,z] - [w,h,d]/2; U/=norm(U); mul(U,U,3) + 0.3*eye(3)" tensors3d 0.8 tensors3d : check "${1=1}>=0 && isint(${2=2},0) && ${3=0.05}>=0" e[^-1] "Generate 3D tensor field(s) from image$?, with radius factor $1, "\ ${"if $2 u ellipsoid else u box fi"}" shape and radius min $3." foreach { # Check input image format. if s==1 100%,100%,100%,6,"[i#0,0,0,i#0,0,i#0]" k. elif s==3 100%,100%,100%,6,"[R#0,G#0,0,B#0,0,0]" k. elif s==4 100%,100%,100%,6,"[R#0,G#0,0,A#0,0,0]" k. elif s==9 100%,100%,100%,6,"I=I#0;[I[0],I[1],I[2],I[4],I[5],I[8]]" k. fi if s!=6 error[0--4] "Command '$0': Image '"{n}"' has an invalid size (spectrum="{s}")." fi # Estimate eigenvalues/eigenvectors. 100%,100%,100%,12," T = I(#0); M = [ T[0], T[1], T[2], T[1], T[3], T[4], T[2], T[4], T[5] ]; eig = eig(M); if (det(eig[3,9])<0, eig[3]*=-1; eig[4]*=-1; eig[5]*=-1); eig[0] = max(0,eig[0]); eig[1] = max(0,eig[1]); eig[2] = max(0,eig[2]); eig" k. # Create 3D object. if $2 sphere3d 1,{$2-1} else box3d 1 fi N,P:=i[6],i[7] siz:=h n3d. c3d. .x{0,whd-1} f[0] " const N = "$N"; const P = "$P"; const siz = "$siz"; eig = I; const d = size(eig); ind = 1 + x + w*y + wh*z; L = eig[0,3]; if (!max(L), # Empty tensor -> do not display i[#ind,6] = i[#ind,7] = 0; resize(#ind,1,8,1,1,0); _(else), L*=$1; L[0] = max($3,L[0]); L[1] = max($3,L[1]); L[2] = max($3,L[2]); R = eig[3,9]; anisotropy = sqrt(((L[0] - L[1])^2 + (L[1] - L[2])^2 + (L[2] - L[0])^2)/(2*(L[0]^2 + L[1]^2 + L[2]^2))); ref(crop(#ind,0,8,0,0,1,3*N,1,1),pts); pts *= resize(L,size(pts),0,2); pts = mul(pts,R,3); pts += resize([x,y,z],size(pts),0,2); draw(#ind,pts,0,8,0,0,1,size(pts),1,1); col0 = cut(255*anisotropy*abs([ R[0],R[1],R[2] ]) + (1 - anisotropy)*200,0,255); col = resize(col0,3*P,0,2); const off = siz - 4*P; draw(#ind,col,0,off,0,0,1,size(col),1,1); 0); I" rm[0] +3d } #@cli text_pointcloud3d : _"text1",_"text2",_smoothness #@cli : Input 3D text pointcloud from the two specified strings. #@cli : Default values: 'text1="text1"', 'text2="text2"' and 'smoothness=1'. #@cli : $ text_pointcloud3d "G'MIC","Rocks!" +text_pointcloud3d : skip "${1=text1}","${2=text2}",${3=1} e[^-1] "Input 3D pointcloud text object from strings '$1' and '$2', with smoothness $3." 0 t. "$1",0,0,53,1,1 0 t. "$2",0,0,53,1,1 mirror. y autocrop[-2,-1] 0 expand[-2,-1] xy,2 dilate[-2,-1] 2 permute. zyxc r[-2,-1] ${-max_whd} &[-2,-1] 100%,100% rand. 0,{-2,d-1} round. ri. .. f. z==i distance. 1 +. 1 +f. 1 rv[-2,-1] /[-2,-1] *. .. +dilate. 0,0,{d} ==[-2,-1] *. .. 1,100%,100% rand. 0,{-2,w-1} round. ri. .. f. x==i distance. 1 +. 1 +f. 1 rv[-2,-1] /[-2,-1] *. ... +dilate. 0,0,{d} ==[-2,-1] *[-3,-1] -|[-2,-1] b. $3 isosurface3d. 25% c3d. n3d. => "[3D text pointcloud]" #@cli text3d : text,_{ font_height>=0 | custom_font },_depth>0,_smoothness #@cli : Input a 3D text object from specified text. #@cli : Default values: 'font_height=53', 'depth=10' and 'smoothness=1.5'. #@cli : $ text3d "G'MIC as a\n3D logo!" +text3d : skip ${2=53},${3=10},${4=1.5} if "isexpr($2)" str="height $2" is_custom_font=0 else str="'$2'" is_custom_font=1 fi e[^-1] "Input 3D text object '$1', with font "$str", depth $3 and smoothness $4." 0 t. "$1",0,0,$2,1,1 autocrop. 0 r. 100%,100%,$3 expand. xyz,10 b. $4 isosurface3d. 40% rv3d. => "[3D text '$1']" #@cli t3d : eq. to 'texturize3d'. t3d : check ${"is_image_arg $1"}" && (!narg(${2=}) || "${"is_image_arg $2"}")" e[^-1] "Texturize 3D object$? with texture $1"\ ${"if narg($2) u \" and texture coordinates $2\" else u \"\" fi"}"." pass$1 0 slices. 0 if s==1 to_rgb. else channels. 0,2 fi if narg($2) pass$2 else 0 fi v + _texturize3d #@cli texturize3d : [ind_texture],_[ind_coords] #@cli : Texturize selected 3D objects with specified texture and coordinates. #@cli : (eq. to 't3d').\n #@cli : When '[ind_coords]' is omitted, default XY texture projection is performed. #@cli : Default value: 'ind_coords=(undefined)'. #@cli : $ image.jpg torus3d 100,30 texturize3d[-1] [-2] keep[-1] texturize3d : check ${"is_image_arg $1"}" && (!narg(${2=}) || "${"is_image_arg $2"}")" e[^-1] "Texturize 3D object$? with texture $1"\ ${"if narg($2) u \" and texture coordinates $2\" else u \"\" fi"}"." pass$1 0 slices. 0 if s==1 to_rgb. else channels. 0,2 fi if narg($2) pass$2 else 0 fi v + _$0 _texturize3d : repeat $!-2 { l[$>,-2,-1] { np:=f2ui(i[7]) s3d[0] # Retrieve texture coordinates for each vertex. if !w +r[2] 3,{2,round(h/3)},1,1,-1 s. x,3 rm. n.. 0,{6,w-1} n. 0,{6,h-1} a[-2,-1] x mv. -2 fi # Texturize 3D object 1,{5,2*h} 1,{5,3*h} 1,{5,h},1,1,1 eval " add_material() = ( ind_tex>=0?( # Shared texture copy(i[#-2,qc],[ -128,ind_tex,0,0 ],4); qc+=4 ):( # Non-shared texture qc + whds#6 + 4>=h(#-2)?resize(#-2,1,int(1.5*qc + whds#6 + 4),1,1,0,0); copy(i[#-2,qc],[ -128,w#6,h#6,s#6 ],4); qc+=4; copy(i[#-2,qc],i(#6),whds#6); qc+=whds#6; ind_tex = np; ); ); ind_tex = -1; for (pp = pc = qp = qc = np = 0, pp=h(#-3)?resize(#-3,1,int(1.5*qp + 13),1,1,0,0); qc + 3>=h(#-2)?resize(#-2,1,int(1.5*qc + 3),1,1,0,0); N = i[#3,pp++]; N==1?( # Colored point v0 = f2ui(i[#3,pp++]); tx0 = i(#7,0,v0); ty0 = i(#7,1,v0); R = i(#6,tx0,ty0,0,0); G = i(#6,tx0,ty0,0,1); B = i(#6,tx0,ty0,0,2); copy(i[#-3,qp],[ 1,ui2f(v0) ],2); qp+=2; copy(i[#-2,qc],[ R,G,B ],3); qc+=3; ):(N==2 || N==6)?( # Colored segment v0 = f2ui(i[#3,pp++]); tx0 = i(#7,0,v0); ty0 = i(#7,1,v0); v1 = f2ui(i[#3,pp++]); tx1 = i(#7,0,v1); ty1 = i(#7,1,v1); N==6?(pp+=4); copy(i[#-3,qp],[ 6,ui2f(v0),ui2f(v1),tx0,ty0,tx1,ty1 ],7); qp+=7; add_material(); ):(N==3 || N==9)?( # Colored triangle v0 = f2ui(i[#3,pp++]); tx0 = i(#7,0,v0); ty0 = i(#7,1,v0); v1 = f2ui(i[#3,pp++]); tx1 = i(#7,0,v1); ty1 = i(#7,1,v1); v2 = f2ui(i[#3,pp++]); tx2 = i(#7,0,v2); ty2 = i(#7,1,v2); N==9?(pp+=6); copy(i[#-3,qp],[ 9,ui2f(v0),ui2f(v1),ui2f(v2),tx0,ty0,tx1,ty1,tx2,ty2 ],10); qp+=10; add_material(); ):(N==4 || N==12)?( # Colored quadrangle v0 = f2ui(i[#3,pp++]); tx0 = i(#7,0,v0); ty0 = i(#7,1,v0); v1 = f2ui(i[#3,pp++]); tx1 = i(#7,0,v1); ty1 = i(#7,1,v1); v2 = f2ui(i[#3,pp++]); tx2 = i(#7,0,v2); ty2 = i(#7,1,v2); v3 = f2ui(i[#3,pp++]); tx3 = i(#7,0,v3); ty3 = i(#7,1,v3); N==12?(pp+=8); copy(i[#-3,qp],[ 12,ui2f(v0),ui2f(v1),ui2f(v2),ui2f(v3),tx0,ty0,tx1,ty1,tx2,ty2,tx3,ty3 ],13); qp+=13; add_material(); ):N==5?( # Colored sphere v0 = f2ui(i[#3,pp++]); tx0 = i(#7,0,v0); ty0 = i(#7,1,v0); v1 = f2ui(i[#3,pp++]); tx1 = i(#7,0,v1); ty1 = i(#7,1,v1); v2 = f2ui(i[#3,pp++]); pp+=2; (tx0+=tx1)/=2; (ty0+=ty1)/=2; R = i(#6,tx0,ty0,0,0); G = i(#6,tx0,ty0,0,1); B = i(#6,tx0,ty0,0,2); copy(i[#-3,qp],[ 5,ui2f(v0),ui2f(v1),ui2f(v2),0,0 ],6); qp+=6; copy(i[#-2,qc],[ R,G,B ],3); qc+=3; ); ); resize(#-3,1,qp,1,1,0,0); resize(#-2,1,qc,1,1,0,0)" rm[3-5] mv[-3--1] 3 if !w rm.. fi a[0-5] y } } rm[-2,-1] #@cli torus3d : _radius1,_radius2,_nb_subdivisions1>2,_nb_subdivisions2>2 #@cli : Input 3D torus at (0,0,0), with specified geometry. #@cli : Default values: 'radius1=1', 'radius2=0.3', 'nb_subdivisions1=24' and 'nb_subdivisions2=12'. #@cli : $ torus3d 10,3 +primitives3d 1 color3d[-2] ${-rgb} +torus3d : check "${1=1} && ${2=0.3} && ${3=24}>2 && ${4=12}>2" e[^-1] "Input 3D torus, with radii ($1,$2) and subdivisions ($3,$4)." # Header. nbp:=$3*$4 1,8,1,1,67.5,73.5,109.5,103.5,51.5,100.5,$nbp,{$4*$3} # Vertices. (0;{2*pi}) +y. x r.. 1,{$3+1},1,1,3 z.. 0,0,0,{$3-1} r. {$4+1},1,1,1,3 z. 0,{$4-1} +sin[-2,-1] cos[-4,-3] r[-4--1] $4,$3 *... $2 +... $1 *. $2 *[-4] ... *[-3,-2] y[-3--1] a[-3--1] x # Primitives. 1,$3,1,1,'y' *. $4 +shift. 0,-1 $4,1,1,1,'x' +shift. -1 r[-4--1] $4,$3 ++[-4,-1] +.. [-4] +[-5] ... +[-4,-3] y[-4--1] i[-5] 1,{h},1,1,4 a[-5--1] x # Colors / opacities. 3,{h},1,1,200 1,{h},1,1,1 y[-4--2] a[-5--1] y => [3D\ torus] #@cli triangle3d : x0,y0,z0,x1,y1,z1,x2,y2,z2 #@cli : Input 3D triangle at specified coordinates. #@cli : $ repeat 100 { a:=$>*pi/50 triangle3d 0,0,0,0,0,3,{cos(3*$a)},{sin(2*$a)},0 color3d[-1] ${-rgb} } add3d +triangle3d : e[^-1] "Input 3D triangle ($1,$2,$3)-($4,$5,$6)-($7,$8,$9)." 1,25,1,1,67.5,73.5,109.5,103.5,51.5,100.5,3,1,${1-9},3,0,1,2,200,200,200,1 => [3D\ triangle] #@cli volume3d #@cli : Transform selected 3D volumetric images as 3D parallelepipedic objects. #@cli : $ image.jpg animate blur,0,5,30 append z volume3d volume3d : e[^-1] "Transform image$? as 3D parallelepipedic objects." foreach { w,h,d={w},{h},{d} +slices[0] 0 +columns[0] 0 permute. zyxc mirror. x +slices[0] 100% mirror. x +columns[0] 100% permute. zyxc +rows[0] 100% permute. xzyc +rows[0] 0 permute. xzyc mirror. y rm[0] image6cube3d *3d $w,$h,$d } #@cli voxelize3d : _max_resolution>0,_fill_interior={ 0 | 1 },_preserve_colors={ 0 | 1 } #@cli : Convert selected 3D objects as 3D volumetric images of binary voxels, using 3D mesh rasterization. #@cli : Default values: 'max_resolution=128', 'fill_interior=1' and 'preserve_colors=0'. voxelize3d : check "${1=128}>0 && isbool(${2=1}) && isbool(${3=0})" check3d s0,s1,t0,t1="",", interior filling","scalar","color" e[^-1] "Voxelize 3D object$?, with max resolution $1"$s$2" and "$t$3" output." foreach { nm={n} p3d 2 +boundingbox3d l. { s3d store[2] bbpts rm } # Get exact bounding box (for objects containing 3d sphere primitives) s3d nbp={1,@1} k[2,3,4] => pts,prims,cols l[pts] { $bbpts a y # Merge bounding box vertices r 3,{h/3},1,1,-1 s x w,h,d:="const dx = iM#0 - im#0; const dy = iM#1 - im#1; const dz = iM#2 - im#2; M = max(dx,dy,dz); S = round([ dx, dy, dz ]*$1/M); [ max(S[0],1), max(S[1],1), max(S[2],1) ]" n[0] 0,{$w-1} n[1] 0,{$h-1} n[2] 0,{$d-1} round a c } if $3 l[cols] { r 3,{h/3},1,1,-1 permute zycx +norm1 ==. 0 + } else rm[cols] fi $w,$h,$d => out $nbp eval. "> begin( p = 0; label = 2; P0 = P1 = P2 = P3 = vector3(); rasterize_point(P) = ( i(#$out,P#) = label; ); rasterize_segment(P0,P1) = ( l01 = max(1,abs(P1# - P0#)); repeat (l01 + 1,l, ln = l/l01; Q = round(lerp(P0#,P1#,ln)); i(#$out,Q) = label; ); ); rasterize_triangle(P0,P1,P2) = ( N = cross(P1# - P0#,P2# - P0#); # Normal vector rasterize_segment(P0#,P1#); # Hack: Ensure correct jonction between adjacent triangles rasterize_segment(P0#,P2#); rasterize_segment(P1#,P2#); norm(N)?( # Non-colinear vertices S = -dot(N,P0#); # Affine shift proj_plane = argmax(abs(N)); # Best 2D projection plane !proj_plane?( # YZ-backprojection Q0 = [ P0#[1],P0#[2] ]; Q1 = [ P1#[1],P1#[2] ]; Q2 = [ P2#[1],P2#[2] ]; _rasterize_triangle(Q0,Q1,Q2,round((-N[1]*a - N[2]*b - S)/N[0]),a,b); ):proj_plane==1?( # XZ-backprojection Q0 = [ P0#[0],P0#[2] ]; Q1 = [ P1#[0],P1#[2] ]; Q2 = [ P2#[0],P2#[2] ]; _rasterize_triangle(Q0,Q1,Q2,a,round((-N[0]*a - N[2]*b - S)/N[1]),b); ):( # XY-backprojection Q0 = [ P0#[0],P0#[1] ]; Q1 = [ P1#[0],P1#[1] ]; Q2 = [ P2#[0],P2#[1] ]; _rasterize_triangle(Q0,Q1,Q2,a,b,round((-N[0]*a - N[1]*b - S)/N[2])); ); ); ); _rasterize_triangle(P0,P1,P2,projX,projY,projZ) = ( # Back-project 2D triangle P0#[1]>P1#[1]?swap(P0#,P1#); P0#[1]>P2#[1]?swap(P0#,P2#); P1#[1]>P2#[1]?swap(P1#,P2#); db01 = P1#[1] - P0#[1]; m1db01 = max(1,db01); db02 = P2#[1] - P0#[1]; m1db02 = max(1,db02); db12 = P2#[1] - P1#[1]; m1db12 = max(1,db12); repeat (db01,l, b = P0#[1] + l; a0 = round(lerp(P0#[0],P1#[0],l/m1db01)); a1 = round(lerp(P0#[0],P2#[0],l/m1db02)); a0>a1?swap(a0,a1); for (a = a0, a<=a1, ++a, i(#$out,projX#,projY#,projZ#) = label); ); repeat (db12 + 1,l, b = P1#[1] + l; a0 = round(lerp(P1#[0],P2#[0],l/m1db12)); a1 = round(lerp(P0#[0],P2#[0],(db01 + l)/m1db02)); a0>a1?swap(a0,a1); for (a = a0, a<=a1, ++a, i(#$out,projX#,projY#,projZ#) = label); ); ); rasterize_sphere(P0,P1) = ( P = (P0# + P1#)/2; R = norm(P1# - P0#)/2; xm = floor(P[0] - R); xM = ceil(P[0] + R); ym = floor(P[1] - R); yM = ceil(P[1] + R); zm = floor(P[2] - R); zM = ceil(P[2] + R); for (z = zm, z<=zM, ++z, for (y = ym, y<=yM, ++y, for (x = xm, x<=xM, ++x, norm([x,y,z] - P)<=R?(i(#$out,x,y,z) = label); ); ); ); ); ); n = i[#$prims,p++]; # Get coordinates of vertices in voxel space. P0 = I[#$pts,f2ui(i[#$prims,p++])]; n>1?(P1 = I[#$pts,f2ui(i[#$prims,p++])]); n>2?(P2 = I[#$pts,f2ui(i[#$prims,p++])]); n>3?(P3 = I[#$pts,f2ui(i[#$prims,p++])]); n>4?(p+=n - 4); # Draw primitives. n==1?rasterize_point(P0): n==2 || n==6?rasterize_segment(P0,P1): n==3 || n==9?rasterize_triangle(P0,P1,P2): n==4 || n==12?(rasterize_triangle(P0,P1,P2); rasterize_triangle(P0,P2,P3)): n==5?rasterize_sphere(P0,P1); $3?++label" rm. if $2 expand[out] xyz,1 flood[out] 0,0,0,0,0,1,-1 +<[out] 2 +[out,-1] shrink[out] xyz,1 fi # Fill interior if $3 r[cols] 1,{cols,h+2},1,100%,0,0,0,1 point[cols] 0,1,0,1,1 map[out] [cols] # Output colors else >[out] 0 # Output scalars fi k[out] => $nm } #@cli weird3d : _resolution>0 #@cli : Input 3D weird object at (0,0,0), with specified resolution. #@cli : Default value: 'resolution=32'. #@cli : $ weird3d 48 +primitives3d 1 color3d[-2] ${-rgb} +weird3d : skip ${1=32} e[^-1] "Input 3D weird object, with resolution $1." isosurface3d '" T = 1.61803399; 2 - (cos(x + T*y) + cos(x - T*y) + cos(y + T*z) + cos(y - T*z) + cos(z - T*x) + cos(z + T*x))"',\ 0,-4.7,-4.7,-4.7,4.7,4.7,4.7,$1,$1,$1 c3d. n3d. => [3D\ weird] #------------------------------- # #@cli :: Flow Control # #------------------------------- # #@cli { : (+) # #@cli : Do nothing. # #@cli : This command is be used in cunjunction with `}` (shortcut for ''done''), for script decoration. # #@cli : (eq. to ','). # #@cli : $ sample duck,dog,cat foreach { repeat 3 { sharpen 100 } } #@cli ap : eq. to 'apply_parallel'. ap : _gmic_s="$?" v + _apply_parallel "$*" #@cli apply_parallel : "command" #@cli : Apply specified command on each of the selected images, by parallelizing it for all image of the list. #@cli : (eq. to 'ap'). #@cli : $ image.jpg +mirror x +mirror y apply_parallel "blur 3" apply_parallel : _gmic_s="$?" v + _$0 "$*" _apply_parallel : e[0--3] "Apply command '$*' on all image"$_gmic_s" in parallel, using "$_cpus" threads." if $!" && "narg("$*") m "_ap : foreach { $* if $! k[0] else 0 fi }" N:=min($!,$_cpus) commands= sep= repeat $N { commands=$commands${sep}_ap[$>--1:$N] sep=, } parallel $commands um _ap fi #@cli apc : eq. to 'apply_parallel_channels'. apc : _gmic_s="$?" v + _apply_parallel_channels "$*" #@cli apply_parallel_channels : "command" #@cli : Apply specified command on each of the selected images, by parallelizing it for all channel #@cli : of the images independently. #@cli : (eq. to 'apc'). #@cli : $ image.jpg apply_parallel_channels "blur 3" apply_parallel_channels : _gmic_s="$?" v + _$0 "$*" _apply_parallel_channels : e[0--3] "Apply command '$*' on all channels of image"$_gmic_s" in parallel, using "$_cpus" threads." N=$! repeat $N { s$>={$>,s} } s c ap "$1" repeat $N { a[$>-{$>+${s$>}-1}] c } #@cli apo : eq. to 'apply_parallel_overlap'. apo : check "${2=0}>=0 && isint(${3=0},0)" _gmic_s="$?" v + _apply_parallel_overlap "$1",${2--1} #@cli apply_parallel_overlap : "command",overlap[%],nb_threads={ 0:auto | 1 | 2 | 4 | 8 | 16 } #@cli : Apply specified command on each of the selected images, by parallelizing it on 'nb_threads' #@cli : overlapped sub-images. #@cli : (eq. to 'apo').\n #@cli : 'nb_threads' must be a power of 2. #@cli : Default values: 'overlap=0','nb_threads=0'. #@cli : $ image.jpg +apply_parallel_overlap "smooth 500,0,1",1 apply_parallel_overlap : check "${2=0}>=0 && isint(${3=0},0)" _gmic_s="$?" v + _$0 "$1",${2--1} _apply_parallel_overlap : check "${2=0}>=0 && isint(${3=0},0)" N:=$3?max(1,round($3)):$_cpus N:=2^int(log2(min(16,$N))) e[0--3] "Apply parallelized command '$1' on image"$_gmic_s", with overlap $2 and "$N" threads." __apo_exception="" m "_check1 : if $!!=1 rm 0 __apo_exception=\"Command 'apply_parallel_overlap': Specified command '$1' changes the size of the image stack.\" fi" foreach { _apply_parallel_overlap$N "$1",$2 } um _check1 _apply_parallel_overlap1 : $1 if narg($__apo_exception) error[0--12] $__apo_exception fi _apply_parallel_overlap2 : if w>=h ovx:=round(ispercentage($2)?w*$2:$2) w2:=int(w/2) +z[0] {$w2-$ovx},100% z[0] 0,{$w2+$ovx-1} parallel "l[0] { $1 _check1 }","l[1] { $1 _check1 }" if narg($__apo_exception) error[0--12] $__apo_exception fi z[0] 0,{0,w-1-$ovx} z[1] $ovx,100% a x else ovy:=round(ispercentage($2)?h*$2:$2) h2:=int(h/2) +rows[0] {$h2-$ovy},100% rows[0] 0,{$h2+$ovy-1} parallel "l[0] { $1 _check1 }","l[1] { $1 _check1 }" if narg($__apo_exception) error[0--12] $__apo_exception fi rows[0] 0,{0,h-1-$ovy} rows[1] $ovy,100% a y fi _apply_parallel_overlap4 : if max(w,h)/min(w,h)>=3 _apply_parallel_overlap2 "_apply_parallel_overlap2 \"$1\",$2",$2 else ovx:=round(ispercentage($2)?w*$2:$2) w2:=int(w/2) ovy:=round(ispercentage($2)?h*$2:$2) h2:=int(h/2) +z[0] {$w2-$ovx},0,100%,{$h2+$ovy-1} +z[0] 0,{$h2-$ovy},{$w2+$ovx-1},100% +z[0] {$w2-$ovx},{$h2-$ovy},100%,100% z[0] 0,0,{$w2+$ovx-1},{$h2+$ovy-1} parallel "l[0] { $1 _check1 }","l[1] { $1 _check1 }","l[2] { $1 _check1 }","l[3] { $1 _check1 }" if narg($__apo_exception) error[0--12] $__apo_exception fi z[0] 0,0,{0,w-1-$ovx},{0,h-1-$ovy} z[1] $ovx,0,100%,{1,h-1-$ovy} z[2] 0,$ovy,{2,w-1-$ovx},100% z[3] $ovx,$ovy,100%,100% a[0,1] x a[1,2] x a y fi _apply_parallel_overlap8 : _apply_parallel_overlap2 "_apply_parallel_overlap4 \"$1\",$2",$2 _apply_parallel_overlap16 : _apply_parallel_overlap2 "_apply_parallel_overlap8 \"$1\",$2",$2 #@cli at : eq. to 'apply_tiles'. at : check "${2=10%}>0 && ${3=10%}>0 && ${4=10%}>0 && ${5=0}>=0 && ${6=0}>=0 && ${7=0}>=0 && isint(${8=1},0,3)" _gmic_s="$?" v + _apply_tiles "$1",${2--1} #@cli apply_tiles : "command",_tile_width[%]>0,_tile_height[%]>0,_tile_depth[%]>0,_overlap_width[%]>=0,\ # _overlap_height[%]>=0,_overlap_depth[%]>=0,_boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Apply specified command on each tile (neighborhood) of the selected images, eventually with overlapping tiles. #@cli : (eq. to 'at'). #@cli : Default values: 'tile_width=tile_height=tile_depth=10%','overlap_width=overlap_height=overlap_depth=0' \ # and 'boundary_conditions=1'. #@cli : $ image.jpg +equalize[0] 256 +apply_tiles[0] "equalize 256",16,16,1,50%,50% apply_tiles : check "${2=10%}>0 && ${3=10%}>0 && ${4=10%}>0 && ${5=0}>=0 && ${6=0}>=0 && ${7=0}>=0 && isint(${8=1},0,3)" _gmic_s="$?" v + _$0 "$1",${2--1} _apply_tiles : e[0--3] "Apply command '$1' on $2x$3x$4 tiles of image$?, with overlaps ($5,$6,$7) and "\ ${"arg0 $8,dirichlet,neumann,periodic,mirror"}" boundary conditions." foreach { bw:=cut(round(ispercentage($2)?w*$2:$2),1,w) bh:=cut(round(ispercentage($3)?h*$3:$3),1,h) bd:=cut(round(ispercentage($4)?d*$4:$4),1,d) ow:=round(ispercentage($5)?$bw*$5:$5) oh:=round(ispercentage($6)?$bh*$6:$6) od:=round(ispercentage($7)?$bd*$7:$7) sw:=cut($bw-$ow,1,$bw) sh:=cut($bh-$oh,1,$bh) sd:=cut($bd-$od,1,$bd) 100%,100%,100%,{s+1} # Reconstructed image + weights if $ow>0" || "$oh>0" || "$od>0 l[] { # Generate gaussian weight in case of overlap $bw,1,1 1,$bh,1 1,1,$bd = 1,50%,50%,50% distance 1 /[0] {0.3*$bw} /[1] {0.3*$bh} /[2] {0.3*$bd} sqr * -1 exp r $bw,$bh,$bd,1 * } else $bw,$bh,$bd,1,1 fi $bw,$bh,$bd,[0] m "__at : $1 k. r "$bw,$bh,$bd,{0,s},0 eval " ref(crop(#2),mask); for (z = 0, z0:with specified timeout (in seconds) } #@cli : Apply a command with a timeout. #@cli : Set variable '$_is_timeout' to '1' if timeout occurred, '0' otherwise. #@cli : Default value: 'timeout=20'. apply_timeout : check "${2=20}>=0" if $2<=0 e[0--3] "Apply command '$1' on image$?, with no timeout." $1 _is_timeout=0 else e[0--3] "Apply command '$1' on image$?, with a timeout of $2 seconds." l[] { ('$/') id:=is rm } l { +store initial __done$id=0 __is_timeout$id=0 parallel "$1 __done"$id"=1",\ "l[] do if $|-"$|">$2 __is_timeout"$id"=1 error \"\" elif $__done"$id" break fi wait 100 while 1 done" onfail rm $initial _is_timeout=0 if ${__is_timeout$id} _is_timeout=1 error[0--5] "Command '$0': Time out ($2 seconds) for command '$1'." else error[0--5] "Command '$0': "${} fi } fi #@cli check : condition : (+) #@cli : Evaluate specified condition and display an error message if evaluated to false. #@cli check3d : _is_full_check={ 0 | 1 } : (+) #@cli : Check validity of selected 3D vector objects, and display an error message #@cli : if one of the selected images is not a valid 3D vector object. #@cli : Full 3D object check is slower but more precise. #@cli : Default value: 'is_full_check=1'. # check_display : calling_command_name # Check if a display is available, and throw an error otherwise. check_display : skip "${1=check_display}" if !{*,u} error[0--3] "Command '$1': No display available." fi # check_opencv : calling_command_name # Check is OpenCV features are available, and throw an error otherwise. check_opencv : skip "${1=check_opencv}" if find(['$_flags'],'opencv')<0 error[0--3] "Command '$1': No OpenCV features available. "\ "Your G'MIC interpreter has not been compiled with OpenCV support." fi #@cli continue : (+) #@cli : Go to end of current 'do...while', 'for...done', 'foreach...done', 'local...done' or 'repeat...done' block. #@cli : $ image.jpg repeat 10 blur 1 if 1==1 continue fi deform 10 done #@cli break : (+) #@cli : Break current 'do...while', 'for...done', 'foreach...done', 'local...done' or 'repeat...done' block. #@cli : $ image.jpg repeat 10 blur 1 if 1==1 break fi deform 10 done #@cli do : (+) #@cli : Start a 'do...while' block. #@cli : $ image.jpg luminance i:=ia+2 do set 255,{u(100)}%,{u(100)}% while ia<$i #@cli done : (+) #@cli : End a 'for/foreach/local/repeat...done' block, and go to associated 'for/foreach/repeat' if iterations remain. #@cli : (eq. to '}'). #@cli elif : condition : (+) #@cli : Start a 'elif...[else]...fi' block if previous 'if' was not verified #@cli : and test if specified condition holds #@cli : 'condition' is a mathematical expression, whose evaluation is interpreted as { 0:false | other:true }.. #@cli : $$ https://gmic.eu/tutorial/iffi #@cli else : (+) #@cli : Execute following commands if previous 'if' or 'elif' conditions failed. #@cli : $$ https://gmic.eu/tutorial/iffi #@cli fi : (+) #@cli : End a 'if...[elif]...[else]...fi' block. #@cli : (eq. to 'fi').\n #@cli : $$ https://gmic.eu/tutorial/iffi #@cli error : message : (+) #@cli : Print specified error message on the standard error (stderr) and exit interpreter, except #@cli : if error is caught by a 'onfail' command. #@cli : Command selection (if any) stands for displayed call stack subset instead of image indices. #@cli eval : expression : (+) #@cli : Evaluate specified math expression. #@cli : - If no command selection is specified, the expression is evaluated once and its result is set to status. #@cli : - If command selection is specified, the evaluation is looped over selected images. Status is unchanged. \ # In this case, 'eval' is similar to ''fill'' without assigning the image values. #@cli x : eq. to 'exec'. : (+) #@cli exec : _is_verbose={ 0 | 1 },"command" : (+) #@cli : Execute external command using a system call. #@cli : The status value is then set to the error code returned by the system call. #@cli : If 'is_verbose=1', the executed command is allowed to output on stdout/stderr. #@cli : (eq. to 'x'). #@cli : Default value: 'is_verbose=1'. #@cli xo : eq. to 'exec_out'. xo : v + _exec_out $"*" #@cli exec_out : _mode,"command" #@cli : Execute external command using a system call, and return resulting `stdout` and/or `stderr`. #@cli : 'mode' can be { 0:stdout | 1:stderr | 2:stdout+stderr }. exec_out : v + _exec_out $"*" _exec_out : l[] { if "isint($1,0,2)" mode=$1 command="${2--1}" else mode=0 command="$*" fi onfail mode=0 command="$*" } s0,s1,s2=stdout,stderr,stdout+stderr e[0--3] "Execute external command '"$command"', and return "${s$mode}" output." filename_rand filename=${} if !$mode x $command" > "$filename elif $mode==1 x $command" 2> "$filename else x $command" >"$filename" 2>&1" fi it $filename u {t} rm. delete $filename #@cli for : condition : (+) #@cli : Start a `for...done` block. #@cli : $ image.jpg rescale2d ,32 400,400,1,3 x=0 for $x<400 image[1] [0],$x,$x x+=40 done #@cli foreach : (+) #@cli : Start a 'foreach...done' block, that iterates over all images in the selection, \ # with a separate local environment for each one. #@cli : $ sample colorful,earth,duck,dog foreach[^2] +blur 10 sub normalize 0,255 done #@cli if : condition : (+) #@cli : Start a 'if...[elif]...[else]...fi' block and test if specified condition holds. #@cli : 'condition' is a mathematical expression, whose evaluation is interpreted as { 0:false | other:true }. #@cli : $ image.jpg if ia<64 add 50% elif ia<128 add 25% elif ia<192 sub 25% else sub 50% fi cut 0,255 #@cli : $$ https://gmic.eu/tutorial/iffi #@cli l : eq. to 'local'. : (+) #@cli local : (+) #@cli : Start a 'local...[onfail]...done' block, with selected images. #@cli : (eq. to 'l'). #@cli : $ image.jpg local[] 300,300,1,3 rand[0] 0,255 blur 4 sharpen 1000 done #@cli : $ image.jpg +local repeat 3 { deform 20 } done #@cli : $$ https://gmic.eu/oldtutorial/_local #@cli mutex : index,_action={ 0:unlock | 1:lock } : (+) #@cli : Lock or unlock specified mutex for multi-threaded programming. #@cli : A locked mutex can be unlocked only by the same thread. All mutexes are unlocked by default. #@cli : 'index' designates the mutex index, in [0,255]. #@cli : Default value: 'action=1'. #@cli noarg : (+) #@cli : Used in a custom command, 'noarg' tells the command that its argument list have not been used #@cli : finally, and so they must be evaluated next in the G'MIC pipeline, just as if the custom #@cli : command takes no arguments at all. #@cli : Use this command to write a custom command which can decide if it takes arguments or not. #@cli onfail : (+) #@cli : Execute following commands when an error is encountered in the body of the 'local...done' block. #@cli : The status value is set with the corresponding error message. #@cli : $ image.jpg +local blur -3 onfail mirror x done #@cli parallel : _wait_threads,"command1","command2",... : (+) #@cli : Execute specified commands in parallel, each in a different thread. #@cli : Parallel threads share the list of images. #@cli : 'wait_threads' can be { 0:when current environment ends | 1:immediately }. #@cli : Default value: 'wait_threads=1'. #@cli : $ image.jpg [0] parallel "blur[0] 3","mirror[1] c" # The implementation below allows to use parallel as a regular command with selections. parallel : skip "${1=},${2=},${3=},${4=},${5=},${6=},${7=},${8=},${9=},${10=},${11=},${12=},${13=},${14=},${15=}" if inrange($1,0,2) e[0--3] "Execute "{$#-1}" commands '${2--1}' in parallel on image$?." else e[0--3] "Execute "$#" commands '$*' in parallel on image$?." fi parallel $"*" #@cli progress : 0<=value<=100 : -1 : (+) #@cli : Set the progress index of the current processing pipeline. #@cli : This command is useful only when G'MIC is used by an embedding application. #@cli q : eq. to 'quit'. : (+) #@cli quit : (+) #@cli : Quit G'MIC interpreter. #@cli : (eq. to 'q'). #@cli repeat : nb_iterations : (+) #@cli : Start 'nb_iterations' iterations of a `repeat...done` block. #@cli : 'nb_iterations' is a mathematical expression that will be evaluated. #@cli : $ image.jpg split y repeat $! n=$> shift[$n] $<,0,0,0,2 done append y #@cli : $ image.jpg mode3d 2 repeat 4 imagecube3d rotate3d 1,1,0,40 snapshot3d 400,1.4 done #@cli : $$ https://gmic.eu/oldtutorial/_repeat #@cli return : (+) #@cli : Return from current custom command. #@cli rprogress : 0<=value<=100 | -1 | "command",0<=value_min<=100,0<=value_max<=100 #@cli : Set the progress index of the current processing pipeline (relatively to #@cli : previously defined progress bounds), or call the specified command with #@cli : specified progress bounds. rprogress : skip ${2=""} if !narg($_progress_bounds) _progress_bounds=0,100 fi m:=arg(-2,$_progress_bounds) M:=arg(-1,$_progress_bounds) if $#==2&&!narg($2) # 1 argument -> Set progress bar. e[0--3] "Set relative progress index to $1%." progress {$1<0?-1:min(100,max(0,$m+($M-$m)*$1%))} elif $#==3 # 3 arguments -> Call command with specified bounds. nm:=min($2,$-1) nM:=max($2,$-1) e[0--3] "Call command '$1' with progress bounds ["$nm,$nM"]." progress $m _progress_bounds=$_progress_bounds,{$m+$nm*($M-$m)/100},{$m+$nM*($M-$m)/100} # Push new bounds. run "$1" progress $M ($_progress_bounds) _progress_bounds={@0--3} rm. # Pop bounds. else error[0--3] "Command '$0': Invalid argument '$*'." fi #@cli run : "G'MIC pipeline" #@cli : Run specified G'MIC pipeline. run : m "__run : $*" v=$^ v {$v+2} __run v $v um __run #@cli skip : item : (+) #@cli : Do nothing but skip specified item. #@cli u : eq. to 'status'. : (+) #@cli status : status_string : (+) #@cli : Set the current status. Used to define a returning value from a function. #@cli : (eq. to 'u'). #@cli : $ image.jpg command "foo : u0=Dark u1=Bright status ${u{ia>=128}}" text_outline ${-foo},2,2,23,2,1,255 #@cli while : condition : (+) #@cli : End a 'do...while' block and go back to associated 'do' if specified condition holds. #@cli : 'condition' is a mathematical expression, whose evaluation is interpreted as { 0:false | other:true }. #------------------------- # #@cli :: Neural Networks # #------------------------- #@cli nn_lib : #@cli : Return the list of library functions that has to be included in a math expression,\ # in order to use the neural network library. nn_lib : u " #--------------------------------------------------------------------------- # Definition of the library environment variables and convenience functions. #--------------------------------------------------------------------------- begin( const nn_nb_threads_max = n; const nn_is_training = $_nn_is_training!=0; # works also when $_nn_is_training is nan ('undefined'). ); begin_t( nn_thread_id = t; nn_nb_threads_used = nn_thread_id + 1; nn_batch_size = 0; ); ++nn_batch_size; end( merge(nn_nb_threads_used,max); merge(nn_batch_size,+); ); nn_display(L) = display(L#,L#_width,L#_height,L#_depth,L#_spectrum); nn_display(L,M) = display(L#,M#_width,M#_height,M#_depth,M#_spectrum); nn_store(L) = store(['L#'],L#,L#_width,L#_height,L#_depth,L#_spectrum); nn_store(L,M) = store(['L#'],L#,M#_width,M#_height,M#_depth,M#_spectrum); #-------------------------------------------- # Activation functions and their derivatives. #-------------------------------------------- # elu: Exponential Linear Unit. #------------------------------- nn_activation_elu(z) = (z<0?exp(z) - 1:z); nn_activation_d_elu(z) = (z<0?exp(z):1); # expr: custom expression. #------------------------- "{['$_expr']!=0}"?( begin( _nn_tab_expr = expr('z = lerp(-32,32,x/(w-1)); "$_expr"',65537); _nn_tab_d_expr = vector(#size(_nn_tab_expr)); const _nn_delta_expr = 64/(size(_nn_tab_expr) - 1); fill(_nn_tab_d_expr,_nn_k, _nn_pk = _nn_k?_nn_k - 1:_nn_k; _nn_nk = _nn_k=0); # sigmoid : Sigmoid (a.k.a Logistic). #------------------------------------ nn_activation_sigmoid(z) = (0.5 + 0.5*tanh(z/2)); nn_activation_d_sigmoid(z) = (_nn_sig = nn_activation_sigmoid(z); _nn_sig*(1 - _nn_sig)); # sin: Sine. #----------- nn_activation_sin(z) = sin(z); nn_activation_d_sin(z) = cos(z); # sinc: Sinc. #------------ nn_activation_sinc(z) = sinc(z); nn_activation_d_sinc(z) = ((cos(z) - sinc(z))/(1e-10+z)); # sqr: Square. #------------- nn_activation_sqr(z) = (z^2); nn_activation_d_sqr(z) = (2*z); # sqrt: sqrt(|z|). #----------------- nn_activation_sqrt(z) = (sqrt(abs(z))); nn_activation_d_sqrt(z) = (0.5*sign(x)/sqrt(max(1e-8,abs(z)))); # steps. #------- const _nn_steps_S0 = isnan($_S0)?2:$_S0; const _nn_steps_S1 = isnan($_S1)?0.5:$_S1; const _nn_steps_b = (1 - _nn_steps_S1)/2; const _nn_steps_eps = _nn_steps_b/max(1e-10,_nn_steps_S0 - _nn_steps_S1); nn_activation_steps(z) = ( _nn_steps_zi = round(z); _nn_steps_zf = z - _nn_steps_zi; _nn_steps_zi + (_nn_steps_zf<-_nn_steps_eps?_nn_steps_S1*_nn_steps_zf - _nn_steps_b: _nn_steps_zf<_nn_steps_eps?_nn_steps_S0*_nn_steps_zf: _nn_steps_S1*_nn_steps_zf + _nn_steps_b); ); nn_activation_d_steps(z) = ( _nn_steps_zi = round(z); _nn_steps_zf = z - _nn_steps_zi; abs(_nn_steps_zf)<_nn_steps_eps?_nn_steps_S0:_nn_steps_S1; ); # swish. #-------- nn_activation_swish(z) = (z*nn_activation_sigmoid(z)); nn_activation_d_swish(z) = ( _nn_sig = nn_activation_sigmoid(z); _nn_swi = z*_nn_sig; _nn_swi + _nn_sig*(1 - _nn_swi) ); # tanh. #------ nn_activation_tanh(z) = (tanh(z)); nn_activation_d_tanh(z) = (1 - tanh(z)^2); # waves. #------- nn_activation_waves(z,a,b,c) = ( _nn_waves_zi = round(z); _nn_waves_zf = z - _nn_waves_zi; _nn_waves_zi + a*_nn_waves_zf^3 + b*sign(_nn_waves_zf)*abs(_nn_waves_zf)^2 + c*_nn_waves_zf; ); nn_activation_d_waves(z,a,b,c) = ( _nn_waves_zi = round(z); _nn_waves_zf = z - _nn_waves_zi; 3*a*_nn_waves_zf^2 + 2*b*abs(_nn_waves_zf) + c; ); const _nn_waves0_S0 = isnan($_S0)?2:$_S0; const _nn_waves0_S1 = isnan($_S1)?-1:$_S1; const _nn_waves0_a = 4*_nn_waves0_S0 + 4*_nn_waves0_S1 - 8; const _nn_waves0_b = -4*_nn_waves0_S0 - 2*_nn_waves0_S1 + 6; const _nn_waves0_c = _nn_waves0_S0; nn_activation_waves0(z) = nn_activation_waves(z,_nn_waves0_a,_nn_waves0_b,_nn_waves0_c); nn_activation_d_waves0(z) = nn_activation_d_waves(z,_nn_waves0_a,_nn_waves0_b,_nn_waves0_c); nn_activation_waves1(z) = nn_activation_waves(z,-6,3,1); # Waves 1 nn_activation_d_waves1(z) = nn_activation_d_waves(z,-6,3,1); nn_activation_waves2(z) = nn_activation_waves(z,-4,0,2); # Waves 2 nn_activation_d_waves2(z) = nn_activation_d_waves(z,-4,0,2); nn_activation_waves3(z) = nn_activation_waves(z,-2,-3,3); # Waves 2 nn_activation_d_waves3(z) = nn_activation_d_waves(z,-2,-3,3); nn_activation_waves4(z) = nn_activation_waves(z,0,-6,4); # Waves 4 nn_activation_d_waves4(z) = nn_activation_d_waves(z,0,-6,4); # zpsinz. #-------- nn_activation_zpsinz(z) = (z + 0.5*sin(2*pi*z)); nn_activation_d_zpsinz(z) = (1 + pi*cos(2*pi*z)); #--------- # Trainer. #--------- nn_trainer_init_backward(T,L,O,S) = ( const T#_ind = $T#; nn_iteration = i[##T#_ind,0]; nn_learning_rate = i[##T#_ind,1]; nn_learning_rate0 = i[##T#_ind,2]; nn_previous_loss = i[##T#_ind,3]; nn_best_loss = i[##T#_ind,4]; nn_optimizer_#O#_init(); nn_scheduler_#S#_init(); ); nn_trainer_update(T,L,S) = ( nn_scheduler_#S#_update(#T,#L); i[##T#_ind,0] = ++nn_iteration; i[##T#_ind,1] = nn_learning_rate; i[##T#_ind,3] = L#; i[##T#_ind,4] = min(nn_best_loss,L#); ); #------------- # Schedulers. #------------- # Constant. #---------- nn_scheduler_constant_init() = 0; nn_scheduler_constant_update(T,L) = 0; # Linear Decrease. #----------------- nn_scheduler_linear_init() = 0; nn_scheduler_linear_update(T,L) = ( nn_learning_rate = lerp(nn_learning_rate0,1e-8,min(1,nn_iteration/1000)); ); # Exponential Decrease. #---------------------- nn_scheduler_exponential_init() = 0; nn_scheduler_exponential_update(T,L) = ( nn_learning_rate*=0.999; ); # Adaptive Learning Rate. #------------------------ nn_scheduler_adaptive_init(T) = ( nn_adaptive_trend_moment = i(##T#_ind,0,2,0,0); nn_adaptive_nb_decreases = i(##T#_ind,1,2,0,0); nn_adaptive_nb_increases = i(##T#_ind,2,2,0,0); ); nn_scheduler_adaptive_update(T,L) = ( nn_iteration?( # Compute trend: percentage of loss decrease(<0) or increase(>0). nn_adaptive_trend = (L# - nn_previous_loss)/max(1e-8,nn_previous_loss); nn_adaptive_trend_moment = lerp(sign(nn_adaptive_trend),nn_adaptive_trend_moment,0.75); # Adapt strategy regarding to trend. nn_adaptive_trend>=0.3?( # Large local loss increase -> drastically reduce learning rate nn_learning_rate = max(0.25*nn_learning_rate,1e-8); nn_adaptive_nb_increases = nn_adaptive_nb_decreases = 0; ):nn_adaptive_trend_moment>=0?( # Global loss increase ++nn_adaptive_nb_increases; nn_adaptive_nb_decreases = 0; nn_adaptive_nb_increases>=2?( # Global loss increase -> slightly reduce learning rate nn_learning_rate = max(0.75*nn_learning_rate,1e-8); nn_adaptive_nb_increases = 0; ); ):nn_adaptive_trend_moment<0?( # Global loss decrease ++nn_adaptive_nb_decreases; nn_adaptive_nb_decreases>=4?( # Global loss decrease -> slightly increase learning rate nn_learning_rate = min(nn_learning_rate*1.15,0.1); nn_adaptive_nb_decreases = 0; ); ); ); copy(i(##T#_ind,0,2,0,0),[ nn_adaptive_trend_moment,nn_adaptive_nb_decreases,nn_adaptive_nb_increases ]); ); #------------ # Optimizers. #------------ # Generic. #--------- nn_optimizer_generic_update_normalize(O,P,learning_mode) = ( P#_is_channelwise?( # Channel-by-channel normalization draw(##P#_ind,batch_#P#_avg,2,0,1,h##P#_ind,0.01); draw(##P#_ind,batch_#P#_var,3,0,1,h##P#_ind,0.01); learning_mode&1?( nn_optimizer_#O#_update_weights(P#_alpha); draw(##P#_ind,P#_alpha,0,0,1,h##P#_ind); ); learning_mode&2?( nn_optimizer_#O#_update_weights(P#_beta); draw(##P#_ind,P#_beta,1,0,1,h##P#_ind); ); ):( # Global normalization i[##P#_ind,2] = lerp(P#_avg,batch_#P#_avg,0.01); i[##P#_ind,3] = lerp(P#_var,batch_#P#_var,0.01); learning_mode&1?( nn_optimizer_#O#_update_weights(P#_alpha); i[##P#_ind,0] = P#_alpha; ); learning_mode&2?( nn_optimizer_#O#_update_weights(P#_beta); i[##P#_ind,1] = P#_beta; ); ); ); # Stochastic gradient descent. #----------------------------- nn_optimizer_sgd_init() = 0; nn_optimizer_sgd_update_weights(W) = ( W#-=nn_learning_rate*batch_d#W; ); nn_optimizer_sgd_update_conv_or_fc(P,learning_mode) = ( learning_mode&1?( nn_optimizer_sgd_update_weights(P#_weights); draw(##P#_ind,P#_weights,0,0,w##P#_ind - 1,h##P#_ind); ); learning_mode&2?( nn_optimizer_sgd_update_weights(P#_biases); draw(##P#_ind,P#_biases,w##P#_ind - 1,0,1,h##P#_ind); ); ); nn_optimizer_sgd_update_conv2d(P,learning_mode) = ( learning_mode?nn_optimizer_sgd_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_sgd_update_conv3d(P,learning_mode) = ( learning_mode?nn_optimizer_sgd_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_sgd_update_fc(P,learning_mode) = ( learning_mode?nn_optimizer_sgd_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_sgd_update_normalize(P,learning_mode) = ( nn_optimizer_generic_update_normalize(sgd,#P,#learning_mode); ); nn_optimizer_sgd_update_nlfc(P,learning_mode) = ( learning_mode&1?( nn_optimizer_sgd_update_weights(P#_weights); draw(##P#_ind,P#_weights,0,0,w##P#_ind/2,h##P#_ind); ); learning_mode&2?( nn_optimizer_sgd_update_weights(P#_biases); draw(##P#_ind,P#_biases,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); ); ); # RMSprop optimizer. #------------------- nn_optimizer_rmsprop_init() = ( const nn_optimizer_rmsprop_beta = 0.9; ); nn_optimizer_rmsprop_update_weights(W) = ( d#W#_g2 = lerp(batch_d#W^2,d#W#_g2,nn_optimizer_rmsprop_beta); W#-=nn_learning_rate*batch_d#W/sqrt(1e-8 + d#W#_g2); ); nn_optimizer_rmsprop_update_conv_or_fc(P,learning_mode) = ( const P#_ind_g2 = $P#_g2; learning_mode&1?( d#P#_weights_g2 = crop(##P#_ind_g2,0,0,w##P#_ind - 1,h##P#_ind); nn_optimizer_rmsprop_update_weights(P#_weights); draw(##P#_ind,P#_weights,0,0,w##P#_ind - 1,h##P#_ind); draw(##P#_ind_g2,d#P#_weights_g2,0,0,w##P#_ind - 1,h##P#_ind); ); learning_mode&2?( d#P#_biases_g2 = crop(##P#_ind_g2,w##P#_ind - 1,0,1,h##P#_ind); nn_optimizer_rmsprop_update_weights(P#_biases); draw(##P#_ind,P#_biases,w##P#_ind - 1,0,1,h##P#_ind); draw(##P#_ind_g2,d#P#_biases_g2,w##P#_ind - 1,0,1,h##P#_ind); ); ); nn_optimizer_rmsprop_update_conv2d(P,learning_mode) = ( learning_mode?nn_optimizer_rmsprop_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_rmsprop_update_conv3d(P,learning_mode) = ( learning_mode?nn_optimizer_rmsprop_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_rmsprop_update_fc(P,learning_mode) = ( learning_mode?nn_optimizer_rmsprop_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_rmsprop_update_normalize(P,learning_mode) = ( const P#_ind_g2 = $P#_g2; learning_mode?( P#_is_channelwise?( # Channel-by-channel normalization d#P#_alpha_g2 = crop(##P#_ind_g2,0,1); d#P#_beta_g2 = crop(##P#_ind_g2,1,1); ):( # Global normalization d#P#_alpha_g2 = i[##P#_ind_g2,0]; d#P#_beta_g2 = i[##P#_ind_g2,1]; ); ); nn_optimizer_generic_update_normalize(rmsprop,#P,#learning_mode); ); nn_optimizer_rmsprop_update_nlfc(P,learning_mode) = ( const P#_ind_g2 = $P#_g2; learning_mode&1?( d#P#_weights_g2 = crop(##P#_ind_g2,0,0,w##P#_ind/2,h##P#_ind); nn_optimizer_rmsprop_update_weights(P#_weights); draw(##P#_ind,P#_weights,0,0,w##P#_ind/2,h##P#_ind); draw(##P#_ind_g2,d#P#_weights_g2,0,0,w##P#_ind/2,h##P#_ind); ); learning_mode&2?( d#P#_biases_g2 = crop(##P#_ind_g2,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); nn_optimizer_rmsprop_update_weights(P#_biases); draw(##P#_ind,P#_biases,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); draw(##P#_ind_g2,d#P#_biases_g2,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); ); ); # Adam optimizer. #---------------- nn_optimizer_adam_init() = ( begin( const nn_optimizer_adam_beta1 = 0.9; const nn_optimizer_adam_beta2 = 0.999; nn_optimizer_adam_beta1_t = nn_iteration>200?0:nn_optimizer_adam_beta1^(nn_iteration + 1); nn_optimizer_adam_beta2_t = nn_iteration>200?0:nn_optimizer_adam_beta2^(nn_iteration + 1); nn_optimizer_adam_alpha_t = sqrt(1 - nn_optimizer_adam_beta2_t)/(1 - nn_optimizer_adam_beta1_t); ); ); nn_optimizer_adam_update_weights(W) = ( d#W#_m = lerp(batch_d#W,d#W#_m,nn_optimizer_adam_beta1); d#W#_v = lerp(batch_d#W^2,d#W#_v,nn_optimizer_adam_beta2); W#-=nn_learning_rate*nn_optimizer_adam_alpha_t*d#W#_m/sqrt(1e-8 + d#W#_v); ); nn_optimizer_adam_update_conv_or_fc(P,learning_mode) = ( const P#_ind_m = $P#_m; const P#_ind_v = $P#_v; learning_mode&1?( d#P#_weights_m = crop(##P#_ind_m,0,0,w##P#_ind - 1,h##P#_ind); d#P#_weights_v = crop(##P#_ind_v,0,0,w##P#_ind - 1,h##P#_ind); nn_optimizer_adam_update_weights(P#_weights); draw(##P#_ind,P#_weights,0,0,w##P#_ind - 1,h##P#_ind); draw(##P#_ind_m,d#P#_weights_m,0,0,w##P#_ind - 1,h##P#_ind); draw(##P#_ind_v,d#P#_weights_v,0,0,w##P#_ind - 1,h##P#_ind); ); learning_mode&2?( d#P#_biases_m = crop(##P#_ind_m,w##P#_ind - 1,0,1,h##P#_ind); d#P#_biases_v = crop(##P#_ind_v,w##P#_ind - 1,0,1,h##P#_ind); nn_optimizer_adam_update_weights(P#_biases); draw(##P#_ind,P#_biases,w##P#_ind - 1,0,1,h##P#_ind); draw(##P#_ind_m,d#P#_biases_m,w##P#_ind - 1,0,1,h##P#_ind); draw(##P#_ind_v,d#P#_biases_v,w##P#_ind - 1,0,1,h##P#_ind); ); ); nn_optimizer_adam_update_conv2d(P,learning_mode) = ( learning_mode?nn_optimizer_adam_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_adam_update_conv3d(P,learning_mode) = ( learning_mode?nn_optimizer_adam_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_adam_update_fc(P,learning_mode) = ( learning_mode?nn_optimizer_adam_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_adam_update_normalize(P,learning_mode) = ( const P#_ind_m = $P#_m; const P#_ind_v = $P#_v; learning_mode?( P#_is_channelwise?( # Channel-by-channel normalization d#P#_alpha_m = crop(##P#_ind_m,0,1); d#P#_beta_m = crop(##P#_ind_m,1,1); d#P#_alpha_v = crop(##P#_ind_v,0,1); d#P#_beta_v = crop(##P#_ind_v,1,1); ):( # Global normalization d#P#_alpha_m = i[##P#_ind_m,0]; d#P#_beta_m = i[##P#_ind_m,1]; d#P#_alpha_v = i[##P#_ind_v,0]; d#P#_beta_v = i[##P#_ind_v,1]; ); ); nn_optimizer_generic_update_normalize(adam,#P,#learning_mode); ); nn_optimizer_adam_update_nlfc(P,learning_mode) = ( learning_mode?( const P#_ind_m = $P#_m; const P#_ind_v = $P#_v; learning_mode&1?( d#P#_weights_m = crop(##P#_ind_m,0,0,w##P#_ind/2,h##P#_ind); d#P#_weights_v = crop(##P#_ind_v,0,0,w##P#_ind/2,h##P#_ind); nn_optimizer_adam_update_weights(P#_weights); draw(##P#_ind,P#_weights,0,0,w##P#_ind/2,h##P#_ind); draw(##P#_ind_m,d#P#_weights_m,0,0,w##P#_ind/2,h##P#_ind); draw(##P#_ind_v,d#P#_weights_v,0,0,w##P#_ind/2,h##P#_ind); ); learning_mode&2?( d#P#_biases_m = crop(##P#_ind_m,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); d#P#_biases_v = crop(##P#_ind_v,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); nn_optimizer_adam_update_weights(P#_biases); draw(##P#_ind,P#_biases,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); draw(##P#_ind_m,d#P#_biases_m,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); draw(##P#_ind_v,d#P#_biases_v,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); ); ); ); # Adamax optimizer. #------------------ nn_optimizer_adamax_init() = ( begin( const nn_optimizer_adamax_beta1 = 0.9; const nn_optimizer_adamax_beta2 = 0.999; nn_optimizer_adamax_ombeta1_t = nn_iteration>200?1:(1 - nn_optimizer_adamax_beta1^(nn_iteration + 1)); ); ); nn_optimizer_adamax_update_weights(W) = ( d#W#_m = lerp(batch_d#W,d#W#_m,nn_optimizer_adamax_beta1); d#W#_v = vmax(abs(batch_d#W),nn_optimizer_adamax_beta2*d#W#_v); W#-=(nn_learning_rate/nn_optimizer_adamax_ombeta1_t)*d#W#_m/sqrt(1e-8 + d#W#_v); ); nn_optimizer_adamax_update_conv_or_fc(P,learning_mode) = ( const P#_ind_m = $P#_m; const P#_ind_v = $P#_v; learning_mode&1?( d#P#_weights_m = crop(##P#_ind_m,0,0,w##P#_ind - 1,h##P#_ind); d#P#_weights_v = crop(##P#_ind_v,0,0,w##P#_ind - 1,h##P#_ind); nn_optimizer_adamax_update_weights(P#_weights); draw(##P#_ind,P#_weights,0,0,w##P#_ind - 1,h##P#_ind); draw(##P#_ind_m,d#P#_weights_m,0,0,w##P#_ind - 1,h##P#_ind); draw(##P#_ind_v,d#P#_weights_v,0,0,w##P#_ind - 1,h##P#_ind); ); learning_mode&2?( d#P#_biases_m = crop(##P#_ind_m,w##P#_ind - 1,0,1,h##P#_ind); d#P#_biases_v = crop(##P#_ind_v,w##P#_ind - 1,0,1,h##P#_ind); nn_optimizer_adamax_update_weights(P#_biases); draw(##P#_ind,P#_biases,w##P#_ind - 1,0,1,h##P#_ind); draw(##P#_ind_m,d#P#_biases_m,w##P#_ind - 1,0,1,h##P#_ind); draw(##P#_ind_v,d#P#_biases_v,w##P#_ind - 1,0,1,h##P#_ind); ); ); nn_optimizer_adamax_update_conv2d(P,learning_mode) = ( learning_mode?nn_optimizer_adamax_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_adamax_update_conv3d(P,learning_mode) = ( learning_mode?nn_optimizer_adamax_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_adamax_update_fc(P,learning_mode) = ( learning_mode?nn_optimizer_adamax_update_conv_or_fc(#P,learning_mode); ); nn_optimizer_adamax_update_normalize(P,learning_mode) = ( const P#_ind_m = $P#_m; const P#_ind_v = $P#_v; learning_mode?( P#_is_channelwise?( # Channel-by-channel normalization d#P#_alpha_m = crop(##P#_ind_m,0,1); d#P#_beta_m = crop(##P#_ind_m,1,1); d#P#_alpha_v = crop(##P#_ind_v,0,1); d#P#_beta_v = crop(##P#_ind_v,1,1); ):( # Global normalization d#P#_alpha_m = i[##P#_ind_m,0]; d#P#_beta_m = i[##P#_ind_m,1]; d#P#_alpha_v = i[##P#_ind_v,0]; d#P#_beta_v = i[##P#_ind_v,1]; ); ); nn_optimizer_generic_update_normalize(adamax,#P,#learning_mode); ); nn_optimizer_adamax_update_nlfc(P,learning_mode) = ( const P#_ind_m = $P#_m; const P#_ind_v = $P#_v; learning_mode&1?( d#P#_weights_m = crop(##P#_ind_m,0,0,w##P#_ind/2,h##P#_ind); d#P#_weights_v = crop(##P#_ind_v,0,0,w##P#_ind/2,h##P#_ind); nn_optimizer_adamax_update_weights(P#_weights); draw(##P#_ind,P#_weights,0,0,w##P#_ind/2,h##P#_ind); draw(##P#_ind_m,d#P#_weights_m,0,0,w##P#_ind/2,h##P#_ind); draw(##P#_ind_v,d#P#_weights_v,0,0,w##P#_ind/2,h##P#_ind); ); learning_mode&2?( d#P#_biases_m = crop(##P#_ind_m,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); d#P#_biases_v = crop(##P#_ind_v,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); nn_optimizer_adamax_update_weights(P#_biases); draw(##P#_ind,P#_biases,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); draw(##P#_ind_m,d#P#_biases_m,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); draw(##P#_ind_v,d#P#_biases_v,w##P#_ind/2,0,w##P#_ind/2,h##P#_ind); ); ); #---------------- # Network layers. #---------------- # add: Add two inputs. #--------------------- nn_add_init_forward(OUT,IN) = ( const OUT#_width = IN#_width; const OUT#_height = IN#_height; const OUT#_depth = IN#_depth; const OUT#_spectrum = IN#_spectrum; ); nn_add_forward(OUT,IN0,IN1) = ( OUT# = IN0# + IN1#; ); nn_add_backward(OUT,IN0,IN1) = ( d#IN0 = d#IN1 = d#OUT/2; ); # append: Append two inputs as a new output. #-------------------------------------------- nn_append_init_forward(OUT,IN0,IN1) = ( const OUT#_width = IN0#_width; const OUT#_height = IN0#_height; const OUT#_depth = IN0#_depth; const OUT#_spectrum = IN0#_spectrum + IN1#_spectrum; ); nn_append_forward(OUT,IN0,IN1) = ( OUT# = vector(#size(IN0#) + size(IN1#)); copy(OUT#,IN0#,size(IN0#)); copy(OUT#[size(IN0#)],IN1#,size(IN1#)); ); nn_append_backward(OUT,IN0,IN1) = ( d#IN0 = d#OUT[0,size(IN0#)]; d#IN1 = d#OUT[size(IN0#),size(IN1#)]; ); # avgpool2d : 2D average pooling. #-------------------------------- nn_avgpool2d_init_forward(OUT,IN,patch_size) = ( const OUT#_width = int(IN#_width/patch_size); const OUT#_height = int(IN#_height/patch_size); const OUT#_depth = IN#_depth; const OUT#_spectrum = IN#_spectrum; ); nn_avgpool2d_forward(OUT,IN) = ( OUT# = resize(IN#, IN#_width,IN#_height,IN#_depth,IN#_spectrum, OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, 2); ); nn_avgpool2d_backward(OUT,IN) = ( d#IN = resize(d#OUT, OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, IN#_width,IN#_height,IN#_depth,IN#_spectrum, 1); ); # avgpool3d : 3D average pooling. #-------------------------------- nn_avgpool3d_init_forward(OUT,IN,patch_size) = ( const OUT#_width = int(IN#_width/patch_size); const OUT#_height = int(IN#_height/patch_size); const OUT#_depth = int(IN#_depth/patch_size); const OUT#_spectrum = IN#_spectrum; ); nn_avgpool3d_forward(OUT,IN) = nn_avgpool2d_forward(OUT#,IN#); # Same as avgpool2d nn_avgpool3d_backward(OUT,IN) = nn_avgpool2d_backward(OUT#,IN#); # Same as avgpool2d # clone: Duplicate input as two new outputs. #------------------------------------------- nn_clone_init_forward(OUT0,OUT1,IN) = ( const OUT0#_width = IN#_width; const OUT0#_height = IN#_height; const OUT0#_depth = IN#_depth; const OUT0#_spectrum = IN#_spectrum; const OUT1#_width = IN#_width; const OUT1#_height = IN#_height; const OUT1#_depth = IN#_depth; const OUT1#_spectrum = IN#_spectrum; ); nn_clone_forward(OUT0,OUT1,IN) = ( OUT0# = IN#; OUT1# = IN#; ); nn_clone_backward(OUT0,OUT1,IN) = ( d#IN = d#OUT0 + d#OUT1; ); # conv2d: 2D convolutional layer. #-------------------------------- nn_conv2d_init_forward(OUT,IN,size,stride,dilation,shrink,boundary_conditions) = ( const OUT#_ind = $OUT#; const OUT#_shrink = shrink; const OUT#_width = int(IN#_width/stride) - 2*OUT#_shrink; const OUT#_height = int(IN#_height/stride) - 2*OUT#_shrink; const OUT#_depth = IN#_depth; const OUT#_spectrum = h##OUT#_ind; const OUT#_whd = OUT#_width*OUT#_height*OUT#_depth; const OUT#_kernel_size = size; const OUT#_kernel_size2 = OUT#_kernel_size^2; const OUT#_kernel_center = OUT#_kernel_size - 1 - int(OUT#_kernel_size/2); const OUT#_stride = stride; const OUT#_dilation = dilation; const OUT#_boundary_conditions = boundary_conditions; OUT#_weights = crop(##OUT#_ind,0,0,w##OUT#_ind - 1,OUT#_spectrum); OUT#_biases = crop(##OUT#_ind,w##OUT#_ind - 1,0,1,OUT#_spectrum); ); nn_conv2d_forward(OUT,IN) = ( OUT# = convolve(IN#, IN#_width,IN#_height,IN#_depth,IN#_spectrum, OUT#_weights, OUT#_kernel_size,OUT#_kernel_size,1,IN#_spectrum*OUT#_spectrum, OUT#_boundary_conditions,0,2, OUT#_kernel_center,OUT#_kernel_center,0, OUT#_shrink,OUT#_shrink,0, OUT#_width - 1 + OUT#_shrink,OUT#_height - 1 + OUT#_shrink,OUT#_depth - 1, OUT#_stride,OUT#_stride,1, OUT#_dilation,OUT#_dilation,1); repeat (OUT#_spectrum,_nn_k,copy(OUT#[_nn_k*OUT#_whd],OUT#_biases[_nn_k],OUT#_whd,1,0,-1)); # Add biases ); nn_conv2d_init_backward(OUT,IN) = ( batch_d#OUT#_weights = vector(#size(OUT#_weights)); batch_d#OUT#_biases = vector(#size(OUT#_biases)); ); nn_conv2d_backward(OUT,IN) = ( # Compute dLOSS/dIN. const OUT#_offset = -int(OUT#_shrink/OUT#_stride); const OUT#_invstride = 1/OUT#_stride; const OUT#_dons = OUT#_dilation/OUT#_stride; d#IN = vector(#size(IN#)); repeat (IN#_spectrum,_nn_k, draw(d#IN,IN#_width,IN#_height,IN#_depth,IN#_spectrum, correlate(d#OUT, OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, crop(OUT#_weights,w##OUT#_ind - 1,OUT#_spectrum,1,1, _nn_k*OUT#_kernel_size2,0,OUT#_kernel_size2,OUT#_spectrum), OUT#_kernel_size,OUT#_kernel_size,1,OUT#_spectrum, 0,0,2, OUT#_kernel_center,OUT#_kernel_center,0, OUT#_offset,OUT#_offset,0, IN#_width - 1 + OUT#_offset,IN#_height - 1 + OUT#_offset,IN#_depth - 1, OUT#_invstride,OUT#_invstride,1, OUT#_dons,OUT#_dons,1), 0,0,0,_nn_k,IN#_width,IN#_height,IN#_depth,1); ); # Compute dLOSS/dbiases. d#OUT#_biases = vector(#size(OUT#_biases)); fill(d#OUT#_biases,_nn_k,sum(d#OUT[_nn_k*OUT#_whd,OUT#_whd])); batch_d#OUT#_biases+=d#OUT#_biases; # Compute dLOSS/dweights. const OUT#_dcenter = OUT#_dilation*OUT#_kernel_center; const OUT#_kend = OUT#_kernel_size - 1 + OUT#_offset; d#OUT#_weights = vector(#size(OUT#_weights)); repeat (OUT#_spectrum,_nn_k, draw(d#OUT#_weights, OUT#_kernel_size,OUT#_kernel_size,1,OUT#_spectrum*IN#_spectrum, correlate(crop(d#OUT,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, 0,0,0,_nn_k,OUT#_width,OUT#_height,OUT#_depth,1), OUT#_width,OUT#_height,OUT#_depth,1, IN#, IN#_width,IN#_height,IN#_depth,IN#_spectrum, 0,0,1, OUT#_dcenter,OUT#_dcenter,0, OUT#_offset,OUT#_offset,0, OUT#_kend,OUT#_kend,0, OUT#_dons,OUT#_dons,1, OUT#_invstride,OUT#_invstride,1), 0,0,0,_nn_k*IN#_spectrum, OUT#_kernel_size,OUT#_kernel_size,1,IN#_spectrum); ); batch_d#OUT#_weights+=d#OUT#_weights; ); nn_conv2d_end_backward(OUT) = ( merge(batch_d#OUT#_weights,+); merge(batch_d#OUT#_biases,+); batch_d#OUT#_weights/=nn_batch_size; batch_d#OUT#_biases/=nn_batch_size; ); # conv3d: 3D convolutional layer. #-------------------------------- nn_conv3d_init_forward(OUT,IN,size,stride,dilation,shrink,boundary_conditions) = ( const OUT#_ind = $OUT#; const OUT#_shrink = shrink; const OUT#_width = int(IN#_width/stride) - 2*OUT#_shrink; const OUT#_height = int(IN#_height/stride) - 2*OUT#_shrink; const OUT#_depth = int(IN#_depth/stride) - 2*OUT#_shrink; const OUT#_spectrum = h##OUT#_ind; const OUT#_whd = OUT#_width*OUT#_height*OUT#_depth; const OUT#_kernel_size = size; const OUT#_kernel_size3 = OUT#_kernel_size^3; const OUT#_kernel_center = OUT#_kernel_size - 1 - int(OUT#_kernel_size/2); const OUT#_stride = stride; const OUT#_dilation = dilation; const OUT#_boundary_conditions = boundary_conditions; OUT#_weights = crop(##OUT#_ind,0,0,w##OUT#_ind - 1,OUT#_spectrum); OUT#_biases = crop(##OUT#_ind,w##OUT#_ind - 1,0,1,OUT#_spectrum); ); nn_conv3d_forward(OUT,IN) = ( OUT# = convolve(IN#, IN#_width,IN#_height,IN#_depth,IN#_spectrum, OUT#_weights, OUT#_kernel_size,OUT#_kernel_size,OUT#_kernel_size,IN#_spectrum*OUT#_spectrum, OUT#_boundary_conditions,0,2, OUT#_kernel_center,OUT#_kernel_center,OUT#_kernel_center, OUT#_shrink,OUT#_shrink,OUT#_shrink, OUT#_width - 1 + OUT#_shrink,OUT#_height - 1 + OUT#_shrink,OUT#_depth - 1 + OUT#_shrink, OUT#_stride,OUT#_stride,OUT#_stride, OUT#_dilation,OUT#_dilation,OUT#_dilation); repeat (OUT#_spectrum,_nn_k,copy(OUT#[_nn_k*OUT#_whd],OUT#_biases[_nn_k],OUT#_whd,1,0,-1)); # Add biases ); nn_conv3d_init_backward(OUT,IN) = ( batch_d#OUT#_weights = vector(#size(OUT#_weights)); batch_d#OUT#_biases = vector(#size(OUT#_biases)); ); nn_conv3d_backward(OUT,IN) = ( # Compute dLOSS/dIN. const OUT#_offset = -int(OUT#_shrink/OUT#_stride); const OUT#_invstride = 1/OUT#_stride; const OUT#_dons = OUT#_dilation/OUT#_stride; d#IN = vector(#size(IN#)); repeat (IN#_spectrum,_nn_k, draw(d#IN,IN#_width,IN#_height,IN#_depth,IN#_spectrum, correlate(d#OUT, OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, crop(OUT#_weights,w##OUT#_ind - 1,OUT#_spectrum,1,1, _nn_k*OUT#_kernel_size3,0,OUT#_kernel_size3,OUT#_spectrum), OUT#_kernel_size,OUT#_kernel_size,OUT#_kernel_size,OUT#_spectrum, 0,0,2, OUT#_kernel_center,OUT#_kernel_center,OUT#_kernel_center, OUT#_offset,OUT#_offset,OUT#_offset, IN#_width - 1 + OUT#_offset,IN#_height - 1 + OUT#_offset,IN#_depth - 1 + OUT#_offset, OUT#_invstride,OUT#_invstride,OUT#_invstride, OUT#_dons,OUT#_dons,OUT#_dons), 0,0,0,_nn_k,IN#_width,IN#_height,IN#_depth,1); ); # Compute dLOSS/dbiases. d#OUT#_biases = vector(#size(OUT#_biases)); fill(d#OUT#_biases,_nn_k,sum(d#OUT[_nn_k*OUT#_whd,OUT#_whd])); batch_d#OUT#_biases+=d#OUT#_biases; # Compute dLOSS/dweights. const OUT#_dcenter = OUT#_dilation*OUT#_kernel_center; const OUT#_kend = OUT#_kernel_size - 1 + OUT#_offset; d#OUT#_weights = vector(#size(OUT#_weights)); repeat (OUT#_spectrum,_nn_k, draw(d#OUT#_weights, OUT#_kernel_size,OUT#_kernel_size,OUT#_kernel_size,OUT#_spectrum*IN#_spectrum, correlate(crop(d#OUT,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, 0,0,0,_nn_k,OUT#_width,OUT#_height,OUT#_depth,1), OUT#_width,OUT#_height,OUT#_depth,1, IN#, IN#_width,IN#_height,IN#_depth,IN#_spectrum, 0,0,1, OUT#_dcenter,OUT#_dcenter,OUT#_dcenter, OUT#_offset,OUT#_offset,OUT#_offset, OUT#_kend,OUT#_kend,OUT#_kend, OUT#_dons,OUT#_dons,OUT#_dons, OUT#_invstride,OUT#_invstride,OUT#_invstride), 0,0,0,_nn_k*IN#_spectrum, OUT#_kernel_size,OUT#_kernel_size,OUT#_kernel_size,IN#_spectrum); ); batch_d#OUT#_weights+=d#OUT#_weights; ); nn_conv3d_end_backward(OUT) = ( merge(batch_d#OUT#_weights,+); merge(batch_d#OUT#_biases,+); batch_d#OUT#_weights/=nn_batch_size; batch_d#OUT#_biases/=nn_batch_size; ); # crop : Crop layer. #-------------------- nn_crop_init_forward(OUT,IN,x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions) = ( const OUT#_x0 = x0; const OUT#_y0 = y0; const OUT#_z0 = z0; const OUT#_c0 = c0; const OUT#_width = abs(x1 - x0) + 1; const OUT#_height = abs(y1 - y0) + 1; const OUT#_depth = abs(z1 - z0) + 1; const OUT#_spectrum = abs(c1 - c0) + 1; const OUT#_boundary_conditions = boundary_conditions; ); nn_crop_forward(OUT,IN) = ( OUT# = crop(IN#,IN#_width,IN#_height,IN#_depth,IN#_spectrum, OUT#_x0,OUT#_y0,OUT#_z0,OUT#_c0, OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, OUT#_boundary_conditions); ); nn_crop_backward(OUT,IN) = ( d#IN = crop(d#OUT,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, -OUT#_x0,-OUT#_y0,-OUT#_z0,-OUT#_c0, IN#_width,IN#_height,IN#_depth,IN#_spectrum, 0); ); # distance : Distance between two inputs. #---------------------------------------- nn_distance_init_forward(OUT,metric) = ( const OUT#_width = 1; const OUT#_height = 1; const OUT#_depth = 1; const OUT#_spectrum = 1; const OUT#_metric = metric; ); nn_distance_forward(OUT,IN0,IN1) = ( OUT#_diff = IN0# - IN1#; OUT# = [ OUT#_metric>0?norm#OUT#_metric(OUT#_diff):norm(OUT#_diff)^2 ]; ); nn_distance_backward(OUT,IN0,IN1) = ( !OUT_#metric?( # L2-squared d#IN0 = 2*OUT#_diff; ):OUT#_metric==2?( # L2 d#IN0 = OUT#_diff/(1e-8 + OUT#[0]); ):( # L d#IN0 = (abs(OUT#_diff)/(1e-8 + OUT#[0]))^(OUT#_metric - 1)*sign(OUT#_diff); ); d#IN1 = -d#IN0; ); # dropout : Dropout layer. #------------------------- nn_dropout_init_forward(OUT,IN,dropout_rate) = ( const OUT#_width = IN#_width; const OUT#_height = IN#_height; const OUT#_depth = IN#_depth; const OUT#_spectrum = IN#_spectrum; const OUT#_dropout_rate = nn_is_training?cut(dropout_rate,0,1):0; OUT#_dropout_rate?( OUT#_dropout_channels = vector(##OUT#_spectrum); fill(OUT#_dropout_channels,u>=OUT#_dropout_rate); OUT#_dropout_sum = sum(OUT#_dropout_channels); !OUT#_dropout_sum?(OUT#_dropout_channels = 1; OUT#_dropout_sum = size(OUT#_dropout_channels)); OUT#_dropout_mask = resize(OUT#_dropout_channels,size(IN#),1); OUT#_dropout_mask*=size(OUT#_dropout_channels)/OUT#_dropout_sum; ); ); nn_dropout_forward(OUT,IN) = ( OUT#_dropout_rate?( OUT# = IN#*OUT#_dropout_mask; ):( ref(IN#,OUT#); ); ); nn_dropout_backward(OUT,IN) = ( OUT#_dropout_rate?( d#IN = d#OUT*OUT#_dropout_mask; ):( ref(d#OUT,d#IN); ); ); # fc: Fully-connected layer. #--------------------------- nn_fc_init_forward(OUT,IN) = ( const OUT#_ind = $OUT#; const OUT#_width = 1; const OUT#_height = 1; const OUT#_depth = 1; const OUT#_spectrum = h##OUT#_ind; OUT#_weights = crop(##OUT#_ind,0,0,size(IN#),h##OUT#_ind); OUT#_biases = crop(##OUT#_ind,size(IN#),0,1,h##OUT#_ind); ); nn_fc_forward(OUT,IN) = ( OUT# = mul(OUT#_weights,IN#); OUT#+=OUT#_biases; ); nn_fc_init_backward(OUT) = ( batch_d#OUT#_weights = vector(#size(OUT#_weights)); batch_d#OUT#_biases = vector(#size(OUT#_biases)); ); nn_fc_backward(OUT,IN) = ( d#IN = mul(transpose(OUT#_weights,size(IN#)),d#OUT); d#OUT#_weights = mul(d#OUT,IN#,size(IN#)); batch_d#OUT#_weights+=d#OUT#_weights; batch_d#OUT#_biases+=d#OUT; ); nn_fc_end_backward(OUT) = ( merge(batch_d#OUT#_weights,+); merge(batch_d#OUT#_biases,+); batch_d#OUT#_weights/=nn_batch_size; batch_d#OUT#_biases/=nn_batch_size; ); # nlfc: Nonlinear fully-connected layer. #--------------------------------------- nn_nlfc_init_forward(OUT,IN) = ( const OUT#_ind = $OUT#; const OUT#_width = 1; const OUT#_height = 1; const OUT#_depth = 1; const OUT#_spectrum = h##OUT#_ind; OUT#_weights = crop(##OUT#_ind,0,0,size(IN#),h##OUT#_ind); OUT#_biases = crop(##OUT#_ind,size(IN#),0,size(IN#),h##OUT#_ind); ); nn_nlfc_forward(OUT,IN,activation) = ( OUT# = vector(##OUT#_spectrum); repeat(OUT#_spectrum,_nn_i, _nn_res = 0; repeat (size(IN#),_nn_j, _nn_off = _nn_i*size(IN#) + _nn_j; _nn_res+=nn_activation_#activation(OUT#_weights[_nn_off]*IN#[_nn_j] + OUT#_biases[_nn_off]); ); OUT#[_nn_i] = _nn_res; ); ); nn_nlfc_init_backward(OUT) = ( batch_d#OUT#_weights = vector(#size(OUT#_weights)); batch_d#OUT#_biases = vector(#size(OUT#_biases)); ); nn_nlfc_backward(OUT,IN,activation) = ( d#IN = vector(#size(IN#)); repeat (size(IN#),_nn_j, _nn_res = _nn_off = 0; repeat (OUT#_spectrum,_nn_i, _nn_off = _nn_i*size(IN#) + _nn_j; _nn_Wij = OUT#_weights[_nn_off]; _nn_res+=nn_activation_d_#activation(_nn_Wij*IN#[_nn_j] + OUT#_biases[_nn_off])*_nn_Wij*d#OUT[_nn_i]; ); d#IN[_nn_j] = _nn_res; ); d#OUT#_weights = vector(#size(OUT#_weights)); d#OUT#_biases = vector(#size(OUT#_biases)); _nn_ptr = 0; repeat (OUT#_spectrum,_nn_i, repeat (size(IN#),_nn_j, _nn_off = _nn_i*size(IN#) + _nn_j; _nn_Wij = OUT#_weights[_nn_off]; _nn_sigmad = nn_activation_d_#activation(_nn_Wij*IN#[_nn_j] + OUT#_biases[_nn_off]); d#OUT#_weights[_nn_ptr] = _nn_sigmad*IN#[_nn_j]*d#OUT[_nn_i]; d#OUT#_biases[_nn_ptr] = _nn_sigmad*d#OUT#[_nn_i]; ++_nn_ptr; ); ); batch_d#OUT#_weights+=d#OUT#_weights; batch_d#OUT#_biases+=d#OUT#_biases; ); nn_nlfc_end_backward(OUT) = ( merge(batch_d#OUT#_weights,+); merge(batch_d#OUT#_biases,+); batch_d#OUT#_weights/=nn_batch_size; batch_d#OUT#_biases/=nn_batch_size; ); # input: Input image or vector data. #----------------------------------- nn_input_init_forward(OUT,w,h,d,s) = ( const OUT#_width = w; const OUT#_height = h; const OUT#_depth = d; const OUT#_spectrum = s; ); # maxpool2d : 2D max pooling. #---------------------------- nn_maxpool2d_init_forward(OUT,IN,patch_size) = ( const OUT#_width = int(IN#_width/patch_size); const OUT#_height = int(IN#_height/patch_size); const OUT#_depth = IN#_depth; const OUT#_spectrum = IN#_spectrum; const OUT#_patch_size = patch_size; ); nn_maxpool2d_forward(OUT,IN) = ( OUT# = vector(##OUT#_width*OUT#_height*OUT#_depth*OUT#_spectrum); OUT#_from = vector(#size(OUT#)); _nn_off = 0; repeat (OUT#_spectrum,_nn_c, repeat (OUT#_depth,_nn_z, repeat (OUT#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (OUT#_width,_nn_x, OUT#_patch = crop(IN#,IN#_width,IN#_height,IN#_depth,IN#_spectrum, _nn_x*OUT#_patch_size,_nn_yp,_nn_z,_nn_c, OUT#_patch_size,OUT#_patch_size,1,1); _nn_argmax = argmax(OUT#_patch); OUT#[_nn_off] = OUT#_patch[_nn_argmax]; OUT#_from[_nn_off++] = _nn_argmax; ); ); ); ); ); nn_maxpool2d_backward(OUT,IN) = ( d#IN = vector(#size(IN#)); _nn_offOUT = 0; repeat (OUT#_spectrum,_nn_c, repeat (OUT#_depth,_nn_z, _nn_offIN0 = IN#_width*IN#_height*(_nn_z + IN#_depth*_nn_c); repeat (OUT#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (OUT#_width,_nn_x, _nn_from = OUT#_from[_nn_offOUT]; _nn_from_x = _nn_from%OUT#_patch_size; _nn_from_y = int(_nn_from/OUT#_patch_size); _nn_offIN = _nn_offIN0 + _nn_x*OUT#_patch_size + _nn_from_x + IN#_width*(_nn_yp + _nn_from_y); d#IN[_nn_offIN] = d#OUT[_nn_offOUT++]; ); ); ); ); ); # maxpool3d : 3D max pooling. #---------------------------- nn_maxpool3d_init_forward(OUT,IN,patch_size) = ( const OUT#_width = int(IN#_width/patch_size); const OUT#_height = int(IN#_height/patch_size); const OUT#_depth = int(IN#_depth/patch_size); const OUT#_spectrum = IN#_spectrum; const OUT#_patch_size = patch_size; ); nn_maxpool3d_forward(OUT,IN) = ( OUT# = vector(##OUT#_width*OUT#_height*OUT#_depth*OUT#_spectrum); OUT#_from = vector(#size(OUT#)); _nn_off = 0; repeat (OUT#_spectrum,_nn_c, repeat (OUT#_depth,_nn_z, _nn_zp = _nn_z*OUT#_patch_size; repeat (OUT#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (OUT#_width,_nn_x, OUT#_patch = crop(IN#,IN#_width,IN#_height,IN#_depth,IN#_spectrum, _nn_x*OUT#_patch_size,_nn_yp,_nn_zp,_nn_c, OUT#_patch_size,OUT#_patch_size,OUT#_patch_size,1); _nn_argmax = argmax(OUT#_patch); OUT#[_nn_off] = OUT#_patch[_nn_argmax]; OUT#_from[_nn_off++] = _nn_argmax; ); ); ); ); ); nn_maxpool3d_backward(OUT,IN) = ( d#IN = vector(#size(IN#)); _nn_offOUT = 0; repeat (OUT#_spectrum,_nn_c, _nn_offIN0 = IN#_width*IN#_height*IN#_depth*_nn_c; repeat (OUT#_depth,_nn_z, _nn_zp = _nn_z*OUT#_patch_size; repeat (OUT#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (OUT#_width,_nn_x, _nn_from = OUT#_from[_nn_offOUT]; _nn_from_x = _nn_from%OUT#_patch_size; _nn_from_y = int(_nn_from/OUT#_patch_size)%OUT#_patch_size; _nn_from_z = int(_nn_from/OUT#_patch_size^2); _nn_offIN = _nn_offIN0 + _nn_x*OUT#_patch_size + _nn_from_x + IN#_width*(_nn_yp + _nn_from_y + IN#_height*(_nn_zp + _nn_from_z)); d#IN[_nn_offIN] = d#OUT[_nn_offOUT++]; ); ); ); ); ); # mul: Multiply two inputs. #--------------------------- nn_mul_init_forward(OUT,IN) = ( const OUT#_width = IN#_width; const OUT#_height = IN#_height; const OUT#_depth = IN#_depth; const OUT#_spectrum = IN#_spectrum; ); nn_mul_forward(OUT,IN0,IN1) = ( OUT# = IN0#*IN1#; ); nn_mul_backward(OUT,IN0,IN1) = ( d#IN0 = IN1#*d#OUT; d#IN1 = IN0#*d#OUT; ); # nl: Non-linearity. #------------------- nn_nl_init_forward(OUT,IN) = ( const OUT#_width = IN#_width; const OUT#_height = IN#_height; const OUT#_depth = IN#_depth; const OUT#_spectrum = IN#_spectrum; ); nn_nl_forward(OUT,IN,activation) = ( OUT# = vector(#size(IN#)); fill(OUT#,_nn_k,OUT#[_nn_k] = nn_activation_#activation(IN#[_nn_k])); ); nn_nl_backward(OUT,IN,activation) = ( d#IN = vector(#size(IN#)); fill(d#IN,_nn_k,d#OUT[_nn_k]*nn_activation_d_#activation(IN#[_nn_k])); ); # Special case: Softmax. nn_softmax_forward(OUT,IN) = ( OUT# = exp(IN# - max(IN#)); OUT#/=sum(OUT#); ); nn_softmax_backward(OUT,IN) = ( d#IN = d#OUT - dot(d#OUT,OUT#); d#IN*=OUT#; ); # normalize: Layer normalization. #-------------------------------- nn_normalize_init_forward(OUT,IN,is_channelwise) = ( const OUT#_ind = $OUT#; const OUT#_width = IN#_width; const OUT#_height = IN#_height; const OUT#_depth = IN#_depth; const OUT#_spectrum = IN#_spectrum; const OUT#_is_channelwise = is_channelwise; OUT#_is_channelwise?( # Channel-by-channel normalization OUT#_alpha = crop(##OUT#_ind,0,1); OUT#_beta = crop(##OUT#_ind,1,1); OUT#_avg = crop(##OUT#_ind,2,1); OUT#_var = crop(##OUT#_ind,3,1); ):( # Global normalization OUT#_alpha = i[##OUT#_ind,0]; OUT#_beta = i[##OUT#_ind,1]; OUT#_avg = i[##OUT#_ind,2]; OUT#_var = i[##OUT#_ind,3]; ); OUT#_std = sqrt(OUT#_var); OUT#_std+=1e-8; ); nn_normalize_forward(OUT,IN) = ( OUT#_is_channelwise?( # Channel-by-channel normalization const OUT#_whd = OUT#_width*OUT#_height*OUT#_depth; OUT#_hat = vector(#size(IN#)); OUT# = vector(#size(IN#)); repeat (size(OUT#_avg),_nn_k, OUT#_tmp = IN#[_nn_k*OUT#_whd,OUT#_whd] - OUT#_avg[_nn_k]; # One channel of IN OUT#_tmp/=OUT#_std[_nn_k]; copy(OUT#_hat[_nn_k*OUT#_whd],OUT#_tmp); OUT#_tmp*=OUT#_alpha[_nn_k]; OUT#_tmp+=OUT#_beta[_nn_k]; copy(OUT#[_nn_k*OUT#_whd],OUT#_tmp); ); ):( # Global normalization OUT#_hat = IN# - OUT#_avg; OUT#_hat/=OUT#_std; OUT# = OUT#_hat*OUT#_alpha; OUT#+=OUT#_beta; ); ); nn_normalize_init_backward(OUT) = ( OUT#_is_channelwise?( # Channel-by-channel normalization batch_d#OUT#_alpha = vector(#size(OUT#_avg)); batch_d#OUT#_beta = vector(#size(OUT#_avg)); batch_#OUT#_avg = vector(#size(OUT#_avg)); batch_#OUT#_var = vector(#size(OUT#_avg)); ):( # Global normalization batch_d#OUT#_alpha = batch_d#OUT#_beta = batch_#OUT#_avg = batch_#OUT#_var = 0; ); ); nn_normalize_backward(OUT,IN) = ( OUT#_is_channelwise?( # Channel-by-channel normalization d#IN = vector(#size(IN#)); repeat (size(#OUT#_avg),_nn_k, OUT#_tmp = d#OUT[_nn_k*OUT#_whd,OUT#_whd]; # One channel of dOUT copy(d#IN[_nn_k*OUT#_whd],OUT#_tmp*OUT#_alpha[_nn_k]/OUT#_std[_nn_k]); batch_d#OUT#_alpha[_nn_k]+=dot(OUT#_tmp,OUT#_hat[_nn_k*OUT#_whd,OUT#_whd]); batch_d#OUT#_beta[_nn_k]+=sum(OUT#_tmp); OUT#_tmp = IN#[_nn_k*OUT#_whd,OUT#_whd]; # One channel of IN batch_#OUT#_avg[_nn_k]+=avg(OUT#_tmp); batch_#OUT#_var[_nn_k]+=var(OUT#_tmp); ); ):( # Global normalization d#IN = d#OUT*OUT#_alpha/OUT#_std; batch_d#OUT#_alpha+=dot(d#OUT,OUT#_hat); batch_d#OUT#_beta+=sum(d#OUT); batch_#OUT#_avg+=avg(IN#); batch_#OUT#_var+=var(IN#); ); ); nn_normalize_end_backward(OUT) = ( merge(batch_d#OUT#_alpha,+); merge(batch_d#OUT#_beta,+); merge(batch_#OUT#_avg,+); merge(batch_#OUT#_var,+); batch_d#OUT#_alpha/=nn_batch_size; batch_d#OUT#_beta/=nn_batch_size; batch_#OUT#_avg/=nn_batch_size; batch_#OUT#_var/=nn_batch_size; ); # patchdown2d: 2D downscale by patch. #------------------------------------ nn_patchdown2d_init_forward(OUT,IN,patch_size) = ( const OUT#_width = int(IN#_width/patch_size); const OUT#_height = int(IN#_height/patch_size); const OUT#_depth = IN#_depth; const OUT#_spectrum = IN#_spectrum*patch_size^2; const OUT#_patch_size = patch_size; ); nn_patchdown2d_forward(OUT,IN) = ( OUT# = vector(##OUT#_width*OUT#_height*OUT#_depth*OUT#_spectrum); repeat (OUT#_depth,_nn_z, repeat (OUT#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (OUT#_width,_nn_x, draw(OUT#,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, crop(IN#,IN#_width,IN#_height,IN#_depth,IN#_spectrum, _nn_x*OUT#_patch_size,_nn_yp,_nn_z, OUT#_patch_size,OUT#_patch_size,1), _nn_x,_nn_y,_nn_z,1,1,1); ); ); ); ); nn_patchdown2d_backward(OUT,IN) = ( d#IN = vector(#size(IN#)); repeat (OUT#_depth,_nn_z, repeat (OUT#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (OUT#_width,_nn_x, draw(d#IN#,IN#_width,IN#_height,IN#_depth,IN#_spectrum, crop(d#OUT,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum,_nn_x,_nn_y,_nn_z,1,1,1), _nn_x*OUT#_patch_size,_nn_yp,_nn_z, OUT#_patch_size,OUT#_patch_size,1); ); ); ); ); # patchdown3d: 3D downscale by patch. #------------------------------------ nn_patchdown3d_init_forward(OUT,IN,patch_size) = ( const OUT#_width = int(IN#_width/patch_size); const OUT#_height = int(IN#_height/patch_size); const OUT#_depth = int(IN#_depth/patch_size); const OUT#_spectrum = IN#_spectrum*patch_size^3; const OUT#_patch_size = patch_size; ); nn_patchdown3d_forward(OUT,IN) = ( OUT# = vector(##OUT#_width*OUT#_height*OUT#_depth*OUT#_spectrum); repeat (OUT#_depth,_nn_z, _nn_zp = _nn_z*OUT#_patch_size; repeat (OUT#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (OUT#_width,_nn_x, draw(OUT#,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, crop(IN#,IN#_width,IN#_height,IN#_depth,IN#_spectrum, _nn_x*OUT#_patch_size,_nn_yp,_nn_zp, OUT#_patch_size,OUT#_patch_size,OUT#_patch_size), _nn_x,_nn_y,_nn_z,1,1,1); ); ); ); ); nn_patchdown3d_backward(OUT,IN) = ( d#IN = vector(#size(IN#)); repeat (OUT#_depth,_nn_z, _nn_zp = _nn_z*OUT#_patch_size; repeat (OUT#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (OUT#_width,_nn_x, draw(d#IN#,IN#_width,IN#_height,IN#_depth,IN#_spectrum, crop(d#OUT,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum,_nn_x,_nn_y,_nn_z,1,1,1), _nn_x*OUT#_patch_size,_nn_yp,_nn_zp, OUT#_patch_size,OUT#_patch_size,OUT#_patch_size); ); ); ); ); # patchup2d: 2D upscale by patch. #-------------------------------- nn_patchup2d_init_forward(OUT,IN,patch_size) = ( const OUT#_width = IN#_width*patch_size; const OUT#_height = IN#_height*patch_size; const OUT#_depth = IN#_depth; const OUT#_spectrum = IN#_spectrum/patch_size^2; const OUT#_patchsize = patch_size; ); nn_patchup2d_forward(OUT,IN) = ( OUT# = vector(##OUT#_width*OUT#_height*OUT#_depth*OUT#_spectrum); repeat (IN#_depth,_nn_z, repeat (IN#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (IN#_width,_nn_x, draw(OUT#,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, crop(#IN,IN#_width,IN#_height,IN#_depth,IN#_spectrum,_nn_x,_nn_y,_nn_z,1,1,1), _nn_x*OUT#_patch_size,_nn_yp,_nn_z, OUT#_patch_size,OUT#_patch_size,1); ); ); ); ); nn_patchup2d_backward(OUT,IN) = ( d#IN = vector(#size(IN#)); repeat (IN#_depth,_nn_z, repeat (IN#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (IN#_width,_nn_x, draw(d#IN,IN#_width,IN#_height,IN#_depth,IN#_spectrum, crop(d#OUT,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, _nn_x*OUT#_patch_size,_nn_yp,_nn_z, OUT#_patch_size,OUT#_patch_size,1), _nn_x,_nn_y,_nn_z,1,1,1); ); ); ); ); # patchup3d: 3D upscale by patch. #-------------------------------- nn_patchup3d_init_forward(OUT,IN,patch_size) = ( const OUT#_width = IN#_width*patch_size; const OUT#_height = IN#_height*patch_size; const OUT#_depth = IN#_depth*patch_size; const OUT#_spectrum = IN#_spectrum/patch_size^3; const OUT#_patch_size = patch_size; ); nn_patchup3d_forward(OUT,IN) = ( OUT# = vector(##OUT#_width*OUT#_height*OUT#_depth*OUT#_spectrum); repeat (IN#_depth,_nn_z, _nn_zp = _nn_z*OUT#_patch_size; repeat (IN#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (IN#_width,_nn_x, draw(OUT#,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, crop(#IN,IN#_width,IN#_height,IN#_depth,IN#_spectrum,_nn_x,_nn_y,_nn_z,1,1,1), _nn_x*OUT#_patch_size,_nn_yp,_nn_zp, OUT#_patch_size,OUT#_patch_size,OUT#_patch_size); ); ); ); ); nn_patchup3d_backward(OUT,IN) = ( d#IN = vector(#size(IN#)); repeat (IN#_depth,_nn_z, _nn_zp = _nn_z*OUT#_patch_size; repeat (IN#_height,_nn_y, _nn_yp = _nn_y*OUT#_patch_size; repeat (IN#_width,_nn_x, draw(d#IN,IN#_width,IN#_height,IN#_depth,IN#_spectrum, crop(d#OUT,OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, _nn_x*OUT#_patch_size,_nn_yp,_nn_zp, OUT#_patch_size,OUT#_patch_size,OUT#_patch_size), _nn_x,_nn_y,_nn_z,1,1,1); ); ); ); ); # rename: Rename input. #---------------------- nn_rename_init_forward(OUT,IN) = ( const OUT#_width = IN#_width; const OUT#_height = IN#_height; const OUT#_depth = IN#_depth; const OUT#_spectrum = IN#_spectrum; ref(IN#,OUT#); ); nn_rename_backward(OUT,IN) = ( ref(d#OUT,d#IN); ); # reshape: Reshape input to output with compatible size. #------------------------------------------------------- nn_reshape_init_forward(OUT,IN,w,h,d,s) = ( const OUT#_width = w; const OUT#_height = h; const OUT#_depth = d; const OUT#_spectrum = s; ref(IN#,OUT#); ); nn_reshape_init_backward(OUT,IN) = ( ref(d#OUT,d#IN); ); # resize: Resize input. #---------------------- nn_resize_init_forward(OUT,IN,w,h,d,s,interpolation) = ( const OUT#_width = w; const OUT#_height = h; const OUT#_depth = d; const OUT#_spectrum = s; const OUT#_interpolation = interpolation; ); nn_resize_forward(OUT,IN) = ( OUT# = resize(IN#, IN#_width,IN#_height,IN#_depth,IN#_spectrum, OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, OUT#_interpolation); ); nn_resize_backward(OUT,IN) = ( d#IN = resize(d#OUT, OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, IN#_width,IN#_height,IN#_depth,IN#_spectrum, 2); ); # run: Transform input with G'MIC command. #----------------------------------------- nn_run_init_forward(OUT,IN,command,w,h,d,s) = ( const OUT#_width = w; const OUT#_height = h; const OUT#_depth = d; const OUT#_spectrum = s; OUT#_varname = 'nn_#OUT'; OUT#_pipeline = string('l[] { $nn_#IN ',command,' k. r ', OUT#_width,',',OUT#_height,',',OUT#_depth,',',OUT#_spectrum, ',3 k. store ',OUT#_varname,' }'); ); nn_run_forward(OUT,IN) = ( critical( store('nn_#IN',IN#,IN#_width,IN#_height,IN#_depth,IN#_spectrum); run(OUT#_pipeline); OUT# = get(OUT#_varname,OUT#_width*OUT#_height*OUT#_depth*OUT#_spectrum); ); ); nn_run_backward(OUT,IN) = ( d#IN = resize(d#OUT, OUT#_width,OUT#_height,OUT#_depth,OUT#_spectrum, IN#_width,IN#_height,IN#_depth,IN#_spectrum, 3); ); # split: Split input as two outputs. #----------------------------------- nn_split_init_forward(OUT0,OUT1,IN,nb_channels0) = ( const OUT0#_width = IN#_width; const OUT0#_height = IN#_height; const OUT0#_depth = IN#_depth; const OUT0#_spectrum = nb_channels0; const OUT1#_width = IN#_width; const OUT1#_height = IN#_height; const OUT1#_depth = IN#_depth; const OUT1#_spectrum = IN#_spectrum - nb_channels0; ); nn_split_forward(OUT0,OUT1,IN) = ( const OUT0#_siz0 = OUT0#_width*OUT0#_height*OUT0#_depth*OUT0#_spectrum; OUT0# = IN#[0,OUT0#_siz0]; OUT1# = IN#[OUT0#_siz0,OUT1#_width*OUT1#_height*OUT1#_depth*OUT1#_spectrum]; ); nn_split_backward(OUT0,OUT1,IN) = ( d#IN = vector(#size(IN#)); copy(d#IN,d#OUT0,size(OUT0#)); copy(d#IN[size(OUT0#)],d#OUT1,size(OUT1#)); ); #---------------- # Loss functions. #---------------- # binary_crossentropy : Binary cross-entropy. #-------------------------------------------- nn_loss_binary_crossentropy_init_forward(L) = ( batch_#L = 0; ); nn_loss_binary_crossentropy_forward(L,IN,TRUTH) = ( L#_epsiloned = IN# + 1e-5; L#_epsiloned1 = 1 - IN# + 1e-5; L# = -sum(TRUTH*log(L#_epsiloned) + (1 - TRUTH)*log(L#_epsiloned1)); batch_#L+=L#; ); nn_loss_binary_crossentropy_end_forward(L) = ( merge(batch_#L,+); batch_#L/=nn_batch_size; L# = batch_#L; ); nn_loss_binary_crossentropy_backward(L,IN,TRUTH) = ( d#IN = (IN# - TRUTH)/(L#_epsiloned*L#_epsiloned1); ); # crossentropy : Cross-entropy. #------------------------------ nn_loss_crossentropy_init_forward(L) = ( batch_#L = 0; ); nn_loss_crossentropy_forward(L,IN,TRUTH) = ( L#_epsiloned = IN# + 1e-8; L# = -sum(TRUTH*log(L#_epsiloned)); batch_#L+=L#; ); nn_loss_crossentropy_end_forward(L) = ( merge(batch_#L,+); batch_#L/=nn_batch_size; L# = batch_#L; ); nn_loss_crossentropy_backward(L,IN,TRUTH) = ( d#IN = -TRUTH/L#_epsiloned; ); # mse : Mean-squared error. #-------------------------- nn_loss_mse_init_forward(L) = ( batch_#L = 0; ); nn_loss_mse_forward(L,IN,TRUTH) = ( d#IN = IN# - TRUTH; L# = norm(d#IN)^2/size(d#IN); batch_#L+=L#; ); nn_loss_mse_end_forward(L) = ( merge(batch_#L,+); batch_#L/=nn_batch_size; L# = batch_#L; ); nn_loss_mse_backward(L,IN,TRUTH) = ( d#IN*=2; ); # normp : p-norm. #----------------- nn_loss_normp_init_forward(L,metric) = ( const L#_metric = metric; batch_#L = 0; ); nn_loss_normp_forward(L,IN,TRUTH) = ( d#IN = IN# - TRUTH; L# = (!L#_metric?norm(d#IN)^2:norm#L#_metric(d#IN))/size(d#IN); batch_#L+=L#; ); nn_loss_normp_end_forward(L) = ( merge(batch_#L,+); batch_#L/=nn_batch_size; L# = batch_#L; ); nn_loss_normp_backward(L,IN,TRUTH) = ( !L#_metric?( # L2-squared d#IN*=2; ):L#_metric==2?( # L2 d#IN/=(1e-8 + L#); ):( # Lp d#IN = (abs(d#IN)/(1e-8 + L#))^(L#_metric - 1)*sign(d#IN); ); ); # softmax_crossentropy : Softmax + cross entropy. #------------------------------------------------ nn_loss_softmax_crossentropy_init_forward(L) = ( batch_#L = 0; ); nn_loss_softmax_crossentropy_forward(L,IN,TRUTH) = ( L#_softmax = exp(IN# - max(IN#)); L#_softmax/=sum(L#_softmax); L#_softmax+=1e-8; L# = -sum(TRUTH*log(L#_softmax)); d#IN = L#_softmax - TRUTH; batch_#L+=L#; ); nn_loss_softmax_crossentropy_end_forward(L) = ( merge(batch_#L,+); batch_#L/=nn_batch_size; L# = batch_#L; ); nn_loss_softmax_crossentropy_backward(L,IN,TRUTH) = ( L#_softmax = exp(IN# - max(IN#)); L#_softmax/=sum(L#_softmax); L#_softmax+=1e-8; L# = -sum(TRUTH*log(L#_softmax)); d#IN = L#_softmax - TRUTH; batch_#L+=L#; );" #@cli nn_init #@cli : Initialize a new network. nn_init : e[^-1] "[nn_lib] Initialize new network." _nn_modules_names,_nn_modules_types,_nn_trainer_data,_nn_forward,_nn_loss,_nn_backward,_nn_update,_nn_latest= _nn_init="$0 " #@cli nn_check_layer : name #@cli : Check that the layer with specified name already exists in the network. nn_check_layer : if !isvarname('"$1"') error[0--3] "Command 'nn_check_layer': Invalid layer name '$1'." elif narg($_nn_$1_size)!=4 error[0--3] "Command 'nn_check_layer': Layer with name '$1' does not exist." fi #@cli nn_add : out,in0,_in1 #@cli : Add an 'add' layer to the network. #@cli : Default value: 'in1=. (previous layer)'. nn_add : if ['$2']=='.' in0=$_nn_latest else in0=$2 fi nn_check_layer $in0 if ['${3=.}']=='.' in1=$_nn_latest else in1=$3 fi nn_check_layer $in1 check "isvarname('$1') && "[${_nn_${in0}_size}]==[${_nn_${in1}_size}] e[^-1] "[nn_lib] Add 'add' layer '$1', with inputs '"$in0"' and '"$in1"'." _nn_modules_names.=$1, _nn_modules_types.=add, _nn_$1_size=${_nn_${in0}_size} _nn_$1_input=$in0,$in1 _nn_forward.="begin(nn_add_init_forward($1,"$in0"));"\ "nn_add_forward($1,"$in0","$in1");" _nn_backward..="nn_add_backward($1,"$in0","$in1");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_append : out,in0,_in1 #@cli : Add an 'append' layer to the network. #@cli : Default value: 'in1=. (previous layer)'. nn_append : if ['$2']=='.' in0=$_nn_latest else in0=$2 fi nn_check_layer $in0 if ['${3=.}']=='.' in1=$_nn_latest else in1=$3 fi nn_check_layer $in1 check "isvarname('$1') && "[${_nn_${in0}_size}][0,3]==[${_nn_${in1}_size}][0,3] e[^-1] "[nn_lib] Add 'append' layer '$1', with inputs '"$in0"' and '"$in1"'." _nn_modules_names.=$1, _nn_modules_types.=append, _nn_$1_size:=[${_nn_${in0}_size}]+[0,0,0,[${_nn_${in1}_size}][3]] _nn_$1_input=$in0,$in1 _nn_forward.="begin(nn_append_init_forward($1,"$in0","$in1"));"\ "nn_append_forward($1,"$in0","$in1");" _nn_backward..="nn_append_backward($1,"$in0","$in1");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_avgpool2d : out,_in,_patch_size>1 #@cli : Add a 'avgpool2d' layer (2D average pooling) to the network. #@cli : Default value: 'in=. (previous layer)'. nn_avgpool2d : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=2},2)" e[^-1] "[nn_lib] Add 'avgpool2d' layer '$1', with input '"$in"' and patch size $3x$3." _nn_modules_names.=$1, _nn_modules_types.=avgpool2d, _nn_$1_size:=s=[${_nn_${in}_size}];[int(s[0,2]/$3),s[2,2]] _nn_$1_input=$in _nn_$1_properties="patch_size=$3x$3" _nn_forward.="begin(nn_avgpool2d_init_forward($1,"$in",$3));"\ "nn_avgpool2d_forward($1,"$in");" _nn_backward..="nn_avgpool2d_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_avgpool3d : out,_in,_patch_size>1 #@cli : Add a 'avgpool3d' layer (3D average pooling) to the network. #@cli : Default value: 'in=. (previous layer)'. nn_avgpool3d : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=2},2)" e[^-1] "[nn_lib] Add 'avgpool3d' layer '$1', with input '"$in"' and patch size $3x$3x$3." _nn_modules_names.=$1, _nn_modules_types.=avgpool3d, _nn_$1_size:=s=[${_nn_${in}_size}];[int(s[0,3]/$3),s[3]] _nn_$1_input=$in _nn_$1_properties="patch_size=$3x$3x$3" _nn_forward.="begin(nn_avgpool3d_init_forward($1,"$in",$3));"\ "nn_avgpool3d_forward($1,"$in");" _nn_backward..="nn_avgpool3d_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_clone : name0,name1,_in #@cli : Add a 'clone' layer to the network. #@cli : Default value: 'in=. (previous layer)'. nn_clone : if ['${3=.}']=='.' in=$_nn_latest else in=$3 fi nn_check_layer $in check "isvarname('$1') && isvarname('$2')" e[^-1] "[nn_lib] Add 'clone' layer with input '"$in"' and outputs '$1' and '$2'." _nn_modules_names.=$1,$2, _nn_modules_types.=clone,clone, _nn_$1_size=${_nn_${in}_size} _nn_$2_size=${_nn_${in}_size} _nn_$1_input,_nn_$2_input=$in _nn_forward.="begin(nn_clone_init_forward($1,$2,"$in"));"\ "nn_clone_forward($1,$2,"$in");" _nn_backward..="nn_clone_backward($1,$2,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_conv2d : out,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation,_border_shrink>=0,_boundary_conditions,\ # 0<=_learning_mode<=3 #@cli : Add a 'conv2d' layer (2D convolutional layer) to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'kernel_size=3', 'stride=1', 'dilation=1', \ # 'border_shrink=0', 'boundary_conditions=1' and 'learning_mode=3'. nn_conv2d : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint($3,1) && isint(${4=3},1) && ${5=1}>0 && isint(${7=0},0) && "\ "isint(${8=1},0,3) && isint(${9=3},0,3)" skip "${6=1}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'conv2d' layer '$1', with input '"$in"', $3 channels, $4x$4 kernels, stride $5, dilation $6, "\ "border shrink $7 and "$s$8" boundary conditions." init=$_nn_init _nn_conv2d $1_conv2d,$in,${3-9} nn_rename $1,$1_conv2d _nn_init=${init}"$0 $* " _nn_latest=$1 # [Internal] Insert 'conv2d' layer, with parameter image having name '$1'. _nn_conv2d : _nn_modules_names.=$1, _nn_modules_types.=conv2d, _nn_$1_size:=s=[$_nn_$2_size];[int(s[0,2]/$5)-2*$7,s[2],$3] _nn_$1_learning_mode=$9 _nn_$1_input=$2 s0,s1,s2,s3=dirichlet,neumann,periodic,mirror _nn_$1_properties="kernel=$4x$4, stride=$5, dilation=$6, border_shrink=$7, boundary_conditions="$s$8", "\ "learning_mode=$9" if !isint($$1) {[$_nn_$2_size][3]*$4^2+1},$3,1,1,4*g/(w-1) if $9<2 1,100% j.. .,100% rm. fi # Set biases to 0 if they are not learnt => $1 fi _nn_forward.="begin(nn_conv2d_init_forward($1,$2,${4-8}));"\ "nn_conv2d_forward($1,$2);" _nn_backward..="begin(nn_conv2d_init_backward($1,$2));"\ "nn_conv2d_backward($1,$2);"\ "end(nn_conv2d_end_backward($1));" _nn_init.="$0 $* " #@cli nn_conv2dnl : out,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation>0,_border_shrink>=0,_boundary_conditions,\ # _activation,0<=_learning_mode<=3 #@cli : Add a 'conv2dnl' (2D convolutional layer followed by a non-linearity) to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'kernel_size=3', 'stride=1', 'dilation=1', 'border_shrink=0', \ # 'boundary_conditions=1', 'activation=leakyrelu' and 'learning_mode=3'. nn_conv2dnl : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint($3,1) && isint(${4=3},1) && ${5=1}>0 && "\ "isint(${7=0},0) && isint(${8=1},0,3) && isint(${10=3},0,3)" skip "${6=1}","${9=leakyrelu}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'conv2d+nl' layer '$1', with input '"$in"', $3 channels, $4x$4 kernels, "\ "stride $5, dilation $6, border shrink $7, "$s$8" boundary conditions and '$9' activation." init=$_nn_init _nn_conv2d $1_conv2d,$in,${3-8},$10 nn_nl $1,$1_conv2d,$9 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_conv2dnnl : out,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation>0,_border_shrink>=0,_boundary_conditions,\ # _activation,0<=_learning_mode<=3 #@cli : Add a 'conv2dnnl' (2D convolutional layer followed by a normalization layer, then a non-linearity) \ # to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'kernel_size=3', 'stride=1', 'dilation=1', 'border_shrink=0', \ # 'boundary_conditions=1', 'activation=leakyrelu' and 'learning_mode=3'. nn_conv2dnnl : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint($3,1) && isint(${4=3},1) && ${5=1}>0 && "\ "isint(${7=0},0) && isint(${8=1},0,3) && isint(${10=3},0,3)" skip "${6=1}","${9=leakyrelu}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'conv2d+normalize+nl' layer '$1', with input '"$in"', $3 channels, $4x$4 kernels, "\ "stride $5, dilation $6, border shrink $7, "$s$8" boundary conditions and '$9' activation." init=$_nn_init _nn_conv2d $1_conv2d,$in,${3-8},$10 _nn_normalize $1_normalize,$1_conv2d,1,3 nn_nl $1,$1_normalize,$9 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_conv3d : out,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation,_border_shrink>=0,_boundary_conditions,\ # 0<=_learning_mode<=3 #@cli : Add a 'conv3d' layer (3D convolutional layer) to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'kernel_size=3', 'stride=1', 'dilation=1', 'border_shrink=0', \ # 'boundary_conditions=1' and 'learning_mode=3'. nn_conv3d : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint($3,1) && isint(${4=3},1) && ${5=1}>0 && isint(${7=0},0) && "\ "isint(${8=1},0,3) && isint(${9=3},0,3)" skip "${6=1}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'conv3d' layer '$1', with input '"$in"', $3 channels, $4x$4x$4 kernels, stride $5, "\ "dilation $6, border shrink $7 and "$s$8" boundary conditions." init=$_nn_init _nn_conv3d $1_conv3d,$in,${3-9} nn_rename $1,$1_conv3d _nn_init=${init}"$0 $* " _nn_latest=$1 # [Internal] Insert 'conv3d' layer, with parameter image having name '$1'. _nn_conv3d : _nn_modules_names.=$1, _nn_modules_types.=conv3d, _nn_$1_size:=s=[$_nn_$2_size];[int(s[0,3]/$5)-2*$7,$3] _nn_$1_learning_mode=$9 _nn_$1_input=$2 s0,s1,s2,s3=dirichlet,neumann,periodic,mirror _nn_$1_properties="kernel=$4x$4x$4, stride=$5, dilation=$6, border_shrink=$7, boundary_conditions="$s$8", "\ "learning_mode=$9" if !isint($$1) {[$_nn_$2_size][3]*$4^3+1},$3,1,1,4*g/(w-1) if $9<2 1,100% j.. .,100% rm. fi # Set biases to 0 if they are not learnt => $1 fi _nn_forward.="begin(nn_conv3d_init_forward($1,$2,${4-8}));"\ "nn_conv3d_forward($1,$2);" _nn_backward..="begin(nn_conv3d_init_backward($1,$2));"\ "nn_conv3d_backward($1,$2);"\ "end(nn_conv3d_end_backward($1));" _nn_init.="$0 $* " #@cli nn_conv3dnl : out,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation>0,_border_shrink>=0,_boundary_conditions,\ # _activation,0<=_learning_mode<=3 #@cli : Add a 'conv3dnl' (3D convolutional layer followed by a non-linearity) to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'kernel_size=3', 'stride=1', 'dilation=1', 'border_shrink=0', \ # 'boundary_conditions=1', 'activation=leakyrelu' and 'learning_mode=3'. nn_conv3dnl : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint($3,1) && isint(${4=3},1) && ${5=1}>0 && "\ "isint(${7=0},0) && isint(${8=1},0,3) && isint(${10=3},0,3)" skip "${6=1}","${9=leakyrelu}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'conv3d+nl' layer '$1', with input '"$in"', $3 channels, $4x$4x$4 kernels, "\ "stride $5, dilation $6, border shrink $7, "$s$8" boundary conditions and '$9' activation." init=$_nn_init _nn_conv3d $1_conv3d,$in,${3-8},$10 nn_nl $1,$1_conv3d,$9 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_conv3dnnl : out,in,nb_channels>0,_kernel_size>0,_stride>0,_dilation>0,_border_shrink>=0,_boundary_conditions,\ # _activation,0<=_learning_mode<=3 #@cli : Add a 'conv3dnnl' (3D convolutional layer followed by a normalization layer, then a non-linearity) \ # to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'kernel_size=3', 'stride=1', 'dilation=1', 'border_shrink=0', \ # 'boundary_conditions=1', 'activation=leakyrelu' and 'learning_mode=3'. nn_conv3dnnl : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint($3,1) && isint(${4=3},1) && ${5=1}>0 && "\ "isint(${7=0},0) && isint(${8=1},0,3) && isint(${10=3},0,3)" skip "${6=1}","${9=leakyrelu}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'conv3d+normalize+nl' layer '$1', with input '"$in"', $3 channels, $4x$4x$4 kernels, "\ "stride $5, dilation $6, border shrink $7, "$s$8" boundary conditions and '$9' activation." init=$_nn_init _nn_conv3d $1_conv3d,$in,${3-8},$10 _nn_normalize $1_normalize,$1_conv3d,1,3 nn_nl $1,$1_normalize,$9 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_crop : out,in,x0,y0,z0,c0,x1,y1,z1,c1,_boundary_conditions #@cli : Add a 'crop' layer to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default value: 'boundary_conditions=0'. nn_crop : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "inrange(${11=0},0,3) && min(isint([${3-11}]))" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^1] "[nn_lin] Add 'crop' layer '$1', with input '"$in"', coordinates (${3-6})-(${7-10}) and "\ $s$11" boundary conditions." _nn_modules_names.=$1, _nn_modules_types.=crop, cmin:=vmin([${3-6}],[${7-10}]) cmax:=vmax([${3-6}],[${7-10}]) _nn_$1_size:=[$cmax]-[$cmin]+1 _nn_$1_input=$in _nn_$1_properties="coordinates=(${3-6})x(${7-10})" _nn_forward.="begin(nn_crop_init_forward($1,"$in",${3-11}));"\ "nn_crop_forward($1,"$in");" _nn_backward..="nn_crop_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_distance : out,in0,_in1,_metric={ 0:squared-L2 | p>0:Lp-norm } #@cli : Add a 'distance' layer to the network (distance between two inputs, with specified metric). #@cli : Default value: 'in=. (previous layer)', nn_distance : if ['$2']=='.' in0=$_nn_latest else in0=$2 fi nn_check_layer $in0 if ['${3=.}']=='.' in1=$_nn_latest else in1=$3 fi nn_check_layer $in1 check "isvarname('$1') && ${4=0}>=0 && "[${_nn_${in0}_size}]==[${_nn_${in1}_size}] s0,s1="squared-L2 norm","L$4" e[^-1] "[nn_lib] Add 'distance' layer '$1' ("${s{$4>0}}"), with inputs '"$in0"' and '"$in1"'." _nn_modules_names.=$1, _nn_modules_types.=distance, _nn_$1_size=1,1,1,1 _nn_$1_input=$in0,$in1 _nn_$1_properties="metric="${s{$4>0}} _nn_forward.="begin(nn_distance_init_forward($1,$4));"\ "nn_distance_forward($1,"$in0","$in1");" _nn_backward..="nn_distance_backward($1,"$in0","$in1");" _nn_init.="$0 $*" _nn_latest=$1 #@cli nn_dropout : out,in,0<=dropout_rate<1 #@cli : Add a 'dropout' layer to the network. nn_dropout : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && inrange($3,0,1,1,0)" e[^-1] "[nn_lib] Add 'dropout' layer '$1', with input '"$in"' and dropout rate $3." _nn_modules_names.=$1, _nn_modules_types.=dropout, _nn_$1_size=${_nn_${in}_size} _nn_$1_input=$in _nn_$1_properties="dropout_rate=$3" _nn_forward.="begin(nn_dropout_init_forward($1,"$in",$3));"\ "nn_dropout_forward($1,"$in");" _nn_backward..="nn_dropout_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_fc : out,in,nb_channels>0,0<=_learning_mode<=3 #@cli : Add a 'fc' layer (fully connected layer) to the network. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default value: 'learning_mode=3'. nn_fc : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint($3,1) && isint(${4=3},0,3)" e[^-1] "[nn_lib] Add 'fc' layer '$1', with input '"$in"' and $3 channels." init=$_nn_init _nn_fc $1_fc,$in,${3-4} nn_rename $1,$1_fc _nn_init=${init}"$0 $* " _nn_latest=$1 # [Internal] Insert 'fc' layer, with parameter image having name '$1'. _nn_fc : _nn_modules_names.=$1, _nn_modules_types.=fc, _nn_$1_size=1,1,1,$3 _nn_$1_learning_mode=$4 _nn_$1_input=$2 _nn_$1_properties="learning_mode=$4" if !isint($$1) {prod($_nn_$2_size)+1},$3 f. 4*g/(w-1) if $4<2 1,100% j.. .,100% rm. fi # Set biases to 0 if they are not learnt => $1 fi _nn_forward.="begin(nn_fc_init_forward($1,$2));"\ "nn_fc_forward($1,$2);" _nn_backward..="begin(nn_fc_init_backward($1));"\ "nn_fc_backward($1,$2);"\ "end(nn_fc_end_backward($1));" _nn_init.="$0 $* " #@cli nn_nlfc : out,in,nb_channels>0,_activation,0<=_learning_mode<=3 #@cli : Add a 'nlfc' layer (nonlinear fully connected layer) to the network. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'activation=leakyrelu' and 'learning_mode=3'. nn_nlfc : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint($3,1) && isint(${5=3},0,3)" skip ${4=leakyrelu} e[^-1] "[nn_lib] Add 'nlfc' layer '$1', with input '"$in"', $3 channels and '$4' activation." init=$_nn_init _nn_nlfc $1_nlfc,$in,${3-5} nn_rename $1,$1_nlfc _nn_init=${init}"$0 $* " _nn_latest=$1 # [Internal] Insert 'fc' layer, with parameter image having name '$1'. _nn_nlfc : _nn_modules_names.=$1, _nn_modules_types.=nlfc, _nn_$1_size=1,1,1,$3 _nn_$1_learning_mode=$5 _nn_$1_input=$2 _nn_$1_properties="activation=$4, learning_mode=$5" if !isint($$1) {2*prod($_nn_$2_size)},$3 f. 4*g/(w-1) if $5<2 50%,100% j.. .,50% rm. fi # Set biases to 0 if they are not learnt => $1 fi _nn_forward.="begin(nn_nlfc_init_forward($1,$2));"\ "nn_nlfc_forward($1,$2,$4);" _nn_backward..="begin(nn_nlfc_init_backward($1));"\ "nn_nlfc_backward($1,$2,$4);"\ "end(nn_nlfc_end_backward($1));" _nn_init.="$0 $* " #@cli nn_fcnl : out,in,nb_neurons>0,_activation,0<=_learning_mode<=3 #@cli : Add a 'fcnl' layer (fully connected layer followed by a non-linearity) to the network. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'activation=leakyrelu' and 'learning_mode=3'. nn_fcnl : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint($3,1) && isint(${5=3},0,3)" skip ${4=leakyrelu} e[^-1] "[nn_lib] Add 'fc+nl' layer '$1' to the network, with input '"$in"', $3 channels and '$4' activation." init=$_nn_init _nn_fc $1_fc,$in,$3,$5 nn_nl $1,$1_fc,$4 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_fcnnl : out,in,nb_neurons>0,_activation,0<=_learning_mode<=3 #@cli : Add a 'fcnnl' layer (fully connected layer followed by a normalization layer, then a non-linearity) \ # to the network. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'activation=leakyrelu' and 'learning_mode=3'. nn_fcnnl : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint($3,1) && isint(${5=3},0,3)" skip ${4=leakyrelu} e[^-1] "[nn_lib] Add 'fc+normalize+nl' layer '$1' to the network, with input '"$in"', $3 channels "\ "and '$4' activation." init=$_nn_init _nn_fc $1_fc,$in,$3,$5 _nn_normalize $1_normalize,$1_fc,0,3 nn_nl $1,$1_normalize,$4 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_input : name,width,_height,_depth,_spectrum #@cli : Add a new 'input' to the network. #@cli : Default values: 'height=1', 'depth=1' and 'spectrum=1'. nn_input : check "isvarname('$1') && isint($2,1) && isint(${3=1},1) && isint(${4=1},1) && isint(${5=1},1)" e[^-1] "[nn_lib] Add input '$1', with size ($2,$3,$4,$5)." _nn_modules_names.=$1, _nn_modules_types.=input, _nn_$1_size=$2,$3,$4,$5 _nn_forward.="begin(nn_input_init_forward($1,$2,$3,$4,$5));" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_maxpool2d : out,_in,_patch_size>1 #@cli : Add a 'maxpool2d' layer (2D max pooling) to the network. #@cli : Default values: 'in=. (previous layer)' and 'patch_size=2'. nn_maxpool2d : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=2},2)" e[^-1] "[nn_lib] Add 'maxpool2d' layer '$1', with input '"$in"' and patch size $3x$3." _nn_modules_names.=$1, _nn_modules_types.=maxpool2d, _nn_$1_size:=s=[${_nn_${in}_size}];[int(s[0,2]/$3),s[2,2]] _nn_$1_input=$in _nn_$1_properties="patch_size=$3x$3" _nn_forward.="begin(nn_maxpool2d_init_forward($1,"$in",$3));"\ "nn_maxpool2d_forward($1,"$in");" _nn_backward..="nn_maxpool2d_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_maxpool3d : out,_in,_patch_size>1 #@cli : Add a 'maxpool3d' layer (3d max pooling) to the network. #@cli : Default values: 'in=. (previous layer)' and 'patch_size=2'. nn_maxpool3d : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=2},2)" e[^-1] "[nn_lib] Add 'maxpool3d' layer '$1', with input '"$in"' and patch size $3x$3x$3." _nn_modules_names.=$1, _nn_modules_types.=maxpool3d, _nn_$1_size:=s=[${_nn_${in}_size}];[int(s[0,3]/$3),s[3]] _nn_$1_input=$in _nn_$1_properties="patch_size=$3x$3x$3" _nn_forward.="begin(nn_maxpool3d_init_forward($1,"$in",$3));"\ "nn_maxpool3d_forward($1,"$in");" _nn_backward..="nn_maxpool3d_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_mul : out,in0,_in1 #@cli : Add an 'mul' layer to the network. #@cli : Default value: 'in1=. (previous layer)'. nn_mul : if ['$2']=='.' in0=$_nn_latest else in0=$2 fi nn_check_layer $in0 if ['${3=.}']=='.' in1=$_nn_latest else in1=$3 fi nn_check_layer $in1 check "isvarname('$1') && "[${_nn_${in0}_size}]==[${_nn_${in1}_size}] e[^-1] "[nn_lib] Add 'mul' layer '$1', with inputs '"$in0"' and '"$in1"'." _nn_modules_names.=$1, _nn_modules_types.=mul, _nn_$1_size=${_nn_${in0}_size} _nn_$1_input=$in0,$in1 _nn_forward.="begin(nn_mul_init_forward($1,"$in0"));"\ "nn_mul_forward($1,"$in0","$in1");" _nn_backward..="nn_mul_backward($1,"$in0","$in1");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_nl : out,_in,_activation #@cli : Add a 'nl' (nonlinearity) layer to the network. #@cli : 'activation' can be { elu | gelu | leakyrelu | linear | relu | sigmoid | sin | sinc | softmax | sqr | sqrt | \ # swish | tanh }. #@cli : Default values: 'in=. (previous layer)' and 'activation=leakyrelu'. nn_nl : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1')" skip ${3=leakyrelu} e[^-1] "[nn_lib] Add 'nl' layer '$1', with input '"$in"' and '$3' activation." _nn_modules_names.=$1, _nn_modules_types.=nl, _nn_$1_size=${_nn_${in}_size} _nn_$1_input=$in _nn_$1_properties="activation=$3" _nn_forward.="begin(nn_nl_init_forward($1,"$in"));" if ['$3']=='softmax' _nn_forward.="nn_$3_forward($1,"$in");" _nn_backward..="nn_$3_backward($1,"$in");" else _nn_forward.="nn_nl_forward($1,"$in",$3);" _nn_backward..="nn_nl_backward($1,"$in",$3);" fi _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_normalize : out,_in,_normalization_mode_,0<=_learning_mode<=3 #@cli : Add a 'normalize' layer to the network. #@cli : 'normalization_mode' can be { 0:global parameters | 1:channel-by-channel parameters } #@cli : 'learning_mode' can be { 0:no learning | 1:alpha only | 2:beta only | 3:alpha+beta } #@cli : Default values: 'in=. (previous layer)','normalization_mode=0' and 'learning_mode=3'. nn_normalize : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isbool(${3=0}) && isint(${4=3},0,3)" s0,s1=global,channel-by-channel e[^-1] "[nn_lib] Add 'normalize' layer '$1', with input '"$in"' and "${s$3}" normalization." _nn_normalize $1_normalize,$in,${3-4} nn_rename $1,$1_normalize _nn_latest=$1 # [Internal] Insert 'normalize' layer, with parameter image having name '$1'. _nn_normalize : s0,s1=global,channel-by-channel _nn_modules_names.=$1, _nn_modules_types.=normalize, _nn_$1_size=$_nn_$2_size _nn_$1_learning_mode=$4 _nn_$1_input=$2 _nn_$1_properties="normalization="${s$3}", learning_mode=$4" if !isint($$1) (1,0,0,1) # alpha,beta,running_avg,running_var if $3 r. 100%,{[$_nn_$2_size][3]} fi # channel-by-channel normalization => $1 fi _nn_forward.="begin(nn_normalize_init_forward($1,$2,$3));"\ "nn_normalize_forward($1,$2);" _nn_backward..="begin(nn_normalize_init_backward($1));"\ "nn_normalize_backward($1,$2);"\ "end(nn_normalize_end_backward($1));" _nn_init.="$0 $* " #@cli nn_patchdown2d : out,_in,_patch_size>1 #@cli : Add a 'patchdown2d' (2D downscale by patch) layer to the network. #@cli : Default values: 'in=. (previous layer)' and 'patch_size=2'. nn_patchdown2d : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=2},2)" e[^-1] "[nn_lib] Add 'patchdown2d' layer '$1', with input '"$in"' and patch size $3x$3." _nn_modules_names.=$1, _nn_modules_types.=patchdown2d, _nn_$1_size:=s=[${_nn_${in}_size}];[int(s[0,2]/$3),s[2],s[3]*$3^2] _nn_$1_input=$in _nn_$1_properties="patch_size=$3x$3" if !min($_nn_$1_size) error[0--3] "Command 'nn_patchdown2d': Specified patch size ($3x$3) is higher than "\ "input size ("${_nn_${in}_size}")." fi _nn_forward.="begin(nn_patchdown2d_init_forward($1,"$in",$3));"\ "nn_patchdown2d_forward($1,"$in");" _nn_backward..="nn_patchdown2d_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_patchdown3d : out,_in,_patch_size>1 #@cli : Add a 'patchdown3d' (3D downscale by patch) layer to the network. #@cli : Default values: 'in=. (previous layer)' and 'patch_size=2'. nn_patchdown3d : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=2},2)" e[^-1] "[nn_lib] Add 'patchdown3d' layer '$1', with input '"$in"' and patch size $3x$3x$3." _nn_modules_names.=$1, _nn_modules_types.=patchdown3d, _nn_$1_size:=s=[${_nn_${in}_size}];[int(s[0,3]/$3),s[3]*$3^3] _nn_$1_input=$in _nn_$1_properties="patch_size=$3x$3x$3" if !min($_nn_$1_size) error[0--3] "Command 'nn_patchdown3d': Specified patch size ($3x$3x$3) is higher than "\ "input size ("${_nn_${in}_size}")." fi _nn_forward.="begin(nn_patchdown3d_init_forward($1,"$in",$3));"\ "nn_patchdown3d_forward($1,"$in");" _nn_backward..="nn_patchdown3d_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_patchup2d : out,_in,_patch_size>1 #@cli : Add a 'patchup2d' (2D upscale by patch) layer to the network. #@cli : Default values: 'in=. (previous layer)' and 'patch_size=2'. nn_patchup2d : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=2},2)" e[^-1] "[nn_lib] Add 'patchup2d' layer '$1', with input '"$in"' and patch size $3x$3." _nn_modules_names.=$1, _nn_modules_types.=patchup2d, _nn_$1_size:=s=[${_nn_${in}_size}];[int(s[0,2]*$3),s[2],s[3]/$3^2] _nn_$1_input=$in _nn_$1_properties="patch_size=$3x$3" _nn_forward.="begin(nn_patchup2d_init_forward($1,"$in",$3));"\ "nn_patchup2d_forward($1,"$in");" _nn_backward..="nn_patchup2d_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_patchup3d : out,_in,_patch_size>1 #@cli : Add a 'patchup3d' (3D upscale by patch) layer to the network. #@cli : Default values: 'in=. (previous layer)' and 'patch_size=2'. nn_patchup3d : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=2,2})" e[^-1] "[nn_lib] Add 'patchup3d' layer '$1', with input '"$in"' and patch size $3x$3x$3." _nn_modules_names.=$1, _nn_modules_types.=patchup3d, _nn_$1_size:=s=[${_nn_${in}_size}];[int(s[0,3]*$3),s[3]/$3^3] _nn_$1_input=$in _nn_$1_properties="patch_size=$3x$3x$3" _nn_forward.="begin(nn_patchup3d_init_forward($1,"$in",$3));"\ "nn_patchup3d_forward($1,"$in");" _nn_backward..="nn_patchup3d_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_rename : out,_in #@cli : Add a 'rename' layer to the network. #@cli : Default value: 'in=. (previous layer)'. nn_rename : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1')" e[^-1] "[nn_lib] Add 'rename' layer '$1', with input '"$in"'." _nn_modules_names.=$1, _nn_modules_types.=rename, _nn_$1_size=${_nn_${in}_size} _nn_$1_input=$in _nn_forward.="begin(nn_rename_init_forward($1,"$in"));" _nn_backward..="nn_rename_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_resconv2d : out,_in,_kernel_size>0,_dilation>0,_boundary_conditions,0<=_learning_mode<=3 #@cli : Add a 'resconv2d' (residual 2D convolutional layer) to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'in=. (previous layer)', 'kernel_size=3', 'dilation=1', 'boundary_conditions=1' \ # and 'learning_mode=3'. nn_resconv2d : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=3},1) && isint(${5=1},0,3) && isint(${6=3},0,3)" skip "${4=1}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'resconv2d' layer '$1', with input '"$in"', $3x$3 kernels, dilation $4 and "\ $s$5" boundary conditions." init=$_nn_init nn_clone $1_clone0,$1_clone1,$in _nn_conv2d $1_conv2d,$1_clone1,{[${_nn_${in}_size}][3]},$3,1,$4,0,$5,$6 nn_add $1,$1_conv2d,$1_clone0 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_resconv2dnl : out,_in,_kernel_size>0,_dilation>0,_boundary_conditions,_activation,0<=_learning_mode<=3 #@cli : Add a 'resconv2dnl' (residual 2D convolutional layer followed by a non-linearity) to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'in=. (previous layer)', 'kernel_size=3', 'dilation=1', 'boundary_conditions=1', \ # 'activation=leakyrelu' and 'learning_mode=3'. nn_resconv2dnl : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=3},1) && isint(${5=1},0,3) && isint(${7=3},0,3)" skip "${4=1}","${6=leakyrelu}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'resconv2dnl' layer '$1', with input '"$in"', $3x$3 kernels, dilation $4, "\ $s$5" boundary conditions and '$6' activation." init=$_nn_init nn_clone $1_clone0,$1_clone1,$in _nn_conv2d $1_conv2d,$1_clone1,{[${_nn_${in}_size}][3]},$3,1,$4,0,$5,$7 nn_add $1_add,$1_conv2d,$1_clone0 nn_nl $1,$1_add,$6 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_resconv2dnnl : out,_in,_kernel_size>0,_dilation>0,_boundary_conditions,_activation,0<=_learning_mode<=3 #@cli : Add a 'resconv2dnnl' (residual 2D convolutional layer followed by a normalization layer, then a non-linearity)\ # to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'in=. (previous layer)', 'kernel_size=3', 'dilation=1', 'boundary_conditions=1', \ # 'activation=leakyrelu' and 'learning_mode=3'. nn_resconv2dnnl : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=3},1) && isint(${5=1},0,3) && isint(${7=3},0,3)" skip "${4=1}","${6=leakyrelu}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'resconv2dnnl' layer '$1', with input '"$in"', $3x$3 kernels, dilation $4, "\ $s$5" boundary conditions and '$6' activation." init=$_nn_init nn_clone $1_clone0,$1_clone1,$in _nn_conv2d $1_conv2d,$1_clone1,{[${_nn_${in}_size}][3]},$3,1,$4,0,$5,$7 _nn_normalize $1_normalize,$1_conv2d,1,3 nn_add $1_add,$1_normalize,$1_clone0 nn_nl $1,$1_add,$6 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_resconv3d : out,_in,_kernel_size>0,_dilation>0,_boundary_conditions,0<=_learning_mode<=3 #@cli : Add a 'resconv3d' (residual 3D convolutional layer) to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'in=. (previous layer)', 'kernel_size=3', 'dilation=1', 'boundary_conditions=1' \ # and 'learning_mode=3'. nn_resconv3d : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=3},1) && isint(${5=1},0,3) && isint(${6=3},0,3)" skip "${4=1}" e[^-1] "[nn_lib] Add 'resconv3d' layer '$1', with input '"$in"', $3x$3x$3 kernels, dilation $4 and "\ $s$5" boundary conditions." init=$_nn_init nn_clone $1_clone0,$1_clone1,$in _nn_conv3d $1_conv3d,$1_clone1,{[${_nn_${in}_size}][3]},$3,1,$4,0,$5,$6 nn_add $1,$1_conv3d,$1_clone0 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_resconv3dnl : out,_in,_kernel_size>0,_dilation>0,_boundary_conditions,_activation,0<=_learning_mode<=3 #@cli : Add a 'resconv3dnl' (residual 3D convolutional layer followed by a non-linearity) to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'in=. (previous layer)', 'kernel_size=3', 'dilation=1', 'boundary_conditions=1', \ # activation='leakyrelu' and 'learning_mode=3'. nn_resconv3dnl : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=3},1) && isint(${5=1},0,3) && isint(${7=1},0,3)" skip "${4=1}","${6=leakyrelu}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'resconv3dnl' layer '$1', with input '"$in"', $3x$3x$3 kernels, dilation $4, "\ $s$5" boundary conditions and '$6' activation." init=$_nn_init nn_clone $1_clone0,$1_clone1,$in _nn_conv3d $1_conv3d,$1_clone1,{[${_nn_${in}_size}][3]},$3,1,$4,0,$5,$7 nn_add $1_add,$1_conv3d,$1_clone0 nn_nl $1,$1_add,$6 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_resconv3dnnl : out,_in,_kernel_size>0,_dilation>0,_boundary_conditions,_activation,0<=_learning_mode<=3 #@cli : Add a 'resconv3dnnl' (residual 3D convolutional layer followed by a normalization layer, then a non-linearity)\ # to the network. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'in=. (previous layer)', 'kernel_size=3', 'dilation=1', 'boundary_conditions=1', \ # 'activation=leakyrelu' and 'learning_mode=3'. nn_resconv3dnnl : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=3},1) && isint(${5=1},0,3) && isint(${7=3},0,3)" skip "${4=1}","${6=leakyrelu}" s0,s1,s2,s3=dirichlet,neumann,periodic,mirror e[^-1] "[nn_lib] Add 'resconv3dnnl' layer '$1', with input '"$in"', $3x$3x$3 kernels, dilation $4, "\ $s$5" boundary conditions and '$6' activation." init=$_nn_init nn_clone $1_clone0,$1_clone1,$in _nn_conv3d $1_conv3d,$1_clone1,{[${_nn_${in}_size}][3]},$3,1,$4,0,$5,$7 _nn_normalize $1_normalize,$1_conv3d,1,3 nn_add $1_add,$1_normalize,$1_clone0 nn_nl $1,$1_add,$6 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_resfc : out,_in,0<=_learning_mode<=3 #@cli : Add a 'resfc' (residual fully connecter layer) to the network. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'in=. (previous layer)' and 'learning_mode=3'. nn_resfc : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${3=3},0,3)" e[^-1] "[nn_lib] Add 'resfc' layer '$1', with input '"$in"'." init=$_nn_init nn_clone $1_clone0,$1_clone1,$in _nn_fc $1_fc,$1_clone1,{prod(${_nn_${in}_size})},$3 nn_add $1,$1_fc,$1_clone0 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_resfcnl : out,_in,_activation,0<=_learning_mode<=3 #@cli : Add a 'resfcnl' (residual fully connecter layer followed by a non-linearity) to the network. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'in=. (previous layer)', 'activation=leakyrelu' and 'learning_mode=3'. nn_resfcnl : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${4=3},0,3)" skip ${3=leakyrelu} e[^-1] "[nn_lib] Add 'resfc+nl' layer '$1' to the network, with input '"$in"' and '$4' activation." init=$_nn_init nn_clone $1_clone0,$1_clone1,$in _nn_fc $1_fc,$1_clone1,{prod(${_nn_${in}_size})},$4 nn_add $1_add,$1_fc,$1_clone0 nn_nl $1,$1_add,$3 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_resfcnnl : out,_in,_activation,0<=_learning_mode<=3 #@cli : Add a 'resfcnnl' (residual fully connecter layer followed by a normalization layer, then a non-linearity) \ # to the network. #@cli : 'learning_mode' can be { 0:no learning | 1:weights only | 2:biases only | 3:weights+biases }. #@cli : Default values: 'in=. (previous layer)', 'activation=leakyrelu' and 'learning_mode=3'. nn_resfcnnl : if ['${2=.}']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isint(${4=3},0,3)" skip ${3=leakyrelu} e[^-1] "[nn_lib] Add 'resfc+normalize+nl' layer '$1' to the network, with input '"$in"' and '$4' activation." init=$_nn_init nn_clone $1_clone0,$1_clone1,$in _nn_fc $1_fc,$1_clone1,{prod(${_nn_${in}_size})},1 # $4 _nn_normalize $1_normalize,$1_fc,0,3 nn_add $1_add,$1_normalize,$1_clone0 nn_nl $1,$1_add,$3 _nn_init=${init}"$0 $* " _nn_latest=$1 #@cli nn_reshape : out,in,width>0,height>0,depth>0,spectrum>0 #@cli : Add a 'reshape' layer to the network. nn_reshape : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1')" e[^-1] "[nn_lib] Add 'reshape' layer '$1', with input '"$in"' and size ($3,$4,$5,$6)." _nn_modules_names.=$1, _nn_modules_types.=reshape, _nn_$1_size=$3,$4,$5,$6 _nn_$1_input=$in if prod($_nn_$1_size)!=prod(${_nn_${in}_size}) error[0--3] "Command 'nn_reshape': Cannot reshape input ("${_nn_${in}_size}") to output ("$_nn_$1_size")." fi _nn_forward.="begin(nn_reshape_init_forward($1,"$in",${3-6}));" _nn_backward..="begin(nn_reshape_init_backward($1,"$in"));" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_resize : out,in,width[%]>0,_height[%]>0,_depth[%]>0,_spectrum[%]>0,_interpolation #@cli : Add a 'resize' layer to the network. #@cli : Default values: 'height=depth=spectrum=100%' and 'interpolation=3'. nn_resize : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && $3>0 && ${4=100%}>0 && ${5=100%}>0 && ${6=100%}>0" skip ${7=3} if ispercentage($3) w:=max(1,round([${_nn_${in}_size}][1]*$3)) else w=$3 fi if ispercentage($4) h:=max(1,round([${_nn_${in}_size}][1]*$4)) else h=$4 fi if ispercentage($5) d:=max(1,round([${_nn_${in}_size}][2]*$5)) else d=$5 fi if ispercentage($6) s:=max(1,round([${_nn_${in}_size}][3]*$6)) else s=$6 fi e[^-1] "[nn_lib] Add 'resize' layer '$1', with input '"$in"' and output size ("$w","$h","$d","$s")." _nn_modules_names.=$1, _nn_modules_types.=resize, _nn_$1_size=$w,$h,$d,$s _nn_$1_input=$in _nn_$1_properties="interpolation=$7" _nn_forward.="begin(nn_resize_init_forward($1,"$in","$w","$h","$d","$s",$7));"\ "nn_resize_forward($1,"$in");" _nn_backward..="nn_resize_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_run : out,in,"command",_width[%]>0,_height[%]>0,_depth[%]>0,_spectrum[%]>0 #@cli : Add a 'run' layer to the network. #@cli : Default values: 'width=height=depth=spectrum=100%'. nn_run : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && ${4=100%}>0 && ${5=100%}>0 && ${6=100%}>0 && ${7=100%}>0" if ispercentage($4) w:=max(1,round([${_nn_${in}_size}][0]*$4)) else w=$4 fi if ispercentage($5) h:=max(1,round([${_nn_${in}_size}][1]*$5)) else h=$5 fi if ispercentage($6) d:=max(1,round([${_nn_${in}_size}][2]*$6)) else d=$6 fi if ispercentage($7) s:=max(1,round([${_nn_${in}_size}][3]*$7)) else s=$7 fi e[^-1] "[nn_lib] Add 'run' layer '$1', with input '"$in"', command '$3' and output size ("$w","$h","$d","$s")." _nn_modules_names.=$1, _nn_modules_types.=run, _nn_$1_size=$w,$h,$d,$s _nn_$1_input=$in _nn_$1_properties="command='$3'" _nn_forward.="begin(nn_run_init_forward($1,"$in",'$3',"$w","$h","$d","$s"));"\ "nn_run_forward($1,"$in");" _nn_backward..="nn_run_backward($1,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_split : name0,name1,in,nb_channels0 #@cli : Add a 'split' layer to the network. nn_split : if ['$3']=='.' in=$_nn_latest else in=$3 fi nn_check_layer $in check "isvarname('$1') && isvarname('$2')" e[^-1] "[nn_lib] Add 'split' layer, with input '"$in"' and outputs '$1' and '$2'." _nn_modules_names.=$1,$2, _nn_modules_types.=split,split, _nn_$1_size:=[[${_nn_${in}_size}][0,3],$4] _nn_$1_input=$2 _nn_$2_size:=[${_nn_${in}_size}]-[0,0,0,$4] _nn_$2_input=$2 _nn_forward.="begin(nn_split_init_forward($1,$2,"$in",$4));"\ "nn_split_forward($1,$2,"$in");" _nn_backward..="nn_split_backward($1,$2,"$in");" _nn_init.="$0 $* " _nn_latest=$1 #@cli nn_loss_binary_crossentropy : out,in,ground_truth #@cli : Add a 'binary_crossentropy' loss to the network (binary cross entropy). nn_loss_binary_crossentropy : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isvarname('$3')" e[^-1] "[nn_lib] Add binary cross entropy loss '$1', with input '"$in"' and ground truth '$3'." _nn_modules_names.=$1, _nn_modules_types.=loss_binary_crossentropy, _nn_$1_size=1,1,1,1 _nn_$1_input=$in _nn_$1_properties="ground_truth=$3" _nn_loss.="begin(nn_loss_binary_crossentropy_init_forward($1));"\ "nn_loss_binary_crossentropy_forward($1,"$in",$3);"\ "end(nn_loss_binary_crossentropy_end_forward($1));" _nn_backward..="nn_loss_binary_crossentropy_backward($1,"$in",$3);" _nn_latest=$1 #@cli nn_loss_crossentropy : out,in,ground_truth #@cli : Add a 'crossentropy' loss to the network (cross entropy). nn_loss_crossentropy : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isvarname('$3')" e[^-1] "[nn_lib] Add cross entropy loss '$1', with input '"$in"' and ground truth '$3'." _nn_modules_names.=$1, _nn_modules_types.=loss_crossentropy, _nn_$1_size=1,1,1,1 _nn_$1_input=$in _nn_$1_properties="ground_truth=$3" _nn_loss.="begin(nn_loss_crossentropy_init_forward($1));"\ "nn_loss_crossentropy_forward($1,"$in",$3);"\ "end(nn_loss_crossentropy_end_forward($1));" _nn_backward..="nn_loss_crossentropy_backward($1,"$in",$3);" _nn_latest=$1 #@cli nn_loss_mse : out,in,ground_truth #@cli : Add a 'mse' loss to the network (mean-squared error). nn_loss_mse : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isvarname('$3')" e[^-1] "[nn_lib] Add MSE loss '$1', with input '"$in"' and ground truth '$3'." _nn_modules_names.=$1, _nn_modules_types.=loss_mse, _nn_$1_size=1,1,1,1 _nn_$1_input=$in _nn_$1_properties="ground_truth=$3" _nn_loss.="begin(nn_loss_mse_init_forward($1));"\ "nn_loss_mse_forward($1,"$in",$3);"\ "end(nn_loss_mse_end_forward($1));" _nn_backward..="nn_loss_mse_backward($1,"$in",$3);" _nn_latest=$1 #@cli nn_loss_normp : out,in,ground_truth,_metric={ 0:squared-L2 | p>0:Lp-norm } #@cli : Add a 'normp' loss to the network (||out - ground_truth||_p). #@cli : Default value: 'metric=1'. nn_loss_normp : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isvarname('$3') && isint(${4=1},0)" e[^-1] "[nn_lib] Add normp loss '$1', with input '"$in"', ground truth '$3' and metric $4." _nn_modules_names.=$1, _nn_modules_types.=loss_normp, _nn_$1_size=1,1,1,1 _nn_$1_input=$in _nn_$1_properties="ground_truth=$3, metric=$4" _nn_loss.="begin(nn_loss_normp_init_forward($1,$4));"\ "nn_loss_normp_forward($1,"$in",$3);"\ "end(nn_loss_normp_end_forward($1));" _nn_backward..="nn_loss_normp_backward($1,"$in",$3);" _nn_latest=$1 #@cli nn_loss_softmax_crossentropy : out,in,ground_truth #@cli : Add a 'softmax_crossentropy' loss to the network (softmax followed by cross entropy). nn_loss_softmax_crossentropy : if ['$2']=='.' in=$_nn_latest else in=$2 fi nn_check_layer $in check "isvarname('$1') && isvarname('$3')" e[^-1] "[nn_lib] Add softmax + cross entropy loss '$1', with input '"$in"' and ground truth '$3'." _nn_modules_names.=$1, _nn_modules_types.=loss_softmax_crossentropy, _nn_$1_size=1,1,1,1 _nn_$1_input=$2 _nn_$1_properties="ground_truth=$3" _nn_loss.="begin(nn_loss_softmax_crossentropy_init_forward($1));"\ "nn_loss_softmax_crossentropy_forward($1,"$in",$3);"\ "end(nn_loss_softmax_crossentropy_end_forward($1));" _nn_backward..="nn_loss_softmax_crossentropy_backward($1,"$in",$3);" _nn_latest=$1 #@cli nn_print #@cli : Print info on current neural network. nn_print : e[^-1] "[nn_lib] Print info on current neural network." use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r b=$_vt100_b v 0 e " * List of modules:" repeat narg($_nn_modules_names)-1 { type=${arg0\ $>,$_nn_modules_types} name=${arg0\ $>,$_nn_modules_names} learning_mode=${_nn_${name}_learning_mode} e " - Module: "$b$name$n" (type: "$g$type$n")" if ['${_nn_${name}_input}']!=0 s={`find(['${_nn_${name}_input}'],_',')>0?_'s':0`} e " * Input"$s": "$c${_nn_${name}_input}$n fi e " * Output size: "$c${_nn_${name}_size}$n if ['${_nn_${name}_properties}']!=0 ies={`find(['${_nn_${name}_properties}'],_',')>0?'ies':[_'y',0,0]`} e " * Propert"$ies": "$c${_nn_${name}_properties}$n fi if ${$name} e " * Parameters: "$c{$name,whds}$n fi } e "\n * Total: "${b}{narg($_nn_modules_names)-1}$n" modules, "$b${-nn_size}$n" parameters.\n" #@cli nn_trainer : name,_loss,_learning_rate>0,_optimizer,_scheduler #@cli : Add a network trainer to the network. #@cli : 'optimizer' can be { sgd | rmsprop | adam | adamax }. #@cli : 'scheduler' can be { constant | linear | exponential | adaptive }. #@cli : Default values: 'loss=. (previous loss)', 'learning_rate=2e-4', 'optimizer=rmsprop' and 'scheduler=constant'. nn_trainer : if ['${2=.}']=='.' loss=$_nn_latest else loss=$2 fi nn_check_layer $loss check "isvarname('$1') && ${3=2e-4}>0 && isvarname('${4=rmsprop}') && isvarname('${5=constant}')" e[^-1] "[nn_lib] Add trainer '$1' for loss '"$loss"', with learning rate '$3', $4 optimizer and $5 scheduler." _nn_modules_names.=$1, _nn_modules_types.=trainer, _nn_$1_size=0,0,0,0 _nn_$1_properties="loss="$loss", learning_rate=$3, optimizer=$4, scheduler=$5" if !isint($$1) # (iteration,learning_rate,initial_learning_rate,previous_loss,best_loss; # {optimizer variables}; # {scheduler variables}) l[] { (0,$3,$3,inf,inf) _nn_optimizer_$4 _nn_scheduler_$5 a y => $1 } else =[$1] $3,1,0 # Set learning rate in existing trainer =[$1] $3,2,0 fi # Generate code for updating network weights. _nn_$1_update="begin(nn_trainer_init_backward($1,"$loss",$4,$5));end(" repeat narg($_nn_modules_names)-1 { type=${arg0\ $>,$_nn_modules_types} name=${arg0\ $>,$_nn_modules_names} learning_mode=${_nn_${name}_learning_mode} if isin('$type','conv2d','conv3d','fc','nlfc','normalize') _nn_$1_update.="nn_optimizer_$4_update_"$type"("$name,$learning_mode");" # Allocate data for storing optimizer extra data (moments,...). if $learning_mode if ['$4']=='rmsprop' name_g2=${name}_g2 if !isint(${$name_g2}) +f[$name] 0 => $name_g2 fi _nn_trainer_data.=$name_g2, elif isin('$4','adam','adamax') name_m,name_v=${name}_m,${name}_v if !isint(${$name_m}) +f[$name] 0 => $name_m fi if !isint(${$name_v}) +f[$name] 0 => $name_v fi _nn_trainer_data.=$name_m,$name_v, fi fi fi } _nn_$1_update.="nn_trainer_update($1,"$loss",$5));" # <- Keep this as the last thing done in an 'end()' block! _nn_update=$_nn_$1_update _nn_optimizer_sgd : 1 _nn_optimizer_rmsprop : 1 _nn_optimizer_adam : 1 _nn_optimizer_adamax : 1 _nn_scheduler_constant : 1 _nn_scheduler_linear : 1 _nn_scheduler_exponential : 1 _nn_scheduler_adaptive : (0,0,0) #@cli nn_size #@cli: Return size of the current network (i.e. number of stored parameters). nn_size : e[^-1] "[nn_lib] Return size of the current network." siz=0 repeat narg($_nn_modules_names)-1 { type=${arg0\ $>,$_nn_modules_types} name=${arg0\ $>,$_nn_modules_names} if isin('$type','conv2d','conv3d','fc','nlfc','normalize') siz+={$name,whds} fi } u $siz #@cli nn_load : 'filename.gmz',_include_trainer_data={ 0:no | 1:yes } #@cli : Load and initialize network saved as a .gmz file. #@cli : Neural network files can be only loaded in .gmz format. #@cli : Default value: 'include_trainer_data=1'. +nn_load : check "s=['$1']; find(s,'.gmz')==size(s) - 4 && isbool(${2=1})" e[^-1] "[nn_lib] Load network from file '$1'." l[] { "$1" run {t} rm. if !$2 # Discard training data nmd $_nn_modules_names k[${}] fi } #@cli nn_save : 'filename.gmz',_include_trainer_data={ 0:no | 1:yes } #@cli : Save current network as a .gmz file. #@cli : `.gmz` is mandatory extension, specifying another file extension will throw an error. #@cli : Default value: 'include_trainer_data=1'. nn_save : check "s=['$1']; find(s,'.gmz')==size(s) - 4 && isbool(${2=1})" s0,s1=""," (include trainer data)" e[^-1] "[nn_lib] Save current network as file '$1'"$s$2"." if $2 sel=$_nn_trainer_data fi c= repeat narg($_nn_modules_names)-1 { type=${arg0\ $>,$_nn_modules_types} name=${arg0\ $>,$_nn_modules_names} if isin('$type','conv2d','conv3d','fc','nlfc','normalize','trainer') sel.=$c$name c=, fi } ('$_nn_init') autocrop. {'" "'} => _nn_init o[$sel,-1] "$1" rm. #@cli nn_store : 'variable_name',_include_trainer_data={ 0:no | 1:yes } #@cli : Store current network into a variable. #@cli : Default value: 'include_trainer_data=1'. nn_store : check "isbool(${2=1})" s0,s1=""," (include trainer data)" e[^-1] "[nn_lib] Store current network into variable '$1'"$s$2"." if $2 sel=$_nn_trainer_data fi c= repeat narg($_nn_modules_names)-1 { type=${arg0\ $>,$_nn_modules_types} name=${arg0\ $>,$_nn_modules_names} if isin('$type','conv2d','conv3d','fc','nlfc','normalize','trainer') sel.=$c$name c=, fi } ('$_nn_init') autocrop. {'" "'} => _nn_init +store[$sel,-1] "$1" rm. #---------------------------------- # #@cli :: Arrays, Tiles and Frames # #---------------------------------- #@cli array : M>0,_N>0,_expand_type={ 0:min | 1:max | 2:all } #@cli : Create MxN array from selected images. #@cli : Default values: 'N=M' and 'expand_type=0'. #@cli : $ image.jpg array 3,2,2 array : check "isint($1,1) && isint(${2=$1},1)" skip ${3=0} e[^-1] "Create $1x$2 array from image$?, with expand type $3." r0:=100/max($1,$2) r1:=100/min($1,$2) r2=100 r ${r$3}%,${r$3}%,1,100%,2 r {$1*100}%,{$2*100}%,1,100%,0,2 #@cli array_fade : M>0,_N>0,0<=_fade_start<=100,0<=_fade_end<=100,_expand_type={0:min | 1:max | 2:all} #@cli : Create MxN array from selected images. #@cli : Default values: 'N=M', 'fade_start=60', 'fade_end=90' and 'expand_type=1'. #@cli : $ image.jpg array_fade 3,2 array_fade : skip ${2=$1},${3=60},${4=90},${5=1} e[^-1] "Create $1x$2 array of ($3%,$4%) faded tiles from image$?, with expand type $5." foreach { . shift.. {round(w/2)},{round(h/2)},1,1,2 fade_diamond $3,$4 } array $1,$2,$5 #@cli array_mirror : N>=0,_dir={ 0:x | 1:y | 2:xy | 3:tri-xy },_expand_type={ 0 | 1 } #@cli : Create 2^Nx2^N array from selected images. #@cli : Default values: 'dir=2' and 'expand_type=0'. #@cli : $ image.jpg array_mirror 2 array_mirror : skip ${2=2},${3=0} e[^-1] "Create a 2^$1x2^$1 mirrored-array from image$?, with expand type $2." repeat $1 { if !$3 if $2>=3 r 33%,33%,100%,100%,2 else r 50%,50%,100%,100%,2 fi fi foreach { if !$2 +mirror x a x elif $2==1 +mirror y a y else +mirror x a x +mirror y a y if $2==3 r 150%,150%,1,100%,0,2,1,1 fi fi } } #@cli array_random : Ms>0,_Ns>0,_Md>0,_Nd>0 #@cli : Create MdxNd array of tiles from selected MsxNs source arrays. #@cli : Default values: 'Ns=Ms', 'Md=Ms' and 'Nd=Ns'. #@cli : $ image.jpg +array_random 8,8,15,10 array_random : skip ${2=$1},${3=$1},${4=$2} e[^-1] "Create $3x$4 array of tiles from $1x$2 array$?." foreach { nm={n} split_tiles $1,$2 repeat $3 { repeat $4 { [{u($1*$2-1)}] } } rm[0-{$1*$2-1}] append_tiles $3,$4 => $nm } #@cli frame : axes,size[%]>=0,_col1,...,_colN #@cli : Insert outer frame in selected images, along the specified axes. #@cli : 'axes' can be { x | y | z | xy | xz | yz | xyz }. #@cli : Default value: 'axes=xy', 'size=10%', 'col1=col2=col3=col4=255'. #@cli : $ image.jpg frame xy,10%,255,128,0 frame : check "${2=10%}>=0" skip "${1=xy},${3=255},${4=$3},${5=$3},${6=255}" e[^-1] "Insert outer frame in image$? along ax"{`size(['$1'])>1?_'e':'i'`}"s '$1', with size $2 and color (${3--1})." no_x,no_y,no_z:="arg = ['$1']; [ find(arg,_'x')<0, find(arg,_'y')<0, find(arg,_'z')<0 ]" foreach { {"const siz2 = 2*(ispercentage($2)?floor(max(w,h,d)*$2):$2); [ w + ($no_x?0:siz2), h + ($no_y?0:siz2), d + ($no_z?0:siz2), s ]"} fc. ${3--1} j. ..,0.5~,0.5~,0.5~ k. } #@cli frame_blur : _sharpness>0,_size>=0,_smoothness,_shading,_blur #@cli : Draw RGBA-colored round frame in selected images. #@cli : Default values: 'sharpness=10', 'size=30', 'smoothness=0', 'shading=1' and 'blur=3%'. #@cli : $ image.jpg frame_blur 3,30,8,10% frame_blur : skip ${1=10},${2=30},${3=0},${4=1},${5=3%} e[^-1] "Draw round frame on image$?, with sharpness $1, size $2, smoothness $3, shading $4 and blur $5." to_rgba foreach { nm={n} 100%,100%,1,1,"-(abs(x/w-0.5)^$1 + abs(y/h-0.5)^$1)^(1/$1)" >=. $2% if $4 distance. 1 n. 0,1 *. -1 +. 1 ^. {1/$4} fi b. $3 +b.. $5 mv. -3 blend_fade[0,1] . rm. => $nm } #@cli frame_cube : _depth>=0,_centering_x,_centering_y,_left_side={ 0:normal | 1:mirror-x | 2:mirror-y | 3:mirror-xy },\ # _right_side,_lower_side,_upper_side #@cli : Insert 3D frames in selected images. #@cli : Default values: 'depth=1', 'centering_x=centering_y=0' and 'left_side=right_side,lower_side=upper_side=0'. #@cli : $ image.jpg frame_cube , frame_cube : check "${1=1}>=0" skip ${2=0},${3=0},${4=0},${5=0},${6=0},${7=0} e[^-1] "Insert 3D frame in image$?, with depth $1, centering point ($2,$3) and orientations (${4--1})." foreach { nm={n} split_opacity if $!==2 frame_cube ${1--1} a c # Manage image with alpha-channel. else m:=max(w,h) w,h,s={w},{h},{s} imageplane3d c3d /3d. $w,$h,1 +_frame_cube[0] $4 r3d. 0,1,0,-90 +3d. -0.5,0,-0.5 # Left side. +_frame_cube[0] $5 r3d. 0,1,0,90 +3d. 0.5,0,-0.5 # Right side. +_frame_cube[0] $6 r3d. 1,0,0,-90 +3d. 0,0.5,-0.5 # Lower side. +_frame_cube[0] $7 r3d. 1,0,0,90 +3d. 0,-0.5,-0.5 # Upper side. +3d 0,0,1 +3d *3d $w,$h,$m # Append sides together. f=1000 cx=$2*$w/2*($f+$m*$1)/$f cy=$3*$h/2*($f+$m*$1)/$f s3d r[2] 3,{{2,h}/3},1,1,-1 f[2] "i(2,y)<0.5?i:i+(!x?"$cx":x==1?"$cy":($1-1)*"$m")" y[2] a y *3d 2 {2*$w},{2*$h},1,$s f3d {2*$f} j3d. ..,50%,50%,0,1,2,0,0 rm.. r $w,$h,1,100%,2 fi => $nm } _frame_cube : if $1==1 r3d. 0,1,0,180 rv3d. elif $1==2 r3d. 1,0,0,180 rv3d. elif $1==3 r3d. 0,0,1,180 fi #@cli frame_fuzzy : size_x[%]>=0,_size_y[%]>=0,_fuzzyness>=0,_smoothness[%]>=0,_R,_G,_B,_A #@cli : Draw RGBA-colored fuzzy frame in selected images. #@cli : Default values: 'size_y=size_x', 'fuzzyness=5', 'smoothness=1' and 'R=G=B=A=255'. #@cli : $ image.jpg frame_fuzzy 20 frame_fuzzy : skip ${2=$1},${3=5},${4=1},${5=255},${6=$5},${7=$6},${8=255} e[^-1] "Draw $1x$2 fuzzy frame on image$?, with fuzzyness $3, smoothness $4 and RGBA color ($5,$6,$7,$8)." to_rgba foreach { 100%,100%,1,1,1 padx:=ispercentage($1)?$1*(w-1)/2:$1 pady:=ispercentage($2)?$2*(h-1)/2:$2 rectangle. $padx,$pady,{w-1-$padx},{h-1-$pady} spread. $3 b. $4 100%,100%,1,4 fc. ${5-8} j[0] [2],0,0,0,0,1,[1] k[0] } #@cli frame_painting : _size[%]>=0,0<=_contrast<=1,_profile_smoothness[%]>=0,_R,_G,_B,_vignette_size[%]>=0,\ # _vignette_contrast>=0,_defects_contrast>=0,0<=_defects_density<=100,_defects_size>=0,_defects_smoothness[%]>=0,\ # _serial_number #@cli : Add a painting frame to selected images. #@cli : Default values: 'size=10%', 'contrast=0.4', 'profile_smoothness=6%', 'R=225', 'G=200', 'B=120', \ # 'vignette_size=2%', 'vignette_contrast=400', 'defects_contrast=50', 'defects_density=10', 'defects_size=1', \ # 'defects_smoothness=0.5%' and 'serial_number=123456789'. #@cli : $ image.jpg frame_painting , frame_painting : check "${1=10%}>=0 && ${2=0.4}>=0 && $2<=1 && ${3=6%}>=0 && ${7=2%}>=0 && ${8=400}>=0 && ${9=50}>=0 && ${10=10}>=0 && $10<=100 && ${11=1}>=0 && ${12=0.5%}>=0" skip ${4=225},${5=200},${6=120},${13=123456789} e[^-1] "Add painting frame to image$?, with size $1, contrast $2, profile smoothness $3, color (${4-6}), vignette size $7, vignette strength $8, defects contrast $9, defects density $10, defects size $11, defects smoothness $12 and serial number $13." if !$1 return fi foreach { $1,$1 s:=max(w,h) rm. # Determine size of the frame ('${dec2bin\ $13}') -. {'0'} r. $s # Generate frame profile from serial number transpose. b. $3 n. {1-$2},{1+$2} +r. {{-2,w}+2*$s},100%,1,1 # Upper frame +mirror. y # Lower frame mv... $! transpose. r. 100%,{-4,h+2*$s},1,1 # Left frame +mirror. x # Right frame ...,...,1,1,1 polygon. 3,0,0,{$s-1},{$s-1},0,{$s-1},1,0 polygon. 3,100%,0,{w-$s},100%,100%,100%,1,0 # Upper/lower mask ..,..,1,1,1 polygon. 3,1,0,100%,{$s-2},100%,0 polygon. 3,1,100%,100%,{h-$s+1},100%,100%,1,0 # Left/right mask _frame_painting[-6--3] ${4-6},${9-12} # Add colors + defects # Build full frame picture. {-7,w+2*$s},{-7,h+2*$s},1,3 j. [-7],0,0,0,0,1,...,1 rm[-7] mirror... y j. [-6],0,{h-$s},0,0,1,...,1 rm[-6,-3] j. [-4],0,0,0,0,1,..,1 rm[-4] mirror.. x j. ...,{w-$s},0,0,0,1,..,1 rm[-3,-2] ..,..,1,1,-255 r. ..,..,1,1,0,0,0.5,0.5 +. 255 +b. $7 n. 0,$8 max[-2,-1] c. 0,255 # Frame opacity. a[-2--1] c r.. .,.,1,100%,0,0,0.5,0.5 blend alpha # Insert initial image into frame picture. } _frame_painting : # Add color + texture to each frame part. foreach { +*. $2 +*.. $3 *... $1 a[-3--1] c 100%,100% i=0 do rand. 0,1 remove_pixels. {100-$5}% b. $6 >=. 50% i+=1 while "m=$5/200;(iam+0.2) && "$i"<10" b. $7 g. +[-2,-1] n. -$4,$4 +[-2,-1] c. 0,255 } #@cli frame_pattern : M>=3,_constrain_size={ 0 | 1 } : M>=3,_[frame_image],_constrain_size={ 0 | 1 } #@cli : Insert selected pattern frame in selected images. #@cli : Default values: 'pattern=0' and 'constrain_size=0'. #@cli : $ image.jpg frame_pattern 8 frame_pattern : check $1>=3 skip "${2=0},${3=0}" to_colormode 0 if ${"is_image_arg $2"} # Frame from specified image. e[^-1] "Insert $1x$1 pattern frame on image$?, using frame image$2." pass$2 0 foreach[^-1] { pass. wh={0,[w,h]} +r[1] {0,max(1,w/($1-2))},{0,max(1,h/($1-2))},1,100%,2 r[0] {{0,w}+2*w},{{0,h}+2*h},1,100%,0,0,0.5,0.5 [-1]x{$1+2} a[{-$1-2}--1] x j[0] .,0,0 j[0] .,0,{{0,h}-1-h} rm. [-1]x{$1+1} a[{-$1-2}--1] y j[0] .,0,0 j[0] .,{{0,w}-1-w} rm. if $3 r[0] $wh,1,100%,2 fi rm. } rm. else # Self-frame. e[^-1] "Insert $1x$1 self-pattern frame on image$?." foreach { wh:=w,h +r {max(1,w/($1-2))},{max(1,h/($1-2))},1,100%,2 r.. {$1*w},{$1*h},1,100%,0,0,0.5,0.5 [-1]x{$1+2} a[{-$1-2}--1] x j... .,0,0 j... .,0,{{-3,h}-1-h} rm. [-1]x{$1+1} a[{-$1-2}--1] y j.. .,0,0 j.. .,{{-2,w}-1-w} rm. if $3 r $wh,1,100%,2 fi } fi #@cli frame_round : size_x[%]>=0,size_y[%]>=0,radius[%]>=0,_smoothness[%]>=0,_col1,...,_colN #@cli : Insert an inner round frame in selected images. #@cli : Default values: 'size_x=size_y=5%, 'radius=30%', 'smoothness=0' and 'col=0,0,0,255'. frame_round : check "${1=5%}>=0 && ${2=$1}>=0 && ${3=30%}>=0 && ${4=0}>=0" skip "${5=255},${6=$5},${7=$5},${8=255}" e[^-1] "Insert $1x$2 round frame on image$?, with radius $3, smoothness $4 and color (${5--1})." if !max($1,$2,$3) return fi foreach { fwM,fhM:=round([w,h]/2) fw,fh:=round([ispercentage($1)?lerp(0,$fwM,$1):$1,ispercentage($2)?lerp(0,$fhM,$2):$2]) iw,ih:=[w,h]-2*[$fw,$fh] if $iw<=0" || "$ih<=0 fc ${5--1} else R:=ispercentage($3)?max(w,h)*$3:$3 $iw,$ih shape_circle $R shift. 50%,50%,0,0,2 ==. 0 x0,y0,x1,y1:=w2=round(w/2);h2=round(w/2);[-w2,-h2,w#1-w2,h#1-h2] j.. .,$x0,$y0 j.. .,$x1,$y0 j.. .,$x1,$y1 j.. .,$x0,$y1 rm. frame. x,$fw,1 frame. y,$fh,1 b. $4 1,1,1,{0,s},${5--1} r. ..,..,1,100% j... .,0,0,0,0,1,.. k... fi } #@cli frame_seamless : frame_size>=0,_patch_size>0,_blend_size>=0,\ # _frame_direction={ 0:inner (preserve image size) | 1:outer } #@cli : Insert frame in selected images, so that tiling the resulting image makes less visible seams. #@cli : Default values: 'patch_size=7', 'blend_size=5' and 'frame_direction=1'. #@cli : $ image.jpg +frame_seamless 30 array 2,2 frame_seamless : check "$1>=0 && isint(${2=7},1) && isint(${3=5},0)" skip ${4=1} s0="inner" s1="outer" e[^-1] "Insert "${s{!!$4}}" seamless frame in image$?, with size $1, patch size $2 and blend size $3." foreach { w2,h2:=round([w,h]/2) w4,h4:=round([w,h]/4) if !$4 r {max(1,w-$1)},{max(1,h-$1)},1,100%,0,0,0.5,0.5 fi 100%,100%,1,1,-1 r[-2,-1] {w+$1},{h+$1},1,100%,0,0,0.5,0.5 n. 0,1 shift -$w2,-$h2,0,0,2 inpaint_matchpatch.. [1],0,$2,10,$3 rectangle. $1,$1,{w-1-$1},{h-1-$1} shift -$w4,-$h4,0,0,2 inpaint_matchpatch.. [1],0,$2,10,$3 rm. shift {$w4+$w2},{$h4+$h2},0,0,2 } #@cli img2ascii : _charset,_analysis_scale>0,_analysis_smoothness[%]>=0,_synthesis_scale>0,_output_ascii_filename #@cli : Render selected images as binary ascii art. #@cli : This command returns the corresponding the list of widths and heights (expressed as a number of characters) #@cli : for each selected image. #@cli : Default values: 'charset=[ascii charset]', 'analysis_scale=16', 'analysis_smoothness=20%', \ # 'synthesis_scale=16' and '_output_ascii_filename=[undefined]'. #@cli : $ image.jpg img2ascii , img2ascii : check "${2=16}>0 && ${3=20%}>=0 && ${4=16}>0" skip "${1= !\042#$%&\047()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\133\\\135^_\140abcdefghijk"\ "lmnopqrstuvwxyz\173|\174~}","${5=}" e[^-1] "Render image$? as binary ascii art, with charset '$1', analysis scale $2, analysis smoothness $3, synthesis scale $4 and output ascii filename '$5'." is_multi:=$!>1 # Generate dictionaries for image analysis and synthesis. l[] { ('"$1"') repeat w { C={`92`}${dec2oct\ {0,@$>}} 0 t. $C,0,0,$2,1,1 0 t. $C,0,0,$4,1,1 } rm[0] = -1 = -1,0,100% autocrop = 0 = 0,0,100% l[0--2:2] { r {${-max_w}+1},100%,1,1,0,0,0.5 b $3 n 0,255 a z } l[1--1] { r {${-max_w}+1},100%,1,1,0,0,0.5 a z } } w,h={-2,[w,h]} # Transform selected images to ascii art. repeat $!-2 { l[$>,-2,-1] { luminance[0] n[0] 0,255 nw,nh={0,round([w/$w,h/$h],1,1)} if $> list_wh=$list_wh,$nw,$nh else list_wh=$nw,$nh fi s[0] y,-$h s[0--3] x,-$w r[0--3] $w,$h,1,1,0,0 repeat $!-2 { l[$>,-2,-1] { rprogress {$>*100/($!-2)} ri[0] [1] -[0] [1] sqr[0] r[0] 1,1,100%,1,2 y[0] C={0,ym} rm[0] +slices[1] $C mv. 0 if narg("$5") +f[0] $C a[0,-1] c fi } } append_tiles[0--2] $nw,$nh if narg("$5") s[0] c l[1] { # Export as text file. r $nw,$nh,1,1,1 ('"$1"') map[0] . k[0] s y i[1-$!] ('\n') a x if $is_multi filename=${filename\ "$5",$>} else filename="$5" fi ot $filename rm } fi } } rm[-2,-1] u $list_wh #@cli imagegrid : M>0,_N>0 #@cli : Create MxN image grid from selected images. #@cli : Default value: 'N=M'. #@cli : $ image.jpg imagegrid 16 imagegrid : skip ${2=$1} e[^-1] "Create $1x$2 image grid from image$?." foreach { ({w},{h}) ($1,$2) /[-2,-1] round. 1 r.. {^},..,..,2 rm. ({w},{h}) ($1,$2) *[-2,-1] r.. {^},..,..,2 rm. $1,$2,1,.,1 shift. 1,1 ri. ..,0,2 * } #@cli imagegrid_hexagonal : _resolution>0,0<=_outline<=1 #@cli : Create hexagonal grids from selected images. #@cli : Default values: 'resolution=32', 'outline=0.1' and 'is_antialiased=1'. #@cli : $ image.jpg imagegrid_hexagonal 24 imagegrid_hexagonal : check "isint(${1=32},1) && ${2=0.1}>=0 && $2<=1" e[^-1] "Create hexagonal grid(s) from image$?, with resolution $1 and outline $2." foreach { # Generate hexagonal grid. l[] { # Generate hexagon. ({'CImg3d'},6,6) (0;{2*pi}) -. {pi/2} r. 1,7,1,1,3 +sin. cos.. a[-2,-1] x rows. 0,5 z. 0,2 2,6,1,1,3,0 1,100%,1,1,y ++. 1 %. 6 rv[-2,-1] a[-3--1] x 3,100%,1,1,1 1,100%,1,1,1 y a y *3d. {1-$2} # Generate minimal pattern (2x2 hexagons). ++3d {sqrt(3)} ++3d {sqrt(3)/2},1.5 col3d... 2 col3d.. 3 col3d. 4 +3d /3d 1.5 } # Duplicate it to get a grid with correct size. ny:=1+round(0.5*$1,1,1) nx={0,1+round($1*w/h*3/(sqrt(3)*4),1,1)} array3d. $nx,$ny,1,{4*sqrt(3)/3},2 c3d. *3d. {0,h/$1} # Fill grid with image colors. [0],[0] j3d. ..,50%,50%,0,1,2,0,0 rm.. blend shapeaverage0 } #@cli imagegrid_triangular : pattern_width>=1,_pattern_height>=1,_pattern_type,0<=_outline_opacity<=1,\ # _outline_color1,... #@cli : Create triangular grids from selected images. #@cli : 'pattern type' can be { 0:horizontal | 1:vertical | 2:crossed | 3:cube | 4:decreasing | 5:increasing }. #@cli : Default values: 'pattern_width=24', 'pattern_height=pattern_width', 'pattern_type=0', 'outline_opacity=0.1' \ # and 'outline_color1=0'. #@cli : $ image.jpg imagegrid_triangular 6,10,3,0.5 imagegrid_triangular : check "$1>=1 && ${2=$1}>=1 && isint(${3=0},0,5)" skip ${4=0},${5=0} s0="horizontal" s1="vertical" s2="crossed" s3="cube" e[^-1] "Create triangular grid(s) from image$?, with pattern width $1, height $2, pattern type '"${s$3}"', "\ "outline opacity $4 and outline color (${5--1})." # Create triangular patterns and outlines (always square!). M:=max($1,$2) if $3==4" || "$3==5 # Decreasing/Increasing. $M,$M,1,1,x>y ++. 2 a[-2,-1] x ++. 4 a[-2,-1] y $M,$M,1,1,"!x || !y || x==y" r. 200%,200%,1,1,0,2 a[-2,-1] c if $3==5 mirror. y fi elif $3==3 # Cube. $M,$M,1,1,x>y 100%,100%,1,1,w-1-x>=y a[-2,-1] x ++. 2 mirror. y a[-2,-1] y ++. 4 =. 4,50%,50% =.. 2 a[-2,-1] x label. 0,0 (2,2,2,0,1,2,1,1,3,3,3,1,1,0) map.. . rm. 100%,100%,1,1 line. 0,0,{$M-1},{$M-1},1,1 line. {$M-1},$M,0,100%,1,1 line. {$M-1},{$M-1},{3*$M-1},{$M-1},1,1 line. {2*$M},0,0,0,1,1 line. {2*$M},0,100%,100%,1,1 line. {2*$M},100%,100%,0,1,1 a[-2,-1] c elif $3==2 # Horizontal + vertical. $M,$M,1,1,x>y ++. 2 mirror. x a[-2,-1] x ++. 4 mirror. y a[-2,-1] y 100%,100%,1,1,"!x || !y || x==int(w/2) || y==int(h/2) || x==y || w-1-x==y" a[-2,-1] c elif $3==1 # Vertical. $M,$M,1,1,x>y 100%,100%,1,1,w-1-x<=y a[-2,-1] y ++. 2 mirror. x a[-2,-1] x 100%,100%,1,1,"!x || x==int(w/2) || x==y || w-1-x==y" a[-2,-1] c else # Horizontal. $M,$M,1,1,x>y 100%,100%,1,1,w-1-x>=y a[-2,-1] x ++. 2 mirror. y a[-2,-1] y 100%,100%,1,1,"!y || y==int(h/2) || x==y || w-1-x==y" a[-2,-1] c fi # Apply grid on images. repeat $!-1 { wh={$>,[w,h]} if $1>$2 r[$>] 100%,{$>,$1*h/$2} elif $1<$2 r[$>] {$>,$2*w/$1} fi +r. [$>],[$>],1,2,0,2,0.5,0.5 s. c blend[$>,-2] shapeaverage +fc[$>] ${5--1} j[$>] .,0,0,0,0,$4,.. rm[-2,-1] r[$>] $wh,1,100%,2 } rm. #@cli linearize_tiles : M>0,_N>0 #@cli : Linearize MxN tiles on selected images. #@cli : Default value: 'N=M'. #@cli : $ image.jpg +linearize_tiles 16 linearize_tiles : check "$1>0 && ${2=$1}>0" e[^-1] "Linearize $1x$2 tiles on image$?." foreach { nm={n} s:=s split_tiles $1,$2 s c # Split as tiles for all channels. foreach { wh:=w,h +f x +f. y +f. 1 y a[^0] x solve.. . rm. $wh,1,1,{@0}"*x + "{@1}"*y + "{@2} rm.. } repeat int($!/$s) { a[-$s--1] c mv. 0 } append_tiles $1,$2 => $nm } #@cli map_sprites : _nb_sprites>=1,_allow_rotation={ 0:none | 1:90 deg. | 2:180 deg. } #@cli : Map set of sprites (defined as the 'nb_sprites' latest images of the selection) to other selected images, #@cli : according to the luminosity of their pixel values. #@cli : $ image.jpg rescale2d ,48 repeat 16 ball {8+2*$>},${-rgb} mul[-1] {(1+$>)/16} done map_sprites 16 map_sprites : check "isint($1,1) && isint(${2=0},0,2)" e[^-1] "Map set of $1 sprites to image selection$?." norm[0--{$1+1}] quantize[0--{$1+1}] $1,0,1 slices[-$1--1] 0 r[-$1--1] ${max_wh[-$1--1]},1,100%,0,0,0.5,0.5 if $2==1 N:=4*$1 repeat $!-$1 { *[$>] 4 +rand[$>] 0,3 round. +[$>,-1] } repeat $1 { l[{1+$<}] { +mirror xy +rotate 90 } } elif $2==2 N:=2*$1 repeat $!-$1 { *[$>] 2 +rand[$>] 0,1 round. +[$>,-1] } repeat $1 { l[{1+$<}] { +mirror xy } } else N=$1 fi r[-$N--1] 100%,100%,1,${max_s[-$N--1]} w,h={w},{h} a[-$N--1] x r[^-1] ${w}00%,${h}00%,1,1 *[^-1] $w (0,{$w-1};0,{$w-1}^0,0;{$h-1},{$h-1}) r. $w,$h,1,2,3 round. repeat $!-2 { +r. [$>],[$>],1,2,0,2 r[$>] 100%,100%,1,2,0 +[$>,-1] +warp.. [$>],0,0,1 rv[$>,-1] rm. } rm[-2,-1] #@cli pack : is_ratio_constraint={ 0 | 1 },_sort_criterion #@cli : Pack selected images into a single image. #@cli : The returned status contains the list of new (x,y) offsets for each input image. #@cli : Parameter 'is_ratio_constraint' tells if the resulting image must tend to a square image. #@cli : Default values: 'is_ratio_constraint=0' and 'sort_criterion=max(w,h)'. #@cli : $ image.jpg repeat 10 +rescale2d[-1] 75% balance_gamma[-1] ${-rgb} done pack 0 pack : skip ${1=0},${2=max(w,h)} e[^-1] "Pack image$? into a single image." if $!<2 return fi if ${-max_d}>1 error[0--3] "Command '$0': Selected images contain at least one volumetric image (depth>1). Should all be 2D." fi nm={0,n} to_colormode 0 # Sort images by decreasing size. foreach { nm$>={n} => ${nm$>}:$> } m "_pack : ('{n}') l. { s +,{':'} u {t} rm }" if ['$2']=='n' sort_list +,n else sort_list -,"$2" fi # Start packing offsets${-_pack[0]}=0,0 N=$! i[0] 0 # List of empty slots. do l[0,1,2] { w1,h1,w2,h2:=w#1,h#1,w#2,h#2 # Search an empty slot that fits. slot,min_slot_area=-1,inf repeat h#0 { x,y,w,h={0,crop(0,$>,4,1)} slot_area:=$w*$h if $w>=$w2" && "$h>=$h2" && "$slot_area<=$min_slot_area # Found a fit. slot,min_slot_area=$>,$slot_area fi } if $slot>=0 # Empty slot found -> Use it. x,y,w,h={0,crop(0,$slot,4,1)} j[1] [2],$x,$y offsets${-_pack[2]}=$x,$y l[0] { s y rm[$slot] area1:=max(($w-$w2)*$h,$w2*($h-$h2)) area2:=max(($w-$w2)*$h2,$w*($h-$h2)) if $area1>=$area2 # Split - type1 if $w2<$w i[$slot] ({$x+$w2},$y,{$w-$w2},$h) fi if $h2<$h i[$slot] ($x,{$y+$h2},$w2,{$h-$h2}) fi else # Split - type 2 if $w2<$w i[$slot] ({$x+$w2},$y,{$w-$w2},$h2) fi if $h2<$h i[$slot] ($x,{$y+$h2},$w,{$h-$h2}) fi fi a y if !$! 0 fi } rm[2] else # Empty slot not found -> Append horizontally or vertically. if $1 metric_h:=abs($w1+$w2-max($h1,$h2)) metric_v:=abs($h1+$h2-max($w1,$w2)) else metric_h:=$h2<$h1?$w2*($h1-$h2):$w1*($h2-$h1) metric_v:=$w2<$w1?$h2*($w1-$w2):$h1*($w2-$w1) fi if $metric_h<=$metric_v # Append horizontally. offsets${-_pack[2]}=$w1,0 a[1,2] x,0 if $h2<$h1 ($w1,$h2,$w2,{$h1-$h2}) a[0,-1] y elif $h2>$h1 (0,$h1,$w1,{$h2-$h1}) a[0,-1] y fi else # Append vertically. offsets${-_pack[2]}=0,$h1 a[1,2] y,0 if $w2<$w1 ($w2,$h1,{$w1-$w2},$h2) a[0,-1] y elif $w2>$w1 ($w1,0,{$w2-$w1},$h1) a[0,-1] y fi fi fi } while $!>2 rm[0] # Return offsets. status= repeat $N { if narg($status) status=$status,${offsets$>} else status=${offsets$>} fi } => $nm u $status um _pack #@cli puzzle : _width>0,_height>0,_M>=1,_N>=1,_curvature,_centering,_connectors_variability,_resolution>=1 #@cli : Input puzzle binary mask with specified size and geometry. #@cli : Default values: 'width=height=512', 'M=N=5', 'curvature=0.5', 'centering=0.5', 'connectors_variability=0.5' \ # and 'resolution=64'. #@cli : $ puzzle , puzzle : check "isint(${1=512},1) && isint(${2=$1},1) && isint(${3=5},1) && isint(${4=$3},1) && isint(${8=64},1)" skip ${5=0.5},${6=0.5},${7=0.5} e[^-1] "Draw $3x$4 puzzle pattern on image$?, with curvature $5, centering $6, connectors variability $7 and resolution $8." l[] { if $4>=2 _puzzle[] $3,{$4-1},${5-8} +3d. 0,1 fi if $3>=2 _puzzle[] $4,{$3-1},${5-8} r3d. 0,0,1,-90 +3d. 1,$4 fi *3d {$1/$3},{$2/$4} quadrangle3d 0,0,0,{$1-1},0,0,{$1-1},{$2-1},0,0,{$2-1},0 p3d. 1 +3d col3d 1 $1,$2 j3d. ..,0,0,0,1,1,0,0 rm.. } _puzzle : R:=$6*$1 repeat $2 { ({'CImg3d'},$R,{$R-1}) repeat $1 { sign:=u<=0.5?-1:1 center:=$4*u(-0.25,0.25) knob:=$5*u(-0.05,0.12) ($>,0;\ {0.2+$center+$>},{-$sign*$3*0.1};\ {0.4+$center+$>},0;\ {0.35+$center+$>},{0.1*$sign};\ {0.45+$center+$>},{(0.15+$knob)*$sign};\ {0.55+$center+$>},{(0.15+$knob)*$sign};\ {0.65+$center+$>},{0.1*$sign};\ {0.6+$center+$>},0;\ {0.8+$center+$>},{-$sign*$3*0.1}) } ($1,0) a[-{$1+1}--1] y r. 2,$R,1,1,5 z. 0,2 (2,0,1;2,{$R-2},{$R-1}) r. 3,{$R-1},1,1,3 round. 3,{h},1,1,255 1,{h},1,1,255 y[-5--1] y a[-5--1] y +3d. 0,$> } +3d #@cli quadratize_tiles : M>0,_N>0 #@cli : Quadratize MxN tiles on selected images. #@cli : Default value: 'N=M'. #@cli : $ image.jpg +quadratize_tiles 16 quadratize_tiles : check "$1>0 && ${2=$1}>0" e[^-1] "Quadratize $1x$2 tiles on image$?." foreach { nm={n} s:=s split_tiles $1,$2 s c # Split as tiles for all channels. foreach { wh:=w,h +f x^2 +f. y^2 +f. x*y +f. x +f. y +f. 1 y a[^0] x solve.. . rm. $wh,1,1,{@0}"*x^2 + "{@1}"*y^2 + "{@2}"*x*y +"{@3}"*x + "{@4}"*y + "{@5} rm.. } repeat int($!/$s) { a[-$s--1] c mv. 0 } append_tiles $1,$2 => $nm } #@cli rotate_tiles : angle,_M>0,N>0 #@cli : Apply MxN tiled-rotation effect on selected images. #@cli : Default values: 'M=8' and 'N=M'. #@cli : $ image.jpg to_rgba rotate_tiles 10,8 drop_shadow 10,10 display_rgba rotate_tiles : skip ${2=8},${3=$2} e[^-1] "Apply $2x$3 tiled-rotation effect on image$?, with angle $1 deg." split_tiles $2,$3,1 rotate $1 append_tiles $2,$3 #@cli shift_tiles : M>0,_N>0,_amplitude #@cli : Apply MxN tiled-shift effect on selected images. #@cli : Default values: 'N=M' and 'amplitude=20'. #@cli : $ image.jpg +shift_tiles 8,8,10 shift_tiles : check "${2=$1}>=0" skip ${3=20} e[^-1] "Apply $1x$2 tiled-shift effect on image$?, with amplitude $3." foreach { $1,$2,1,2 noise. $3 r. ..,..,1,2 warp.. .,1,1,0 rm. } #@cli taquin : M>0,_N>0,_remove_tile={ 0:none | 1:first | 2:last | 3:random },_relief,_border_thickness[%],\ # _border_outline[%],_outline_color #@cli : Create MxN taquin puzzle from selected images. #@cli : Default value: 'N=M', 'relief=50', 'border_thickness=5', 'border_outline=0' and 'remove_tile=0'. #@cli : $ image.jpg +taquin 8 taquin : check "isint($1,1) && isint(${2=$1},1)" skip ${3=0},${4=50},${5=5%},${6=0},${7=0},${8=$7},${9=$8},${10=255} e[^-1] "Create $1x$2 taquin puzzle from image$?, with relief $4, border thickness $5, border outline $6 and outline color (${7--1})." foreach { nm={n} split_tiles $1,$2 r ${-min_wh},100%,100%,0 100%,100%,1,1,1 if ispercentage($5) rectangle. {100*$5/2}%,{100*$5/2}%,{100-50*$5}%,{100-50*$5}%,1,0 else rectangle. $5,$5,{w-1-$5},{h-1-$5},1,0 fi *. '1-2*(x/w,-1] { split_opacity[0] +[0] . a[^-1] c } } rm. c 0,255 frame xy,$6,${7-10} if $3==3 f. 0 fi repeat $! { mv[$>] {u($!)} } if $3==1 f[0] 0 elif $3==2 f. 0 fi append_tiles $1,$2 => $nm } #@cli tunnel : _level>=0,_factor>0,_centering_x,_centering_y,_opacity,_angle #@cli : Apply tunnel effect on selected images. #@cli : Default values: 'level=9', 'factor=80%', 'centering_x=centering_y=0.5', 'opacity=1' and 'angle=0' #@cli : $ image.jpg tunnel 20 tunnel : check "${1=9}>=0 && ${2=80%}>0" skip ${3=0.5},${4=0.5},${5=0.1},${6=0} e[^-1] "Apply tunnel effect on image$?, with depth $1, factor $2, centering ($3,$4), opacity $5 and angle $6." foreach { repeat $1 { +r. $2,$2,1,100%,5 if $6 100%,100%,1,1,1 rotate[-2,-1] $6,1,0 erode. 3 j... ..,{({-3,w}-w)*$3},{({-3,h}-h)*$4},0,0,$5,. rm[-2,-1] else j.. .,{({-2,w}-w)*$3},{({-2,h}-h)*$4},0,0,$5 rm. fi } } c 0,255 #----------------------------- # #@cli :: Artistic # #----------------------------- #@cli boxfitting : _min_box_size>=1,_max_box_size>=0,_initial_density>=0,_min_spacing>0 #@cli : Apply box fitting effect on selected images, as displayed the web page: #@cli : . #@cli : Default values: 'min_box_size=1', 'max_box_size=0', 'initial_density=0.25' and 'min_spacing=1'. #@cli : $ image.jpg boxfitting , boxfitting : check "isint(${1=3},1) && isint(${2=0},0) && ${3=0.25}>=0 && isint(${4=1},1)" e[^-1] "Apply box fitting effect on image$?, with box sizes ($1,$2), initial density $3 and min spacing $4." min_spacing,min_size:=$4,$1+$4-1 foreach { max_size:=$4-1+($2?$2:max(w,h)) prec=5 100%,100% repeat inf { # Add random non-intersecting squares with min size. if $><1 # Takes random points for the first iteration. 100%,100% noise. {max(1e-3,$3)},2 ==. 1 else # Then, try to take points near the median axis of the distance function otherwise. +distance. 1 +rand. 0,1 *[-2,-1] max_patch. {round($prec*$min_size)} prec:=max(1,$prec*0.9) fi 0 eval.. ">i && !max(crop(#-3,x-1,y-1,3,3))?(da_push([x,y,1]); i(#-3,x,y) = 1)" if !i[h-1] rm[-2,-1] break fi r. 1,{i[h-1]},1,3,0 rm.. # Dilate each square as much as possible. do f. "> begin(is_new = 0; const boundary = 1); Xc = i0; Yc = i1; N = i2; N>0?( N2 = int(N/2); N1 = N - N2 - 1; is_odd = N%2; is_new = 0; X0 = Xc - N1 - 1; X0m1 = X0 - 1; Y0 = Yc - N1 - 1; Y0m1 = Y0 - 1; X1 = Xc + N2 + 1; X1p1 = X1 + 1; Y1 = Yc + N2 + 1; Y1p1 = Y1 + 1; is_up = is_down = 1; for (X = X0, (is_up || is_down) && X<=X1, ++X, is_up&=!i(#-2,X,Y0) && !i(#-2,X,Y0m1); is_down&=!i(#-2,X,Y1) && !i(#-2,X,Y1p1); ); is_left = is_right = 1; for (Y = Y0, (is_left || is_right) && Y<=Y1, ++Y, is_left&=!i(#-2,X0,Y) && !i(#-2,X0m1,Y); is_right&=!i(#-2,X1,Y) && !i(#-2,X1p1,Y); ); is_left && is_up && !i(#-2,X0m1,Y0m1)?( # Left/Up polygon(#-2,2,X0,Y0,X1 - 1,Y0,1,1); polygon(#-2,2,X0,Y0,X0,Y1 - 1,1,1); Xc-=is_odd; Yc-=is_odd; is_new=1; ):is_right && is_up && !i(#-2,X1p1,Y0m1)?( # Right/Up polygon(#-2,2,X0 + 1,Y0,X1,Y0,1,1); polygon(#-2,2,X1,Y0,X1,Y1 - 1,1,1); Xc+=!is_odd; Yc-=is_odd; is_new=1; ):is_left && is_down && !i(#-2,X0m1,Y1p1)?( # Left/Bottom polygon(#-2,2,X0,Y1,X1 - 1,Y1,1,1); polygon(#-2,2,X0,Y1,X0,Y0 + 1,1,1); Xc-=is_odd; Yc+=!is_odd; is_new=1; ):is_right && is_down && !i(#-2,X1p1,Y1p1)?( # Right/Bottom polygon(#-2,2,X1,Y0 + 1,X1,Y1,1,1); polygon(#-2,2,X1,Y1,X0 + 1,Y1,1,1); Xc+=!is_odd; Yc+=!is_odd; is_new=1; ); is_new?(++N>=$max_size?(N = -N)):(N = -N); ); [ Xc,Yc,N ]" sh. 100% is_new:=iM>0 rm. while $is_new # Remove too small squares. f. " Xc = i0; Yc = i1; N = abs(i2); N<$min_size?( N2 = int(N/2); N1 = N - N2 - 1; X0 = Xc - N1; Y0 = Yc - N1; X1 = Xc + N2; Y1 = Yc + N2; polygon(#-2,4,X0,Y0,X1,Y0,X1,Y1,X0,Y1,1,0); [ -1,-1,-1 ]; ):[ Xc,Yc,N ]" discard. -1 if !h rm. break fi rm. } if $min_spacing>1 r. {[w,h]+2},1,1,0,0,0.5,0.5 erode. {$min_spacing} z. 1,1,{[w,h]-2} fi blend shapeaverage0 } #@cli brushify : [brush],_brush_nb_sizes>=1,0<=_brush_min_size_factor<=1,_brush_nb_orientations>=1,\ # _brush_light_type,0<=_brush_light_strength<=1,_brush_opacity,_painting_density[%]>=0,\ # 0<=_painting_contours_coherence<=1,0<=_painting_orientation_coherence<=1,_painting_coherence_alpha[%]>=0,\ # _painting_coherence_sigma[%]>=0,_painting_primary_angle,0<=_painting_angle_dispersion<=1 #@cli : Apply specified brush to create painterly versions of specified images. #@cli : 'brush_light_type' can be { 0:none | 1:flat | 2:darken | 3:lighten | 4:full }. #@cli : Default values: 'brush_nb_sizes=3', 'brush_min_size_factor=0.66', 'brush_nb_orientations=12', \ # 'brush_light_type=0', 'brush_light_strength=0.25', 'brush_opacity=0.8', 'painting_density=20%', \ # 'painting_contours_coherence=0.9', 'painting_orientation_coherence=0.9', 'painting_coherence_alpha=1', \ # 'painting_coherence_sigma=1', 'painting_primary_angle=0', 'painting_angle_dispersion=0.2' #@cli : $ image.jpg 40,40 gaussian[-1] 10,4 spread[-1] 10,0 brushify[0] [1],1 brushify : check ${"is_image_arg $1"}" &&"\ # $1: [brush] "isint(${2=4},1) &&"\ # $2: brush_nb_sizes "${3=0.25}>=0 && $3<=1 &&"\ # $3: brush_min_size_factor "isint(${4=12},1) &&"\ # $4: brush_nb_orientations "isint(${5=4},0) &&"\ # $5: brush_light_type "${6=0.07}>=0 && $6<=1 &&"\ # $6: brush_light_strength "isnum(${7=0.75}) &&"\ # $7: brush_opacity "${8=40%}>=0 && $8>=0 &&"\ # $8: painting_density[%] "${9=0.7}>=0 && $9<=1 &&"\ # $9: painting_contours_coherence "${10=1}>=0 && $10<=1 &&"\ # $10: painting_orientation_coherence "${11=1}>=0 && ${12=0.5%}>=0 &&"\ # $11 and $12: painting_coherence_alpha and sigma "isnum(${13=45}) &&"\ # $13: painting_primary_angle "${14=0.2}>=0 && $14<=1" # $14: painting_angle_dispersion e[^-1] "Brushify image$?, with brush $1." # Precompute the set of oriented/resized brushes. pass$1 0 l. { slices 0 max 1e-8 norm n 0,1 threshold 0.1,1 autocrop. repeat $4 { +rotate[0] {360*$>/$4} } rm[0] n 0,1 threshold 0.1,1 autocrop r ${-max_wh},1,1,0,0,0.5,0.5 a z => brush wb,hb,whb={w},{h},{wh} ls:=255*$6 if !$5 +f. 0 elif $5==1 +n. -$ls,0 elif $5==2 +g xy +[-2,-1] min. 0 n. -$ls,0 elif $5==3 +g xy +[-2,-1] max. 0 n. 0,$ls else +g xy +[-2,-1] n. -$ls,$ls fi => brushlight repeat $2-1 { ratio={v=(1+$>)/max(1,$2-1);100*((1-v)+$3*v)}% +r[brush,brushlight] $ratio,$ratio,100%,1,2 ri[-2,-1] [brush],0,0,0.5,0.5 } a[0--1:2] z a[^0] z } # Generate images with brushes. repeat $!-2 { l[$>,brush,brushlight] { s={0,s} nm={0,n} to_rgb[0] =>[0] img # Generate set of random points with orientations. +diffusiontensors[img] $9,$10,$11,$12 => geometry +channels[geometry] 0 sh[geometry] 2 +[-2,-1] ^. 0.3 quantize. $2,0 *. -1 +. $2 -. 1 => contours 1,{img,max(1,ispercentage($8)?wh*$8:$8)} rand. 0,{img,w-1} +rand. 0,{img,h-1} a[-2,-1] c => pts +to_rgba[img] => res # Render filter. f[pts] "* begin( S2 = round(0.5*["$wb","$hb"]); brush_r = brush_g = brush_b = brush_a = vector"$whb"(255); ang = $13*pi/180; cu = [ cos(ang),sin(ang) ]; cv = [ -cu[1],cu[0] ]; T = mul(cu,cu,2) + $14*mul(cv,cv,2); ); P = I; G = I(#"$geometry",P); ang = u(pi); V = [ G[0],G[1],G[1],G[2] ]*(T*[ cos(ang),sin(ang) ]); amp = i(#"$contours",P); #cut($2-round(5*$2*sqrt(G[0] + G[2])),0,$2-1); ang = round(((atan2(V[1],V[0])%(2*pi))*$4/(2*pi)))%$4; col = I(#"$img",P); ind = amp*$4 + ang; brush = crop(#"$brush",0,0,ind,0,"$wb","$hb",1,1); brushlight = crop(#"$brushlight",0,0,ind,0,"$wb","$hb",1,1); brush_r = cut(col[0] + brushlight,0,255); brush_g = cut(col[1] + brushlight,0,255); brush_b = cut(col[2] + brushlight,0,255); PmS2 = P - S2; draw(#"$res",[brush_r,brush_g,brush_b,brush_a],PmS2[0],PmS2[1],0,0,"$wb","$hb",1,4,$7,brush,1); P" k[res,brush,brushlight] mv[res] 0 =>[0] $nm to_colormode[0] {$s+($s%2)} } } rm[brush,brushlight] #@cli cartoon : _smoothness,_sharpening,_threshold>=0,_thickness>=0,_color>=0,quantization>0 #@cli : Apply cartoon effect on selected images. #@cli : Default values: 'smoothness=3', 'sharpening=150', 'threshold=20', 'thickness=0.25', 'color=1.5' \ # and 'quantization=8'. #@cli : $ image.jpg cartoon 3,50,10,0.25,3,16 cartoon : skip ${1=3},${2=150},${3=15},${4=0.25},${5=1.5},${6=8} e[^-1] "Apply cartoon effect on image$?, with smoothness $1, sharpening $2, threshold $3, thickness $4, color $5 and quantization $6." foreach { split_opacity l[0] { to_rgb b $1 sharpen $2,1 c 0,255 n 0,255 if $4 +edges $3 b. $4 >=. 0.9 else 100%,100%,1,1,1 fi rgb2lab.. s.. c *[-3,-2] $5 a[-4--2] c lab2rgb.. quantize.. $6,1,-1 n.. 0,255 * } a c } #@cli color_ellipses : _count>0,_radius>=0,_opacity>=0 #@cli : Add random color ellipses to selected images. #@cli : Default values: 'count=400', 'radius=5' and 'opacity=0.1'. #@cli : $ image.jpg +color_ellipses ,,0.15 color_ellipses : skip ${1=1400},${2=5},${3=0.1} e[^-1] "Add $1 random color ellipses to image$?, with maximum radius $2 and opacity $1." repeat $1 { ellipse {u(0,100)}%,{u(0,100)}%,{u(0,$2)}%,{u(0,$2)}%,{u(0,360)},$3,{u(60,255)},{u(60,255)},{u(60,255)},255 } #@cli cubism : _density>=0,0<=_thickness<=50,_max_angle,_opacity,_smoothness>=0 #@cli : Apply cubism effect on selected images. #@cli : Default values: 'density=50', 'thickness=10', 'max_angle=75', 'opacity=0.7' and 'smoothness=0'. #@cli : $ image.jpg cubism , cubism : check "${1=50}>=0 && ${2=10}>=0 && $2<=50 && ${5=0}>=0" skip ${3=75},${4=0.7} e[^-1] "Apply cubism effect on image$?, with density $1, thickness $2, maximum angle $3 deg., opacity $4 and smoothness $5." if "!$1 || !$2 || !$3 || !$4" return fi foreach { w,h,s={w},{h},{s} P:=round($2*max(w,h)/200) N:=round(1.5*$1*w*h/(4*$P)/100) # Define Header + nb vertices / primitives. ('CImg3d') +. 0.5 ({4*$N};$N) # Generate list of random points. 1,$N rand. $P,{$w-1-$P} +rand. $P,{$h-1-$P} a[-2,-1] x round. # Generate list of primitives. ++. '-$P,-$P' ++.. '$P,-$P' ++... '$P,$P' ++[-4] '-$P,$P' a[-4--1] x i.. (12,0,1,2,3;12,{4*($N-1)},{4*($N-1)+1},{4*($N-1)+2},{4*($N-1)+3}) r.. 5,$N,1,1,3 round.. 1 a[-2,-1] x # Generate list of vertices. 1,$N rand. {225+[-$3,$3]} *. {pi/180} +sin. cos.. +*. -1 ... a[-4--3] x a[-2,-1] x z[-4,-2,-1] 0,2 +*[-2,-1] -1 a[-4--1] x *. {sqrt(2)*$P} r... 400%,100%,1,1,0,2 +[-3,-1] # Generate materials. (-128;$w;$h;$s) +b[0] $5 if $N>1 4,{$N-1},1,1,-128,0,0,0 fi 1,$N,1,1,1 # Apply effect on current image. y[1--1] a[1--1] y rv3d. if $4>=1 j3d[0] [1],0,0,0,1,2,0,0 rm[1] else +j3d[0] [1],0,0,0,1,2,0,0 rm[1] blend alpha,$4 fi } #@cli draw_whirl : _amplitude>=0 #@cli : Apply whirl drawing effect on selected images. #@cli : Default value: 'amplitude=100'. #@cli : $ image.jpg draw_whirl , draw_whirl : skip ${1=100} e[^-1] "Apply whirl drawing effect on image$? with amplitude $1." foreach { 100%,100% noise. 70,2 ==. 1 *. 255 ri. .. &[-1,-2] smooth. $1,0,1,2,2 sqrt. n. 0,255 equalize. } #@cli drawing : _amplitude>=0 #@cli : Apply drawing effect on selected images. #@cli : Default value: 'amplitude=200'. #@cli : $ image.jpg +drawing , drawing : skip ${1=200} e[^-1] "Apply drawing effect on image$? with amplitude $1." foreach { split_opacity l[0] { to_rgb smooth $1,0.2,1,3,3 b 2 sharpen 1000 [0] r[0] 20,20,1,3,2 equalize[0] index[1] [0],1,1 => {0,n} rm[0] } a c } #@cli drop_shadow : _offset_x[%],_offset_y[%],_smoothness[%]>=0,_curvature_x,_curvature_y,_expand_size={ 0 | 1 } #@cli : Drop shadow behind selected images. #@cli : Default values: 'offset_x=20', 'offset_y=offset_x', 'smoothness=5', 'curvature=0' and 'expand_size=1'. #@cli : $ image.jpg drop_shadow 10,20,5,0.5 expand. xy,20 display_rgba #@cli drop_shadow : _offset_x[%],_offset_y[%],_smoothness[%]>=0,curvature_x>=0,curvature_y>=0,_expand_size={ 0 | 1 },\ # _output_separate_layers={ 0 | 1 } #@cli : Drop shadow behind selected images. #@cli : Default values: 'offset_x=20', 'offset_y=offset_x', 'smoothness=5', 'curvature_x=curvature_y=0', \ # 'expand_size=1' and 'output_separate_layers=0'. #@cli : $ image.jpg drop_shadow 10,20,5,0.5 display_rgba drop_shadow : check "isexpr(${1=20}) && isexpr(${2=$1}) && ${3=5}>=0 && isnum(${4=0}) && isnum(${5=0}) && "\ "isbool(${6=1}) && isbool(${7=0})" s0,s1,t0,t1=original,expanded,single,separate e[^-1] "Drop shadow behind image$?, with offsets ($1,$2), smoothness $3, curvatures ($4,$5) "${s$6}" size and "\ ${t$7}" layer output." foreach { nm={n} if s>4 error[0--3] "Command 'drop_shadow': Invalid input image (has "{s}" channels)." fi to_a shiftx,shifty:=round([ispercentage($1)?$1*w:$1,ispercentage($2)?$2*h:$2]) sigma:=ispercentage($3)?max(w,h)*$3:$3 # [1] (1xN): crop coordinates of the N connected components. +channels. 100% ge. 50% label_fg. 0,1 1,{iM},1,4,[inf,inf,-inf,-inf] eval.. "> ind = i; ind?( --ind; xi(#-1,0,ind,0,2)?(i(#-1,0,ind,0,2) = x); y>i(#-1,0,ind,0,3)?(i(#-1,0,ind,0,3) = y); )" # Expand image [0], if needed. if $6 s. c x0,y0,x1,y1:=im#-4,im#-3,iM#-2,iM#-1 a[-4--1] c nx0,ny0,nx1,ny1=$x0,$y0,$x1,$y1 nx0,nx1+=$shiftx ny0,ny1+=$shifty if $sigma>0 nx0,ny0,nx1,ny1+="const S = 3.5*$sigma; [-S,-S,S,S]" fi if $4 nx0,nx1+="const S = $shiftx*$4; S>0?[-S,0]:[0,-S]" fi if $5 ny0,ny1+="const S = $shifty*$5; S>0?[-S,0]:[0,-S]" fi nx0,ny0,nx1,ny1={0,round([min($nx0,0),min($ny0,0),max($nx1,w-1),max($ny1,h-1)])} z[0,1] $nx0,$ny0,$nx1,$ny1 +[2] '"const dx = -$nx0; const dy = -$ny0; [dx,dy,dx,dy]"' fi # Render shadow opacity map. [0],[0] repeat h#2 { x0,y0,x1,y1:=I[#2,$>] w,h:=$x1-$x0,$y1-$y0 if w" && "h nx0,ny0,nx1,ny1=$x0,$y0,$x1,$y1 if $4 nx0,nx1+="const S = $shiftx*$4; S>0?[-S,0]:[0,-S]" fi if $5 ny0,ny1+="const S = $shifty*$5; S>0?[-S,0]:[0,-S]" fi nx0,ny0,nx1,ny1:=round([$nx0,$ny0,$nx1,$ny1]) +z[0,1] $nx0,$ny0,0,100%,$nx1,$ny1,0,100% ==. {$>+1} *[-2,-1] if $4" || "$5 100%,100%,1,2," X = lerp($nx0,$nx1,x/(w-1)); Y = lerp($ny0,$ny1,y/(h-1)); ampx = sin((Y-$y0)/($h - 1)*pi); ampy = sin((X-$x0)/($w - 1)*pi); [ -$shiftx*abs($4)*($4>0?ampx:(1 - ampx)), -$shifty*abs($5)*($5>0?ampy:(1 - ampy))]" warp.. .,1,1,0 rm. fi sx0,sy0,sx1,sy1:=$nx0+$shiftx,$ny0+$shifty,$nx1+$shiftx,$ny1+$shifty +z[3] $sx0,$sy0,$sx1,$sy1 max[-2,-1] j[3] .,$sx0,$sy0 rm. fi } rm[1,2] b. $sigma,0 channels. -1,0 rv if !$7 blend alpha fi =>[^] $nm } #@cli ellipsionism : _R>0[%],_r>0[%],_smoothness>=0[%],_opacity,_outline>0,_density>0 #@cli : Apply ellipsionism filter to selected images. #@cli : Default values: 'R=10', 'r=3', 'smoothness=1%', 'opacity=0.7', 'outline=8' and 'density=0.6'. #@cli : $ image.jpg ellipsionism , ellipsionism : check "${1=10}>0 && ${2=3}>0 && ${5=8}>0 && ${6=0.6}>0" skip ${3=1%},${4=0.7} e[^-1] "Apply ellipsionism filter to image$?, with radii ($1,$2), smoothness $3, opacity $4 and outline $5." foreach { to_color # Compute contour angle. +luminance g. xy a[-2,-1] c b. $3 orientation. sh. 0 sh.. 1 atan2. .. *. {180/pi} +. 90 rm[-2,-1] channels. 1,1 # Render ellipses. 100%,100%,1,4 eval " const interpolation = 1; const N = $6*wh/max($1,$2); repeat (N,n, x = v(w - 1); y = v(h - 1); ellipse(x,y,$1,$2,i(#-2,x,y)°,$4,I(#0,x,y),255); ellipse(x,y,-$1,-$2,i(#-2,x,y)°,$4,0xFFFFFFFF,I(#0,x,y)/$5,255); )" rm.. blend alpha } #@cli fire_edges : _edges>=0,0<=_attenuation<=1,_smoothness>=0,_threshold>=0,_nb_frames>0,_starting_frame>=0,\ # frame_skip>=0 #@cli : Generate fire effect from edges of selected images. #@cli : Default values: 'edges=0.7', 'attenuation=0.25', 'smoothness=0.5', 'threshold=25', 'nb_frames=1', \ # 'starting_frame=20' and 'frame_skip=0'. #@cli : $ image.jpg fire_edges , fire_edges : check "${1=0.7}>=0 && ${2=0.25}>=0 && $2<=1 && ${3=0.5}>=0 && ${4=25}>=0 && ${5=1}>0 && ${6=20}>=0 && ${7=0}>=0" e[^-1] "Generate fire effect from edges of image$?, with edges $1, attenuation $2, smoothness $3, threshold $4, $5 frames, starting frame $6 and frame skip $7." foreach { nm={n} norm +gradient_norm n. 0,1 roundify. $1 f[0] 0 (0,0,0;0,0,0;1,1,1;0,1,0) *. {(1-$2^4)/4} repeat $5*(1+$7)+$6 { {0,w},{0,h} rand. 0,255 *. [1] b. $3 if $4 >=. $4% else equalize. fi n. 0,255 j[0] .,0,0,0,0,1,[1],1 rm. correlate[0] [2] if $>>=$6" && "!(($>-$6)%($7+1)) [0] fi } rm[0-2] =>[^] $nm } (0,255,255,255,255^0,0,255,255,255^0,0,0,128,255) r. 256,1,1,3,3 map[^-1] . rm. #@cli fractalize : 0<=detail_level<=1 #@cli : Randomly fractalize selected images. #@cli : Default value: 'detail_level=0.8' #@cli : $ image.jpg fractalize , fractalize : check "${1=0.8}>=0 && $1<=1" e[^-1] "Randomly fractalize image$?, with detail level $1." xc=0.4433 yc=0.2645 delta=0.1 c0r=0.317 c0i=0.03 foreach { nm={n} luminance equalize 256 b 0.25% n 0,255 100%,100% dx:=$delta*w/max(w,h) dy:=$delta*h/max(w,h) x0:=$xc-$dx/2 y0:=$yc-$dy/2 x1:=$xc+$dx/2 y1:=$yc+$dy/2 mandelbrot. $x0,$y0,$x1,$y1,256,1,$c0r,$c0i +==. 0 inpaint.. . rm. n. 0,256 16,1,1,3 rand. 0,255 r. 256,1,1,3,3 map.. . rm. s. c i[2,3] [0] s:=0.1*(1-$1) parallel "register_nonrigid[1] [0],"$s",5","register_nonrigid[3] [2],"$s",5","register_nonrigid[5] [4],"$s",5" rm[0,2,4] a c => $nm } #@cli glow : _amplitude>=0 #@cli : Add soft glow on selected images. #@cli : Default value: 'amplitude=1%'. #@cli : $ image.jpg glow , glow : skip ${1=1%} e[^-1] "Add soft glow on image$?, with amplitude $1." foreach { split_opacity +b[0] $1 n. [0] blend_edges[0,-1] 1 a c } #@cli halftone : nb_levels>=2,_size_dark>=2,_size_bright>=2,_shape={ 0:square | 1:diamond | 2:circle | \ # 3:inv-square | 4:inv-diamond | 5:inv-circle },_smoothness[%]>=0 #@cli : Apply halftone dithering to selected images. #@cli : Default values: 'nb_levels=5', 'size_dark=8', 'size_bright=8', 'shape=5' and 'smoothnesss=0'. #@cli : $ image.jpg halftone , halftone : check "${1=5}>=2 && ${2=8}>=2 && ${3=8}>=2 && ${5=0}>=0" skip ${4=5} s0="square" s1="diamond" s2="circle" s3="inv-square" s4="inv-diamond" s5="inv-circle" e[^-1] "Apply halftone dithering to image$?, with $1 levels, dark size $3, bright size $4, "\ ${s$4}" shape and smoothness $5." foreach { s c foreach { (0,255) a y quantize $1,0 rows 0,{h-2} repeat $1 { s:=round(($2*$<+$3*$>)/($1-1)) $s,$s =. 1,50%,50% distance. 1,{$4%3} +shift. {round(w/2)},{round(h/2)},0,0,2 min[-2,-1] if $4>=3 <. {100*$} else <. {100*$>/($1-1.1)}% *. 255 -. $> fi ri. ..,0,2 b. $5 +==.. $> *[-2,-1] +[-2,-1] } } a c } #@cli hardsketchbw : _amplitude>=0,_density>=0,_opacity,0<=_edge_threshold<=100,_is_fast={ 0 | 1 } #@cli : Apply hard B&W sketch effect on selected images. #@cli : Default values: 'amplitude=1000', 'sampling=3', 'opacity=0.1', 'edge_threshold=20' and 'is_fast=0'. #@cli : $ image.jpg +hardsketchbw 200,70,0.1,10 median[-1] 2 +local reverse blur[-1] 3 blend[-2,-1] overlay done hardsketchbw : skip ${1=300},${2=50},${3=0.1},${4=20},${5=0} e[^-1] "Apply hard B&W sketch effect on image$?, with amplitude $1, density $2, opacity $3 and edge threshold $4." if !$2 channels 0 f 255 return fi luminance n 0,1 if $5 # Fast version. foreach { nm={n} g xy rv *.. -1 a c if $4 +norm >=. $4% * fi 100%,100%,1,1,255 quiver. ..,{max(1,10-$2/6)},$1,0,$3 rm.. => $nm } else # Slower version. foreach { nm={n} # Isolate starting points and tangents. w,h={w},{h} g xy rv *.. -1 a c * $1 +norm >=. $4% 100%,100% noise. $2,2 ==. 1 *[-2,-1] # Retrieve points coordinates and corresponding tangents. pointcloud3d. if i[-1,2] s3d. rm[-6--5,-3--1] r. 3,{h/3},1,1,-1 s. x rm. a[-2,-1] c warp.. .,0,0,1 +-. .. +[-3,-2] s[-2,-1] c # Convert as a 3D object and render on white background. i... 1,{h} 1,{h} a[-6--1] x i.. ('CImg3d') i.. ({2*h},{h}) 1,{h},1,1,2 1,{h},1,1,2*y ++. 1 a[-3--1] x 3,{h} 1,{h},1,1,$3 y[-6--1] a[-6--1] y $w,$h,1,1,255 j3d. ..,0,0,0,1,1,0,0 rm.. else rm $w,$h,1,1,255 fi => $nm } fi #@cli hearts : _density>=0 #@cli : Apply heart effect on selected images. #@cli : Default value: 'density=10'. #@cli : $ image.jpg hearts , hearts : skip ${1=10} e[^-1] "Apply heart filter on image$?, with density $1." foreach { 100%,100%,1 noise. $1,2 ==. 1 ri. .. n. 0,1 *[-1,-2] _heart9x7 dilate.. . rm. } _heart9x7 : (9,7,1,1,0,1,-1,2,-3,2,-1,4,-1,13,-1,7,-3,5,-5,3,-7,1,-4) decompress_rle. #@cli houghsketchbw : _density>=0,_radius>0,0<=_threshold<=100,0<=_opacity<=1,_votesize[%]>0 #@cli : Apply hough B&W sketch effect on selected images. #@cli : Default values: 'density=100', 'radius=3', 'threshold=100', 'opacity=0.1' and 'votesize=100%'. #@cli : $ image.jpg +houghsketchbw , houghsketchbw : check "${1=100}>=0 && ${2=3}>=0 && ${3=100}>=0 && $3<=100 && ${4=0.1}>=0 && $4<=1 && ${5=100%}>0" e[^-1] "Apply hough B&W sketch effect on image$?, with density $1, radius $2, threshold $3, opacity $4 and votesize $5." luminance foreach { nm={n} # Compute normalized Hough transform. res:=round(ispercentage($5)?$5*max(w,h):$5) w,h={w},{h} rhomax:=sqrt(w^2+h^2)/2 hough $res,$res # Retrieve coordinates of maximas in hough space. normalize_local. $1,$2 >. $3% if !iM continue fi pointcloud3d. s3d. rm[-6--5,-3--1] r. 3,{h/3},1,1,-1 columns. 0,1 # Convert to (x0,y0)-(x1,y1) line coordinates. s. x,2 *.. {2*pi/$res} # theta *. {$rhomax/$res} # rho +cos.. *. .. +. {$w/2} # x +sin... *. ... +. {$h/2} # y rm... # Remove rho i... ... cos[-4] sin... *[-4,-3] 10000 # cos(t) sin(t) ++.. ... # x + sin(t) +-.. [-5] # y - cos(t) -[-4] [-5] # x - sin(t) +... [-6] # y + cos(t) rm[-6,-5] # Transform as a 3D object. i... 1,{h} 1,{h} a[-6--1] x # Vertices i.. ('CImg3d') i.. ({2*h},{h}) # Header and size. 1,{h},1,1,2 1,{h},1,1,2*y ++. 1 a[-3--1] x # Primitives. 3,{h},1,1,0 1,{h},1,1,$4 # Colors and opacities y[-6--1] a[-6--1] y # Render on a white image. $w,$h,1,1,255 j3d. ..,0,0,0,1,1,0,0 rm.. => $nm } #@cli lightrays : 100<=_density<=0,_center_x[%],_center_y[%],_ray_length>=0,_ray_attenuation>=0 #@cli : Generate ray lights from the edges of selected images. #@cli : Default values: 'density=50%', 'center_x=50%', 'center_y=50%', 'ray_length=0.9' and 'ray_attenuation=0.5'. #@cli : $ image.jpg +lightrays , + cut 0,255 lightrays : check "${1=50}>=0 && $1<=100 && ${4=1}>=0 && ${5=1}>=0" skip ${2=50%},${3=50%} e[^-1] "Generate ray lights from image$?, with density $1, center point ($2,$3), ray length $4 and attenuation $5." foreach { gradient_norm >= $1% euclidean2polar $2,$3 repeat log2(w) { +shift. {2^$>} +[-2,-1] } function1d 0.5,0,1,{$4*w},1,{1+($4+1-$5)*w},0 r. {-2,w},1,1,1,0 (1,{w}) r. {-2,w},1,1,1,3 /[-2,-1] ri. .. *[-2,-1] polar2euclidean $2,$3 n 0,255 } #@cli light_relief : _ambient_light,_specular_lightness,_specular_size,_darkness,_light_smoothness,_xl,_yl,_zl,\ # _zscale,_opacity_is_heightmap={ 0 | 1 } #@cli : Apply relief light to selected images. #@cli : Default values(s) : 'ambient_light=0.3', 'specular_lightness=0.5', 'specular_size=0.2', 'darkness=0', \ # 'xl=0.2', 'yl=zl=0.5', #@cli : 'zscale=1', 'opacity=1' and 'opacity_is_heightmap=0'. #@cli : $ image.jpg blur 2 light_relief 0.3,4,0.1,0 light_relief : skip ${1=0.3},${2=0.5},${3=0.2},${4=0},${5=0.2},${6=0.5},${7=0.5},${8=1},${9=1},${10=0} e[^-1] "Apply relief light to image$?." foreach { ({-$6},{1-$6};{-$6},{1-$6}^{-$7},{-$7};{1-$7},{1-$7}^$8,$8;$8,$8) r. ..,..,1,3,3 # Create light vector field. if $10 +channels.. 3 to_rgb... else +to_rgb.. norm. fi b. $5% g. xy 100%,100%,1,1,$9 a[-3--1] c # Create normal vector field. orientation[-2,-1] *[-2,-1] s. c +[-3--1] # Normalized scalar product. 100%,100% =. 1,{$6*100}%,{$7*100}% distance. 1 sqr. *. -1 # Compute specular attenuation. /. {($3*max(w,h))^2} exp. *. $2 +. $1 *[-2,-1] -. $4 *. {-2,iM} split_opacity[0] +[0,-1] a c c 0,255 } #@cli linify : 0<=_density<=100,_spreading>=0,_resolution[%]>0,_line_opacity>=0,_line_precision>0,\ # _mode={ 0:subtractive | 1:additive } #@cli : Apply linify effect on selected images. #@cli : The algorithm is inspired from the one described on the webpage . #@cli : Default values: 'density=50', 'spreading=2', 'resolution=40%', 'line_opacity=10', 'line_precision=24' \ # and 'mode=0'. #@cli : $ image.jpg linify 60 linify : check "${1=40}>=0 && $1<=100 && ${2=2}>=0 && ${3=40%}>0 && ${4=10}>=0 && isint(${5=24},1) && isbool(${6=0})" e[^-1] "Apply linify effect on image$?, with density $1, spreading $2, resolution $3, line opacity $4, line precision $5 and "${"arg0 $6,subtractive,additive"}" mode." foreach { remove_opacity nm={n} 100%,100%,1,{s},$6?0:255 if {0,w>h} rs[0] {ispercentage($3)?max(1,$3*w):min(w,$3)} else rs[0] ,{ispercentage($3)?max(1,$3*h):min(h,$3)} fi n[0] 0,100 if narg($_debug)" && "!{*,w} w[] ${-fitscreen[]\ {1,[w,h]}} fi eval " is_in(ind,P) = (P[0]>=0 && P[0]=0 && P[1]best_avg):(avgdensity):(ref $nm } #@cli mosaic : 0<=_density<=100 #@cli : Create random mosaic from selected images. #@cli : Default values: 'density=30'. #@cli : $ image.jpg mosaic , +fill "I!=J(1) || I!=J(0,1)?[0,0,0]:I" mosaic : check "${1=30}>=0" e[^-1] "Apply mosaic effect on image$?, with density $1." foreach { 100%,100%,1,2,'u<0.25*($1%)^4?[u,1]' s. c distance. 1 *. -1 watershed.. . rm. blend shapeaverage } #@cli old_photo #@cli : Apply old photo effect on selected images. #@cli : $ image.jpg old_photo old_photo : e[^-1] "Apply old photo effect on image$?." noise 20 bilateral 30,60 b 2 sharpen 100 frame_fuzzy 8%,8%,6,3 to_rgb shadow_patch 0.75 n 0,255 sepia #@cli pencilbw : _size>=0,_amplitude>=0 #@cli : Apply B&W pencil effect on selected images. #@cli : Default values: 'size=0.3' and 'amplitude=60'. #@cli : $ image.jpg pencilbw , pencilbw : skip ${1=0.3},${2=60} e[^-1] "Apply B&W pencil effect on image$?, with size $1 and amplitude $2." foreach { split_opacity l[0] { norm b $1 sharpen 4000 smooth $2,0,1 equalize sqrt n 0,255 } a c } #@cli pixelsort : _ordering={ + | - },_axis={ x | y | z | xy | yx },_[sorting_criterion],_[mask] #@cli : Apply a 'pixel sorting' algorithm on selected images, as described in the page : #@cli : . #@cli : Default values: 'ordering=+', 'axis=x' and 'sorting_criterion=mask=(undefined)'. #@cli : $ image.jpg +norm +ge[-1] 30% +pixelsort[0] +,y,[1],[2] pixelsort : check "(str1=['${1=+}']; isin(str1,'+','-')) && "\ "(str2=['${2=x}']; isin(str2,'x','y','z','xy','yx')) && "\ "(['${3=}']==0 || "${"is_image_arg $3"}") && "\ "(['${4=}']==0 || "${"is_image_arg $4"}")" s0="descending" s1="ascending" if ['$3']!=0" && "['$4']!=0 e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2, with sorting criterion $3 and mask $4." elif ['$3']!=0" && "['$4']==0 e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2, with sorting criterion $3." elif ['$3']==0" && "['$4']!=0 e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2, with mask $4." else e[^-1] "Apply 'pixelsort' effect to image$? in "${s{['$1']=='+'}}" order, along axis $2." fi repeat $! { if ['$3']!=0 pass$3 0 else +compose_channels[$>] + fi if [w#1,h#1,d#1,s#1]!=[w#0,h#0,d#0,1] error[0--4] "Command 'pixelsort': Invalid specified sorting criterion (size=("{1,[w,h,d,s]}")) for image ["$>"] "\ "(size=("{0,[w,h,d,s]}"))." fi if ['$4']!=0 pass$4 0 else [$>],[$>],[$>],1,1 fi if [w#2,h#2,d#2,s#2]!=[w#0,h#0,d#0,1] error[0--4] "Command 'pixelsort': Invalid specified mask (size=("{1,[w,h,d,s]}")) for image ["$>"] "\ "(size=("{0,[w,h,d,s]}"))." fi l[$>,-2,-1] { nm={0,n} >=. 50% mv.. 0 a c if ['$2']=='x'; _pixelsort $1 elif ['$2']=='y'; permute yxzc _pixelsort $1 permute yxzc elif ['$2']=='z'; permute zxyc _pixelsort $1 permute yzxc elif ['$2']=='xy'; _pixelsort $1 permute yxzc _pixelsort $1 permute yxzc elif ['$2']=='yx'; permute yxzc _pixelsort $1 permute yxzc _pixelsort $1 fi channels 1,{s-2} => $nm } } _pixelsort : s. c,-{s-1} N={-2,s} permute.. cxyz r.. {-2,[w*h,d,s,1]},-1 # Merge image + criterion into a single image sort:=;_'$1'==_'+'; eval. "*begin(ret = 0; V = vector(#w#0); const N = $N); i && !j(-1)?( # Start of a segment for (xe = x + 1, i(xe,y,z) && xex?(dx = xe - x + 1; Ndx = N*dx; Nx = N*x; copy(V,i(#0,Nx),Ndx); copy(i(#0,Nx),sort(V,$sort,dx,N),Ndx)); );" r.. {-2,[$N,w/$N,h,d]},-1 permute.. yzcx a c #@cli polaroid : _size1>=0,_size2>=0 #@cli : Create polaroid effect in selected images. #@cli : Default values: 'size1=10' and 'size2=20'. #@cli : $ image.jpg to_rgba polaroid 5,30 rotate 20 drop_shadow , drgba polaroid : check "${1=10}>=0 && ${2=20}>=0" e[^-1] "Create polaroid effect in image$?, with borders sizes $1 and $2." - 255 r {100+$1}%,{100+$1}%,1,100%,0,0,0.5,0.5 r 100%,{100+$2}%,1,100%,0,0,0 + 255 #@cli polygonize : _warp_amplitude>=0,_smoothness[%]>=0,_min_area[%]>=0,_resolution_x[%]>0,_resolution_y[%]>0 #@cli : Apply polygon effect on selected images. #@cli : Default values: 'warp_amplitude=300', 'smoothness=2%', 'min_area=0.1%', 'resolution_x=resolution_y=10%'. #@cli : $ image.jpg +polygonize 100,10 fill[-1] "I!=J(1) || I!=J(0,1)?[0,0,0]:I" polygonize : check "${1=300}>=0 && ${2=2%}>=0 && ${3=0.1%}>=0 && ${4=10%}>0 && ${5=$4}>0" e[^-1] "Polygonize image$? with warp amplitude $1, smoothness $2, minimal area $3 and resolutions ($4,$5)." foreach { +b $2 gradient_norm. g. a[-2,-1] c channels. 0,2 *. {1/0.1+max(abs(im),abs(iM))} resx:=max(1,round(ispercentage($4)?w*$4:w/$4)-1) resy:=max(1,round(ispercentage($5)?h*$5:h/$5)-1) plane3d 1,1,$resx,$resy *3d. {0,w-1},{0,h-1},1 s3d. rm.. i.. (0;{h-1}) r.. 3,{h},1,1,3 round.. y.. [-4] a[-7--2] y r. 3,{h/3},1,1,-1 z. 0,1 permute. yzcx repeat $1 { +warp[1] .,0,0,1 +[-2,-1] } permute. cxyz z. 0,2 y. j[2] .,0,8 rm[-3,-1] [0],[0] j3d. [1],0,0,0,1,2 rm[1] if $3>0 min_area={0,ispercentage($3)?$3*w*h:$3} +area. 0,1 >=. $min_area +.. 1 *.. . distance. 1 *. -1 watershed.. . rm. fi blend shapeaverage } #@cli poster_edges : 0<=_edge_threshold<=100,0<=_edge_shade<=100,_edge_thickness>=0,_edge_antialiasing>=0,\ # 0<=_posterization_level<=15,_posterization_antialiasing>=0 #@cli : Apply poster edges effect on selected images. #@cli : Default values: 'edge_threshold=40', 'edge_shade=5', 'edge_thickness=0.5', 'edge_antialiasing=10', \ # 'posterization_level=12' and 'posterization_antialiasing=0'. #@cli : $ image.jpg poster_edges , poster_edges : check "${1=40}>=0 && $1<=100 && ${2=5}>=0 && $2<=100 && ${3=0.5}>=0 && ${4=10}>=0 && ${5=12}>=0 && $5<=15 && ${6=0}>=0" e[^-1] "Apply poster edge on image$?, with edge threshold $1, edge shade $2, edge thickness $3, edge antialiasing $4, $5 level of posterization and posterization antialiasing $6." foreach { split_opacity l[0] { +g xy,1 a[-2,-1] c norm. b. $3 n. 0,255 apply_curve. 1,0,1,{max(0,(100-($1%)^0.1*100)*255%)},0.99,{min(255,(101-($1%)^0.1*100+$2)*255%)},0.01,255,0 c. 0,1 if $4 smooth. {min(50,$4)},0,1,{$4/40},{$4/40},0.8,90 fi if $5 autoindex[0] {round((4-sqrt($5+1))*32+2)} fi if $6 smooth[0] {min(50,$6)},0,1,{$6/40},{$6/40},0.8,90 fi * } a c } #@cli poster_hope : _smoothness>=0 #@cli : Apply Hope stencil poster effect on selected images. #@cli : Default value: 'smoothness=3'. #@cli : $ image.jpg poster_hope , poster_hope : check "${1=3}>=0" e[^-1] "Apply Hope stencil poster effect on image$?, with smoothness $1." foreach { to_rgb apc "smooth 200,0,1,$1,1" quantize 7,0 f i!=5?i:i+1-2*(y%2) (0,32,47;0,32,47;209,1,23;209,1,23;90,141,145;-1,-1,-1;253,221,138) permute. yzcx map[0] [1] rm[1] } #@cli rodilius : 0<=_amplitude<=100,_0<=thickness<=100,_sharpness>=0,_nb_orientations>0,_offset,\ # _color_mode={ 0:darker | 1:brighter } #@cli : Apply rodilius (fractalius-like) filter on selected images. #@cli : Default values: 'amplitude=10', 'thickness=10', 'sharpness=400', 'nb_orientations=7', 'offset=0' \ # and 'color_mode=1'. #@cli : $ image.jpg rodilius 12,10,300,10 normalize_local 10,6 #@cli : $ image.jpg normalize_local 10,16 rodilius 10,4,400,16 smooth 60,0,1,1,4 normalize_local 10,16 rodilius : check "${1=10}>=0 && $1<=200 && ${2=10}>=0 && $2<=100 && ${3=400}>=0 && ${4=7}>0" skip ${5=0},${6=1} e[^-1] "Apply rodilius filter on image$? with amplitude $1, thickness $2, sharpness $3, $4 orientations, offset $5 and "${arg0\ !$6,brighter,darker}" color mode." foreach { nm={n} split_opacity l[0] { if !$6 negate fi +f. 0 repeat round($4) { angle:=$5+$>*180/round($4) +blur_linear.. $1%,{$1*$2/100}%,$angle,1 b. 0.7 sharpen. $3 max[1,-1] } k[1] if !$6 negate fi } a c } #@cli sketchbw : _nb_angles>0,_start_angle,_angle_range>=0,_length>=0,_threshold>=0,_opacity,_bgfactor>=0,\ # _density>0,_sharpness>=0,_anisotropy>=0,_smoothness>=0,_coherence>=0,_is_boost={ 0 | 1 },_is_curved={ 0 | 1 } #@cli : Apply sketch effect to selected images. #@cli : Default values: 'nb_angles=2', 'start_angle=45', 'angle_range=180', 'length=30', 'threshold=3', \ # 'opacity=0.03', 'bgfactor=0', 'density=0.6', 'sharpness=0.1', 'anisotropy=0.6', 'smoothness=0.25', 'coherence=1', \ # 'is_boost=0' and 'is_curved=1'. #@cli : $ image.jpg +sketchbw 1 reverse blur[-1] 3 blend[-2,-1] overlay sketchbw : check "${1=2}>0 && ${3=180}>=0 && ${4=30}>=0 && ${5=3}>=0 && ${7=0}>=0 && ${8=0.6}>0 && ${9=0.1}>=0 && ${10=0.6}>=0 && ${11=0.25}>=0 && ${12=1}>=0" skip ${2=45},${6=0.03},${13=0},${14=0} e[^-1] "Apply B&W sketch effect on image$?." nb_angles,start_angle,angle_range,length,threshold,opacity,bgfactor,density,sharpness,\ anisotropy,smoothness,coherence,is_boost,is_curved=${1-14} length:=max($length,1) foreach { {0,[w,h,1,1,0]} # [1] = canvas to draw onto +gradient_norm[0] sqrt. diffusiontensors[0] $sharpness,$anisotropy,$smoothness,$coherence a[0,-1] c # [0] = field of stroke tensors + gradient norm 1,{$density*wh/sqrt($length)},1,2,v([w#0,h#0]-1) # [2] = set of random points repeat $nb_angles { # Compute vector field for considered orientation. [0],[0],1,2," const angle = ("$start_angle" + "$>"*"$angle_range"/"$nb_angles")*pi/180; const ca = cos(angle); const sa = sin(angle); T = I(#0); U = [ T[0]*ca + T[1]*sa, T[1]*ca + T[2]*sa ]; if ("$is_boost",U/=(1e-8 + norm(U))); U" # Draw curved or straight strokes. if $is_curved f[2] "* oub = ovb = ouf = ovf = 0; oixb = xb = xf = i0; oiyb = yb = yf = i1; oixf = oiyf = -1; op = "$opacity" * (i(#0,xf,yf,0,3)<"$threshold"?"$bgfactor":1); omop = 1 - op; if (op>0, repeat ("$length",dl, # Forward ixf = round(xf); iyf = round(yf); if (ixf!=oixf || iyf!=oiyf, (i(#1,ixf,iyf)*=omop)+=op; oixf = ixf; oiyf = iyf); uf = i(#-1,xf,yf,0,0,1,1); vf = i(#-1,xf,yf,0,1,1,1); if (ouf*uf + ovf*vf<0, uf*=-1; vf*=-1); xf+=uf; yf+=vf; ouf = uf; ovf = vf; # Backward ub = i(#-1,xb,yb,0,0,1,1); vb = i(#-1,xb,yb,0,1,1,1); if (oub*ub + ovb*vb<0, ub*=-1; vb*=-1); xb-=ub; yb-=vb; oub = ub; ovb = vb; ixb = round(xb); iyb = round(yb); if (ixb!=oixb || iyb!=oiyb, (i(#1,ixb,iyb)*=omop)+=op; oixb = ixb; oiyb = iyb); )); I" else f[2] "* const l = "$length"; x = i0; y = i1; u = i(#-1,x,y,0,0); v = i(#-1,x,y,0,1); op = "$opacity" * (i(#0,x,y,0,3)<"$threshold"?"$bgfactor":1); omop = 1 - op; polygon(#1,2,x - l*u,y - l*v,x + l*u,y + l*v,op,1); I" fi rm. } k.. * -1 n 0,255 } #@cli sponge : _size>0 #@cli : Apply sponge effect on selected images. #@cli : Default value: 'size=13'. #@cli : $ image.jpg sponge , sponge : skip ${1=13} e[^-1] "Apply sponge filter on image$?, with brush size $1." foreach { 100%,100%,1,1 noise. 20,2 ==. 1 ri. .. n. 0,1 *[-1,-2] _circle $1 dilate.. . rm. } _circle : if !($1%2) 2,2 else 1 fi +. 1 r. $1,$1,1,1,0,0,0.5,0.5 distance. 1 n. 0,1 sqrt. c. 0.85,0.86 *. -1 n. 0,1 #@cli stained_glass : _edges[%]>=0, shading>=0, is_thin_separators={ 0 | 1 } #@cli : Generate stained glass from selected images. #@cli : Default values: 'edges=40%', 'shading=0.2' and 'is_precise=0'. #@cli : $ image.jpg stained_glass 20%,1 cut 0,20 stained_glass : check "${1=40%}>=0 && ${2=0.2}>=0" skip ${3=0} e[^-1] "Apply stained glass effect on image$?, with edges $1, shading $2 and thin-separators "\ ${arg0\ !$3,enabled,disabled}"." foreach { im:=im-1 - $im # Ensure strict positiveness of image labels. +gradient_norm >=. $1 *.. . distance. 1 sharpen. 1e10 !=. 0 if $3 skeleton. 0 fi distance. 1 watershed.. . +.. $im n. 0,1 ^. $2 * } #@cli stars : _density[%]>=0,_depth>=0,_size>0,_nb_branches>=1,0<=_thickness<=1,_smoothness[%]>=0,_R,_G,_B,_opacity #@cli : Add random stars to selected images. #@cli : Default values: 'density=10%', 'depth=1', 'size=32', 'nb_branches=5', 'thickness=0.38', 'smoothness=0.5', \ # 'R=G=B=200' and 'opacity=1'. #@cli : $ image.jpg stars , stars : check "${1=10%}>=0 && ${2=1}>=0 && ${3=32}>0 && ${4=5}>=1 && ${5=0.38}>=0 && $5<=1 && ${6=0.5}>=0" skip ${7=200},${8=$7},${9=$8},${10=1} e[^-1] "Add $1 random stars to image$?, with depth $2, size $3, $4 branches, thickness $5, smoothness $6, color ($7,$8,$9) and opacity $10." if !$1 return fi # Generate star sprites. star3d $4,$5 col3d. 255 *3d. $3 l. { repeat 4 { {round(2*$3)},{round(2*$3)} j3d. [0],50%,50%,0,1,2,0,0 r3d[0] 0,0,1,-90 } rm[0] } autocrop[-4--1] 0 rs[-4--1] ,$3 b[-4--1] $6,0 r[-4--1] 100%,100%,1,4 repeat 4 { sh[{-1-$>}] 0,2 fc. $7,$8,$9 rm. } # Draw stars on selected images. repeat $!-1 { [-4--1] l[$>,-4--1] { N:=round((ispercentage($1)?w*h*$1:$1)/4,1,1) repeat 4 { 2,$N rand. -1,1 1,$N rand. 0,1 a[-2,-1] x i.. ('CImg3d') +.. 0.5 i.. ($N;$N) (1,0;1,{$N-1}) r. 2,$N,1,1,3 round. 4,$N,1,1,1 y[-5,-3--1] a[-5--1] y rv[-2,-1] sprites3d.. .,1 rm. *3d. {0.75*{0,w}},{0.75*{0,h}},{1000*$2} j3d[0] .,50%,50%,0,$10,0,0,0 rm. } } } rm[-4--1] #@cli stencil : _radius[%]>=0,_smoothness>=0,_iterations>=0 #@cli : Apply stencil filter on selected images. #@cli : Default values: 'radius=3', 'smoothness=1' and 'iterations=8'. #@cli : $ image.jpg +norm stencil. 2,1,4 +mul rm[0] stencil : check "${1=3}>=0 && ${2=1}>=0 && ${3=8}>=0" e[^-1] "Apply stencil filter on image$?, with radius $1, smoothness $2 and $3 iterations." n 0,1 repeat $3 { b $1 unsharp {$1+$2},1000 c 0,255 } #@cli stencilbw : _edges>=0,_smoothness>=0 #@cli : Apply B&W stencil effect on selected images. #@cli : Default values: 'edges=15' and 'smoothness=10'. #@cli : $ image.jpg +stencilbw 40,4 stencilbw : skip ${1=15},${2=10} e[^-1] "Apply B&W stencil effect on image$?, with edges $1 and smoothness $2." foreach { split_opacity luminance[0] n[0] 0,255 +edges[0] $1 quantize[0] 3,0,1 b[0] $2 sharpen[0] 1000000 n[0] 0,1 *[0,-1] n[0] 0,255 a c } #@cli stylize : [style_image],_fidelity_finest,_fidelity_coarsest,_fidelity_smoothness_finest>=0,\ # _fidelity_smoothnes_coarsest>=0,0<=_fidelity_chroma<=1,_init_type,_init_resolution>=0,init_max_gradient>=0,\ # _patch_size_analysis>0,_patch_size_synthesis>0,_patch_size_synthesis_final>0,_nb_matches_finest>=0,\ # _nb_matches_coarsest>=0,_penalize_repetitions>=0,_matching_precision>=0,_scale_factor>1,_skip_finest_scales>=0,\ # _"image_matching_command" #@cli : Transfer colors and textures from specified style image to selected images, using a multi-scale \ # patch-mathing algorithm. #@cli : If instant display window[0] is opened, the steps of the image synthesis are displayed on it. #@cli : 'init_type' can be { 0:best-match | 1:identity | 2:randomized }. #@cli : Default values: 'fidelity_finest=0.5', 'fidelity_coarsest=2', 'fidelity_smoothness_finest=3', \ # 'fidelity_smoothness_coarsest=0.5', 'fidelity_chroma=0.1', 'init_type=0', 'init_resolution=16', \ # 'init_max_gradient=0', 'patch_size_analysis=5', 'patch_size_synthesis=5', 'patch_size_synthesis_final=5', \ # 'nb_matches_finest=2', 'nb_matchesc_coarsest=30', 'penalize_repetitions=2', 'matching_precision=2', \ # 'scale_factor=1.85', 'skip_finest_scales=0' and \ # 'image_matching_command'="s c,-3 match_pca[0] [2] b[0,2] xy,0.7 n[0,2] 0,255 n[1,2] 0,200 \ # a[0,1] c a[1,2] c"'. stylize : check ${"is_image_arg $1"}" && isnum(${2=0.5}) && isnum(${3=2}) && ${4=3}>=0 && ${5=0.5}>=0 && ${6=0.1}>=0 && "\ "$6<=1 && isint(${7=0},0,3) && isint(${8=16},0) && ${9=0}>=0 && isint(${10=5},1) && "\ "isint(${11=5},1) && isint(${12=$11},1) && isint(${13=2}) && isint(${14=30}) && "\ "${15=2}>=0 && ${16=2}>=0 && ${17=1.85}>1 && isint(${18=0},0)" skip "${19=s c,-3 match_pca[0] [2] b[0,2] xy,0.7 n[0,2] 0,255 n[1,2] 0,200 a[0,1] c a[1,2] c}" e[^-1] "Stylize image$? with style image $1." fidelity_finest,\ # $2 fidelity_coarsest,\ # $3 fidelity_smoothness_finest,\ # $4 fidelity_smoothness_coarsest,\ # $5 fidelity_chroma,\ # $6 init_type,\ # $7 init_resolution,\ # $8 init_max_gradient,\ # $9 patch_size_analysis,\ # $10 patch_size_synthesis,\ # $11 patch_size_synthesis_final,\ # $12 nb_matches_finest,\ # $13 nb_matches_coarsest,\ # $14 penalize_repetitions,\ # $15 matching_precision,\ # $16 scale_factor,\ # $17 skip_finest_scales=${2-18} \ # $18 m "stylize_match : $19" init_resolution:=max(2*$patch_size_analysis,$init_resolution) mprec0:=round(2+1.5*$matching_precision) mprec1:=1+round(4*$matching_precision) is_window={*} pass$1 repeat $!-1 { l[$>,-1] { to_colormode.. {s} nb_scales:=1+round(log(min(w#0,h#0,w#1,h#1)/$init_resolution)/log($scale_factor),1,-1) if {*} wsiz=${"fitscreen "{0,[w,h]}} w[0] $wsiz,0,"[G'MIC Stylize]" fi repeat $nb_scales { scale=$> size_factor:=100/($scale_factor^$<) if !$scale # Initialization. +r[0,1] $size_factor%,$size_factor%,100%,100%,2 ws,hs={-2,[w,h]} +to_color[0,1] channels[-2,-1] 0,2 gradient_norm[-2,-1] r. [-3],[-3],[-3],1,2 r.. [-4],[-4],[-4],1,2 a[-3,-1] c a[-3,-1] c # Append gradient information as last channel stylize_match[-2,-1] if !$init_type +matchpatch.. .,3,3,1,{2*$mprec0},{2*$mprec1},$penalize_repetitions # Initial image match with 3x3 patches else ..,..,1,2,"round([x,y]*([w#-1,h#-1]-1)/([w,h]-1))" # Identity if d#-2>1 channels. 0,2 fi if $init_type==2 eval. ">P = u([w,h]-1); tmp = I(P); I(P) = I; I() = tmp" fi # Randomize fi rm[-3,-2] if $init_max_gradient>0 # Keep only high gradients of target image +gradient_norm[0] r. ..,..,1,1,2 gt. $init_max_gradient +.. 1 *[-2,-1] _inpaint_warping2d. --. 1 fi if $is_window" && "!{*} break fi else # Upscale. factor:=1-($scale-1)/max(1,$nb_scales-2) # Linear scale factor from 1 to 0 +r[0,1] $size_factor%,$size_factor%,100%,100%,2 +to_color[-2,-1] channels[-2,-1] 0,2 gradient_norm[-2,-1] a[-3,-1] c a[-3,-1] c # Append gradient information as last channel stylize_match[-2,-1] mv[-2,-1] -3 sh. 0 *. {-3,w/$ws} rm. sh. 1 *. {-3,h/$hs} rm. round. # Smart upscale of displacement field. +. 1 r. ...,...,1,100%,4 -. 1 do f. "begin(const boundary = 1; nx = ny = vectors(); nx[0] = ny[1] = 1); i>=0?I:( j(-1)>=0?J(-1) + nx: j(0,-1)>=0?J(0,-1) + ny: j(1)>0?J(1) - nx: j(0,1)>0?J(0,1) - ny:I)" while im<0 ws,hs={-2,[w,h]} if $<<$skip_finest_scales rm[-3,-2] continue fi # Skip finest scales +warp_patch.. .,$patch_size_synthesis,$patch_size_synthesis,1 if {*} w. fi # Inject gradients from target (in Lab colorspace). fidelity:=max(0,$fidelity_finest+($fidelity_coarsest-$fidelity_finest)*$factor) fidelity_smoothness:=$fidelity_smoothness_finest+\ ($fidelity_smoothness_coarsest-$fidelity_smoothness_finest)*$factor if $fidelity>0.1 sh. 0,2 sh[-5] 0,2 +gradient_norm[-2,-1] rm[-4,-3] # Gradient norm on colors only *. $fidelity argmax[-2,-1] b. xy,$fidelity_smoothness n. 0,{min(1,$fidelity)} sh[-5,-2] 0,2 srgb2lab[-2,-1] rm[-2,-1] +*. $fidelity_chroma r. 100%,100%,1,2,1 a[-2,-1] c j.. [-5],0,0,0,0,1,. rm. sh. 0,2 lab2srgb. rm. fi rm[-4] # Iterate patch-matching steps. nb_matches:=max(0,round($nb_matches_finest+($nb_matches_coarsest-$nb_matches_finest)*$factor^2)) nb_scales1:=$nb_scales-1 nb_matches1:=$nb_matches-1 if {*} +r. $wsiz,1,100% to. "Scale "$scale/$nb_scales1": 0%",5,2,24 w. -1,-1,0 rm. fi repeat $nb_matches { matchpatch. ...,$patch_size_analysis,$patch_size_analysis,1,$mprec0,$mprec1,$penalize_repetitions,0,.. -.. . abs.. diff={-2,ia} rm.. +warp_patch.. .,$patch_size_synthesis,$patch_size_synthesis,1 if {*}" && "(!($>%5)" || "$nb_matches<=10) +r. $wsiz,1,100% to. "Scale "$scale/$nb_scales1": "{round(100*($>+1)/$nb_matches)}%,5,2,24 w. -1,-1,0 rm. fi if $is_window" && "!{*} break fi if $diff<1 break fi } rm[-3,-1] fi if $is_window" && "!{*} break fi } if $is_window" && "!{*} k[0,1] break fi # Do final rendering. +warp_patch[1] .,$patch_size_synthesis_final,$patch_size_synthesis_final,1 c. 0,255 rv[0,-1] rm[-2,-1] } } rm. um stylize_match #@cli tetris : _scale>0 #@cli : Apply tetris effect on selected images. #@cli : Default value: 'scale=10'. #@cli : $ image.jpg +tetris 10 tetris : skip ${1=10} e[^-1] "Apply tetris effect on image$?, with scale $1." foreach { wh={w},{h},1,{s} r $1%,$1%,$1%,100%,2 n 0,255 quantize 10,1,0 r $wh b 2 sharpen 300,1 } #@cli warhol : _M>0,_N>0,_smoothness>=0,_color>=0 #@cli : Create MxN Andy Warhol-like artwork from selected images. #@cli : Default values: 'M=3', 'N=M', 'smoothness=2' and 'color=20'. #@cli : $ image.jpg warhol 3,3,3,40 warhol : skip ${1=3},${2=$1},${3=2},${4=20} e[^-1] "Create $1x$2 Andy Warhol-like artwork from image$?." r0:=100/max($1,$2) foreach { norm b $3 r $r0%,$r0%,1,100%,2 quantize 6 n 0,5 round 1 repeat $1 { repeat $2 { (0,1,2,3,4,5) n. 32,224 6,1,1,2,128 noise. $4,0 c. 0,255 a[-2,-1] c ycbcr2rgb. +map[0] . rm.. } } append_tiles[^0] $1,$2 =>[1] {0,n} rm[0] } #@cli weave : _density>=0,0<=_thickness<=100,0<=_shadow<=100,_shading>=0,_fibers_amplitude>=0,_fibers_smoothness>=0,\ # _angle,-1<=_x_curvature<=1,-1<=_y_curvature<=1 #@cli : Apply weave effect to the selected images. #@cli : 'angle' can be { 0:0 deg. | 1:22.5 deg. | 2:45 deg. | 3:67.5 deg. }. #@cli : Default values: 'density=6', 'thickness=65', 'shadow=40', 'shading=0.5', 'fibers_amplitude=0', _\ # 'fibers_smoothness=0', 'angle=0' and 'curvature_x=curvature_y=0' #@cli : $ image.jpg weave , weave : check "${1=6}>=0 && ${2=65}>=0 && $2<=100 && ${3=40}>=0 && $3<=100 && ${4=0.5}>=0" check "${5=0}>=0 && ${6=0}>=0 && ${7=0}>=0 && $7<=3 && ${8=0}>=-1 && $8<=1 && ${9=0}>=-1 && $9<=1" e[^-1] "Apply weave effect to image$?, with $1 strips, thickness $2, shadow $3, shading $4, "\ "fibers amplitude $5 and fibers smoothness $6, angle "{$7*22.5}" deg. and curvatures ($8,$9)." foreach { split_opacity l[0] { w:=round(max(w,h)/$1,1,1) h=$w s:=(100-$3)*255% p:=max(0.01,$4) # Create patterns. 1,$h =. 1,0,50% distance. 1 ^. $p c. 50%,100% r. {max(1,round($2*$w%))},100% $w,1 =. 1,50% distance. 1 ^. $p c. 50%,100% *. -1 r. 100%,{max(1,round($2*$h%))} +*. -1 +*... -1 n[-4,-2] 0,$s n[-3,-1] $s,255 {w},1 1,... rand[-2,-1] 0,1 b[-2,-1] $6% n[-2,-1] -$5,$5 ri. [-4] +[-5] . +[-4,-1] +[-5] . +[-2,-1] +f... 255 a[-4,-1] c +f. 255 a[-2,-1] c amp_x:=$8*($w-w)/2 amp_y:=$9*($w-w)/2 r[-4--1] $w,$h,1,100%,0,0,0.5,0.5 f[-4] i(x+$amp_x*sin(y/h*pi),y,0,c,1,2) f. i(x-$amp_x*sin(y/h*pi),y,0,c,1,2) f... i(x,y+$amp_y*sin(x/w*pi),0,c,1,2) f.. i(x,y-$amp_y*sin(x/w*pi),0,c,1,2) blend[-4,-3] alpha blend[-2,-1] alpha c[-2,-1] 0,255 # Render full pattern and merge. /[-2,-1] 255 . ... a[-4,-2] x a[-2,-1] x a[-2,-1] y rotate_tileable. {$7*22.5} r. ..,..,1,1,0,2 *[-2,-1] } a c } #@cli whirls : _texture>=0,_smoothness>=0,_darkness>=0,_lightness>=0 #@cli : Add random whirl texture to selected images. #@cli : Default values: 'texture=3', 'smoothness=6', 'darkness=0.5' and 'lightness=1.8'. #@cli : $ image.jpg whirls , whirls : skip ${1=3},${2=6},${3=0.5},${4=1.8} e[^-1] "Add random whirl texture to image$?, with texture $1, smoothness $2, darkness $3 and lightness $4." foreach { 100%,100% noise. 0.3,2 ==. 1 repeat $1 { b. $2 +. 0.1 gradient_norm. ^. 0.2 } n. $3,$4 ri. .. * c 0,255 } #------------------------------------ # #@cli :: Warpings # #------------------------------------ #@cli deform : _amplitude>=0,_interpolation #@cli : Apply random smooth deformation on selected images. #@cli : 'interpolation' can be { 0:none | 1:linear | 2:bicubic }. #@cli : Default value: 'amplitude=10'. #@cli : $ image.jpg +deform[0] 10 +deform[0] 20 deform : check "${1=10}>=0 && inrange(${2=1},0,2)" e[^-1] "Apply random smooth deformation on image$?, with amplitude $1." foreach { if d==1 2%,2%,1,2 else 2%,2%,2% fi noise. $1 r. ..,..,..,2,5 warp.. .,1,$2,1 rm. } #@cli euclidean2polar : _center_x[%],_center_y[%],_stretch_factor>0,\ # _boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Apply euclidean to polar transform on selected images. #@cli : Default values: 'center_x=center_y=50%', 'stretch_factor=1' and 'boundary_conditions=3'. #@cli : $ image.jpg +euclidean2polar , euclidean2polar : skip ${1=50%},${2=50%} check "${3=1}>0 && isint(${4=3},0,3)" e[^-1] "Apply euclidean to polar transform on image$?, with center point ($1,$2), stretch factor $3 and "\ ${"arg0 $4,dirichlet,neumann,periodic,mirror"}" boundary conditions." foreach { cx,cy:=ispercentage($1)?$1*(w-1):$1,ispercentage($2)?$2*(h-1):$2 R:=sqrt(max($cx^2,(w-1-$cx)^2)+max($cy^2,(h-1-$cy)^2)) f "r = $R*(x/(w-1))^$3; a = y*2*pi/(h-1); i($cx + r*cos(a),$cy + r*sin(a),z,c,1,$4)" } #@cli equirectangular2nadirzenith #@cli : Transform selected equirectangular images to nadir/zenith rectilinear projections. equirectangular2nadirzenith : e[^-1] "Transform equirectangular image$? to nadir/zenith rectilinear projections." foreach { 100%,100%,1,2 sh. 100% f. " X = 2*x/(w-1) - 1; Y = y/(h-1) - 0.5; if (X<0, sinphi1 = 1; X+=0.5, sinphi1 = -1; X-=0.5 ); rr = sqrt(X*X + Y*Y); cc = atan(2*rr); phi = rr?asin(cos(cc)*sinphi1):0; X = atan2(X,-Y*sinphi1)/pi; Y = phi/pi; (++X)*=0.5*w; (Y+=0.5)*=h; i(#-2) = X; Y;" warp[0] [1],0,0,1 k... } #@cli fisheye : _center_x,_center_y,0<=_radius<=100,_amplitude>=0 #@cli : Apply fish-eye deformation on selected images. #@cli : Default values: 'x=y=50', 'radius=50' and 'amplitude=1.2'. #@cli : $ image.jpg +fisheye , fisheye : skip ${1=50},${2=50},${3=50},${4=1.2} e[^-1] "Apply Fish-eye effect on image$?, centered at ($1%,$2%) with radius $3% and amplitude $4." if !$4 return fi foreach { 100%,100%,1,1 =. 1,$1%,$2% distance. 1 c. 0,$3% *. -1 n. 0,1 ^. {1/$4} i.. ({-$1/100},{1-$1/100};{-$1/100},{1-$1/100}^{-$2/100},{-$2/100};{1-$2/100},{1-$2/100}) r.. .,.,1,2,3 n. 0,{max(w,h)} *[-2,-1] warp.. .,1,1,1 rm. } #@cli flower : _amplitude,_frequency,_offset_r[%],_angle,_center_x[%],_center_y[%],\ # _boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Apply flower deformation on selected images. #@cli : Default values: 'amplitude=30', 'frequency=6', 'offset_r=0', 'angle=0', 'center_x=center_y=50%' \ # and 'boundary_conditions=3'. #@cli : $ image.jpg +flower , flower : skip ${1=30},${2=6},${3=0},${4=0},${5=50%},${6=50%},${7=3} e[^-1] "Apply flower deformation on image$?, with amplitude $1, frequency $2, offset $3, angle $4 deg. and center point ($1,$2)." if ispercentage($3) transform_polar "r + (R*$3) + R*$1/100*cos(a*$2+$4*pi/180)","a",$5,$6,$7 else transform_polar "r + $3 + R*$1/100*cos(a*$2+$4*pi/180)","a",$5,$6,$7 fi #@cli kaleidoscope : _center_x[%],_center_y[%],_radius,_angle,\ # _boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Create kaleidoscope effect from selected images. #@cli : Default values: 'center_x=center_y=50%', 'radius=100', 'angle=30' and 'boundary_conditions=3'. #@cli : $ image.jpg kaleidoscope , kaleidoscope : skip ${1=50%},${2=50%},${3=100},${4=30},${5=3} e[^-1] "Create kaleidoscope effect from image$?, with center point ($1,$2), radius $3, angle $4 deg." euclidean2polar $1,$2,1,$5 foreach { +columns 0,$3% rows. 0,$4% ri. ..,0,2 =>[1] {0,n} rm[0] } polar2euclidean $1,$2,1,$5 #@cli map_sphere : _width>0,_height>0,_radius,_dilation>0,_fading>=0,_fading_power>=0 #@cli : Map selected images on a sphere. #@cli : Default values: 'width=height=512', 'radius=100', 'dilation=0.5', 'fading=0' and 'fading_power=0.5'. #@cli : $ image.jpg map_sphere , map_sphere : check "${1=512}>0 && ${2=512}>0 && ${5=0}>=0 && ${6=0.5}>=0" skip ${3=100},${4=0.5} e[^-1] "Map image$? on spheres in $1x$2 images, with radius $3, dilation $4 and fading $5." r2:=($3*min($1,$2)/200)^2 # Compute squared radius. foreach { i.. 100%,1,1,100%,0 =>[0] {1,n} a y # Add one border line to have a sphere exterior. ({-$1/2},{$1/2}) ({-$2/2};{$2/2}) r[-2,-1] $1,$2,1,1,3 atan2. .. rm.. # Compute theta angle. $1,$2 =. 1,50%,50% distance. 1,3 /. $r2 sqrt. c. 0,1 asin. # Compute phi angle. +.. {pi} *.. {({-3,w}-1)/(2*pi)} # Normalize theta to X-coordinates *. {2/pi} ^. $4 *. {{-3,h}-1} *. -1 +. {{-3,h}-1} # Normalize phi to Y-coordinates if $5 +>=. 1 distance. 1 c. 0,$5% n. 0,1 ^. $6 c.. 1,100% -[-2,-1] fi r[-1,-2] 100%,100%,{-3,d} +f. z a[-3--1] c warp.. .,0,1,1 rm. # Apply image warping } #@cli nadirzenith2equirectangular #@cli : Transform selected nadir/zenith rectilinear projections to equirectangular images. nadirzenith2equirectangular : e[^-1] "Transform nadir/zenith rectilinear projection$? to equirectangular images." foreach { 100%,100%,1,2 sh. 100% f. " X = 2*x/(w-1) - 1; Y = y/(h-1) - 0.5; output = 1; if (Y>0.125, sinphi1 = 1; xc = -0.5, if (Y<-0.125, sinphi1 = -1; xc = 0.5, output = 0 )); cosc = sinphi1*sin(Y*pi); xx = cos(Y*pi)*sin(X*pi)/cosc; yy = -sinphi1*cos(Y*pi)*cos(X*pi)/cosc; if (abs(xx)>1, output=0); (xx*=0.5)+=xc; yy*=0.5; if (!output, xx = yy = -1); (++xx)*=0.5*w; (yy+=0.5)*=h; i(#-2) = xx; yy;" to_a[0] warp[0] [1],0,0,0 k... } #@cli polar2euclidean : _center_x[%],_center_y[%],_stretch_factor>0,\ # _boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Apply euclidean to polar transform on selected images. #@cli : Default values: 'center_x=center_y=50%', 'stretch_factor=1' and 'boundary_conditions=3'. #@cli : $ image.jpg +euclidean2polar , polar2euclidean : skip ${1=50%},${2=50%} check "${3=1}>0 && isint(${4=3},0,3)" e[^-1] "Apply polar to euclidean transform on image$?, with center point ($1,$2), stretch factor $3 and "\ ${"arg0 $4,dirichlet,neumann,periodic,mirror"}" boundary conditions." foreach { cx,cy:=ispercentage($1)?$1*(w-1):$1,ispercentage($2)?$2*(h-1):$2 R:=sqrt(max($cx^2,(w-1-$cx)^2)+max($cy^2,(h-1-$cy)^2)) f "X = sqrt((x - "$cx")^2 + (y - "$cy")^2); tmp = atan2(y - "$cy",x - "$cx"); Y = tmp<0?tmp + 2*pi:tmp; i((X/"$R")^(1/$3)*(w - 1),Y*(h - 1)/(2*pi),z,c,1,$4)" } #@cli raindrops : _amplitude,_density>=0,_wavelength>=0,_merging_steps>=0 #@cli : Apply raindrops deformation on selected images. #@cli : Default values: 'amplitude=80','density=0.1', 'wavelength=1' and 'merging_steps=0'. #@cli : $ image.jpg +raindrops , raindrops : check "${2=0.1}>=0 && ${3=1}>=0 && isint(${4=0},0)" skip ${1=80} e[^-1] "Apply raindrops deformation on image$?, with amplitude $1, density $2, wavelength $3 and $4 merging steps." foreach { 100%,100% noise. $2,2 ==. 1 distance. 1 f. "cos(i)/(1+i/(1e-8+$3))" if $4 i.. (0,1,0;1,0,1;0,1,0) /.. 2 . repeat $4 { +convolve. ...,1 -. ... rm... } rm[-3,-2] fi g. a[-2,-1] c *. {$1/(1e-5+max(abs(im),abs(iM)))} warp.. .,1,1,1 rm. } #@cli ripple : _amplitude,_bandwidth,_shape={ 0:block | 1:triangle | 2:sine | 3:sine+ | 4:random },_angle,_offset #@cli : Apply ripple deformation on selected images. #@cli : Default values: 'amplitude=10', 'bandwidth=10', 'shape=2', 'angle=0' and 'offset=0'. #@cli : $ image.jpg +ripple , ripple : skip ${1=10},${2=20},${3=2},${4=0},${5=0} e[^-1] "Apply ripple deformation on image$?, with amplitude $1, bandwidth $2, shape $3, angle $4 deg. and offset $5." theta:=$4*pi/180 C:=cos($theta) S:=-sin($theta) foreach { 100%,100%,1,1,"x" -. {w/2} 100%,100%,1,1,'y' -. {h/2-$5} *.. $S *. $C +[-2,-1] # Generate rotated Y. _ripple$3. $1,$2 # Generate warp field. +*. {-$S} *.. $C a[-2,-1] c # Rotate warp field. warp.. .,1,1,1 rm. } _ripple0 : f {$1/2}*"(1-2*(i%"{2*$2}"<$2))" _ripple1 : f "I=(i%$2)/$2;$1*(2*(I<0.5?I:1-I)-0.5)" _ripple2 : f {-$1/2}*"cos(i*"{2*pi/$2}")" _ripple3 : f {-$1/2}*"abs(cos(i*"{2*pi/$2}"))" _ripple4 : skip $* n 0,{h-1} 1,{h} rand. {[-$1,$1]/2} m,M:=im,iM b. {$2/10} n. $m,$M map.. . rm. #@cli rotoidoscope : _center_x[%],_center_y[%],_tiles>0,_smoothness[%]>=0,\ # _boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Create rotational kaleidoscope effect from selected images. #@cli : Default values: 'center_x=center_y=50%', 'tiles=10', 'smoothness=1' and 'boundary_conditions=3'. #@cli : $ image.jpg +rotoidoscope , rotoidoscope : skip ${1=50%},${2=50%},${5=1} check "${3=10}>0 && ${4=3}>=0" e[^-1] "Create rotational kaleidoscope effect from image$?, with center point ($1,$2), $3 tiles and smoothness $4." foreach { repeat $3 { +rotate[0] {360/$3},1,$5,$1,$2 blend_edges $4 } } #@cli spherize : _radius[%]>=0,_strength,_smoothness[%]>=0,_center_x[%],_center_y[%],_ratio_x/y>0,_angle,\ # _interpolation #@cli : Apply spherize effect on selected images. #@cli : Default values: 'radius=50%', 'strength=1', 'smoothness=0', 'center_x=center_y=50%', 'ratio_x/y=1', \ # 'angle=0' and 'interpolation=1'. #@cli : $ image.jpg grid 5%,5%,0,0,0.6,255 spherize , spherize : check "${1=50%}>=0 && ${3=0}>=0 && ${6=1}>0 && isint(${8=1},0,2)" skip "${2=1},${4=50%},${5=50%},${7=0}" e[^-1] "Apply spherize effect on image$?, with radius $1, strength $2, smoothness $3, center ($4,$5), x/y-ratio $6, angle $7 and "${"arg0 $8,nearest-neighbor,linear,cubic"}" interpolation." if !$1||!$2 return fi foreach { rmax:=ispercentage($1)?0.5*sqrt((w-1)^2+(h-1)^2)*$1:$1 centerx:=ispercentage($4)?(w-1)*$4:$4 centery:=ispercentage($5)?(h-1)*$5:$5 strength:=$2>0?$2:1-exp($2/5) 100%,100%,1,2," begin( center = [ "$centerx","$centery" ]; wh1 = [ w,h ] - 1; m2wh1 = 0.5*max(wh1); rmax = "$rmax"/m2wh1; const f = 1/"$strength"; const ratio = $6; rotf = rot($7°); rotb = rot(-$7°); ); xy = ([x,y] - center)/m2wh1; xy = rotf*xy; ratio>=1?(xy[1]*=ratio):(xy[0]/=ratio); r = norm(xy); z = r=1?(xy[1]/=ratio):(xy[0]*=ratio); xy = rotb*xy; xy = center + f*xy/(f + z)*m2wh1" b. $3 warp.. .,0,$8,1 rm. } #@cli symmetrize : _x[%],_y[%],_angle,_boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror },\ # _is_antisymmetry={ 0 | 1 },_swap_sides={ 0 | 1 } #@cli : Symmetrize selected images regarding specified axis. #@cli : Default values: 'x=y=50%', 'angle=90', 'boundary_conditions=3', 'is_antisymmetry=0' and 'swap_sides=0'. #@cli : $ image.jpg +symmetrize 50%,50%,45 +symmetrize[-1] 50%,50%,-45 symmetrize : skip ${1=50%},${2=50%},${3=90},${4=3},${5=0},${6=0} e[^-1] "Symmetrize image$?, regarding axis ($1,$2,$3 deg.)." theta:=deg2rad($3) u,v:=cos($theta),sin($theta) if $6 symmetry_cond=A<0 else symmetry_cond=A>0 fi foreach { x0:=ispercentage($1)?w*$1:$1 y0:=ispercentage($2)?h*$2:$2 if $5 f "A = ($y0 - y)*$u - ($x0 - x)*$v; X = x + 2*($x0 - x); Y = y + 2*($y0 - y); "$symmetry_cond"?i(X,Y,z,c,1,$4):i" else f "A = ($y0 - y)*$u - ($x0 - x)*$v; X = x - 2*$v*A; Y = y + 2*$u*A; "$symmetry_cond"?i(X,Y,z,c,1,$4):i" fi } #@cli transform_polar : "expr_radius",_"expr_angle",_center_x[%],_center_y[%],\ # _boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Apply user-defined transform on polar representation of selected images. #@cli : Default values: 'expr_radius=R-r', 'expr_rangle=a', 'center_x=center_y=50%' and 'boundary_conditions=3'. #@cli : $ image.jpg +transform_polar[0] R*(r/R)^2,a +transform_polar[0] r,2*a transform_polar : skip "${1=R-r}","${2=a}",${3=50%},${4=50%},${5=3} e[^-1] "Apply custom polar transform with 'new_r = $1', 'new_a = $2', center point ($3%,$4%)." foreach { cx:=ispercentage($3)?$3*(w-1):$3 cy:=ispercentage($4)?$4*(h-1):$4 R:=sqrt(max($cx^2,(w-1-$cx)^2)+max($cy^2,(h-1-$cy)^2)) f "R ="$R"; r = sqrt((x-"$cx")^2 + (y-"$cy")^2); a = atan2(y-"$cy",x-"$cx"); nr = ($1); na = ($2); i("$cx" + nr*cos(na), "$cy" + nr*sin(na), z, c,1,$5)" } #@cli twirl : _amplitude,_center_x[%],_center_y[%],\ # _boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Apply twirl deformation on selected images. #@cli : Default values: 'amplitude=1', 'center_x=center_y=50%' and 'boundary_conditions=3'. #@cli : $ image.jpg twirl 0.6 twirl : skip ${1=1},${2=50%},${3=50%},${4=3} e[^-1] "Apply twirl deformation on image$?, with amplitude $1 and center point at ($2%,$3%)." euclidean2polar $2,$3,1,$4 foreach { .,.,1,1,$1*x channels. -1,0 warp.. .,1,1,2 rm. } polar2euclidean $2,$3,1,1 #@cli warp : [warping_field],_mode,_interpolation,_boundary_conditions,_nb_frames>0 : (+) #@cli : Warp selected images with specified displacement field. #@cli : 'mode' can be { 0:backward-absolute | 1:backward-relative | 2:forward-absolute | 3:forward-relative }. #@cli : 'interpolation' can be { 0:nearest-neighbor | 1:linear | 2:cubic }. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'mode=0', 'interpolation=1', 'boundary_conditions=0' and 'nb_frames=1'. #@cli : $ image.jpg 100%,100%,1,2,'X=x/w-0.5;Y=y/h-0.5;R=(X*X+Y*Y)^0.5;A=atan2(Y,X);130*R*(!c?cos(4*A):sin(8*A))' \ # warp[-2] [-1],1,1,0 quiver[-1] [-1],10,1,1,1,100 #@cli : $$ https://gmic.eu/oldtutorial/_warp #@cli warp_patch : [warping_field],patch_width>=1,_patch_height>=1,_patch_depth>=1,_std_factor>0,_boundary_conditions. #@cli : Patch-warp selected images, with specified 2D or 3D displacement field (in backward-absolute mode). #@cli : Argument 'std_factor' sets the std of the gaussian weights for the patch overlap, #@cli : equal to 'std = std_factor*patch_size'. #@cli : 'boundary_conditions' can be { 0:dirichlet | 1:neumann | 2:periodic | 3:mirror }. #@cli : Default values: 'std_factor=0.3' and 'boundary_conditions=3'. warp_patch : check ${is_image_arg\ $1}" && isint(${2=3},1) && isint(${3=$2},1) && "\ "isint(${4=1},1) && isnum(${5=0.3}) && $5>0 && isint(${6=3},0,3)" e[^-1] "Patch-warp image$? with backward-absolute displacement field $1, using $2x$3x$4 patches, std factor $5 and "${"arg0 $6,dirichlet,neumann,periodic,mirror"}" boundary conditions." if $2<=1 pass$1 warp[^-1] .,0 rm. return fi repeat $! { pass$1 l[$>,-1] { nm={0,n} [0],[0],[0],1,1 a[0,-1] c # Add weighting channel 100%,100%,100%,{0,s} if s#1>=3 # 3D version eval[1] "> begin( const pw = $2; const ph = $3; const pd = $4; const stdf = $5; const boundary = $6; const pw1 = int(pw/2); const pw2 = pw - pw1 - 1; const ph1 = int(ph/2); const ph2 = ph - ph1 - 1; const pd1 = int(pd/2); const pd2 = pd - pd1 - 1; const pwhd = pw*ph*pd; return = vector(s); # Pre-compute gaussian weights. if (stdf<5, ref(vectorpwhd(),weights); offw = 0; for (zw = -pd1, zw<=pd2, ++zw, for (yw = -ph1, yw<=ph2, ++yw, for (xw = -pw1, xw<=pw2, ++xw, weights[offw++] = exp(-xw^2/(2*(stdf*pw)^2) - yw^2/(2*(stdf*ph)^2) - zw^2/(2*(stdf*pd)^2)); ); ); ); ); ); u = i(x,y,z,0); v = i(x,y,z,1); w = i(x,y,z,2); ref(crop(#0,u - pw1, v - ph1,w - pd1,pw,ph,pd,boundary),patch); stdf<5? draw(#2,patch,x - pw1,y - ph1,z - pd1,0,pw,ph,pd,s#0,-1,weights): draw(#2,patch,x - pw1,y - ph1,z - pd1,0,pw,ph,pd,s#0,-1); return" else # 2D version eval[1] "> begin( const pw = $2; const ph = $3; const stdf = $5; const boundary = $6; const pw1 = int(pw/2); const pw2 = pw - pw1 - 1; const ph1 = int(ph/2); const ph2 = ph - ph1 - 1; const pwh = pw*ph; return = vector(s); # Pre-compute gaussian weights. if (stdf<5, ref(vectorpwh(),weights); offw = 0; for (yw = -ph1, yw<=ph2, ++yw, for (xw = -pw1, xw<=pw2, ++xw, weights[offw++] = exp(-xw^2/(2*(stdf*pw)^2) - yw^2/(2*(stdf*ph)^2)); ); ); ); ); u = i(x,y,z,0); v = i(x,y,z,1); ref(crop(#0,u - pw1, v - ph1,pw,ph,boundary),patch); stdf<5? draw(#2,patch,x - pw1,y - ph1,0,0,pw,ph,1,s#0,-1,weights): draw(#2,patch,x - pw1,y - ph1,0,0,pw,ph,1,s#0,-1); return" fi s. c,-{0,s-1} /[-2,-1] k. => $nm } } #@cli warp_perspective : _x-angle,_y-angle,_zoom>0,_x-center,_y-center,\ # _boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Warp selected images with perspective deformation. #@cli : Default values: 'x-angle=1.5', 'y-angle=0', 'zoom=1', 'x-center=y-center=50' and 'boundary_conditions=2'. #@cli : $ image.jpg warp_perspective , warp_perspective : skip ${1=1.5},${2=0},${3=1},${4=50},${5=50},${6=2} e[^-1] "Apply perspective warp on image$?, with angles ($1 deg.,$2 deg.), zoom $3 and offsets ($4,$5)." foreach { (0,100) -. $4 /. 100 (0;100) -. $5 /. 100 r[-2,-1] ...,...,...,1,3 +*.. $2 +*.. $1 +[-2,-1] +. $3 /... . /[-2,-1] *.. 100 +.. $4 /.. 100 *.. {-3,w} *. 100 +. $5 /. 100 *. {-3,h} a[-2,-1] c warp.. .,0,1,$6 rm. } #@cli warp_rbf : xs0[%],ys0[%],xt0[%],yt0[%],...,xsN[%],ysN[%],xtN[%],ytN[%] #@cli : Warp selected images using RBF-based interpolation. #@cli : Each argument (xsk,ysk)-(xtk,ytk) corresponds to the coordinates of a keypoint #@cli : respectively on the source and target images. The set of all keypoints define the overall image deformation. #@cli : $ image.jpg +warp_rbf 0,0,0,0,100%,0,100%,0,100%,100%,100%,100%,0,100%,0,100%,50%,50%,70%,50%,25%,25%,25%,75% warp_rbf : e[^-1] "Warp image$? using RBF interpolation, with keypoints ($*)." $=arg N:=$#/4 if int($N)!=$N error[0--2] "Command 'warp_rbf': Wrong number of arguments ($#)." fi foreach { # Retrieve absolute keypoints coordinates. 4,$N repeat wh { a=${arg{1+$>}} isp:=ispercentage($a) eval i[$>]=$isp?($>%2?w#0:h#0)*$a:$a } s. x,2 -. .. a[-2,-1] x permute. yzcx # Generate warping field by RBF interpolation. rbf. {0,[w,h]} warp[0] .,1,1,3 rm. } #@cli water : _amplitude,_smoothness>=0,_angle #@cli : Apply water deformation on selected images. #@cli : Default values: 'amplitude=30', 'smoothness=1.5' and 'angle=45'. #@cli : $ image.jpg water , water : check ${2=1.5}>=0 skip ${1=30},${3=1},${4=45} e[^-1] "Apply water deformation on image$?, with amplitude $1, smoothness $2 and angle $3." foreach { 25%,25%,25%,1 noise. $1 g. xy *.. {-sin($3*pi/180)} *. {cos($3*pi/180)} +[-2,-1] b. $2 *. 2 r. ..,..,1,2,3 warp.. .,1,1,1 rm. } #@cli wave : _amplitude>=0,_frequency>=0,_center_x,_center_y #@cli : Apply wave deformation on selected images. #@cli : Default values: 'amplitude=4', 'frequency=0.4' and 'center_x=center_y=50'. #@cli : $ image.jpg wave , wave : skip ${1=4},${2=0.4},${3=50},${4=50} e[^-1] "Apply wave deformation on image$?, with amplitude $1, frequency $2 and center point at ($3%,$4%)." foreach { 100%,100% =. 1,$3%,$4% distance. 1 *. $2 +sin. cos.. a[-2,-1] c *. $1 warp.. .,1,1,1 rm. } #@cli wind : _amplitude>=0,_angle,0<=_attenuation<=1,_threshold #@cli : Apply wind effect on selected images. #@cli : Default values: 'amplitude=20', 'angle=0', 'attenuation=0.7' and 'threshold=20'. #@cli : $ image.jpg +wind , wind : check "isint(${1=20},0) && ${3=0.7}>=0 && $3<=1" skip "${2=0},${4=20}" e[^-1] "Apply wind effect on image$?, with amplitude $1, angle $2 deg., attenuation $3 and threshold $4." if !$1 return fi dx,dy,fact:=[cexp([0,$2°]),(1-$3)^(1/$1)] foreach { +gradient_norm >=. $4% M:=iM r. 100%,100%,1,.. *. .. repeat $1 { +shift. {round($>*[$dx,$dy])} max[0,-1] *. $fact remove_pixels. {100/$1}% } rm. } #@cli zoom : _factor,_cx,_cy,_cz,_boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Apply zoom factor to selected images. #@cli : Default values: 'factor=1', 'cx=cy=cz=0.5' and 'boundary_conditions=0'. #@cli : $ image.jpg +zoom[0] 0.6 +zoom[0] 1.5 zoom : skip ${1=2},${2=0.5},${3=0.5},${4=0.5},${5=0} e[^-1] "Apply zoom effect on image$?, with factor $1 and center ($2,$3)." foreach { if d==1 # 2D image. ({(w-1)*$2*(1-1/$1)},{(w-1)*($2+(1-$2)/$1)}) ({({-2,h}-1)*$3*(1-1/$1)};{({-2,h}-1)*($3+(1-$3)/$1)}) r[-2--1] ...,...,1,1,3 a[-2--1] c warp.. .,0,1,$5 else # 3D image. ({(w-1)*$2*(1-1/$1)},{(w-1)*($2+(1-$2)/$1)}) ({({-2,h}-1)*$3*(1-1/$1)};{({-2,h}-1)*($3+(1-$3)/$1)}) ({({-3,d}-1)*$4*(1-1/$1)}/{({-3,d}-1)*($4+(1-$4)/$1)}) r[-3--1] [-4],[-4],[-4],1,3 a[-3--1] c warp.. .,0,1,$5 fi rm. } #----------------------------- # #@cli :: Degradations # #----------------------------- #@cli cracks : 0<=_density<=100,_is_relief={ 0 | 1 },_opacity,_color1,... #@cli : Draw random cracks on selected images with specified color. #@cli : Default values: 'density=25', 'is_relief=0', 'opacity=1' and 'color1=0'. #@cli : $ image.jpg +cracks , cracks : check "${1=25}>=0" skip ${2=0},${3=1},${4=0} e[^-1] "Add random cracks to image$?, with density $1, opacity $3 and color (${4--1})." foreach { cut:=im,iM 100%,100%,1,2,'u<0.25*($1%)^4?[u,1]:[0,0]' s. c distance. 1 *. -1 watershed.. . rm. +dilate. 3 -[-2,-1] !=. 0 # thinning. 1 if $2 f. "i?i:j(1)?2:j(-1)?0.5:i" n. 0,1 +fc.. ${4--1} *. .. !=.. 0 j... .,0,0,0,0,$3,.. else +fc.. ${4--1} j... .,0,0,0,0,$3,.. fi k[0] } #@cli light_patch : _density>0,_darkness>=0,_lightness>=0 #@cli : Add light patches to selected images. #@cli : Default values: 'density=10', 'darkness=0.9' and 'lightness=1.7'. #@cli : $ image.jpg +light_patch 20,0.9,4 light_patch : skip ${1=10},${2=0.9},${3=1.7} e[^-1] "Apply light patches to image$?, with density $1, darkness $2 and lightness $3." foreach { n 0,255 $1,$1 noise. 40 ri. ..,5 c. 0,255 n. $2,$3 * c 0,255 } #@cli noise_hurl : _amplitude>=0 #@cli : Add hurl noise to selected images. #@cli : Default value: 'amplitude=10'. #@cli : $ image.jpg +noise_hurl , noise_hurl : skip ${1=10} e[^-1] "Add hurl noise to image$?, with amplitude $1%." foreach { +f 0 noise. 10 n. {-2,[im,iM]} 100%,100% noise. $1,2 >. 0 ri. .. *.. . *. -1 +. 1 *[-3,-1] + } #@cli pixelize : _scale_x>0,_scale_y>0,_scale_z>0 #@cli : Pixelize selected images with specified scales. #@cli : Default values: 'scale_x=20' and 'scale_y=scale_z=scale_x'. #@cli : $ image.jpg +pixelize , pixelize : skip ${1=20},${2=$1},${3=$1} e[^-1] "Pixelize image$? with scales ($1%,$2%,$3%)." foreach { whd={w},{h},{d} r $1%,$2%,$3%,100%,2 r $whd } #@cli scanlines : _amplitude,_bandwidth,_shape={ 0:block | 1:triangle | 2:sine | 3:sine+ | 4:random },_angle,_offset #@cli : Apply ripple deformation on selected images. #@cli : Default values: 'amplitude=60', 'bandwidth=2', 'shape=0', 'angle=0' and 'offset=0'. #@cli : $ image.jpg +scanlines , scanlines : skip ${1=60},${2=2},${3=0},${4=0},${5=0} e[^-1] "Apply scanlines effect on image$?, with amplitude $1, bandwidth $2, shape $3, angle $4 deg. and offset $5." theta:=$4*pi/180 C:=cos($theta) S:=-sin($theta) foreach { 100%,100%,1,1,"x" -. {w/2} 100%,100%,1,1,'y' -. {h/2-$5} *.. $S *. $C +[-2,-1] # Generate rotated Y. _ripple$3. $1,$2 # Generate warp field. n. {-$1},$1 + cut 0,255 } #@cli shade_stripes : _frequency>=0,_direction={ 0:horizontal | 1:vertical },_darkness>=0,_lightness>=0 #@cli : Add shade stripes to selected images. #@cli : Default values: 'frequency=5', 'direction=1', 'darkness=0.8' and 'lightness=2'. #@cli : $ image.jpg +shade_stripes 30 shade_stripes : skip ${1=5},${2=1},${3=0.8},${4=2} e[^-1] "Add "${arg0\ !$2,vertical,horizontal}" shaded stripes to image$?, with frequency $1, darkness $3 and lightness $4." n 0,255 foreach { {max(1,w*($2!=0))},{max(1,h*!$2)} noise. $1,2 ==. 1 distance. 1 ri. .. n. $3,$4 * c 0,255 } #@cli shadow_patch : _opacity>=0 #@cli : Add shadow patches to selected images. #@cli : Default value: 'opacity=0.7'. #@cli : $ image.jpg +shadow_patch 0.4 shadow_patch : skip ${1=0.7} e[^-1] "Apply shadow patches to image$?, with opacity $1." foreach { 100%,100%,1,1 shift. -2,-2 shift. 1,1 plasma. 3,0.3,8 abs. b. 1 c. 3%,15% ri. .. n. $1,1 * } #@cli spread : _dx>=0,_dy>=0,_dz>=0 #@cli : Spread pixel values of selected images randomly along x,y and z. #@cli : Default values: 'dx=3', 'dy=dx' and 'dz=0'. #@cli : $ image.jpg +spread 3 spread : skip ${1=3},${2=$1},${3=0} e[^-1] "Spread pixel of image$? randomly, with amplitudes ($1,$2,$3)." foreach { 100%,100%,100%,3 sh. 0 rand. {-$1},$1 rm. sh. 1 rand. {-$2},$2 rm. sh. 2 rand. {-$3},$3 rm. warp.. .,1,1,1 rm. } #@cli stripes_y : _frequency>=0 #@cli : Add vertical stripes to selected images. #@cli : Default value: 'frequency=10'. #@cli : $ image.jpg +stripes_y , stripes_y : skip ${1=10} e[^-1] "Add vertical stripes to image$?, with frequency $1." foreach { 100% noise. $1,2 ==. 1 *. 255 ri. .. *. 0.15 + c 0,255 } #@cli texturize_canvas : _amplitude>=0,_fibrousness>=0,_emboss_level>=0 #@cli : Add paint canvas texture to selected images. #@cli : Default values: 'amplitude=20', 'fibrousness=3' and 'emboss_level=0.6'. #@cli : $ image.jpg +texturize_canvas , texturize_canvas : check "${1=20}>=0 && ${2=3}>=0 && ${3=0.6}>=0 && ${4=80}" e[^-1] "Add canvas texture to image$?, with amplitude $1, fibrousness $2 and emboss level $3." foreach { {w},{h} rand. 0,255 +b x,$2 b.. y,$2 +[-2,-1] g. a[-2,-1] c +compose_channels. + orientation.. compose_channels.. + n.. $3,1 n. 0,255 sharpen. 80 *[-2,-1] n. -$1,$1 + c 0,255 } #@cli texturize_paper #@cli : Add paper texture to selected images. #@cli : $ image.jpg +texturize_paper texturize_paper : e[^-1] "Add paper texture to image$?." foreach { . 30%,30% noise. 1,2 ==. 1 r. ..,..,..,1,0 ifft. rm. shift. {round(w/2)},{round(h/2)},{round(d/2)},0,2 sharpen. 1 n. 1,1.2 ri. .. *[-2,-1] c. ..,.. rm.. } #@cli vignette : _strength>=0,0<=_radius_min<=100,0<=_radius_max<=100 #@cli : Add vignette effect to selected images. #@cli : Default values: 'strength=100', 'radius_min=70' and 'radius_max=90'. #@cli : $ image.jpg vignette , vignette : check "${1=100}>=0 && ${2=70}>=0 && $2<=100 && ${3=90}>=0 && $3<=100" e[^-1] "Add vignette effect to image$?, with strength $1 and size $2." foreach { mM:=im,iM d:=max(w,h) $d,$d =. 1,50%,50% distance. 1 ri. ..,2 c. $2%,$3% n. 0,$1 - c $mM } #@cli watermark_visible : _text,0<_opacity<1,_{ size>0 | font },_angle,_mode={ 0:remove | 1:add },_smoothness>=0 #@cli : Add or remove a visible watermark on selected images (value range must be [0,255]). #@cli : Default values: 'text=(c) G'MIC', 'opacity=0.3', 'size=53', 'angle=25', 'mode=1' and 'smoothness=0'. #@cli : $ image.jpg watermark_visible ,0.7 watermark_visible : check "inrange(${2=0.3},0,1) && ${6=0.5}>=0" skip "${1=\251\ G\47MIC}",${3=53},${4=25},${5=1} e[^-1] ${arg0\ !$5,Add,Remove}" visible watermark '$1' on image$?, with opacity $2, size $3, angle $4 deg." foreach { 0 t. "$1",0,0,$3,1,255 rotate. $4,0,0 b. $6 n. 0,255 ri. ..,0,2 +. .. c. 0,255 # Generate opaque watermark image if $5 *. $2 *.. {1-$2} + # Add watermark else *. $2 - / {1-$2} # Remove watermark fi c 0,255 } #-------------------------------------- # #@cli :: Blending and Fading # #-------------------------------------- #@cli blend : [layer],blending_mode,_opacity[%],_selection_is={ 0:base-layers | 1:top-layers } : \ # blending_mode,_opacity[%] #@cli : Blend selected G,GA,RGB or RGBA images by specified layer or blend all selected images together, #@cli : using specified blending mode. #@cli : 'blending_mode' can be { add | alpha | and | average | blue | burn | darken | difference | #@cli : divide | dodge | edges | exclusion | freeze | grainextract | grainmerge | green | hardlight | #@cli : hardmix | hue | interpolation | lchlightness | lighten | lightness | linearburn | linearlight | luminance | #@cli : multiply | negation | or | overlay | pinlight | red | reflect | saturation | #@cli : screen | seamless | seamless_mixed | shapeareamax | shapeareamax0 | shapeareamin | shapeareamin0 | #@cli : shapeaverage | shapeaverage0 | shapemedian | shapemedian0 | shapemin | shapemin0 | shapemax | shapemax0 | #@cli : shapeprevalent | softburn | softdodge | softlight | stamp | subtract | value | vividlight | xor }. #@cli : 'opacity' must be in range '[0,1]' (or '[0%,100%]'). #@cli : Default values: 'blending_mode=alpha', 'opacity=1' and 'selection_is=0'. #@cli : $ image.jpg +drop_shadow , rescale2d[-1] ,200 rotate[-1] 20 +blend alpha display_rgba[-2] #@cli : $ image.jpg testimage2d {w},{h} blend overlay #@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \ # text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \ # ex add,alpha,and,average,blue,burn,darken #@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \ # text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \ # ex difference,divide,dodge,exclusion,freeze,grainextract,grainmerge #@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \ # text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \ # ex green,hardlight,hardmix,hue,interpolation,lighten,lightness #@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \ # text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \ # ex linearburn,linearlight,luminance,multiply,negation,or,overlay #@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \ # text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \ # ex pinlight,red,reflect,saturation,screen,shapeaverage,softburn #@cli : $ command "ex : $""=arg repeat $""# +blend[0,1] ${arg{$>+1}} \ # text_outline[-1] Mode:\" \"${arg{$>+1}},2,2,23,2,1,255 done" image.jpg testimage2d {w},{h} \ # ex softdodge,softlight,stamp,subtract,value,vividlight,xor blend : skip "${1=},${2=},${3=},${4=}" if !${"is_image_arg $1"} if ['$1']!=0 mode=$1 else mode=alpha fi if ['$2']!=0 opacity=$2 else opacity=1 fi e[0--3] "Blend image$? together, using '"$mode"' mode and opacity "$opacity"." repeat $!-1 { $0.. .,$mode,$opacity rm. } return fi if ['$2']!=0 mode=$2 else mode=alpha fi if ['$3']!=0 opacity:=$3 else opacity=1 fi if ['$4']!=0 selection_mode=$4 else selection_mode=0 fi if $selection_mode s0,s1=foreground,background else s0,s1=background,foreground fi e[^-1] "Blend "$s0" image$? with "$s1" image $1, using '"$mode"' mode and opacity "$opacity"." if !$opacity return fi pass$1 1 foreach[^-1] { nm={n} pass. 0 if $selection_mode rv fi # -> [0] = background, [1] = foreground nb_col_channels,is_alpha_b,is_alpha_f:=max(s#0,s)>=3?3:1,!(s#0%2),!(s%2)||w $nm } rm. _blend_alpha : _blend_normal : _blend_and : &[1] [0] _blend_add : +[1] [0] c[1] 0,255 _blend_average : +[1] [0] /[1] 2 _blend_blue : sh[0] 0,1 j[1] [2] rm[2] _blend_burn : +-[0] 255 +[1] 0.1 /[2] [1] rm[1] +[1] 1 *[1] 255 c[1] 0,255 _blend_darken : min[1] [0] _blend_difference : -[1] [0] abs[1] _blend_divide : +[1] 0.1 ^[1] -1 *[1] [0] *[1] 255 c[1] 0,255 _blend_dodge : -[1] 255.1 ^[1] -1 *[1] [0] *[1] -255 c[1] 0,255 _blend_edges : +blend_edges 0.5 rm[1] _blend_exclusion : +*[0,1] /[2] -127.5 +[1,2] +[1] [0] _blend_freeze : *[1] -255 -[1] 0.1 +-[0] 255 sqr[2] /[2] [1] rm[1] +[1] 1 *[1] 255 c[1] 0,255 _blend_grainextract : -[1] [0] *[1] -1 +[1] 128 c[1] 0,255 _blend_grainmerge : +[1] [0] -[1] 128 c[1] 0,255 _blend_green : sh[0] 0 sh[0] 2 j[1] [2] j[1] [3],0,0,0,2 rm[2,3] _blend_hardlight : +*[0,1] /[2] 127.5 ++[0,1] *[3] 2 -[3] 255 -[3] [2] >[1] 128 j[2] [3],0,0,0,0,1,[1] rm[1,3] c[1] 0,255 _blend_hardmix : +[1] [0] >=[1] 255 *[1] 255 _blend_hue : to_color sh 0,2 rgb2hsv[2,3] sh[2] 1,2 j[1] [4],0,0,0,1 rm[4] hsv2rgb[2,3] rm[2,3] _blend_interpolation : +*[0] {pi/255} *[1] {pi/255} cos[1,2] +[1,2] -[1] 2 *[1] -63.75 c[1] 0,255 _blend_lighten : max[1] [0] _blend_lightness : to_color sh 0,2 rgb2lab[2,3] sh[2] 1,2 j[1] [4],0,0,0,1 rm[4] lab2rgb[2,3] rm[2,3] _blend_lchlightness : _blend_lightness _blend_luminance : to_color sh 0,2 rgb2ycbcr[2,3] sh[2] 1,2 j[1] [4],0,0,0,1 rm[4] ycbcr2rgb[2,3] rm[2,3] _blend_linearburn : +[1] [0] -[1] 255 c. 0,255 _blend_linearlight : *[1] 2 +[1] [0] -[1] 255 c[1] 0,255 _blend_multiply : *[1] [0] /[1] 255 _blend_negation : +[1] [0] -[1] 255 abs[1] *[1] -1 +[1] 255 _blend_or : -|[1] [0] _blend_overlay : +*[0,1] /[2] 127.5 +[1] [0] *[1] 2 -[1] 255 -[1] [2] +<[0] 128 j[1] [2],0,0,0,0,1,[3] rm[2,3] c[1] 0,255 _blend_pinlight : *[1] 2 +blend darken -[1] 256 +blend[0,1] lighten >=[1] 0 j[2] [3],0,0,0,0,1,[1] rm[1,3] _blend_reflect : -[1] 255.1 *[1] -1 +sqr[0] /[2] [1] rm[1] c[1] 0,255 _blend_red : sh[0] 1,100% j[1] [2],0,0,0,1 rm[2] _blend_saturation : to_color sh 0,2 rgb2hsv[2,3] shift[2,3] 0,0,0,-1,2 sh[2] 1,2 j[1] [4],0,0,0,1 rm[4] shift[2,3] 0,0,0,1,2 hsv2rgb[2,3] rm[2,3] _blend_screen : +-[0] 255 -[1] 255 *[1,2] /[1] 255 *[1] -1 +[1] 255 _blend_shapeareamax : f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0 +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0 {1,iM+1},1,1,{0,s+1} f[1] ">area = i(#2); best = I[#3,i]; if (area>best[size(best) - 1], I[#3,i] = [ I(#0),area ]);i" rm[2] channels[2] 0,{s-2} map[1] [2] rm[2] _blend_shapeareamax0 : f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0 +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0 {1,iM+1},1,1,{0,s+1} f[1] ">area = i(#2); best = I[#3,i]; if (area>best[size(best) - 1], I[#3,i] = [ I(#0),area ]);i" rm[2] channels[2] 0,{s-2} point[2] 0,0,0,1,0 map[1] [2] rm[2] _blend_shapeareamin : f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0 +f[0] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[2] round[2] 0.01 area[2] 0,0 {1,iM+1},1,1,{0,s+1},inf f[1] ">area = i(#2); best = I[#3,i]; if (areaarea = i(#2); best = I[#3,i]; if (areai(#2,i(#1,x,y,z,0),0,0,c)+=i;i" +histogram[1] {w},0,{w-1} /[-2,-1] map[1] . rm. _blend_shapeaverage0 : f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0 {iM+1},1,1,{0,s} f[0] ">i(#2,i(#1,x,y,z,0),0,0,c)+=i;i" +histogram[1] {w},0,{w-1} /[-2,-1] point. 0,0,0,1,0 map[1] . rm. _blend_shapemedian : f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0 N:=iM+1 $N,1,1,{s#0} $N,8,1,{s#0} s. x f[1] "> begin(siz = vector"$N"()); k = i; k3 = k + 3; hk3 = h(#k3); copy(i[#k3,siz[k]++],I(#0),s#0,hk3,whd#0); if (siz[k]>=hk3,resize(#k3,1,round(1.5*hk3+1),1,s#0,0,0)); end(repeat (size(siz),k, resize(#k+3,1,siz[k],1,s#0,0,0))); i" repeat s#0 { sh[3--1] $> $N,1,1,1,"ic(#"$N"+3+x)" j[2] .,0,0,0,$> rm[-{$N+1}--1] } map[1] [2] k[0,1] _blend_shapemedian0 : f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0 N:=iM {$N+1},1,1,{s#0} $N,8,1,{s#0} s. x f[1] "> begin(siz = vector"$N"()); k = i; if (k, k1 = k - 1; k2 = k + 2; hk2 = h(#k2); copy(i[#k2,siz[k1]++],I(#0),s#0,hk2,whd#0); if (siz[k1]>=hk2,resize(#k2,1,round(1.5*hk2+1),1,s#0,0,0)); ); end(repeat (size(siz),k, resize(#k+3,1,siz[k],1,s#0,0,0))); i" repeat s#0 { sh[3--1] $> $N,1,1,1,"ic(#"$N"+3+x)" j[2] .,1,0,0,$> rm[-{$N+1}--1] } map[1] [2] k[0,1] _blend_shapemin : f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0 {iM+1},1,1,{0,s},inf f[0] ">i(#2,i(#1,x,y,z,0),0,0,c) = min(i(#2,i(#1,x,y,z,0),0,0,c),i);i" map[1] [2] rm. _blend_shapemin0 : f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0 {iM+1},1,1,{0,s},inf f[0] ">i(#2,i(#1,x,y,z,0),0,0,c) = min(i(#2,i(#1,x,y,z,0),0,0,c),i);i" point. 0,0,0,1,0 map[1] [2] rm. _blend_shapemax : f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm[1] round[1] 0.01 label[1] 0 {iM+1},1,1,{0,s},-inf f[0] ">i(#2,i(#1,x,y,z,0),0,0,c) = max(i(#2,i(#1,x,y,z,0),0,0,c),i);i" map[1] [2] rm. _blend_shapemax0 : f[1] "begin(A = resize([ 0,(s-1)/s ],s,3));I!=0?I+A:I" norm[1] round[1] 0.01 label_fg[1] 0 {iM+1},1,1,{0,s},-inf f[0] ">i(#2,i(#1,x,y,z,0),0,0,c) = max(i(#2,i(#1,x,y,z,0),0,0,c),i);i" point. 0,0,0,1,0 map[1] [2] rm. _blend_shapeprevalent : # Retrieve occurence of each color for each distinct region. label. 0,0 nb_labels:=iM+1 a c +n. 0,255 100%,100%,1,1,"H = 0; repeat (s#-1,p,(H*=31)+=j(#-1,0,0,0,p)); int(H)%2048" rm.. # Compute color hashcodes 1,1,1,{0,s+1}x2048 eval[1] "begin(_color = tmp = vector(#s#0)); color = I(#0); ind = i + 2; whdind = whd(#ind); found = 0; repeat (da_size(#ind),k, copy(_color,i(#ind,0,k,0,1),s#0,1,whdind); _color==color?( found = 1; copy(tmp,i[#ind,0],s#ind,1,whdind); copy(i[#ind,0],i[#ind,k],s#ind,whdind,whdind); copy(i[#ind,k],tmp,s#ind,whdind,1); break(); ); ); found?++i[#ind,k]:da_push(#ind,[ 1,color ])" da_freeze[2--1] a[2--1] y rm.. # Extract most frequent occurence for each distinct region. 1,$nb_labels,1,{0,s} eval.. "P = I; region = P[size(P) - 1]; i[#-1,region][1] 255 j[2] [3],0,0,0,0,1,[1] rm[1,3] c[1] 0,255 _blend_softdodge : +-[1] 255.1 ^[2] -1 *[2] [0] *[2] -127.5 +-[1] 255 ++[0] 0.1 /[3,4] *[3] 127.5 +[3] 255 +[1] [0] >[1] 255 j[2] [3],0,0,0,0,1,[1] rm[1,3] c[1] 0,255 _blend_softlight : +/[0] 255 /[1] 255 +sqr. *[2] [1] *[1] [3] *[1] -2 *[2] 2 +[1-3] *[1] 255 c[1] 0,255 _blend_stamp : *[1] 2 +[1] [0] -[1] 255 c[1] 0,255 _blend_subtract : -[1] [0] *[1] -1 c[1] 0,255 _blend_value : to_color sh 0,2 rgb2hsv[2,3] sh[2] 0,1 j[1] [4] rm[4] hsv2rgb[2,3] rm[2,3] _blend_vividlight : *[1] 2 +blend burn -[1] 256 +blend[0,1] dodge >=[1] 0 j[2] [3],0,0,0,0,1,[1] rm[1,3] _blend_xor : xor[1] [0] #@cli blend_edges : smoothness[%]>=0 #@cli : Blend selected images togethers using 'edges' mode. #@cli : $ image.jpg testimage2d {w},{h} +blend_edges 0.8 blend_edges : check {$1>=0} e[^-1] "Blend image$? using 'edges' mode, with smoothness $1." if $!>1 to_rgb ri[^0] [0],0,0,0.5,0.5 foreach { +gradient_norm +. 1 b. $1 n. 1,10 sqr. s.. c *[-4--2] . a[-4--1] c } ri[^0] [0],0,0,0.5,0.5 + s. c /[-4--2] . rm. a[-3--1] c fi #@cli blend_fade : [fading_shape] #@cli : Blend selected images together using specified fading shape. #@cli : $ image.jpg testimage2d {w},{h} 100%,100%,1,1,'cos(y/10)' normalize[-1] 0,1 +blend_fade[0,1] [2] blend_fade : e[^-1] "Blend image$? together using fading pattern $1." r ${-max_whds},0 pass$1 0 r. [0],[0],[0],100%,1 max. 0 min. {$!-2} repeat $!-1 { +-. $> abs. -. 1 *. -1 max. 0 *[$>,-1] } rm. + _fade : ri.. ...,5 ri. ..,3 c. $1%,$2% n. 0,1 j... ..,0,0,0,0,1,. rm[-2,-1] #@cli blend_median #@cli : Blend selected images together using 'median' mode. #@cli : $ image.jpg testimage2d {w},{h} +mirror[0] y +blend_median blend_median : e[^-1] "Blend image$? using 'median' mode." if $!<2 return fi to_colormode 0 r ${-max_whd},100%,0,0,0.5,0.5,0.5 if $!==2 + / 2 else whds={w},{h},{d},{s} r 100%,100%,{d*s},1,-1 a c 100%,100%,100%,1,"med(I(#0))" k. r $whds,-1 fi #@cli blend_seamless : _is_mixed_mode={ 0 | 1 },_inner_fading[%]>=0,_outer_fading[%]>=0 #@cli : Blend selected images using a seamless blending mode (Poisson-based). #@cli : Default values: 'is_mixed=0', 'inner_fading=0' and 'outer_fading=100%'. blend_seamless : check "${2=0}>=0 && ${3=100%}>=0" skip ${1=0} s0="non-mixed" s1="mixed" e[^-1] "Blend image$? using seamless mode (Poisson-based), in "${s{$1!=0}}" mode with inner fading $2 and outer fading $3." to_a[^0] if ['$3']!='100%' # With outer fading. repeat $!-1 { l[0,1] { +blend_seamless $1,$2,100% channels.. 100% !=.. 0 distance.. 1 iM={-2,iM} ic:=ispercentage($3)?2*$3*$iM:1+$3 if $ic<=$iM c.. 0,{max(1,$ic)} n.. 0,1 else n.. 0,{max(0,2-$ic/$iM)} fi *.. -1 +.. 1 j[0] [2],0,0,0,0,1,[1] rm[1,2] } } else # Without outer fading. repeat $!-1 { l[0,1] { # Get background average color. +r[0] 1,1,1,100%,2 avg={^} rm. # Compute mixed gradients of background and top layer. split_opacity. !=. 0 *.. . erode. 3 g[0,1] xy,1,2 *[-3,-2] . # Modify mask if 'mixed' mode selected. if $1 +a[0,1] c +a[2,3] c norm[-2,-1] <[-2,-1] *[-2,-1] fi # Compute the desired gradient map. if $2 distance. 0 iM:=iM ic:=ispercentage($2)?2*$2*$iM:1+$2 if $ic<=$iM c. 0,{max(1,$ic)} n. 0,1 else n. 0,{max(0,2-$ic/$iM)} fi fi j[-5] ...,0,0,0,0,1,. j[-4] ..,0,0,0,0,1,. rm[-3--1] # Compute divergence (right-hand term of Poisson eq.) -> laplacian map. g[0] x,-1,2 g[1] y,-1,2 + # Inverse laplacian and renormalize ilaplacian 0 +fc. $avg +[-2,-1] c 0,255 } } fi #@cli fade_diamond : 0<=_start<=100,0<=_end<=100 #@cli : Create diamond fading from selected images. #@cli : Default values: 'start=80' and 'end=90'. #@cli : $ image.jpg testimage2d {w},{h} +fade_diamond 80,85 fade_diamond : skip ${1=70},${2=90} e[^-1] "Create ($1%,$2%) diamond-shaped fading from image$?." repeat int($!/2) { l[$>,{$>+1}] { (0,1,0;1,1,1;0,1,0) _fade $1,$2 } } #@cli fade_linear : _angle,0<=_start<=100,0<=_end<=100 #@cli : Create linear fading from selected images. #@cli : Default values: 'angle=45', 'start=30' and 'end=70'. #@cli : $ image.jpg testimage2d {w},{h} +fade_linear 45,48,52 fade_linear : skip ${1=45},${2=30},${3=70} e[^-1] "Create ($2%,$3%) linear fading from image$?, with angle $1 deg." repeat int($!/2) { l[$>,{$>+1}] { 64,64,1,1,"x*cos($1*pi/180) + y*sin($1*pi/180)" _fade $2,$3 } } #@cli fade_radial : 0<=_start<=100,0<=_end<=100 #@cli : Create radial fading from selected images. #@cli : Default values: 'start=30' and 'end=70'. #@cli : $ image.jpg testimage2d {w},{h} +fade_radial 30,70 fade_radial : skip ${1=30},${2=70} e[^-1] "Create ($1%,$2%) radial fading from image$?." repeat int($!/2) { l[$>,{$>+1}] { 100%,100% =. 1,50%,50% distance. 1 _fade $1,$2 } } #@cli fade_x : 0<=_start<=100,0<=_end<=100 #@cli : Create horizontal fading from selected images. #@cli : Default values: 'start=30' and 'end=70'. #@cli : $ image.jpg testimage2d {w},{h} +fade_x 30,70 fade_x : skip ${1=30},${2=70} e[^-1] "Create ($1%,$2%) horizontal fading from image$?." repeat int($!/2) { l[$>,{$>+1}] { (0,1) _fade $1,$2 } } #@cli fade_y : 0<=_start<=100,0<=_end<=100 #@cli : Create vertical fading from selected images. #@cli : Default values: 'start=30' and 'end=70'. #@cli : $ image.jpg testimage2d {w},{h} +fade_y 30,70 fade_y : skip ${1=30},${2=70} e[^-1] "Create ($1%,$2%) vertical fading from image$?." repeat int($!/2) { l[$>,{$>+1}] { (0;1) _fade $1,$2 } } #@cli fade_z : 0<=_start<=100,0<=_end<=100 #@cli : Create transversal fading from selected images. #@cli : Default values: 'start=30' and 'end=70'. fade_z : skip ${1=30},${2=70} e[^-1] "Create ($1%,$2%) transversal fading from image$?." repeat int($!/2) { l[$>,{$>+1}] { (0/1) _fade $1,$2 } } #@cli sub_alpha : [base_image],0<=_minimize_alpha<=1 #@cli : Compute the alpha-channel difference (opposite of alpha blending) between the selected images #@cli : and the specified base image. #@cli : The alpha difference A-B is defined as the image having 'minimal' opacity, such that alpha_blend(B,A-B) = A. #@cli : The 'min_alpha' argument is used to relax the alpha minimality constraint. When set to '1', \ # alpha is constrained to be minimal. When set to '0', alpha is maximal (i.e. '255'). #@cli : Default value: 'minimize_alpha=1'. #@cli : $ image.jpg testimage2d {w},{h} +sub_alpha[0] [1] display_rgba sub_alpha : check ${"is_image_arg $1"}" && inrange(${2=1},0,1)" e[^-1] "Compute minimal alpha-channel difference between image$? and base image $1, "\ "with alpha minimization factor $2." remove_opacity pass$1 foreach[^-1] { nm={n} pass. rv 100%,100%,1,{s+1},"begin(null = vector(#s,0); I_b = vector(#s#0)); I_a = I(#0); I_c = I(#1); I_c==I_a?null:( dI_ac = I_a - I_c; fill(I_b,k,I_c[k]>I_a[k]?lerp(I_c[k],255,$2):lerp(I_c[k],0,$2)); alpha_b = max(dI_ac/(I_a - I_b)); I_b = I_a - dI_ac/alpha_b; cut([ I_b,alpha_b*255 ],0,255) )" k. => $nm } rm. #--------------------------------------------- # #@cli :: Image Sequences and Videos # #--------------------------------------------- #@cli animate : filter_name,"param1_start,...,paramN_start","param1_end,...,paramN_end",nb_frames>=0,\ # _output_frames={ 0 | 1 },_output_filename : delay>0,_back and forth={ 0 | 1 } #@cli : Animate filter from starting parameters to ending parameters or animate selected images #@cli : in a display window. #@cli : Default value: 'delay=30'. #@cli : $ image.jpg animate flower,"0,3","20,8",9 animate : skip ${1=30},${2=0},${3=""},${4=10},${5=1},"${6=}" if "isnum($1)" e[0--3] "Animate image$?, with a delay of $1 ms"${"if $2 u \", in back-and-forth mode\" else u \"\" fi"}. if !$! return fi is_volumetric:=$!==1" && "d>1 speed,pause,direction,scale,frame=$1,-1,1,1,0 is_same_size={"res = 1; s = [ w#0,h#0 ]; for (k = 1, k=$nb_frames frame,direction:=$nb_frames-1,-1 fi else frame%=$nb_frames fi wait $speed # Increase window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} scale*=1.5 wait -1 fi # Decrease window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} scale/=1.5 wait -1 fi # Reset window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} scale=1 wait -1 fi if {*,o} speed:=min(500,max(10,$speed-10*{*,o})) wait -1 fi if {*,SPACE} if $pause>=0 direction=$pause pause=-1 else pause=$direction direction=0 fi wait -1 fi while {*}" && "!{*,Q}" && "!{*,ESC} w 0 else e[0--3] "Compute animated version of filter '$1', from parameters $2 to $3 with $4 frames." if !($5||narg("$6")) return fi ($2) ($3) y[-2,-1] x a[-2,-1] y r. 100%,$4,1,1,3 mv. 0 rprogress 0 repeat $!-1 { u=$> e " > Animate image ["$>"]" repeat $4 { +l[0,1] { -$1. {0,@{$>*{0,w}}-{($>+1)*{0,w}-1}} rm[0] if narg("$6") o ${filename\ "$6",$u,$>} fi if !$5 rm fi rprogress {100*($>+1)/$4} e "\r > Animate image ["$u"] : Frame "{$>+1}"/$4 " } } rm[1] } rm[0] fi #@cli apply_camera : _"command",_camera_index>=0,_skip_frames>=0,_output_filename #@cli : Apply specified command on live camera stream, and display it on display window [0]. #@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default). #@cli : Default values: 'command=""', 'camera_index=0' (default camera), 'skip_frames=0' and 'output_filename=""'. apply_camera : check_opencv $0 skip "${1=},${4=}" check "${2=0}>=0 && ${3=0}>=0" e[^-1] "Apply command '$1' on camera stream ""#$2, with $3 frames skip and output filename '$4'." m "_apply_camera_com : $1" is_ext "$4",avi is_outavi=${} is_ext "$4",mp4 is_outmp4=${} is_fs=0 l[] { i=0 do camera $2,1,$3 _apply_camera_com. if !$! 640,480,1,3 else k. fi if {*}" && "[w,h]!=[{*,d,e}] +rs. {*,d,e},1,1 else . fi w. -1,-1,"[G'MIC] Camera ""#$2 ("{0,w}x{0,h}")" k[0] if narg("$4") if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$4",25,mp4v,1 else o. ${filename\ "$4",$i} i+=1 fi fi if {*,S} o. gmic_camera.png fi is_ctrl:={*,CTRLLEFT}" || "{*,CTRLRIGHT} if !$is_fs" && "$is_ctrl" && "{*,-D} w[] {{*,w}*1.5},{{*,h}*1.5} fi # Increase window size if !$is_fs" && "$is_ctrl" && "{*,-C} w[] {{*,w}/1.5},{{*,h}/1.5} fi # Decrease window size if $is_ctrl" && "{*,-F} # Switch fullscreen mode if !$is_fs fs_wh={*,w,h} w[] {*,u,v},1,1 is_fs=1 else w[] $fs_wh,1,0 is_fs=0 fi fi if $is_ctrl" && "{*,-R} w[] {0,w},{0,h},1,0 is_fs=0 fi # Reset window size rm while {*}" && "!{*,ESC}" && "!{*,Q} camera $2,0 } #@cli apply_files : "filename_pattern",_"command",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\ # _output_filename #@cli : Apply a G'MIC command on specified input image files, in a streamed way. #@cli : If a display window is opened, rendered frames are displayed in it during processing. #@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image file #@cli : extension (saved as a sequence of images). #@cli : Default values: 'command=(undefined)', 'first_frame=0', 'last_frame=-1', 'frame_step=1' \ # and 'output_filename=(undefined)'. apply_files : check "isint(${3=0},0) && isint(${4=-1},-1) && ${5=1}>=1" skip "${2=},${6=}" e[^-1] "Apply command '$2' on input image files '$1', with first frame $3, last frame $4, frame step $5 and output filename '$6'.\n" files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} v + _apply_stream[] "${_file{$frame+1}}","$2",${3-5},"$6" #@cli apply_video : video_filename,_"command",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\ # _output_filename #@cli : Apply a G'MIC command on all frames of the specified input video file, in a streamed way. #@cli : If a display window is opened, rendered frames are displayed in it during processing. #@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image #@cli : file extension (saved as a sequence of images). #@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default). #@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1' and 'output_filename=(undefined)'. apply_video : check_opencv $0 check "isint(${3=0},0) && isint(${4=-1},-1) && ${5=1}>=1" skip "${2=},${6=}" e[^-1] "Apply command '$2' on input video file '$1', with first frame $3, last frame $4, frame step $5 and output filename '$6'.\n" _N= v + _apply_stream[] "\"$1\",$frame","$2",${3-5},"$6" _apply_stream : skip "${2=},${6=}" is_ext "$6",avi is_outavi=${} is_ext "$6",mp4 is_outmp4=${} frame=$3 i=0 go_on=1 do l[] { $1 onfail go_on=0 } if $go_on e "\r > Frame ""#"$frame$_N" " frame+=$5 l { $2 onfail error[0--5] "Command 'apply_stream': Specified command errored: "${} } if !$! continue fi if narg("$6") if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$6",25,mp4v,1 else o. ${filename\ "$6",$i} i+=1 fi fi if {*} title="[G'MIC] Frame ""#"$frame if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,0,$title else w. -1,-1,0,$title fi if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi # Reset window size. fi rm. fi while $go_on" && "($4==-1" || "$frame<=$4) if $is_outavi||$is_outmp4 o[] "$6",25,mp4v,0 fi #@cli average_files : "filename_pattern",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename #@cli : Average specified input image files, in a streamed way. #@cli : If a display window is opened, rendered frames are displayed in it during processing. #@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image #@cli : file extension (saved as a sequence of images). #@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1' and 'output_filename=(undefined)'. average_files : check "isint(${2=0},0) && isint(${3=-1},-1) && ${4=1}>=1" skip "${5=}" e[^-1] "Average input image files '$1', with first frame $2, last frame $3, frame step $4 and output filename '$5'.\n" files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} v + _average_stream[] "${_file{$frame+1}}",${2-4},"$5" #@cli average_video : video_filename,_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename #@cli : Average frames of specified input video file, in a streamed way. #@cli : If a display window is opened, rendered frames are displayed in it during processing. #@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image #@cli : file extension (saved as a sequence of images). #@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default). #@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1' and 'output_filename=(undefined)'. average_video : check_opencv $0 check "isint(${2=0},0) && isint(${3=-1},-1) && ${4=1}>=1" skip "${5=}" e[^-1] "Average frames of input video file '$1', with first frame $2, last frame $3, frame step $4 and output filename '$5'.\n" _N= v + _average_stream[] "\"$1\",$frame",${2-4},"$5" _average_stream : skip "${5=}" is_ext "$5",avi is_outavi=${} is_ext "$5",mp4 is_outmp4=${} frame=$2 i=0 go_on=1 N=0 imM=inf,-inf do l[] { $1 onfail go_on=0 } if $go_on e "\r > Frame ""#"$frame$_N" " imM:=v=[$imM];[min(im,v[0]),max(iM,v[1])] N+=1 if $!>1 r ${-max_whds} + fi if narg("$5") +/. $N c. $imM if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$5",25,mp4v,1 else o. ${filename\ "$5",$i} i+=1 fi rm. fi if {*} title="[G'MIC] Frame ""#"$frame +n 0,255 if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,0,$title else w. -1,-1,0,$title fi if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi # Reset window size. rm. fi frame+=$4 fi while $go_on" && "($3==-1" || "$frame<=$3) / $N c $imM if $is_outavi||$is_outmp4 o[] "$5",25,mp4v,0 fi #@cli fade_files : "filename_pattern",_nb_inner_frames>0,_first_frame>=0,_last_frame={ >=0 | -1=last },\ # _frame_step>=1,_output_filename #@cli : Generate a temporal fading from specified input image files, in a streamed way. #@cli : If a display window is opened, rendered frames are displayed in it during processing. #@cli : The output filename may have extension 'avi' or 'mp4' (saved as a video), or any other usual image #@cli : file extension (saved as a sequence of images). #@cli : Default values: 'nb_inner_frames=10', 'first_frame=0', 'last_frame=-1', 'frame_step=1' \ # and 'output_filename=(undefined)'. fade_files : check "isint(${2=10},1) && isint(${3=0},0) && isint(${4=-1},-1) && ${5=1}>=1" skip "${6=}" e[^-1] "Fade input image files '$1', with $2 inner frames, first frame $3, last frame $4, frame step $5 and output filename '$6'.\n" files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} v + _fade_stream[] "${_file{$frame+1}}",${2-5},"$6" #@cli fade_video : video_filename,_nb_inner_frames>0,_first_frame>=0,_last_frame={ >=0 | -1=last },\ # _frame_step>=1,_output_filename #@cli : Create a temporal fading sequence from specified input video file, in a streamed way. #@cli : If a display window is opened, rendered frames are displayed in it during processing. #@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default). #@cli : Default values: 'nb_inner_frames=10', 'first_frame=0', 'last_frame=-1', 'frame_step=1' \ # and 'output_filename=(undefined)'. fade_video : check_opencv $0 check "isint(${2=10},1) && isint(${3=0},0) && isint(${4=-1},-1) && ${5=1}>=1" skip "${6=}" e[^-1] "Fade frames of input video file '$1', with $2 inner frames, first frame $3, last frame $4, frame step $5 and output filename '$6'.\n" _N= v + _fade_stream[] "\"$1\",$frame",${2-5},"$6" _fade_stream : skip "${6=}" is_ext "$6",avi is_outavi=${} is_ext "$6",mp4 is_outmp4=${} frame=$3 i=0 go_on=1 l { $1 onfail go_on=0 } # Load first image. if !$go_on return fi w,h,s={w},{h},{s} if {*} w. ${fitscreen\ $w,$h},0,"[G'MIC]" fi pframe=$frame frame+=$5 do l[] { $1 onfail go_on=0 } if !$go_on break fi to_colormode. $s r. $w,$h repeat $2+2 { if $< title="[G'MIC] Frame ""#"$pframe" -> ""#"$frame$_N" ("{1+$>}/$2")" e "\r - "$title +j[0] [1],0,0,0,0,{$>/($2+1)} if narg("$6") if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$6",25,mp4v,1 else filename "$6",$i i+=1 o. ${} fi fi if {*} w. -1,-1,0,$title if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi # Reset window size. fi rm. fi } rm[0] pframe=$frame frame+=$5 while $go_on" && "($4==-1" || "$frame<=$4) # Output last frame. if narg("$6") if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o[] "$6",25,mp4v,0 else filename "$6",$i o. ${} fi fi rm #@cli files2video : "filename_pattern",_output_filename,_fps>0,_codec #@cli : Convert several files into a single video file. #@cli : Default values: 'output_filename=output.mp4', 'fps=25' and 'codec=mp4v'. files2video : check "isint(${3=25},1)" skip "${2=output.mp4}",${4=mp4v} files=${"files \"$1\""} arg2var _file,$files nb_files=${} ('$files') if w>128 z. 0,127 s_files={t}... else s_files=$files fi rm. e[^-1] "Convert image files '"$s_files"' into frames of output video '$2', with $3 fps and $4 codec.\n" repeat $nb_files { l[] { file=${_file{$>+1}} _file=${basename\ $file} e "\r - Image "{1+$>}/$nb_files" ["$_file"] -> [$2] " i $file o "$2",$3,$4,1 rm onfail e "\n - Error occurred on input file '"$file"'.\n" } } o $"$2",0,0,0 #@cli median_files : "filename_pattern",_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\ # _frame_rows[%]>=1,_is_fast_approximation={ 0 | 1 } #@cli : Compute the median frame of specified input image files, in a streamed way. #@cli : If a display window is opened, rendered frame is displayed in it during processing. #@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1', 'frame_rows=20%' \ # and 'is_fast_approximation=0'. median_files : check "isint(${2=0},0) && isint(${3=-1},-1) && ${4=1}>=1 && ${5=20%}>0 && isnum(${6=0})" s0="fast" s1="precise" e[^-1] "Compute median of input image files '$1', with first frame $2, last frame $3, frame step $4, frame rows $5, using "${s{!$6}}" algorithm." files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} l[] { ${_file{$frame+1}} => res f. 0 v + _median_stream "${_file{$frame+1}}",${2-6} v - } #@cli median_video : video_filename,_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,\ # _frame_rows[%]>=1,_is_fast_approximation={ 0 | 1 } #@cli : Compute the median of all frames of an input video file, in a streamed way. #@cli : If a display window is opened, rendered frame is displayed in it during processing. #@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default). #@cli : Default values: 'first_frame=0', 'last_frame=-1', 'frame_step=1', 'frame_rows=100%' \ # and 'is_fast_approximation=1'. median_video : check_opencv $0 check "isint(${2=0},0) && isint(${3=-1},-1) && ${4=1}>=1 && ${5=100%}>0 && isnum(${6=1})" s0="fast" s1="precise" e[^-1] "Compute median frame of input video file '$1', with first frame $2, last frame $3, frame step $4, frame rows $5, using "${s{!$6}}" algorithm." _N= l[] { "$1",0 => res f. 0 v + _median_stream "\"$1\",$frame",${2-6} v - } _median_stream : # Retrieve min/max values of all frames when fast method is used. if $6 e "- Retrieve min/max values of all frames.\n" frame=$2 go_on=1 imM=inf,-inf do l[] { $1 onfail go_on=0 } if $go_on e "\r > Frame ""#"$frame$_N" " imM:=v=[$imM];[min(im,v[0]),max(iM,v[1])] if {*} title="[G'MIC] Frame ""#"$frame if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,1,$title else w. -1,-1,1,$title fi fi rm. frame+=$4 fi while $go_on" && "($3==-1" || "$frame<=$3) _N=/{$frame-$4} fact:=v=[$imM];dv=v[1]-v[0];dv<=0?0:255/dv fi # Start median computation. h1:=h-1 drows:=round(ispercentage($5)?$5*h:$5) nb_iter:=round(h/$drows,1,1) repeat $nb_iter { row0:=$drows*$> row1={0,min(h,$row0+$drows-1)} e "- Iteration \#"{$>+1}/$nb_iter": Load rows "$row0-$row1/$h1".\n" frame=$2 go_on=1 if $6 # Fast method : compute median frame using streamed histogram computation. N=0 {w},$drows,256,{s} => hist do l[] { $1 => img onfail go_on=0 } if $img e "\r > Frame ""#"$frame$_N" " if {*}" && "!$> title="[G'MIC] Frame ""#"$frame if !narg($wh) wh=${fitscreen[]\ {img,[w,h]}} w[img] $wh,1,$title else w[img] -1,-1,1,$title fi fi rows[img] $row0,$row1 f[img] ":++i(#-2,x,y,round(i*"$fact"),c)" rm[img] frame+=$4 N+=1 fi while $go_on" && "($3==-1" || "$frame<=$3) cumulate[hist] z N2:=int($N/2) [hist],[hist],1,[hist] if $N%2 # Odd number of frames. f. ":go_on = 1; for (z = 0, i(#"$hist",x,y,z,c)<"$N2" && z<256, ++z); z" else # Even number of frames. f. ":begin(N2p = "$N2"; N2n = N2p + 1); go_on = 1; for (zp = 0, i(#"$hist",x,y,zp,c) img onfail go_on=0 } if $go_on e "\r > Frame ""#"$frame$_N" " if {*}" && "!$> title="[G'MIC] Frame ""#"$frame if !narg($wh) wh=${fitscreen[]\ {[w,h]}} w. $wh,1,$title else w. -1,-1,1,$title fi fi rows. $row0,$row1 frame+=$4 fi while $go_on" && "($3==-1" || "$frame<=$3) e "\r > Compute median blending of "$!" frames." __median_stream } fi _N=/{$frame-$4} j[res] .,0,$row0 if {*} w[res] -1,-1,1,"[G'MIC] Iteration ""#"$> fi rm. } e "- Done!" # Median blending optimized to deal with a lot of input frames. __median_stream : if $!<2 return elif $!==2 + / 2 else f. ": stack = vector"{0,2*$!}"(); stacksize = 0; push(elt0,elt1) = (stack[stacksize++] = elt0; stack[stacksize++] = elt1); pop() = (_s1 = stack[--stacksize]; _s0 = stack[--stacksize]; [_s0,_s1]); push(0,"$!" - 1); while (stacksize>0, range = pop(); lo = range[0]; hi = range[1]; pivot = i(#int((lo + hi)/2)); while (lo<=hi, while (i(#lo)=1,_smoothness>=0,_precision>=0 #@cli : Create morphing sequence between selected images. #@cli : Default values: 'smoothness=0.1' and 'precision=4'. #@cli : $ image.jpg +rotate 20,1,1,50%,50% morph 9 morph : check "$1>=0.5 && ${2=0.1}>=0 && ${3=4}>=0" nbf:=round($1) e[^-1] "Create morphing sequence between image$?, with "$nbf" inner frames, smoothness $2 and precision $3." e "" nchan=${-max_s} if $nchan<=4 to_colormode $nchan else channels 0,{$nchan-1} fi ri[^0] [0],3 repeat $!-1 { nm={$>,n} l[$<,{$<+1}] { e "\r > Morph image ["$>"] to image ["{$>+1}"]. " +equalize[0,1] n[-2,-1] 0,255 +displacement[3] [2],$2,$3 +displacement[2] [3],$2,$3 rm[-4,-3] repeat $nbf+2 { if $>&&$< t:=$>/($nbf+1) omt:=1-$t +*[2] $t +warp[0] .,1,1,1 rm.. *. $omt +*[3] {1-$t} +warp[1] .,1,1,1 rm.. *. $t +[-2,-1] fi } rm[2,3] mv[2--1] 1 =>[^] $nm } } #@cli morph_files : "filename_pattern",_nb_inner_frames>0,_smoothness>=0,_precision>=0,\ # _first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename #@cli : Generate a temporal morphing from specified input image files, in a streamed way. #@cli : If a display window is opened, rendered frames are displayed in it during processing. #@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image #@cli : file extension (saved as a sequence of images). #@cli : Default values: 'nb_inner_frames=10', 'smoothness=0.1', 'precision=4', 'first_frame=0', 'last_frame=-1', \ # 'frame_step=1' and 'output_filename=(undefined)'. morph_files : check "isint(${2=10},1) && ${3=0.1}>=0 && ${4=4}>=0 && isint(${5=0},0) && isint(${6=-1},-1) && ${7=1}>=1" skip "${8=}" e[^-1] "Morph input image files '$1', with $2 inner frames, smoothness $3, precision $4, first frame $5, last frame $6, frame step $7 and output filename '$8'.\n" files 3,"$1" _N=/{narg(${})-1} arg2var _file,${} _morph_stream[] "${_file{$frame+1}}",${2-7},"$8" #@cli morph_rbf : nb_inner_frames>=1,xs0[%],ys0[%],xt0[%],yt0[%],...,xsN[%],ysN[%],xtN[%],ytN[%] #@cli : Create morphing sequence between selected images, using RBF-based interpolation. #@cli : Each argument (xsk,ysk)-(xtk,ytk) corresponds to the coordinates of a keypoint #@cli : respectively on the source and target images. The set of all keypoints define the overall image deformation. morph_rbf : check "$1>=0.5" nbf:=round($1) e[^-1] "Create morphing sequence between image$? using RBF interpolation, "\ "with "$nbf" inner frames and keypoints ($*)." $=arg N:=($#-1)/4 if int($N)!=$N error[0--2] "Command 'morph_rbf': Wrong number of arguments ($#)." fi ri[^0] [0],3 repeat $!-1 { nm={$>,n} l[$<,{$<+1}] { e "\r > Morph image "$>" to image "{$>+1}". " # Retrieve absolute keypoints coordinates. 4,$N repeat wh { a=${arg{2+$>}} isp:=ispercentage($a) eval i[$>]=$isp?($>%2?w#0:h#0)*$a:$a } permute. yzcx # Generate forward and backward warping fields. +f. "[i0,i1,i2-i0,i3-i1]" f.. "[i2,i3,i0-i2,i1-i3]" rbf[-2,-1] {0,[w,h]} # Compute morphing sequence. repeat $nbf+2 { if $>&&$< [0],[0],1,[0]," const interpolation = 1; const boundary = 3; const t = "$>"/("$nbf"+1); const omt = 1 - t; begin(print([t,omt])); ub = i(#2,x,y,0,0); vb = i(#2,x,y,0,1); uf = i(#3,x,y,0,0); vf = i(#3,x,y,0,1); omt*I(#0,x - t*uf,y - t*vf) + t*I(#1,x - omt*ub,y - omt*vb)" fi } rm[2,3] mv[2--1] 1 =>[^] $nm } } #@cli morph_video : video_filename,_nb_inner_frames>0,_smoothness>=0,_precision>=0,\ # _first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1,_output_filename #@cli : Generate a temporal morphing from specified input video file, in a streamed way. #@cli : If a display window is opened, rendered frames are displayed in it during processing. #@cli : The output filename may have extension '.avi' or '.mp4' (saved as a video), or any other usual image #@cli : file extension (saved as a sequence of images). #@cli : This command requires features from the OpenCV library (not enabled in G'MIC by default). #@cli : Default values: 'nb_inner_frames=10', 'smoothness=0.1', 'precision=4', 'first_frame=0', 'last_frame=-1', \ # 'frame_step=1' and 'output_filename=(undefined)'. morph_video : check_opencv $0 check "isint(${2=10},1) && ${3=0.1}>=0 && ${4=4}>=0 && isint(${5=0},0) && isint(${6=-1},-1) && ${7=1}>=1" skip "${8=}" e[^-1] "Morph frames of input video file '$1', with $2 fading frames, smoothness $3, precision $4, first frame $5, last frame $6, frame step $7 and output filename '$8'.\n" _N= v + _morph_stream[] "\"$1\",$frame",${2-7},"$8" _morph_stream : skip "${8=}" is_ext "$8",avi is_outavi=${} is_ext "$8",mp4 is_outmp4=${} frame=$5 i=0 go_on=1 l { $1 onfail go_on=0 } # Load first image. if !$go_on return fi w,h,s={w},{h},{s} if {*} w. ${fitscreen\ $w,$h},0,"[G'MIC]" fi pframe=$frame frame+=$7 do l[] { $1 onfail go_on=0 } if !$go_on break fi to_colormode. $s r. $w,$h cutvals:=min(im#0,im#1),max(iM#0,iM#1) e "\r - Frame ""#"$pframe" -> ""#"$frame" " +equalize[0,1] n[-2,-1] 0,255 +displacement[3] [2],$3,$4 +displacement[2] [3],$3,$4 rm[-4,-3] repeat $2+2 { if $< title="Frame ""#"$pframe" -> ""#"$frame" ("$>/$2") " e "\r - "$title t:=$>/($2+1) omt:=1-$t +*[2] $t +warp[0] .,1,1,1 rm.. *. $omt +*[3] {1-$t} +warp[1] .,1,1,1 rm.. *. $t +[-2,-1] c. $cutvals if narg("$8") if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o. "$8",25,mp4v,1 else filename "$8",$i i+=1 o. ${} fi fi if {*} w. -1,-1,0,$title if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 fi # Increase window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 fi # Decrease window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} wait -1 fi # Reset window size. fi rm. fi } rm[0,-2,-1] pframe=$frame frame+=$7 while $go_on" && "($6==-1" || "$frame<=$6) # Output last frame. if narg("$8") if $is_outavi||$is_outmp4 z. 0,{w-(w%8)-1} o[] "$8",25,mp4v,0 else filename "$8",$i o. ${} fi fi rm #@cli register_nonrigid : [destination],_smoothness>=0,_precision>0,_nb_scale>=0 #@cli : Register selected source images with specified destination image, using non-rigid warp. #@cli : Default values: 'smoothness=0.2', 'precision=6' and 'nb_scale=0(auto)'. #@cli : $ image.jpg +rotate 20,1,1,50%,50% +register_nonrigid[0] [1] register_nonrigid : check ${is_image_arg\ $1}" && ${2=0.2}>=0 && ${3=5}>0 && ${4=0}>=0" e[^-1] "Register source image$? with destination image $1, using non-rigid warp with smoothness $2, precision $3 and $4 scale(s)." pass$1 0 equalize. n. 0,255 repeat $!-1 { +equalize[$>] n. 0,255 +displacement.. .,$2,$3,$4 rm.. warp[$>] .,1,1,1 rm. } rm. #@cli register_rigid : [destination],_smoothness>=0,\ # _boundary_conditions={ 0:dirichlet | 1:neumann | 2:periodic | 3:mirror } #@cli : Register selected source images with specified destination image, using rigid warp (shift). #@cli : Default values: 'smoothness=0.1%' and 'boundary_conditions=0'. #@cli : $ image.jpg +shift 30,20 +register_rigid[0] [1] register_rigid : check ${is_image_arg\ $1}" && ${2=0.1%}>=0 && isint(${3=0},0,3)" e[^-1] "Register source image$? with destination image $1, using rigid warp with smoothness $2." m "_register_rigid : b $2 equalize 256 n 0,1" pass$1 0 W,H,D,S={w},{h},{d},{s} f:=max(w,h)/1024 if $f<=1 # Small image: one-step registration _register_rigid. repeat $!-1 { if {$>,w!=$W||h!=$H||d!=$D} error[0--4] "Images have incompatible sizes ("{$>,[w,h,d,s]}") and ("{[$W,$H,$D,$S]}")." fi +_register_rigid[$>] phase_correlation. .. shift[$>] {^},0,$3 rm. } rm. else # Large image: two-steps registration +r. {[min(w,1024),min(h,1024),min(d,1024)]},100%,0,0,0.5,0.5 rs.. 1024,1024,2 _register_rigid[-2,-1] repeat $!-2 { if {$>,w!=$W||h!=$H||d!=$D} error[0--4] "Images have incompatible sizes ("{$>,[w,h,d,s]}") and ("{[$W,$H,$D,$S]}")." fi # Low scale. +rs[$>] 1024,1024,2 _register_rigid. phase_correlation. ... s:=$f*crop() rm. # High scale. +shift[$>] $s,0,$3 r. {[min(w,1024),min(h,1024),min(d,1024)]},100%,0,0,0.5,0.5 _register_rigid. phase_correlation. .. s:=[$s]+crop() rm. shift[$>] $s,0,$3 } rm[-2,-1] fi um _register_rigid #@cli transition : [transition_shape],nb_added_frames>=0,100>=shading>=0,_single_frame_only={ -1=disabled | >=0 } #@cli : Generate a transition sequence between selected images. #@cli : Default values: 'shading=0' and 'single_frame_only=-1'. #@cli : $ image.jpg +mirror c 100%,100% plasma[-1] 1,1,6 transition[0,1] [2],5 transition : check ${is_image_arg\ $1}" && $2>=0 && ${3=0}>=0 && $3<=100" skip ${4=-1} frame:=round($4) s0=" and shading $3" s1=", shading $3 and single-frame-only "$frame e[^-1] "Create transition sequence between image$? with $2 added frames, transition shape $1"${s{$4>0}}"." if $!<2" || "!$2 return fi to_colormode 0 r ${-max_whd},100%,0,0,0.5,0.5 pass$1 0 norm. r. [0],[0],[0],1,3 n. 0,1 mv. 0 repeat $!-2 { l[0,{$<+1},{$<+2}] { nm0={1,n} if $3 repeat $2 { if $4<0" || "$>==$frame val0:=($>+0.5)/$2-$3% val1:=($>+0.5)/$2+$3% +f[0] "(i - $val0)/($val1 - $val0)" c. 0,1 +j[2] [1],0,0,0,0,1,. rm.. => $nm0\ ""#{1+$>} fi } else repeat $2 { if $4<0" || "$>==$frame +>=[0] {($>+0.5)/$2} +j[2] [1],0,0,0,0,1,. rm.. => $nm0\ ""#{1+$>} fi } fi mv[2] $! } } rm[0] #@cli transition3d : _nb_frames>=2,_nb_xtiles>0,_nb_ytiles>0,_axis_x,_axis_y,_axis_z,_is_antialias={ 0 | 1 } #@cli : Create 3D transition sequence between selected consecutive images. #@cli : 'axis_x', 'axis_y' and 'axis_z' can be set as mathematical expressions, depending on 'x' and 'y'. #@cli : Default values: 'nb_frames=10', 'nb_xtiles=nb_ytiles=3', 'axis_x=1', 'axis_y=1', 'axis_z=0' \ # and 'is_antialias=1'. #@cli : $ image.jpg +blur 5 transition3d 9 display_rgba transition3d : check "isint(${1=10},2) && isint(${2=3},1) && isint(${3=$2},1)" skip ${4=1},${5=1},${6=0},${7=1} e[^-1] "Create 3D transition sequence between image$?, with $1 frames, $2x$3 tiles and rotation axis ($4,$5,$6).\n" if $!<2 return fi slices 0 to_rgb r ${-max_whds},3 off=0 repeat $!-1 { l[{$>+$off},{$>+$off+1}] { e "\r > Generate transition from image "$>" to image "{$>+1}". " # Create 3D rotation vectors. $2,$3,1,1,'$4' $2,$3,1,1,'$5' $2,$3,1,1,'$6' a[-3--1] z permute. zxyc r. 3,{$2*$3},1,1,-1 repeat h { rot$>={@0-2} shift. 0,-1,0,0 } rm. # Create 3D tiles. +split_tiles[-2,-1] $2,$3 mv[0,1] $! N:=$2*$3 i=0 y=0 repeat $3 { x=0 repeat $2 { lw,lh={$i,[w,h]} imageplane3d[$i] imageplane3d[$N] r3d[$N] ${rot$i},180 c3d[$i,$N] +3d[$i,$N] x$i=$x y$i=$y x+=$lw i+=1 } y+=$lh } # Generate intermediate animation frames. repeat $1-2 { repeat $N { r3d[$>] ${rot$>},{180/(1-$1)} ++3d[$>] ${x$>},${y$>},0 } +3d[-$N--1] c3d. if $7 # Antialiased rendering. i... {-2,2*[w,h]},1,3,-1 *3d. 2 j3d... .,50%,50%,0,1,2,0,0 rm. to_rgba.. replace_color.. 0,0,-1,-1,-1,255,0,0,0,0 downsize_aliased.. 50 else # Standard rendering. i... {-2,w},{-2,h},1,3,-1 j3d... .,50%,50%,0,1,2,0,0 rm. to_rgba.. replace_color.. 0,0,-1,-1,-1,255,0,0,0,0 fi } rm[0-{$N-1}] =>[1--2] {0,n} off+=$1-2 } } #@cli video2files : input_filename,_output_filename,_first_frame>=0,_last_frame={ >=0 | -1=last },_frame_step>=1 #@cli : Split specified input video file into image files, one for each frame. #@cli : First and last frames as well as step between frames can be specified. #@cli : Default values: 'output_filename=frame.png', 'first_frame=0', 'last_frame=-1' and 'frame_step=1'. video2files : check "isint(${3=0},0) && isint(${4=-1},-1) && isint(${5=1},1)" skip ${2="frame.png"} e[^-1] "Split input video file '$1' into image frames '$2', with first frame $3, last frame $4, and frame step $5.\n" frame=$3 stopflag=0 do l[] { i "$1",$frame if $! o ${"filename \"$2\","$frame} rm e "\r > Frame ""#"$frame frame+=$5 else stopflag=1 fi onfail stopflag=1 } while !$stopflag" && "($frame<=$4" || "$4==-1) #------------------------------ # #@cli :: Convenience Functions # #------------------------------ #@cli add_copymark #@cli : Add copymark suffix in names of selected images. add_copymark : foreach { nm={n} 0 => $nm . =>[0] {n} k[0] } #@cli alert : _title,_message,_label_button1,_label_button2,... #@cli : Display an alert box and wait for user's choice. #@cli : If a single image is in the selection, it is used as an icon for the alert box. #@cli : Default values: 'title=[G'MIC Alert]' and 'message=This is an alert box.'. alert : skip "${1=[G"{`39`}"MIC Alert]},${2=This is an alert box.},${3=OK}" if $!==1 e[0--3] "Display alert box, with image$?, title '$1', message '$2' and buttons '${3--1}'." else e[0--3] "Display alert box, with title '$1', message '$2' and buttons '${3--1}'." fi if $!==1 logo= else logo=[] fi +l$logo { # Manage alert icon. if $!==1 to_rgb else # No logo provided, generate default logo (alert). 64,64 polygon 3,50%,10%,10%,90%,90%,90%,1,1 b 3 >= 50% +erode. 5 -. .. ==. 0 polygon. 4,47%,43%,53%,43%,53%,66%,47%,66%,1,0 circle. 50%,76%,2,1,0 +*[0] 255 . 100%,100% a[-3--1] c -. '3*(y-h/2)' c. 0,255 *. .. rm.. *[0] 255 rv a c drop_shadow 3,3,1 i[0] 100%,100%,1,3,200 blend alpha fi channels -1,2 # Create buttons graphics. $=arg repeat $#-2 { label=${arg{$>+3}} 0 t. $label,0,0,${"font \"Times New Roman\",18"},1,-200 } r[^0] {min(128,max(64,${max_w[^0]}+12))},{min(48,max(24,${max_h[^0]}+12))},1,1,0,0,0.5,0.5 +[^0] 200 to_rgb[^0] mv[0] $! [0],[0],1,1,'(y-h/2)' *. -2 c. -30,30 +[0--3] . rm. c[^-1] 0,255 # Add shading to buttons. foreach[^-1] { # Create selected buttons. +rectangle 0,0,100%,100%,1,0xFFFFFFFF,0 rectangle. 1,1,{w-2},{h-2},1,0xFFFFFFFF,0 line. 2,{h-3},{w-3},{h-3},1,150 line. {w-3},{h-3},{w-3},2,1,150 line. 1,1,{w-3},1,1,255 line. 1,1,1,{h-3},1,255 rectangle. 4,4,{w-5},{h-5},1,0xAAAAAAAA,0 # Create clicked buttons. +shift.. 1,1,0,0,2 rectangle. 0,0,100%,100%,1,0xFFFFFFFF,0 rectangle. 1,1,{w-2},{h-2},1,0xFFFFFFFF,150 rectangle. 4,4,{w-5},{h-5},1,0xAAAAAAAA,0 # Create default aspect. rectangle... 0,0,100%,100%,1,0xFFFFFFFF,0 line... 1,{h-2},{w-2},{h-2},1,150 line... {w-2},{h-2},{w-2},1,1,150 line... 0,0,{w-2},0,1,255 line... 0,0,0,{h-2},1,255 # Create coordinates image. i[0] 100%,100% =[0] 1,0,0 a c } # Render alert box graphics. +l { channels 0,3 sh 1,100% -[50%--1] 200 rm[50%--1] frame xy,8,0 if $!<6 a[^-1] x else append_tiles[^-1] , fi 0 t. "$2",0,0,${"font \"Times New Roman\",18"},1,0,-200,-200,-200 r. {w+16},{h+8},1,4,0 a[-2,-1] x,0.5 rv a y,0.5 sh 1,100% +. 200 rm. rectangle 0,0,100%,100%,1,0xFFFFFFFF,0 line 0,0,{w-2},0,1,0,255,255,255 line 0,0,0,{h-2},1,0,255,255,255 } rm.. +channels. 0 # Retrieve (x,y) coordinates of the buttons and fill active area. (0,{w-1}) (0;{-2,h-1}) ri[-2,-1] ...,3 a[-2,-1] c round. rv[-2,-1] *[-2,-1] discard. 0 y. r. {h/2},2,1,1,-1 channels.. 1,3 rv[-2,-1] 100%,100% repeat w#-3 { x0={-3,i($>,0)} y0={-3,i($>,1)} rectangle. $x0,$y0,{$x0+{0,w}-1},{$y0+{0,h}-1},1,{1+$>} } a[-2,-1] c # Enter event loop. repeat 9 { if !{*$>} disp=$> break fi } # Find available display window. if !narg($disp) error[0--4] "Command '$0': Cannot open display window for alert box." fi selected:=$#==3?0:-1 clicked=-1 do # Render current view. +channels. 0,2 if $clicked>=0 x0={-3,i($clicked,0)} y0={-3,i($clicked,1)} sh[$clicked] 7,9 j.. .,$x0,$y0 rm. elif $selected>=0 x0={-3,i($selected,0)} y0={-3,i($selected,1)} sh[$selected] 4,6 j.. .,$x0,$y0 rm. fi w$disp. 100%,100%,0,"$1" rm. wait # Handle user interactions. xm,ym={*$disp,x,y} bm:={*$disp,b}&1 val:=i($xm,$ym,0,3) if $bm" && "$val clicked:=$val-1 elif $bm" && "!$val" && "$clicked>=0 selected=$clicked clicked=-1 elif !$bm" && "$clicked>=0" && "$clicked==$val-1 break fi if {*$disp,ARROWRIGHT} selected:=($selected+1)%{-2,w} wait -1 elif {*$disp,ARROWLEFT} selected:=($selected-1)%{-2,w}+($selected==-1) wait -1 elif $selected>=0" && "{*$disp,ENTER} clicked=$selected break fi while {*$disp}" && "!{*$disp,ESC} # Return result (index of clicked button or '-1'). w$disp 0 rm u $clicked } #@cli arg : n>=1,_arg1,...,_argN #@cli : Return the n-th argument of the specified argument list. arg : check "isint($1,1)" $=arg u ${arg{1+($1)}} #@cli arg0 : n>=0,_arg0,...,_argN #@cli : Return the n-th argument of the specified argument list (where 'n' starts from '0'). arg0 : check "isint($1,0)" $=arg u ${arg{2+($1)}} #@cli arg2img : argument_1,...,argument_N #@cli : Split specified list of arguments and return each as a new image (as a null-terminated string). arg2img : $=arg repeat $# { arg=${arg{1+$>}} ({'$arg'},0) => $arg } #@cli arg2var : variable_name,argument_1,...,argument_N #@cli : For each i in [1...N], set 'variable_name$i=argument_i'. #@cli : The variable name should be global to make this command useful (i.e. starts by an underscore). arg2var : $=arg u {$#-1} repeat ${} { $1{1+$>}=${arg{2+$>}} } #@cli autocrop_coords : value1,value2,... | auto #@cli : Return coordinates (x0,y0,z0,x1,y1,z1) of the autocrop that could be performed on the latest #@cli : of the selected images. #@cli : Default value: 'auto' autocrop_coords : skip ${1=auto} is_auto:=['"$1"']=='auto' w,h,d,value={w},{h},{d},{i(w-1,h-1,d-1)} +=. {1+$value},100%,100%,100% _autocrop$is_auto. ${1--1} =. $value,100%,100%,100% x0,y0,z0:=$w-w,$h-h,$d-d _autocrop$is_auto. ${1--1} x1,y1,z1:=$x0+w-1,$y0+h-1,$z0+d-1 rm. u $x0,$y0,$z0,$x1,$y1,$z1 _autocrop0 : autocrop $* _autocrop1 : skip $* autocrop #@cli average_vectors #@cli : Return the vector-valued average of the latest of the selected images. average_vectors : if !w u "" return fi repeat s-1 { sh. $> res.={ia}, rm. } sh. 100% u $res{ia} rm. #@cli base642img : "base64_string" #@cli : Decode given base64-encoded string as a newly inserted image at the end of the list. #@cli : The argument string must have been generated using command 'img2base64'. +base642img : base642uint8 "$1" unserialize. #@cli base642uint8 : "base64_string" #@cli : Decode given base64-encoded string as a newly inserted 1-column image at the end of the list. #@cli : The argument string must have been generated using command 'uint82base64'. +base642uint8 : 0 eval " ref(vector256(),hash); for (k = _'A', k<=_'Z', ++k, hash[k] = k - _'A'); for (k = _'a', k<=_'z', ++k, hash[k] = k - _'a' + 26); for (k = _'0', k<=_'9', ++k, hash[k] = k - _'0' + 52); hash[_'+'] = hash[_'-'] = 62; hash[_'/'] = hash[_'_'] = 63; s = ['$1']; const ss = size(s); ss>=2?( resize(#-1,1,ss*3/4 - (s[ss-1]==_'=') - (s[ss-2]==_'='),1,1); od = 0; for (os = 0, os>4); i[#-1,od++] = ((c2&15)<<4) | (c3>>2); i[#-1,od++] = ((c3&3)<<6) | c4; ) )" => "[unnamed]" #@cli basename : file_path,_variable_name_for_folder #@cli : Return the basename of a file path, and opt. its folder location. #@cli : When specified 'variable_name_for_folder' must starts by an underscore #@cli : (global variable accessible from calling function). basename : skip ${2=unused} l[] { ({"'$1'"}) replace 92,47 s +,47 if i==47 a y $2={t} u "" elif $!==1 u {t} $2="" else a[^-1] y u {t} $2={-2,t} fi rm } #@cli bin : binary_int1,... #@cli : Print specified binary integers into their octal, decimal, hexadecimal and string representations. bin : dec=${bin2dec\ ${^0}} e[^-1] "Convert binary integer"${arg0\ ($#>1),"",s}" '${^0}' to octal '"${dec2oct\ $dec}"', decimal '"$dec"', hexadecimal '"${dec2hex\ $dec}"' and string '"${dec2str\ $dec}"'." #@cli bin2dec : binary_int1,... #@cli : Convert specified binary integers into their decimal representations. bin2dec : l[] { ('$*') s. +,{','} $!,1,1,1,"f = i[#x,0]; f!=_','?(resize(#x,1,h#x+2,1,1,0,0,0,1); f==_'-'?copy(i[#x,0],'-0b'):copy(i[#x,0],'0b'))" rm. a y u {[{t}]} rm } #@cli cat : filename,_display_line_numbers={ 0 | 1 },_line_selection, #@cli : Print specified line selection of given filename on stdout. #@cli : Default values: 'display_line_numbers=1' and 'line_selection=^'. cat : skip "${2=},${3=}" if "isbool($2)" dln=$2 if "['$3']!=0" ls=${3--1} else ls=^ fi else dln=0 if "['$2']!=0" ls=${2--1} else ls=^ fi fi it "$1" 1 eval.. ">i==10?da_push(0,-1):i!=13?da_push(i); end(da_freeze())" s. -,-1 0 rv[0,-1] rm. m "_$0 : u $""[]" sel=${"-_$0["$ls"]"} um _$0 k[$ls] e "" if $dln ($sel) foreach[^-1] { pass. 1 +e "#"{@$>}:" "{/{0,t}} rm. } else foreach { +e {/{t}} } fi rm # Command to check what lines of G'MIC sources are larger than 120 columns. _check120 : use_vt100 it[] "$1" s +,10 if !$! return fi 1,$!,1,2,">begin(line = 1); is_lines=i[#y,0]==10; line+=is_lines?h#y:0; [is_lines || h#y<=120?-1:y,line]" lines={{@-1}-1} f. "I = I; I[0]<0?(I[1]=-1); I" discard. -1 if w r. 1,{h/2},1,2,-1 repeat h { l,L:=I[$>] e $_vt100_c" - [Line "$_vt100_b$L$_vt100_n$_vt100_c", "\ $_vt100_b{$l,h}$_vt100_n$_vt100_c" chars]: "$_vt100_n{$l,t} } fi rm _total_lines+=$lines e " - Scanned : "${lines}" lines" check120 : _total_lines=0 files 0,*.h c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi files 0,*.cpp c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi files 0,*.c c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi files 0,*.gmic c={`narg($files)?',':0`} if narg(${}) files=$files$c${} fi repeat narg($files) { arg0 $>,$files file=${} e " * File '"$file"'." v + _check120 $file v - } e " - Total scanned : "${_total_lines}" lines" #@cli color2name : R,G,B #@cli : Return the name (as a string, in English) that most matches the specified color. color2name : _color2name ($1^$2^$3) r. {-2,w},1,1,3 -[-2,-1] norm. arg0 {xm},${"u "{n}} ('${}') replace. {'~'},{'" "'} u {t} rm[-2,-1] _color2name : if ['$__color2name']==0 base642img "\ MiB1aW50OCBsaXR0bGVfZW5kaWFuCjg4MSAxIDEgMyAjMjU4MQp4nCVWeTjViRr+cu/MnZvubSoNDVmqqVSKc6xFWUOLJUSJaDmYQ459zfll38JJu\ Q7GNvYlxlIiu2ZUv4Mw5OBUllOyVs5kTvjmzHO/f7/leb/3eZ/3+6A6tMZ7abRrMedD+TFk63BorEjhF2D2nZ6pwTVYistbXkdhaOTjHMHhLNRJew\ Y61sdasVClggZyAEDNpzlsjbfQ5gVNPn+JnKoQJHh55Ex8M+MyC+aLTv/cvog41QVpw1h7fp7xtKKGBwc8dn7+oM4n8N4ErywKQ/rTJzlT2UzPRyu\ JDdzK52zElbgv1/Aw3kIcyxYA2KHgugNYNcD3zSbWCTbIVk6YSdZONDkiCXdtuMJHgAinwrbvap93kz0FizYd0s5LZGuyhXtdOXke0tqJet60uTu6\ 1vRMLOTjrqIoWIvOHlz7LSB2BtunYsamYMWpHAaRH8VeRS64lBAtIAN/x5biGwdmTnDxVdkymCKqeanM15zZxpk8Ddsa22qXa/An+5VC1eVmxTNH3\ 3Uuxb6lmw/txdLeej6JXaGI5NofiNaVS6mvu4UDKetPEOVjAqqx+34rUHYIeTx3lwb3dBqS+MezPswp+9j25aOT+P8+raFv7PtOovo1a9FYucJxxG\ ekSPjxjA8NJqo+NaZqh4DJbysr6wMbs6bCbicknK0YmS7EOcYOEQ2T6oLjgvQmTMPb8cpsqSfdqDJU+YcQYcdJrfVh74IZ0qB3LcilLZKga9dnqJU\ 8xP+HULDC/TihhKcgUgZcWzLnhcK5QED+BwseIWclKnjxkOx8IwOUkc8Mcn0u4RVsPdy5tuEADuPLz6nTSeMXHKJbsFqADHhFXkMnIic3zWw9K8Pg\ XVES7vkyyaodCsfJ2zRc+Ng1P78uGji19lvbnQIIgA0bDCG+NAL/zHaZxPmOlPLrg49BL1DY8+J1ZgnhQnnI/WcC4sLy491C7EhbttgMSs9F/bTDG\ 5wp6I+M+ABIHrm8irFtT8squlpFOYjx7/2Rodj9Lcb57STgRTq8+8yHM1AcA3NwkhROCZHKwklhb8lg/8jHqS8mCX3LoN7LmXjJW904uoDRADLHeb\ qQGy/0DC1qi6fByFY9XL2K9oW4IeR+FdA+fXrSbTA5kpvMTIteoPh/XtKFHMAcDkgJMH8QV4u9XfD803afhQ+Aq4u4jusZT5RFoA+Dlknro8jcFaU\ rvINZ+4JbaaP3HC5Jz/aIcp5DLewFJvY+61hnvd0YOXhLYPfD1EGv17DpinbVrFfiNIBfaowUNZ9d6XzQ283BkKvHKmPeePArNATb+FKpitk/02NC\ 1NszmnDRS5YEKJLOZalffIMqIecyK96OGyXjQyMdT02nmhNut/Sd+kolzUYi7vjEe3rQNJkRJ+burSitOD9ek5AW4EP9AU8DrQTX6Gt7HmrrAD2Rs\ gPSHku06Jh794ANZcjstAS79Zqjk0HGU7cUPXjlUCOt+x4BupUjThtrY3eGFPVcWZC1tfIuebsxECaEeHifcFrzcT9U/7U6WEfqj2UyV8LvlFcNt/\ hd73hVaOsyRbRgBVGSU1l4bfm+6hCygn3SwSNop5tjEUtxzjL2UgnobfxO3XAJUZHCE1d9xi1vql6u5ivlGO595oyN34bxyaXO1c/CXHy/jBdfEiR\ WCgcWXj5/kFvTE9uF+emInAtLpGfuGY/DvuxmKljJ6YHR0jvduV8tuBnBLeGdkK8AytWX08tbKxL5VIWjwkZWxzh8rTttw6h1Fb7Y3sBrrAoLhcER\ 7+jE1vAUq9lf9u4w7eL+6TtWh1g35XX354zGYKPTFqOB5wwquTzwpaFehO84BijXC1xViEgiSbX2kWZnJNPPrjDH1cq++XdGc5bmDVD4rpS2f6Arm\ MuflcpcPD7TXQjgOv2cO+FKcg5pFW1NYcpQ99uVwGa1feLjQUfaTMZrkp9MGeWeLHAyv/iaWJRRgVg9P5FVyQdamlma0cmio2oK5eKXDeNp1B/MVE\ EexBRl9lm5GsHR7YYHL+721xUXO0Myt8SBvqPJXlqwrIMZQPJuN0vuSdiyRebkHUm9Q1pGgS79XQ2KBKvGq+DmOReHvD2sl5iA8hICg0b9oITswej\ izZ5KbW2lFffb9TRHfXuGvS4SaXqbhJp+in4MSGc5twLuZ64izoaEU7kjFqmkGVk+RNcwqsuI8jqrHbXUQfip2nq3esY1DvOv5vn5fJVU9hqHMobM\ TELYpmJU2EjzdLccvwNouEV+1Fx/Orn2m1p9TXkAnQ3hp25YXQhP20nmAupn8rCSYeBuc0REQ25G9HydGr4jB14trnMma+7f+2whUdz4/buWdjT4l\ uPaZG4LZLSfbITS8C84KGFIr6eJLHPpQ4ewql4snM8Yu6vfJSf5HxDzVK/hXHu7ALhe1VzC7iPniInitxHHnlov7qScdSbkY3zBck/B1dxqbSV7hU\ o6yYbgo2wp3afSAwr6plV4tbilc6in17wCnqjtz5ONBZANUQ3bKKeqMRtUQXVxs6SBRZCRw/xY7FvtkT5tigBnQyFPwTohUBwU6BSNhCNAqkJwssR\ QgrnRiwGbmEIT01Qqujs1P6BXueGBrCYLQtpySIQ9MPzeudOXdPPpUnFRDH1ZhusuxV3hHku77M823UnSHMw2We8pxnaQG9tMwwe1NA2IN9suUsoJ\ l1zRhQuEALqBRR0lT4sukg+W5FXTe+qO9QdoOXh8c8k2/MfkTYfc53CJej7MUfU72xjJ7ERWomTX7ojxLNpP8mNCFPQBcHZCi3lOMZ94j8z+gZRf3\ rQHlwnZvNfdZQCUr/hZeWPGinLnyVh1LAoJS/F/MQwOpmOWZSZiBtejFEzACT3KyFa+igWhEKRhqveljHNIrXxg1Gshg4wKDKvUSNQM5ZMthK1TvY\ 8V+DMYVptmYm9axAnTA95IJrHtk28fS8y08wkP+tWxkuhHiWtM7/nRXYeP8QONb4XDbrlLPlqPi0FTtJ2cqY6c6xCn/+gpMJZ8Ck7TE5njwuZTN3+\ /0h9YQDgG1hU8UslH/qGm1GC7FhlqPjzEuD0xZnzP3c/vLr8o9p6IGq9JTbqizv8BZKyQLSlFEE+WHhDcB/fvDRddcNzjdMnD/YIhSY9iw1ltqtae\ raZyEHgqKvn4uZg03W3/0CSJDWnMbM+T6mTmeYdz4pBMe1Pq6H/+suy/pF3O/tdg3zYAMeX4YO8CQtLpQGNhnhqIrowtJ1piwRckHAqd4F5gG0q2N\ VVWVHaI24Dv7PsqTopk9DdCAz9LSoNxugW07F0PvwQHmM0NdaSjp5Uz1Y9qW1iqAonuEHySEtfWQcT6g/3LvIr6fjAudeyt5cX2LdbZF2qY2LPp2m\ VnkgUYZfxsWoGmsE1RkL6/vNRu+31VWZHhMqsWarrvZnmkq2Ecgj5Zi335BVFiGsahN8fogf8+QoeIvz8lKiXye/2vIKUk6S/7jHMmMSAxMDgwNCA\ xIDEgIzQyNDMKeJxtWsl247oRzQdpk2SRk6Vnu9tqK5LcTr8dJEESnkBCDZJ2Uwt+e25NIN15R+cAt0ASQ6FGQA/zP/52tWlS7Fo//OFzml1tw254\ yN7Xsyum9zlsXT18Dyn6dnYV8rDqzj6HlEPbD9ex87OrGLa+wIvLAR/HmPrhJbv6QK1Vqnezq8qBbo/DrffnYdHlc/Rj4yLUpwlVnl4Seqs2PqP07\ bFvMIt6l9M4z7oNPztMILummVCpvvhC3nfbYxNcoZfdpi/E2zG0ePWMpSb0/rNzXFS0ErTnbRu2w3OoCAMe08nb2LmPYeeHHx7r/ZhdNUc8cOi5TR\ W+WdPypZMu+xSJMRcgKYc3v5ldu42ysaDhrveNkMwUQeljBx5cu5PPwzzEiEqf1vgNc1djedcu18PS7wDaNvrmGM4003527V2nw3j3Th35gJ25dZd\ L9Dt9Eg7DLT54Ee5ch+YntzZttorY+lGDaFuM/+yxr7Pr6LYnKQd0rg3DTcouKn6J4d0rXh3dLn00RNXbI8ZW6QDdtEPa723rrqPvICrDPSRiS2S4\ QLZsrjGlnaxUSLDtBpxP0Qkx7+oGmyLEt5uVgAWEK9X6/iIcKl+3QlyXl3kDGX3BahqBK3c+H0PWL1UbGP/A5ImfwG3Yd9xJc5xd4w0UNBbE6iRTz\ eFwbCFJYIQRIjnj3HXtxGZo2sGh584k/brb71HkQ1fvegKx/wAbCNWhIV7q9oBuTfmEWAVf106JV1an6/4CbpBwKuqq2Y3b+VY4LJCFB7DCU52FUd\ r/jdv74aobnl1ohfiWQgaqNjnsDt56q3zkMonY3kBoc6+V6Q+oXW/P8y7Ujr7JYbPBTpThc0WT1nqYLwjWrg2pLp/WPgbH75LSOZtDzqkd5926j42\ b3fidy8PN0cOw3PgI4aypBol3oLwNV10kyTag/Rl1nxN/rKTt5c3RVWd3oLka0gkeXd4mWhtANbZBwbpGzALmkzOZg9Q0qbyCSdUdRjqGGFiigLA6\ fQpu4GtrJqxyaqRx+Zi2mB9M/jq7XSDG8VyslcfHF7BYXUOodhvIIQOoKnQBUgOyZctGNbEsukxDQYE9GuFSqk0wxqf6YNueUlShSmf4Ea1Gngm5g\ LTaK7IihYm2I8EuRbbVYmS4tP7zLr3TVqUsxR5Lxpc6kVw3IdJrDdnnm7RxsTXqGTaT3ulTa4buJrWwFwPL5ewmewcVyaFqaMFSD69Xs5sutrDnO0\ zbZUynp/FRDIucth5O6aaHwpFhOXvFthX9NkInahjifNIxBRaVZJK7ZHQX/RaM38qCuOkhxZ2vMwyBkKQmw3///nchv3T1IZq/4pavR3cKAp/du1M\ EC52Hx5Qb+B5umbsDrKM+Zgs+7US1SPH2GHR0ddyMaeMYrBzZeMXeTftRwyT41E+WtWJh/J1m6yw0/HV9mPa17vLPLpHGMqnSf+veexE5jjtUpwUX\ DVVSFYypz4zjkIUkjNHK7fck9UKUaTN1doiGUOZT1Ka1Q8gEWIdKSnu98bnVCl3CCd6GSleYYDpVam+z2wxTAelOxpeu3R41fLnbJKjM3TZ3szssk\ d4gv3x3OJzhaVsCcBFwckWCOKIplG5coZV7d5WHcu1QQ9nJD9+BK3A2JDqePZ810G4b/u5hnGOEZRmbtD+KDu9IqXgF9471AFXHHdyDCxCsVIAw/d\ 5h3fc+mxO4Dz7uBmIMQRhKNgz3sFAost+Qy2U0YHjyEtw3qRqVv1AEcP4+suEQ/t2njN1X7SETeJ+x3uMggZIREgwZxSsQbAGmkuLjC1GV9+auey+\ ErE3w0jXnDRl9ayhipbQykJ3NhmdsQ0pdbOh9F9/Jct93l0s/vFE5e3ChbjYJEf0DHHPCih48LDzvM9w0OcuHIzpWZjxE122pjwdyQL2arocXZT9Z\ nGHuW2xe2ApFHGNgs2DivmtIBsRCqfgqsYBF741QczhasofUQjdtONiaYLpPRN0PqyogTbg6k8xSu0yAAFk++472skyo7KwgixCFohBRkMWISmmQK\ BTvh8BnynR2SqyOPF5XV8SU2SPiRmeLeoRP8D87iD3QO8sYljl79A7chhIBQVHgRM9TKIt9TFFiPDNbj6mGa40ayz+mdGLHQqM/IrYxm02Yheuxq9\ vyxtPWQ8IOs6cI7xwoZIL9NAV/qiitgzqQqjxB4D5SrgB2/kx6TqoPIpjxZlzrywx1uURBf6Uabnv6DFOQAA3df40+KCM/PxCPogpLWdPhr19QkXl\ g78Ah5uwpQ3ehmmEDaXhq3AZTodjkCZtBUyuq9PSeoGBf3NlxQGSxZGlQHfviGm2/XFgnhy/w/F/oSaqxlXEG54DRu93sk5f4inH7ghGxRFS96D97\ XRY/QRBTPEAKMPuaNp6KwMX26GZfV69mj5+x5g65bjG3z+4cGpCXLgYQTcnDnh1ikKijs2O3bwYxdCNNsygEM2ZKIXspJEvhM6yv9StDUQl3Gfb7k\ erIdgmG+dgGmitTc95MGoRJlZMpUXT0OYDdLbrh3EhmxlACvYI/RXXaSiGSwE+WW5qKZSnDSzMvTyGvj7EGN0IsMP0P6FIsDaRYAjWwUaJENkqb1C\ k5xi/a0Hrs1+SNMjXyHVwOzx3yEDASEoR1YxthoPGVNPA2ErBRa0Tw9UhxydODlFA4qiIV6GxguE5leu88BAWAt+nQKHzJB+Yo4RIZgUSkN3dbZFZ\ 1GOCk6RlUB207EphndyIsZkjrsr1Gk1UwbOa20BozG/3oLvwQrmoeYIgBYQIDdXZMmGEPEPgVlJOR/oQ/j1EDpbkDM9mzzZFwgu9U7/ikas7hBpd8\ XsO4DTQBZKC0XwxhzCItPKdUT0ZhkvZBITkfcfFcaujHOJI/nbtfoeoqm9VI6K59atL9srbPb4jjUZGxxt++IOs8wb8//u1j1UX5qC/D9ZrFzf2O0\ v7JyZi2TJ9KBs9uWTqyZjG0Rpa4Wxs0g1DKpinUqFbWMCYG1jJNBbRtTAa0QWy7zopXaid+Sho7sFPUi0Q34qwLhcl8eD9t6OpNlxvq5BeflLJ1mI\ fdrsjeiG2GkxZbqzRxmjLi8d3Puy1tnz/ldRX428PPH043OuxqdkIGypzriVWch5PbpbGXKtgySYkS1QcqQprMkXQVxSBpM0PrrWn7YZ5IyRly8D5\ PO68mew53D9/YQMFg6mlnZVL4wrpIiGsQ0sK30Gmi0BvK320D4EH11S5KWG1g1F7YVgrh5n1uR373CCO2Wg2i1bNvbkdnqSfNRr45sLaxlX6Dq/0z\ aeD8jZJNnm1BZbxv3rIeRjIeQ4vkv3lIYaIsmodBEgNT/43OtY6pTfot/NzFzV62RyQiL4ixy4EgESytBJ6RhSrQwIEIMU2EmOcEViG+89PATyjN5\ 7xqhMO/ZtP0nzDp8kvd/5q9nOGWXxA5OutbXKfGarZ0Jc3WK0l2056QU1NMcjnC3/tYpV3BugOfqMn7bFOkEo4qLm90FBtLwj5py/A8sNUWBbz82t\ BhM+p9suPnl9fBzn9otguEO3s7k1kguoNg2QYv4HpaCraLwqLFsxkVVE4XmYID5jNdJnjSgvjTwkBuU6tHUSjakaWV/hEoUAqeOKa1aZzJkkNKz8B\ 07teU/sEVznGAQlPeR+AR7Y0WmQHeAuJZ97WaqoV31Epl4aBQCzqrpvMwLuI4uTGaGuHk21whEidTQRELmW0OKNDeBDt3NUInqpTmHUbq5hkp6zAC\ 6U8heP8Mk1IwUVFYt/AfrQWZi2NMv1AiLY3JmoTQeZAr8WrzQbSJsynzfMgjD3q0vQh12RWCa8CZPTmNe0wEq7Ei1WOmkHDmDfKRBYkahBoLZzGjh\ UUtWL8IlMu2RYpyT7DgKSU8oQhuQbE0yDPZQ9Qwh9RsLOSbJ10XJG7r6fTGHkq0Zpq4yB11nLtmsl1NT/c8dGJb5tHRqroYK6enAsPrYoWW6nxCSF\ beYpnnVQi0lF1JCrbsSXEI7k+Eqr21kxU2vKALpMulkMwn4I6O3Wf/6UqCL5B35D8dXd6ojQRRuy1SzkT3DhrjLqFKW02bly40nNzSydvS/emOs/F\ YpyBOj/yEZrFbug87DCUodzRLvp8b5JrO3rtcKtfSQhiS2ZH+kdxvt854zPOZmE/Cxj/CdODBgYKKG2M97CC8/HHNtW70CD/1aE2T1GQSfRBEitTI\ u5pcj/BTT9b0uSe2hUvPV9TB3Ofy2LfHarYMfAbHvC5wuE+H7P7x7/9r+ic17YufXyLVroe7g8YWywRf245nWssElw7PUNvJ5jKRrKogyEagQBJVb\ 1It+BbxhCA5i2UoaZFglihBUD0ELp0+kAEaSxcYfndRkLCA7oEo8yVeJth6OUtbpt5JCskHxFNazhakwbaDCVXVZccC1m3seJQvfalQak/5yhKaTH\ umCq28U8o2lNfdcSSHL1dQEbrTQMCyaiXlpY9WToJenvfK7X1b/h/wl5RO007aV44eIfkmX0Q3zbRrmn5LJezlY3QqsB21Z9SXQc9lLnqba8C6U8r\ kkm+65HBPIqvVlg7zWtRHBybokNsj3Wpdd8UMrrYU8wbj15jBFDQO4fl2G1W0aXonB/Urv6UrtZWnI3mKv6x3f0asKPfoWun8kVbBHp9sMJm3Yrg0\ nzWWbo7UQKeo+gVIlPRUVjSlxqmKdVIdkGrykMmiQPYYHjLQ+oSU3gXLToU68WpgmyONyBJkBJIrPvVcndIGWRkiinKmYmCcABrMJk8yxMmpyyqGy\ tJaOj5uuU7cERmRVU38hIvUaHGVakrwdLkSJdKl+Mzsgt4GFJLHU8L8vtEyg0LwHJQyuVdSxp6YHsPj2rVB9W8k9SR/EhgqlBvqT2nylBiZ2NJdeK\ oqUiAZazykEijTa0Ok/2ZgVaGkfCtEzlqKxxshXWQjeKGWhEEnh1b8HwfxxSs6SqV+sOvELHJWmHR3LoKD1Ls1Lemz29JtuXJvjXWvJ5fv419v1uM\ 10trldvyATC2XsjnroqWE2MQDRC6EAWtoYqUitoYuUEtG5kO3xGg5QhxgZdfHLpaQcw1bReGJN3K/d7Vu4zqQj/9IcQ/YSrSrrFzD27QJVYd4mI+y\ 1xl6wnEGOL7nuyq0dSrl69yfLd5ad/u2MVhtoucDi/E4pCB7x0hdfDfJhtdds3VWKeeVWHKnDNflFXDxI0T5l4vFq+s+h/Hw8fVKhkVNPbxGyIgeJ\ 02wvkMNwjiB/AUHRq81Fub4/0MrutB/pQsiBDV8V6CrR9s7xfJwJmnPfylo+vhO/5OSjurq07nP62KY3gESren/67lB+Coh3iulJcbq19XVva7nO9\ pu+5M5OJAhQgy1Hp5gA7/7g2vEj3z3tW8t+0CmhtQFhm4m16Z0ZPsJkR1CV6LtfxEn/RZN2UEXAjiFFEIrlMmO+P++ogkVSIffwzX0ZVaMiwHl0nc\ 6EB7PIIQslkrIUReFtpWk2M7e6B8wYn/fvL/od1iXwzM+UnkLUWyR/dWKGzS7ZzwxOUy/weTnio/x3kK9a8AoEtE3mgEVw232h4Yg5/+nXuGBbeEt\ n1e9BTp4h2d6o9s32dT/utrtOqraI1wc1xQk/XB21qei9NtJhJIWfStJAbhCi8GN1CNwO6fDRioUnk+J3wfRxol06AM1e5+ov3qPzC79X2j246meq\ 5z/QUEYKkStf/sfIaI+Kg==" +store. __color2name else $__color2name fi #@cli covariance_vectors : _avg_outvarname #@cli : Return the covariance matrix of the vector-valued colors in the latest of the selected images #@cli : (for arbitrary number of channels). #@cli : Parameter 'avg_outvarname' is used as a variable name that takes the value of the average vector-value. covariance_vectors : skip "${1=avg}" $1=${-average_vectors} eval. "*begin(avg = [ "$""$1" ]; C = vector(#s^2,0)); mI = I - avg; C+=mul(mI,mI,s); end(merge(C,+); C/=whd - 1; run('u ',v2s(C)))" #@cli da_freeze #@cli : Convert each of the selected dynamic arrays into a 1-column image whose height is the number of array elements. da_freeze : e[^-1] "Freeze dynamic array$?." $! eval. "da_freeze(#x)" rm. #@cli dec : decimal_int1,... #@cli : Print specified decimal integers into their binary, octal, hexadecimal and string representations. dec : e[^-1] "Convert decimal integer"${arg0\ ($#>1),"",s}" '${^0}' to binary '"${dec2bin\ ${^0}}"',"\ " octal '"${dec2oct\ ${^0}}"', hexadecimal '"${dec2hex\ ${^0}}"' and string '"${dec2str\ ${^0}}"'." #@cli dec2str : decimal_int1,... #@cli : Convert specifial decimal integers into its string representation. dec2str : u {`[${^0}]`} #@cli dec2bin : decimal_int1,... #@cli : Convert specified decimal integers into their binary representations. dec2bin : $=arg res,sep= repeat $# { res=$res$sep${_$0\ ${arg{$>+1}}} sep=, } u $res _dec2bin : u {`"const sgn = sign($1); const N = (isinf($1) || isnan($1)?1:1 + floor(log2(max(1,abs($1))))) + (sgn<0); res = vectorN(); sgn>=0?0:(res[0] = _'-'); for (val = abs($1); k = size(res) - 1, k>=(sgn<0), --k, res[k] = _'0' + (val&1); val>>=1); res"`} #@cli dec2hex : decimal_int1,... #@cli : Convert specified decimal integers into their hexadecimal representations. dec2hex : $=arg res,sep= repeat $# { res=$res$sep${_$0\ ${arg{$>+1}}} sep=, } u $res _dec2hex : u {`"begin(tab = [ _'0',_'1',_'2',_'3',_'4',_'5',_'6',_'7',_'8',_'9',_'a',_'b',_'c',_'d',_'e',_'f' ]); const sgn = sign($1); const N = (isinf($1) || isnan($1)?1:1 + floor(log2(max(1,abs($1)))/4)) + (sgn>=0?0:1); res = vectorN(); sgn>=0?0:(res[0] = _'-'); for (val = abs($1); k = size(res) - 1, k>=(sgn<0?1:0), --k, res[k] = tab[val&15]; val>>=4); res"`} #@cli dec2oct : decimal_int1,... #@cli : Convert specified decimal integers into their octal representations. dec2oct : $=arg res,sep= repeat $# { res=$res$sep${_$0\ ${arg{$>+1}}} sep=, } u $res _dec2oct : u {`"const sgn = sign($1); const N = (isinf($1) || isnan($1)?1:1 + floor(log2(max(1,abs($1)))/3)) + (sgn<0); res = vectorN(); sgn>=0?0:(res[0] = _'-'); for (val = abs($1); k = size(res) - 1, k>=(sgn<0), --k, res[k] = _'0' + (val&7); val>>=3); res"`} #@cli fibonacci : N>=0 #@cli : Return the Nth number of the Fibonacci sequence. #@cli : $ echo ${"fibonacci 10"} #@cli : \n~~~\n[gmic]-0./ Start G'MIC interpreter.\n[gmic]-0./ 55\n[gmic]-0./ End G'MIC interpreter.\n~~~\n fibonacci : check "$1>=0" u {N=$1;N<2?N:for(n=N;F0=0;F1=1,n=n-1,F2=F0+F1;F0=F1;F1=F2)} #@cli file_mv : filename_src,filename_dest #@cli : Rename or move a file from a location $1 to another location $2. file_mv : e[^-1] "Move file '$1' to location '$2'." if ${-is_windows} x "move "$1" "$2 else x "mv "$1" "$2 fi #@cli filename : filename,_number1,_number2,...,_numberN #@cli : Return a filename numbered with specified indices. filename : skip "${1=default}" if $#==1 u "$1" else (${2--1}) => "$1" u {f}{b} repeat w { u ${}_{int(i/100000)%10}{int(i/10000)%10}{int(i/1000)%10}{int(i/100)%10}{int(i/10)%10}{i%10} shift. -1 } if narg({'{x}'}) u ${}.{x} fi rm. fi #@cli filename_rand #@cli : Return a random filename for storing temporary data. filename_rand : do filename=${-path_tmp}gmic$_pid{`v(vector6(_'0'),vector6(_'9'))`} while isfile(['{/$filename}']) u $filename #@cli filename_dated : filename #@cli : Convert specified filename to one stamped with the current date (`filename_YYYYMMDD_HHMMSS.ext`). filename_dated : 0 => "$1" ext={x} folder={f} basename={b} rm. if ['$ext']!=0 ext..=. fi u ${folder}${basename}_{`" str='00000000_000000'; copy(str,v2s(date(0)),4); v = date(1); copy(str[4 + (v<10)],v2s(v),2); v = date(2); copy(str[6 + (v<10)],v2s(v),2); v = date(4); copy(str[9 + (v<10)],v2s(v),2); v = date(5); copy(str[11 + (v<10)],v2s(v),2); v = date(6); copy(str[13 + (v<10)],v2s(v),v<10?1:2); str"`}$ext #@cli files : _mode,path : (+) #@cli : Return the list of files and/or subfolders from specified path. #@cli : 'path' can be eventually a matching pattern. #@cli : 'mode' can be { 0:files only | 1:folders only | 2:files + folders }. #@cli : Add '3' to 'mode' to return full paths instead of filenames only. #@cli : Default value: 'mode=5'. #@cli files2img : _mode,path #@cli : Insert a new image where each vector-valued pixel is a string encoding the filenames returned by \ # command ''files''. #@cli : Useful to manage list of filenames containing characters that have a special meaning in the G'MIC language,\ # such as spaces or commas. +files2img : if ['$$_$0_f2i']==0 m "_$0_f2i: $""=arg repeat $""# { ({'${arg{1+$>}}'}:^) } a y" fi files $"*" l[] { _$0_f2i[] ${} if !$! 0 fi } #@cli fitratio_wh : min_width,min_height,ratio_wh #@cli : Return a 2D size 'width,height' which is bigger than 'min_width,min_height' and has the specified w/h ratio. fitratio_wh : if $3*$2>$1 u {int($3*$2)},$2 else u $1,{int($1/$3)} fi #@cli fitscreen : width,height,_depth,_minimal_size[%],_maximal_size[%] : [image],_minimal_size[%],_maximal_size[%] #@cli : Return the 'ideal' size WxH for a window intended to display an image of specified size on screen. #@cli : Default values: 'depth=1', 'minimal_size=128' and 'maximal_size=85%'. fitscreen : skip "${2=},${3=},${4=},${5=}" if ${"is_image_arg $1"} l$1 { W,H,D={w},{h},{d} } if narg($2) m=$2 else m=25% fi if narg($3) M=$3 else M=85% fi else W,H=${1-2} if narg($3) D=$3 else D=1 fi if narg($4) m=$4 else m=25% fi if narg($5) M=$5 else M=85% fi fi eval " const u = "{*,u}"; const v = "{*,v}"; ms = round(ispercentage("$m")?[ u,v ]*"$m":[ "$m,$m" ]); Ms = round(ispercentage("$M")?[ u,v ]*"$M":[ "$M,$M" ]); s = [ "$W,$H" ]; "$D">1?(s+="$D"); s[0]Ms[0]?(s = [ Ms[0],s[1]*Ms[0]/s[0] ]); s[1]>Ms[1]?(s = [ s[0]*Ms[1]/s[1],Ms[1] ]); s[0] = max(1,s[0],ms[0]); s[1] = max(1,s[1],ms[1]); round(s)" #@cli fontchart : display_mode. #@cli : Insert G'MIC font chart at the end of the image list. #@cli : 'display_mode' can be { 0: List of characters | N: List of fonts with height 'N'}. #@cli : Default value: 'display_mode=0'. #@cli : $ fontchart 0 fontchart 64 +fontchart : check "isint(${1=0},0)" s0,s1=characters,"fonts, with height $1" e[^-1] "Generate G'MIC font chart (list of "${s{!!$1}}")." if !$1 l[] { # List of characters repeat 256 { if $>==92 char=\\ else char={`max(1,(c=$>;c>=23&&c<=28?32:c))`} fi 0 t. {``$char},0,0,50,1,255 } a z,0.5 s z foreach { t $>,1,-1,13,1,200 0 t. \\${dec2oct\ $>},1,-1,13,1,1 100%,100%,1,1,200 j... .,{-3,[w,h]-[w#-1,h#-1]},0,0,1,.. rm[-2,-1] } frame xy,1,128 append_tiles , } else l[] { # List of fonts input_cached gmic_fonts.gmz foreach { ('{n}') strcapitalize {t} name=${} rm. e " - "$name rm 0 t. $name:\n" Hello World!",0,0,${"font \""$name"\",$1"},1,255 } r ${-max_wh},1,1,0,0,0.5,0.5 frame x,10,0 frame y,25,0 frame xy,3,128 append_tiles 3 } fi # font2cimgh # Encode a font image as a C-style string for CImg.h. font2cimgh : e[^-1] "Encode font image$? as a C-style string for CImg.h." foreach { bnm={0,b} W,H:=w/256,h e " > Encode font '"$bnm"'." if !isint($W) error[0--4] "Font image '"$bnm"' has wrong dimensions ("{[w,h,d,s]}")." fi +f "i==im || i==iM" is_binary:=im==1 rm. # Find best parameters for RLE compression. Mm=0 MM=100 do M:=floor(($Mm+$MM)/2) +compress_rle $is_binary,$M rows. 6,100% +. {32-im} iM:=iM if iM<126 Mm=$M rm. elif iM>126 MM=$M rm. fi while $iM!=126 k. nb_chunks:=1+int(h/65536) e "\r > Encode font '"$bnm"' -> W = "$W", H = "$H", M = "$M", is_binary = "$is_binary", nb_chunks = "$nb_chunks"." # Generate C-style string for storing font data. replace_str "\\","\\\\" replace_str "\"","\\\"" s y,-111 repeat $! { if {$>,i[h-1]==_'\\'&&i[h-2]!=_'\\'} rows[$>] 0,{$>,h-2} rows[{$>+1}] -1,100% =[{$>+1}] {'\\'} fi l[$>] { i[0] ('" \""') ('\"') if !$< ('" };"') fi ('\n') y a y } } repeat $nb_chunks-1 { ind:=int($!*($>+1)/$nb_chunks) l[$ind] { = {','},0,100% ('\n') y a y } } i[0] ('" static const char *const data_font"${W}x${H}"[] = {"\n') y a y ot $bnm.h } #@cli fps #@cli : Return the number of time this function is called per second, or -1 if this info is not yet available. #@cli : Useful to display the framerate when displaying animations. fps : if narg($_fps_fps) dt:=$|-$_fps_time if $dt>1 _fps_fps:=round($_fps_nbframes/$dt) _fps_time=$| _fps_nbframes=0 fi u $_fps_fps _fps_nbframes+=1 else _fps_nbframes=0 _fps_time=$| _fps_fps=-1 u -1 fi #@cli hex : hexadecimal_int1,... #@cli : Print specified hexadecimal integers into their binary, octal, decimal and string representations. hex : dec=${hex2dec\ ${^0}} e[^-1] "Convert hexadecimal integer"${arg0\ ($#>1),s,""}" '${^0}' to binary '"${dec2bin\ $dec}"', octal '"${dec2oct\ $dec}"', decimal '"$dec"' and string '"${dec2str\ $dec}"'." #@cli hex2dec : hexadecimal_int1,... #@cli : Convert specified hexadecimal integers into their decimal representations. hex2dec : l[] { ('$*') s. +,{','} $!,1,1,1,"f = i[#x,0]; f!=_','?(resize(#x,1,h#x+2,1,1,0,0,0,1); f==_'-'?copy(i[#x,0],'-0x'):copy(i[#x,0],'0x'))" rm. a y u {[{t}]} rm } #@cli hex2img : "hexadecimal_string" #@cli : Insert new image 1xN at the end of the list with values specified by the given hexadecimal-encoded string. +hex2img : ('"$1"') 1,{w/2} f. "* from_char(x) = x>=48 && x<=57?x - 48:x-87; off = 2*y; from_char(i[#-2,off])*16 + from_char(i[#-2,off + 1])" rm.. #@cli hex2str : hexadecimal_string #@cli : Convert specified hexadecimal string into a string. #@cli : See also: ''str2hex''. hex2str : skip ${1=""} if !narg("$1") u "" return fi ('$*') if w<2 rm. u "" return fi f. "v = i - (i>=97?87:48); x%2?v:v*16" r. 2,{int(w/2)},1,1,-1 cumulate. x z. 1,1 u {t} rm. #@cli img2base64 : _encoding={ 0:base64 | 1:base64url },_store_names={ 0 | 1 } #@cli : Encode selected images as a base64-encoded string. #@cli : The images can be then decoded using command 'base642img'. #@cli : Default values: 'encoding=0' and 'store_names=1'. img2base64 : check "isbool(${1=0}) && isbool(${2=1})" if isnum("$1") encoding=$1 else encoding=0 noarg fi +serialize auto,1,$2 u ${uint82base64\ $encoding} rm. #@cli img2hex #@cli : Return representation of last image as an hexadecimal-encoded string. #@cli : Input image must have values that are integers in [0,255]. img2hex : whds={w},{h},{d},{s} y. 2,{h} f.. "* to_char(x) = x>=0 && x<=9?48 + x:87 + x; i(#-1,0,y) = to_char(int(i/16)); i(#-1,1,y) = to_char(i%16); i" u {t} rm. r. $whds,-1 #@cli img2str #@cli : Return the content of the selected images, as special G'MIC input strings. img2str : str,sep= foreach { str.=$sep({`"s = v2s(crop()); for (k = 1; p = find(s,_','), p>=0, p = find(s,_',',++p), s[p] = k%w?_',':k%wh?_';':k%whd?_'/':_'^'; ++k; ); s"`}) sep=" " } u $str #@cli img2text : _line_separator #@cli : Return text contained in a multi-line image. #@cli : Default value: 'line_separator= '. img2text : skip "${1= }" +l { s y s -,0 y x if $!>1 i[1--2] ('"$1"') fi a x u {0,t} rm } #@cli is_mesh3d #@cli : Return 1 if all of the selected images are 3D meshes, 0 otherwise. is_mesh3d : u 1 l { check3d 1 onfail u 0 } # Faster version. _is_mesh3d : u {"w==1 && h>6 && d==1 && s==1 && int(crop(0,0,1,6))==['CImg3d']"} #@cli is_change : _value={ 0:false | 1:true } #@cli : Set or unset the 'is_change' flag associated to the image list. #@cli : This flag tells the interpreter whether or not the image list should be displayed when the pipeline ends. #@cli : Default value: 'value=1'. is_change : l[] { check "isbool(${1=1})" arg=$1 onfail noarg arg=1 } if $arg 0 rm. elif {*9} w9[] -1,-1 else w9 0 fi #@cli is_half #@cli : Return 1 if the type of image pixels is limited to half-float. is_half : (2049) u {i==2048} rm. #@cli is_ext : filename,_extension #@cli : Return 1 if specified filename has a given extension. is_ext : skip "${1=},${2=}" 0 => "_$1" u {"lowercase(['"{x}"'])==lowercase(['$2'])"} rm. #@cli is_image_arg : string #@cli : Return 1 if specified string looks like '[ind]'. is_image_arg : skip "${1=;}" u {"str = ['$1']; s1 = size(str) - 1; (str[0]==_'[' && str[s1]==_']') || (str[0]==_'.' && str[s1]==_'.') && min(str)>=45 && max(str)<=122"} #@cli is_pattern : string #@cli : Return 1 if specified string looks like a drawing pattern '0x......'. is_pattern : skip "${1=;}" u {"str = ['$1']; size(str)>2 && same(str,'0x',2)"} #@cli is_varname : string #@cli : Return 1 if specified string can be considered as a valid variable name. is_varname : skip "${1=}" u {" S = ['$1']; is_varname = 1; inrange(S[0],_'0',_'9')?(is_varname = 0): repeat(size(S),k, c = S[k]; is_varname&=inrange(c,_'0',_'9') || inrange(c,_'a',_'z') || inrange(c,_'A',_'Z') || c==_'_'; !is_varname?break(); ); is_varname"} #@cli is_videofilename : filename #@cli : Return 1 if extension of specified filename is typical from video files. is_videofilename : skip "${1=}" 0 => "_$1" u {" ext = lowercase(['"{x}"']); isin(ext,'avi','mov','asf','divx','flv','mpg','m1v','m2v','m4v','mjp','mp4','mkv','mpe','movie','ogm','ogg','ogv', 'qt','rm','vob','wmv','xvid','mpeg')"} rm. #@cli is_macos #@cli : Return 1 if current computer OS is Darwin (MacOS), 0 otherwise. is_macos : if !narg($_is_macos) _is_macos:=isdir('/Library/Apple/System/Library') fi u $_is_macos #@cli is_windows #@cli : Return 1 if current computer OS is Windows, 0 otherwise. is_windows : if !narg($_is_windows) _is_windows:=['$OS']!=0" && "['$WINDIR']!=0 fi u $_is_windows #@cli lof : feature #@cli : Return the list of specified features (separated by commas) for each selected images. lof : foreach { res.=$c{"$*"} c=, } u $res #@cli math_lib #@cli : Return string that defines a set of several useful macros for the embedded math evaluator. math_lib : u " # Distance from point A to point B dist(A,B) = ( norm(B - A); ); # Distance from point X to segment [A,B] dist(X,A,B) = ( AB = B - A; P = A + dot(X - A,B - A)/max(1e-8,dot(AB,AB))*AB; dot(P - A,P - B)<=0?norm(P - X):min(norm(A - X),norm(B - X)); ); # Distance from segment [A,B] to segment [C,D] dist(A,B,C,D) = ( min(dist(A,C,D),dist(B,C,D),dist(C,A,B),dist(D,A,B)); ); # Return the 'm' largest eigenvalues of a (large) square matrix A, # using the Arnoldi iteration method (https://en.wikipedia.org/wiki/Arnoldi_iteration). # A larger 'm' goes with better numerical precision. meig(A,m,n) = ( unref(meig_m,meig_n); const meig_n = n; const meig_m = min(m,meig_n); # Initialize data. ref(vector(#meig_m*meig_n),meig_V); ref(vector(#meig_m^2),meig_H); ref(expr('u(-1,1)',1,meig_n),meig_vj); meig_vj/=norm(meig_vj); # Iteration loop. repeat (meig_m,meig_j, copy(meig_V[meig_j*meig_n],meig_vj,meig_n); meig_vj = A*meig_vj; for (meig_i = 0, meig_i<=meig_j, ++meig_i, ref(meig_V[meig_i*meig_n,meig_n],meig_vi); meig_d = dot(meig_vj,meig_vi); meig_H[meig_j*meig_m + meig_i] = meig_d; meig_vj-=meig_d*meig_vi; ); meig_nvj = norm(meig_vj); meig_H[meig_j*meig_m + meig_j + 1] = meig_nvj; meig_nvj<1e-12?break(); meig_vj/=meig_nvj; ); eig(meig_H)[0,m]; ); # Dichotomic search : Find x in [xmin,xmax] s.a f(x) = target, with a epsilon-precision search_dichotomic(fn_x,target,epsilon,xmin,xmax) = ( # fn_x must be a strictly monotonic function ! # if 'xmin = xmax = nan', range of 'x' is auto-detected. _dicho_fn(x) = _dicho_sgn*(fn_x); _dicho_epsilon = epsilon; _dicho_m = xmin; _dicho_M = xmax; _dicho_sgn = 1; _dicho_autom = isnan(_dicho_m); _dicho_autoM = isnan(_dicho_M); if (_dicho_autom, _dicho_m = -1); if (_dicho_autoM, _dicho_M = 1); _dicho_sgn = _dicho_fn(_dicho_m)>_dicho_fn(_dicho_M)?-1:1; _dicho_res = nan; _dicho_target = _dicho_sgn*target; _dicho_nb_attempts = 30; _dicho_autom?do ( _dicho_fm = _dicho_fn(_dicho_m); _dicho_fm<_dicho_target?break(); _dicho_m*=2; _(while), --_dicho_nb_attempts); _dicho_nb_attempts?( _dicho_autoM?do ( _dicho_fM = _dicho_fn(_dicho_M); _dicho_fM>_dicho_target?break(); _dicho_M*=2; _(while), --_dicho_nb_attempts); _dicho_nb_attempts?( _dicho_nb_attempts = 100; do ( _dicho_c = (_dicho_m + _dicho_M)/2; _dicho_fc = _dicho_fn(_dicho_c); abs(_dicho_fc - _dicho_target)<_dicho_epsilon?(_dicho_res = _dicho_c; break()): _dicho_fc<_dicho_target?(_dicho_m = _dicho_c): (_dicho_M = _dicho_c); _(while), --_dicho_nb_attempts ); ); ); _dicho_res; ); search_dichotomic(fn_x,target,epsilon) = search_dichotomic(fn_x,target,1e-3,nan,nan); search_dichotomic(fn_x,target) = search_dichotomic(fn_x,target,1e-3); search_dichotomic(fn_x) = search_dichotomic(fn_x,0); arrow(ind,P0,P1,angle,length,opacity,color) = ( # Draw an arrow from P0 to P1 on image #ind unref(_da_color); _opacity = opacity; _da_color = color; _P0 = P0; _P1 = P1; _P0P1 = _P1; _P0P1-=_P0; if (length<0, _P0P1*=-length/100, _P0P1*=length/norm(_P0P1)); coords = [ _P0,_P1,_P1,_P1 - rot(angle°)*_P0P1,_P1,_P1 - rot(-angle°)*_P0P1 ]; repeat (3,_k, polygon(#ind,2,coords[4*_k,2],coords[4*_k+2,2],_opacity,_da_color)); ); spline(ind,P0,T0,P1,T1,opacity,color) = ( # Draw spline P0-P1 with tangents T0,T1 on image #ind unref(_ds_color); _P0 = P0; _P1 = P1; _opacity = opacity; _ds_color = resize(color,s#ind)*=abs(_opacity); _omopacity = 1 - max(_opacity,0); _C = mul([ 2,-2,1,1,-3,3,-2,-1,0,0,1,0,1,0,0,0 ],[ _P0,P1,T0,T1 ],2); _dt = _dtmin = 1/max(abs(_P1 - _P0)); _P0 = inf; for (_t = 0, _t<=1, _t+=_dt, _P = round(mul([_t^3,_t^2,_t,1],_C,2)); _dP = abs(mul([3*_t^2,2*_t,1,0],_C,2)); _dt = min(_dtmin,0.75/max(_dP)); if (_P0!=_P, I(#ind,_P) = _ds_color + _omopacity*I(#ind,_P)); _P0 = _P; ); nan; ); thickspline(ind,P0,T0,P1,T1,thickness,opacity,color) = ( # Draw thick spline P0-P1 with tangents T0,T1 on image #ind unref(_ds_color); unref(_ds_mask); _P0 = P0; _P1 = P1; _opacity = opacity; _radius = thickness/2; _ds_color = resize(color,s#ind)*=abs(_opacity); _omopacity = 1 - max(_opacity,0); _C = mul([ 2,-2,1,1,-3,3,-2,-1,0,0,1,0,1,0,0,0 ],[ _P0,P1,T0,T1 ],2); _dt = _dtmin = 1/max(abs(_P1 - _P0)); _P0 = inf; for (_t = 0, _t<=1, _t+=_dt, _P = round(mul([_t^3,_t^2,_t,1],_C,2)); _dP = abs(mul([3*_t^2,2*_t,1,0],_C,2)); _dt = min(_dtmin,0.75/max(_dP)); if (_P0!=_P, ellipse(#ind,_P[0],_P[1],_radius,_radius,0,_opacity,_ds_color); ); _P0 = _P; ); nan; ); triangle(ind,P0,P1,P2,opacity,color0,color1,color2) = ( # Draw a shaded triangle P0-P1-P2 on image #ind unref(_dt_color); unref(_dt_color0); unref(_dt_color1); unref(_dt_color2); _opacity = opacity; _dt_color0 = resize(color0,s#ind); _dt_color1 = resize(color1,s#ind); _dt_color2 = resize(color2,s#ind); _A = round(P0); _B = round(P1); _C = round(P2); _xmin = max(0,min(_A[0],_B[0],_C[0])); _xmax = min(w#ind-1,max(_A[0],_B[0],_C[0])); _ymin = max(0,min(_A[1],_B[1],_C[1])); _ymax = min(h#ind-1,max(_A[1],_B[1],_C[1])); _M = transpose([_A,1,_B,1,_C,1],3); for (_y = _ymin, _y<_ymax, ++_y, for (_x = _xmin, _x<_xmax, ++_x, _L = round(solve(_M,[_x,_y,1]),1e-5); if (min(_L)>=0, _dt_color = _L[0]*_dt_color0 + _L[1]*_dt_color1 + _L[2]*_dt_color2; copy(i(#ind,_x,_y,0,0),_dt_color,size(_dt_color),whd#ind,1,_opacity); ); ); ); nan; ); # Implementation of the Xiaolin Wu's antialiased line algorithm, as described in # https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm # line_aa_plot(ind,x,y,o) = ( _line_aa_o = o*line_aa_opacity; (I(#ind,x,y)*=1 - _line_aa_o)+=line_aa_color*_line_aa_o; ); line_aa_fpart(x) = (x - floor(x)); line_aa_rfpart(x) = (1 - line_aa_fpart(x)); line_aa(x0,y0,x1,y1,opacity,color) = line_aa(#-1,#x0,#y0,#x1,#y1,opacity,color); line_aa(ind,x0,y0,x1,y1,opacity,color) = ( line_aa_x0 = x0; line_aa_y0 = y0; line_aa_x1 = x1; line_aa_y1 = y1; line_aa_color = color; line_aa_opacity = opacity; line_aa_steep = abs(line_aa_y1 - line_aa_y0)>abs(line_aa_x1 - line_aa_x0); line_aa_steep?(swap(line_aa_x0, line_aa_y0); swap(line_aa_x1, line_aa_y1)); line_aa_x0>line_aa_x1?(swap(line_aa_x0, line_aa_x1); swap(line_aa_y0, line_aa_y1)); line_aa_dx = line_aa_x1 - line_aa_x0; line_aa_dy = line_aa_y1 - line_aa_y0; line_aa_gradient = line_aa_dx?line_aa_dy/line_aa_dx;1; # Draw first endpoint. line_aa_xend = round(line_aa_x0); line_aa_yend = line_aa_y0 + line_aa_gradient*(line_aa_xend - line_aa_x0); line_aa_xgap = line_aa_rfpart(line_aa_x0 + 0.5); line_aa_xpxl1 = line_aa_xend; line_aa_ypxl1 = floor(line_aa_yend); line_aa_steep?( line_aa_plot(#ind,line_aa_ypxl1,line_aa_xpxl1,line_aa_rfpart(line_aa_yend)*line_aa_xgap); line_aa_plot(#ind,line_aa_ypxl1 + 1,line_aa_xpxl1,line_aa_fpart(line_aa_yend)*line_aa_xgap); ):( line_aa_plot(#ind,line_aa_xpxl1,line_aa_ypxl1,line_aa_rfpart(line_aa_yend)*line_aa_xgap); line_aa_plot(#ind,line_aa_xpxl1,line_aa_ypxl1 + 1,line_aa_fpart(line_aa_yend)*line_aa_xgap); ); line_aa_intery = line_aa_yend + line_aa_gradient; # Draw second endpoint. line_aa_xend = round(line_aa_x1); line_aa_yend = line_aa_y1 + line_aa_gradient*(line_aa_xend - line_aa_x1); line_aa_xgap = line_aa_fpart(line_aa_x1 + 0.5); line_aa_xpxl2 = line_aa_xend; line_aa_ypxl2 = floor(line_aa_yend); line_aa_steep?( line_aa_plot(#ind,line_aa_ypxl2,line_aa_xpxl2,line_aa_rfpart(line_aa_yend)*line_aa_xgap); line_aa_plot(#ind,line_aa_ypxl2 + 1,line_aa_xpxl2,line_aa_fpart(line_aa_yend)*line_aa_xgap); ):( line_aa_plot(#ind,line_aa_xpxl2,line_aa_ypxl2,line_aa_rfpart(line_aa_yend)*line_aa_xgap); line_aa_plot(#ind,line_aa_xpxl2,line_aa_ypxl2 + 1,line_aa_fpart(line_aa_yend)*line_aa_xgap); ); # Draw intermediate points. line_aa_steep?( for (line_aa_x = line_aa_xpxl1 + 1, line_aa_x<=line_aa_xpxl2 - 1, ++line_aa_x, line_aa_plot(#ind,floor(line_aa_intery),line_aa_x,line_aa_rfpart(line_aa_intery)); line_aa_plot(#ind,floor(line_aa_intery) + 1,line_aa_x,line_aa_fpart(line_aa_intery)); line_aa_intery+=line_aa_gradient; ); ):( for (line_aa_x = line_aa_xpxl1 + 1, line_aa_x<=line_aa_xpxl2 - 1, ++line_aa_x, line_aa_plot(#ind,line_aa_x,floor(line_aa_intery),line_aa_rfpart(line_aa_intery)); line_aa_plot(#ind,line_aa_x,floor(line_aa_intery) + 1,line_aa_fpart(line_aa_intery)); line_aa_intery+=line_aa_gradient; ); ); ); hsv2rgb(I...) = ( # Convert HSV vector to RGB ref([I],_I); _I[0]%=360; _I[1] = cut(_I[1],0,1); _I[2] = cut(_I[2],0,1); _c = _I[2]*_I[1]; _x = _c*(1-abs((_I[0]/60)%2-1)); (arg(1 + int(_I[0]/60),[_c,_x,0],[_x,_c,0],[0,_c,_x],[0,_x,_c],[_x,0,_c],[_c,0,_x])+=_I[2] - _c)*=255; ); rgb2hsv(I...) = ( # Convert RGB vector to HSV ref([I],_I); _M = max(_I); _C = _M - min(_I); [ 60*(!_C?0:_M==_I[0]?((_I[1] - _I[2])/_C)%6:_M==_I[1]?(_I[2] - _I[0])/_C + 2:(_I[0] - _I[1])/_C + 4), _M<=0?0:_C/_M, _M/255 ]; ); is_intriangle(P,A,B,C) = ( # Test if a point is inside a triangle _v0 = C - A; _v1 = B - A; _v2 = P - A; _dot00 = dot(_v0,_v0); _dot01 = dot(_v0,_v1); _dot02 = dot(_v0,_v2); _dot11 = dot(_v1,_v1); _dot12 = dot(_v1,_v2); _invDenom = 1/(_dot00*_dot11 - _dot01*_dot01); _u = (_dot11*_dot02 - _dot01*_dot12)*_invDenom; _v = (_dot00*_dot12 - _dot01*_dot02)*_invDenom; _u>=0 && _v>=0 && _u + _v<1); is_inquadrilateral(P,A,B,C,D) = ( # Test if a point is inside a quadrilateral is_intriangle(P,A,B,D) || is_intriangle(P,B,C,D); ); # Check if two segments [p0,p1] and [q0,q1] intersect. # Return (0:false or 1:true). do_intersect(p0x,p0y,p1x,p1y,q0x,q0y,q1x,q1y) = ( _o1 = _doi_orientation(p0x,p0y,p1x,p1y,q0x,q0y); _o2 = _doi_orientation(p0x,p0y,p1x,p1y,q1x,q1y); _o3 = _doi_orientation(q0x,q0y,q1x,q1y,p0x,p0y); _o4 = _doi_orientation(q0x,q0y,q1x,q1y,p1x,p1y); (_o1!=_o2 && _o3!=_o4) || (!_o1 && _doi_on_segment(p0x,p0y,q0x,q0y,p1x,p1y)) || (!_o2 && _doi_on_segment(p0x,p0y,q1x,q1y,p1x,p1y)) || (!_o3 && _doi_on_segment(q0x,q0y,p0x,p0y,q1x,q1y)) || (!_o4 && _doi_on_segment(q0x,q0y,p1x,p1y,q1x,q1y)) ); _doi_on_segment(px,py,qx,qy,rx,ry) = ( inrange(qx,min(px,rx),max(px,rx)) && inrange(qy,min(py,ry),max(py,ry)); ); _doi_orientation(px,py,qx,qy,rx,ry) = ( _doi_orientation_val = (qy - py)*(rx - qx) - (qx - px)*(ry - qy); !_doi_orientation_val?0:_doi_orientation_val>0?1:2; ); length_spline(P0,T0,P1,T1) = ( # Compute the length of a spline P0-P1 with tangents T0,T1 _P0 = P0; _P1 = P1; _C = mul([ 2,-2,1,1,-3,3,-2,-1,0,0,1,0,1,0,0,0 ],[ _P0,_P1,T0,T1 ],2); _l = norm(_P1 - _P0); if (_l, _nl = _l + 1; _dt = 1/_l; while (_nl - _l>=0.01, _l = _nl; _nl = 0; __P0 = _P0; for (_t = 0, _t<=1, _t+=_dt, __P = mul([_t^3,_t^2,_t,1],_C,2); _nl+=norm(__P - __P0); __P0 = __P; ); _dt = 1/max(1,_nl); ); ); _l ); pexp(x) = ( # Good polynomial approximation of 'exp(-x^2)' for |x|<2 _pexp_x = abs(x); _pexp_x<2?-0.110353*_pexp_x^4 + 0.683221*_pexp_x^3 -1.17282*_pexp_x^2 + 1:0 ); proj(X,A,B) = ( # Projection of point X onto line (A,B) _AB = B - A; P = A + dot(X - A,_AB)/max(1e-8,dot(_AB,_AB))*_AB; ); # CIE DeltaE_2000 color difference function. # (code adapted from https://github.com/gfiumara/CIEDE2000). deltaE00(lab1,lab2) = ( _deltaE00_deg2rad(deg) = (deg*pi/180); unref(_deltaE00_lab1); _deltaE00_lab1 = lab1; unref(_deltaE00_lab2); _deltaE00_lab2 = lab2; unref(_deltaE00_k_L); const _deltaE00_k_L = 1; unref(_deltaE00_k_C); const _deltaE00_k_C = 1; unref(_deltaE00_k_H); const _deltaE00_k_H = 1; unref(_deltaE00_deg360inrad); const _deltaE00_deg360inrad = _deltaE00_deg2rad(360); unref(_deltaE00_deg180inrad); const _deltaE00_deg180inrad = _deltaE00_deg2rad(180); unref(_deltaE00_pow25to7); const _deltaE00_pow25to7 = 25^7; # Step 1 _deltaE00_C1 = norm(_deltaE00_lab1[1,2]); _deltaE00_C2 = norm(_deltaE00_lab2[1,2]); _deltaE00_barC = (_deltaE00_C1 + _deltaE00_C2)/2; _deltaE00_G = 0.5*(1 - sqrt(_deltaE00_barC^7/(_deltaE00_barC^7 + _deltaE00_pow25to7))); _deltaE00_a1prime = (1 + _deltaE00_G)*_deltaE00_lab1[1]; _deltaE00_a2prime = (1 + _deltaE00_G)*_deltaE00_lab2[1]; _deltaE00_Cprime1 = norm(_deltaE00_a1prime,_deltaE00_lab1[2]); _deltaE00_Cprime2 = norm(_deltaE00_a2prime,_deltaE00_lab2[2]); !_deltaE00_lab1[2] && !_deltaE00_a1prime?( _deltaE00_hprime1 = _deltaE00_hprime2 = 0; ):( _deltaE00_hprime1 = atan2(_deltaE00_lab1[2],_deltaE00_a1prime); _deltaE00_hprime1<0?(_deltaE00_hprime1+=_deltaE00_deg360inrad); !_deltaE00_lab2[2] && !_deltaE00_a2prime?( _deltaE00_hprime2 = 0; ):( _deltaE00_hprime2 = atan2(_deltaE00_lab2[2],_deltaE00_a2prime); _deltaE00_hprime2<0?(_deltaE00_hprime2+=_deltaE00_deg360inrad); ); ); # Step 2 _deltaE00_deltaLprime = _deltaE00_lab2[0] - _deltaE00_lab1[0]; _deltaE00_deltaCprime = _deltaE00_Cprime2 - _deltaE00_Cprime1; _deltaE00_CprimeProduct = _deltaE00_Cprime1*_deltaE00_Cprime2; !_deltaE00_CprimeProduct?( _deltaE00_deltahprime = 0; ):( _deltaE00_deltahprime = _deltaE00_hprime2 - _deltaE00_hprime1; _deltaE00_deltahprime<-_deltaE00_deg180inrad?( _deltaE00_deltahprime += _deltaE00_deg360inrad; ):_deltaE00_deltahprime>_deltaE00_deg180inrad?( _deltaE00_deltahprime -= _deltaE00_deg360inrad; ); ); _deltaE00_deltaHprime = 2*sqrt(_deltaE00_CprimeProduct)*sin(_deltaE00_deltahprime/2); # Step 3 _deltaE00_barLprime = (_deltaE00_lab1[0] + _deltaE00_lab2[0])/2; _deltaE00_barCprime = (_deltaE00_Cprime1 + _deltaE00_Cprime2)/2; _deltaE00_hprimeSum = _deltaE00_hprime1 + _deltaE00_hprime2; !(_deltaE00_Cprime1*_deltaE00_Cprime2)?( _deltaE00_barhprime = _deltaE00_hprimeSum; ):( abs(_deltaE00_hprime1 - _deltaE00_hprime2)<=_deltaE00_deg180inrad?( _deltaE00_barhprime = _deltaE00_hprimeSum/2; ):( _deltaE00_hprimeSum<_deltaE00_deg360inrad?( _deltaE00_barhprime = (_deltaE00_hprimeSum + _deltaE00_deg360inrad)/2; ):( _deltaE00_barhprime = (_deltaE00_hprimeSum - _deltaE00_deg360inrad)/2; ); ); ); _deltaE00_T = 1.0 - (0.17*cos(_deltaE00_barhprime - _deltaE00_deg2rad(30))) + (0.24*cos(2*_deltaE00_barhprime)) + (0.32*cos(3*_deltaE00_barhprime + _deltaE00_deg2rad(6))) - (0.2*cos(4*_deltaE00_barhprime - _deltaE00_deg2rad(63))); _deltaE00_deltaTheta = _deltaE00_deg2rad(30)* exp(-((_deltaE00_barhprime - _deltaE00_deg2rad(275)) / _deltaE00_deg2rad(25))^2); _deltaE00_R_C = 2*sqrt(_deltaE00_barCprime^7/(_deltaE00_barCprime^7 + _deltaE00_pow25to7)); _deltaE00_S_L = 1 + 0.015*(_deltaE00_barLprime - 50)^2/sqrt(20 + (_deltaE00_barLprime - 50)^2); _deltaE00_S_C = 1 + 0.045*_deltaE00_barCprime; _deltaE00_S_H = 1 + 0.015*_deltaE00_barCprime*_deltaE00_T; _deltaE00_R_T = -sin(2*_deltaE00_deltaTheta)* _deltaE00_R_C; _deltaE00_den = _deltaE00_k_H*_deltaE00_S_H; sqrt((_deltaE00_deltaLprime/(_deltaE00_k_L*_deltaE00_S_L))^2 + (_deltaE00_deltaCprime/(_deltaE00_k_C*_deltaE00_S_C))^2 + (_deltaE00_deltaHprime/_deltaE00_den)^2 + _deltaE00_R_T*_deltaE00_deltaCprime/(_deltaE00_k_C*_deltaE00_S_C)* _deltaE00_deltaHprime/_deltaE00_den); ); # Functions to manage curve motion with edgels. # k: index of image. # P: input edgel = [ x,y,n ] where 'n' is the canonical normal, can be { 0:right | 1:bottom | 2:left | 3:top }. _next_edgel4(k,P,op) = ( ref(P#[0],_u); ref(P#[1],_v); !P#[2]?(i(#k,_u,_nv = _v + 1,0,0)#op?[ _u,_v,1 ]:i(#k,_nu = _u + 1,_nv,0,0)#op?[ _u,_nv,0 ]:[ _nu,_nv,3 ]): P#[2]==1?(i(#k,_pu = _u - 1,_v,0,0)#op?[ _u,_v,2 ]:i(#k,_pu,_nv = _v + 1,0,0)#op?[ _pu,_v,1 ]:[ _pu,_nv,0 ]): P#[2]==2?(i(#k,_u,_pv = _v - 1,0,0)#op?[ _u,_v,3 ]:i(#k,_pu = _u - 1,_pv,0,0)#op?[ _u,_pv,2 ]:[ _pu,_pv,1 ]): (i(#k,_nu = _u + 1,_v,0,0)#op?[ _u,_v,0 ]:i(#k,_nu,_pv = _v - 1,0,0)#op?[ _nu,_v,3 ]:[ _nu,_pv,2 ]); ); next_edgel4_fg(k,P,fg) = _next_edgel4(#k,#P,!=fg); # Next edgel, 4-connexity (foreground value specified) next_edgel4_bg(k,P,bg) = _next_edgel4(#k,#P,==bg); # Next edgel, 4-connexity (background value specified) _previous_edgel4(k,P,op) = ( ref(P#[0],_u); ref(P#[1],_v); !P#[2]?(i(#k,_u,_pv = _v - 1,0,0)#op?[ _u,_v,3 ]:i(#k,_nu = _u + 1,_pv,0,0)#op?[ _u,_pv,0 ]:[ _nu,_pv,1 ]): P#[2]==1?(i(#k,_nu = _u + 1,_v,0,0)#op?[ _u,_v,0 ]:i(#k,_nu,_nv = _v + 1,0,0)#op?[ _nu,_v,1 ]:[ _nu,_nv,2 ]): P#[2]==2?(i(#k,_u,_nv = _v + 1,0,0)#op?[ _u,_v,1 ]:i(#k,_pu = _u - 1,_nv,0,0)#op?[ _u,_nv,2 ]:[ _pu,_nv,3 ]): (i(#k,_pu = _u - 1,_v,0,0)#op?[ _u,_v,2 ]:i(#k,_pu,_pv = _v - 1,0,0)#op?[ _pu,_v,3 ]:[ _pu,_pv,0 ]); ); previous_edgel4_fg(k,P,fg) = _previous_edgel4(#k,#P,!=fg); # Previous edgel, 4-connexity (foreground value specified) previous_edgel4_bg(k,P,bg) = _previous_edgel4(#k,#P,==bg); # Previous edgel, 4-connexity (background value specified) _next_edgel8(k,P,op) = ( ref(P#[0],_u); ref(P#[1],_v); !P#[2]?(i(#k,_nu = _u + 1,_nv = _v + 1,0,0)#op?[ _nu,_nv,3 ]:i(#k,_u,_nv,0,0)#op?[ _u,_nv,0 ]:[ _u,_v,1 ]): P#[2]==1?(i(#k,_pu = _u - 1,_nv = _v + 1,0,0)#op?[ _pu,_nv,0 ]:i(#k,_pu,_v,0,0)#op?[ _pu,_v,1 ]:[ _u,_v,2 ]): P#[2]==2?(i(#k,_pu = _u - 1,_pv = _v - 1,0,0)#op?[ _pu,_pv,1 ]:i(#k,_u,_pv,0,0)#op?[ _u,_pv,2 ]:[ _u,_v,3 ]): (i(#k,_nu = _u + 1,_pv = _v - 1,0,0)#op?[ _nu,_pv,2 ]:i(#k,_nu,_v,0,0)#op?[ _nu,_v,3 ]:[ _u,_v,0 ]); ); next_edgel8_fg(k,P,fg) = _next_edgel8(#k,#P,==fg); # Next edgel, 8-connexity (foreground value specified) next_edgel8_bg(k,P,bg) = _next_edgel8(#k,#P,!=bg); # Next edgel, 8-connexity (background value specified) _previous_edgel8(k,P,op) = ( ref(P#[0],_u); ref(P#[1],_v); !P#[2]?(i(#k,_nu = _u + 1,_pv = _v - 1,0,0)#op?[ _nu,_pv,1 ]:i(#k,_u,_pv,0,0)#op?[ _u,_pv,0 ]:[ _u,_v,3 ]): P#[2]==1?(i(#k,_nu = _u + 1,_nv = _v + 1,0,0)#op?[ _nu,_nv,2 ]:i(#k,_nu,_v,0,0)#op?[ _nu,_v,1 ]:[ _u,_v,0 ]): P#[2]==2?(i(#k,_pu = _u - 1,_nv = _v + 1,0,0)#op?[ _pu,_nv,3 ]:i(#k,_u,_nv,0,0)#op?[ _u,_nv,2 ]:[ _u,_v,1 ]): (i(#k,_pu = _u - 1,_pv = _v - 1,0,0)#op?[ _pu,_pv,0 ]:i(#k,_pu,_v,0,0)#op?[ _pu,_v,3 ]:[ _u,_v,2 ]); ); previous_edgel8_fg(k,P,fg) = _previous_edgel8(#k,#P,==fg); # Previous edgel, 8-connexity (foreground value specified) previous_edgel8_bg(k,P,bg) = _previous_edgel8(#k,#P,!=bg); # Previous edgel, 8-connexity (background value specified) " #@cli mad #@cli : Return the MAD (Maximum Absolute Deviation) of the last selected image. #@cli : The MAD is defined as MAD = med_i|x_i-med_j(x_j)| mad : if $! +-. {ic} abs. u {1.4826*ic} rm. else u 0 fi #@cli max_w #@cli : Return the maximal width between selected images. max_w : _minmax_whds max,0,1 #@cli max_h #@cli : Return the maximal height between selected images. max_h : _minmax_whds max,1,1 #@cli max_d #@cli : Return the maximal depth between selected images. max_d : _minmax_whds max,2,1 #@cli max_s #@cli : Return the maximal spectrum between selected images. max_s : _minmax_whds max,3,1 #@cli max_wh #@cli : Return the maximal wxh size of selected images. max_wh : _minmax_whds max,0,2 #@cli max_whd #@cli : Return the maximal wxhxd size of selected images. max_whd : _minmax_whds max,0,3 #@cli max_whds #@cli : Return the maximal wxhxdxs size of selected images. max_whds : _minmax_whds max,0,4 #@cli median_vectors #@cli : Return the median vector value of the last selected image (median computed channel by channel) median_vectors : u {"expr('med(crop(#-1,0,0,0,y,w#-1,h#-1,d#-1,1))',1,s#-1)"} #@cli min_w #@cli : Return the minimal width between selected images. min_w : _minmax_whds min,0,1 #@cli min_h #@cli : Return the minimal height between selected images. min_h : _minmax_whds min,1,1 #@cli min_d #@cli : Return the minimal depth between selected images. min_d : _minmax_whds min,2,1 #@cli min_s #@cli : Return the minimal s size of selected images. min_s : _minmax_whds min,3,1 #@cli min_wh #@cli : Return the minimal wxh size of selected images. min_wh : _minmax_whds min,0,2 #@cli min_whd #@cli : Return the minimal wxhxd size of selected images. min_whd : _minmax_whds min,0,3 #@cli min_whds #@cli : Return the minimal wxhxdxs size of selected images. min_whds : _minmax_whds min,0,4 _minmax_whds : u {" mw = w; mh = h; md = d; ms = s; repeat (l,k, mw = $1(mw,w#k); mh = $1(mh,h#k); md = $1(md,d#k); ms = $1(ms,s#k); ); ([mw,mh,md,ms])[$2,$3]"} #@cli name2color : name #@cli : Return the R,G,B color that matches the specified color name. name2color : strvar "$1" name=${} _color2name names={`"s = ['"{n}"']; m = !(s-_'~')*(_'_'-_'~'); s+=m; lowercase(s)"`} l. { s x => $names u {$name,^} rm onfail rm error[0--3] "Command 'name2color': Unknown color '$1'." } #@cli nmd : eq. to 'named' : (+) #@cli named : _mode,"name1","name2",... : (+) #@cli : Return the set of indices corresponding to images of the selection with specified names. #@cli : After this command returns, the status contains a list of indices (unsigned integers), #@cli : separated by commas (or an empty string if no images with those names have been found). #@cli : (eq. to 'nmd'). #@cli : 'mode' can be { 0:all indices (default) | 1:lowest index | 2:highest index | \ # 3:all indices (case insensitive) | 4:lowest index (case insensitive) | 5:highest index (case insensitive)} #@cli narg : arg1,arg2,...,argN #@cli : Return number of specified arguments. narg : u $# #@cli normalize_filename : filename #@cli : Return a "normalized" version of the specified filename, without spaces and capital letters. normalize_filename : ('"$1"') f. "i>=65 && i<=90?i+32:i==32?95:i" u {t} rm. #@cli oct : octal_int1,... #@cli : Print specified octal integers into their binary, decimal, hexadecimal and string representations. oct : dec=${oct2dec\ ${^0}} e[^-1] "Convert octal integer"${arg0\ ($#>1),"",s}" '${^0}' to binary '"${dec2bin\ $dec}"', decimal '"$dec"', hexadecimal '"${dec2hex\ $dec}"' and string '"${dec2str\ $dec}"'." #@cli oct2dec : octal_int1,... #@cli : Convert specified octal integers into their decimal representations. oct2dec : $=arg res,sep= repeat $# { res=$res$sep${_$0\ ${arg{$>+1}}} sep=, } u $res _oct2dec : u {"str = v2s(abs($1)); for (k = val = 0, str[k], ++k, c = str[k]; (val<<=3)+=(c>=_'0' && c<=_'7'?c - _'0':nan); isnan(val)?break() ); sign($1)*val"} #gmic ovh2stats : logfile_globname #gmic : Generate statistics on the usage of G'MIC-Qt from OVH server logfiles. #gmic : logfiles must be located in current directory. #gmic : Default value: 'logfile_globname=gmic.eu-*-*-*.log.gz" ovh2stats : skip "${1=gmic.eu-*-*-*.log.gz}" e[^-1] "Generate statistics on the usage of G'MIC-Qt from OVH server logfiles '$1'." use_vt100 # Load log files. files "$1" files=${} e "* Load log files." if !narg(${}) error[0--2] "No OVH logfiles matching name '$1' have been found." fi repeat narg(${}) { arg0 $>,$files file=${} day,month,year:="str = ['"$file"']; t1 = find(str,_'-') + 1; t2 = find(str,_'-',t1) + 1; t3 = find(str,_'-',t2) + 1; t1>0 && t2>t1 && t3>t2?( day = s2v(str,t1); month = s2v(str,t2); year = s2v(str,t3); [ day,month,year ]; ):[0,0,0]" if !$day&&!$month&&!$year day,month,year=n.a fi e " > "$year/$month/$day:" "$_vt100_g$file$_vt100_n it $file # Check if data is gzipped. if "find(#-1,['G'MIC-Qt for '])<0" rm. i raw:$file,uint8 o. raw:${-path_tmp}gmic.gz,uint8 rm. x "gunzip -f "${-path_tmp}gmic.gz it ${-path_tmp}gmic fi => ${year}_${month}_${day} } # Keep only lines containing 'G'MIC-Qt'. foreach { nm={n} s -,{'\n'} eval " str = ['G'MIC-Qt']; to_keep = vectorl(); p = 0; repeat (l,k, find(#k,str)>=0?(to_keep[p++] = k)); store('to_keep',to_keep,p); run('$to_keep k[{^}]')" if $!>1 i[1--2] ('\n') fi a y => $nm } # Analyze log files. e "\n* Analyze log files (day by day)." +_ovh2stats # Display stats, day by day. e "\n* Analyze log files (all days)." a y _ovh2stats # Display stats, all days. _ovh2stats : v + N=$! foreach { ('{n}') replace. {'_'},{'^'} ({t}) date={`[v2s(i0,0,4),_'/',i1<10?[_'0',i1+_'0']:v2s(i1,0,2),_'/',i2<10?[_'0',i2+_'0']:v2s(i2,0,2)]`} s -,{'\n'} # Name each image by the IP address. eval " name = vector256(); repeat (l,k, p = find(#k,_' '); copy(name,i[#k,0],p); name[p] = 0; run('name[',k,'] ',name); )" # Discard IP duplicates. eval " const N = "$!"; ref(vector(#2*N),tab); repeat (N,k, ref(name(#k,256),nam); for (i = j = 0, i=0?++gimp: find(str,'krita')>=0?++krita: find(str,'8bf')>=0?++_8bf: find(str,'paint.net')>=0?++paintdotnet: find(str,'digikam')>=0?++digikam: ++standalone; find(str,'windows')>=0?++windows: find(str,'linux')>=0?++linux: find(str,'mac os')>=0?++macos: find(str,'bsd')>=0?++bsd: ++unknown; (eos = find(str,0))<0?(eos = size(str)); space = find(str,_' ',eos,-1); ver = sum(([ str[space + 1],str[space + 3],str[space + 5] ] - _'0')*[ 100,10,1 ]); ++versions[ver]; find(str,'_pre#')>=0?++prereleases; ++ips; ); store('features', [ips,gimp,krita,_8bf,paintdotnet,digikam,standalone,windows,linux,macos,bsd,unknown,prereleases]); store('versions',versions)" rm $features ips,gimp,krita,_8bf,paintdotnet,digikam,standalone,windows,linux,macos,bsd,unknown,prereleases={^} $versions a y if $N>1 e " > "$date": "\ ${_vt100_b}"Unique IPs"${_vt100_n}" = "$ips" (100%)," e " "\ ${_vt100_m}${_vt100_b}"GIMP"${_vt100_n}" = "$gimp" ("{_round($gimp/$ips*100,0.1)}"%), "\ ${_vt100_c}${_vt100_b}"Krita"${_vt100_n}" = "$krita" ("{_round($krita/$ips*100,0.1)}"%), "\ ${_vt100_g}${_vt100_b}"8bf"${_vt100_n}" = "$_8bf" ("{_round($_8bf/$ips*100,0.1)}"%), "\ ${_vt100_r}${_vt100_b}"Paint.NET"${_vt100_n}" = "$paintdotnet" ("{_round($paintdotnet/$ips*100,0.1)}"%), "\ ${_vt100_m}${_vt100_b}"Digikam"${_vt100_n}" = "$digikam" ("{_round($digikam/$ips*100,0.1)}"%), "\ ${_vt100_c}${_vt100_b}"Standalone"${_vt100_n}" = "$standalone" ("{_round($standalone/$ips*100,0.1)}"%)." e " "\ ${_vt100_r}${_vt100_b}"Windows"${_vt100_n}" = "$windows" ("{_round($windows/$ips*100,0.1)}"%), "\ ${_vt100_g}${_vt100_b}"Linux"${_vt100_n}" = "$linux" ("{_round($linux/$ips*100,0.1)}"%), "\ ${_vt100_m}${_vt100_b}"Mac OS"${_vt100_n}" = "$macos" ("{_round($macos/$ips*100,0.1)}"%), "\ ${_vt100_c}${_vt100_b}"BSD"${_vt100_n}" = "$bsd" ("{_round($bsd/$ips*100,0.1)}"%), "\ ${_vt100_c}${_vt100_b}"Unknown"${_vt100_n}" = "$unknown" ("{_round($unknown/$ips*100,0.1)}"%)." e " "\ ${_vt100_b}"Prereleases"${_vt100_n}" = "$prereleases" ("{_round($prereleases/$ips*100,0.1)}"%)." _ovh2stats_versions fi } + / $N round. +z. 0,0,12,0 ips,gimp,krita,_8bf,paintdotnet,digikam,standalone,windows,linux,macos,bsd,unknown,prereleases={^} rm. summary0,summary1="Total: ","Average: " e " > "${summary{$N>1}}\ ${_vt100_b}"Unique IPs"${_vt100_n}" = "$ips" (100%)," e " "\ ${_vt100_m}${_vt100_b}"GIMP"${_vt100_n}" = "$gimp" ("{_round($gimp/$ips*100,0.1)}"%), "\ ${_vt100_c}${_vt100_b}"Krita"${_vt100_n}" = "$krita" ("{_round($krita/$ips*100,0.1)}"%), "\ ${_vt100_g}${_vt100_b}"8bf"${_vt100_n}" = "$_8bf" ("{_round($_8bf/$ips*100,0.1)}"%), "\ ${_vt100_r}${_vt100_b}"Paint.NET"${_vt100_n}" = "$paintdotnet" ("{_round($paintdotnet/$ips*100,0.1)}"%), "\ ${_vt100_m}${_vt100_b}"Digikam"${_vt100_n}" = "$digikam" ("{_round($digikam/$ips*100,0.1)}"%), "\ ${_vt100_c}${_vt100_b}"Standalone"${_vt100_n}" = "$standalone" ("{_round($standalone/$ips*100,0.1)}"%)." e " "\ ${_vt100_r}${_vt100_b}"Windows"${_vt100_n}" = "$windows" ("{_round($windows/$ips*100,0.1)}"%), "\ ${_vt100_g}${_vt100_b}"Linux"${_vt100_n}" = "$linux" ("{_round($linux/$ips*100,0.1)}"%), "\ ${_vt100_m}${_vt100_b}"Mac OS"${_vt100_n}" = "$macos" ("{_round($macos/$ips*100,0.1)}"%), "\ ${_vt100_c}${_vt100_b}"BSD"${_vt100_n}" = "$bsd" ("{_round($bsd/$ips*100,0.1)}"%), "\ ${_vt100_c}${_vt100_b}"Unknown"${_vt100_n}" = "$unknown" ("{_round($unknown/$ips*100,0.1)}"%)." e " "\ ${_vt100_b}"Prereleases"${_vt100_n}" = "$prereleases" ("{_round($prereleases/$ips*100,0.1)}"%)." _ovh2stats_versions rm _ovh2stats_versions : v + +rows. 100% l. { ips:=is +f x a y sort -,x eo0:=min(9,find(crop(),0)) if $eo0 str= v=0 c= for i($v,0)>0" && "$v<9 { str.=$c${_vt100_b}{i($v,1)}${_vt100_n}" ("{i($v,0)}": "{_round(i($v,0)/$ips*100,0.1)}"%)" v+=1 c=", " } e " "${_vt100_c}${_vt100_b}"Version"${_vt100_n}" = "$str".\n" fi rm } #@cli padint : number,_size>0 #@cli : Return a integer with 'size' digits (eventually left-padded with '0'). padint : check "isint($1)" skip ${2=4} u "" repeat $2 { u ${}{int($1/10^$<)%10} } #@cli path_cache #@cli : Return a path to store G'MIC data files for one user (whose value is OS-dependent). path_cache : if !narg({'$_path_cache'}) _path_cache=$_path_rc if ['$GMIC_CACHE_PATH']!=0 _patch_cache=$GMIC_CACHE_PATH elif !${-is_windows} if isdir(['{/$HOME/.cache}']) _path_cache=$HOME/.cache/gmic/ elif ['$XDG_CACHE_HOME']!=0 _path_cache=$XDG_CACHE_HOME/gmic/ fi if !isdir('{/$_path_cache}') x "mkdir -p "$_path_cache fi fi fi u $_path_cache #@cli path_current #@cli : Return current folder from where G'MIC has been run. path_current : if !${-is_windows} filename=${-path_tmp}gmic_pwd l[] { x "pwd > "$filename it $filename autocrop {'\n'} autocrop {'" "'} u {t}/ rm onfail u "./" } else u "./" fi #@cli path_gimp #@cli : Return a path to store GIMP configuration files for one user (whose value is OS-dependent). path_gimp : if !narg({'$_path_gimp'}) if narg({'${GIMP2_DIRECTORY}'}) _path_gimp=${GIMP2_DIRECTORY} elif narg({'${USERPROFILE}'}) _path_gimp=${USERPROFILE} elif narg({'${HOME}'}) _path_gimp=${HOME} fi if ${-is_windows} sep={`92`} else sep=/ fi if isdir(['{/$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.10}']) _path_gimp=$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.10${sep} elif isdir(['{/$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.8}']) _path_gimp=$_path_gimp${sep}AppData${sep}Roaming${sep}GIMP${sep}2.8${sep} elif isdir(['{/$_path_gimp${sep}.config${sep}GIMP${sep}2.10}']) _path_gimp=$_path_gimp${sep}.config${sep}GIMP${sep}2.10${sep} elif isdir(['{/$_path_gimp${sep}.gimp-2.8}']) _path_gimp=$_path_gimp${sep}.gimp-2.8${sep} elif isdir(['{/$_path_gimp${sep}.gimp-2.6}']) _path_gimp=$_path_gimp${sep}.gimp-2.6${sep} else _path_gimp=${-path_tmp} fi fi u $_path_gimp #@cli path_tmp #@cli : Return a path to store temporary files (whose value is OS-dependent). path_tmp : if !narg({'$_path_tmp'}) if narg({'${TMP}'}) _path_tmp=${TMP} elif narg({'${TEMP}'}) _path_tmp=${TEMP} elif narg({'${TMPDIR}'}) _path_tmp=${TMPDIR} elif narg({'${HOME}'}) _path_tmp="/tmp" fi if ${-is_windows} _path_tmp=$_path_tmp{`92`} else _path_tmp=$_path_tmp/ fi fi u $_path_tmp #@cli remove_copymark #@cli : Remove copymark suffix in names of selected images. remove_copymark : foreach { => {`" nm = ['"{n}'"]; const siz = size(nm); i = find(nm,'_c',siz - 1,-1); inrange(i,0,siz - 3) && inrange(nm[i + 2],_'1',_'9')?( ext = ['"{x}"']; size(ext)?copy(nm[i],[_'.',ext,0]):(nm[i] = 0); ); nm;"`} } # render_donations: Parse the PayPal .CSV file and generate 'latest-months' donation gfx. # $1 = filename.csv # $2 = number of latest months. render_donations : skip "${1="$HOME"/work/src/private_tschumperle/gmic_donations.csv}",${2=0} use_vt100 nb_months:=$2?$2:4 l[] { # Parse CSV data. "$1" # eur2usd:=1/0.7988 # Change rate from EUR to USD (Paypal conversion rate in 2020). # eur2usd:=1/0.8568 # Change rate from EUR to USD (Paypal conversion rate in 2021). # eur2usd:=1/0.873824 # Change rate from EUR to USD (Paypal conversion rate in 2023/01). eur2usd:=1/0.927900 # Change rate from EUR to USD (Paypal conversion rate in 2024/01). nb_entries:=h repeat $nb_entries { e=$> date_$e={`I(0,$e)`} name_$e={`I(1,$e)`} type_$e={`I(2,$e)`} currency_$e={`lowercase(I(3,$e))`} donation_$e={{`I(4,$e)`}} charge_paypal_$e={{`I(5,$e)`}} charge_lila_$e={{`I(6,$e)`}} mail_$e={`I(7,$e)`} message_$e={`I(8,$e)`} # Deduced values. l[] { ('${date_$e}') s -,{'/'} day_$e,month_$e,year_$e={0,t},{1,t},{2,t} rm edate_$e:=${day_$e}+100*(${month_$e}+100*${year_$e}) } donation_charged_$e:=${donation_$e}+${charge_paypal_$e}+${charge_lila_$e} if ['${currency_$e}']=='eur' donation_eur_$e=${donation_$e} donation_charged_eur_$e=${donation_charged_$e} donation_usd_$e:=${donation_$e}*$eur2usd donation_charged_usd_$e:=${donation_charged_$e}*$eur2usd else donation_usd_$e=${donation_$e} donation_charged_usd_$e=${donation_charged_$e} donation_eur_$e:=${donation_$e}/$eur2usd donation_charged_eur_$e:=${donation_charged_$e}/$eur2usd fi # Print infos. if ${donation_$e}>=10 col=$_vt100_r else col= fi e "- ["$_vt100_c$_vt100_b"#"$e$_vt100_n" - "$_vt100_c${date_$e}$_vt100_n"] "\ $col$_vt100_b{_${donation_$e}}" "${currency_$e}$_vt100_n\ " (paypal: "{_${charge_paypal_$e}}" "${currency_$e},\ " lila: "{_${charge_lila_$e}}" "${currency_$e}" ->"\ " "{_${donation_charged_$e}}" "${currency_$e}" ="\ " "{_${donation_charged_eur_$e}}" eur),"\ " from "${name_$e}" ("${mail_$e}") : '"$_vt100_b${message_$e}$_vt100_n"'" } rm # Compute image for donations of the last months. e "" all_eur,all_usd,all_charged_eur,all_charged_usd,asep_eur,asep_usd= repeat $nb_months { edate:=y=date(0);m=date(1)-1-$>;while(m<=0,--y;m+=12);100*y+m s_month=${"arg "$edate%100",January,February,March,April,May,June,July,August,\ September,October,November,December"} s_year:=int($edate/100) month_eur,month_charged_eur,month_usd,month_charged_usd,msep_eur,msep_usd= nb_sponsors=0 repeat $nb_entries { e=$> if $edate==int(${edate_$e}/100) currency=${currency_$e} val_eur=${donation_eur_$e} val_charged_eur=${donation_charged_eur_$e} val_usd=${donation_usd_$e} val_charged_usd=${donation_charged_usd_$e} nb_sponsors+=1 if ['${currency_$e}']=='eur' all_eur.=$asep_eur$val_eur all_charged_eur.=$asep_eur$val_charged_eur month_eur.=$msep_eur$val_eur month_charged_eur.=$msep_eur$val_charged_eur asep_eur=, msep_eur=, else all_usd.=$asep_usd$val_usd all_charged_usd.=$asep_usd$val_charged_usd month_usd.=$msep_usd$val_usd month_charged_usd.=$msep_usd$val_charged_usd asep_usd=, msep_usd=, fi fi } month_sum_eur:=sum(0$month_eur)+sum(0$month_usd)/$eur2usd month_sum_charged_eur:=sum(0$month_charged_eur)+sum(0$month_charged_usd)/$eur2usd e "* "$_vt100_c$_vt100_b$s_month" "$s_year": "$_vt100_n\ $_vt100_b{_$month_sum_eur}" eur"$_vt100_n" (charged: "{_$month_sum_charged_eur}" eur)"\ " = "{_sum(0$month_eur)}" eur"$_vt100_n" (charged: "{_sum(0$month_charged_eur)}" eur)"\ " + "{_sum(0$month_usd)}" usd (charged: "{_sum(0$month_charged_usd)}" usd)" 600,30,1,3,'x<$month_sum_eur?[139,181,173]+(y>h/2?0:40):[220,230,240]' r. 500,30,1,3 c. 0,255 shape_circle 24 s. x,2 s[-2,-1] y,2 ri[-4] [-5],0,1,0,0 ri[-3] [-5],0,1,0,1 ri[-2] [-5],0,1,1,0 ri[-1] [-5],0,1,1,1 min[-4--1] channels. 0 *.. . *. 255 a[-2,-1] c r. {[w,h]+4},1,100%,0,0,0.5,0.5 sh. 100% dilate_circ. 3 b. 1 rm. t. $s_month" "$s_year,0.02~,0.5~,24,0.5,0,0,0,255 t. {_round($month_sum_eur)}" \37 = "{_round($month_sum_eur*$eur2usd)}" $",0.5~,0.5~,24,1,0,0,0,255 t. $nb_sponsors" sponsor"${"if "$nb_sponsors"!=1 u s else u \"\" fi"},0.97~,0.5~,24,0.5,0,0,0,255 } rv all_max:=max($all_eur,[$all_usd]/$eur2usd) all_min:=min($all_eur,[$all_usd]/$eur2usd) all_sum_eur:=sum($all_eur) all_sum_usd:=sum($all_usd) all_sum_charged_eur:=sum($all_charged_eur) all_sum_charged_usd:=sum($all_charged_usd) all_avg:=avg($all_eur,[$all_usd]/$eur2usd) all_med:=med($all_eur,[$all_usd]/$eur2usd) 0 t. "Avg: "{_max(0.1,round($all_avg,0.1))}"\37 / "\ "Med: "{_max(0.1,round($all_med,0.1))}"\37 / "\ "Min: "{_max(0.1,round($all_min,0.1))}"\37 / "\ "Max: "{_max(0.1,round($all_max,0.1))}"\37",0,0,24,1,1 *. 200 i[-2] 100%,100%,1,3 a[-2,-1] c rows -5,100% a y,0.5 rs 480 if !$2 o $HOME/work/src/gmic/html/img/donations_latest_months.png fi } total_eur:=$all_sum_eur+$all_sum_usd/$eur2usd total_charged_eur:=$all_sum_charged_eur+$all_sum_charged_usd/$eur2usd e "\n==> "$_vt100_c${_vt100_b}"TOTAL : "$_vt100_n\ $_vt100_b{_floor($total_eur)}" eur"$_vt100_n" (charged : "{_floor($total_charged_eur)}" eur)"\ " = "{_floor($all_sum_eur)}" eur (charged: "{_floor($all_sum_charged_eur)}" eur)"\ " + "{_floor($all_sum_usd)}" usd (charged: "{_floor($all_sum_charged_usd)}" usd)\n" # Generate report image for Twitter. sp gmicky,{h} l[-2,-1] { rgb:=I(w-1) to_rgba rv frame xy,10,0,0,0,0 drgba $rgb a x shape_heart 82 +r. 100%,100%,1,3 hsv2rgb. rv[-2,-1] *. 255 a[-2,-1] c r. ..,..,1,100%,0,0,0.23,0.15 drop_shadow. 3,3,2,0,0 blend alpha,0.7 rs 500 if !$2 o $HOME/Desktop/donations_latest_months.jpg,85 rm return fi } # Generate coin graphics. l[] { $HOME/work/src/gmic/html/img/icon_coin.png N=0 text$N,r$N,g$N,b$N,file$N="2 \37",32,48,32,"don_2eur.png" N+=1 text$N,r$N,g$N,b$N,file$N="5 \37",200,200,200,"don_5eur.png" N+=1 text$N,r$N,g$N,b$N,file$N="10 \37",190,100,100,"don_10eur.png" N+=1 text$N,r$N,g$N,b$N,file$N="+ \37",255,128,0,"don_moreeur.png" N+=1 text$N,r$N,g$N,b$N,file$N="2 $",32,48,32,"don_2usd.png" N+=1 text$N,r$N,g$N,b$N,file$N="5 $",200,200,200,"don_5usd.png" N+=1 text$N,r$N,g$N,b$N,file$N="10 $",190,100,100,"don_10usd.png" N+=1 text$N,r$N,g$N,b$N,file$N="+ $",255,128,0,"don_moreusd.png" N+=1 repeat $N { +l[0] { frame. xy,10,0,0,0,0 sh. 0,2 (${r$>}^${g$>}^${b$>}) rgb2hsl[-2,-1] H,S:=R,G rm. l. { s c -[0] {ia#0-$H} %[0] 360 -[1] {ia#1-$S} +[2] 0.1 /[1] 2 c[1,2] 0,1 a c } hsl2rgb. rm. sh. 100% dilate_circ. 10 rm. 100%,100% t. ${text$>},0.5~,0.5~,45%,1,255 dilate_circ. 10 +dilate_circ. 15 to_rgb.. a[-2,-1] c blend[-2,-1] alpha drop_shadow. 1,1 rs. 48 frame x,10,0,0,0,0 frame y,5,0,0,0,0 outfile=$HOME/work/src/gmic/html/img/${file$>} if !isfile('$outfile') o. $outfile fi rm. } } rm[0] } #@cli reset #@cli : Reset global parameters of the interpreter environment. reset : e[^-1] "Reset global parameters of the interpreter environment." db3d m3d md3d f3d l3d sl3d ss3d #@cli rgb #@cli : Return a random int-valued RGB color. rgb : u {v([255,255,255])} RGB : rgb #@cli rgba #@cli : Return a random int-valued RGBA color. rgba : u {v([255,255,255,255])} RGBA : rgba #@cli shell_cols #@cli : Return the estimated number of columns of the current shell. shell_cols : if ${-is_windows}" || "${-is_macos} u 80 else filename_rand filename=${} l[] { cols=80 x "tput cols > "$filename it $filename if isint({t}) cols={{t}} fi rm onfail cols=80 rm } delete $filename u $cols fi #@cli size_value #@cli : Return the size (in bytes) of image values. size_value : u {str=['$_pixeltype'];str=='float32'?4:str=='float64'?8:0} #@cli std_noise #@cli : Return the estimated noise standard deviation of the last selected image. std_noise : if $! +laplacian. -. {ic} abs. u {1.4826*ic/sqrt(d==1?20:42)} rm. else u 0 fi #@cli str : string #@cli : Print specified string into its binary, octal, decimal and hexadecimal representations. str : skip "$1" dec={'"$*"'} e[^-1] "Convert string '$*' to binary '"${dec2bin\ $dec}"', octal '"${dec2oct\ $dec}"', decimal '"$dec"' and hexadecimal '"${dec2hex\ $dec}"'." #@cli strbuffer : buffer_size #@cli : Return a string describing a size for the specified buffer size. strbuffer : u {`"const limit = 10000; unit = vector4(); siz = $1; siz=0"} #@cli strclut : "string" #@cli : Return simplified version of the specified string that can be used as a CLUT name. strclut : 0 => "$1" nm={b} rm. u {`"ss = lowercase([['"{/$nm}"'],0]); const N = 2*size(ss); sd = vectorN(); for (ps = pd = 0, ss[ps], ++ps, ss[ps]<=_' '?(sd[pd++] = _'_'): (ss[ps]==_'(' || ss[ps]==_')' || ss[ps]==_'{' || ss[ps]==_'}' || ss[ps]==_'[' || ss[ps]==_']' || ss[ps]==_'\'' || ss[ps]==_'\"')?0: (ps && ss[ps]>=_'0' && ss[ps]<=_'9' && ss[ps-1]>=_'a' && ss[ps-1]<=_'z')?(sd[pd++] = _'_'; sd[pd++] = ss[ps]): (sd[pd++] = ss[ps]); ); sd[pd] = 0; sd"`} #@cli strlen : string1 #@cli : Return the length of specified string argument. strlen : skip "${1=}" u {narg({'"$1"'})} #@cli strreplace : string,search,replace #@cli : Search and replace substrings in an input string. strreplace : skip "${3=}" if narg("$3") ls=${strlen\ "$2"} lr:=${strlen\ "$3"}-1 l[] { ('"$1"':y) s +,{'"$2"'} s y,-$ls repeat $! { if [{$>,^}]==['"$2"'] rows[$>] 0,$lr f[$>] {'"$3"'} fi } a y u {t} rm } else l[] { ('"$1"') s -,{'"$2"'} a y u {t} rm } fi #@cli strlowercase : string #@cli : Return a lower-case version of the specified string. strlowercase : ('"$*"') u {`lowercase([{^}])`} rm. #@cli struppercase : string #@cli : Return an upper-case version of the specified string. struppercase : ('"$*"') u {`uppercase([{^}])`} rm. #@cli strvar : "string" #@cli : Return a simplified version of the specified string, that can be used as a variable name. #@cli : (version that creates a lowercase result, no longer than 128 chars). strvar : _strvar_fn="lowercase(c)" _strvar "$*" #@cli strcasevar : "string" #@cli : Return a simplified version of the specified string, that can be used as a variable name. #@cli : (version that keeps original case of specified string, no longer than 128 chars). strcasevar : _strvar_fn="c" _strvar "$*" _strvar : eval "in = ['"{/"$*"}"']; out = vector196(); q = 0; inrange(in[0],_'0',_'9')?out[q++]=_'_'; for (p = 0, p>2]; ov = v; n = 1): n==1?(i[#-1,od++] = hash[((ov&3)<<4) | (v>>4)]; ov = v; n = 2): (i[#-1,od++] = hash[((ov&15)<<2) | (v>>6)]; i[#-1,od++] = hash[v&63]; n = 0); ); n==1?(i[#-1,od++] = hash[((ov&3)<<4)]; copy(i[#-1,od],_'=',2,1,0); od+=2): n==2?(i[#-1,od++] = hash[((ov&15)<<2)]; i[#-1,od++] = _'='); " u {t} rm. #------------------------------------- # #@cli :: Other Interactive Commands # #------------------------------------- #@cli demos : _run_in_parallel={ 0:no | 1:yes | 2:auto } #@cli : Show a menu to select and view all G'MIC interactive demos. demos : check "isint(${1=2},0,2)" check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r strver=${strver" "$_version,$_prerelease} e "\n ------ "${g}"G\47MIC demos"$n" ------------------\n ----\n ---- "${c}"Mouse button"$n" to select a demo.\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -------------------------------------" l[] { entries="2048 game","Blobs Editor","Bouncing Balls","Connect Four","Fire Effect","Fireworks","Fish-Eye Effect",\ "Fourier Filtering","Tower of Hano\357","Histogram Demo","Hough Transform","Jawbreaker","Virtual Landscape",\ "The Game of Life","Light Effect","Mandelbrot Explorer","3D Metaballs","Minesweeper","Minimal Path",\ "Pacman","Paint","Plasma Effect","RGB Quantization","3D Reflection","3D Rubber Object","Shade Bobs",\ "Spline Editor","3D Starfield","Tetris","Tic-Tac-Toe","Image Waves","Fractal Whirls","Color Curves" commands=x_2048,x_blobs,x_bouncing,x_connect4,x_fire,x_fireworks,x_fisheye,\ x_fourier,x_hanoi,x_histogram,x_hough,x_jawbreaker,x_landscape,x_life,\ x_light,x_mandelbrot,x_metaballs3d,x_minesweeper,x_minimal_path,x_pacman,x_paint,\ x_plasma,x_quantize_rgb,x_reflection3d,x_rubber3d,x_shadebobs,x_spline,x_starfield3d,\ x_tetris,x_tictactoe,x_waves,x_whirl,_demo_color_curves nb_entries:=narg($entries) parallel_mode:=1-($1!=2?$1:$_cpus>=2) # Generate menu graphics. l[] { _demos_font repeat $nb_entries { arg0 $>,$entries entry=${} 0 t. $entry,0,0,_font,1,1 } r ${-max_wh},1,1,0,0,0.5,0.5 frame x,12,0 frame y,6,0 a z +n[0] 0,255 +shift[0] 1,3,0,0 max[0,-1] +f[0] z+1 1,1,100%,3,u(128,255) r. 1,[0],[0],3 *. 'y/(h-1)' r. [0],[0],[0],3,3 200%,200%,1,1,"x = min(x,w-1-x); y = min(y,h-1-y); (x*y/wh)^0.7>0.01" +dilate. 3 xor.. . distance.. 1 c.. 0,2 negate.. distance. 0 c. 0,2 rs[-2,-1] 50% n[-2,-1] 0,1 +[0] .. *.. 100 +[1,-2] *[2,3] . rm. frame xy,4,0 a c s z append_tiles 3 s c to_rgb[1] a[3-5] c 0 t. "G\'MIC demos",2,2,32,1,1,1,1 if $_prerelease 0 t. "Version:\n"$strver,40,1,18,1,1,1,1 rows. 2,100% else 0 t. "Version: "$strver,40,1,18,1,1,1,1 rows. -2,100% fi a[-2,-1] x,0.5 b. 0.5 n. 0,1 (255;255^255;16^128;0) r. ..,..,1,3,3 *[-2,-1] round. 1,-1 +!=. 0 channels. 1 s=8 r[^-2,-1] 100%,{h+h#0+2*$s+6},1,100%,0,0,0,1 rectangle[0] 0,$s,100%,{$s+6+h-1},1,0.6 rectangle[1] 0,$s,100%,{$s+6+h-1},1,120,32,32 j[0] .,{(w#0-w)/2},{$s+3},0,0,-1 j[1] ..,{(w#0-w)/2},{$s+3},0,0,1,. rm[-2,-1] c[0] 0,1 => menu_opac,menu_fgcol,menu_ind,menu_bgcol } # Generate 3D cursors. arrow3d 20,20,0,0,0,0,20%,40%,40% col3d. 255,128,0 => cursor3d # Generate 3D background object. l[] { n=16 chromeball64x64[] 200,100,64 n. 0,230 s. c,-3 rgb2hsv.. r.. 100%,100%,$n,3 f.. "[z*360/d,G,B]" hsv2rgb.. N:=6*$n P:=2*$N-1 i[0] ('CImg3d') i[1] ($N,$P) i[2] 3,$N # Header, points i[3] 2,$N,1,1,"x?y:1" i[4] 3,{$N-1},1,1,"!x?2:x==1?y:y+1" y[3,4] a[3,4] y # Primitives l[4] { # Colors s z i[0--2] (-128,{w},{h},3) 4,{$N-$n},1,1,'!x?-128:x==1?y%$n:0' # Colors 3,{$P-$N},1,1,200 y a y } l[5] { # Opacities n 0,0.5 i[0] (-128,{w},{h},1) 4,{$N-1},1,1,-128,0,0,0 1,{$P-$N},1,1,0.25 y a y } y a y => background3d } # Generate background. {menu_fgcol,[w,h]},1,3 +plasma. 1,1,5 n. 0,230 water. 100 (0.1;0.3^0.1;0.2^0.5;0.3) ri. ..,3 *[-2,-1] (0;1) r. ..,..,1,1,3 ^. 1.5 n. 0.2,1.15 *[-2,-1] n. 0,128 => background w. -1,-1,0,"[G'MIC - "$strver"]" cursor 0 w[] -1,-1,0,0,{([{*,u,v}]-[{*,w,h}])/2} # Enter event loop. omb,ind_clicked,cfx,cfy,cfz,alpha=0 nfx,nfy,nfz:=g,g,g time0:=$|-4 do mx,my,mb={menu_fgcol,x={*,x};y={*,y};[x<0?-1:x*(w-1)/({*,w}-1),y<0?-1:y*(h-1)/({*,h}-1),{*,b}]} ind={menu_ind,i($mx,$my)} if $mb" && "!$ind_clicked ind_clicked=$ind fi # Render current view. [background] 3,$N,1,1,"const t = 0.8*"$|"; const a = "$alpha"; const oma = 1 - a; !x?oma*cos("$cfx"*y + t) + a*cos("$nfx"*y + t): x==1?oma*sin("$cfy"*y + t) + a*sin("$nfy"*y + t): oma*sin("$cfz"*y + t) + a*sin("$nfz"*y + t)" y. j[background3d] .,0,8 rm. +r3d[background3d] 1,2,3,{20*$|} *3d. {menu_fgcol,[w,h]/2-30},300 +3d. 0,0,300 j3d.. .,50%,50%,0,1,1,0,0,200 rm. if $|-$time0>5 alpha+=0.02 fi if $alpha>1 alpha-=1 cfx,cfy,cfz=$nfx,$nfy,$nfz nfx,nfy,nfz:=g,g,g time0:=$|-u*3 fi if $ind>0 +==[menu_ind] # Draw selection background $ind j.. [menu_bgcol],0,0,0,0,{$mb" && "$ind_clicked==$ind?0.6:1},. rm. fi j. [menu_fgcol],0,0,0,0,1,[menu_opac] if $mx>0 +r3d[cursor3d] 1,1.3,0.6,{50*cos($|)} j3d.. .,$mx,$my,0,1,4,0,0,800,{-2,[w,h]/2},-1000,0.7 rm. fi w. -1,-1,0 if {*,CTRLLEFT}" && "{*,D} w. 150%,150% elif {*,CTRLLEFT}" && "{*,C} w. 100%,100% fi rm. # Manage user selection. if !$mb" && "$omb" && "$ind_clicked" && "$ind_clicked==$ind m "com : v 1 "${arg\ $ind,$commands} parallel $parallel_mode,"l[] { com rm }" um com elif !$mb ind_clicked=0 fi omb=$mb wait 20 while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0 } v 0 e "" _demos_font : # Define font used for menu items l[] { base642img \ "MjU3IHVpbnQ4IGxpdHRsZV9lbmRpYW4KMjggNzggMSAxICMyNQp4nGNgGAWjYBSMglEwCkbBKBgYAAAIiAABMjggNzggMSAxICMyNQp4nGNgGAWjYBSMg"\ "lEwCkbBKBgYAAAIiAABNDMgNzggMSAxICMxNDUKeJztlEkSwCAIwPj/p+mpMwpBwfEmOVWJYl0QaZrGoD9Zb2erJy0GcmCCG5pO9oHI5Qmod+4avp2LqT"\ "Ek2FwktEloHrd0Xt6R6vaD2klVwYzHHqga8Yx6d18LB1u/LqlLuHkFBXWOKgVXsdTjNmXE/TGWl1J5ovImErkWseTNSoFnmUWwF+Kgb62meZEP+3ShiTQ"\ "zIDc4IDEgMSAjMTM1Cnic7dTBDoAgDANQ/v+n58kItKXD6In1SJ+YEUJrlUplStzJOqcDk4YCC4lWQrCkUZjvwFblCFAkhvVyLldybNdy6LeplB1wm3bC"\ "yoe8oKFyDP3jXFN3YOO6fH9f4bP1psZOXWgLVQjM1rk1q+x5U//CyAG83HngOeYQsXaD96xSOTAXsqErADQzIDc4IDEgMSAjMTc0Cnic7ZRLEoAwCEN7/"\ "0vjwvGTkExhXEpWCm8wIO1ao9GIFJeq3I6OrDJoYENm1oKJFRnH1qO2hZQoNJtfycCTRPR8worEAsm27jyi5luvMBalApEBnZa1aBY4I7RCc1O2CV2KNK"\ "jcEoOqHfmO1g3U26oP60aQRTSAND+2sS79JbSrLVxXDkznGDYOt79+VVyyoStEiueIYFm2gT3ZueA1rMEMew74PTYa/VAHA++SmDQzIDc4IDEgMSAjMTY"\ "3Cnic7dRLDoQwDANQ7n/pII1GIontmK6JV6h59Au9rs1m0xL/vHWWB+Y15FhAYrXslhSEFT2wVjstK7EkZTzFSlmPrTrIZBNVsyxdzYvPwEpD66bUqUxy"\ "on2jNYUjKXSUkqJUEyBSUCYpDSpHymSlXCZR30EJtH5tdPz86GSzXVL6s7M8+blPrgyxUWFa4YxxsNCRC/Dy4IIXmEPE2mXv1Wbzxdzf1CMINDMgNzggM"\ "SAxICMxNTEKeJzt1MEOgCAMA1D+/6fnCQnruo7E6MH1JOwFBYQxOp2Oi81UndKGKUOCiURLIdigQCwZIeqVnyUllhJ5F8NWZnM56/5RDavoAmQ1YDMOqF"\ "XoeIHC0jxCP5/WyRZkR2q13LAYT9X/WqFbObcRrRzDxLI1rlwZ5KrGFd57/QSClxkPn4GUzAaQYCIBc7d5zTqdH+YCqxUYEzM2IDc4IDEgMSAjMTAwCni"\ "c7dU5EgAgCARB/v9pTQzwYLAMLDw2pQNhA0V+YieV+MJWqc6MGaGWLJqOgBE4gB5Ya7nH22u4picMkVtN0CqiGSInmqBn/pX2U0Xwo3GNivkIIkuGNuay"\ "JohWpjgtGbwoTc80NSA3OCAxIDEgIzE0OAp4nO3USRLAIAhEUe5/abONgA3fZEkv9dnlkIrZZPJHFgjBqJnsA20aHZFgdHnuquU7hHdRb3Zc3cKJjjNnn"\ "M1cYa/TcYRPWo+2KvKOq6NYA9fXjzD6CFo4zgkbsCoOTT280rUS62LXVditrCre2vp41bt447o4+5MRLCzDXkvLsN3jwjJsxDJsd7hhJ5PJ1zxsuK19Mj"\ "ggNzggMSAxICMyNQp4nGNgGAWjYBSMglEwCkbBKBgYAAAIiAABMjggNzggMSAxICMyNQp4nGNgGAWjYBSMglEwCkbBKBgYAAAIiAABNDEgNzggMSAxICM"\ "4MQp4nO3UwQkAMAgEQftv+pIS1mgIxNv3gA/FCOccSMKQSYlJiUlBSF12cNqJNRXWN9d2Cq9cy+33b+YreHcjpc2dODyYP2D+0reEzrnxLROaylI0MSA3"\ "OCAxIDEgIzE0MAp4nO3V3QrAIAgFYN//pd3FiH482mG4BUMv5SObWROpQKF35EHNhuwGhXU0TF/wXOXDTazja3kG2ixqYrs/apJQPYTeXoyEsPPdN1voN"\ "nut7Z/KDINJBBC6pXY0DyMMKgPouKl2CIclY8fDLlm4c94cbSQLQ8dDfMu+gfn/Zh6+8HgKDSsq/hUXnbrfLzI4IDc4IDEgMSAjMjUKeJxjYBgFo2AUjI"\ "JRMApGwSgYGAAACIgAATM5IDc4IDEgMSAjMTM5Cnic7dTtCoAgEETRff+Xnui7nNVuEATl/NL1CLKoET09a8SUkGNMIk5CjjGJOIk4CTnGJOJUhqnUJYo"\ "yd6kyV1GFq6qTayjKxNTmLtTqGLtUs3uUATW6D7Lpf/OiPztn3t6Yf0tnURZss9+kjNm9XGZZca9tw+PmqKW19jJrH/wmS5rV0/OLDD/okpg0NSA3OCAx"\ "IDEgIzE0NQp4nO3UbQuAIAwEYP//n15FRak3d0cYBbtPNh7Il2kpmcyc2BGBMtyM12a8bq2GB7q3E7Gvv4ulBfr44QmOcKuHVsNFsZWOTbx/AHg+ppem7"\ "KkVTNpdK5i2m/4jhr0AOwTjteY9exJGbzL6I+4kjP271M8PNv/xjcuVv41RGScEr+B4ng1mNd6tTCYzJQui0SUUNDcgNzggMSAxICMxNDMKeJzt1EkKwD"\ "AMQ1Hf/9IulFDS1LIl6EDBf6m8ZRKzrnslH1GzT2nzdEDNxwE5jwN6Tvdgdm1O9nB2NH/CQf/mktc4ujMJ5z267ylnPXx9d3D4FVSa8Od/qfQatzVNpz7"\ "QiQ819ECrHP5ogk904FN98YVefKlVbpqePKUPT+rhaW2a3r3CTdNd1z3eBghhMhUzNyA3OCAxIDEgIzEzNAp4nO3VQQ6AIBAEQf7/6TYcNAoL23gzMudK"\ "CLAMpez8LBhDrkAohEIgyBXkCoQyCHJFG2MiFaBORaZFoWnUwDzU0NyVQRNzqak5VYIwBmOqUkgtJ3eXqIXDlHenpsDN00i9GF/5WtS765VBoZGtovrJN"\ "Z1Dqlhlj6sfoSqDhNnZ+U4OwWM19TMzIDc4IDEgMSAjODEKeJzt0EEKACAIRFHvf2lbhIE2GkFExMyy/4hIhON+ntrKmJjYAwHZkaQPkXYTK1D0LjYAej"\ "Xs/hB1d4qBzCD5+xeAEhCcBbq8gePurgGh3apyMzMgNzggMSAxICM3NQp4nO2QMQ4AIBDC+P+ncXI5QXOT0dBN20QDEMJvcOLuEyRoBTgEeCug9K2AyrM"\ "ehBersxnsivqiCXwhfq29Tsz2Ri/7hnCNAZhxuWMzMyA3OCAxIDEgIzk5Cnic7dLBDsAgCANQ/v+nu+sWakox7mDoUR+CxojJ5N7gnSMAHiBiG8AFSSiQ"\ "9jVAFwQH6ADwDhrQHugB1MF3SQIitgHqgBc4FWomCfKtSi+XO1gAFggK2AGrjzmZ/JEHt7utbzM0IDc4IDEgMSAjMTAxCnic7dHBCsAwCAPQ/P9Pu+NAE"\ "2Y6YYeZY3jVlgKbzU8Sd1TpiDgS6iKpqiIfckTwJQ1xUDhr+89/IdhXihGpHBUAI2KEJcIRACfPQixhAraoZEAUUMiAIKAhMC0o2Gy+ygXsjatxNDkgNz"\ "ggMSAxICMxMTUKeJztlUEKwCAMBPP/T8drLdbdVYJCd84zKAY1wphS8gtBHQbAfgdY7wPGT1F/BKSfqp9DXxsTtruA0sv9qPZD9eWgfIH1gMSBFtw46Z8"\ "uId9SPtkq5Od4ni1/KHwy3RUI1G+XTIjjAgGKwHiMMYdpAdBN6zI4IDc4IDEgMSAjMjUKeJxjYBgFo2AUjIJRMApGwSgYGAAACIgAATI4IDc4IDEgMSAj"\ "MjUKeJxjYBgFo2AUjIJRMApGwSgYGAAACIgAATI4IDc4IDEgMSAjMjUKeJxjYBgFo2AUjIJRMApGwSgYGAAACIgAATI4IDc4IDEgMSAjMjUKeJxjYBgFo"\ "2AUjIJRMApGwSgYGAAACIgAATI4IDc4IDEgMSAjMjUKeJxjYBgFo2AUjIJRMApGwSgYGAAACIgAATI4IDc4IDEgMSAjMjUKeJxjYBgFo2AUjIJRMApGwS"\ "gYGAAACIgAATI4IDc4IDEgMSAjMjUKeJxjYBgFo2AUjIJRMApGwSgYGAAACIgAATI4IDc4IDEgMSAjMjUKeJxjYBgFo2AUjIJRMApGwSgYGAAACIgAATQ"\ "yIDc4IDEgMSAjMTE1Cnic7dRBDoAgDERR7n9pZeHG9E8yYAlqOkt8NsU2tlap/DbHLSbTmBzZeCykfp0gt2TC/siFdkWQufD1JdVGxO1IlfMX+oLM+fJL"\ "5ebLTxVd0ulA0eeU/nVE4zHPDyvAsMX45cJw86aLWLNKpXLlBH9A+BYyOCA3OCAxIDEgIzI1CnicY2AYBaNgFIyCUTAKRsEoGBgAAAiIAAE3IDc4IDEgM"\ "SAjMzUKeJxjYBgY8B8EqEUxEEcxUI2i3PNoQfAfhUK4ehAAABH3iHgxNyA3OCAxIDEgIzM1CnicY2AYBfjBfyBAYZIkwECBAMPQEGAgQ2AUjIIhCAAp63"\ "6CMzUgNzggMSAxICM4MAp4nO2RMQ4AIAgD+f+nqyuJpIJoHHqbcklpMBNClAFAPhEp4ArPSSnVnOwqFyqD0Kb8U7l9lcBvzzmo/PjM7uVH6wq6cm2ViA1"\ "FiMkACruibDI4IDc4IDEgMSAjMTMzCnic7dNRDoAgDANQ7n/p+oW02yomxAQT+qHGJ2HAbO1kHgB/MMAgKF6Uk5D25/uqJHUWxO8eLNEe5uokNIbaBlpD"\ "fe72WBWzkVa4ok84KTe0UjCDY1vLXR/zGmtfWN0Bc3szn/6jYjyc197voXehRv0ZPs7hEowkblUsyPo2t5PVXIoMzUEzOSA3OCAxIDEgIzE1OAp4nO2Tw"\ "Q6AMAhD+f+fxsSDc7WPEOPJ0NMcL4OtNWI0Gi3lKV8SyINrM5O5tedWFWW4raXb1k8sCIVYtrCUkxmjaehqOLR5GjNO76XJN/GUUtCgLtDeCoUJfUH1W3"\ "5JbZ7ww9whADe3qt9NjzWcd4sjlYKlpZr5rjAeBytVmwbGt1EsIlylotRboJZHuVHPGBjnfao4R6PRH3UA3aWAjjQwIDc4IDEgMSAjMTcwCnic7ZPNDoA"\ "wCIP3/i+NBzWDtQVMPJi43lw/9kNxjK2t78isw9zqURk57RQMVsGJKrkdlOn1F7i0Ti03OdmXZlsi12xzGW8Dm241Vqf9ZPoyyoEFpo5kSTU4dTsyl8Z6"\ "wgNFkAeKR4vcAVw5UsOs9eE1F8AEC2DKTRCaMjhYcXTI+Cxh2mLoaDR6w+KvNqcEdO91Hv8klqHEwxWF1xLQ5eHa1tYfdAC0rAIbNSA3OCAxIDEgIzIxC"\ "nicY2CgAfgPAiCSgYbEcAIABug9wzE5IDc4IDEgMSAjNjEKeJxjYBgFWMH//5giaGL/iRQhYAxR5pBpEFHayDWJXMNp64SRLERL84eaMlLCiJpiWN3CgC"\ "lCbQAADT3zDTE4IDc4IDEgMSAjNjEKeJxjYBjB4P9/TBEMsQERIkcfrRWRoY1M26glgt3V9BKho08HlSZylFBFD0GB/9hUoFqLpUygGQAAC9XeIjI2IDc"\ "4IDEgMSAjOTYKeJztkEEOACEIA/n/p+sekAi2l028dU6aASlGmIfg44fRr/WrNiBFORWqZbsrQCkSLV0JtNnZCBzPd0lMKHMVniKYqvEstVx1n9gXcEOW"\ "H4VzxZlRrqWUEMYYQ1gD7rBQMzEgNzggMSAxICM0Mgp4nGNgGAWjYBQMFfD///9R6QGX/o8PUCo9kB4blSYsPQpGAQUAAD3cpFw5IDc4IDEgMSAjMzgKe"\ "JxjYBgFwxD8//8fSkEYaCxUIRxyuBkkKGHALsAAoykDAH+dRroyMSA3OCAxIDEgIzMwCnicY2AYBaNgFNAR/Mcm8h8TkCI4CkbBIAYAAVA6xjcgNzggMS"\ "AxICMyOAp4nGNgGAV0B////4eQIBpC/UfhwajBAABOxBzkMjEgNzggMSAxICM1NQp4nGNgGAVkgP///2MTwypInG4aGIlLNw2MJE73SPAjHR00EvxInO6"\ "R4EcKHcSAVZBOAADDg8Y6MjkgNzggMSAxICMxMTQKeJzt1M0SgCAIBGDe/6XplMKy/Mx0jD2Fnzpqlsjmh9E3lUVWH2L4aIx0PDWdBgdCnSyBTdri1XTb"\ "ycDbmKLyWecYrEZZdJic35e30t+EoOMLxhC6zdC2oLpaQWPVfJFm8eyncMK2zckyoc2myAP5h5R6MTcgNzggMSAxICM2MAp4nO3SMQ4AIAgEwfv/p9fGG"\ "JcOW7kKB0woSCaNAOUpwAACMGA45QZ3cn/VnCCCstrAp1DvdPKWBb3I1ioyOCA3OCAxIDEgIzg1Cnic7ZJBCgAgCAT7/6ftZIi6BmFR4ZzSId2g1or/IQ"\ "YKa4mgFA0tnYpEYRYAJxo29riIXsQn7CwXubMj3X+xLeRakPyM6TneVx6BmrhoZlEoOv4gR8cyOSA3OCAxIDEgIzExOAp4nO2TwQ7AIAhD/f+fZhejw7a"\ "QqMkOoyfxreJW1lrph7KhAAG2VcBY0cvgUWwy1+x23PjeUy8Gmx9C/vkPjXtMjMJMPHDphgkO2mb3vUH3rIfnZlCHoPzDGED5r5hbEEYycrVLGNI2EHbR"\ "Q5KOT6nE9QCb3kPLMzMgNzggMSAxICMxMDMKeJzt0EsOgCAQA9C5/6VHo0b5TCkyumt3tC8hYKYo2fgeto+AE+BZ4AQcWwacEwbXAoHPABuAuwfgqRdB0"\ "YagLCNQdSugrnrQNO9BW7Bz/kb6pv5TOMCZAERYGLwIfAwU5b9slqtdsTI2IDc4IDEgMSAjOTQKeJzt0tsKABAQBND9/5+mvLCXIZc2tPNEJ8IgijycZA"\ "TK3hp8gIvFBk+RL6mBI6yA+FDvyXGJOrfANCuD93KQ45sVkVzBLJ90Hc28bVi0jX+IMuNAnV4ifycDLsdGyDI4IDc4IDEgMSAjMTE3Cnic7ZJBDsAgCAT"\ "5/6e3TZooyK410Z7K3MwIBbdmxZ/AjRTcoaGMrpHtJjNoRc15pR9pq0yoj8peW/ZYMThEnEMitIoDPGeSaXRidvm/qfWcPOZs3/EPThZ0M626IQlalpNI"\ "0fcju5lS55atxN+hKBoXUY5qpDI4IDc4IDEgMSAjNjEKeJxjYBgFwxb8xw3wyOHTh0cOnyPwyJGpDZccfbUNeQ/QP7gGh0so0EaGkWRqG/IewFsujIJRg"\ "AAAJMsCDTI3IDc4IDEgMSAjMTMyCnic7ZPBDoAwCEP5/5/GgwlCaTEunnS9uTcIdNVs62vykARIHVVJvZY+sAUlmfWpMiLz8qK4vY6GMSbklHSmXIM9s9"\ "2s+3k2vyUumSsVodY4LazrYzbYIjwbwt6C2LSsaEQmTRvR5cCLiGYtBrhBj3KY/j0VPW+CFjId6nzrtzoAq9qYdjI4IDc4IDEgMSAjMTE1Cnic7dLBDoA"\ "wCANQ/v+n8bTZlUKyRT0YetK9jeDQrPP7+EwurB5CtO5D4yqaEEVrEzdtrCkaB6XZu6abqU1jYcvFqHHSI7zCJGjq9+b4Q8hCUCneCH7Crh2WfP7YlxX9"\ "hDylVNbpKSgOpe1J6HQoF214baE3IDc4IDEgMSAjMzEKeJxjYBiJ4D8I4KcYICStrCfgCIb/NLSeNAAA3ss2yjkgNzggMSAxICM0Ngp4nGNgGAVkg/9AA"\ "Gf8h9EoDLgUmEVPl6E4DNUZ2ITIYpAgg0IDWVTxJgC5o2ScMjggNzggMSAxICM2OAp4nO3QMQoAIBADwfz/09HCThNU0Guy7YA5BFJKJZFalJHKKOlePs"\ "0cHTBoKe7n7JOb6lbfaO1ZahbOuhpLaa4BdfKdYzMxIDc4IDEgMSAjMzgKeJxjYBgFo2AUjAL6g//4AKXSIxXg8zrFgToa5qNgFFAGAMG0r1EyOCA3OCA"\ "xIDEgIzYyCnicY2AYBaNgFNAU/McnBwT45HDK/h8gWfyuGmCL8UmSaChux5AvM+AOwB4I+OMEhwT+ZD0KRgEpAABVvptlMTcgNzggMSAxICM3Nwp4nO2S"\ "MQ4AIAgD+/9PY9yILaDRwYHbuATSAECTYxMSXpotysh4nU8HCTYPhMxGWcPoug7b9RLLxDgUOz34lfIz9I6zI6pnai4YBGetUzQ5IDc4IDEgMSAjMjAzC"\ "nic7ZNLEoMwDENz/0uX6abYkvwJDEMX1o5YDzmJs9ZoNBp5fZz23CWj7AkiywkSVYL1LFvViu3pTkM7E40T947uDe34ranltzYF/NZ8I4HfzoQGAvu3wl"\ "vFAN8qHiYHOEsDEDcKQH6DHYAOAL7yERENpgEMrHuAvPNnAb4VAoqARgJ9ZYCcgi0AU8Xk8cPHNYvLhx/NP7wuj0IT5qXht46wdfbnO6WNCaKhK8D/EXw"\ "SLeK9kOhPPATnYhqiVPTVdSuoMo9e0gEP6ZGLMzkgNzggMSAxICMxNDMKeJzt1c0OgCAMA2De/6WrHIxzdKzzRrIe188Yf4AxOp0DgjuaUlnuNAZIboqc"\ "QWZDeCcaw8t27mn3DDJ7fehMV2CRs03MILPvNdS5eYEx56ecLdMCWxyZRYzlF4sUYfSpkKp1rLHNh/G/A1G+2DKkSmWF5WEXZaBsmTJk93QsVJVNavaJU"\ "o+TTuf0XNqrfpAyOSA3OCAxIDEgIzExMwp4nO3UwQrAMAgDUP//p7PD2JgaI3grM8e9wmiUmm3+E4RIdE4QDvNRio9X+NWM1iIqtKNwXILo1s0sDiHNc7"\ "4JqNaMb4Iu4f5Y1VfiqxStRYzQxjj/p7qKLEGh6LYemZ82WxPEo4SaR2qz6XMBEJjSPDM3IDc4IDEgMSAjMTAxCnic7ZRbCgAgCAS9/6Xtt2xNe0Kw85c"\ "NZkQrQshtNNhuiA3oIcVobkVtBfV2l3U9dAZtlx3JOscOezbRudMoTUiP3+7rVqlPtWVpwuqyx0mxLupQBUWkBc8/UoAHr0vIXxRv5VK8MzggNzggMSAx"\ "ICMxMjMKeJzt1MsKwCAMRNH5/5+2K4s1Y2YKpS9yl3IohqpAVT1eC3mK0YWa3LzAHdtJZIv9Tk5MJVR3So0sUTihmlLw1M7uU519VOFC9dYZ/f8olX9yo"\ "JR/7qGUddOsuz2gtRpR/jLFMaJJXrm2x1eP0Y+nhig6cFX9qg37dtU5MjggNzggMSAxICM1Mgp4nGNgGAXDFvzHASiWw6ICJofPLaNyg0MOe+xRKDeI/D"\ "cqR7ocrvilhdwoGAVoAABGIYSKMjYgNzggMSAxICM0NQp4nGNgGAVDFPzHASiQwWIJThmGUZlBJIMlRmkiM/A+HZWhoswoGEEAAMsVTsAzOCA3OCAxIDE"\ "gIzEyMwp4nO3TwQrAMAgD0P7/T7vD2FZdohbcoGCO5mHbQ8fodH6PBLVOgkAIjXHOSMyE3CFAZ6MQfXUC6cMjNNJo51VFB1aq6st/rcz3qFbexepVxGbF"\ "2VUuKMruzmVGEfY0HpsK4U6NKdNTQfv46B2wPTIAwld3OtvnAFzTq2MzNiA3OCAxIDEgIzUyCnicY2AYBaOAtuA/COCXHlUzqmaA1fxHqMEPqKuGGPcMi"\ "vAZVTOqBr+aUTAKhgoAABNdyEY2IDc4IDEgMSAjMTkKeJxjYKAH+A8CI5SkPwAA+Z7RLzE0IDc4IDEgMSAjMzgKeJxjYBiR4D8QjPJGeVTn/SfAI0YhAc"\ "5/TDYqB8N9DLQFAFU95RszMyA3OCAxIDEgIzE1Ngp4nO2UORKAMAwD8/9PA0UA2ZbkoaCzyuzOhPhgrcnkS44r/HSfUwE4FZAzIXAiRK4E84rEi5B5Fgp"\ "f9ULySY4HgXEUKAeB81cQ/BEUvwXJUaB8E813czX/ILgr3CPCM10dtBFL7XqhjNxuNw/cqCPnZpIZbOzbveg3q9/Nfrvd/yEZqnihA1kIhm4hzEEVwHCD"\ "JMdwMvkvJ/BnmXUyOCA3OCAxIDEgIzM1CnicY2AYBcMW/AeCUblRuVG5UTlCctgBbeRGwShAAwC3dP8BNDcgNzggMSAxICMxODUKeJzt1EsOhDAMA9De/"\ "9IeMT+a2g6NkGY28QbRPAxUgjE6nY4PjuwbbPgaB/HU1zgET/zKR8rxFpFb/xwFntUjgPPM+Ncgcl8Py6WPjI66XB6F/yzucQh+TsiH6SWH58rH4S3OHm"\ "W+XjmLxaPM6VLqo8L5hG8/c3qw5e0mL4tSzgO71bpHcd4Cy+3HmHBQueXfv1CB86rl5h97wccut19twte1hPMuzpwmMj/g8tGT9k6n8588AIStUsozNyA"\ "3OCAxIDEgIzE0Mwp4nO3U0QrAIAiF4fP+L+0YjFLzqGNj7KL/1o+KiIDd7uvkLB9fiKvbiCqDmLKIqIGyc02U3INCXC0oUhpRZRDb0CKiHIpViLzyKFxq"\ "QZFaUaAYkgKtaj4VryrkVYKkQE4RhBYyiiKtCjRVjLTiCC00VYmkQEOlCC2kv5KHCC2EFsJ7CC1UjXe733cAdkf4FjQ1IDc4IDEgMSAjMTQxCnic7dVLD"\ "oAwCATQ3v/SuGv4DAPGmhjDLOGJtdq41mTy94g0mU5fUg5oymMv57COdXpH0ChXFyrQRs331Opq/wMm1owuX+xymFo1uh6szG1c2I06g7ca/EHc0+9/dR"\ "394FjVJ9ZcSLRtc+y6fLTvEQ1aknFYF8zpCPAo9KfiAy3mGY2eyslkciIXNJvwHjI5IDc4IDEgMSAjODUKeJzt0kEKACEMQ9Hc/9K6KmqbVnAlmr99BZk"\ "hgPqn5ipxOWDYZiSXBKcDjsYJDmWIczR9BXGC2x9fITgWS1hG5FcRBuZDjuENAtmnCK9GpfZ1f4txnTQ2IDc4IDEgMSAjMTYxCnic7dPBEoAgCARQ//+n"\ "6dSMyAILNtOFPcYTKXOtyWSCIyoFGntgfW+LgYcFhzeaBAOSGHDmW5H4rGenppqTZ0y2VjxvfacTvPGSZgYZ/Z1unGXKS39VTW+G4Fc3Lb/GemnAj3Ksz"\ "2rc3NQiDkriefxcsPf2FOvdDZXXwRh715oFsTzXsJq9gE1eHKb1quX27/1lObtNTa/6/JOf8gCiyjjkMzkgNzggMSAxICMxMzUKeJzt1DEWgCAMA1Duf2"\ "lcLUnTgIv62s3yhwLBMbq63ldzKZMlkjAGYzd10OQOe9QlLYMx94AlA3+JbZyb16qZd/VekEQsZ/igKT99CwxtsXQ/gnEXF+QW1lFrpo73gJl36iZk7+f"\ "gvnqcmA7sMjgnYJhPndaKxRyKiJnsDhPQ1fWjugCz1MtDMjggNzggMSAxICMxMTAKeJzt00ESgCAMA0D+/2kYD0JJ04BcdJzmplsRaS0l8/9Uk1iAHQ2c"\ "KydzrxB03RO2pldM7fMIaWMAuQZNBXyu4bhsLLy5KfkoxfMDlm373HytjBTc30f/sEGofUV37vayskBlNDCxZDImDaWwUb0zNyA3OCAxIDEgIzQ5CnicY"\ "2AYBaOAzuA/tQBxJmG1HU0MuxuJ8sioolFFo4pGFY0qop6iUTAKhhAAAMWLMd0zNiA3OCAxIDEgIzg1Cnic7dMxDgAhCERR7n/pMTYqOBALN2sxv4QXYq"\ "OZUh+HXr2WkZH51Zz803vGSoNpUnTJjFWBvKFomWfIjTnyQ2BX2SReDqfB4i8sRVQUKPV4DaIYmnQzOSA3OCAxIDEgIzE0Nwp4nO3USw6AIAwE0N7/0pg"\ "oEko/M+jGmM4SXwuKIFKpfC7tDBLdoT4cm/g71ni2FGTNMHNKnjH1gGSx49g6HDiOmUGS+Y5jXGXI7Azucu1rGWYdx7j9S5neZ08tLP9T2z7zleqQHUp9"\ "2iI1sfSEywa7L4xYjS55s/nOyxTLrumg6jc2ZLLFkGIZ/GaVyk9zALsUYK41OSA3OCAxIDEgIzIwNQp4nO2VQRKEIAwE8/9PY2lZBQmZSYD1tJnTStMYy"\ "SoilUrlH9LuLBJ5CVdd1M5UVlc7VhHSP7JqH/UnPIMfqv4EvP3j2KFqJ5CmJ9RgVVgx7pwemdcmTU+o4aqgYtI5c205abp3baogKHqADHI46Vxmx8HD+K"\ "riaNnEvyuHJu7sKXs/DUfLovczp06m+eDsq4i59Wq+qbLO+eaoIgZuOh5fq+oLWOeQ2VVY0Y7KjhHF11AvC6vkpsLwd6qEKjTBIR2zM7VSqVR+mQutmHm"\ "jMzcgNzggMSAxICMxNTUKeJzt1EsOgDAIBNDe/9IYjfYDwwxuTEzKEl5oa4ut7djxedgZrGZCjZKlai5kaM0nCqCgfBKpmEqQ6A336RfEZwEoGKfopzNu"\ "loq6c2H6guzx9Co1Ty9uhqogatSml1YVw1XpdFddqLtK1Xx1xXHljZgKD7M2rsIkyufQgjEVFWoOkdomOUttXIUJ40qQ+kO/G9cdO/4bB4aaknwzOCA3O"\ "CAxIDEgIzExMgp4nO3TSw7AIAgEUO9/aRoTa4oMDLu2ycwSnvE/hqK8HpuhbSvZblJF57RDQeYapUrGpGWsQhWyUEMb6Jdai6BL7W87InDQCDnVv1qMjs"\ "eUoaZ6Pt8crT75Cfd9ErRZjdiXcoohKakPKEX5dy5ViDfXMzQgNzggMSAxICM1NQp4nGNgGAWjgDLwHz+glgq81hN2H0EVdFAw6ohRR4w6Yrg74j8aFwk"\ "QLOiopGIUjIIBBwBJ74uDMTUgNzggMSAxICMzOAp4nGNgGPrgPxCg8hAAKxcuCFaNZtQod5Q7yiUqHyG4VAQAs471CzIxIDc4IDEgMSAjNTcKeJxjYBgF"\ "RID///9jFcQUxSqIUz+RhpKmnyaGDkqfDkpH0cTQQelTOjlq5PiUro7CFKMZAADtg8c5MTUgNzggMSAxICMzOQp4nGNgGEbgPxCACQRA56IpRuWPcke5o"\ "1woF38++o/Gpx4AAD/s7RMyMyA3OCAxIDEgIzcwCnic7dDBDQAgCARB+m96fUiIwBXg4/Ylk0iMEe6v0IpyUA7KQfmV5TkPrrH5M+jjs551k6U1y8eqr8"\ "gtMXmCc861DoaTZ5kzMSA3OCAxIDEgIzMyCnicY2AYBaNgFIyCUTAKRsGQA//xAUqlRwH1AQBpQ1aqMTMgNzggMSAxICMzNgp4nGNgGJLgPwig8IiRwlS"\ "IQ4qBWCkMhST5YhSMggEBAFgZLdMyNyA3OCAxIDEgIzk5Cnic7ZJBDgAhCAP7/09rohGLVJO97EHpCZxqCBZIpVI/qDRZt5wbZI8TIbL3Gq4Lb09fGGgU"\ "YlZ5ydyCwMY8IbmYC9D3bSh22Pz2K0McOEExNjNQFLaVcA7hgEepR1UB1wMZ9TI2IDc4IDEgMSAjOTUKeJztkksOwCAIROf+l8aaNHxGaozpSnk75hGiI"\ "lD8jnzED2XONbFLK1GikYAZrb2iERi66Ew94BwaJebNspvuGBxm7N2WjUwNsi3QtnmUU7bG+P8842mSvLiGBk1tZKoyNCA3OCAxIDEgIzc5CnicY2AYBa"\ "NgFADBfwjAKogm8x8NoAhjsDE1YxMGCWAVRjIcl6uJV05rcVzOoaq9JFuMUwNREigRhpBBj2ssKQWHMKoUNkePgiEKAF5Pwz0yNyA3OCAxIDEgIzEwMAp"\ "4nO2SMQ7AIAwD8/9PuywVtkmkdkBIbW4jF4SBRDQnQFUftPqkwiyIwkQVlND994I3yyHWaIYrySUoTa4y80y52aTSFDtjvH+NzKFU9Veu4+AJpI8GRlvZ"\ "kPIZE9X8lAtRWmqkMjQgNzggMSAxICM4MQp4nO2SQQoAIAgE/f+nt/AQZWOnIALnlLMUopkVRdGRg3KLJAymio9LBW+CdkH65JV6MUefTeuVv3chTmj4s"\ "BiDICyYPkSi14h6Kz6lAWpx8BAyOCA3OCAxIDEgIzYyCnicY2AYBYMQ/IcD3DLosv//45RD4WNK4XEFPgdSXxsuOTK1DXU59BhFjUSy5Ojuh1G5UblRMA"\ "oIAABldwAPMjcgNzggMSAxICMxMjAKeJztk8EOwCAIQ/n/n8YsWWqBYuJlh4WexIcGKZqNRqMP5I92FPcddCd5FCFKf6+NUb5bEN4pBKgeQrYghjJPSDb"\ "mB+i+G4odOt9aWcbBiVDZmCQaKDlslh7LoOmDDWpJ63/z87CoLtMyuJyC7KaGoigJpBbRXJZ4MjUgNzggMSAxICM2MQp4nO2PMQ4AMAgC/f+naSdSCWMn"\ "wi1Gj0SdKV+An14qUsQKsQFZAgsKtiJ0z5PRc8Dihf2gIlaUVA6GfEPLNyA3OCAxIDEgIzMxCnicY2AYFOA/EEBIXBSQQb7hCFOGCAX39MADAEsmhHwxM"\ "iA3OCAxIDEgIzQ2CnicY2AYMeA/ECAxSWEDebR3HTZ7R9lksdHjkQbGE68Eh2MQTLQYZ8AGAOARzDQyNCA3OCAxIDEgIzEwOQp4nO3SQQ6AMAhEUe5/aU"\ "xsY2fK78adBpaPxtLBiK6XlazZ/kPPnOD+sPtic2F15bAT/knikPv3gYmnFx5eeQwHIeWhcRM01r00T23ou07vPebjmQc19vxhL9ao+4W9S4P+H0qe0+3"\ "6aF2XwA8AMTEgNzggMSAxICMzMQp4nGNgGGzgP5wBBKPM4cQEckD8/zCAJD78AABSYb9BNDIgNzggMSAxICM5NQp4nO2QwQ7AIAhD+f+fZktmtCm4LJ52"\ "eO8iliKmEQAAcEiu6sZ6osw6J9oSZRSpmG8oz6k3GS1K+MooSuqM/c6UtcWtNYGNs8mqz7CbfU9bpS9v4sT5fycAbLgAj/qVeTI1IDc4IDEgMSAjNjMKe"\ "JxjYBgFo2AU4AH/ESwgQOYgRCEAReI/MkBIwPlIMiAK2Ww0NchuQZZAdeV/uPHo7h+VGN4So2C4AgCryfMNMjkgNzggMSAxICM5MAp4nO2SSQ4AIAjE/P"\ "+n8eKCUozxZjK9jdUIhFKEEB9gjYMCbRtBUWhxv+tD/CRxzqb1JQ+H5b6dDK4f/yffhsD2avBgw2Ikrm8XuqmNd9dWsDNuWogTFUYdD/8yNyA3OCAxIDE"\ "gIzk2Cnic7ZJBDsAgCAT5/6e3aWJgXcBbL5Y5CZMYXDEbhuFD4IcXFqtCsCvskIqand5iUYrgaZKJVjbe6xRqZf9ThzT6DDl5lCZ/ZbsAsjaoF4qeoMYn"\ "1H49/KjrVM8DhVRkqjI3IDc4IDEgMSAjOTIKeJztkjkOACAIBPn/pzHBeOAuJjYWylTKKIhBJEmSK2glCHdCMZw7Nyss4BQW5qaFouduVdgkv/SKOv8N5"\ "nRWCkaFOBiAJm3lt26W1okaZmkWs3JS/aCMAmzHYa0xOSA3OCAxIDEgIzU4Cnic7c1BCgAgDAPB/P/TKyoEbc96yh60jGCllJ6GB7juOaxMUIlC7MMf++"\ "3Y10Rd1CX0hNKfBoAQkHAyMCA3OCAxIDEgIzgyCnic7dAxDgAgCANA/v9pHNRYKDHowkInc6IBRDqd+ugMC7AqGT5Cg2/Mnfs/aymkCbDnEP812Ne1V29"\ "P0wcme7GmzBTrscX+RKOxdIoyAFYGvkIyMyA3OCAxIDEgIzY0CnicY2AYBaOAzuD/f+yidBT+jwWQLAw3CbuNo8IUC2MRxyFMlCn/UYX/w9lo4uhxjCKO"\ "aTyORDcKhgIAANz9xjoyNSA3OCAxIDEgIzY0Cnic7Y8xDgAgCMT4/6erMVGBEOOquY7twGEmhDhBqehkO5TCs4EduApMkwox4PQ6S8TtiToMddbK18RHN"\ "E/a9wkyNyA3OCAxIDEgIzk4Cnic7dLLDoAgDETR+f+frjEqGehtJG7cdDYEDpDykDqdzh+JMzxWkbcFBQos26LVvPtCMRHPU5oXvChtv1euF5KubVC+US"\ "eJrSZ4IaNVHqsJ5T7bRyLhbzfX3+lcOQAkEcE/NDUgNzggMSAxICMxMjgKeJztk0EOgCAMBPv/T2P0ABQGKOjFZOfW3ZEAQTMhhBD/J92Eo7BsYzkN5dS"\ "7LHdLg1xmaNqsGn3zDFuyz+phS26/pB63lAvKYP+LM/l7nGQvZLqj2UOZZscyHbt/wqVYZKXAY4NsIOMKuSCZXPrxBmEuAtmZHAuFEN9xASDNY6syNyA3"\ "OCAxIDEgIzk0Cnic7ZFBCgAgCAT9/6cLJCprkqhDh5ybjsRqIkEQPCEpU6s5NJMzlXHO4PB8N4mZEprmQJUembI5merAwFX2Fbp1DO0us4u3MUW5/Vern"\ "Or0X1nRcZxDB/+QAYxW3CQyNSA3OCAxIDEgIzg3Cnic7ZFJDgAgCAP5/6frkhClVB+g9CLOXCA1q1Qqt0Ai9DCdqMQLAokrASdkEIWrOfkXMds+EYdFdy"\ "wv+EzoAserxanZ1SRxatDSzGUJkzcRWKUBHgtynDI0IDc4IDEgMSAjNDgKeJxjYBgFo2AUYIL/WAA54lhMxWEbacKDxHAy7By6huOyE0vEkyo+CoYFAAC"\ "6BtknMjMgNzggMSAxICM4Ngp4nO2SwQoAIAxC9/8/XVC30KFRUDCPD7PajCidVRuCcMEQ4vMB4cQqTbBlLixj2eysN+kXtJIE/jzjj+ZIyEzeWtqXeCPF"\ "TWe1pfm8p87Vt9UBFOgOATUgNzggMSAxICMyMAp4nGNgoAr4////KEGYwAUALeKwUDIzIDc4IDEgMSAjNzkKeJztkkkOACAIA/3/pzHxakchNZ7ocShLt"\ "GO0TMUSYFGghkuBVhf4we5Paaw54FffzDmiSGpzCJq9pPxWJXPjHbszfDNlhmjygv+a/oIECzMxIDc4IDEgMSAjNjYKeJzt0DEKACAMQ9Hc/9IRRGzV2N"\ "FB8jb9hYqAmdmP2NV1HxhnZjqLDXGrnsAzY26gqutDIHIMFN9wT2b2UgOLgGScMjggNzggMSAxICMyNQp4nGNgGAWjYBSMglEwCkbBKBgYAAAIiAABNDM"\ "gNzggMSAxICMxNDQKeJztkzkSwDAIA/n/p0mR2AYhX10KqWJ21uMAjpmiKMpP4m+2rMHEncFIO2cMaOOMoelzWKgz5sOElusFvYLx5GbSedohTAhVOuGo"\ "sm2UFVV1uuKpaicqc9cfUBq4VvkrA7aYKy7GwP2c7PY64FFGt5T54sIWr5jCo3+Dtu0M0mHu3iO0DNAURVHu8wBxOEbWMzkgNzggMSAxICMxMjYKeJztk"\ "0sOgDAIRLn/pccYN7bMADVqNOEtp49C+jFrmuYzAEiFE2MkpGMJs+alfa1kzROkFhasK5vROhfqIzRvsQ5FjVkkfVdTdSta9DoC6yYtbOrD5zWQMI6i7R"\ "KNfg5VCuSeoK5VvGJbPV7yAGgbekE+twGfNM3v2QAof5aUMzggNzggMSAxICMxNTkKeJzt1UsOgCAMBFDuf+m6MSG0M9OimJDILMnjVxBbO9kh1lMgHAa"\ "EGEDBYeMYRQMrKYE6i22J4k3JIj5VsIRK1cYStZlV6wqRN9kQMLhXsHwPVW1GsUevbpcqtFygLK+9Us7xd2ZeGX+zVqr2SvEpHyhaV6qMK3jtokLvnDsh"\ "mpJS+6UKM187/jfinboBtxAY9XWwM+U5anf1h1wV7j4JMzMgNzggMSAxICMxMzAKeJztlMEOwCAIQ/n/n2anJRttrRqO9EifRAsxYjTiyldrVzHpiAoAg"\ "kCeA0Da53pCAQfEPRC7QO8l/iUA6gwKgGPi/T7EGYA+v4/y6/h1A/dimokJVQLh/D5ADZ7mLBtcrA5UZI7iAPilgr7YB5qb8v3/tfFJwhZRrd3RqEcP0b"\ "+6YjM5IDc4IDEgMSAjMTQ5Cnic7dXLDoAgDERR/v+ncalt5w6oCzFhls3h0Wq0tZ3l0i+ZIGglyo5QZKweMKOI5aJkdUpTSjdg2f3trMN5Omeon6t1Ukq"\ "XoUbZoQrQqdPFGjmxcsDgHqjqGcD4xjxK9xJ9znLfK93tNevEJp8psroKNqMvQ60K14c1EXUAqoGDtlhN/rEQFqWgQtEasbPzsxzUp0H3MzAgNzggMSAx"\ "ICMxMTEKeJzt01EKwCAMA1Dvf2kHgq7rkliVfQyaP3lV1w5LyWR+m9pCwUbZzdC6EowoQ6TmOK/PHp3yAe1pePOMUZnWd0Fc5cnyqwBWjGvDkPjhD4ze6"\ "7mtYb9jhUZlak909s70G8UFtHc0tkzG5gL/xF6+MzcgNzggMSAxICM4OAp4nO2SOwoAIAxDvf+lo4ODNqmI4KJ5W8uDfksxxlwHjRh3KIFEwoyUEBESOd"\ "0aY+HIJg4l2SpV3ZmZJbXjOLU8BPQVlvvbkegRLFmylOQt/SQ9QQU4QN5MNDMgNzggMSAxICMxMzAKeJzt1MEOgDAIA1D+/6cxMR6QdQU2PY2eDLw0zqg"\ "inU7n+Ogdvn2IuUxTpN87ZXaJiqfKSitUyMnQGFPckB9WKBrHhw3p+C5MH+HYMZUV6u+ASL9jVFaoRrJCBVIobVNQaqvyNJQVOtqp/Il6S2SF8v/SJ5RL"\ "9vU1bbpDj8sFP3UjCDM3IDc4IDEgMSAjMTI1Cnic7dRLDsAgCEVR979pOrV8zAVr44A31CMaJY7RuTIyB6GICVEaucogTyEElbVIbaIzpf7eb7MUunT0g"\ "ARJHrE+RqjcxkVklWNqyDNGTRMhek1EKon0iqBQpNSgi8xgEkloGFquSx2AoezNLRB6qA/7Av5onc7teQCPGTzuNDEgNzggMSAxICM5Mwp4nO2TSwqAMB"\ "QDc/9LR8SF2iRdFUHN7DoMD/oDSimfgydBcxpCdMOGDZeEqheG+q/pOxPSdi48JEewe7GeR8Nxm3kEUihHGifOHoJc4aXCfU0ZE2j4+vCXbKP8/C41MSA"\ "3OCAxIDEgIzEyNQp4nO3TUQ6AMAgDUO5/afybbhQYcyaLaf+kPLeYKMIwDPOHaMt0GQnchofgOhaoTw5BC3WSCrtSJ1oi+iHpbzYl+q09JBuRHE/Mz3Cb"\ "OtG3xBpLxkn4DDfGlVZ5c9/sI/ajP6tArBBQRmOMnFeJeCYf9/3klGGYg3IBipInEjQzIDc4IDEgMSAjMTM0Cnic7dTBDoAwCANQ/v+nq4lGXSmD6MFE6"\ "WmwdxgzzqzT6XSOYM1QDA2Cxw4u0XDvgyIllBwsEupH2HpCnrZAocbV0lOuyGJC44Gnt5FU1eO4K/8kxSsHwHOafdhq5cu7lCQ1gnXwQsj4PzajBauel0"\ "xm1qxozacMFQ8Z2zls2vSPWQB7abiANDUgNzggMSAxICM2NAp4nO3ROwoAMAgEUe9/aQOpBD+wKSTFTOtrVDMiIjEXAr/jdO3yBd1rwGDwh9glHEfLOOl"\ "58Rq3KZaIbgcyQNZUNDMgNzggMSAxICM5MAp4nO3UMQ7AIAwEwf3/p50yIfaBGxq47SyPEBIS4Jy7vXir5u9oanooZTfNu01UTtPl4pzQ9H+fGQ31PJH+"\ "Ih1tS5tC10LXMtaGGhew4pIlvJCmphf2AM9s8EgzOSA3OCAxIDEgIzEyNQp4nO3Syw6AMAhE0f7/T+PG58CdVFcamSU5NC1ljE6n857EmgniqJoalkodI"\ "HGszs6pw3m1u1yrWHkR2wqvwsNyNyhtv834+/iwZtPM/cIDZjaEWBjG2xtRwFxSVmSOpQex4llflBsPM3Uw7DxXYlvNrAun2YfYb7IAnR0IMTQ1IDc4ID"\ "EgMSAjMTIzCnic7dTBDoAgDANQ/v+n8aQJ2q6rJB5Me8QHJGNzjCRJkq3MKl23Yk0v3KEn7tnpWB+3t98/4Org+pKyw1X6mOJgE6Mrqf0Ggx76F94tnfW"\ "Cujesrnvdz3pS0AxSq6fbwWtB2/9OqR+24MgSjSn0BV02SJckCc0BkqixeTM5IDc4IDEgMSAjNjEKeJzt0LENADAIA0H2X9rpQUSWaEj0X19jRxDR+uQE"\ "K6w70fwaBvue3Zw8p9yIFTdj2fUz3POIHukANX12pjM5IDc4IDEgMSAjMTM2Cnic7dRLDsAgCARQ7n9pumsMzODEuqAJsyQPP61qNmkXfyOQQiYEIVTJE"\ "RUgV66pxcU6cbmI2LaVVXaMbIsOVjL2xUXmdM7YT1UvxtOZpaP6iZ2srfM/vcDEu3DxZoHByZwnb0j1IiGHWj0WUwG5GI2R3XNVODPFWYqEAMRo0Rswmf"\ "wqDwW94lY1MCA3OCAxIDEgIzEzNgp4nO3WQQ7AIAgEQP//aXpVQFgqGA/ssctUY0zTMTqdG6EwWFMhiOcFIYBLUoRNNJAsVGCSeSouBiLW1yKLnAqA8Ik"\ "CQWLCI7JPF3JTHtHaIyGJXoLCfQoKTnZNotiujhRV28WOJO3YLXFyfSygtoTnnggA+VmqEBQGv0QQQL82nU7nrXwNkKeRMzQgNzggMSAxICM2MQp4nGNg"\ "GAWDEvwnKP2fABjBKnAFGmElRJhCjDF0UzKU3YtDDUEVowqGkwIS8y02MKoCXcUoGAUDDgDLjna0MzUgNzggMSAxICM0MQp4nGNgGAWDHvwnLP+fEBhVg"\ "isUR5WMKhlVMqpkVMkoGAWDFgAA5N3PTTUyIDc4IDEgMSAjMTQ0Cnic7dY5EoAgEARA/v9pTLWYCxWPqp2QoQU2srVK5dX0Y6ZBwKCQigquJIHICIQ8GV"\ "FiuiZ6GXVJk9+ArOuXRoTUxsDeEGTcMeijllw2PTTjloeMH9s95sszWGTGMfnBgQ0rDLyIQbDWhrQK0REJxCvapFcIiZ8Oe6Iyu3benDmnjDWwDP60KpX"\ "KH7MBOuVEAzQ5IDc4IDEgMSAjMTQ5Cnic7ZVLCoAwDERz/0vHjYuamfwKhYKZlSbvldKPikwmx6LkKeSX/EJQkh4dOD7OlZgHJcWNUeFXo8YvRlXQLv8a"\ "UPNeuABHAw1b6PQBgCkU+jBkLsgIJwT1hXynK0IIkLOito8VfwL8Bn36jG9c0d2PQNkQ6RkiPUNMmni2vE6aOJViGJQKftt/ekuYTCYX5QEPB27YNDMgN"\ "zggMSAxICM1Mwp4nO3VsREAIAgEQfpvGgtQxx9T9uKN+IAqja9z12noN93vfqOnjVAURdFZ9PU2JOUtcx6NqzQ4IDc4IDEgMSAjMTE5Cnic7ZOxEcAgDA"\ "O1/9KkSWGwsKNcLjT6Bixe7gCM+YQxHeEWkxG4szWZgtInC+zb/9Mvnw/76Tu98hdB9at5faZBs67x0/oU7QbuVwBaQfQBqQBIBUArQCogIeplgelFY6P"\ "zxl4mnVYG+Y/2T/nPCsaYQ1z0eEQDNDkgNzggMSAxICMxNTMKeJztlTkSwCAMA/3/T5MaW4ddMJPCKtEu5ISIzeZRzp0Z7RyIc4XiRJE8MAxfDMsno8Ff"\ "Bqmo0Z4JD4uba/CwVDxqNQ96J4QSEJ8Bu0AmvBBcqM8RXPQK/xTGb/qt0PpaZ//DWEATuAWsUVstoFYZxwpqWyfDjV369EJX9vz8yJofilapvDQQLhSGY"\ "0fS2WrBm83mYT5n13bQNDggNzggMSAxICM2OAp4nO3UsQ0AMAjEQPZf+tOmxKmI8NX+CokqSfpPAnM0SNAgQYOwfmPeAfOF/TVobB/utXzQ7ek/wf8K/0"\ "NJGu0Amb7/DzI5IDc4IDEgMSAjOTIKeJzt0MsKwCAMRNH5/5++hdqHTTLFbrrKgInkKIhSp/MxvBlewSt4Ba+sYdZ9eKyoY3QWCtNVyaa7kUxTJ5rmTXU"\ "sPcJgePlzO66qQFVfIZvGxsY17PyXDaUZ8BA0OCA3OCAxIDEgIzcxCnic7dQxDsAgEANB/v/pTZuSLSIRbqe2r0F4rSS5Bci4KoAqgCswvoBh8/w+7+Jb"\ "91+RTx54UFz/d7sneq/0HibJIR7CJQAdMjkgNzggMSAxICM5Ngp4nO3QUQrAIAwDUO9/6Wyg06Tt/BjDrwTE0gdabc1xfgwAo9H4CakBqXsDBfY2SKXCs"\ "yOgtEZJu5wBQja5IdnUNGGYXN/GOlb+Q6wkIy1samnVnEFf7daNOedyAWuJ7BQ0OSA3OCAxIDEgIzExMAp4nO2TMQ7AIAwD+f+ngxBCoBJDDFNb3+izJ0"\ "JKQgjxIcyYuAhXgbgKR4G4iUmBuAs0iObI4L7vVn3Prvuz3/WfDWNg+2Xxy8GwQOOxcvJw/Gnwx8efN/+Bbr5iLO4qGjcVj6tiYiGEeCsZ/QmXhTMwIDc"\ "4IDEgMSAjMTM0Cnic7ZTBDoAwCEP5/5+umcbARkf1ohd6GfKywyzUrPW1MFTCHQYKDBQYKDBQYKwqYcQEOqbwxht4Yf9YzoFzyXu0srlYemeZPIpPskRt"\ "96+zQ03/p8Rfd5DMhrv/ZJrY/E3jmVrxctL0Eg7FDor9FbsvckNkjsgrkXXK3zpjW60XOgDjBXCsMjggNzggMSAxICMyNQp4nGNgGAWjYBSMglEwCkbBK"\ "BgYAAAIiAABOCA3OCAxIDEgIzM3CnicY2AYMeD///9QCsT4j4NmgFJUtxcX/X8Q0//x0OQBABxSiXcyOCA3OCAxIDEgIzExMgp4nO2SUQrAMAhDvf+lMw"\ "a10xoZhY75Yb7ER21sI9JqVRKASiyDGEpBxAha0PDi4Ky1Y87B9LQVnAePeGHCmfV7iK1PUJmd3t0N3f6jjD1xYbkOWborDzWDZpCBNLuR0msJmJT1kx1"\ "+Y60vdQFWYkXJMzUgNzggMSAxICMxMTMKeJzt0sEOgCAMA1D+/6enJ2SsFYOMcGhPQl5wg5WiKMmxmiEgyEKwaBfwELcciXsD/oZ0wQ6B4vcha8jHUg6p"\ "dn3PcZjiqCWRUMt0QyJcJBJzgryuA3BOgGjY82kvthOIAdGpukMuAd2BouzJBeM9c5szMyA3OCAxIDEgIzEyNgp4nO2SSQrAMAwD8/9Pq5BSvElxercOp"\ "ZYm2FnWGo1GXni/kPmOAEUARjDEfE+EPyQWptwZVKWfn6mMFSwF6LIaLcBWHFtWYDUz/QbaFldAfzB5qOMKcVlghbe2fX4O4sV8hY9yS4PrRsg5B0Llnu"\ "A5u4BCNPloNBJ6AM3FLeEzOCA3OCAxIDEgIzEyOAp4nO2SwQ7AIAhD+f+fdtnByWqxXHRZQg8e6IMAYlYqfa52S9tLrD3uChvegvJWjL2MCIM4pzCZF5u"\ "CDCMxHtGJqSaCuclAMwTJqUWLX1Ol8AACaJiJy1SlehEB9QtWlKUgdbihtlC5vpCS0CHKT+ReGKT2dYJKYKXSn3UB0FutYTUgNzggMSAxICMyMAp4nGNg"\ "oAr4////wBFD3/UAKZWVazI3IDc4IDEgMSAjMTU1Cnic7dRBDoAgDERR7n/pmghKKX9KjG40diU+UJlUSvnrg2V7SQA0XzP1OSj1OpAfiEXwEZdJbTVJ4"\ "dwui3hkvYlWbyVEpnN16VHmRZgfBTNt80RjGoxaQZiPOlmG3ZXZrWUUXmo0j0gEOxpkLn6kZXiGVBLqxu169HKkZiTtLZpYsp/3oXNijDnMFLTs8+OIoF"\ "zVbiW8tDYKOK5gMTkgNzggMSAxICMzNgp4nGNgGAUDB/7//4/MgYjAxaBssoWwGD8KRsEoGJYAAIJyMc80NSA3OCAxIDEgIzE2OAp4nO2VOxaAMAgEc/9"\ "Lx0YNn4UsamHBVoaMwAPEMVqtVgtoXuLJLT+RcjY8O5QxnubIB7Al2bGsuySqxLIaQEm50NAxbMg6eHa9ZF2H6Vsn1mYy8llrX0lrssCI5hy/h0P2x/DH"\ "1eCbUmp3MkhyksTgSnhaQD1b18Gco9ibz8oWwaF8fVFG9JKpra/aYqyt3Noyv2+o34S4VopYjyek4bdkq/VMB+zd6iQyMyA3OCAxIDEgIzg5Cnic7ZFLD"\ "gAQEEPn/peu+EWNioVELOYt6LyFKGbBP6Ah5ez7OOsxsXdZRMrQ2l/pVq89bVezGHerPh5bbur4VxMaUgPi7LzX3BZqQp24nvjoIAhekwAZ061TMjkgNz"\ "ggMSAxICM2MQp4nO2PMQoAIAzE+v9PR+km9MSlCnLZ0tzSCGPM3yBlKghJZRHeNurG5dZZd38e1XraXoXkQYoxHQz558s1MzEgNzggMSAxICMzNgp4nGN"\ "gGAWjYBSMgkEH/uMDlErjtXZUms7So2AUjBQAADXBeoYyOCA3OCAxIDEgIzI1CnicY2AYBaNgFIyCUTAKRsEoGBgAAAiIAAEzMyA3OCAxIDEgIzEzMQp4"\ "nO2TsRLAIAhD+f+fxqHXE5IIDp16ZLLwoIJgNvqlfKtxKyQbmSALGA4pK3+26ltv84tiBcGeIogI33BogefgJeCxBAZMA6lDDEALM0APKTK0gAkALk+vK"\ "Bp4yAkdjnVBJJJOP4fIdmD6kZMEjXU59mpx9G5db6dyj0aj0edaXQoR/TE4IDc4IDEgMSAjMjYKeJxjYBgFgxf8RwdEiYyCUTAKBj8AAFyhMs4xOSA3OC"\ "AxIDEgIzYzCnicY2AYBVQB/yEAQwBJEMpBEkOShbGQzYCI/ccqhGo11YXQbUQRQxJC98d/ZIAzJJBCYxSMglFABQAAO2SLdTMxIDc4IDEgMSAjNTEKeJx"\ "jYBgFo2DYgf///49KD7j0f3yAYml0mwaPv0elQdIMeKWpDWia1kbBKKAWAABu+/8BMTggNzggMSAxICM2MAp4nGNgGLrg/39UHhRgCMCE4Ey4EEI1VAhJ"\ "OwPxImjOICBAhJL/6CJUECDTCGRAnMgoGAWjgAgAADHFk20xOCA3OCAxIDEgIzc5Cnic7ZAxDgAgCAP5/6frICZAq8wm3KI0WgpmHwPkyqn1kfLNz/C7W"\ "iL7X5TuCURKSq3mIBvVq0/IEbdCkyJvA7FpWWJJqBMPw/BgASTNg30xMiA3OCAxIDEgIzM0CnicY2AYSuA/CCCxCQszEBTGooIBOxOFPQpGAR0BAOCnLd"\ "MzMCA3OCAxIDEgIzgzCnic7dBJCgAgCAVQ73/pX4vIAQ0iggb/qnyRKFEmk7k5iEuoscSl1N8VEwqrGCu0WRXX3hes4GfmSArlf63itAu24Y/+iNJWPXD"\ "gpWUNUgDbwVK8MzggNzggMSAxICMxMDgKeJzt00sOwCAIRVH2v+k66MQqn2tDjDG8qUcxAUQqlcPyqAHko0zSKfO8V3Ep9iHvSwgtKwetKg+V+qsyOwRV"\ "5nzBiYbbATfNZILYqHQ3Kw2qarpS6mDF+sjVzoLJKFDcJDz0stBcmQZ3/7N3NyA3OCAxIDEgIzI4CnicY2AYBcSA////Q0gQDaH+o/Bg1LADAP+DHOQxN"\ "SA3OCAxIDEgIzU0CnicY2AYBaNgFNAf/AcCVC4DTi6mJLLAfyiA8rCowMrHbR2m7f9hBsON+o8EsLiCfAAArW9MtDE0IDc4IDEgMSAjNDUKeJxjYBj84P"\ "///8hsOO//fyTefyQelAEjIKLIJIKHacPw4I2CUUAvAACNwlykMjMgNzggMSAxICM3OQp4nO2RQQoAIAgE/f+n6yKsuRIlBB12bjsKKpqJjxhO5dYCUvT"\ "UwhYxzXGfx0PzrneaLuvoxianV6KamrNHCFsW79l+04hCCSGeMgHClLRMMjkgNzggMSAxICM1Mgp4nGNgGAWjYBQMR/AfHw8IsHOGpex/DNn/5MmiSeLT"\ "iddUfK4dDnL4U98oGAW0BAAWdbVLNDYgNzggMSAxICMxNjUKeJzt1VEOgDAIA9Dd/9KYuGgca6GGfdIvN17QGKZjdDodFzMTN+e+rM2gZh10PSWo4K17U"\ "9e4hJ+D1djrg7UI1zXHmw5bAx1gr+PWJZ3gis5wQac41PtRWfVW08cRH0PSep5DvXU0NWgYqYYDI+r3jP9oLepnxb+eaCVqf6G0VvRnwfSCXbIHSTS6IR"\ "lZPpj4l0IwqgQzf0AznHxDO53O0VxClEHNNDYgNzggMSAxICMxNjQKeJztlNsKwDAIQ/f/P+0YbKytSRRs38yT6GnoTa+r1WqtMjOUZCyoQIcXTtJmmCY"\ "GTzZHf66+RGBSwxshNXIhm2gOO1paA1rAK62tS3QAV+gILtAhrOigT+aFqFWGxByyzvLW4+8n+F+wKUxYgzBxADFbEBDB29Ni9J2zLnvge0q8zJgLHz0F"\ "8xmcsZaw7xDeoGBwzgqtKe23LelWq3VINyc1YK40NyA3OCAxIDEgIzE3Mwp4nO2UUQ6AIAxDuf+lNTEGcWtLVfxbf3TwKDI2WyuVSkrbKTjFYLQgD3XKw"\ "gco89BceqX95NaRVmY4WdRsnTlIO6XhHXFzeKn6oJGfpCUSc3xEDPo9Tml8PIaTipQ57y/Tb8HNJ5qoJRgVm0oUMpfVmWIfP0J59hyuwiHt4tclmub3p2"\ "cucLjWqFCjRFl7YzyaB33DeYvCGfWje0IvwHXLeGNzp1Kp9L92om1rozE2IDc4IDEgMSAjODIKeJxjYBgFwxH8//8fjYsi8B9NgCg+jZxKHYDuPgz3ElJ"\ "AgI8tAPFz/6Py8HNxmYTNXjzOIsjH5k28KQUjKcDcjxBG5yOJoGtiQAW0T1IADoKrVTM5IDc4IDEgMSAjMTU2Cnic7dVLDoAgDARQ7n/pMSyISD8zRKIx"\ "2GV5LaEqljIdqLHYPd9uZtt32ilMdp8PbXDiSwLNaQyQXBWcQWaFzwQyazpzbTVnkNnpQ9etrWFeSdyMsGuN64b8fTZWOc5kQ2brjLO5kDmhKXcHjXmns"\ "kMyyqY1lj0/cFYmGKhS2fh5ZAymImtHGahSWX+XMQbtZtzkx/nH3nEAOKipZTM5IDc4IDEgMSAjMTUzCnic7dVLEoAgDANQ7n/pOCwcauknVTaKWTZPHU"\ "W0tQdBD8fefrJcFdhKxbAvZPm6E45jAOW6yBlo1ohnwjEMFrmzjRloNrzrRFdgnpONz0Cz6zGmU/MCs5ye2myaFtjkjJnHrNxinjKYeVdI1TzmWLAw+nU"\ "wlC5ChlSxrLA95KZ0lCxThuyairmq8pHqfaK2+nP+2ToHSTKrYzM5IDc4IDEgMSAjMTc2Cnic7dXdDkAwDIDRvf9LlxBU9eeruCF62R5bWDtj3AyZg6ma"\ "iSAnyK2icls9Z/sq+XJHMXO6FLtzIWL2eXQYr44n+22wfoOMteVyRLB7ERu4yQHbdDEMetV8Tzwy6a6q1mCRwwPYntNwV5NvMM/ZrM8u2Qa7OCcXMSeYs"\ "q7B3LeyN2PwKW+w5GBsOzjKFlImpaKsMR56KAOliyWTUlGm77KKwd//1/+Yf/wxxwT7dshGMzkgNzggMSAxICMxNjMKeJzt1csSgCAIBVD//6dpUWMEXL"\ "haGxtZOXjwOWVra4aQjHKCmPQ42wXqdlrppWCEF2wdpZaI5DSMYlnp0ouaYcQmhGbEZq9+gjWeFU5odnvoVN83LCrBgxXsWRM6k3/HbBrM6rKQ+TrnfA6"\ "yIDgVzsCxaFf+kJxi2cj9Sc3aACOeAZLZzyNj4iqS4bLBVG+qWKb/ZRXLHms7644d/44DPQHATjM5IDc4IDEgMSAjMTU5Cnic7dTdDoAgCAVg3/+laWvN"\ "H+DAcXbRUq4Mv3BaWMqOISLu2Kg614+/wLYL7ijIExPKyduM2MQNJljonnmClZzV6djxrHnoVC+kxUImigUN6D+gNYNyKr/GdBqsarKQ2feMsznInOCUu"\ "0LK/N2bLD4jgs18P8mZ/bdI5qqxAGYTXdQ3Zc7wBoYaoWpFYtbfZRkjbtCszIkTP4kLYlGuYDM5IDc4IDEgMSAjMTY5Cnic7dNbCoBACAXQ2f+mjSich3"\ "q9A0IR+akn52WtvSHkDAql8BKZ0zJ2WiRZK2LM3rSc3Ij0QEwhRk8FubUdxg5cDdOxTFn68DS7Qeb6HJHjVvnLQDYUkRtKgMnCAjdVIAu+CZvFbkkHbE0"\ "HhzBZl9nkBjNO/OCUu0LK4n3kp7KOu3Lwfsw4zKxiBHcHH6ixHcfcF7CrQqVNcLOJxUodyf744+txAE4H0zs1NCA3OCAxIDEgIzE1Mwp4nO3TQQ6AMAgE"\ "QP7/abyoMZWFhbRNjOxJsRNaRZFOp/OVKEoJeQojSr2f+EiiQ1hK0kor6ly+Q12rN6h7cUZpWY1XCbRcPVfSSsvKvmFb2X9m1IpVQ5VTYxXtyG3FvQ328"\ "BOUiSJlI0bFJatVSTG1sFWo8KxMV+Ad+wp9mAUKzkBdoTHD4+YoZ0gj5Wwd7LDT6fw3B4koVMgzNyA3OCAxIDEgIzEzMwp4nO2UUQ7AIAhDuf+l2dc2hw"\ "Ua3VxM6J/wUlEUkVLpa2mSfignIIcQg7kRtRHk7S7beMoEtsOMsMymRutKKuiXtmxtRf3gKUoJqps9zhTrRh2KoBFpheuPEMDB495gkL6glGGh14yIdjM"\ "X0IDcphS1DopqOhNh3dRL4R4T+QlHdQASBLNbMjcgNzggMSAxICM3Nwp4nGNgIAD+gwDJUgTlqKuNkJF4tOGSwieHz8iBA/9xAdxSeHQx4IwMmBQeZ4xK"\ "0VQKX37EFpEUSdHRX6NSlEvhiE6qS42CEQ4ASp+jazI3IDc4IDEgMSAjNzAKeJxjYCAa/AcBEmUY8MuQpQmP8+imCYfMgIL/OABlUljsYSAzSEelqCSFL"\ "0dhi0iKpOjor1EpyqVwRCfVpUbBCAcAtcOibDI4IDc4IDEgMSAjODkKeJzt0UsKACAIBNDuf+kp6EOZE4EVLZydvrDCEPaClJURBDeAYm7rWJoattZs3X"\ "GJwySBQwlZ0Sm/BCRmU66qtnqL2ztjyDdrtMf/cztrbL83zOMRiSZYvFIyOCA3OCAxIDEgIzc1CnicY2AYSuD///84OGDuf2Q2qko4/z8JchhmUsEL9AT"\ "/cQCK5bBYBZPD55ZRucEhhz1mKZQbRP4blSNdDlf80kJuFIwCNAAAEwCibDEyIDc4IDEgMSAjMzgKeJxjYEAC/4EAmf2fJAkMVVglGIiSoAQgO3CUPXLY"\ "wxoAAJuL/QMxMiA3OCAxIDEgIzM3CnicY2BAB/9BAIPJgMbEIcxArjCGM8gB6A4bZY8M9ggAAEmG/wEyMSA3OCAxIDEgIzY1CnicY2DAA/4DAVZBdNH//"\ "zFF/2MRhPDRRKE8FFE4B0kUlYnFhUh6MPyA1SH0BtiCblRwVHBUkFaCo2BQAAC2XRvzMTkgNzggMSAxICM0Mgp4nGNgoD/4//8/Gvv/f4QYlE28EAOq0E"\ "ABLNaPCo0KjQqNAroAAHge/gI0MyA3OCAxIDEgIzEzNAp4nO3UUQ6AMAgDUO5/afwWSikxi8bQP9mbZhvObLP5cTxnQCteUIRjmeC6mOrsY2GgWYREsyU"\ "7GS3b9GD78xlQl6hNqUvUXqc2pqCf87SP0DM7oNBzB4umPaI+o3gefWn/c5ePRHYXES3cBhSKugzSuh8nF7zQ4iVF6wQUss1mI+cCqbo93zM3IDc4IDEg"\ "MSAjMTY1Cnic7dRBCsAwCARA//9pe2kTo65uSg8tdG8hg4qEiHwqSiFCaYp05DyUZtI7xo6ACBgzKMK8KnAn8/paIoWg2kdIrQiogarhZ6VCGYS3ZRFUE"\ "QE1nyFSC0INVwSUQ7lKkVcepaUCylREiUJIGxRVhkLDFHlVIG2QUwAJhRYFkVUN0gYZVSCh0FA90gZdqkb2K8FInkNCIaGQUEg20J8/380BeWcz6TQ1ID"\ "c4IDEgMSAjMTU2Cnic7dXbEoAgCARQ//+naXpScFmg21TjPsqJ1Elr7Xhkz436Da3rE/le61bCP46o5CXlgLp8rvkcDmPtvhEUwtmFc3A031Oto/2fMLG"\ "qdXxTGEzt0DpuPJgyDmxHaSzZW3PhR3FOVz6kEu4moU8cq/jEqgeJ1mWOTZW3tjWm55J4HI4L5rQFWAr9qZjghWDuWuspXFlZuSQbya4WBzQ1IDc4IDEg"\ "MSAjMTU0Cnic7dXBDsMgDANQ/v+nqXqhDTh2kKCqUHys3zzWdVopK1LvzOCzh6N26/CWQ5ydahKXlAPq8rHzObyMtfuOoJCnk2dwNL+ntlX33/Sx7yo2b"\ "IT+YTzTevhlprGwDUWGm0r8QxzT3zx1kae/e5kejky/e6VtzXHX0umhIxo01ePwuqPpBPgo9E+lC6SYu3TwXGYymRW5AIRIFwY0NSA3OCAxIDEgIzE4MA"\ "p4nO3VSxLDMAgDUN//0uqiHTfYQohJ20XHLM0LiT84Y9wNAB1ra6Ch0dBP5+FXTav0RI5+k1pfQYVjXus129nJvwyE8KXkhKZ8S+WcD3OdvZFpMRf+ddz"\ "uWq9pzFbrH/LeXnmFg7A689LzVfdM08aFndgpHC41D+Pgn+ARD2uJv3zqnNO/PFYXdkqv3d26CuxLRustJzTJION0nGtdgkxF/VTWoJTzlG5eyxMnTnwi"\ "HtcyMOw0NCA3OCAxIDEgIzE2OQp4nO3V0Q6AIAgFUP//p28PrVKBy6XV1pq8FUdFsmrtV4GK5bjPgllcsV9I9BjwAO3LSyTficE6/VZohVf6Kz4NR0baZ"\ "ELN7kKhLibbmDHtz4R5L4es9AZIdACpvUROS/Y0Ar1jlRKOCd+xWPbV/rb6OVPsOUY47MMCeQnKxF0+s5it+C3heM4RbDOItHfbx8EMgOHhYtG/xd2Eqy"\ "NpNIMrVqyoxwYtASryNDQgNzggMSAxICMxNTcKeJzt1dEOgCAIBVD//6dvT5nKBa5tbm3Bk5MTKrNqrYIEADrmtINx/FX7h9DOjylkGGkiPW0yro5moVC"\ "Kg2OsOOzPguNeTtmk7WPh9FLv2EcI78qO7Uagb6yyhbvgGYuyR/vb9u+ZYvszwmWfFsi3oBQe8pnFasVvSYzXXIBtBp5m0xw7FQDD3cW8fws9BNWeNDqC"\ "FRUV+3EBvrUWBzI1IDc4IDEgMSAjODIKeJztkDEOACAIA/n/p2uMDAptYlxceguhFxWMMMa8AJEA1WSCagButvYwqlHH6YMsz4jkOSTJt/FvhboK4EaNq"\ "xb89W+zkKVW6aIlxjAGDF6fYTQ1IDc4IDEgMSAjMjA0Cnic7dRBDsMgDERR7n9pGqlSYuw/g7ON8KqFhxOwyRgnTnwh5nyFSc8Yy6injydb6V+BzY9eF4"\ "OVz2rYm9cxoKh3Ns6qd4h7bNpRsLFLNpn4nkhYJs75XGJ6xsY+f1zpmpgrwWeBDegwLJatSSOE5VCzXxU2L1am3I4Ro5110teolqrapdzbe0CYN5f7Of8"\ "Sifn2SKzOBW1sKWnXe6DfgSrRtRZzV+o6M+avOa7PnhOEZi0ht7Cnte3MqZsTZt22l35hT5z4TPwAW+h1pzM1IDc4IDEgMSAjOTcKeJzt1EsKACAIBFDv"\ "f2mjRf8xCyIKZpb6UEhIZCMac8jcGbO66sAYuTfm7Tin0xQSEpIviPdz+UQzMc0eMUzqrBJoSt0ydRmbtggeeaj0l0CHURS4egZ6hPoM81QCytLIRjM2I"\ "Dc4IDEgMSAjMTAyCnic7dTRCsAgCAVQ//+nHdvDWHZLKVkF9z7qQYNAkcHonWkiQZI0xiGJYxI2RcYcEecDny4NDc1a4xyln404h0PjpofeTthA9Km3UF"\ "HGyBS1Vq2KXW9GKwp+YVdYBQHD7J4LSG3HRzM2IDc4IDEgMSAjMTE2Cnic7dTBDsAgCANQ/v+nmXGZU1agu3Ggx/bFLDNR5H90hCAZIoxqiu45RM8YoTX"\ "55l3cg/bBQWeNkSnz3107yRXOtU2bNvVN8viTRngTvdisidBaaAPR1nvoqDEypX6V19iTzdGKgr8wFFZB0OlUzwUWteQqMzUgNzggMSAxICMxMDEKeJzt"\ "1DsOwCAMA9Dc/9LuRPnZTdkA2RN1nooEEhH3BoD4aMpaQxPMa/EXQSLf6JqIE+jGJiYmx5DszcoJXiLNGhGmTP4SamqvTFtz05fkkKdmvAl2MWChW3+BE"\ "bG542yVByofyUUzOCA3OCAxIDEgIzEzNQp4nO2T2w6AMAhD9/8/jTFeJqPQaXhwCX2zPTKm0Np3ya4U6AWVAyWXSjovs9RaEjIAZxpjdxhRPYuKPRKfUo"\ "mHad87c3AxZVyIGQ9RjsVfxE3QVoNri360EPjQCFIU+bVCSo1j4kAdmxhfoZt7UHS95VJItSmILp5qraii/k+VSktrA4nmZakzMSA3OCAxIDEgIzEwMwp"\ "4nO3SOw7AIAwDUN//0qAOfJpgg6ADleKRlyqEBohEWtITzZOvL+YuE7ZFQ24FhEuBb/hycR8+WHUy9xkX32TcywuD4eRRQVn+UL0OdpmSke5E7SFh12hs"\ "rjed/5+sPBL5KBl8yoaIMjggNzggMSAxICMxMjgKeJztk9sOwCAIQ/v/P82ybHFSCi5mS3ywT+LxAhaBraVkTTkJ0EiMxPgO9ULwFSNmGvUzBRPV/sAm0"\ "6T3YyaWhVhYogN3ojLo9SHfsJlcaGPRjrqTKh9Kb6+5pCcy1qBiGDFLGBZjvoZgjmtxXx79jEfIGTzraExua+vUAd+InnAyNyA3OCAxIDEgIzExMwp4nO"\ "2SUQ6AMAhDuf+lqx8qbYAZiYlG17/tQZdBzaYeFVYN0Ig1LRttdn/biWWn7eXSdULvcVAvgooQlW+2cgre8XkpjSvcURZBQtkXi9w6SgfzAXR9GhmrJ1+"\ "vMsQBRGJsPFBV2EwQQXKd+rcWLu9HxzI3IDc4IDEgMSAjMTE4Cnic7ZNLDsAgCES9/6VpmrRlRj7GxoUaZ6U+IIhjKUezSG7FaHBS0sSfpO5yeVJAMjS7"\ "hJ5KqvMPYgwJEIQ/BqCdqa1xpqF34TTrJhHyrhhYUpE7mA1Q/zQ8Jm1kmLEDOsjaRg0FZqsJ/TQCS3/Co1G6AO4vRsgyNyA3OCAxIDEgIzEyOQp4nO2TU"\ "Q7AIAhDvf+lmck2oVBITPYxjf3Yhs8YVktrRz+QdOWkQpRJiZ4H7yFD5kVIhSLTBd+lrSNKikWEdyJufUB0AKB1dJx1f0EVztZ9tJ+YlRexGBnEfjGJrC"\ "JqzAZo3g3GCufTqwxxwBnysdFAuUm0BEYMwJrTd/SxLpzIY6syNyA3OCAxIDEgIzExNwp4nO2SwQ7AIAhD+f+fZgcN0BSIu3jY6En2Gqe1IqMRSRtUMkW"\ "kW2uVETOckWA5PFGHrgsPrvjd7+smCgMz2MNawUR78+/BSomamcM2d0LEn7tBaTAfQO/TyFidfP2UVIfYIK6NF6oqmwAK0EbKYfQzPVjHWLYyNyA3OCAx"\ "IDEgIzExMgp4nO2SSw6AMAhEuf+lx40gUz5uqomRWQGvIUBHZDRqBcAnTCz38ROoGeMj4oXAdRhdLuChIff8bMvZ2jshyjTKZ41Ea6Edo3T7/Sid4ha9O"\ "WF3qIyhRPVXBjt4B0XbXIaqzCaEHLQ0LDv6mQ5JtU3BMjcgNzggMSAxICMxMjkKeJztk1EOwCAIQ73/pVmWOKHa8mGM2xL7yYsIBUo5+qvslgQU1iBjT2"\ "hEHhnYNDKBkjJqv6Jr2bJItl9YnmHciwffDGC0rSVD1HmAedGFhohvEbE+xPI5ot1PomSVl6PlxWdGsWd6KHqU/ZRhg/DaouD/juDNAPjIOR29qwuOCW+"\ "fNDMgNzggMSAxICMxMjEKeJztk9sOgCAMQ/f/P40hIezSqiPRt57HtmzDoZkQQoi/GZMXZasjeqhUeVkgpCSegin4QVdqnafb0AbFCkL3E4WrGjO6UaSf"\ "/CJqDEVx4adlaRaMvZ2ksWzYJHROjm/bw8VO74I8GWxaSpFZ8C+9GVoI0eQCNZm0WjI0IDc4IDEgMSAjMTAwCnic7ZIxDgAgCAP5/6dR46BAS3RwMXQyV"\ "1EsipRKpS6dgtA56mRwWMdihAeAeDucdX2+/Qt+k0NecGSYgS3Hzxr9FIKNg+4nD0FYKL7irB2VJKo89QfcJI66D8ewlB+pAT1ID/8yNCA3OCAxIDEgIz"\ "EwMAp4nO2SSw7AIAhEuf+lxy6MogwqCU3axLfjTVD8iFzSwUMoeL8hd6Wcho8BkBOgw61OVKH92NyqebdamiGqcLydee8tro9+mywf/mUrfxqAB+ph6GO"\ "z6xSSmN3d2S5/pQBPjxn1MjUgNzggMSAxICMxMDAKeJztksEKACAIQ/v/nzY6hMpcVEhEtJt7aYIr5eugpImBtAb69WrD0qC8hksl4TFERWxHTOkIvop8"\ "LWGVbjCAu88AFPNlI3tHwOasaaL3gAQY4g5tAzCISeA7FG749Y4qMdYc8jI1IDc4IDEgMSAjMTE1Cnic7ZPNDoAgDIN5/5euHoz7axfCQY2hB8j6ERhjj"\ "LH1jHBK+RoQggZcAzuZAzcVv6Z8xwm4MBIFdPBh8brDJOx8WVfEUJ60ivkWsrdBB2ruM6BK+dAt/i5Y3Gua2HvwT5FaIDdA0ybED4hmuPUfHXEqNdkyNS"\ "A3OCAxIDEgIzEwNQp4nO2TwQ7AIAhD+f+frtlhGbUlMx6WRe0J+wwCasTRpgJsjEt54QBk8QYoVXn4r0W9da4gkDpfQh1P3hQGSCm3UQGtfQSoKh8uk23"\ "uYzCZa5g89yEvIBH3jcwIaZfxCdkKj9ZRA3UGHfExMiA3OCAxIDEgIzM4CnicY2AYIuA/EGDl/Efn4FRFmgQDURJUA1gdNMomnj28AQDgYpdpMTMgNzgg"\ "MSAxICM0NQp4nGNgGMLgPwhgYTOgs3FKMBAjgVURugQl/iAAcDtglEMmh5bRNQQAAJC0mWcxOSA3OCAxIDEgIzY2CnicY2AYBajg////mCLoYphC//+ji"\ "4G5GEJoNvyHC/1HVYSsDAsLUwjNVBxCAweIDFUsqkaFhqDQAKe2kQcArLexTzE3IDc4IDEgMSAjNDcKeJxjYBgF1Af///9HMCF8qAiURYYAhqH0BahWIl"\ "yHLDIqMOgEMCNqFJAPAIxFnWMyOSA3OCAxIDEgIzE1MQp4nO2TSxKAIAxDe/9LV1fSkKQq44wbshJePzRgxNZ/yo6doq0KEcM6eZVTbJo6F1VopEg06Fu"\ "WTdV6HpmGIxEK8nIKZjoXKis+gJrnAfU+LaRW+3i44q54GEkVRA+Apr8dC27JDaP9kDMhbJ7NA8jsE7hwoLjxQDetUHmLUSLRXWf99j8PvBkdW0VdDELO"\ "aGur0QFt16JsMjUgNzggMSAxICM4NAp4nO2QwQoAIAhD/f+fXlDC1DA6dTDfJdtCXSLNRyDTkThOx0KrKIP2Wee0TL/e+wU+kL1QtQm12MPPw32hMWzv8"\ "MYtA3aRxIj7t1HbaKoyAD6QLuAyOSA3OCAxIDEgIzEwNAp4nO3S2woAIQgEUP//p20fWtJmcqMINnDe4mgXSSTz2+iTRd1sPbbxYqucar0oWhMQYe0C5u"\ "oowRqOMTq6AtsUkD/sA4OZ3IhEI7QTAp0aPFH4GAN7fxe1xgqFTpk5hkdnMlEKBJE61DI5IDc4IDEgMSAjMTEwCnic7ZJRCsAwCEN7/0tnDMqm1WSlsA/"\ "B/NVXq6aO0aog3FLwl0TZzmni0aNfiZRpWEkA+0tYKeaxCyw0vWsPsQhhhtL+SOITzAefUWFKRSjnpA5J+7TxCQ2LQdjW1rzNZ1WADHkcUKuldAEDFDfX"\ "MjkgNzggMSAxICMxMjgKeJztlNsOwCAIQ/3/n2ZLZpRLW19dQl/UHieCZGO0Lpe9UkxDQu0AadTP59ANkGmI6LbqnbyDIFn8UvzJzEuxiL2RadobaA2TD"\ "gI3IB86D+Y2TZy4rV7EHcXhuBjKPGmFZPlo4cP5NeKalq4Ji0PX2Pyb4CgGUcQFtVpKD9FOWbUyOSA3OCAxIDEgIzExOAp4nO2SwQ6AIAxD9/8/PWMkAl"\ "1X0Ise1hPhFUJHzUqll3IJBXWE3nStOLst+2wwPXidhp+JZZwI5TAhD4hfDt6JJt/Ql+QFCRsozdY2efC0MUtoP4YyZzqh9WxZMcAWD8LPB/dOa84t0j6"\ "sJo/NUamkdAAql0vDMjkgNzggMSAxICMxMTQKeJztk0EKwDAIBP3/py2FUOw6MSX00IJ7ShwjrhizVusVuXu8KLsi8bwB/enLsqEfypN9IchdlBAXl9wb"\ "xUnHQtDBhAWK3kaQjY8oD6WE9mFY+pxOaD1bWgxJyw8Xf04pbs0Zgu3T1WTbjFqtSgeyQkLMMzEgNzggMSAxICM0OQp4nGNgGAWjYBQMGvD//3/8svjkK"\ "ZMmYPcogIP/+ACl0qOAYcCzwWg0jAIaAQCMz4F/MjkgNzggMSAxICMxMTQKeJztkkEKwDAIBP3/pw0UanTdzSGnQp1LkLF1TWs2DMMncYfyYRdoXp2dE4"\ "qDvuxwtnAmc8F7lZMyxpI0cXBHZVRE4gpi4SZ73+EjtQhaqnVv78KJ20nY5WOfWBj/w+pSPOpKV8j2PBveObnhPywn4V2xMjUgNzggMSAxICM4Mgp4nGN"\ "gGAU0Bv+BgEQZMrVQ1TAStTBQU8vgAlgc+R+rtyBCoxJDSOI/cRIwEXSZ/zgk/sMlEEpgxqNKwAGyQ1GEGZAzCorwkMlBo4A8AAAdfSPrMjUgNzggMSAx"\ "ICM4Mwp4nGNgGAV0BP+BgFRxkjVQxWayNJBkEE4Ngw1gceV/rN6CCI1KDCGJ/8RJwETQZf7jkPgPl0AogRmPKgEHyA5FEWZAzikowkMnC40CsgAAUDwj6"\ "zI1IDc4IDEgMSAjOTkKeJztk+EKwCAIhH3/l75FbHLqNRiDscD7UXhfVliatT4Rhh4BQBPcgHNQB2hAU/HrzTxOgMJIVmAd/FGq1vLdvNINtgfl508nAz"\ "BA9hGWXOkRuGjzaBu3SLA36J3WGx1CbTrUMjUgNzggMSAxICM4Mgp4nO2PSQ7AMAgD+f+nJ2qjSDhQ9RoR5oRtsZk1dwJpzYMXWUAQf4GM+lx+Jsl97A9"\ "N97U6qBAQfCy24AN2H6lW+5IobrjastXbU1pTlgF3pCXpMjUgNzggMSAxICMxMDgKeJztk0EKwCAMBPP/T6+UombNVogHKeJeKjPEpA01u9kXPElwm/F0"\ "wedI2YLURbMCyX8XMSbk+77oihMEAlcClQwGLOBwawuOm4cxDerxwl99vOifTAu52foMG+yCF+DP47KECQ0Flin68qFtMjcgNzggMSAxICM5NQp4nO2SQ"\ "Q4AIQgD/f+n614MlIKevKzMCRljQmWM5jKo+h+tXlLh1lIwWIFxys7ehUdWgytyVKjL5jooFGm8pzZp1Bn65JEa/UpZAHgha2PICNHIiuoMGa1+o2om5R"\ "ywXjI1IDc4IDEgMSAjMTAzCnic7ZRBCsBACAP9/6dTukVq3Omth9LdnGIGQRGM2FpSEhc6Bf5NENX7JF8UzKdro56OaIM/AE05AWXSiBwkGi5Luco8Htu"\ "gNcYNFgN8QHgq5RwPDXDB2+EjU5d3z7EjXMx0AMe1o2sxIDE5NDIgMSAxICM1MjgKeJw9lTtuHDEUBHUkdvN/Ake+gDLFgiLfHzDArqeEGQu7M1U9f/5+"\ "fvx8/ftu79Q7/c7+zvHO+c71zv3O886bW1zObeW6cl8BKAQFoTAUiEJxKOY3hOJQHIpDcSgOxaE4lB5KD6XzV0LpofRQeig9lB5KD2WEMkIZoQyeSCgjl"\ "BHKCGWEMkKZocxQZigzlMmDDWWGMkOZocxQVigrlBXKCmWFsng/oaxQVigrlB3KDmWHskPZoexQNq85lB3KDuWEckI5oZxQTignlBPKwZZQTig3lBvKDe"\ "WGckO5odxQbigX6co6tGt41xCvYV5DvYZ7Dfka9jX0a/B+NYZXIpfJpXK5XDKXzaUzPguh5eoCHk4LqYXVQmvhtRBbmC3UFm6rV2jw0Fv4LQQXhgvFheN"\ "CcmG50FyjyoWH6UJ14bqQXdgudBe+C+GF8Zo1BfCQXlgvtBfeC/GF+UJ94b6QX6u2BR7+iwBEASIB0YCIQFQgMhAdaNdYwSMF0YKIQdQgchA9iCBEESIJ"\ "nVo/eFQhshBdiDBEGSIN0YaIQ9ShW3Nae8qg0ofpw/Rh+jB9mD5MH6YP04dVAw2PPkwfpg/Th+nD9GH6cO19Df7v4sOrza/Rr9Wv2a/dr+GnD9OH6cO9P"\ "iHw6MP0YfowfZg+TB+mD9OH6cOjvknw6MP0YfowfZg+TB+mD9OH6cOzPnLz4z8MZjG7" rs ,32 r 300%,300%,1,100%,3 (0,1,0;1,1,1;0,1,0) dilate[^-1] . rm. r 33%,33%,1,100%,2 n 0,255 sharpen 0.2 store 1,_font } _demo_color_curves : use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r if !narg($__demo_color_curve) e "\n ------ "${g}"Color curves"$n" ----------------------------------------------------------------------------\n ----\n ---- "${c}"Left mouse button"$n" on a curve creates a new control point (or moves an existing one).\n ---- "${c}"Right mouse button"$n" on a control point deletes it.\n ---- "${c}"Left mouse button"$n" on the main image window shows the initial image until button is released.\n ---- "${c}"Right mouse button"$n" on the main image window adds a keypoint to all curves from picked color.\n ---- Key '"${c}"R"$n"' on a curve resets it.\n ---- Keys '"${c}"CTRL+D"$n"' increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n ---- Keys '"${c}"CTRL+R"$n"' reset window size.\n ---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' close the current window.\n ----\n ------------------------------------------------------------------------------------------------" __demo_color_curve=1 l[] { do rm sp ? while s!=3 } x_color_curves rgb __demo_color_curve= else e "\n ------ "${g}"Color curves"$n" ----------------------------------------------------------------------------\n ----\n ---- Only "${c}"one session"$n" allowed at the same time !\n ----\n ------------------------------------------------------------------------------------------------" fi #@cli tixy : "expression" #@cli : Animate specified mathematical expression with a 16x16 grid of circles, \ # using the rules described at . tixy : skip "${1=sin(t-sqrt((x-8)^2+(y-8)^2))}" check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r b=$_vt100_b if narg("$*") expr="$*" else expr="$1" fi l[] { ('$expr') replace_str "%","%25" replace_str "&","%26" replace_str "(","%28" replace_str ")","%29" replace_str "+","%2B" replace_str ",","%2C" replace_str ";","%2C" replace_str "/","%2F" replace_str "[","%5B" replace_str "]","%5D" replace_str "|","%7C" replace_str "^","**" replace_str " ","+" url=https://tixy.land?code={t} rm } e "\n ------ "${g}"tixy"$n" ------------------------------------------------------------------------------------\n ----\n ---- tixy - creative code golfing: "${g}"https://tixy.land"$n"\n ----\n ---- "$r${b}"Expression:"$n" "$expr"\n ----\n ---- "$r${b}"Corresponding URL:"$n" "$url"\n ----\n ---- "$r${b}"Rules:"$n"\n ---- . Specified expression depends on 4 variables "${g}"(t,i,x,y)"$n" and is rendered as an animation.\n ---- . Variables "${g}"x"$n" and "${g}"y"$n", in range "${c}"[0,15]"$n", are the spatial position of each dot.\n ---- . Variable "${g}"i"$n", in range "${c}"[0,255]"$n", is the dot index, i.e. "${c}"i = x + 16*y"$n".\n ---- . Variable "${g}"t>=0"$n" is the (decimal) number of elapsed seconds.\n ---- . Specified expression "${g}"func(t,i,x,y)"$n" must return a complex value "${c}"c"$n".\n ---- . Variable "${g}"z"$n" can be used as a shortcut for "${g}"[ x,y ]"$n".\n ---- . Variable "${g}"j"$n" is the complex imaginary number "${g}"[ 0,1 ]"$n".\n ---- . "${c}"cabs(c)"$n" in "${g}"[0,1]"$n" determines the radius of each dot at "${c}"(x,y)"$n".\n ---- . "${c}"carg(c)"$n" in "${g}"[-pi,pi]"$n" determine the color of each dot at "${c}"(x,y)"$n".\n ----\n ---- Key '"${c}"SPACE"$n"' starts/stops recording animation as video file '"${c}"out.mp4"$n"'.\n ---- Key '"${c}"ENTER"$n"' takes a screenshot of current image.\n ---- Key '"${c}"A"$n"' enables/disables anti-aliasing.\n ---- Key '"${c}"F"$n"' displays formula on the image.\n ---- Key '"${c}"R"$n"' resets time.\n ---- Keys '"${c}"CTRL+D"$n"' increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n ---- Keys '"${c}"CTRL+R"$n"' reset window size.\n ---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' close the current window.\n ------------------------------------------------------------------------------------------------" w[] 400,400,0,"[G'MIC] tixy: "$expr t0=$| is_recording=0 is_antialias=1 is_screenshot=0 is_formula=0 nb_frames=0 360,1,1,3,[x,abs(cos(x/w*pi))^0.5,1] hsv2rgb. shift. -180,0,0,0,2 => cmap strvar $expr basename=${} do # Manage user-events. is_ctrl:={*,CTRLLEFT}" || "{*,CTRLRIGHT} if $is_ctrl" && "{*,-D} w[] {1.25*[{*,w,h}]} rmn formula,mformula elif $is_ctrl" && "{*,-C} w[] {0.8*[{*,w,h}]} rmn formula,mformula elif $is_ctrl" && "{*,R} w[] 400,400 rmn formula,mformula elif {*,-A} is_antialias:=!$is_antialias elif {*,-F} is_formula:=!$is_formula elif {*,R} t0=$| elif {*,-SPACE} if $is_recording o[] $basename.mp4,15,0,0 nb_frames=0 else t0=$| fi is_recording:=!$is_recording elif {*,-ENTER} is_screenshot=1 fi # Render animation frame. m:=min({*,w,h}) _is_antialias:=$is_antialias" && "$m<512 {m=$m*($_is_antialias?2:1);[m,m]},1,3 16,16,1,1,">begin( const b = w#-1/16; const t = "$|-$t0"; j = [ 0,1 ]; ); i = x + 16*y; ref([ x,y ],z); val = ("$expr"); n = cabs(val); r = floor(0.48*b*cut(n,0,1)); a = (360+carg(val)*180/pi)%360; c = I[#"$cmap",a]; r>1?ellipse(#-1,b*(x + 0.5),b*(y + 0.5),r,r,0,1,c): r>0?ellipse(#-1,b*(x + 0.5),b*(y + 0.5),1,1,0,0.5,c)" rm. if $_is_antialias r. 50%,50%,1,3,2 fi if {*,w}!={*,h} -. 128 r. {*,w,h},1,3,0,0,0.5,0.5 +. 128 fi if $is_formula if !narg($formula) 0 t. {``$expr},0,0,48,1,255 frame. 5,5,0 +dilate. 11 max. 128 to_rgb.. if w>0.75*{*,w} rs[-2,-1] {0.75*{*,w}} fi mv[-2,-1] 0 =>[0,1] formula,mformula fi j. [formula],0.5~,1~,0,0,1,[mformula],255 fi if $is_screenshot o. $basename.png is_screenshot=0 fi if $is_recording o. $basename.mp4,15,0,1 nb_frames+=1 to. "Recording video\n[Frame \#"$nb_frames"]",0,0,5% fi w. rm. wait 40 while {*}" && "!{*,ESC}" && "!{*,Q} rmn cmap,formula,mformula w[] 0 #@cli x_2048 #@cli : Launch the 2048 game. x_2048 : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"2048"$n" -----------------------------------------------\n ----\n ---- Join the numbers and get to the "${g}"2048"$n" tile!\n ----\n ---- Use your "${c}"arrow keys"$n" to move the tiles. When two tiles\n ---- with the same number touch, they merge into one!\n ---- This command is a port of the '"${c}"2048"$n"' game originally\n ---- designed by "${c}"Gabriele Cirulli"$n", and available at:\n ---- "${g}"http://gabrielecirulli.github.io/2048/"$n"\n ----\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------------------------" l[] { score=0 f3d 50 m3d 0 m "_x_2048_setrandom : +==[0] 0 f. i?4*y+x:-1 discard. -1 off:=i[v(h-1)] rm. x:=$off&3 y:=$off>>2 n:=u<0.75?1:2 =[0] $n,$x,$y [{2+$n}] c3d. repeat 6 { j3d[1] .,{78+$x*121},{190+$y*121},{10*$<},{(1+$>)/6} w[1] wait 20 } rm." m "_x_2048_object3d : +f[0] i?i*16+4*y+x:-1 discard. -1 N:=h repeat h { v={-{1+$>},@$>} ++3d[{2+($v>>4)}] {$v&3},{($v>>2)&3} } +3d[-$N--1] rm.." i[0] 4,4 # Pre-render game canvas and numbered titles. b0=204,192,179 b1=238,228,218 b2=237,224,200 b3=242,177,121 b4=245,149,99 b5=246,124,95 b6=246,94,59 b7=237,207,114 c0=119,110,101 c1=249,246,242 s0=" " 520,630,1,3 fc. 250,248,239 t. "2048",20,10,86,1,$c0 t. "Join the numbers and get to the 2048 tile!",20,90,20,1,$c0 rectangle. 422,20,501,75,1,187,173,160 t. "SCORE",439,25,15,1,238,228,218 repeat 12 { 107,107,1,3 fc. ${b{min($>,7)}} +fc. ${c{$>>2}} s1:=2^$> 0 t. ${s{$>>0}},0,0,52,1,1 r. ..,..,1,1,0,0,0.5,0.5 dilate_circ. 3 b. 0.5 j... ..,0,0,0,0,1,.,1 rm[-2,-1] } frame_round[2--1] 0,0,20%,0.5,187,173,160 frame[2--1] xy,7,187,173,160 to_rgb[2--1] r[2] 400%,400%,1,3,0,2 j[1] [2],18,130 sprite3d[3--1] # Run game. w[1] 100%,100%,0,"[G"{`39`}"MIC] 2048" insert_new=1 repeat 2 { _x_2048_setrandom } do # Render game graphics at current iteration. if $insert_new _x_2048_object3d *3d. 121 j[1] [2],18,130 j3d[1] .,78,190 rm. 80,25,1,3 fc. $c1 0 t. $score,0,0,25,1,1,1,1 ri. ..,0,0,0.5,0.8 rectangle[1] 422,45,501,69,1,187,173,160 j[1] ..,422,45,0,0,1,. rm[-2,-1] w[1] insert_new=0 fi # Check for the end of the game. ++[0] 1 f. j(-1)==i||j(1)==i||j(0,-1)==i||j(1,0)==i||i==1 if {0,iM==11} # Game won. alert "Game Over","\nCongratulations! You got the 2048 title!\n\n Your score: "$score,"OK" break elif !iM # Game lost. alert "Game Over","\nBad luck! You lost the game!\n\n Your score: "$score,"OK" break fi rm. # Manage user events. wait is_shift=0 um shift2048,ishift2048,vshift2048 if {*,ARROWLEFT} m "shift2048:" m "ishift2048:" m "vshift2048:" is_shift=1 elif {*,ARROWRIGHT} m "shift2048: rotate 180" m "ishift2048: rotate 180" m "vshift2048: s3d l[2] { r 3,{h/3},1,1,-1 s x -[0,1] 3 *[0,1] -1 a x y } a y" is_shift=1 elif {*,ARROWUP} m "shift2048: rotate -90" m "ishift2048: rotate 90" m "vshift2048: s3d l[2] { r 3,{h/3},1,1,-1 s x rv[0,1] -[0] 3 *[0] -1 a x y } a y" is_shift=1 elif {*,ARROWDOWN} m "shift2048: rotate 90" m "ishift2048: rotate -90" m "vshift2048: s3d l[2] { r 3,{h/3},1,1,-1 s x rv[0,1] -[1] 3 *[1] -1 a x y } a y" is_shift=1 fi if {*,r} w[1] 100%,100% fi # Manage tile shifts and fusions. if $is_shift wait -1 shift2048[0] repeat 2 { # Tile shifts. _x_2048_object3d +s[0] y discard[-4--1] 0 y[-4--1] x r[-4--1] 4,1,1,1,0,0 a[-4--1] y +==[0,-1] insert_new:=$insert_new||!im rm. +f[0,-1] "i?x:-1" discard[-2,-1] -1 rv[-2,-1] -[-2,-1] rv[0,-2] rm.. if (im||iM)&&!{*,k} # Render animation for shift. /. 5 z. 0,2 y. repeat 5 { j.. .,0,8,0,0,-1 j[1] [2],18,130 +vshift2048.. *3d. 121 j3d[1] .,78,190 rm. w[1] wait 20 } fi rm[-2,-1] # Tile fusions. if !$> dscore=0 [0] +f[0] "i?i*16+4*y+x:-1" discard. -1 repeat h { x:=i[$>]&3 y:=(i[$>]>>2)&3 n:=i[$>]>>4 if $x>0" && "{0,i($x-1,$y)}==$n =[0] 0,$x,$y =[0] {$n+1},{$x-1},$y =.. 0,$x,$y insert_new=1 dscore+=2^($n+1) else =. -1,0,$> fi } score+=$dscore if iM<0 rm[-2,-1] else # At least one tile fusions. discard. -1 rv[0,-2] _x_2048_object3d rv[0,-3] vshift2048. *3d. 121 # Only tiles that do not move in this step. j[1] [2],18,130 j3d[1] .,78,190 rm[-3,-1] N:=h repeat h { v={-{1+$>},@$>} ++3d[{2+($v>>4)}] {$v&3},{($v>>2)&3} } # Only tiles that move. +3d[-$N--1] rm.. 0 t. +$dscore,0,0,33,1,1 100%,100%,1,3 fc. $c0 repeat 6 { # Render animation for fusion. +vshift2048... *3d. 121 +j3d[1] .,78,190 j. ...,440,{40-3*$>},0,0,{min(1,$1 {0,[w,h]},1,2 repeat h#1 { r={1,i[2]*(1+i[3]*cos(i[4]+i[5]*$|*1000))} ellipse. {1,@0,1},$r,$r,0,1,{1,@6-7} d:=sqrt(($x-{1,@0})^2+($y-{1,@1})^2) if $d<$r nearest=$> fi shift[1] 0,-1,0,0,2 } b. 15 +norm. +>=. 50 <=.. 40 *[-3,-1] +*[0,-1] rm.. rv[-2,-1] *. 1.6 c. 0,255 +[-2,-1] if $fps>0 to. $fps" fps",5,{h-29},24,2,0.2 fi w. if {*,CTRLLEFT}" && "{*,D} w[] {2*[w,h]} elif {*,CTRLLEFT}" && "{*,C} w[] {[w,h]} fi rm. else +to[0] "G\47MIC Blobs Editor",75,100,35,3,1,200,128,255 to. "* Left mouse button : Create and move blobs.\n\n\ * Right mouse button : Remove blob.\n\n\ * Middle mouse button : Remove all blobs.\n\n\ * Key 'ESC' or 'Q' : Quit.\n\n\ * Colors and sizes of appearing blobs are\n chosen randomly",\ 50,180,18,1,1,255 w. if {*,CTRLLEFT}" && "{*,D} w[] {1.5*[w,h]} elif {*,CTRLLEFT}" && "{*,C} w[] {[w,h]} fi rm. fi wait 20 # Manage blob insertion, removal or move. if $x<0||$y<0 continue fi if $b&1 if $nearest>=0" || "$moving>=0 # Move existing blob. if $moving<0 moving=$nearest fi =[1] $x,0,$moving =[1] $y,1,$moving else # Insert new blob. ($x,$y,{u(20,50)},{u(-0.3,0.3)},{u(0,pi/2)},{u(0,0.009)},{u(64,255)},{u(64,255)}) a[^0] y moving:=h-1 fi elif $b&2 # Remove existing blob. if $nearest>=0 l[1] { s y rm[$nearest] a y } nearest=-1 fi elif $b&4 # Remove all blobs. k[0] else moving=-1 fi while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0 } #@cli x_bouncing #@cli : Launch the bouncing balls demo. x_bouncing : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Bouncing balls"$n" ------------------------------\n ----\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -----------------------------------------------------" l[] { 520,320,1,3 plasma 1,1,9 n 0,220 N=12 repeat $N { ball[] {v(32,80)},${-rgb} t$>:=u(200) x$>:=u(10,w#0-10) h$>:=u(150,300) vx$>:=(u<0.5?1:-1)*u(1,8) } mv[0] $! w. {f=w<0.5*{*,u}?1.5:1;[w,h]*=f},0,"[G"{`39`}"MIC] Bouncing Balls" (0;0.7;1) r. {-2,w},70,1,1,3 do [$N] repeat $N { bw,bh={$>,[w,h]} y:=${h$>}*abs(cos(${t$>}*pi/60))-$bh/2 dt=1 if $y<0 d:=-$y y=0 bh-=$d bw+=$d dt:=max(0.2,1-($d/$bh)^2) else dt=1 fi if ${x$>}+$bw/2>w d:=${x$>}+$bw/2-w bw-=$d bh+=0.5*$d if ${x$>}+$bw/4>w vx$>:=-${vx$>} fi fi if ${x$>}-$bw/2<0 d:=$bw/2-${x$>} bw-=$d bh+=0.5*$d if ${x$>}-$bw/4<0 vx$>:=-${vx$>} fi fi +r[$>] $bw,$bh,1,4,3 s. c,-3 j... ..,{max(0,min({$N,w-$bw},${x$>}-$bw/2))},{{$N,h}-{h}-$y-70},0,0,1,.,255 rm[-2,-1] t$>+=$dt x$>+=$dt*${vx$>} } +rows. {h-2*70},{h-1-70} mirror. y *. [{$N+1}] j.. .,0,{-2,h-71},0,0,0.5 rm. fps=${-fps} if $fps>0 to. $fps" fps",5,{h-29},24,2,0.2 fi if {*,CTRLLEFT}" && "{*,D} w[] {1.5*w},{1.5*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi w. rm. wait 20 while {*}" && "!{*,ESC}" && "!{*,Q} w 0 rm } #@cli x_color_curves : _colorspace={ rgb | cmy | cmyk | hsi | hsl | hsv | lab | lch | ycbcr | last } #@cli : Apply color curves on selected RGB[A] images, using an interactive window. #@cli : Set 'colorspace' to 'last' to apply last defined color curves without opening interactive windows. #@cli : Default value: 'colorspace=rgb'. x_color_curves : skip ${1=rgb} if ['"$1"']!='last'&&!{*,u} error[0--3] "Command '$0': No display available." return fi use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e[^-1] "Apply color curves of image$?, in the '$1' colorspace." if ['"$1"']=='last' if !narg($_xcc_colorbase) return fi __x_color_curves[] $_xcc_colorbase else e "\n ------------------------------------------------------------------------------------------------\n ----\n ---- "${c}"Left mouse button"$n" on a curve creates a new control point (or moves an existing one).\n ---- "${c}"Right mouse button"$n" on a control point deletes it.\n ---- "${c}"Left mouse button"$n" on the main image window shows the initial image until button is released.\n ---- "${c}"Right mouse button"$n" on the main image window adds a keypoint to all curves from picked color.\n ---- Key '"${c}"R"$n"' on a curve resets it.\n ---- Keys '"${c}"CTRL+D"$n"' increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n ---- Keys '"${c}"CTRL+R"$n"' reset window size.\n ---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' close the current window.\n ----\n ------------------------------------------------------------------------------------------------" __x_color_curves[] $1 _xcc_colorbase=$1 fi to_color foreach { if ['"$1"']!='last' # Open interactive windows to set color curves. +r[0] ${fitscreen[]\ {0,[w,h,1]},128,70%},1,100%,3 +l. { # Compute additional info for each image channel (histogram and color axis). xcc_goto s c histogram 256,0,255 xcc_info } __C0= __C1= __C2= __C3= __C4= if narg($__xcc_C0) __C0=$__xcc_C0 fi if narg($__xcc_C1) __C1=$__xcc_C1 fi if narg($__xcc_C2) __C2=$__xcc_C2 fi if narg($__xcc_C3) __C3=$__xcc_C3 fi if narg($__xcc_C4) __C4=$__xcc_C4 fi x={1,({*,u}-560-w)/2} y={1,({*,v}-h)/2} if $!==5 # 3 channels. parallel "w[] 256,256,0,0,"$x","$y",\"Curve: "$_title0"\" x_select_function1d... __C0,"$_color0"",\ "w[] 256,256,0,0,"{$x+280}","$y",\"Curve: "$_title1"\" x_select_function1d.. __C1,"$_color1"",\ "w[] 256,256,0,0,"$x","{$y+300}",\"Curve: "$_title2"\" x_select_function1d. __C2,"$_color2"",\ "w. 100%,100%,0,0,"{$x+560}","$y" _x_color_curves[-4]" elif $!==6 # 4 channels. parallel "w[] 256,256,0,0,"$x","$y",\"Curve: "$_title0"\" x_select_function1d[-4] __C0,"$_color0"",\ "w[] 256,256,0,0,"{$x+280}","$y",\"Curve: "$_title1"\" x_select_function1d... __C1,"$_color1"",\ "w[] 256,256,0,0,"$x","{$y+300}",\"Curve: "$_title2"\" x_select_function1d.. __C2,"$_color2"",\ "w[] 256,256,0,0,"{$x+280}","{$y+300}",\"Curve: "$_title3"\" x_select_function1d. __C3,"$_color3"",\ "w. 100%,100%,0,0,"{$x+560}","$y" _x_color_curves[-5]" elif $!==7 # 5 channels. parallel "w[] 256,256,0,0,"$x","$y",\"Curve: "$_title0"\" x_select_function1d[-5] __C0,"$_color0"",\ "w[] 256,256,0,0,"{$x+280}","$y",\"Curve: "$_title1"\" x_select_function1d[-4] __C1,"$_color1"",\ "w[] 256,256,0,0,"$x","{$y+300}",\"Curve: "$_title2"\" x_select_function1d... __C2,"$_color2"",\ "w[] 256,256,0,0,"{$x+280}","{$y+300}",\"Curve: "$_title3"\" x_select_function1d.. __C3,"$_color3"",\ "w[] 256,256,0,0,"{$x+280}","{$y+600}",\"Curve: "$_title4"\" x_select_function1d. __C4,"$_color4"",\ "w. 100%,100%,0,0,"{$x+560}","$y" _x_color_curves[-6]" fi k[0] fi # Apply color curves on fullres image. xcc_goto repeat s { function1d[] 1,${__xcc_C$>} *. {255%} r. 256,1,1,1,5 c. 0,255 sh[0] $> map. .. rm[-2,-1] } xcc_backto } um xcc_goto,xcc_backto,xcc_info _x_color_curves : title={0,b} if narg({'{0,x}'}) title=$title.{0,x} fi ('$title') discard. {'~'} title={t} rm. +drgba. w. 100%,100%,0,"[G"{`39`}"MIC] Image: "$title rm. xcc_goto. . oC0= oC1= oC2= oC3= oC4= viewmode=0 do wait 100 need_refresh=0 # Manage user events. oviewmode=$viewmode is_ctrl:={*,CTRLLEFT}" || "{*,CTRLRIGHT} x,y={*,x,y} if {*,r} need_refresh=1 # Window resize. elif $is_ctrl" && "{*,-D} w[] {{*,w}*125%},{{*,h}*125%} need_refresh=1 # Increase window size. elif $is_ctrl" && "{*,-C} w[] {{*,w}*80%},{{*,h}*80%} need_refresh=1 # Decrease window size. elif $is_ctrl" && "{*,-R} w[] {w},{h} need_refresh=1 # Reset window size. elif {*,b}&1 viewmode:=x={*,x};x=0" && "$y>=0 # Add control point from picked color. xc:=$x*w/{*,w} yc:=$y*h/{*,h} +z[0] $xc,$yc,$xc,$yc repeat s { (${__C$>},{i[$>]/255%},{i[$>]/255%}) r. 2,{w/2},1,1,-1 sort. +,y __C$>={^} rm. } rm. wait -1 else viewmode=0 fi need_refresh:=$need_refresh||$oviewmode!=$viewmode # Update result. repeat s { if ['_${oC$>}']!=['_${__C$>}'] # Channel must be updated. function1d[] 1,${__C$>} *. {255%} r. 256,1,1,1,5 c. 0,255 +channels[0] $> map. .. j[1] .,0,0,0,$> rm[-2,-1] need_refresh=1 oC$>=${__C$>} fi } # Display view. if $need_refresh if !$viewmode # Modified view. +xcc_backto[1] elif $viewmode%2 # Split view. w2={0,int(w/2)} b:=$viewmode==1 +z[{!$b}] 0,{$w2-1} +z[$b] $w2,100% xcc_backto.. xcc_backto. a[-2,-1] x line. 50%,0,50%,100%,1,0 else # Original view. +xcc_backto[0] fi if s>3 drgba. fi w. rm. refresh=0 fi while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,SPACE}" && "!{*,ENTER} w 0 # Transfer curves to output variable and request curve widgets to close. repeat 5 { if narg(${__C$>}) __xcc_C$>=${__C$>} __C$>=-1 fi } # Define colorspace conversion functions. __x_color_curves : if ['"$1"']=='rgb' _color0="255,180,180" _color1="180,255,180" _color2="180,180,255" _color3="220,220,220" _title0=Red _title1=Green _title2=Blue _title3=Alpha m "xcc_goto:" m "xcc_backto:" m "xcc_info: (0,255;0,0;0,0) (0,0;0,255;0,0) (0,0;0,0;0,255) r[-3--1] 256,3,1,1,3 a[0,-3] y a[1,-2] y a[2,-1] y" elif ['"$1"']=='cmy' _color0="180,255,255" _color1="255,180,255" _color2="255,255,100" _color3="220,220,220" _title0=Cyan _title1=Magenta _title2=Yellow _title3=Alpha m "xcc_goto: s c,-3 rgb2cmy[0] a c" m "xcc_backto: s c,-3 cmy2rgb[0] a c" m "xcc_info: (255,0;255,255;255,255) (255,255;255,0;255,255) (255,255;255,255;255,0) r[-3--1] 256,3,1,1,3 a[0,-3] y a[1,-2] y a[2,-1] y" elif ['"$1"']=='cmyk' _color0="180,255,255" _color1="255,180,255" _color2="255,255,100" _color3="180,180,180" _color4="220,220,220" _title0=Cyan _title1=Magenta _title2=Yellow _title3=Key _title4=Alpha m "xcc_goto: s c,-3 rgb2cmyk[0] a c" m "xcc_backto: s c,-4 cmyk2rgb[0] a c" m "xcc_info: (255,0;255,255;255,255) (255,255;255,0;255,255) (255,255;255,255;255,0) (255,0) r[-4--1] 256,3,1,1,3 a[0,-4] y a[1,-3] y a[2,-2] y a[3,-1] y" elif ['"$1"']=='hsi' _color0="255,220,220" _color1="220,220,220" _color2="180,180,180" _color3="220,220,220" _title0=Hue _title1=Saturation _title2=Intensity _title3=Alpha m "xcc_goto: s c,-3 rgb2hsi8[0] a c" m "xcc_backto: s c,-3 hsi82rgb[0] a c" m "xcc_info: 256,1,1,3,!c?x:128 256,1,1,3,!c?0:c==1?x:128 256,1,1,3,!c?0:c==1?0:x hsi82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y" elif ['"$1"']=='hsl' _color0="255,220,220" _color1="220,220,220" _color2="180,180,180" _color3="220,220,220" _title0=Hue _title1=Saturation _title2=Lightness _title3=Alpha m "xcc_goto: s c,-3 rgb2hsl8[0] a c" m "xcc_backto: s c,-3 hsl82rgb[0] a c" m "xcc_info: 256,1,1,3,!c?x:128 256,1,1,3,!c?0:c==1?x:128 256,1,1,3,!c?0:c==1?0:x hsl82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y" elif ['"$1"']=='hsv' _color0="255,220,220" _color1="220,220,220" _color2="180,180,180" _color3="220,220,220" _title0=Hue _title1=Saturation _title2=Value _title3=Alpha m "xcc_goto: s c,-3 rgb2hsv8[0] a c" m "xcc_backto: s c,-3 hsv82rgb[0] a c" m "xcc_info: 256,1,1,3,!c?x:255 256,1,1,3,!c?0:c==1?x:128 256,1,1,3,!c?0:c==1?0:x hsv82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y" elif ['"$1"']=='lab' _color0="180,180,180" _color1="220,180,220" _color2="220,220,180" _color3="220,220,220" _title0=Lightness _title1=Chroma-A _title2=Chroma-B _title3=Alpha m "xcc_goto: s c,-3 srgb2rgb[0] apo[0] rgb2lab8,0,4 a c" m "xcc_backto: s c,-3 apo[0] lab82rgb,0,4 rgb2srgb[0] a c" m "xcc_info: 256,1,1,3,!c?x:128 256,1,1,3,!c?240:c==1?x:128 256,1,1,3,!c?240:c==1?128:x lab82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y" elif ['"$1"']=='lch' _color0="180,180,180" _color1="220,180,220" _color2="255,220,220" _color3="220,220,220" _title0=Lightness _title1=Chroma _title2=Hue _title3=Alpha m "xcc_goto: s c,-3 srgb2rgb[0] apo[0] rgb2lch8[0],0,4 a c" m "xcc_backto: s c,-3 apo[0] lch82rgb[0],0,4 rgb2srgb[0] a c" m "xcc_info: 256,1,1,3,!c?x:0 256,1,1,3,!c?255:c==1?x:128 256,1,1,3,!c?220:c==1?128:x lch82rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y" elif ['"$1"']=='ycbcr' _color0="180,180,180" _color1="220,220,255" _color2="255,220,220" _color3="220,220,220" _title0=Luma _title1=Blue\ chroma _title2=Red\ chroma _title3=Alpha m "xcc_goto: s c,-3 rgb2ycbcr[0] a c" m "xcc_backto: s c,-3 ycbcr2rgb[0] a c" m "xcc_info: 256,1,1,3,!c?x:128 256,1,1,3,!c?128:c==1?x:128 256,1,1,3,!c?128:c==1?128:x ycbcr2rgb[-3--1] permute[-3--1] xcyz a[0,-3] y a[1,-2] y a[2,-1] y" else error[0--3] "Command 'x_color_curves': Unknown specified color space '$1'." fi #@cli x_colorize : _is_lineart={ 0 | 1 },_max_resolution={ 0 | >=128 },_multichannels_output={ 0 | 1 },\ # _[palette1],_[palette2],_[grabber1] #@cli : Colorized selected B&W images, using an interactive window. #@cli : When >0, argument 'max_resolution' defines the maximal image resolution used in the interactive window. #@cli : Default values: 'is_lineart=1', 'max_resolution=1024' and 'multichannels_output=0'. x_colorize : skip ${1=0},${3=0},${4=0},${5=0},${6=0} check "!${2=1024} || $2>=128" check_display $0 s0="image" s1="lineart" s2="multichannel" s3="merged" use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e[^-1] "Colorize selected B&W "${s{!$1}}"$? interactively, with maximum resolution $2 and "${s{2+!$3}}" output." e "\n --------------------------------------------------------------------------------------\n ----\n ---- "${c}"Left mouse button"$n" creates a new colored control point (or moves an existing one).\n ---- "${c}"Right mouse button"$n" or key '"${c}"X"$n"' over a control point deletes it.\n ---- "${c}"Right mouse button"$n" or key '"${c}"P"$n"' anywhere else picks a color from the image.\n ---- "${c}"Mouse wheel"$n", or keys '"${c}"CTRL+arrows UP/DOWN"$n"' zoom view in/out.\n ---- '"${c}"CTRL+mouse wheel"$n"', '"${c}"SHIFT+mouse wheel"$n"' or "${c}"arrow keys"$n" move image in zoomed view.\n ---- Key '"${c}"SPACE"$n"' updates the extrapolated color field.\n ---- Key '"${c}"TAB"$n"' toggles between markers view modes.\n ---- Key '"${c}"BACKSPACE"$n"' deletes the last control point added.\n ---- Key '"${c}"PAGE UP"$n"' increases image contrast.\n ---- Key '"${c}"PAGE DOWN"$n"' decreases image contrast.\n ---- Key '"${c}"R"$n"' toggles color replace mode.\n ---- Keys '"${c}"CTRL+D"$n"' increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n ---- Keys '"${c}"CTRL+R"$n"' reset window size.\n ---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' exit the interactive window.\n ----\n --------------------------------------------------------------------------------------" N=$! thread_main="_x_colorize[0] ${1--1}" thread_color="w[] 400,320,0,\"Palette: main\" x_select_color[] __color,255,255,255" is_palette1=${"is_image_arg[] $4"} if $is_palette1 pass$4 1 ('{b}') discard. {'~'} palette_title1={t} rm. thread_palette1="w[] 400,400,0,\"Palette: "$palette_title1"\" x_select_palette["{$!-1}"] __color" fi is_palette2=${"is_image_arg[] $5"} if $is_palette2 pass$5 1 ('{b}') discard. {'~'} palette_title2={t} rm. thread_palette2="w[] 400,400,0,\"Palette: "$palette_title2"\" x_select_palette["{$!-1}"] __color" fi is_grabber=${"is_image_arg[] $6"} if $is_grabber pass$6 1 ('{b}') discard. {'~'} palette_grabber={t} rm. thread_grabber="w[] ${\"fitscreen[] {[w,h,1]},128,50%\"},0,\"Grabber: "$palette_grabber"\" x_grab_color["{$!-1}"] __color" fi __color=255,255,255 if !$is_palette1" && "!$is_palette2" && "!$is_grabber foreach[0-{$N-1}] { parallel $thread_main,$thread_color } else repeat $N { l[$>,$N--1] { parallel $thread_main,$thread_color,$thread_palette1,$thread_palette2,$thread_grabber } } fi k[0-{$N-1}] _x_colorize : # Init variables and images. name={n} title={b} if narg({x}) title=$title.{x} fi w,h={w},{h} if $1 # Line-art. if s==4 sh. 3 if abs(im-iM)>64 +*. -1 rm.. +. 255 else rm. sh. 0 fi else sh. 0 fi n 0,255 else # Regular image. if s==1 sh. 0 else +luminance. fi fi => img fdim=${fitscreen[]\ $w,$h} ww:=arg(1,$fdim) wh:=arg(2,$fdim) x0=0 y0=0 x1:=w-1 y1:=h-1 selection=-1 view_markers=2 contrast=9 xpan=-1 ypan=-1 replace_color= current_replace_color= if narg($_gui_control_points)>=6 # Import list of control points from plug-in GUI. ($_gui_control_points) r. {w/6},6,1,1,-1 else 0 # Empty list of control points. fi => points # Compute potential map. if $2>0 if $w>$h +rs[img] {min($2,$w)} else +rs[img] ,{min($2,$h)},2 fi else [img] fi __x_colorize. $1 pw,ph={potential,[w,h]} # Start event loop. do # Handle user events for zoom/navigation/resizing. if narg($replace_color)" && "{*,x}<0" && "{*,y}<0 wait 200 else wait fi x={*,x} y={*,y} b={*,b} o={*,-o} is_ctrl:={*,CTRLLEFT}" || "{*,CTRLRIGHT} is_shift:={*,SHIFTLEFT}" || "{*,SHIFTRIGHT} is_mouseout:=$x<0" || "$y<0 x:=$x0+$x*($x1-$x0+1)/$ww y:=$y0+$y*($y1-$y0+1)/$wh oww=$ww owh=$wh ox0=$x0 oy0=$y0 ox1=$x1 oy1=$y1 if {*,r} # When window resized. nww={*,d} nwh={*,e} m:=min($nww,$nwh) cx:=($x0+$x1)/2 cy:=($y0+$y1)/2 dx:=$nww*($x1-$x0+1)/$ww dy:=$nwh*($y1-$y0+1)/$wh x0:=$cx-$dx/2 x1:=$cx+$dx/2 y0:=$cy-$dy/2 y1:=$cy+$dy/2 ww=$nww wh=$nwh elif $is_ctrl" && "{*,-D} # Increase window size. nww:=min({*,u},$ww*1.25) nwh:=min({*,v},$wh*1.25) m:=min($nww,$nwh) if $m==$nww ww=$m wh:=$h*$m/$w else ww:=$w*$m/$h wh=$m fi elif $is_ctrl" && "{*,-C} # Decrease window size. nww:=$ww/1.25 nwh:=$wh/1.25 if min($nww,$nwh)>=64 ww=$nww wh=$nwh fi elif $is_ctrl" && "{*,R} # Reset window size. fdim=${fitscreen[]\ $w,$h} ww:=arg(1,$fdim) wh:=arg(2,$fdim) x0=0 y0=0 x1:=$w-1 y1:=$h-1 elif ($is_shift" && "$o<0)" || "{*,ARROWLEFT} # Go left. dx:=($x1-$x0)/6 x0-=$dx x1-=$dx elif ($is_shift" && "$o>0)" || "{*,ARROWRIGHT} # Go right. dx:=($x1-$x0)/6 x0+=$dx x1+=$dx elif ($is_ctrl" && "$o>0)" || "({*,ARROWUP}" && "!$is_ctrl) # Go up. dy:=($y1-$y0)/6 y0-=$dy y1-=$dy elif ($is_ctrl" && "$o<0)" || "({*,ARROWDOWN}" && "!$is_ctrl) # Go down. dy:=($y1-$y0)/6 y0+=$dy y1+=$dy elif $o>0" || "($is_ctrl" && "{*,ARROWUP}) # Zoom in. if $x1-$x0>16" && "$y1-$y0>16 cx:=$x>=0" && "!{*,ARROWUP}?$x:($x0+$x1)/2) cy:=$y>=0" && "!{*,ARROWUP}?$y:($y0+$y1)/2) x0:=$cx+($x0-$cx)*0.75} y0={$cy+($y0-$cy)*0.75 x1:=$cx+($x1-$cx)*0.75} y1={$cy+($y1-$cy)*0.75 fi elif $o<0" || "($is_ctrl" && "{*,ARROWDOWN}) # Zoom out. zfactor:=max(($x1-$x0+1)/$w,($y1-$y0+1)/$h) if $zfactor<1.3 cx:=$x>=0" && "!{*,ARROWDOWN}?$x:($x0+$x1)/2) cy:=$y>=0" && "!{*,ARROWDOWN}?$y:($y0+$y1)/2) x0:=$cx+($x0-$cx)/0.75} y0={$cy+($y0-$cy)/0.75 x1:=$cx+($x1-$cx)/0.75} y1={$cy+($y1-$cy)/0.75 dx:=$zfactor^2*($w-$x0-$x1)/2} dy={$zfactor^2*($h-$y0-$y1)/2 x0+=$dx x1+=$dx y0+=$dy y1+=$dy else dx:=($w-$x0-$x1)/2 dy:=($h-$y0-$y1)/2 x0+=$dx x1+=$dx y0+=$dy y1+=$dy fi elif $b&4" && "!$is_mouseout # Pan. if $panx<0" && "$pany<0 panx=$x pany=$y else dx:=round($panx-$x) dy:=round($pany-$y) x0+=$dx y0+=$dy x1+=$dx y1+=$dy fi else panx=-1 pany=-1 fi if $ww!=$oww" || "$wh!=$owh" || "$ox0!=$x0" || "$oy0!=$y0" || "$ox1!=$x1" || "$oy1!=$y1 rm[baseview] fi # Handle events related to control points management. N={points,w} if narg($baseview)" && "($b&3" || "{*,X}" || "{*,P})" && "$x>=0" && "$y>=0" && "$x<$w" && "$y<$h if $selection==-1" && "$N # Check for selection of an existing point. ($x;$y) r. $N,2 -. [points] *. {max($ww,$wh)/max($x1-$x0,$y1-$y0)} sqr. s. y +[-2,-1] dmin:=im selection:=$dmin>25?-1:xm rm. fi if narg($replace_color) # Go back from 'Replace color' mode. replace_color= wait -1 elif $selection>=0 if $b&1" && "$view_markers # Move existing point. +columns[points] $selection ox:=i[0] oy:=i[1] =. $x =. $y,0,1 j[points] .,$selection rm. rm[view] elif ($b&2" || "{*,X})" && "$view_markers # Remove existing point. if $N>1 +z[points] {$selection+1},100% j[points] .,$selection rm. r[points] {$N-1},100%,1,1,0 else rm[points] 0 => points fi wait -1 rm[view] fi elif $b&1 # Add new point ($x;$y;0) ($__color) y. y +. 1 a[-2,-1] y a[points,-1] x selection=$N if !$view_markers view_markers=2 fi rm[view] elif $b&2" || "{*,P} # Pick color from image. __color={colors,I($x*$pw/$w,$y*$ph/$h)} fi else selection=-1 if {*,-SPACE}" && "narg($colors) replace_color= rm[colors] # Update color map. elif {*,-TAB} view_markers:=($view_markers-1)%3 rm[view] wait -1 # Toggle markers. elif !$is_ctrl" && "{*,-R} # Switch color replace mode. if narg($replace_color) replace_color= else replace_color=$__color fi rm[baseview] wait -1 elif {*,PAGEDOWN} contrast:=max(0,$contrast-1) rm[view] wait -1 # Decrease contrast. elif {*,PAGEUP} contrast:=min(9,$contrast+1) rm[view] wait -1 # Increase contrast. elif {*,BACKSPACE}" && "$N # Remove last point. if $N>1 z[points] 0,{$N-2} else i=$points rm[points] i[$i] 0 =>[$i] points fi rm[view] wait -1 fi fi # Manage zoomed view bounds. w2,h2:=round(([$x1-$x0,$y1-$y0])/2) if $x0<-$w2 x1-=$x0+$w2 x0=-$w2 fi if $y0<-$h2 y1-=$y0+$h2 y0=-$h2 fi if $x1>=$w+$w2 x0+=$w-1+$w2-$x1 x1:=$w-1+$w2 fi if $y1>=$h+$h2 y0+=$h-1+$h2-$y1 y1:=$h-1+$h2 fi # Render color map. if !narg($colors) N={points,w} if narg($view) to[view] "Processing...",5,5,20,2 w[view] fi if $N [points] sh. 0,0,0,0 *. {$pw/$w} rm. sh. 1,1,0,0 *. {$ph/$h} rm. pointcloud. -1,$pw,$ph # Additional term that depends on marker's positions. +compose_channels. max !=. 0 distance. 1 *. 0.02 +. 1 ^. -1 +. [potential] if !$1 dilate.. 3 fi watershed.. . rm. -. 1 else [potential],[potential],1,3,255 fi => colors if narg($baseview) rm[baseview] fi fi # Manage replace color mode. if !narg($replace_color)" && "narg($points_replaced) rm[points,colors,view] =>[colors_replaced] colors =>[points_replaced] points current_replace_color= elif narg($replace_color)" && "['$__color']!=['$current_replace_color'] if narg($colors_replaced) rm[colors_replaced,points_replaced] fi current_replace_color=$__color if {points,w} +replace_color[colors] 0,0,$replace_color,$current_replace_color +rows[points] 3,5 permute. xzcy -. 1 replace_color. 0,0,$replace_color,$current_replace_color +. 1 permute. xcyz +j[points] .,0,3 rm.. else 0 0 fi => colors_replaced,points_replaced if narg($baseview) rm[baseview] fi fi # Render base image. if !narg($baseview) nx0:=$x0*$pw/$w ny0:=$y0*$ph/$h nx1:=$x1*$pw/$w ny1:=$y1*$ph/$h +z[img] $x0,$y0,$x1,$y1 r. $ww,$wh,1,100%,{$ww baseview if narg($view) rm[view] fi fi # Render view. if !narg($view) [baseview] r. 100%,100%,1,3 if $contrast<9 /. {10-$contrast} +. {128*(1-1/(10-$contrast))} fi if $view_markers if $view_markers==2 rad1=5 rad2=3 else rad1=3 rad2=2 fi if narg($replace_color)" && "{points,w} ipoints=$points_replaced else ipoints=$points fi repeat w#$ipoints { +columns[$ipoints] $> x:=(i[0]-$x0)*$ww/(1+$x1-$x0) y:=(i[1]-$y0)*$wh/(1+$y1-$y0) col:=i[3]-1,i[4]-1,i[5]-1 rm. circle. $x,$y,$rad1,1,0 circle. $x,$y,$rad2,1,$col } fi if narg($replace_color) to. "Replace by",5,5,20,2 rectangle. 80,8,111,25,1,0 rectangle. 82,10,109,23,1,$replace_color rectangle. 150,8,181,25,1,0 rectangle. 152,10,179,23,1,$current_replace_color fi => view w[view] $ww,$wh,0,$title fi while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER} # Recompute colors at full resolution. if narg($view) to[view] "Processing fullres...",5,5,20,2 w[view] fi k[0,img,points] N={points,w} status= if $N status={points,^} [img] __x_colorize. $1 pointcloud[points] -1,$w,$h +compose_channels[points] max !=. 0 distance. 1 *. 0.02 +. 1 ^. -1 +[potential,-1] if !$1 zfact:={img,max(w,h)}/{potential,max(w,h)} dilate[points] {int(3*$zfact)} fi watershed[points] [potential] -[points] 1 =>[points] colors else [img],[img],1,3,255 => colors fi if $3 # Multichannels output. k[0,colors] a c else # Merge for single layer output. k[0,img,colors] if $1 +*[img] -1 +. 255 channels. -3,0 blend[colors,-1] alpha rm[0,img] else rgb2ycbcr[colors] j[colors] [img],0,0,0,0 rm[0,img] ycbcr2rgb[colors] fi fi a c => $name __color=-1 # Force color selectors to close. u $status # Return control points. w 0 # Compute potential function. __x_colorize : if $1 # Potential for lineart. n. 0,1 ^. 5 repeat 4 { +b. 0.5% min } else # Potential for generic grayscale image. gradient_norm. n. 0,255 normalize_local. 3,3 *. -1 n. 0,255 b. 0.05% n. 0,1 sqr. +b. 0.5% n[-2,-1] 0,1 min[-2,-1] fi => potential #@cli x_connect4 #@cli : Launch the Connect Four game. x_connect4 : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Connect Four"$n" --------------------------------------------\n ----\n ---- Connect four tokens in a row, column or diagonally\n ---- to win the game.\n ----\n ---- "${c}"Left mouse button"$n" on a column inserts a new token.\n ---- Keys '"${c}"SPACE"$n"' or '"${c}"ENTER"$n"' lets the computer play the turn\n ---- (or restart game when it's over).\n ---- Key '"${c}"ENTER"$n"' also enables autoplay for the current player.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' close the window.\n ----\n ----------------------------------------------------------------" l[] { # Create sprite graphics. 7,6 => board R={board,u={*,u};v={*,v};int(0.5*min(u/w,v/h))} {2*$R},{2*$R} circle. 50%,50%,32%,1,1 b. 2% g. xy +[-2,-1] n. 0,1 +n. -1,0.5 abs. negate. +f. 200 rv[-3--1] a[-3--1] c hsv2rgb. to_rgba. circle. 50%,50%,27%,1,0 {4*$R},{4*$R} circle. 50%,50%,20%,1,1 b. 1% g. xy +[-2,-1] n. 0,1 negate. sqrt. +n. -1,0.8 abs. negate. +f. 10 rv[-3--1] a[-3--1] c . sh. 0 f. 60 rm. hsv2rgb[-2,-1] 100%,100% circle. 50%,50%,30%,1,255 a[-3,-2] .,c rm. rs[-3--1] $R s[-3--1] c,-3 rm... n[^0] 0,255 round[^0] =>[^0] cache,cachem,token0,token1,tokenm +b[cachem] 2% shift. 1%,1%,0,0,2 max[cachem,-1] r[cache,cachem] 100%,{board,h*100}%,1,100%,0,2 # Define board evaluation function. evalf="const op = 3 - p; case(dx,dy) = ( pgood = pbad = 0; for (k = -2, k<2, ++k, X = x + k*dx; Y = y + k*dy; if (X>=0 && X=0 && Y=0 && val==op, pgood = -1, pgood+=!!val); if (pbad>=0 && val==p, pbad = -1, pbad+=!!val); ); ); pgood = arg(2 + pgood,0,1,2,4,100,1e8); pbad = arg(2 + pbad,0,1,2,4,10000,1e8); pgood - pbad; ); case(1,0) + case(0,1) + case(1,1) + case(1,-1)" # Start game. do # Initialize game. if !narg($visu) {board,[w,h]*$R},1,3,64 repeat h#$board { y=$> repeat w#$board { x=$> val={board,i($x,$y)} if $val j. [token{$val-1}],{$x*$R},{$y*$R},0,0,1,[tokenm],255 fi } } +r[cache,cachem] {board,[w,h]*$R},1,100%,0,2 a[-2,-1] c blend[-2,-1] alpha => visu w[visu] 100%,100%,0,"[G'MIC] Connect Four" turn=0 is_falling=0 x=-1 yv=0 dyv=0 winner= autoplayer0=0 autoplayer1=0 fi # Estimate ymax for each column. if !narg($ymax) {board,w},1,1,1,"y = -1; repeat (h#"$board",k, !i(#"$board",x,k)?(y = k)); y" => ymax if iM<0 winner=-1,-1,-1 fi # Draw game fi if narg($winner) # End of game animation [visu] => tmpvisu x:=arg(1,$winner) y:=arg(2,$winner) c:=arg(3,$winner) if $x<0 # Draw game if !narg($text) 0 t. "Draw game",0,0,57,1,1 rs. {tmpvisu,w/2} expand. xy,3 +dilate. 5 n.. 0,255 to_rgb.. => text,textm fi j[tmpvisu] [text],{tmpvisu,([w,h]-[w#$text,h#$text])/2},0,0,{0.7+0.3*sin(5*$|)},[textm] else repeat 4 { [token$turn] rgb2hsv. sh. 1,2 +. {0.4*sin(5*$|)} c. 0,1 rm. hsv2rgb. j. [cache],0,0,0,0,1,[cachem],255 j[tmpvisu] .,{$R*[$x,$y]},0,0,1 rm. x+=$c!=2 y+=$c==1?0:$c==4?-1:1 } fi w[tmpvisu] 100%,100% rm[tmpvisu] wait 20 if {*,-SPACE}" || "{*,-ENTER} # Restart new game rm[visu,ymax] f[board] 0 winner= fi elif !$is_falling # Manage column selection if !${autoplayer$turn} x={visu,X={*,x};X<0?X:int(X*w#$board/w)} yM:=i("#"$ymax,$x) if {board,$x<0" || "$x>=w} w[visu] 100%,100% else [visu] $R,100%,1,3,($yM>=0)*($turn?[255,255,0]:[255,0,0]) j.. .,{$x*$R},0,0,0,{$yM>=0?0.15:0.3} rm. w. 100%,100% rm. fi wait fi if {*,-b}&1" && "$yM>=0" && "$x>=0 is_falling=1 yv=0 dyv=1 # Manual play elif ${autoplayer$turn}" || "{*,-SPACE}" || "{*,ENTER} # Computer play if {*,-ENTER} autoplayer$turn=1 fi max_score=-inf max_col= repeat w#$board { move1=$> yM1:=i("#"$ymax,$move1) if $yM1>=0 +=[board] {1+$turn},$move1,$yM1 => board1 {board,w},1,1,1,"y = -1; repeat (h#"$board1",k, !i(#"$board1",x,k)?(y = k)); y" => ymax1 opp_max_score=-inf opp_max_board={board,^} opp_turn:=($turn+1)%2 repeat w#$board { move2=$> yM2:=i("#"$ymax1,$move2) if $yM2>=0 +=[board1] {1+$opp_turn},$move2,$yM2 +f. "const p = 1 + "$opp_turn"; "$evalf score:=is+u rm. if $score>$opp_max_score opp_max_score=$score opp_max_board={^} fi rm. fi } rm[board1,ymax1] {board,[w,h,1,1]},$opp_max_board f. "const p = 1 + "$turn"; "$evalf score:=is+u rm. if $score>$max_score max_score=$score max_col=$move1 fi fi } x=$max_col is_falling=1 yv=0 dyv=1 fi else # Manage token falling animation if !narg($column) $R,{board,h*$R},1,3,64 repeat h#$board { v={board,i($x,$>)} if $v j. [token{$v-1}],0,{$>*$R},0,0,1,[tokenm],255 fi } => column fi yM:=i("#"$ymax,$x) [column] j. [token$turn],0,$yv,0,0,1,[tokenm],255 j. [cache],0,0,0,0,1,[cachem],255 [visu] => tmpvisu j[tmpvisu] ..,{$R*$x},0,0,0 rm.. w[tmpvisu] 100%,100% if $yv>=$yM*$R j[visu] [tmpvisu] is_falling=0 =[board] {$turn+1},$x,$yM rm[ymax,column] # Check end of game. +f[board] "if (!i,0, case_h = i==j(1) && i==j(2) && i==j(3); case_v = i==j(0,1) && i==j(0,2) && i==j(0,3); case_d1 = i==j(1,1) && i==j(2,2) && i==j(3,3); case_d2 = i==j(1,-1) && i==j(2,-2) && i==j(3,-3); case_h?1:case_v?2:case_d1?3:case_d2?4)" if iM winner:=xM,yM,i(xM,yM) # Player won ! else turn:=($turn+1)%2 fi rm. fi rm[tmpvisu] yv:=min($yM*$R,$yv+$dyv) dyv+={visu,h/100} wait 20 fi while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0 } #@cli xz : eq. to 'x_crop' xz : _gmic_s="$?" v + _x_crop #@cli x_crop #@cli : Crop selected images interactively. #@cli : If multiple input images are selected, the same crop is applied to all images. #@cli : (eq. to 'xz'). x_crop : _gmic_s="$?" v + _$0 _x_crop : e[0--3] "Crop image"$_gmic_s" interactively." if $!<1 return fi w[0] ${"fitscreen "{0,[w,h,d]}},1,"[G'MIC] "{n}" - Interactive crop" +select[0] 2,{0,round([w,h,d]/2)},0,1 coords={^} rm. z $coords u $coords #@cli x_cut #@cli : Cut selected images interactively. x_cut : e[^-1] "Cut image"$_gmic_s" interactively." foreach { wsiz0=${"fitscreen ."} value0,value1=-1,-1 w[] $wsiz0,0,"[G'MIC] "{n}" - Interactive cut" 0 for {*}" && "!{*,ESC} { if [w#1,h#1]!=[{*,w,h}] # Generate image view rm[1] +slices[0] 50% r. {*,w,h},1,100%,1 w. fi mx,my,mb={*,x,y,b} if $mb" && "$mx>=0" && "$my>=0 value0,value1:="dw1 = "{*,w}" - 1; value0 = "$mx"*100/dw1; dh1 = "{*,h}" - 1; value1 = "$my"*100/dh1; [ value0,value1 ];" update_view=1 fi if $update_view if $value0>=0" && "$value1>=0 +c[1] $value0%,$value1% n. 0,255 to. "Min: "{_round($value0,0.1)}%"\n"\ "Max: "{_round($value1,0.1)}%,1%,1%,{max(13,3.5*h%)} w. rm. else w. fi update_view=0 fi wait if {*,r} update_view=1 fi if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 update_view=1 fi if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 update_view=1 fi if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] $wsiz0 wait -1 update_view=1 fi } w[] 0 rm. u $value0%,$value1% c ${} } #@cli x_fire #@cli : Launch the fire effect demo. x_fire : skip "${1=}" check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Fire effect"$n" ------------------------\n ----\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -------------------------------------------" if "['$1']==0" label="G\47MIC" is_fps=1 else label="$1" is_fps=0 fi # Init image data. i[0] 100,32 w[0] {[4.5*w,6.75*h]},0,"[G"{`39`}"MIC] Fire Effect" if {*,w}<0.5*{*,u} w[] {[{*,w},{*,h}]*1.5} fi i[1] (0,255,255,255,255^0,0,255,255,255^0,0,0,128,255) r[1] 256,1,1,3,3 i[2] (0,0,0;0,0,0;1,1,1;0,1,0) *[2] 0.21 text3d $label,${"font macondo,50"},8,1 mv. 3 c3d[3] n3d[3] *3d[3] 320 col3d[3] 255,205,130 db3d 0 f3d 300 100,100 rand. 0,255 ellipse. 50%,50%,5,5,0,1,300 b. 10 sharpen. 1000 shrink. xy,1 n. 0,255 to_rgb. light3d . rm. # Start animation loop. angle=0 do correlate[0] [2] # Apply fire effect. {0,w},1 rand. 128,256 j[0] .,0,{{0,h}-1} rm. # Add new random values at the bottom line. +r[0] 400,200,1,1,3 map. [1] # Map fire palette +r3d[3] 0,1,0,$angle j3d.. .,50%,50%,0,1,5,0,0 # Draw 3D object. *3d. 0.25,0.16,1 j3d[0] .,50%,50%,0,1,3,0,0 rm. angle+=3 # Update 3D angle. if $is_fps fps=${-fps} if $fps>0 to. $fps" fps",5,{h-22},16,1,0.2 fi fi w. if {*,CTRLLEFT}" && "{*,D} w[] {1.5*[w,h]} elif {*,CTRLLEFT}" && "{*,C} w[] {[w,h]} fi rm. wait 40 while {*}" && "!{*,ESC}" && "!{*,Q} # Exit properly. rm[0-3] w 0 #@cli x_fireworks #@cli : Launch the fireworks demo. x_fireworks : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Fireworks"$n" --------------------------\n ----\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------" l[] { (16;64^64;32^128;32) r 320,160,1,3,3 # [-2] = Background (color gradient). . # [-1] = Rendered image. w. ${"fitscreen .,35%"},0,"[G"{`39`}"MIC] Fireworks" # Display window. time=0 do # Start animation loop. time-=1 if $!==2\ ||\ $time<0 # Insert new rocket. i[0] ({u(w)},\ # X-position {h},\ # Y-position {u(-3,3)},\ # X-velocity {u(2)-5},\ # Y-velocity {30+u(20)},\ # Time of explosion 1.5,\ # Radius 255,255,255) # Color time:=u(20) # Elapsed time until next rocket. fi *. 0.99 # Create fading effect with previous frames. j. ..,0,0,0,0,0.2 # Add background. i=0 repeat $!-2 { to_be_removed=0 radius:={$i,@4}>0?{$i,@5}/3:{$i,@5}*(1+2*({$i,@4}+2)/120) ellipse. {$i,@0},{$i,@1},{$i,@5},{max(1,$radius)},{atan2({$i,@3},{$i,@2})*180/pi},0.6,{$i,@6-8} # Draw rocket. ({$i,@2},{$i,@3},0,0.09,-1,0,0,0,0) +[$i,-1] # Compute new position of the rocket. if {$i,@0}<0\ ||\ {$i,@0}>=w\ ||\ {$i,@1}>=h\ ||\ $radius<0 to_be_removed=1 fi # Discard if rocket disappear. if {$i,@4}<0\ &&\ {$i,@4}>=-1 # In case of explosion -> Split current rocket into several colorful rockets. color:=min(255,80+u(200)),min(255,80+u(200)),min(255,80+u(200)) radius:=u(10) N:=5+u(10) repeat $N { angle:=$>*2*pi/$N i... ({$i,@0,1},{2*cos($angle)+{$i,@2}/1.5},{2*sin($angle)+{$i,@3}/1.5},-2,$radius,$color) } to_be_removed=1 fi if $to_be_removed rm[$i] else i+=1 fi # If processed rocket has to be removed. } fps=${-fps} if $fps>0 to. $fps" fps",3,{h-20},14,1,0.2 fi w. wait 20 # Display rendered frame. if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0 } #@cli x_fisheye #@cli : Launch the fish-eye effect demo. x_fisheye : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Fish-eye effect"$n" --------------------\n ----\n ---- "${c}"Mouse pointer"$n" moves fish-eye center.\n ---- "${c}"Mouse buttons"$n" set fish-eye size.\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -------------------------------------------" if $!>0 a x n 0,255 rs ,220 else 120,90,1,3 rand. 0,255 plasma. 0.3,3 n 0,255 t " G\47MIC\nFISH-EYE\n EFFECT",20,13,23,1,255 scale3x b 5 sharpen 1000 f i+150-3*abs(y-h/2) c. 0,255 frame_fuzzy. 15,10,15,1.5,0 to_rgb. fi torus3d 20,6 col3d. {u(30,255)},{u(30,255)},{u(30,255)} +r3d. 1,0,0,90 col3d. {u(30,255)},{u(30,255)},{u(30,255)} +3d. 15 +3d[-2,-1] *3d. 4 db3d 0 c3d. R=30 w.. {1.25*{-2,w}},{1.25*{-2,h}},0,"[G"{`39`}"MIC] Fish-Eye Effect" do wait 40 if {*,b}==1 R:=min(80,$R+8) fi if {*,b}==2 R:=max(3,$R-8) fi +j3d.. .,{50+30*cos($|*2.5)}%,{50+30*sin($|*1.6)}%,{80+230*sin($|*2.6)},0.7,3,0,0 r3d.. 1,0.2,0.6,3 if {*,x}>=0 fisheye. {{*,x}*100/{*,w}},{{*,y}*100/{*,h}},$R fi w. if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi rm. if !{*}" || "{*,ESC}" || "{*,Q} rm[-2,-1] w 0 return fi while 1 #@cli x_fourier #@cli : Launch the fourier filtering demo. x_fourier : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Fourier-filtering"$n" ----------------------------------------\n ----\n ---- "${c}"Mouse buttons"$n" on the right image to set min/max frequencies.\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to decrease window size.\n ---- Keys '"${c}"CTRL+R"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -----------------------------------------------------------------" if !$! sp ? rs 400 fi foreach { # Init variables. need_update=1 # need_update (boolean) freqmin=0 # min freq. (in %) freqmax=100 # max freq. (in %) if w>3*{*,u}/5 rs. {3*{*,u}/10} fi # Reduce image size if necessary. if h>3*{*,v}/5 rs. ,{3*{*,v}/5} fi # Compute fourier transform. +fft. => real,imag # Generate log-magnitude image. +sqr[real,imag] +[-2,-1] sqrt. +. 1 log. n. 0,255 shift. {round(w/2)},{round(h/2)},0,0,2 to_colormode. {-2,s} => logmag +rs. ,128 frame. 1,1,0 => thumb w[0,-2] -1,-1,0,"[G"{`39`}"MIC] Fourier Filtering" l { if !narg($first_time) parallel 0,"alert[thumb] \"[G"{`39`}"MIC Fourier Filtering]\",\ \"The G\47MIC Fourier filtering demo illustrates the effect\n\ of bandpass frequency filtering on an image. Use your mouse\n\ buttons to select low and high bounds for the frequencies\n\ displayed on the Fourier representation of the image\n\ (right image).\",\ \"OK\"" first_time=0 fi # Enter user event-loop. do if $need_update # If image must be updated. # Generated filtering mask. [logmag],[logmag] => mask r:=sqrt(w^2+h^2)*$freqmax/200 ellipse[mask] 50%,50%,$r,$r,0,1,1 r:=max(0,sqrt(w^2+h^2)*$freqmin/200-1) if $r ellipse[mask] 50%,50%,$r,$r,0,1,0 fi # Compute filtered log-magnitude. +*[logmag] [mask] +. [mask] /. 2 n. 0,255 # Compute filtered fourier representation. shift[mask] -{mask,round(w/2)},-{mask,round(h/2)},0,0,2 +*[real,imag] [mask] rm[mask] # Compute filtered image by inverse fourier. ifft[-2,-1] rm. n. 0,255 # Display filtered image. rv[-2,-1] if {*} r[-2,-1] {{*,w}/2},{*,h} fi t. "Freq. Min/Max = "{int($freqmin)}"% / "{int($freqmax)}"%",5,5,13,1,255 w[-2,-1] rm[-2,-1] need_update=0 fi wait if {*,b}" && "{*,x}>={*,w}/2 # If mouse button pressed on the right pane. r:=200*sqrt(({*,x}-3*{*,w}/4)^2+({*,y}-{*,h}/2)^2)/\ # Compute selected radius (in %). sqrt(({*,w}/2)^2+{*,h}^2) if {*,b}&1 freqmax=$r # Update max freq. if left button. else freqmin:=max(0,$r-3) # Update min freq. if other button. fi if $freqmin>=$freqmax freqmin=$freqmax fi # Check that the min/max freq. are ordered. need_update=1 # Tell that the image must be updated. fi if {*,r} need_update=1 fi # Increase window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} need_update=1 fi # Decrease window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} need_update=1 fi # Reset window size. if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {2*{0,w}},{0,h} need_update=1 fi while {*}" && "!{*,ESC}" && "!{*,Q} w 0 } rm[^0] # Clean images and window. } rm #@cli x_grab_color : _variable_name #@cli : Open a color grabber widget from the first selected image. #@cli : Argument 'variable_name' specifies the variable that contains the selected color values at any time. #@cli : Assigning '-1' to it forces the interactive window to close. #@cli : Default values: 'variable_name=xgc_variable'. x_grab_color : skip ${1=xgc_variable} check_display $0 if !$! error[0--3] "Command '$0': Missing specified input image." fi l[0] { nm={n} => img e[^-1] "Open "${arg\ {0,s},GRAY,GRAYA,RGB,RGBA}" color grabber widget for image$?, with variable name '$1'." if !{*} w[] ${fitscreen[]\ {[w,h,1]},128,50%},0,0,-1,-1,"Grab a color" fi _x_grab_color +dilate. 3 => icon_mask *.. 255 to_rgb.. =>.. icon_sprite xc=5 yc=5 o$1=$$1 cursor[0] 0 do if !narg($visu0) +r[img] {*,w},{*,h},1,100%,2 drgba. w. => visu0 fi x,y,b={*,x,y,b} mouse_over={$x>=0" && "$y>=0} hc:=narg($$1)?40:24 yc={visu0,nhc=h-$hc-8;!$mouse_over?$yc:$y<$hc||$yc+$hc>=h?nhc:$y>=nhc?5:$yc} if [0$ox,0$oy,0$ob,0$ohc,0$oyc,0$ocolor,0${o$1}]!=[$x,$y,$b,$hc,$yc,0$color,0$$1] [visu0] => visu if narg($color) 24,$hc,1,[img] fc. $color if narg($$1) rectangle. 0,24,100%,100%,1,$$1 line. 0,24,100%,24,1,0 fi drgba. frame. 1,1,0 frame. 1,1,255 j[visu] .,$xc,$yc rm. 0 if narg($$1) t. "Position ("$X","$Y")\nColor ("{``$color}")\nSelected ("{``$$1}")",1,0,15,1,255 else t. "Position ("$X","$Y")\nColor ("{``$color}")",1,0,15,1,255 fi +dilate. 5 r.. 100%,100%,1,3 j[visu] ..,{30+$xc},$yc,0,0,0.85,.,255 rm[-2,-1] fi if $mouse_over X={img,round($x*(w-1)/({*,w}-1))} Y={img,round($y*(h-1)/({*,h}-1))} color={img,round(I($X,$Y))} j[visu] [icon_sprite],$x,{icon_sprite,$y-h+1},0,0,1,[icon_mask] if $b&1 $1=$color fi fi w[visu] rm[visu] ox=$x oy=$y ob=$b ohc=$hc oyc=$yc ocolor=$color o$1=$$1 fi if arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_' wait 50 else wait fi if {*,r} w[] -1 rm[visu0] yc=5 fi if ['$$1']=='-1' break fi # Close request while {*}" && "!{*,ESC}" && "!{*,Q} w 0 k[img] => $nm } u $color # Define color grabber icon. _x_grab_color : base642img[] \ "MSB1bnNpZ25lZF9jaGFyIGxpdHRsZV9lbmRpYW4KMSAzMzcgMSAxICMyNzcKeJyNiNlOwlAYBi9IDDcaY+LGpiJbj7tBKEbglTQE7dcbkZS1gfNXFCx"\ "lK6WE83w+gDaaeG0mmWTm67NSea5s1Pz6Ot80tnrb/d3BnhkYBq3QKDyOTA5nmfqDnW2W5nK77Mj60yLXgZvrqm6Oq47MYWc4ZhnCJE2wrgjmBWFwRm"\ "r/hNQ3ZqivKQIlCN0YQZdeQGgxzXOD1T3XWEMhVWMtz1Wmw4h1QHEOShogqQc67YPOBwpdmgpdWzDSY9DNFJS1wWUH/NZF926FTl4oel48tgui3C6KU"\ "rMo7rWCYCvJg4k/fvp/5/dJq9QyuUy48UXMOZ5H7ah9ND2YRMbhUXgUsoLDgOnxsf++0/TX1zRf1fcNa0iPgw==" -. 127 decompress_rle. frame. 10,10,0 rs. 24 #@cli x_hanoi #@cli : Launch the Tower of Hanoi game. x_hanoi : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Tower of Hanoï"$n" ---------------------\n ----\n ---- "${c}"Left button"$n" and "${c}"mouse"$n" to move a disk.\n ---- "${c}"Right button"$n" to rotate 3D view.\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -------------------------------------------" l[] { l[] { # Create 3D rods cylinder3d 1,10 r3d 1,0,0,90 ++3d 10,0,0 +-3d.. 10,0,0 box3d 30,1,10 -3d. 15,0,5 +3d => rods3d 400,400 noise. 1 fftpolar. f.. "r = sqrt((x - w/2)^2 + .01*(y - h/2)^2);i/(1 + r)" ifftpolar[-2,-1] n. 0,31 (86,50,50;132,36,12;218,109,66;231,207,180;255,193,140) permute. yzcx r. 32,1,1,3,3 map.. . rm. b. 2% b. x,1% sharpen. 100 c. 0,255 r3d.. 1,0,0,-90 texturize3d.. . r3d.. 1,0,0,90 rm. } l[] { # Create 3D disks 6,1,1,3,'[360*x/w,0.9*(1-(x/w)^0.5),0.9]' hsi2rgb. ytop0=0 repeat w#0 { R,r:=3-0.3*$>,1.6-0.22*$> torus3d $R,$r,36,10 300,300 plasma. 1,1,3 b. 20 sharpen. 300 n. 150,255 1,1,1,3,{0,I[$>]} r. ..,..,1,3 rv[-2,-1] blend[-2,-1] luminance,0.75 # Marble texture texturize3d.. . rm. /3d. 1,1,{0.3+$r} r3d. 1,0,0,90 -3d. 0,0.8,0 a$>,x$>,y$>,h$>=0,0,$ytop0,{1.8*$r/(0.3+$r)} ytop0+=${h$>} => disk3d$> } rm[0] } w[] 640,400,0,"[G"{`39`}"MIC] Tower of Hano\357" 1,3,1,3,'!y?[32,128,100]:y==1?[64,16,0]:[0,0,0]' r. {*,w},{*,h},1,3,3 => background nb_moves,buttons,motion3d_x,motion3d_y=0 x,rod,rod_source,rod_target,selected=-1 fading=$| error=0 do # Display 3D view for current game state. repeat 6 { +r3d[disk3d$>] 1,1,1,${a$>} +3d. {10*(${x$>}-1)},-${y$>},0 } +3d[-6--1] +3d. [rods3d] r3d. 1,0,0,20 if !($buttons&2) r3d. 0,1,0.3,{5*cos(1.5*$|)} r3d. 0.3,0,1,{3*sin(0.8*$|)} fi r3d. 1,0,0,$motion3d_y r3d. 0,-1,0,$motion3d_x *3d. 20 [background] j3d. ..,50%,70%,10,1,5,0,1,800,200,0,-3000,0.15,0.2 t. "#Moves: "$nb_moves,2%,92%,20,1,255 if $error (255^0^0) ri. .. j.. .,0,0,0,0,$error error:=max(0,$error-0.2) rm. fi if $|-$fading<1 *. {$|-$fading} fi w. wait 40 if {*,CTRLLEFT}" && "{*,D} w[] {w*1.5},{h*1.5} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi rm[-2,-1] # Get index of top disk for each rod. top0,top1,top2,ytop0,ytop1,ytop2=-1 repeat 6 { rod:=round(${x$>}) if $selected!=$>" && "${y$>}+${h$>}>${ytop$rod} ytop$rod:=${y$>}+${h$>} top$rod=$> fi } # Manage user events. prev_buttons=$buttons mouse_x,mouse_y,buttons={*,x,y,b} if $mouse_x>=0 x:=2.6*($mouse_x/{*,w}-0.5)+1 rod:=round($x) fi if $mouse_x>=0" && "$buttons&2 # Right mouse button motion3d_x,motion3d_y:=([$mouse_x,$mouse_y]/[{*,w},{*,h}]-0.5)*90 elif $mouse_x>=0" && "$buttons&1 # Left mouse button if $selected<0 # Select a disk selected=${top$rod} rod_source:=$selected<0?-1:$rod fi if $selected>=0" && "$rod>=0 # Move a selected disk if ${y$selected}<11 y$selected:=min(11,${y$selected}+3) else x$selected+=d=$rod-${x$selected};sign(d)*min(0.3,abs(d)) y$selected:=x=${x$selected};11+1.5*sin(pi*abs(x-round(x))) a$selected:=x=${x$selected};45*sin(pi*abs(x-round(x))) fi fi elif !$buttons # No mouse button if $rod>=0" && "$selected>=0 if $rod_target<0 if ${top$rod}<$selected rod_target=$rod nb_moves+=$rod_target!=$rod_source # Allowed move else rod_target=$rod_source error=0.8 # Forbidden move fi fi x$selected=$rod_target a$selected=0 ytop:=max(0,${ytop$rod_target}) if ${y$selected}>$ytop y$selected:=max($ytop,${y$selected}-3) else x,rod,rod_source,rod_target,selected=-1 fi fi fi if !($buttons&2) # Slowly go back to initial 3D view motion3d_x-=sign($motion3d_x)*min(1,abs($motion3d_x)) motion3d_y-=sign($motion3d_y)*min(1,abs($motion3d_y)) fi while {*}" && "!{*,ESC}" && "!{*,Q} w[] 0 rm } #@cli x_histogram #@cli : Launch the histogram demo. x_histogram : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Histogram demo"$n" -------------------------------\n ----\n ---- "${c}"Mouse"$n" to set parameters.\n ---- "${c}"Right button"$n" or key '"${c}"SPACE"$n"' to reset.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -----------------------------------------------------" if !$! sp ? to_rgb else k[0] to_rgb rs ,300,2 if w>800 r 800,100%,1,3,2 fi n 0,255 fi # Prepare image layout. +frame. 1,1,0 300,{h},1,3,220 t. "Gamma :",5,0,16,1,0 t. "Contrast :",5,50,16,1,0 t. "Brightness :",5,100,16,1,0 t. "Smoothness :",5,150,16,1,0 t. "Sharpness :",5,200,16,1,0 t. "Clusters :",5,250,16,1,0 a[-2,-1] x {w},200,1,3,255 grid. 10%,10%,0,0,0.3,0xCCCCCCCC,0 rectangle. 0,0,100%,100%,1,0xFFFFFFFF,0 axes. 0,255,1,0,13,1,0 frame[-2,-1] xy,5,220 a[-2,-1] y # Initialize variables. clusters=64 sharpness=0 smoothness=0 contrast=1 brightness=0 gamma=1 # Start event loop. do # Render corrected image and insert it in canvas. [0] ia:=ia if $gamma /. 255 ^. {1/$gamma} *. 255 fi -. $ia *. $contrast +. $brightness +. $ia b. $smoothness sharpen. $sharpness c. 0,255 +j.. .,6,6 # Render parameter sliders. sx:=w#0+12 _x_histogram. {$gamma*100/4} j.. .,$sx,25 rm. _x_histogram. {$contrast*100/4} j.. .,$sx,75 rm. _x_histogram. {($brightness+128)*100/256} j.. .,$sx,125 rm. _x_histogram. {$smoothness*100/10} j.. .,$sx,175 rm. _x_histogram. {$sharpness*100/2000} j.. .,$sx,225 rm. _x_histogram. {$clusters*100/256} j.. .,$sx,275 rm. # Render corresponding histogram. +s.. c histogram[-3--1] $clusters,0,255 /[-3--1] {6*{0,wh}/$clusters} rm[-5] +z[-4] 5,{0,h+16},{{-4,w}-5},{{-4,h}-6} graph. [-4],3,0,1,0,0.2,255,0,0 graph. ...,3,0,1,0,0.2,0,255,0 graph. ..,3,0,1,0,0.2,0,0,255 rm[-4--2] j.. .,5,{0,h+16} rm. if {*,b}&1\ &&\ {*,x}<{0,w}\ &&\ {*,y}<{0,h} j. [0],6,6 to. Original,10,10,16 fi # Display rendering. w. {w},{h},0,"[G"{`39`}"MIC] Histogram Demo" rm. wait # Manage user interactions. if {*,b}&1\ &&\ {*,x}>={0,w}-10 if {*,y}>=25\ &&\ {*,y}<=42 gamma:=max(0,min(4,({*,x}-$sx)*4/280)) elif {*,y}>=75\ &&\ {*,y}<=92 contrast:=max(0,min(4,({*,x}-$sx)*4/280)) elif {*,y}>=125\ &&\ {*,y}<=142 brightness:=max(-128,min(128,({*,x}-$sx)*256/280-128)) elif {*,y}>=175\ &&\ {*,y}<=192 smoothness:=max(0,min(10,({*,x}-$sx)*10/280)) elif {*,y}>=225\ &&\ {*,y}<=242 sharpness:=max(0,min(2000,({*,x}-$sx)*2000/280)) elif {*,y}>=275\ &&\ {*,y}<=292 clusters:=max(2,min(256,({*,x}-$sx)*256/280)) fi fi if {*,b}&2\ ||\ {*,SPACE} clusters=64 sharpness=0 smoothness=0 contrast=1 brightness=0 gamma=1 fi while {*}" && "!{*,ESC}" && "!{*,Q} w 0 rm _x_histogram : val:=max(0,min(100,$1)) 280,2,1,3,255 line. 0,0,$val%,0,1,0,255,0 line. 0,1,$val%,1,1,240,255,62 r. 100%,16,1,3,3 0 t. {round($val)}%,0,0,14,1,1 +*. -255 +. 255 r. 100%,100%,1,3 j... .,{(280-w)/2},{(16-h)/2},0,0,1,.. rm[-2,-1] r. {w+2},{h+2},1,3,0,0,0.5,0.5 #@cli x_hough #@cli : Launch the hough transform demo. x_hough : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Hough-transform"$n" -----------------------------------------\n ----\n ---- "${c}"Mouse buttons"$n" on the vote image to draw corresponding line.\n ---- "${c}"Mouse buttons"$n" on the image to vote for all lines crossing.\n ---- the clicked point.\n ---- Key '"${c}"SPACE"$n"' to reset the hough window.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -----------------------------------------------------------------" if !$! l[] { sp greece onfail testimage2d 400 } fi n 0,255 foreach { r. ${fitscreen\ {[w,h]}},1,100%,3 # Resize to fit screen if necessary. if !narg($first_time) parallel 0,"+l[0] { rs ,128 frame xy,1,0 \ alert \"[G"{`39`}"MIC Hough Transform]\",\ \"The G\47MIC Hough transform demo illustrates the application\n\ of the Hough transform to detect lines in an image. Use your\n\ mouse buttons to explore the transform image and see how\n\ lines in images are represented by points in the transform.\",\ \"OK\" \ rm }" first_time=0 fi rhomax:=sqrt(w^2+h^2)/2 +b. 1.5 hough. 512,400 b. 0.5 +. 1 log. n. 0,255 w.. -1,-1,0,"[G"{`39`}"MIC] Image" w1. -1,-1,0,"[G"{`39`}"MIC] Hough Transform" do wait if {*,b} # When clicking on the image. x0:={*,x}-{*,w}/2 y0:={*,y}-{*,h}/2 rho0:=sqrt(($x0)^2+($y0)^2) theta0:=atan2($y0,$x0) (0,{2*pi}) ($theta0,{$theta0-2*pi}) r[-2,-1] {-3,w},1,1,1,3 cos. *. $rho0 +<. 0 abs.. *. {pi} +[-3,-1] %.. {2*pi} *.. {0.5*{-3,w}/pi} *. {{-3,h}/$rhomax} a[-2,-1] y repeat w { point.. {i($>,0)},{i($>,1)},0,0.3,255 } rm. w1. elif {*1,x}>=0" && "{*1,b} # When clicking on the vote window. theta:={*1,x}*2*pi/{*1,w} rho:={*1,y}*$rhomax/{*1,h} x:={-2,w}/2+$rho*cos($theta) y:={-2,h}/2+$rho*sin($theta) x0:=$x+1000*sin($theta) y0:=$y-1000*cos($theta) x1:=$x-1000*sin($theta) y1:=$y+1000*cos($theta) .. line. $x0,$y0,$x1,$y1,1,0x0F0F0F0F,255 line. {$x0+1},$y0,$x1,$y1,1,0x0F0F0F0F,255 line. $x0,{$y0+1},$x1,$y1,1,0x0F0F0F0F,255 line. $x0,$y0,$x1,$y1,1,0xF0F0F0F0,0 line. {$x0+1},$y0,$x1,$y1,1,0xF0F0F0F0,0 line. $x0,{$y0+1},$x1,$y1,1,0xF0F0F0F0,0 w. rm. elif {*,SPACE}" || "{*1,SPACE} rm. +b. 1.5 hough. 512,400 b. 0.5 +. 1 log. n. 0,255 w1. -1,-1,0,"Hough Transform" elif {*,r} w.. elif {*1,r} w1. fi while {*}" && "{*1}" && "!{*,ESC}" && "!{*,Q}" && "!{*1,ESC}" && "!{*1,Q} w 0 w1 0 rm. if !{*}" || "!{*1} break fi } rm #@cli x_jawbreaker : 0<_width<20,0<_height<20,0<_balls<=8 #@cli : Launch the Jawbreaker game. x_jawbreaker : check "${1=12}>0 && $1<20 && ${2=13}>0 && $2<20 && ${3=5}>0 && $3<=8" check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Jawbreaker"$n" --------------------------------------------\n ----\n ---- The goal of the game is to "${c}"remove the maximum number of\n ---- balls on the board"$n", simply by clicking on them. But a\n ---- colored ball can disappear only if it is grouped with at\n ---- least one ball of the same color. The score is higher if\n ---- you destroy larger sets of connected colored balls.\n ----\n ---- "${c}"Left mouse button"$n" to select/destroy balls on board.\n ---- Key '"${c}"BACKSPACE"$n"' or '"${c}"SPACE"$n"' to undo the last move.\n ---- Key '"${c}"S"$n"' to save snapshot of the current view.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------------------------" # Init images and variables. $1,$2 => board rand[board] 1,$3 round[board] 1 . => undo 40,40,1,4 => balls _x_jawbreaker_ball. autocrop. 0 expand. xy,1 *. 1.5 c. 0,255 r. {{board,w}*w},{{board,h}*h},1,1,0,2 /. 255 {w},{h},1,3 => back l. { rand 0,255 b x,6 b y,20 equalize 100,0,255 b x,2 b y,4 sh 0 sh.. 1 sh... 2 /... 4 /.. 8 /. 2 rm[-3--1] } [back] => visu score=0 undoscore=0 render_board=1 shapescorey=0 shapescore=0 # Enter user-event loop. do # Render board graphics. if $render_board +abs[board] -. 1 *. {360/$3} +>=[board] 0 *. 0.9 +!=[board] 0 ri[-3--1] [balls] [balls] *[-2,-1] a[-3--1] c hsv2rgb. +compose_channels. + >. 0 dilate. 3 j[visu] [back] j[visu] ..,0,0,0,0,1,. rm[-2,-1] if !$shapescorey w[visu] {back,w},{back,h},0,"[G"{`39`}"MIC] Jawbreaker (Score : "$score")" fi render_board=0 fi # Add shape score sprite if necessary. if $shapescorey +t[visu] "+"$shapescore,{*,x},{{*,y}-64+$shapescorey},32,{($shapescorey-1)/31},255 shapescorey:=max(0,$shapescorey-1) w. {back,w},{back,h},0,"[G"{`39`}"MIC] Jawbreaker (Score : "$score")" rm. wait 25 else wait fi # Check for the end of the game. +f[board] "i?j(-1)==i || j(1)==i || j(0,1)==i || j(0,-1)==i:0" if !is rm. break fi rm. # Manage user-events if {*,r} render_board=1 # Will resize window to initial size, if resized. elif {*,S} o[visu] gmic_jawbreaker.png # Save snapshot if requested. elif {*,BACKSPACE}" || "{*,SPACE} # Manage undo move. abs[undo] j[board] [undo] score=$undoscore render_board=1 elif {*,x}>=0" && "{*,b} # Manage button click. # Retrieve board coordinates. wait -1 x:=int({*,x}*{board,w}/{*,w}) y:=int({*,y}*{board,h}/{*,h}) # When selecting a ball -> display selection and init new shape score sprite. if {{board,i($x,$y)}>0} abs[board] flood[board] $x,$y,0,0,0,1,-{board,i($x,$y)} +>=[board] 0 -. 1 shapescore:=(is+1)^2 shapescorey:=$shapescore?32:0 rm. # When confirming selection of a ball -> remove set of connected balls. elif {board,i($x,$y)} +flood[board] $x,$y,0,0,0,1,-1 ==. -1 if is>1 # If selected ball is connected to at least one ball. # Save undo state. j[undo] [board] undoscore=$score # Manage board shifts (vertical and horizontal). flood[board] $x,$y,0,0,0,1,0 repeat w#$board { +columns[board] $> mirror. y h={board,h} l. { s -,0 a y if $! r 1,$h,1,1,0 mirror y else i 1,$h fi } j[board] .,$> rm. } rows[board] -1,100% f[board] "!y?(i(x,h-1)?x:w):i" sort[board] +,x rows[board] 1,100% # Update score. score+=int((is-1)^2) fi rm. # Remove selection mask. else abs[board] # Remove previous selection if clicked outside balls. fi render_board=1 fi while {*}" && "!{*,Q}" && "!{*,ESC} # Game over. if {*}" && "!{*,ESC} w[] {visu,w},{visu,h},0,"[G"{`39`}"MIC] Jawbreaker (Final Score : "$score")" 260,85 => gameover t. "Game Over!",3,0,53,1,1 t. "Score : "$score,23,53,32,1,1 +dilate. 5 => "mgameover" *.. 255 r.. 100%,100%,1,3 repeat 25 { +r[gameover,mgameover] {400-12*($>+1)}%,{400-12*($>+1)}% +j[visu] ..,{({visu,w}-w)/2},{({visu,h}-h)/2},0,0,{$>/25},. w. rm[-3--1] wait 25 } do wait if {*,r} w[] {*,w},{*,h} wait -1 fi while {*}" && "!{*,Q}" && "!{*,ESC}" && "!{*,b} rm[gameover,mgameover] fi # End properly. rm[board,undo,balls,back,visu] w 0 _x_jawbreaker_ball : mwh:=min(w,h) sh 3 f. 0 rm. ellipse {0.5*$mwh},{0.5*$mwh},{0.5*$mwh-4},{0.5*$mwh-4},0,1,240,240,240,1 sh 0,2 *. '($mwh+y-x)/(2*w)' rm. sh 3 *.. . dilate. 5 rm. sh 0,2 +. '"i && (!j(-1) || !j(1) || !j(0,-1) || !j(0,1))?240/6:0"' rm. ellipse {$mwh*0.7},{$mwh*0.3},{min(30,$mwh*$mwh/512)},{min(30,$mwh*$mwh/512)},0,{min($mwh/64,1)},255,255,255,1 #@cli x_landscape #@cli : Launch the virtual landscape demo. x_landscape : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Virtual landscape"$n" -------------------------------------\n ----\n ---- Enjoy the view!\n ----\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------------------------" l[] { W=150 H=350 # Generate global map + colors. 900,900 plasma. 1,1,6 b. 0.07% n. 0,255 => map +g. *. 0.5 +[-2,-1] n. 0,1 ^. 2 n. -150,330 equalize[map] 256 n[map] -400,160 c[map] 0,100% # Add water. (0,102,51;149,175,124;102,42,0;255,255,255) permute. yzcx srgb2rgb. r. 256,1,1,3,3 rgb2srgb. +n[map] 0,255 map. .. rm.. +. .. rm.. c. 0,255 => colors # Colors. # Pre-compute some images used on each frame. $W,$H,1,1,x y. x => x # Increasing x. $W,$H,1,1,"1 + x + y*w" y. x => offsets # Offsets (+1). $W,$H,1,1,"0.5*y" => gmap Mgmap:=iM # Z-increment for altitude map. $W,$H,1,3 fc. 60,80,135 => ccolors # Color for the horizon. $W,$H,1,1,"(y/$H)^2" => mcolors # Mask for the horizon. $W,400,1,1,"b=h-1-$Mgmap;y>=b?256+(y-b)*255/(h-1-b):y*255/b" round. # Background. (96^16^128) (0^200^255) a[-2,-1] x r. 256,1,1,3,3 (0^32^0) (0^64^128) a[-2,-1] x r. 256,1,1,3,3 a[-2,-1] x map.. . rm. => background quadrangle3d[] -0.45,0,0,0.45,0,0,0.55,1,0,-0.55,1,0 *3d. {$W/2},{$H/2} => viewrange3d # View range. (64^16^0) r. $W => groundcolor # Ground color. # Start animation. w[] 600,400,0,"[G"{`39`}"MIC] Virtual Landscape" do # Get part of the map to display. t:=$|*0.03 xm={map,w/2+(w-$H/2)/2*cos(3.1*$t)} ym={map,h/2+(h-$H/2)/2*sin(2.8*$t)} u={map,(w-$H/2)*cos(2.5*$t)} v={map,(h-$H/2)*sin(9.7*$t)} a:=atan2($v,$u)*180/pi +r3d[viewrange3d] 0,0,1,$a y. x ({$xm+i[8]},{$xm+i[11]};{$xm+i[17]},{$xm+i[14]}^{$ym+i[9]},{$ym+i[12]};{$ym+i[18]},{$ym+i[15]}) rm.. r. $W,$H,1,2,3 +warp[map,colors] .,0,1,0 rm... => lmap,lcolors # Add color shading and altitude to local maps. +!=[lmap] 0 => ground +[lmap] [gmap] j[lcolors] [ccolors],0,0,0,0,1,[mcolors] j[lcolors] [groundcolor] +round[lmap] f. ">m = abs(j(0,-1)); i>m?i:-m" => y0 # Compute visible top points. +shift. 0,1 abs. +. 1 => y1 # Compute visible bottom points. *[y0,y1] [ground] rm[ground] r[lcolors,y0,y1] {$W*$H},1,1,100%,-1 # Keep only visible primitives. +>[y0] 0 *. [offsets] discard. 0 y. if h # There is something to display (ground). -. 1 +warp[x] .,0,0,0 => lx warp[lcolors,y0,y1] ..,0,0,0 rm.. # Generate 3D object. N:=h ({'CImg3d'},{2*$N},$N) +a[lx] [y0],x rm[y0] +a[lx] [y1],x rm[lx,y1] a[-2,-1] y z. 0,2 1,$N,1,1,2 +f. y ++. $N a[-3--1] x mv[lcolors] $! permute. cyzx 1,$N,1,1,1 y[-5--1] y a[-5--1] y *3d. -1,-1 +j3d[background] .,{background,w-1},{background,h},0,1,1,0,0,0 rm[-3,-2] else # Case of nothing to display (only water). rm[-5--1] [background] fi r. {*,w},{*,h},1,3 fps=${-fps} if $fps>0 to. $fps" fps",5,5,24,2,0.2 fi w. -1,-1,0 rm. if {*,CTRLLEFT}" && "{*,D} w[] 900,600 elif {*,CTRLLEFT}" && "{*,C} w[] 600,400 fi wait 20 while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0 } #@cli x_life #@cli : Launch the game of life. x_life : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"The game of life"$n" --------------------------------------\n ----\n ---- The goal is to create the "${c}"biggest possible biological\n ---- system"$n". You start with a stock of cells which you can\n ---- spread over the board. For each new cells created\n ---- simultaneously and spontaneously by your system, you\n ---- gain more new cells to scatter.\n ----\n ---- "${c}"Left mouse button"$n" to scatter cells in stock.\n ---- "${c}"Right mouse button"$n" to reset game.\n ---- Key '"${c}"S"$n"' to save snapshot of the current view.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------------------------" i[0] 90,90,1,1,0 # Image[0] = game state. i[1] [0] f[1] 0 # Image[1] = generation counter. i[2] 400,400,1,3 # Image[2] = visualization. i[3] 1 # Image[3] = colormap (to be initialized). iteration=0 # Iteration counter. score=0 # Current score. bestscore=0 # Best score. stock=500 # Remaining cells. w[0] 400,400,0,"[G"{`39`}"MIC] The Game of Life" # Initialize display window. cursor[0] 0 # Start user-event loop. do (1,1,1;1,0,1;1,1,1) +correlate[0] .,0 rm.. # Count numbers of neighboring living cells. +ir. 2,2 &. [0] ir.. 3,3 -|[-2,-1] # Make the game evolve (kill or create cells). rv[0,-1] # Update game state. if {*,x}>0" && "{*,b}==1" && "$stock>0 # Add random cells to the game if mouse button pressed. nb:=u*7 repeat $nb { x:={*,x}/{*,w}*{0,w}+u(-4,4) y:={*,y}/{*,h}*{0,h}+u(-3,3) =[0] 1,$x,$y =[1] $iteration,$x,$y point[2] {$x*{2,w}/{0,w}},{$y*{2,h}/{0,h}},0,0.8,255 } stock:=round(max(0,$stock-$nb)) fi -. [0] *. -1 # Compute difference between consecutive states. stock-=2*(min(0,int(is/16*$score/150))) # Increment available cells if the evolution is fast. +[1] [0] # Increment generation counter for still existing cells. min. 0 +. 1 *[1,-1] # Reset generation counter for died cells. if {*,b}==2 # Reset game if right mouse button has been pressed. f[0-2] 0 iteration=0 score=0 bestscore=0 stock=500 rm[3] i[3] 1 fi if {3,w}==1 # Create color palette if necessary. rm[3] i[3] {u(3,12)},1,1,3,u(100,255) r[3] {u(100,300)}%,1,1,3,4 point[3] 0,0,0,1,0 r[3] {u(100,600)}%,1,1,3,5 c[3] 0,255 fi +r[1] {2,w},{2,h} &. 7 b. {1+$score*0.05} # Render colored image of the game and display it. n. 0,{3,w} map. [3] *. 0.1 +[2,-1] /[2] 1.1 [2] if {*,x}>0 # Add a small target icon at the mouse position. opac:=0.7*min(1,$stock/500) r:=min(500,$stock)*cos($iteration)/100 ellipse. {*,x},{*,y},{15+$r},{15+$r},0,$opac,0,196,0 ellipse. {*,x},{*,y},{10+$r},{10+$r},0,$opac,32,64,16 ellipse. {*,x},{*,y},{5+$r},{5+$r},0,$opac,255,230,0 fi t. "Living cells : "$score"\n"\ # Add score description. "Stock : "$stock"\n"\ "Score : "$bestscore,5,3,22,0.7,255 w. {*,w},{*,h} if {*,S} o. gmic_life.png fi # Save snapshot if requested. rm. if !($iteration%10) # Re-compute current and best scores, every 10th iterations score={0,is} bestscore:=max($score,$bestscore) fi wait 60 iteration+=1 while {*}" && "!{*,ESC}" && "!{*,Q} # End game and quit properly. rm[0-3] w 0 #@cli x_light #@cli : Launch the light effect demo. x_light : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Light effect"$n" ------------------------\n ----\n ---- Move light position with "${c}"mouse"$n".\n ---- "${c}"Mouse buttons"$n" fade light in/out.\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -------------------------------------------" # Create warping and color images. 0 t. " G\47MIC\nLight effect",0,0,${"font \"Acme\",120"},1,255 expand. xy,15 b. 3 . n.. 0,1 r.. 100%,100%,1,3 sh.. 0 *. 120 rm. sh.. 1 *. 70 rm. sh.. 0,50%,0,2 *. 120 rm. 25%,25%,1,1 rand. -20,20 smooth. 10,0,1,1,4 ri. ..,3 b. 3 n. -100,100 +[-2,-1] g. xy a[-2,-1] c n. -150,150 w[] {1.5*{-2,w}},{1.5*{-2,h}},0,"[G"{`39`}"MIC] Light Effect" # Init display window. cursor[0] 0 # Create a large light image. light=70 640,640 gaussian. $light n. 0,255 t=0 # Start animation. do # Manage light position and intensity. if {*,x}>=0 X:=round((w-{*,x})/2) Y:=round((h-{*,y})/2) else X:=round((w-{-2,w}*(1+cos(2*$t)))/2) Y:=round((h-{-2,h}*(1+sin(2.5*$t)))/2) t+=0.02 fi if {*,b}&1 light:=min(200,$light+10) gaussian. $light n. 0,255 fi if {*,b}&2 light:=max(10,$light-10) gaussian. $light n. 0,255 fi # Render lightened image. +z. $X,$Y,{$X+{-2,w}-1},{$Y+{-2,h}-1} warp. ...,1,0,1 r. 100%,100%,1,3 +. [-4] c. 0,255 fps=${-fps} if $fps>0 to. $fps" fps",5,5,16,1,0.2 fi w. if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi rm. if {*,x}>=0" && "!{*,b} wait else wait 20 fi while {*}" && "!{*,ESC}" && "!{*,Q} w[] 0 rm[-3--1] #@cli x_mandelbrot : _julia={ 0 | 1 },_c0r,_c0i #@cli : Launch Mandelbrot/Julia explorer. x_mandelbrot : skip ${1=0},${2=0.317},${3=0.03} check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Mandelbrot/Julia explorer"$n" -----------------\n ----\n ---- Select zooming region with "${c}"mouse"$n".\n ---- "${c}"Click once"$n" to reset zoom factor.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ---- Key '"${c}"C"$n"' to print current fractal coordinates.\n ----\n --------------------------------------------------" # Init variables and display. rm w 512,512,0 _x_mandelbrot_coords $1 _x_mandelbrot_palette # Start event loop. do siz:=min({*,w,h}) # Desired window dimension. $siz,$siz mandelbrot. {0,^},256,$1,{[$1?$2:0,$1?$3:0]} map. [1] # Render fractal. if $1 w. $siz,$siz,0,"[G"{`39`}"MIC] Julia Set c=("{0,@0-1}")-("{0,@2-3}"), c0=($2,$3)" # Display on window. else w. $siz,$siz,0,"[G"{`39`}"MIC] Mandelbrot Set c=("{0,@0-1}")-("{0,@2-3}")" fi w,h={w},{h} round. select. 2,0,0,0,1 # Get the user selection. if i[0]>0 # If valid selection found. M:=max(i[3]-i[0],i[4]-i[1]) # Compute max dimension of selected rectangle. if $M<5 _x_mandelbrot_coords $1 rm[1] _x_mandelbrot_palette mv. 1 # If selection too small, reset the view, else ({{0,@0}+{@0}*({0,@2}-{0,@0})/$w};\ # Else compute new fractal coordinates. {{0,@1}+{@1}*({0,@3}-{0,@1})/$h};\ {{0,@0}+({@0}+$M)*({0,@2}-{0,@0})/$w};\ {{0,@1}+({@1}+$M)*({0,@3}-{0,@1})/$h}) fi rm[0] mv. 0 # Validate new coordinates. fi rm. # Delete latest rendering. if {*,C} # If 'C' key has been pressed. if $1 e "Julia set, at c = ("{0,@0-1}")-("{0,@2-3}"), with c0 = ($2,$3)." else e "Mandelbrot set, at c = ("{0,@0-1}")-("{0,@2-3}")." fi fi if !{*}" || "{*,ESC}" || "{*,Q} break fi wait -1 while 1 rm w 0 _x_mandelbrot_coords : if $1 (-2;-2;2;2) else (-2.1;-1.5;1.2;1.5) fi _x_mandelbrot_palette : 6,1,1,3 rand. 20,255 r. 32,1,1,3,3 r. 1024,1,1,3,0,2 =. 0,0,0,0,0 =. 0,0,0,0,1 =. 0,0,0,0,2 #@cli x_mask_color : _colorspace={ all | rgb | lrgb | ycbcr | lab | lch | hsv | hsi | hsl | cmy | cmyk | yiq },\ # _spatial_tolerance>=0,_color_tolerance>=0 #@cli : Interactively select a color, and add an alpha channel containing the corresponding color mask. #@cli : Argument 'colorspace' refers to the color metric used to compute color similarities, and can be basically #@cli : one of { rgb | lrgb | ycbcr | lab | lch | hsv | hsi | hsl | cmy | cmyk | yiq }. #@cli : You can also select one one particular channel of this colorspace, by setting 'colorspace' as #@cli : 'colorspace_channel' (e.g. 'hsv_h' for the hue). #@cli : Default values: 'colorspace=all', 'spatial_tolerance=5' and 'color_tolerance=5'. x_mask_color : check "${2=5}>=0 && ${3=5}>=0" skip ${1=all} check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e[^-1] "Interactively create color mask for image$?, with color space $1, spatial tolerance $2 and color tolerance $3." e "\n ----------------------------------------------------------------------------------------------------\n ----\n ---- "${c}"Left mouse button"$n" adds a wanted color to the selection.\n ---- "${c}"Right mouse button"$n" adds an unwanted colors to the selection.\n ---- "${c}"Middle mouse button"$n" or key '"${c}"R"$n"' resets color mask.\n ---- Key '"${c}"SPACE"$n"' or '"${c}"TAB"$n"' toggles view modes (masked RGB or mask).\n ---- Keys '"${c}"CTRL+D"$n"' increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n ---- Keys '"${c}"CTRL+R"$n"' reset window size.\n ---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' exit the interactive window.\n ----\n ----------------------------------------------------------------------------------------------------" l[] { _ac_$1 onfail error[0--3] "Command '$0' : Invalid colorspace '$*'." } m _ac_forward:$_f foreach { slices 0 basename {0,n} nm=${} wh=${fitscreen\ {[w,h,1]},128,1024} +r $wh,1,100%,2 +_ac_forward. channels. $_s if {1,s>3} channels[1] 0,2 fi to_rgb[1] w[1] 100%,100%,0,$nm colors_add=-1 colors_sub=-1 visumode=0 is_clicked=0 time=0 delay=0.1 do # Manage user events. time:=$is_clicked?$time:$| wait x:=round({*,x}*(w#2-1)/(w-1)) y:=round({*,y}*(h#2-1)/(h-1)) b={*,b} c=$x,$y,{2,I($x,$y)} is_add:=arg(1,$colors_add)>=0 is_sub:=arg(1,$colors_sub)>=0 is_ctrl:={*,CTRLLEFT}" || "{*,CTRLRIGHT} is_resized=0 refresh=0 if $x>=0" && "$b&1 # Add a color if $is_add colors_add=$colors_add,$c else colors_add=$c fi is_clicked=1 refresh:=$|-$time>$delay elif $x>=0" && "$b&2 # Subtract a color if $is_sub colors_sub=$colors_sub,$c else colors_sub=$c fi is_clicked=1 refresh:=$|-$time>$delay elif $b&4" || "{*,R} # Reset colors colors_add=-1 colors_sub=-1 refresh=1 is_clicked=1 elif !$b refresh:=$is_clicked==1 is_clicked=0 fi if {*,-TAB}" || "{*,-SPACE} visumode:=($visumode+1)%3 refresh=1 fi # Change visu mode if {*,r} is_resized=1 elif $is_ctrl" && "{*,-D} w[] {1,1.25*[w,h]} is_resized=1 elif $is_ctrl" && "{*,-C} w[] {1,0.8*[w,h]} is_resized=1 elif $is_ctrl" && "{*,R} w[] ${fitscreen\ {0,[w,h,1]},128,1024} is_resized=1 fi if $is_resized rm[1,2] +r {*,d},{*,e},1,3,2 +_ac_forward. channels. $_s refresh=1 fi # Refresh view. if $refresh _x_mask_color[2] {$2*w#2/w#0},$3,{``$colors_add},{``$colors_sub} delay=${} if !$visumode +. 64 c. 0,255 +a[1,-1] c drgba. w. -1,-1,$nm" [half-masked]" rm. elif $visumode==1 +a[1,-1] c drgba. w. -1,-1,$nm" [masked]" rm. else w. -1,-1,$nm" [mask]" fi rm. time=$| fi while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER} # Rescale to original image dimensions. if arg(1,$colors_add)>=0 ($colors_add) r. {2+s#2},{w/(2+s#2)},1,1,-1 +z. 0,1 *. '{(w#0-1)/(w#2-1)},{(h#0-1)/(h#2-1)}' j.. . colors_add={-2,^} rm[-2,-1] fi if arg(1,$colors_sub)>=0 ($colors_sub) r. {2+s#2},{w/(2+s#2)},1,1,-1 +z. 0,1 *. '{(w#0-1)/(w#2-1)},{(h#0-1)/(h#2-1)}' j.. . colors_sub={-2,^} rm[-2,-1] fi rm[-2,-1] +_ac_forward channels. $_s _x_mask_color. $2,$3,{``$colors_add},{``$colors_sub} rm.. a c } um _ac_forward _x_mask_color : # Estimate color mask image. 100%,100% is_add:=arg(1,$3)>=0 is_sub:=arg(1,$4)>=0 t0=$| if $is_add" || "$is_sub if $is_add ($3) r. {2+s#0},{w/(2+s#0)},1,1,-1 N_add:=h M_add:="M = vectorw(); fill(M,k,med(crop(k,1)));M" rm. fi if $is_sub ($4) r. {2+s#0},{w/(2+s#0)},1,1,-1 N_sub:=h M_sub:="M = vectorw(); fill(M,k,med(crop(k,1)));M" rm. fi f. "begin( const is_add = "$is_add"; const is_sub = "$is_sub"; const ss = sqrt(2)*$1; const sc = sqrt(2)*$2; colors_add = [ $3 ]; colors_sub = [ $4 ]; M_add = [ 0"$M_add"]; M_sub = [ 0"$M_sub"]; const N_add = 0"$N_add"; const N_sub = 0"$N_sub"; const siz = 2 + s#0; const siz2 = sqr(siz); sigma = vectorsiz(sc); sigma[0] = sigma[1] = ss; tensor(op) = ( T = vectorsiz2(); if (is_#op, for (k = 0, k,fy$>,fz$>:=2*[g,g,g] } w[0] -1,-1,0,"[G"{`39`}"MIC] 3D Metaballs" do repeat $M { x$>:=w/2+0.5*(w-{2,w}-4)*cos(${fx$>}*$|) y$>:=h/2+0.5*(h-{2,h}-4)*sin(${fy$>}*$|) z$>:=d/2+0.5*(d-{2,d}-4)*sin(${fz$>}*$|) } f[3] 0 repeat $M { j[3] [2],{${x$>}-{2,w/2}},{${y$>}-{2,h/2}},{${z$>}-{2,d/2}},0,-1 } +r[3] 28,28,28,1,2 isosurface3d. 0.4 -3d. 12,12,12 *3d. 13 rv3d. r3d. 1,2,1,{100*$|} N:=i[7] (255,255,150;200,96,164;50,150,230) r. 3,$N,1,1,3 y. j.. .,0,{{-2,h}-4*$N} # Do some color tweaks. if !$mode circles3d.. 4 fi if !{1,w} 0 t. ${s$mode},5,5,${"font Sofia,43"},0.5,255,255,255 r. {w+5},100%,1,3,0 b. 0.7 n. 0,255 +dilate. 3 +j[0] ..,5,3,0,0,1,.,255 mv. 1 rm[2,-2,-1] fi +j3d[1] ..,50%,50%,0,1,{!$mode?3:$mode},0,0,300,0,0,-500,0.1,1.5 fps=${-fps} if $fps>0 to. $fps" fps",5,{h-22},16,2,0.2 fi w. if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi rm[-3--1] wait 20 if {*,b}" || "{*,SPACE} mode:=($mode+({*,b}&2?-1:1))%6 wait -1 rm[1] i[1] 0 fi while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0 } #@cli x_minesweeper : 8<=_width=<20,8<=_height<=20 #@cli : Launch the Minesweeper game. x_minesweeper : check "${1=20}>=8 && $1<=30 && ${2=$1}>=8 && $2<=30" check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Minesweeper"$n" -------------------------------------------\n ----\n ---- The goal is to "${c}"clear the minefield"$n" without detonating a\n ---- mine.\n ----\n ---- "${c}"Left mouse button"$n" to try clearing one square.\n ---- "${c}"Right mouse button"$n" to flag or unflag a square.\n ---- "${c}"Middle mouse button"$n" to reset mine field.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------------------------\n" # Generate random mine field and player board. # Labels : 0:mine, 1:empty, 2:'1-near', 3:'2-near', ..., 9:'8-near', 10:still unknown. $1,$2 noise. 30,2 ==. 1 nb_mines:=is (1,1,1;1,0,1;1,1,1) +convolve.. .,0 rm.. +. 1 ==.. 0 *[-2,-1] => field do x:=v(w-1) y:=v(h-1) while i($x,$y)!=1 # Find a good starting point. +f[field] 11 =. 12,$x,$y => board # Generate sprite graphics. 24,24,1,3,200 fc. 255,180,130 ellipse. 12,12,4,4 line. 6,12,18,12 line. 12,6,12,18 line. 13,10,14,10,1,255 line. 13,11,14,11,1,255 z. 1,1,{w-2},{h-2} frame. xy,1,0 +fc. 230,250,255 +t. "1",10,5,13,1,0,196,0 +t.. "2",9,5,13,1,0,128,0 +t... "3",9,5,13,1,0,0,255 +t[-4] "4",9,5,13,1,255,0,0 +t[-5] "5",9,5,13,1,200,0,0 +t[-6] "6",9,5,13,1,150,0,0 +t[-7] "7",9,5,13,1,128,0,0 +t[-8] "8",9,5,13,1,64,0,0 +f. "x<=1 || y<=1 || x>=w-2 || y>=h-2?(x sprites # Pre-calculate offsets and canvas for faster board rendering. (0,23;0,23^0,0;23,23) r. 24,24,1,2,3 r. {board,w*24},{board,h*24},1,2,0,2 => offsets .,.,1,3,255 frame. xy,1,0 frame. xy,23,255 0 t. "Number of mines : "$nb_mines,0,0,18,1,100,200,255 negate. j.. .,{({-2,w}-w)/2},{{-2,h}-h-2} rm. => canvas # Start user interaction loop. failed=0 succeeded=0 nb_flags=0 started=0 do # Render board. +*[board] 24 r. [offsets],[offsets] channels. 0,1 +. [offsets] +warp[sprites] .,0,0,1 rm.. j[canvas] .,24,24 rm. # Wait for user's selection. wait -1 if $failed 0 t. "Game\nOver!",3,3,38,1,255 r. 100%,100%,1,4 sh. 3 dilate. 5 /. 2 rm. drop_shadow. 5,5,1 blend[canvas,-1] alpha 0 t. "Boom! You failed!",0,0,18,1,100,255,255 r. {canvas,w},100%,1,3,0,0,0.5,0.5 negate. j[canvas] .,0,3 rm. do w[canvas] {w},{h} wait while {*}" && "!{*,ESC}" && "!{*,Q} elif $succeeded 0 t. "Success!",3,3,38,1,255 r. 100%,100%,1,4 sh. 3 dilate. 5 /. 2 rm. drop_shadow. 5,5,1 blend[canvas,-1] alpha 0 t. "Congratulations! ("{round($|-$tic)}" s)",0,0,18,1,255,100,255 r. {canvas,w},100%,1,3,0,0,0.5,0.5 negate. j[canvas] .,0,3 rm. do w[canvas] {w},{h} wait while {*}" && "!{*,ESC}" && "!{*,Q} else +==[board] 10 nb_flags:=is rm. do if !$started tic=$| fi 0 t. "Elapsed time : "{round($|-$tic)}" s / Flags : "$nb_flags,0,0,18,1,255,200,0 r. {canvas,w},100%,1,3,0,0,0.5,0.5 negate. j[canvas] .,0,3 rm. wait 50 x,y:=int(([{*,x,y}]-24)/24) b={*,b} w[canvas] {w},{h},0,"[G"{`39`}"MIC] Minesweeper" while {*}" && "!{*,ESC}" && "!{*,Q}" && "!$b fi # Manage selected square. if $x>=0\ &&\ $y>=0\ &&\ $x<{board,w}\ &&\ $y<{board,h} if $b&1 # Try to clean square. started=1 val={field,i($x,$y)} if !$val +==[field] 0 j[board] [field],0,0,0,0,1,. rm. failed=1 # Found a mine -> boom! elif $val==1 # Found an empty area +flood[field] $x,$y,0,0,1,1,-1 ==. -1 dilate. 3 j[board] [field],0,0,0,0,1,. rm. else =[board] $val,$x,$y # Close to one or several mines fi elif n={board,i($x,$y)};$b&2" && "n>=10" && "n<=11 =[board] {board,i($x,$y)==11?10:11},$x,$y # Flag or unflag a square. elif $b&4 f[board] 10 # Reset minefield. fi fi if $nb_mines==$nb_flags\ &&\ {board,iM}!=11 succeeded=1 fi # Check if board is cleared. while {*}" && "!{*,ESC}" && "!{*,Q} w 0 #@cli x_minimal_path #@cli : Launch the minimal path demo. x_minimal_path : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Minimal path"$n" ------------------------------------------\n ----\n ---- "${c}"Click on two points"$n" to compute and display the minimal\n ---- path between those points. The ending point is then\n ---- chosen as the next starting point for another path.\n ---- Key '"${c}"S"$n"' to save snapshot of the current view.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------------------------" if !$! sp ? fi n 0,200 round 1 foreach { w[0] -1,-1,0,"[G"{`39`}"MIC] Select Starting Point P0" if !narg($first_time) parallel 0,"+l[0] { rs ,128 frame xy,1,0 \ alert \"[G"{`39`}"MIC Minimal Path]\",\ \"The G\47MIC minimal path demo illustrates how minimal paths\n\ can be computed in images to detect and track edge points.\n\ Use your mouse to select desired starting and ending points,\n\ and see what is the minimal path computed between these points.\",\ \"OK\" \ rm }" first_time=0 fi +gradient_norm b. 1 f. exp(-i/10) to_rgb[0] +select[0] 0 P0={^} ellipse[0] {@0,1},3,3,0,1,255,0,255 ellipse[0] {@0,1},3,3,0,1,0xFFFFFFFF,255,255,255 rm. if min($P0)>=0 p=1 do w[0] -1,-1,0,"[G"{`39`}"MIC] Select Ending Point P"$p +select[0] 0 if {*,S} rm. +to[0] "Saving snapshot...",5,5,13,1,1,255,255,255 w. rm. o[0] gmic_minimal_path.png wait -1 else P1={^} ellipse[0] {@0,1},3,3,0,1,255,0,255 ellipse[0] {@0,1},3,3,0,1,0xFFFFFFFF,255,255,255 rm. if min($P1)>=0 +to[0] "Processing...",5,5,13,1,1,255,255,255 w. rm. +minimal_path[1] $P0,$P1,1 transpose. pointcloud. 0 *. 255 r. 100%,100%,1,[0],0,0,0,0,0,0.5 ri. [0],0 -|[0,-1] P0=$P1 p+=1 fi fi while {*}" && "!{*,ESC}" && "!{*,Q} fi rm[1] w 0 } #@cli x_morph : _nb_frames>=2,_preview_fidelity={ 0:coarsest | 1:coarse | 2:normal | 3:fine | 4:finest } #@cli : Launch the interactive image morpher. #@cli : Default values: 'nb_frames=16' and 'preview_fidelity=3'. x_morph : check "isint(${1=16}) && isint(${2=3},0,4)" check_display $0 if $!<2 error[0--3] "Command '$0': Requires at least two input images!" return fi use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r b=$_vt100_b e "\n ------ "${g}"Interactive image morpher"$n" ---------------------------\n ----\n ---- "${b}"Source/target window:"$n"\n ---- "${c}"Left mouse button"$n": Add new keypoint on current image\n ---- and move it on the other one.\n ---- "${c}"Right mouse button"$n": Add/move keypoint on current image.\n ---- Key '"${c}"DELETE"$n"' or "${c}"middle mouse button"$n": Delete keypoint.\n ---- Key '"${c}"SPACE"$n"' or "${c}"mouse wheel"$n": Toggle source/target.\n\n ---- "${b}"In-between window:"$n"\n ---- "${c}"Mouse wheel"$n": Change morphing time, from 0 to 1.\n ---- "${c}"Left mouse button"$n": Reset morphing time to 0.5.\n\n ---- "${b}"Both windows:"$n"\n ---- Key '"${c}"TAB"$n"': Change keypoint radius.\n ---- Key '"${c}"ENTER"$n"': Play/stop in-between animation.\n ---- Key '"${c}"R"$n"': Reset keypoints.\n ---- Key '"${c}"K"$n"': Show/hide keypoints.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"': Process fullres and exit.\n ----\n -----------------------------------------------------------" offset=0 radius_keypoints=3 repeat $!-1 { l[{[$>,$>+1]+$offset}] { nm0={0,n} nm1={1,n} to_colormode 0 rs ${-max_wh},2,1 => img0,img1 256,1,1,1,"x>=4?x-2:1" map. 2 => colormap # Start interactive window. selected_keypoint=-1 view_keypoints=1 time_inbetween=0.5 animate_inbetween=0 move_other=0 frame=0 do # Generate set of keypoints (x0,y0,x1,y1,index_color) if !narg($keypoints) if narg($__x_morph_keypoints) ($__x_morph_keypoints) r. 1,{w/5},1,5,-1 else 1,4,1,5,"p = y%2; q = int(y/2); [ [ p,q,p,q ]*100,y ]" fi N0:=h => keypoints rmn warp0,warp1 fi # Generate base images. if !narg($imgb0)" || "!narg($imgb1) if {*} wdims=${"fitscreen "{*,d},{img0,{*,d}*h/w}} else wdims=${"fitscreen "{img0,[w,h,1]},128,50%} fi w[] $wdims,0,"[G'MIC] Interactive Morph" repeat 2 { +to_color[img$>] r. $wdims,1,100%,2 n. 0,255 => imgb$> } rmn imgr,warp0,warp1 fi # Generate warp fields. if (!narg($warp0)" || "!narg($warp1)) subsamp:=arg(1+$2,8,6,4,2,1) +_x_warp_rbf[keypoints] {imgb0,round([w,h]/$subsamp)} +l[keypoints] { s c,-2 rv[0,1] a c } +_x_warp_rbf. {imgb0,round([w,h]/$subsamp)} rm.. *[-2,-1] $subsamp r[-2,-1] {imgb0,[w,h]},1,100%,3 => warp0,warp1 rmn imgm fi # Render visualization. if !narg($imgr) [imgb$frame] => imgr if s==4 drgba. fi if $view_keypoints eval[keypoints] "* begin( fact = ([ w#"$imgb0",h#"$imgb0" ] - 1)/100; const radius1 = "$radius_keypoints"; const radius2 = radius1 + 2; const opacity = min(1,3/"($radius_keypoints-1)"); ); X = round((I)[2*"$frame",2]*fact); y=="$selected_keypoint"?(ellipse(#-1,X,radius2+2,radius2+2,0,1,255)); ellipse(#-1,X,radius2,radius2,0,opacity,0); ellipse(#-1,X,radius1,radius1,0,opacity,I[#"$colormap",i4]); I" fi w. -1,-1,0,"[G'MIC] Interactive Morph ("${"s0=Source s1=Target u ${s"$frame"}"}")" fi # Generate morph image if !narg($imgm) t,onemt:=t=$animate_inbetween?0.5*(1+sin(2*($time_inbetween+$|-$animate_inbetween))):$time_inbetween;[t,1-t] +*[warp1] $t *. -1 +warp[imgb0] .,1,2,3 rm.. *. $onemt +*[warp0] $onemt *. -1 +warp[imgb1] .,1,2,3 rm.. *. $t +[-2,-1] c. 0,255 => imgm w1. -1,-1,0,"[G'MIC] Interactive Morph (In-between)" fi # Manage user interaction if $animate_inbetween wait 40 else wait fi mb={*,b} mxy:=[{*,x,y}]*100/([{*,w,h}]-1) mouse_over:={*,x}>=0 if $mouse_over" && "($mb" || "{*,DELETE})" && "$selected_keypoint<0" && "h#$keypoints>0 # Determine selected keypoint. selected_keypoint={keypoints,"dmin = inf; kmin = -1; fact = ([w#"$imgr",h#"$imgr"]-1)%; repeat (h,k, dist = norm(((I[k])[2*"$frame",2] - ["$mxy"])*fact); dist=0 && dmin<"max(8,1.5*$radius_keypoints)"?kmin:-1"} fi if {*,-o}" || "{*,-SPACE}" || "{*1,-SPACE} frame:=!$frame rmn imgr fi # Swap frames if {*,-K}" || "{*1,-K} view_keypoints:=!$view_keypoints rmn imgr fi # Show/hide keypoints if {*,-R}" || "{*1,-R} rmn keypoints,imgr __x_morph_keypoints= fi # Reset keypoints if {*,-TAB}" || "{*1,-TAB} # Change keypoint radius radius_keypoints:=max(2,($radius_keypoints+2)%8) view_keypoints=1 rmn imgr fi if {*,-ENTER}" || "{*1,-ENTER} animate_inbetween:=$animate_inbetween?0:$| fi # Start in-between animation if {*,r} rmn imgb0,imgb1 fi # Window resize if {*1,r} rmn imgm fi if {*1,o} # Change time of in-between time_inbetween:=max(0,min(1,$time_inbetween+0.05*{*1,-o})) animate_inbetween=0 rmn imgm fi if {*1,-b} time_inbetween=0.5 animate_inbetween=0 rmn imgm fi # Reset in-between time if $mouse_over" && "($mb==1" || "$mb==2)" && "$selected_keypoint<0 # Add keypoint if $mb==1" && "!$move_other frame:=!$frame move_other=1 fi # Move on other window ({keypoints,[$mxy,$mxy,h]}) permute. zycx a[keypoints,-1] y selected_keypoint={keypoints,h-1} rmn imgr elif $mouse_over" && "($mb==1" || "$mb==2)" && "$selected_keypoint>=0 # Move keypoint if $mb==1" && "!$move_other frame:=!$frame move_other=1 fi # Move on other window ({[$mxy]}) permute. zycx j[keypoints] .,0,$selected_keypoint,0,{2*$frame} rm. rmn imgr elif $mouse_over" && "$selected_keypoint>=0" && ("{*,-DELETE}" || "$mb==4") && "h#$keypoints>4 # Delete keypoint 1,1,1,5,-1 j[keypoints] .,0,$selected_keypoint rm. discard[keypoints] -1 r[keypoints] 1,{keypoints,h/5},1,5,-1 N0-=$selected_keypoint<$N0 selected_keypoint=-1 rmn warp0,warp1,imgr elif !{*,b}" && "$selected_keypoint>=0 if $move_other frame:=!$frame move_other=0 fi selected_keypoint=-1 rmn warp0,warp1,imgr fi if $animate_inbetween rmn imgm fi while {*}" && "!{*,ESC}" && "!{*,Q}" && "{*1}" && "!{*1,ESC}" && "!{*1,Q} if !$< __x_morph_keypoints={keypoints,^} fi # Render fullres morphing. rmn colormap,imgb0,imgb1,warp0,warp1,imgr,imgm +_x_warp_rbf[keypoints] {img0,[w,h]} +l[keypoints] { s c,-2 rv[0,1] a c } +_x_warp_rbf. {img0,[w,h]} rm.. => warp0,warp1 repeat $1 { t,onemt:=t=$>/max(1,$1-1);[t,1-t] +*[warp1] $t *. -1 +warp[img0] .,1,2,3 rm.. *. $onemt +*[warp0] $onemt *. -1 +warp[img1] .,1,2,3 rm.. *. $t +[-2,-1] c. 0,255 => ${nm0}_$> if {*}" && "{*1} w1 0 fi if {*}" || "{*1} text="Processing frame \#"$>"/"{$1-1}"..." if {*} +r. {*,d,e},1,100%,2 to. $text,5,5,20,2 w. rm. fi if {*1} +r. {*1,d,e},1,100%,2 to. $text,5,5,20,2 w1. rm. fi fi } => $nm1 k[-$1--1] offset+=$!-2 } } w0 0 w1 0 #@cli x_pacman #@cli : Launch pacman game. x_pacman : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Pacman"$n" -----------------------------------------------\n ----\n ---- This is a G\47MIC implementation of the "${g}"pacman"$n" game.\n ----\n ---- Move the pacman to eat all pacdots on the different levels.\n ---- Eating a pacgum makes pacman invincible for "${c}"10 seconds"$n",\n ---- which mean pacman can eat ghosts during this time.\n ---- Eating a ghost earns "${c}"100 pts"$n".\n ---- Eating a cherry earns "${c}"10 pts"$n".\n ---- Eating a strawberry earns "${c}"100 pts"$n".\n ---- Eating an orange earns "${c}"1000 pts"$n".\n ---- Eating a banana earns "${c}"5000 pts"$n".\n ----\n ---- "${c}"Arrow keys"$n" to control pacman.\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------------------------" l[] { # Initialize characters gfx. m "_pacman_ghost_base_gfx : 31,19 circle. 15,15,15,1,1 31,12,1,1,'y<4+8*abs(cos(x*0.3+0.25*pi*$""1))' a[-2,-1] y" repeat 4 { _pacman_ghost_standard_gfx 255,0,0,$> => ghost0_$> _pacman_ghost_standard_gfx 0,255,222,$> => ghost1_$> _pacman_ghost_standard_gfx 255,184,222,$> => ghost2_$> _pacman_ghost_standard_gfx 255,184,71,$> => ghost3_$> _pacman_ghost_afraid_gfx $> => ghosta_$> _pacman_ghost_base_gfx $> r. 16,16,1,1,2 => ghostm_$> _pacman_ghost_standard_gfx 0,0,0,$> => ghostd_$> _pacman_pacman_gfx $> => pacman_$>,pacmanm_$> } +channels[ghostd_0] 0 !=. 0 => ghostdm _pacman_cherry_gfx _pacman_strawberry_gfx _pacman_orange_gfx _pacman_banana_gfx => fruit0,fruit1,fruit2,fruit3 20,2,1,3,200 => gate score0,score1,score2,score3,score4=10,100,1000,5000,"Argh!" repeat 5 { 0 t. ${score$>},0,0,13,1,255,255,255 autocrop. 0 expand. xy,1 +dilate. 3 => score$>,scorem$> } time4=255,255,255 time3=255,255,32 time2=255,128,32 time1=255,32,32 repeat 11 { 0 t. $<" s",0,0,23,1,${time{min(4,round(($<+1)/2))}} => time$< } 0 t. "Get Ready!",0,0,${"font Acme,32"},1,255 autocrop. 0 expand. xy,4 +dilate. 8 r.. 100%,100%,1,3 => get_ready,get_readym 0 t. "Game\nOver!",0,0,${"font Acme,53"},1,255 autocrop. 0 expand. xy,4 +dilate. 8 r.. 100%,100%,1,3 => game_over,game_overm # Start game. score=0 level=-1 lives=3 is_quit=0 do # Build new level if necessary. if $level<0 _rlevel=33 _glevel=33 _blevel=255 _pacman_map_level{((-$level-1)%6)+1} mw,mh,mw2,mh2:=w,h,int(w/2),int(h/2) if $level<-6 replace. 3,2 fi . => map0,map # Precompute valid directions on each map point, and shortest path to the ghost's home. +shift[map] -1,0 +shift[map] 0,-1 +shift[map] 1,0 +shift[map] 0,1 a[-4--1] z !=. 1 => can_go +==[map] 1 100%,100% =. 1,$mw2,$mh2 distance. 1,..,3 channels. 100% f. "i==2?0:i==8?1:i==1?2:i==4?3:i" => path +==[map] 2 pacdots:=is rm. level:=-$level fi # Render board gfx. f[map] "i>=4?0:i" +==[map] 1 expand. xy,1 r. 1600%,1600% erode. 9 b. 2 g. xy abs[-2,-1] +[-2,-1] >=. 80% b. 2 n. 0,1 shrink. xy,16 +*. $_glevel +*.. $_blevel *... $_rlevel a[-3--1] c 16,16,1,1,'x' +-[map] 1 max. 0 *. 16 r. 1600%,1600% 16,16,1,1,'y' ri[-3,-1] ..,0,2 +[-2,-1] a[-2,-1] c 16,16,1,3 _pacman_pacdots_gfx _pacman_pacgum_gfx a[-3--1] y warp. ..,0,0,1 rm.. -|[-2,-1] r. 100%,{h+24},1,3,0,0,0,1 t. "Lives :",10,0,24,1,255 t. "Score :",{w-140},0,24,1,255 if $lives +r[pacman_2] 12,12,1,4,2 r. {100*$lives}%,100%,1,4,0,2 j.. .,90,7 rm. fi => visu w[visu] {visu,f=h<0.5*{*,v}?1.5:1;[w,h]*=f},0,"[G"{`39`}"MIC] Pacman" cursor[0] 0 0 t. "Level "$level,0,0,${"font Acme,64"},1,1 autocrop. 0 expand. xy,4 (0,255^0,255^0,0) +map.. . rm.. dilate.. 8 => levelm_N,level_N repeat 4 { xg$>:=16*$mw2 yg$>:=16*$mh2+4*$> dg$>=3 mg$>=0 } xp:=16*10 yp:=16*21 dp=-1 pacgum_timer=-1 fruit_timer=$| dying_pacman=0 is_get_ready=1 xscore=0 yscore=0 nscore=0 oscore=0 # Start game interaction. do # Display board graphics. t:=int(6*$|)%4 left:=$pacgum_timer>=0?10-$|+$pacgum_timer:-1 [visu] repeat 4 { mg=${mg$>} xg=${xg$>} yg:=${yg$>}+24 if !$mg j. [ghost$>_$t],$xg,$yg,0,0,1,[ghostm_$t] elif $mg==1 t2:=$left>7?$t:$left>3?int(12*$|)%4:int(24*$|)%4 j. [ghosta_$t2],$xg,$yg,0,0,1,[ghostm_$t] elif $mg==2 j. [ghostd_$t],$xg,$yg,0,0,0.8,[ghostdm] else j. [ghost$>_$t],$xg,$yg,0,0,{$mg-2},[ghostm_$t] j. [ghostd_$t],$xg,$yg,0,0,1,[ghostdm] fi } if $dying_pacman _pacman_pacman_gfx {$dying_pacman/2} rotate[-2,-1] {90*(abs($dp)-1)} j... ..,$xp,{24+$yp},0,0,1,.,255 rm[-2,-1] dying_pacman+=1 if $dying_pacman>64 if $lives!=1 rm. break fi j. [game_over],{(w-{game_over,w})/2},{12+(h-{game_over,h})/2},0,0,{min(1,($dying_pacman-64)/50)},\ [game_overm],255 rectangle. 90,7,101,18,1,0 fi else +rotate[pacman_$t,pacmanm_$t] {90*(abs($dp)-1)} j... ..,$xp,{24+$yp},0,0,1,.,255 rm[-2,-1] if $left>=0" && "($left>=5" || "$t<=2) j. [time{round($left)}],{(w-{time0,w})/2-10},1 fi fi t. $score,{w-60},3,20,1,255 if $is_get_ready j. [level_N],{(w-{level_N,w})/2},{12+(h-1.5*{level_N,h})/2},0,0,1,[levelm_N] if int($|*4)%2 j. [get_ready],{(w-{get_ready,w})/2},{24+(h+{get_ready,h})/2},0,0,1,[get_readym],255 fi fi if $oscore>0 j. [score$nscore],$xscore,$yscore,0,0,$oscore,[scorem$nscore],255 oscore-=0.04 yscore-=1 fi j. [gate],158,223,0,0,0.6 w. if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h} elif {*,CTRLLEFT}" && "{*,C} w[] {f=h<0.5*{*,v}?1.5:1;[w,h]*=f} fi rm. # Manage ghosts displacements and collisions. repeat 4 { xg=${xg$>} yg=${yg$>} dg=${dg$>} mg=${mg$>} if max(abs($xg-$xp),abs($yg-$yp))<=8 # Test collision between ghost and pacman. if !$mg" && "!$dying_pacman dying_pacman=1 # Was in normal mode -> dying pacman. xscore=$xp yscore:=$yp+12 oscore=1 nscore=4 elif $mg==1 mg=2 mg$>=$mg score+=100 # Was in invicibility mode -> dying ghost. xscore=$xp yscore:=$yp+12 oscore=1 nscore=1 fi fi if $mg>=2" && "($xg>>4)==$mw2" && "($yg>>4)==$mh2 # Check if dying ghost has returned to home. mg+=0.01 if $mg>=3 mg=0 xg&=-2 yg&=-2 fi mg$>=$mg fi if !($xg&15)" && "!($yg&15) # Check if ghost can take a new direction. ({u},{u},{u},{u};0,1,2,3) if $mg<2 # Try to chase or escape pacman =. {u(0.6,1)},{!$mg?(dX0=$xp-$xg;dY0=$yp-$yg;abs(dX0)>abs(dY0)?(dX0>0?0:2):(dY0>0?1:3)):\ (dX1=$xp-$xg;dY1=$yp-$yg;abs(dX1)0?2:0):(dY1>0?3:1))} =. 0,{($dg+2)%4} if $is_get_ready =. 0.8,{path,i({$xg>>4},{$yg>>4})} fi else =. 1,{path,i({$xg>>4},{$yg>>4})} # If dying ghost, follow the best path to home. fi sort. -,x repeat 4 { d:=i($>,1) # Try directions until it matches. if {can_go,i({$xg>>4},{$yg>>4},$d)} dg=$d break fi } rm. dg$>=$d fi u:=D=${dg$>};(D==0)-(D==2) v:=D=${dg$>};(D==1)-(D==3) xg$>:=($xg+$u*(1+!$mg))%(16*$mw) yg$>:=($yg+$v*(1+!$mg))%(16*$mh) } wait 22 # Manage pacman displacement. if !$dying_pacman d:={*,ARROWRIGHT}?1:{*,ARROWDOWN}?2:{*,ARROWLEFT}?3:{*,ARROWUP}?4:$dp if !($xp&15)" && "!($yp&15) i={map,i({$xp>>4},{$yp>>4})} if $i==2 score+=10 pacdots-=1 # Pacdot eaten. elif $i==3 pacgum_timer=$| repeat 4 { if !${mg$>} mg$>=1 dg$>:=(${dg$>}+2)%4 fi } # Pacgum eaten. elif $i>=4 score+=${score{$i-4}} xscore=$xp yscore:=$yp+12 oscore=1 nscore:=$i-4 # Fruit eaten. fi =[map] 0,{$xp>>4},{$yp>>4} 16,16,1,3 j[visu] .,$xp,{24+$yp} rm. d={can_go,i($xp>>4,$yp>>4,abs($d)-1)?$d:$dp} d={can_go,i($xp>>4,$yp>>4,abs($d)-1)?$d:-abs($dp)} dp=$d else dp:=abs($d-$dp)==2?$d:$dp # Allow to turn back on non-integer locations. fi is_get_ready:=$dp>0?0:$is_get_ready u:=($dp==1)-($dp==3) v:=($dp==2)-($dp==4) xp:=($xp+2*$u)%(16*$mw) yp:=($yp+2*$v)%(16*$mh) if $pacgum_timer>=0" && "$|>$pacgum_timer+10 # Check if pacgum still has some effect. repeat 4 { xg$>&=-2 yg$>&=-2 mg$>:=${mg$>}==1?0:${mg$>} } pacgum_timer=-1 fi if !$is_get_ready" && "($|-$fruit_timer)>=10 x:=v({map0,w}) y:=v({map0,h}) if !{map,i($x,$y)}" && "{map0,i($x,$y)}==2 n:=min(3,int(abs(g*1.7))) =[map] {4+$n},$x,$y j[visu] [fruit$n],{16*$x},{16*$y+24} fruit_timer=$| fi fi fi if !{*}" || "{*,Q}" || "{*,ESC} is_quit=1 fi while !$is_quit" && "$pacdots if $is_quit break # Player asked to quit elif $pacdots # Player lost a life lives-=1 else # Player achieved level level:=-$level-1 wait[0] -1 rm[map0,map,can_go,path] fi rm[visu,level_N,levelm_N] while $lives rm w 0 } # The functions below create the various sprite gfx. _pacman_ghost_standard_gfx : _pacman_ghost_base_gfx $4 (0,$1^0,$2^0,$3) map.. . rm. ellipse. 10,11,3,4,0,1,255 ellipse. 20,11,3,4,0,1,255 r. 16,16,1,3,2 point. 5,{7-($4>=2)},0,1,1 point. 10,{7-($4>=2)},0,1,1 _pacman_ghost_afraid_gfx : _pacman_ghost_base_gfx $1 if $1<2 col=255,255,255 (0,0^0,0^0,208) else col=255,0,0 (0,248^0,248^0,248) fi map.. . rm. r. 16,16,1,3,2 line. 4,4,6,6,1,$col,255 line. 4,6,6,4,1,$col,255 line. 9,4,11,6,1,$col,255 line. 9,6,11,4,1,$col,255 f. "y>=9 && y<=10 && x>=2 && x<=13 && ((int((x+1)/2)+y)%2)?arg(c+1,$col):i" _pacman_pacman_gfx : 32,32,1,1,'X=x-15;Y=y-15;A=atan2(Y,X);R=sqrt(X^2+Y^2);R<15.5&&abs(A)>0.8*0.33*$1' (0,255^0,255^0,0^0,255) map.. . rm. r. 16,16,1,4,2 s. c,-3 _pacman_pacdots_gfx : (255^184^151) r. 4,4,1,3 r. 16,16,1,3,0,0,0.5,0.5 _pacman_pacgum_gfx : 64,64,1,3 circle. 31,31,31,1,255,128,64 rs. 16 _pacman_cherry_gfx : base642img[] "MSBpbnQ4IGxpdHRsZV9lbmRpYW4KNzYgMSAxIDEgIzU5CnicFYpBCgAxDALVXvv/p5ZtmsgmIMoM7k0Cx/ySYYIXrE5qOgTmE1KGlo"\ "UW1pp1qVUqmkt3Hj9Whx3S" decompress_rle. (0,0,255,255^0,173,0,255^0,0,0,255) map.. . rm. rs. ,14 r. 16,16,1,3,0,0,0.5,0.5 _pacman_strawberry_gfx : base642img[] "MSBpbnQ4IGxpdHRsZV9lbmRpYW4KNzIgMSAxIDEgIzYwCnicJYlJDoAwEMOyHOH/H+UANJ0RBaRIlp1tJ4HAd9HFaoUtTNWC1yIPWT"\ "5ew5em+lT994guKHgAoIoa8w==" decompress_rle. (0,0,255,255^0,173,0,255^0,0,0,255) map.. . rm. rs. ,14 r. 16,16,1,3,0,0,0.5,0.5 _pacman_orange_gfx : base642img[] "MSBpbnQ4IGxpdHRsZV9lbmRpYW4KNDQgMSAxIDEgIzQ2CnicBcHBDQAgDAOxXPjC/gsxU4VKi7DnAukKPU6SYjTVptxhbSv8wpVufU"\ "wDEZ4=" decompress_rle. (0,0,255,255^0,173,173,255^0,0,0,255) map.. . rm. rs. ,14 r. 16,16,1,3,0,0,0.5,0.5 _pacman_banana_gfx : base642img[] "MSBpbnQ4IGxpdHRsZV9lbmRpYW4KNzAgMSAxIDEgIzQ4CnicNcqxDQAgDMTANx0S++8aIC9ShObceC6QQoSJ5Ai5vWW2WTI6xr+bvL"\ "U/BnUW3g==" decompress_rle. (0,255,255^0,173,255^0,0,255) map.. . rm. rs. ,14 r. 16,16,1,3,0,0,0.5,0.5 _pacman_map_level1 : base642img[] "MSBpbnQ4IGxpdHRsZV9lbmRpYW4KMjAxIDEgMSAxICM5Nwp4nGWOUQ6AMAhDKX56Be9/QnWJArEM9MdsGa9QYOsGiOy4FaZYHAo1aM"\ "ezIJ+QJnszLsq2aRaf9Q9GiaBnGmEZhSe8wLOdVqO+My+cFdJj9OpVQ0vzRm8l/L954AHE9jns" decompress_rle. +mirror. x z. 1,100% a[-2,-1] x _rlevel=33 _glevel=33 _blevel=255 _pacman_map_level2 : base642img[] "MSBpbnQ4IGxpdHRsZV9lbmRpYW4KMjA4IDEgMSAxICMxMDAKeJxljkEOAzEIAxn32C/0/y+MdlUloAKJemgvjAOG+PkCs8ElHsjRQk"\ "0iycwS+/2jcuJlnhhhf3SxPKWw9DaJmYsbmLOqmyaXn92UsVV9Y+sweOvb7YB1vQPPDnV4a/ABHS45BA==" decompress_rle. +mirror. x z. 1,100% a[-2,-1] x _rlevel=200 _glevel=33 _blevel=33 _pacman_map_level3 : base642img[] "MSBpbnQ4IGxpdHRsZV9lbmRpYW4KMjA5IDEgMSAxICMxMDEKeJxVTlsOwDAIAve5K+z+N+zStGomeyTdjwIKYT9IoHGAbpOgdQ3n1i"\ "1ZinHdnO+20FuKg3nf4WIO3YolP8QEQ75CEkoKaUIDT3K95w8OZbp4lDcMj1PNUjXyDg+u4LTGC5LiOwA=" decompress_rle. +mirror. x z. 1,100% a[-2,-1] x _rlevel=33 _glevel=200 _blevel=255 _pacman_map_level4 : base642img[] "MSBpbnQ4IGxpdHRsZV9lbmRpYW4KMjEwIDEgMSAxICM5MAp4nHVPQQ6AMAhr8egX/P8TN+cGGSy6TBMTQgqlUPaDBDINLBJZbMQHqm"\ "wq7e40eahgy8i/jDKB1QhlRWBzcPH09Rnr9ALTrHSOuN5N+IFF9LIY9uOPDitcQvE=" decompress_rle. +mirror. x z. 1,100% a[-2,-1] x _rlevel=200 _glevel=200 _blevel=33 _pacman_map_level5 : base642img[] "MSBpbnQ4IGxpdHRsZV9lbmRpYW4KMjA3IDEgMSAxICMxMDAKeJxdjlEKxCAMRPOmn3uF3v+ELXRrDE2qwrIgzPB8Mn52MDv4ihBbQy"\ "jQyuRBz96RF8NmSYAb98s65j95Wd0bFqxG1MNV/s1ealMujRHdI5wtf9X0WnWmFWO/7JmJXScPVxI1Cg==" decompress_rle. +mirror. x z. 1,100% a[-2,-1] x _rlevel=200 _glevel=255 _blevel=33 _pacman_map_level6 : base642img[] "MSBpbnQ4IGxpdHRsZV9lbmRpYW4KMTgzIDEgMSAxICM5Mgp4nGVO0QqAQAhz67Ff6P+/sK66TNqJUBCozDHn5gUwW9EIBnhi2nmBPQ"\ "mHCeMwDbuzBNCH1oZeDBEiPSWiAoXU8Yfhn4PyiNc1X3i99NwUJPMoVyVs3PAASqEr4g==" decompress_rle. +mirror. x z. 1,100% a[-2,-1] x _rlevel=255 _glevel=130 _blevel=233 #@cli x_paint #@cli : Launch the interactive painter. x_paint : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Interactive painter"$n" -----------------------\n ----\n ---- Use "${c}"mouse"$n" to select color and brush.\n ---- "${c}"Left button"$n" draws a colored stroke.\n ---- "${c}"Right button"$n" fills a colored region.\n ---- "${c}"Arrow keys"$n" or '"${c}"SPACE"$n"' and '"${c}"BACKSPACE"$n"' to swap\n ---- between available images.\n ---- Key '"${c}"S"$n"' to save snapshot of the current view.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------------" to_rgb if !$! i[0] 512,512,1,3,255 =>[0] "[New image]" else k[0] fi 1 # Brush image [-1] parallel "_x_paint[]","w[] 400,320,0,Palette x_select_color[] __color,0,0,0" k[0] _x_paint : # Init variables. pass[-2,-1] 1 ('{-2,n}') discard. {'_c1'} =>[0] {t} rm. __color={0,vector3(ia<128?255:0)} brushsize=1 brushopacity=0 brushangle=90 brushthickness=1 image=0 refresh_image=1 refresh_brush=1 ox1=-1 oy1=-1 # Start user event loop. do # Open/refresh brush window. if $refresh_brush rm. (32,64;64,32) r. 24,24,1,3,1 r. {8*48},{4*48},1,3,0,2 repeat 4 { y=$> repeat 8 { ellipse. {48*$>+24},{48*$y+24},{2*$>+1},{(2*$>+1)*$brushthickness},$brushangle,{1-$y/4},255 } } rectangle. {$brushsize*48},{$brushopacity*48},\ {$brushsize*48+47},{$brushopacity*48+47},\ 1,0xFFFFFFFF,255,128,128 {w},16,1,3 line. 0,50%,100%,50%,1,0x55555555,128,64,128 bx:=$brushangle*w/180 rectangle. {$bx-16},20%,{$bx+16},80%,1,128 line. {$bx-16},20%,{$bx+16},20%,1,255 line. {$bx+16},20%,{$bx+16},80%,1,255 line. {$bx-16},80%,{$bx+16},80%,1,64 line. {$bx-16},20%,{$bx-16},80%,1,64 a[-2,-1] y 16,{h-16},1,3 line. 50%,0,50%,100%,1,0x55555555,128,64,128 by:=$brushthickness*(h-16) rectangle. 20%,{$by-16},80%,{$by+16},1,128 line. 20%,{$by-16},80%,{$by-16},1,255 line. 80%,{$by-16},80%,{$by+16},1,255 line. 20%,{$by-16},20%,{$by+16},1,64 line. 20%,{$by+16},80%,{$by+16},1,64 a[-2,-1] x w3. {w},{h},0,"Brush" refresh_brush=0 fi # Open/refresh image window. if $refresh_image w1[$image] {$image,w},{$image,h},0,"Image "\#$image" : "{$image,b}.{$image,x} refresh_image=0 fi # Manage user events. x1={*1,x} y1={*1,y} x2={*2,x} y2={*2,y} x3={*3,x} y3={*3,y} if $x1>=0 # Event in the image window. if {*1,b}&1 # Left button -> draw brush stroke. ox1:=$ox1<0?$x1:$ox1 oy1:=$oy1<0?$y1:$oy1 delta:=max(abs($x1-$ox1),abs($y1-$oy1)) r1:=2*$brushsize+1 r2:=$r1*$brushthickness dx:=2*($x1-$ox1)/max(1,$delta) dy:=2*($y1-$oy1)/max(1,$delta) o:=1-($brushopacity/4)^0.04 repeat max(1,($delta+1)/2) { ellipse[$image] {$ox1+$>*$dx},{$oy1+$>*$dy},$r1,$r2,$brushangle,$o,$__color } ox1=$x1 oy1=$y1 refresh_image=1 else ox1=-1 oy1=-1 if {*1,b}&2 # Right button -> fill region. flood[$image] $x1,$y1,0,10,0,1,$__color refresh_image=1 fi fi fi if {*1,ARROWRIGHT}" || "{*2,ARROWRIGHT}" || "{*3,ARROWRIGHT}" || "\ # Manage image selection. {*1,ARROWUP}" || "{*2,ARROWUP}" || "{*3,ARROWUP}" || "\ {*1,SPACE}" || "{*2,SPACE}" || "{*3,SPACE} image:=($image+1)%($!-2) refresh_image=1 elif {*1,ARROWLEFT}" || "{*2,ARROWLEFT}" || "{*3,ARROWLEFT}" || "\ {*1,ARROWDOWN}" || "{*2,ARROWDOWN}" || "{*3,ARROWDOWN}" || "\ {*1,BACKSPACE}" || "{*2,BACKSPACE}" || "{*3,BACKSPACE} image:=($image-1)%($!-2) refresh_image=1 fi if {*1,S} o[$image] gmic_paint.png fi # Save snapshot if requested. if {*3,b}" && "$x3>=0 # Manage brush selection. if $x3<384" && "$y3>=192 brushangle:=$x3*180/(w-16) # Bottom slider -> select brush angle. elif $x3>=384" && "$y3<192 brushthickness:=$y3/(h-16) # Right slider -> select brush thickness. elif $x3<384" && "$y3<192 brushsize:=int($x3*8/(w-16)) brushopacity:=int($y3*4/(h-16)) fi refresh_brush=1 fi wait while {*1}" && "!{*1,Q}" && "!{*1,ESC} # Exit properly. __color=-1 w1[] 0 w2[] 0 w3[] 0 rm[-2,-1] #@cli x_plasma #@cli : Launch the plasma effect demo. x_plasma : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Plasma effect"$n" ----------------------\n ----\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -------------------------------------------" l[] { # Init plasma backgrounds. N=8 repeat $N { 320,200,1,3 rand. 0,255 plasma. 1,0,7 n. 0,255 amp:=u(-40,40) freq:=v(2,6) dir$>:=(u<0.5?-1:1)*v(1,2) 100%,100%,1,1,'$amp*cos(y*2*pi*$freq/h)' } {w+2},100%,1,1,'x' 100%,100%,1,1,'Y=(y-80+15*cos(x/30)+10*sin(x/22));Y<0||Y>=50?-1:Y' a[-2,-1] c 0 t. "** Welcome to G\47MIC, a powerful image processing framework **",0,0,50,1,255 b. 0.5 n. 0,255 M:=w # Start animation loop. w[] {0,f=1.5*h<0.5*{*,v}?3:1.5;[w,h]*=f},0,"[G"{`39`}"MIC] Plasma Effect" t=0 tt:=-1.5*{0,w} do tic=$| # Render interpolated background between two successive plasmas. a:=int($t) a2:=2*$a a21:=$a2+1 b:=($a+1)%$N b2:=2*$b b21:=$b2+1 +warp[$a2] [$a21],1,0,2 +warp[$b2] [$b21],1,0,2 j.. .,0,0,0,0,{$t-$a} rm. shift[$a21] 0,${dir$a},0,0,2 # Animate plasma background. shift[$b21] 0,${dir$b},0,0,2 if int($t+0.005)>int($t) dir$a:=(u<0.5?-1:1)*v(1,3) fi t:=($t+max(0.005,($|-$tic)))%$N # Render text scrolling. +z.. $tt,{$tt+w-1+2} warp. [-4],0,0,0 r. 100%,100%,1,3 +*. -1 +. 255 j... .,0,0,0,0,1,..,255 rm. j.. .,-2,-2,0,0,1,.,255 rm. tt+=max(2,($|-$tic)*250) # Animate scrolling. if $tt>=$M tt:=-1.5*{0,w} fi # Display rendered frame. fps=${-fps} if $fps>0 to. $fps" fps",5,5,16,1,0.2 fi w. if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi rm. wait 20 while {*}" && "!{*,ESC}" && "!{*,Q} rm[{-2*$N-2}--1] w[] 0 } #@cli x_quantize_rgb : _nbcolors>=2 #@cli : Launch the RGB color quantization demo. x_quantize_rgb : check "isint(${1=16},2)" check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"RGB Quantization"$n" --------------------------------------\n ----\n ---- This demo shows how RGB colors can be quantified using\n ---- the "${c}"k-means algorithm"$n".\n ----\n ---- "${c}"Left mouse button"$n" on 3D view rotates the color cube.\n ---- "${c}"Right mouse button"$n" on 3D view toggles colors/clusters mode.\n ---- "${c}"Left mouse button"$n" on image toggles dithering mode,\n ---- "${c}"Left mouse button"$n" on colormap adds a random color.\n ---- "${c}"Right mouse button"$n" on colormap removes a color.\n ---- Key '"${c}"R"$n"' init colormap with random values.\n ---- Key '"${c}"U"$n"' init colormap with uniform sampling.\n ---- Key '"${c}"M"$n"' init colormap with median-cut algorithm.\n ---- Key '"${c}"SPACE"$n"' does a single iteration of k-means and pauses.\n ---- Key '"${c}"ENTER"$n"' runs k-means algorithm.\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------------------------" if !$! sp ? fi k[0] to_rgb if h>300 rs ,300 round 1 fi => img # Resize input image if necessary. +r {w*h},1,1,3,-1 r. {min(w,8192)},1,1,3 => colors # Get reduced set of image colors. $1,1,1,3 rand. 0,255 round. 1 => centroids # Initialize random centroids. _x_quantize_rgb_3d (1,0,0,0;0,1,0,0;0,0,1,0) => pose3d # Init 3D object. _x_quantize_rgb_text "Colors",clustering0 _x_quantize_rgb_text "Clusters",clustering1 _x_quantize_rgb_text "Dithering: off",dithering0 _x_quantize_rgb_text "Dithering: on",dithering1 if {img,h<300} +rs[img] ,300,1 else [img] fi # Generate visualization canvas. {w+315},365,1,3,255 rm.. rectangle. 4,4,305,305,1,0xFFFFFFFF,0 rectangle. 309,4,{w-5},305,1,0xFFFFFFFF,0 rectangle. 4,309,{w-5},360,1,0xFFFFFFFF,0 .,. rectangle. 310,5,{w-6},305,1,1 rectangle. 5,310,{w-6},360,1,2 300,300,1,1,'(y<<11)+(x<<2)+3' j.. .,5,5 rm. a[-2,-1] c => visu # Start k-means iterations. dithering=0 clustering=0 pause=1 s0=off s1=on do # Create and display visualization. if !narg($visu_3d) # Update 3D visualization. +-[centroids] 3 ++[centroids] 3 a[-2,-1] x permute. cxyz y. -. 128 j[obj3d] .,0,8 rm. # Update centroids position in 3D object. [obj3d] if $clustering if {colors,iM}<256 # Estimate nearest centroids for all colors. +index[colors] [centroids] *. 256 +[colors,-1] fi +channels[colors] 0 >>. 8 map. 2 permute. cxyz y. j.. .,0,{{-2,h}-$_N-h} rm. fi pose3d. {pose3d,^} 600,600,1,3 j3d. ..,50%,50%,-150,1,2,0,0,600 *. 1.5 b. 0.75 c. 0,255 rm.. rs. 50% j. [clustering$clustering],2,0,0,0,1,[mclustering$clustering],255 => visu_3d j[visu] [visu_3d],5,5 fi if !narg($visu_img) # Update indexed image. +index[img] [centroids],{0.7*$dithering},1 if h<300 rs. ,300,1 fi j. [dithering$dithering],2,0,0,0,1,[mdithering$dithering],255 => visu_img j[visu] [visu_img],310,5 fi if !narg($visu_centroids) # Update colormap. +luminance[centroids] a. [centroids],y sort. +,x rows. 1 r. {visu,w-10},50,1,3 0 t. "Colors: "{centroids,w},2,0,16,1,255,255,255 +dilate. 3 j... ..,2,2,0,0,1,.,255 rm[-2,-1] => visu_centroids j[visu] [visu_centroids],5,310 fi l[visu] { w -1,-1,0,"[G"{`39`}"MIC] RGB Quantization" if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi } # Check for user's interactions. x:=int({*,x}*{visu,w}/{*,w}) y:=int({*,y}*{visu,h}/{*,h}) b={*,b} i={visu,i($x,$y,0,3)} if $b&1" && "$i==1 # Toggle dithering. dithering:=!$dithering rm[visu_img] wait -1 elif $b&1" && "$i==2 # Add new color. (${-rgb}) y. c a[centroids,-1] x _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255 pause=1 wait 100 elif $b&2" && "$i==2" && "{centroids,w}>2 # Remove color. r[centroids] {centroids,w-1} _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255 pause=1 wait 100 elif $b&2" && "$i>=3 # Toggle clusters/colors mode. clustering:=!$clustering rm[visu_3d] wait -1 elif {*,M} # Init colormap with median-cut. +&[colors] 255 colormap. {centroids,w},0,0 rm[centroids] => centroids _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255 pause=1 wait -1 elif {*,R} # Init colormap with random values. rand[centroids] 0,255 round[centroids] 1 _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255 pause=1 wait -1 elif {*,U} # Init colormap with uniform sampling. uniform_distribution {centroids,w},3 *. 255 rm[centroids] => centroids _x_quantize_rgb_3d rm[visu_3d,visu_img,visu_centroids] &[colors] 255 pause=1 wait -1 elif {*,ENTER} # Start k-means iterations. pause=0 elif $b&1" && "$i>=3 # Manage 3D view rotation. coords={visu,i($x,$y,0,3)-3} u1:=(($coords>>2)&511)-150 v1:=($coords>>11)-150 if !narg($u0) u0=$u1 v0=$v1 fi if $u0!=$u1" || "$v0!=$v1 n0:=sqrt(($u0)^2+($v0)^2) nu0:=$n0>135?$u0*135/$n0:$u0 nv0:=$n0>135?$v0*135/$n0:$v0 nw0:=sqrt(max(0,18225-($nu0)^2-($nv0)^2)) n1:=sqrt(($u1)^2+($v1)^2) nu1:=$n1>135?$u1*135/$n1:$u1 nv1:=$n1>135?$v1*135/$n1:$v1 nw1:=sqrt(max(0,18225-($nu1)^2-($nv1)^2)) u:=$nv0*$nw1-$nw0*$nv1 v:=$nw0*$nu1-$nu0*$nw1 w:=$nv0*$nu1-$nu0*$nv1 n:=sqrt(($u)^2+($v)^2+($w)^2) rotation3d[] $u,$v,$w,{-asin($n/18225)*180/pi} mv[pose3d] $! m*[-2,-1] => pose3d u0=$u1 v0=$v1 rm[visu_3d] fi elif !($b&1) u0= fi if !$pause" || "{*,SPACE} # Do one iteration of k-means. pause={*,-SPACE} # Estimate new centroids positions. &[colors] 255 +index[colors] [centroids] *. 256 +[colors,-1] # Estimate nearest centroids for all colors. repeat s#$colors { # Recompute centroid positions. sh[colors] $> +histogram. {centroids,w*256},0,{centroids,w*256-1} rm.. i.. 256,1,1,1,'x' r.. {w},1,1,1,0,2 *.. . r[-2,-1] {centroids,w},1,1,1,2 max. 0.01 /[-2,-1] } a[-{colors,s}--1] c rm[centroids] => centroids # Reassign unused centroids. +>>[colors] 8 channels. 0 histogram. {centroids,w},0,{centroids,w-1} cmax:=xM repeat w { if !i($>) point[centroids] $>,0,0,1,{centroids,I($cmax)} point[centroids] $>,0,0,-0.001,${-rgb} fi } rm. c[centroids] 0,255 if $visu_3d rm[visu_3d] fi if $visu_img rm[visu_img] fi if $visu_centroids rm[visu_centroids] fi wait 20 else if $visu_img wait fi fi while {*}" && "!{*,Q}" && "!{*,ESC} rm w 0 _x_quantize_rgb_3d : if $obj3d rm[obj3d] fi +distribution3d[centroids] circles3d. 5 col3d. 255 # Pre-compute 3D object. colorcube3d 1 +&[colors] 255 distribution3d. circles3d. 5 o3d. 0.2 +3d[-3--1] -3d. 128,128,128 => obj3d _N:=i[7] _x_quantize_rgb_text : 0 t. "$1",0,0,16,1,255 r. {w+2},15,1,1,0,0,0.5,0.5 +dilate. 3 to_rgb.. => $2,m$2 #@cli x_reflection3d #@cli : Launch the 3D reflection demo. x_reflection3d : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"3D reflection"$n" ----------------------\n ----\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -------------------------------------------" # Render background. 200,400,1,3 rand. 0,255 plasma. 1,100 b. x,30 b. y,2 sh. 0 n. 0,90 rm. sh. 1 n. 0,60 rm. sh. 2 n. 0,180 rm. +mirror. x [-2,-1] a[-4--1] x +luminance. mirror. x b. 2 n. 0,255 # Create 3D objects. torus3d 45,16 col3d. 255,200,0 spherical3d "125+32*abs(cos(2*theta))",51,50 s3d. rm.. i.. 3,{h},1,1,150,220,255,200,255,255 y.. a[-6--1] y spherical3d "150*abs(1+0.6*cos(3*phi)*sin(4*theta))",51,50 r3d[-2,-1] 0,1,0,90 db3d 0 # Start animation loop. xb,xl,anim=0 w[] ${"fitscreen 400,400,1,35%"},0,"[G"{`39`}"MIC] 3D Reflection" do tic=$| # Recreate 3D interpolated background object. +rows. 8,{8+3*i[6]-1} +j... .,0,8,0,0,{$anim<250?0:0.5-0.5*cos(($anim-250)/100)} rm.. # Render 3D background object (with flat colors). +z[-6] $xb,0,{$xb+399},399 j3d. ..,75%,50%,0,1,3,0,0 # Render light reflection map. +z[-6] $xl,0,{$xl+399},399 xf:=min(30,$anim-70)+20*cos(1.8*$|) yf:=50+20*sin(2.7*$|) j3d. [-6],{20+$xf}%,$yf%,0,1,4,0,0 # Add light reflection to 3D background object. l3d . rm. +j3d. ..,75%,50%,0,1,5,0,0 j.. .,0,0,0,0,0.6 rm[-3,-1] # Add 3D foreground object. j3d. [-4],$xf%,$yf%,0,1,4,0,0 # Display frame and update animation variables. fps=${-fps} if $fps>0 to. $fps" fps",5,{h-19},13,1,0.2 fi w. rm. if {*,CTRLLEFT}" && "{*,D} w[] 800,800 elif {*,CTRLLEFT}" && "{*,C} w[] 400,400 fi xb:=($xb+6)%400 xl:=($xl-6)%400 anim+=1 r3d[-2,-1] {sin(0.5*$|)},{cos($|)},1,{max(0.005,$|-$tic)*33} r3d... -1,0.3,0.8,{max(0.005,$|-$tic)*100} wait 20 while {*}" && "!{*,ESC}" && "!{*,Q} rm[-5--1] w[] 0 #@cli x_rubber3d #@cli : Launch the 3D rubber object demo. x_rubber3d : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"3D rubber object"$n" -------------------\n ----\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -------------------------------------------" rm sphere3d 150,0 torus3d 70,15 cylinder3d 20,40 col3d... 200,200,200,0.3 col3d.. 128,200,76 col3d. 200,128,76 c3d[-3--1] r3d. 1,0,0,70 +3d[-3--1] +3d. 10,-8,20 *3d. 1.5 400,400,64,3 {w},{h},1,3,'!c?x:c==1?y:y*{1,d}/h' {w},{h},1,3 w[] ${"fitscreen .,35%"},0,"[G"{`39`}"MIC] 3D Rubber Object" frame=0 do fps=${-fps} {w},{h},1,3 fc. 16,32,32 j3d. [0],50%,50%,0,1,3,0,0 j[1] .,0,0,$frame rm. r3d[0] 0.1,1,0.6,{3*cos($|*1.25)} r3d[0] 1,0.2,0.6,-1 +warp[1] [2],0,0,1 *[3] 0.8 *. 0.2 +[3] . rm. if $fps>0 to. $fps" fps",5,{h-29},24,2,0.2 fi w. if {*,CTRLLEFT}" && "{*,D} w[] {2*w},{2*h} elif {*,CTRLLEFT}" && "{*,C} w[] {w},{h} fi wait[0] 20 sh[2] 2 -. 1 &. {{1,d}-1} rm. frame:=($frame-1)%{1,d} while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0 #@cli x_segment : _max_resolution={ 0 | >=128 } #@cli : Segment foreground from background in selected opaque RGB images, interactively. #@cli : Return RGBA images with binary alpha-channels. #@cli : Default value: 'max_resolution=1024'. x_segment : check "!${1=1024} || $1>=128" check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e[^-1] "Extract foreground from background in image$? interactively, with maximum resolution $1." e "\n ----------------------------------------------------------------------------------------------------\n ----\n ---- "${c}"Left mouse button"$n" or key '"${c}"F"$n"' create a new foreground control point (or move an existing one).\n ---- "${c}"Right mouse button"$n" or key '"${c}"B"$n"' create a new background control point (or move an existing one).\n ---- "${c}"Mouse wheel"$n", or keys '"${c}"CTRL+arrows UP/DOWN"$n"' zoom view in/out.\n ---- '"${c}"CTRL+mouse wheel"$n"', '"${c}"SHIFT+mouse wheel"$n"' or "${c}"arrow keys"$n" move image in zoomed view.\n ---- Key '"${c}"SPACE"$n"' updates the extraction mask.\n ---- Key '"${c}"TAB"$n"' toggles background view modes.\n ---- Key '"${c}"M"$n"' toggles marker view modes.\n ---- Key '"${c}"BACKSPACE"$n"' deletes the last control point added.\n ---- Key '"${c}"PAGE UP"$n"' increases background opacity.\n ---- Key '"${c}"PAGE DOWN"$n"' decreases background opacity.\n ---- Keys '"${c}"CTRL+D"$n"' increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' decrease window size.\n ---- Keys '"${c}"CTRL+R"$n"' reset window size.\n ---- Keys '"${c}"ESC"$n"', '"${c}"Q"$n"' or '"${c}"ENTER"$n"' exit the interactive window.\n ----\n ----------------------------------------------------------------------------------------------------" foreach { # Init variables and images. name={0,n} title={0,b} if narg({0,x}) title=$title.{0,x} fi w,h={w},{h} fdim=${fitscreen[]\ $w,$h} ww:=arg(1,$fdim) wh:=arg(2,$fdim) x0=0 y0=0 x1:=w-1 y1:=h-1 selection=-1 marker_mode=2 xpan=-1 ypan=-1 bg_mode=0 opacity=64 to_rgb => img if narg($_gui_control_points)>=4 # Import list of control points from plug-in GUI. ($_gui_control_points) r. {w/4},4,1,1,-1 else 0 # Empty list of control points. fi => points # Compute potential map. if $1>0 if $w>$h +rs[img] {min($1,$w)} else +rs[img] ,{min($1,$h)},2 fi else [img] fi _x_segment. pw,ph={potential,[w,h]} # Start event loop. do # Handle user events for zoom/navigation/resizing. wait x,y,b,o={*,x,y,b,-o} is_ctrl:={*,CTRLLEFT}" || "{*,CTRLRIGHT} is_shift:={*,SHIFTLEFT}" || "{*,SHIFTRIGHT} is_mouseout:=$x<0" || "$y<0 x:=$x0+$x*($x1-$x0+1)/$ww y:=$y0+$y*($y1-$y0+1)/$wh oww=$ww owh=$wh ox0=$x0 oy0=$y0 ox1=$x1 oy1=$y1 if {*,r} # When window resized. nww,nwh={*,d,e} m:=min($nww,$nwh) cx:=($x0+$x1)/2 cy:=($y0+$y1)/2 dx:=$nww*($x1-$x0+1)/$ww dy:=$nwh*($y1-$y0+1)/$wh x0:=$cx-$dx/2 x1:=$cx+$dx/2 y0:=$cy-$dy/2 y1:=$cy+$dy/2 ww=$nww wh=$nwh elif $is_ctrl" && "{*,-D} # Increase window size. nww:=min({*,u},$ww*1.25) nwh:=min({*,v},$wh*1.25) m:=min($nww,$nwh) if $m==$nww ww=$m wh:=$h*$m/$w else ww:=$w*$m/$h wh=$m fi elif $is_ctrl" && "{*,-C} # Decrease window size. nww:=$ww/1.25 nwh:=$wh/1.25 if min($nww,$nwh)>=64 ww=$nww wh=$nwh fi elif $is_ctrl" && "{*,-R} # Reset window size. fdim=${fitscreen[]\ $w,$h} ww:=arg(1,$fdim) wh:=arg(2,$fdim) x0=0 y0=0 x1:=$w-1 y1:=$h-1 elif ($is_shift" && "$o<0)" || "{*,ARROWLEFT} # Go left. dx:=($x1-$x0)/6 x0-=$dx x1-=$dx elif ($is_shift" && "$o>0)" || "{*,ARROWRIGHT} # Go right. dx:=($x1-$x0)/6 x0+=$dx x1+=$dx elif ($is_ctrl" && "$o>0)" || "({*,ARROWUP}" && "!$is_ctrl) # Go up. dy:=($y1-$y0)/6 y0-=$dy y1-=$dy elif ($is_ctrl" && "$o<0)" || "({*,ARROWDOWN}" && "!$is_ctrl) # Go down. dy:=($y1-$y0)/6 y0+=$dy y1+=$dy elif $o>0" || "($is_ctrl" && "{*,ARROWUP}) # Zoom in. if $x1-$x0>16" && "$y1-$y0>16 cx:=$x>=0" && "!{*,ARROWUP}?$x:($x0+$x1)/2 cy:=$y>=0" && "!{*,ARROWUP}?$y:($y0+$y1)/2 x0:=$cx+($x0-$cx)*0.75} y0={$cy+($y0-$cy)*0.75 x1:=$cx+($x1-$cx)*0.75} y1={$cy+($y1-$cy)*0.75 fi elif $o<0" || "($is_ctrl" && "{*,ARROWDOWN}) # Zoom out. zfactor:=max(($x1-$x0+1)/$w,($y1-$y0+1)/$h) if $zfactor<1.3 cx:=$x>=0" && "!{*,ARROWDOWN}?$x:($x0+$x1)/2 cy:=$y>=0" && "!{*,ARROWDOWN}?$y:($y0+$y1)/2 x0:=$cx+($x0-$cx)/0.75} y0={$cy+($y0-$cy)/0.75 x1:=$cx+($x1-$cx)/0.75} y1={$cy+($y1-$cy)/0.75 dx:=$zfactor^2*($w-$x0-$x1)/2 dy:=$zfactor^2*($h-$y0-$y1)/2 x0+=$dx x1+=$dx y0+=$dy y1+=$dy else dx:=($w-$x0-$x1)/2 dy:=($h-$y0-$y1)/2 x0+=$dx x1+=$dx y0+=$dy y1+=$dy fi elif $b&4" && "!$is_mouseout # Pan. if $panx<0" && "$pany<0 panx=$x pany=$y else dx:=round($panx-$x) dy:=round($pany-$y) x0+=$dx y0+=$dy x1+=$dx y1+=$dy fi else panx=-1 pany=-1 fi if $ww!=$oww" || "$wh!=$owh" || "$ox0!=$x0" || "$oy0!=$y0" || "$ox1!=$x1" || "$oy1!=$y1 rm[baseview] fi # Handle events related to control points management. N={points,w} is_left_button:=$b&1" || "{*,F} is_right_button:=$b&2" || "{*,B} is_button:=$is_left_button" || "$is_right_button if narg($baseview)" && "$is_button" && "$x>=0" && "$y>=0" && "$x<$w" && "$y<$h if $selection==-1" && "$N # Check for selection of an existing point. ($x;$y) r. $N,2 -. [points] *. {max($ww,$wh)/max($x1-$x0,$y1-$y0)} sqr. s. y +[-2,-1] dmin:=im selection:=$dmin>25?-1:xm rm. fi if $selection>=0 if $marker_mode # Move existing point. +columns[points] $selection ox,oy:=i[0],i[1] =. $x =. $y,0,1 =. {1+$is_left_button},0,3 j[points] .,$selection rm. rm[view] fi else # Add new foreground or background point. ($x;$y;0;{1+$is_left_button}) a[points,-1] x selection=$N if !$marker_mode marker_mode=2 fi rm[view] fi else selection=-1 if {*,SPACE}" && "narg($labels) rm[labels] # Update labels. elif {*,TAB}" && "narg($baseview) # Toggle background view modes bg_mode:=($bg_mode+1)%6 rm[baseview] wait -1 elif {*,M}" && "narg($view) # Toggle markers view modes marker_mode:=($marker_mode-1)%3 rm[view] wait -1 elif {*,PAGEDOWN}" && "narg($baseview) # Decrease background opacity opacity:=max(0,$opacity-32) rm[baseview] wait -1 elif {*,PAGEUP}" && "narg($baseview) # Increase background opacity opacity:=min(255,$opacity+32) rm[baseview] wait -1 elif {*,BACKSPACE}" && "$N # Remove last point. if $N>1 z[points] 0,{$N-2} else i=$points rm[points] i[$i] 0 =>[$i] points fi rm[view] wait -1 fi fi # Manage zoomed view bounds. w2:=round(($x1-$x0)/2) h2:=round(($y1-$y0)/2) if $x0<-$w2 x1-=$x0+$w2 x0=-$w2 fi if $y0<-$h2 y1-=$y0+$h2 y0=-$h2 fi if $x1>=$w+$w2 x0+=$w-1+$w2-$x1 x1:=$w-1+$w2 fi if $y1>=$h+$h2 y0+=$h-1+$h2-$y1 y1:=$h-1+$h2 fi # Render labels. if !narg($labels) N={points,w} if narg($view) to[view] "Processing...",5,5,20,2 w[view] fi if $N [points] sh. 0,0,0,0 *. {$pw/$w} rm. sh. 1,1,0,0 *. {$ph/$h} rm. pointcloud. -1,$pw,$ph dilate. 3 watershed. [potential] -. 1 else [potential],[potential],1,1,1 fi => labels if narg($baseview) rm[baseview] fi fi # Render base image. if !narg($baseview) nx0:=$x0*$pw/$w ny0:=$y0*$ph/$h nx1:=$x1*$pw/$w ny1:=$y1*$ph/$h +z[img] $x0,$y0,$x1,$y1 r. $ww,$wh,1,100%,{$ww=3 *. -1 +. 1 fi *. {255-$opacity} +. $opacity a[-2,-1] c if $bg_mode%3>=1 i.. 100%,100%,1,3,{(($bg_mode-1)%3)*255} blend[-2,-1] alpha else drgba. fi => baseview if narg($view) rm[view] fi fi # Render view. if !narg($view) [baseview] r. 100%,100%,1,3 if $marker_mode if $marker_mode==2 rad1=5 rad2=3 opa=1 else rad1=3 rad2=2 opa=0.8 fi col0=255,0,0 col1=0,255,0 repeat w#$points { +columns[points] $> x:=(i[0]-$x0)*$ww/(1+$x1-$x0) y:=(i[1]-$y0)*$wh/(1+$y1-$y0) l:=i[3]-1 rm. circle. $x,$y,$rad1,1,0 circle. $x,$y,$rad2,$opa,${col$l} } fi => view w[view] $ww,$wh,0,$title fi while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER} # Recompute labels at full resolution. if narg($view) to[view] "Processing fullres...",5,5,20,2 w[view] fi k[img,points] N={points,w} status= if $N status={points,^} [img] _x_segment. pointcloud[points] -1,$w,$h zfact:={img,max(w,h)}/{potential,max(w,h)} dilate[points] {int(3*$zfact)} watershed[points] [potential] -[points] 1 k[img,points] *. 255 else k[img] [img],[img],1,1,255 fi a c => $name } u $status # Return control points of last image. w 0 # Compute potential function. _x_segment : b. 0.2% gradient_norm. f. 1/(1+i^2) => potential #@cli x_select_color : _variable_name #@cli : Display a RGB or RGBA color selector. #@cli : Argument 'variable_name' specifies the variable that contains the selected color values (as R,G,B,[A]) #@cli : at any time. #@cli : Its value specifies the initial selected color. Assigning '-1' to it forces the interactive window to close. #@cli : Default value: 'variable_name=xsc_variable'. x_select_color : skip ${1=xsc_variable} check_display $0 rm n:=narg($$1) if !$n $1=0,0,0 fi rgba_mode:=$n>=4 R:=arg(1,$$1) G:=arg(2,$$1) B:=arg(3,$$1) A:=$rgba_mode?arg(4,$$1):255 e[^-1] "Open "${arg0\ $rgba_mode,RGB,RGBA}" color selector widget, with variable '$1' and starting color "\ ($$1)"." if !{*} w[] {400+24*$rgba_mode},400,0,"Select a color" fi update_view=1 is_sv=0 is_h=0 is_a=0 colordb=0 is_thread_variable:=arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_' # Manage color presets. m "add_preset : if !narg($_xsc_preset$""1) _xsc_preset$""1=$""2,$""3,$""4 fi" add_preset 0,0,0,0,0 add_preset 1,255,255,255 add_preset 2,255,0,0 add_preset 3,0,255,0 add_preset 4,0,0,255 add_preset 5,255,255,0 add_preset 6,255,0,255 add_preset 7,0,255,255 add_preset 8,50,50,50 add_preset 9,100,100,100 add_preset 10,150,150,150 add_preset 11,200,200,200 um add_preset if !narg($_xsc_preset) _xsc_preset=11 fi ($R^$G^$B) c. 0,255 rgb2hsv. H,S,V:=i[0],i[1],i[2] rm. # Start event loop. do w,h,x,y,b={*,d,e,x,y,b} # Update base image. if !$! $w,$h,1,3,200 if $rgba_mode x1:=w-89 y1:=h-57 x2:=w-80 else x1:=w-49 y1:=h-57 x2:=w-40 fi x0=8 y0=8 x3:=$x2+31 x4:=w-40 x5:=$x4+31 x6:=max($x0+232+32*$rgba_mode,w-152) y6:=$y1+7 rectangle {$x0-1},{$y0-1},{$x1+1},{$y1+1},1,0xFFFFFFFF,232 line {$x0-1},{$y0-1},{$x1+1},{$y0-1},1,128 line {$x0-1},{$y0-1},{$x0-1},{$y1+1},1,128 (1;0) (0,1) r[-2,-1] {$x1-$x0+1},{$y1-$y0+1},1,1,3 i... 100%,100%,1,1,$H a[-3--1] c hsv2rgb. j.. .,$x0,$y0 rm. rectangle {$x2-1},{$y0-1},{$x3+1},{$y1+1},1,0xFFFFFFFF,232 line {$x2-1},{$y0-1},{$x3+1},{$y0-1},1,128 line {$x2-1},{$y0-1},{$x2-1},{$y1+1},1,128 (359;0^1;1^1;1) r. {$x3-$x2+1},{$y1-$y0+1},1,3,3 hsv2rgb. j.. .,$x2,$y0 rm. if $rgba_mode rectangle {$x4-1},{$y0-1},{$x5+1},{$y1+1},1,0xFFFFFFFF,232 line {$x4-1},{$y0-1},{$x5+1},{$y0-1},1,128 line {$x4-1},{$y0-1},{$x4-1},{$y1+1},1,128 (1;0) r. {$x5-$x4+1},{$y1-$y0+1},1,4,3 *. 255 drgba. j.. .,$x4,$y0 rm. fi t. "Current",$x0,{$y1+12},14,1,0 if narg($_xsc_old) t. "Old",$x0,{$y1+34},14,1,0 ($_xsc_old) y. c r. 48,16 drgba. r. {w+2},{h+2},1,3,0,0,0.5,0.5 j.. .,{$x0+55},{$y1+32} rm. fi repeat 12 { (${_xsc_preset$>}) -. 255 r. 4,1,1,1,0 +. 255 y. c r. 18,18 drgba. frame. 1,1,{255*($>==$_xsc_preset)} j.. .,{$x6+($>%6)*25},{$y6+($>>=6)*25} rm. } update_view=1 fi # Update view. if $update_view . cx:=$x0+$V*($x1-$x0) cy:=$y0+(1-$S)*($y1-$y0) if $cx>$x0 line. {$cx-1},$y0,{$cx-1},$y1,1,200 fi if $cx<$x1 line. {$cx+1},$y0,{$cx+1},$y1,1,200 fi if $cy>$y0 line. $x0,{$cy-1},$x1,{$cy-1},1,200 fi if $cy<$y1 line. $x0,{$cy+1},$x1,{$cy+1},1,200 fi line. $x0,$cy,$x1,$cy,1,0 line. $cx,$y0,$cx,$y1,1,0 cy:=$y0+(359-$H)*($y1-$y0)/359 if $cy>$y0 line. $x2,{$cy-1},$x3,{$cy-1},1,200 fi if $cy<$y1 line. $x2,{$cy+1},$x3,{$cy+1},1,200 fi line. $x2,$cy,$x3,$cy,1,0 if $rgba_mode cy:=$y0+(255-$A)*($y1-$y0)/255 if $cy>$y0 line. $x4,{$cy-1},$x5,{$cy-1},1,200 fi if $cy<$y1 line. $x4,{$cy+1},$x5,{$cy+1},1,200 fi line. $x4,$cy,$x5,$cy,1,0 fi ($H^$S^$V^$A) sh. 0,2 hsv2rgb. rm. round. R,G,B:=i[0],i[1],i[2] r. 48,16 drgba. r. {w+2},{h+2},1,3,0,0,0.5,0.5 j.. .,{$x0+55},{$y1+10} rm. t. "HSV ("{round($H)}","{round($S*255)}","{round($V*255)}")",{$x0+115},{$y1+24},14,1,0 if $rgba_mode t. "RGBA ("$R","$G","$B","{round($A)}")",{$x0+115},{$y1+8},14,1,0 else t. "RGB ("$R","$G","$B")",{$x0+115},{$y1+8},14,1,0 fi ('${dec2hex\ {$R*65536+$G*256+$B}}') -. {'0'} r. 6,1,1,1,0,0,1,0 +. {'0'} f. i>=_'a'" && "i<=_'z'?i+_'A'-_'a':i t.. "html ""#"{t},{$x0+115},{$y1+40},14,1,0 rm. w. 100%,100%,0 rm. if $rgba_mode $1=$R,$G,$B,$A else $1=$R,$G,$B fi update_view=0 fi if $is_thread_variable wait 50 else wait fi # Manage window size. ww,wh={*,w,h} is_ctrl:={*,CTRLLEFT}" || "{*,CTRLRIGHT} if {*,r} ww,wh={*,d,e} elif $is_ctrl" && "{*,-D} ww,wh:=1.25*[$ww,$wh] elif $is_ctrl" && "{*,-C} ww,wh:=0.8*[$ww,$wh] elif $is_ctrl" && "{*,R} ww,wh:=400+[24*$rgba_mode,0] fi ww,wh:=max(200,$ww),max(200,$wh) if $ww!={*,w}" || "$wh!={*,h} w[] $ww,$wh rm fi # Manage user events. if $b&1" && "$x>=0" && "$y>=0 if !$is_h" && "!$is_a" && "($is_sv" || "($x>=$x0" && "$x<=$x1" && "$y>=$y0" && "$y<=$y1)) # SV selection S:=max(0,min(1,1-($y-$y0)/($y1-$y0))) V:=max(0,min(1,($x-$x0)/($x1-$x0))) update_view=1 colordb=0 is_sv=1 k[0] elif !$is_sv" && "!$is_a" && "($is_h" || "($x>=$x2" && "$x<=$x3" && "$y>=$y0" && "$y<=$y1)) # H selection H:=max(0,min(359,359-($y-$y0)*359/($y1-$y0))) colordb=0 is_h=1 rm elif !$is_sv" && "!$is_h" && "($is_a" || "($x>=$x4" && "$x<=$x5" && "$y>=$y0" && "$y<=$y1)) # A selection A:=round(max(0,min(255,255-($y-$y0)*255/($y1-$y0)))) colordb=0 is_a=1 update_view=1 k[0] elif !$is_sv" && "!$is_h" && "!$is_a" && "{narg($_xsc_old)}" && "$x>=$x0+55" && "$x<=$x0+102" && "\ $y>=$y1+32" && "$y<=$y1+47 # Old color ($_xsc_old) y. c sh. 0,2 rgb2hsv. rm. H,S,V,A:=i[0],i[1],i[2],i[3] colordb=0 rm elif !$is_sv" && "!$is_h" && "!$is_a" && "$x>=$x6" && "$x<=$x5" && "$y>=$y6" && "$y<=$y6+50" && "\ ($x-$x6)%25<=20" && "($y-$y6)%25<=20 # Preset. p:=int(($x-$x6)/25)+6*int(($y-$y6)/25) (${_xsc_preset$p}) -. 255 r. 4,1,1,1,0 +. 255 y. c sh. 0,2 rgb2hsv. rm. H,S,V,A:=i[0],i[1],i[2],i[3] colordb=0 rm elif !$is_sv" && "!$is_h" && "!$is_a" && "$x>=$x0+55" && "$x<=$x0+102" && "$y>=$y1+10" && "$y<=$y1+27 # Add current as old and/or preset. _xsc_old=$R,$G,$B,$A colordb:=($colordb+1)%2 if !$colordb # Double-click to add to preset. _xsc_preset$_xsc_preset=$R,$G,$B,$A _xsc_preset:=($_xsc_preset-1)%12 fi rm wait -1 else colordb=0 fi elif !$b is_sv=0 is_h=0 is_a=0 fi if {*,ARROWUP} colordb=0 S:=min(1,$S+1/256) update_view=1 k[0] wait -1 elif {*,ARROWDOWN} colordb=0 S:=max(0,$S-1/256) update_view=1 k[0] wait -1 elif {*,ARROWRIGHT} colordb=0 V:=min(1,$V+1/256) update_view=1 k[0] wait -1 elif {*,ARROWLEFT} colordb=0 V:=max(0,$V-1/256) update_view=1 k[0] wait -1 elif {*,PAGEUP} colordb=0 H:=min(359,$H+1) rm wait -1 elif {*,PAGEDOWN} colordb=0 H:=max(0,$H-1) rm wait -1 fi # Check RGB variable modification from another thread. if ['$$1']=='-1' break fi # Close request if (($rgba_mode" && "['$$1']!='$R,$G,$B,$A')" || "(!$rgba_mode" && "['$$1']!='$R,$G,$B'))" && "\ $x<0" && "$y<0" && "!$is_sv" && "!$is_h" && "!$is_a ($$1) y. c -. 255 r. 1,1,1,4,0 +. 255 sh. 0,2 rgb2hsv. rm. H,S,V,A:=i[0],i[1],i[2],i[3] rm fi while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0 if $rgba_mode u $R,$G,$B,$A else u $R,$G,$B fi _xsc_old=${} #@cli x_select_function1d : _variable_name,_background_curve_R,_background_curve_G,_background_curve_B #@cli : Open an interactive window, where the user can defined its own 1D function. #@cli : If an image is selected, it is used to display additional information : #@cli : - The first row defines the values of a background curve displayed on the window (e.g. an histogram). #@cli : - The 2nd, 3rd and 4th rows define the R,G,B color components displayed beside the X and Y axes. #@cli : Argument 'variable_name' specifies the variable that contains the selected function keypoints at any time. #@cli : Assigning '-1' to it forces the interactive window to close. #@cli : Default values: 'variable_name=xsf_variable', 'background_curve_R=220', \ # 'background_curve_G=background_curve_B=background_curve_T'. x_select_function1d : skip ${1=xsf_variable},${2=220},${3=$2},${4=$2} check_display $0 e[^-1] "Open 1D function widget, with variable name '$1'." if $! k[0] fi is_additional_data=$! if !{*} w[] 400,400,0,"Create a 1D function" fi reset_w,reset_h={*,w,h} if !narg($$1) $1=0,0,100,100 fi ($$1) => points y. x r. 2,{w/2},1,1,-1 is_thread_variable:=arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_' selected=-1 X=-1 Y=-1 do # Update base view. if !narg($baseview) {{*,d}-48},{{*,e}-48},1,3,255 if $is_additional_data # Render background graph. 100%,100% +rows[0] 0 graph.. .,3,0,0,0,1,1 rm. c. 0,1 +fc.. ${2-4} j... .,0,0,0,0,1,.. rm[-2,-1] fi grid. {(w-1)/8},{(h-1)/8},0,0,0.2,0xCCCCCCCC,0 line. 0,100%,100%,0,0.2,0 frame. 24,24,200 rectangle. 23,23,{w-24},{h-24},1,0xFFFFFFFF,232 line. 23,23,23,{h-24},1,128 line. 23,23,{w-24},23,1,128 if $is_additional_data" && "{0,h}>1 # Render colored X-axis guide. if {0,h}>2 +rows[0] 1,3 else +rows[0] 1 r. 100%,3 fi r. {-2,w-48},3,1,1,3 permute. xzcy r. 100%,8 frame. 1,1,0 j.. .,23,{-2,h-19} rotate. -90 j.. .,{-2,w-19},23 rm. fi => baseview l { rm[view] onfail } fi # Update view. if !narg($view) +z[baseview] 24,24,{baseview,w-25},{baseview,h-25} r. 200%,200% # Draw curve. function1d[] 1,{points,^} l. { c 0,100 transpose i[0] ({'CImg3d'},{h},{h-1}) i.. 1,100%,1,1,y 1,100% a[-3--1] x 1,{h-1},1,1,2 +f. y ++. 1 a[-3--1] x 4,100%,1,1,1 y a y col3d 0 } *3d. {-2,(w-1)/100},{-2,(1-h)/100} j3d.. .,0,100%,0,1,1,0,0 rm. # Draw control points. repeat h#$points { x,y={points,[i(0,$>),100-i(1,$>)]} circle. $x%,$y%,6,1,0xFFFFFFFF,0 } if $selected>=0 x,y={points,[i(0,$selected),100-i(1,$selected)]} circle. $x%,$y%,3,1,0 fi r. 50%,50%,1,3,2 +j[baseview] .,24,24 rm.. # Draw current coordinates. if $X>=0" && "$Y>=0 t. "X: "{min(255,round(255*$X/100))}" Y: "{min(255,round(255*$Y/100))},24,6,12,1 fi => view w[view] fi if $is_thread_variable wait 50 else wait fi # Manage user events. x,y,b={*,x,y,b} is_ctrl:={*,CTRLLEFT}" || "{*,CTRLRIGHT} X:=($x-24)*100/({*,w}-49) Y:=100-($y-24)*100/({*,h}-49) oww,owh={*,w,h} ww=$oww wh=$owh if {*,r} ww,wh={*,d,e} # Resize window. elif $is_ctrl" && "{*,-D} ww={view,w*125%} wh={view,h*125%} # Increase window size. elif $is_ctrl" && "{*,-C} ww={view,w*75%} wh={view,h*75%} # Decrease window size. elif $is_ctrl" && "{*,R} ww=$reset_w wh=$reset_h # Reset window size. elif !$is_ctrl" && "{*,R} rm[points] (0,0;100,100) => points $1={points,^} rm[view] # Reset keypoints. elif $b&3 # Add/move/delete point. is_inside:=$X>=0" && "$Y>=0" && "$X<=100" && "$Y<=100 # Check for a point selection. if $selected<0 +f[points] "sqrt((i - $X)^2 + (j(1) - $Y)^2)*"{*,w}% z. 0,0 selected:=im>8?-1:ym rm. fi if $x>=0" && "$b&1" && "$selected>=0 # Move an existing point. if {*,SHIFTLEFT}" || "{*,SHIFTRIGHT} X={points,i(0,$selected)} fi if {*,CTRLLEFT}" || "{*,CTRLRIGHT} Y={points,i(1,$selected)} fi if {points,$selected>0" && "$selected8?-1:ym $1={points,^} rm[view,-1] elif $b&2" && "$selected>0" && "$selected<{points,h-1}" && "$is_inside # Delete an existing point. l[points] { s y rm[$selected] a y } wait -1 selected=-1 $1={points,^} rm[view] fi elif !($b&1) selected=-1 fi # Manage window size. ww:=min(90%*{*,u},max(200,$ww)) wh:=min(90%*{*,v},max(200,$wh)) if $oww!=$ww" || "$owh!=$wh w[] $ww,$wh rm[baseview,view] fi # Check points variable modification from another thread. if ['$$1']=='-1' break fi # Close request if ['$$1']!=['{points,^}'] # Keypoints changed rm[points] ($$1) => points y. x r. 2,{w/2},1,1,-1 l { rm[view] onfail } fi while {*}" && "!{*,ESC}" && "!{*,Q} w[] 0 u {points,^} if $is_additional_data rm[^0] else rm fi #@cli x_select_palette : _variable_name,_number_of_columns={ 0:auto | >0 } #@cli : Open a RGB or RGBA color selector widget from a palette. #@cli : The palette is given as a selected image. #@cli : Argument 'variable_name' specifies the variable that contains the selected color values (as R,G,B,[A]) #@cli : at any time. #@cli : Assigning '-1' to it forces the interactive window to close. #@cli : Default values: 'variable_name=xsp_variable' and 'number_of_columns=2'. x_select_palette : skip ${1=xsp_variable},${2=0} check_display $0 if !$! error[0--3] "Command '$0': Missing specified palette image." fi k[0] +r {w*h*d},1,1,{s},-1 to_color. rgba_mode:=s==4 to_rgba. => palette e[^-1] "Open "${arg0\ $rgba_mode,RGB,RGBA}" color selector widget for palette$?, with variable name '$1'." if w>1024 error[0--3] "Command '$0': Too much colors ("{w}") in selected palette." fi if !{*} w[] 400,400,0,0,-1,-1,"Palette: "{0,b} fi selected=-1 oselected=-1 do ww,wh={*,w,h} R,G,B,A={palette,round(I[$selected])} # Update color in specified variable. if $selected>=0" && "$oselected!=$selected if $rgba_mode $1=$R,$G,$B,$A else $1=$R,$G,$B fi fi # Check close request from external thread. if ['$$1']=='-1' break fi # Create base view. if !narg($baseview) l[palette] { {w},1,1,1,x +. 1 s. x append_tiles[^0] $2 M,N:=w,h 100%,100%,1,1,1 +r. {$ww-17},100%,1,1,4 r.. 100%,{$wh-57},1,1,4 r[-2,-1] .,.. -|[-2,-1] line. 100%,0,100%,100%,1,1 line. 0,100%,100%,100%,1,1 -. 1 *. -1 r.. .,.,1,1,1 -.. 1 +map.. [0],0 drgba. rv[-2,-1] *[-2,-1] +!=.. -1 dilate. 3 mv... $! +. 1 a[-3--1] c => baseview } if narg($view) rm[view] fi fi # Create and display view. if !narg($view) $ww,$wh,1,3,200 if $selected<0 sh[baseview] 0,2 else +channels[baseview] 0,2 +channels[baseview] 4,4 !=. {$selected+1} rectangle. 0,0,100%,100%,1,0xFFFFFFFF,1 +dilate. 5 -[-2,-1] *. -1 +dilate. 5 *.. 255 r.. 100%,100%,1,3 j... ..,0,0,0,0,1,. rm[-2,-1] if $rgba_mode t.. "RGBA ("$R","$G","$B","$A")",8,{$wh-45},14,1,0 else t.. "RGB ("$R","$G","$B")",8,{$wh-45},14,1,0 fi ($R^$G^$B) rgb2hsv. H,S,V:=round([i[0],i[1]*255,i[2]*255]) rm. t.. "HSV ("$H","$S","$V")",8,{$wh-31},14,1,0 ('${dec2hex\ {$R*65536+$G*256+$B}}') -. {'0'} r. 6,1,1,1,0,0,1,0 +. {'0'} f. i>=_'a'" && "i<=_'z'?i+_'A'-_'a':i t... "html ""#"{t},8,{$wh-17},14,1,0 rm. fi sh[baseview] 3 j... ..,8,8,0,0,1,. rm[-2,-1] => view w[view] fi if arg(1,{'$1'})==_'_'" && "arg(2,{'$1'})==_'_' wait 50 else wait fi # Manage window size. is_ctrl:={*,CTRLLEFT}" || "{*,CTRLRIGHT} if {*,r} ww,wh={*,d,e} elif $is_ctrl" && "{*,-D} ww,wh:=1.25*[$ww,$wh] elif $is_ctrl" && "{*,-C} ww,wh:=0.8*[$ww,$wh] elif $is_ctrl" && "{*,R} ww,wh=400 fi ww,wh:=max(200,$ww),max(200,$wh) if ($ww!={*,w}" || "$wh!={*,h})" && "narg($baseview) w[] $ww,$wh rm[baseview] fi # Handle user events. oselected=$selected if narg($baseview) x,y,b={*,x,y,b} if $b&1" && "$x>=0" && "$y>=0 # Select color. if {baseview,i($x-8,$y-8,0,4)} selected={baseview,i($x-8,$y-8,0,4)-1} else selected=-1 fi rm[view] wait -1 elif {*,ARROWUP}" && "$selected>=$M selected-=$M rm[view] wait -1 elif {*,ARROWDOWN}" && "$selected<{0,w-$M} selected+=$M rm[view] wait -1 elif {*,ARROWRIGHT}" && "$selected<{0,w-1} selected+=1 rm[view] wait -1 elif {*,ARROWLEFT}" && "$selected>0 selected-=1 rm[view] wait -1 fi fi while {*}" && "!{*,ESC}" && "!{*,Q} w 0 k[0] if $selected>=0 if $rgba_mode u $R,$G,$B,$A else u $R,$G,$B fi else u -1 fi #@cli x_shadebobs #@cli : Launch the shade bobs demo. x_shadebobs : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Shade bobs"$n" -------------------------------\n ----\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -------------------------------------------------" rm t=100 w ${"fitscreen 512,512,1,35%"},0,"[G"{`39`}"MIC] Shade Bobs" # Start animation loop. do t+=0.015 if $t>4*pi" || "{*,b} # Reset motions variables if necessary. rx,ry,rz,rt,rcx,t:=u(-1,1),u(-1,1),u(-1,1),u(-1,1),u(-0.6*0.6),0 N:=20+v(80) R:=(2+v(40))*min({*,w},{*,h})/300 if $obj3d rm[colormap,img,obj3d] fi {4+v(12)},1,1,3 noise[0] 255,2 ==. 1 r[0] 256,1,1,3,3 *[0] 255 shift[0] 1 => colormap (67.5;73.5;109.5;103.5;51.5;100.5;{2*$N};$N) 3,{2*$N},1,1,0 1,$N,1,1,5 2,$N,1,1,'y+x*$N' a[-2--1] x z. 0,5 4,$N,1,1,1 y[-3--1] a[-4--1] y => obj3d {*,w},{*,h} => img wait -1 fi # Compute bobs coordinates. r:=$ry+$rx*cos(6*$rz*$t)+(1-$rx)*sin(6*$rt*$t) (0;{30*$ry*($N-1)}) ($t;{2*pi*($N-1)/$N+$t}) r[-2,-1] 1,$N,1,1,3 +.. {360*sin($rz*$t)} *.. {pi/180} +sin[-2,-1] cos[-4,-3] *[-4,-2] $r *[-3,-1] $rcx +[-4,-3] +[-2,-1] *.. {{*,w}/2} *. {{*,h}/2} a[-2,-1] x ++. $R -.. $R a[-2,-1] y z. 0,2 y. j[obj3d] .,0,8 rm. # Draw bobs, map colors and display. j3d[img] [obj3d],50%,50%,0,-1,2,0,0 &[img] 255 +map[img] [colormap] w. rm. wait 20 if {*,CTRLLEFT}" && "{*,D} w[] 1024,1024 elif {*,CTRLLEFT}" && "{*,C} w[] 512,512 fi while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0 #@cli x_spline #@cli : Launch spline curve editor. x_spline : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Spline curve editor"$n" --------------------------\n ----\n ---- "${c}"Mouse"$n" to insert/move/delete points.\n ---- Key '"${c}"R"$n"' to reset the curve.\n ---- Key '"${c}"SPACE"$n"' to shows/hide spline curve.\n ---- Key '"${c}"P"$n"' to shows/hide control points.\n ---- Key '"${c}"ENTER"$n"' to shows/hide control polygon.\n ---- Key '"${c}"T"$n"' to shows/hide point tangents.\n ---- Key '"${c}"I"$n"' to shows/hide point indices.\n ---- Key '"${c}"C"$n"' to shows/hide point coordinates.\n ---- Keys '"${c}"+"$n"' and '"${c}"-"$n"' to increase/decrease roundness.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -----------------------------------------------------" # Init display and variables. if $! a x n 0,255 to_rgb else (0;0^0;128^0;0) r. 1024,1024,1,3,3 => "[G"{`39`}"MIC] Spline Editor" fi w[0] {0,[w,h]/2},0,0,{n} 0 # Point coordinates roundness=0.5 # Curve roundness visuflags=23 # Visualisation flags nearest=-1 # Nearest point active=-1 # Active point # Start event loop. do # Init coordinates [1] if necessary. if !{1,whds} rm[1] roundness=0.5 nearest=-1 active=-1 i[1] ({0.2*w},{0.2*h};\ {0.2*w},{0.8*h};\ {0.8*w},{0.8*h};\ {0.8*w},{0.2*h}) fi # Estimate screen-normalized coordinates [2], curve tangents [3] and tangent orientations [4]. [1] ({{*,w}*2/{0,w}},{{*,h}*2/{0,h}}) *[-2,-1] # Normalized coordinates. +shift[2] 0,-1,0,0,2 +shift[2] 0,1,0,0,2 -[-2,-1] *. $roundness # Curve tangents. +s. x sqr[-2,-1] +[-2,-1] sqrt. r. 2 +/[-2,-1] rm.. # Tangent orientations. # Display curve, control points, polygon and tangents. +r[0] {2*[{*,w},{*,h}]},1,3 if $visuflags&4 polygon. {2,h},{2,^},0.3,128,200,255 fi repeat h#1 { line. {2,@0-3},0.3,255,255,0 if $visuflags&1 spline. {2,@0-1},{3,@0-1},{2,@2-3},{3,@2-3},1,255 fi if $visuflags&8 thickline. {{2,@0}-{4,@0}*40},{{2,@1}-{4,@1}*40},{{2,@0}+{4,@0}*40},{{2,@1}+{4,@1}*40},3,1,0,255,0 fi if $visuflags&16 t. $>,{{2,@0}-10},{{2,@1}-42},35,1,255,255,0 fi if $visuflags&32 t. "("{round({1,@0})}","{round({1,@1})}")",{{2,@0}-50},{{2,@1}+16},30,1,100,200,255 fi shift[1-4] 0,-1,0,0,2 } if $visuflags&2 repeat h#1 { ellipse. {2,@0-1},10,10,0,1,0,0,0 ellipse. {2,@0-1},8,8,0,1,255,100,155 shift[2] 0,1,0,0,2 } fi rs. 50% w. rm[3,4,-1] wait # Handle key events. if {*,SPACE} visuflags+=$visuflags&1?-1:1 wait -1 fi # Show/hide spline if {*,P} visuflags+=$visuflags&2?-2:2 wait -1 fi # Show/hide points if {*,ENTER} visuflags+=$visuflags&4?-4:4 wait -1 fi # Show/hide polygon if {*,T} visuflags+=$visuflags&8?-8:8 wait -1 fi # Show/hide tangents if {*,I} visuflags+=$visuflags&16?-16:16 wait -1 fi # Show/hide indices if {*,C}" && "!{*,CTRLLEFT}" && "!{*,CTRLRIGHT} # Show/hide coordinates visuflags+=$visuflags&32?-32:32 wait -1 fi if {*,PADADD}" && "$roundness<1 roundness*=1.1 wait -1 fi # Increase roundness if {*,PADSUB}" && "$roundness>0.1 roundness*=0.9 wait -1 fi # Decrease roundness if {*,R}" && "!{*,CTRLLEFT}" && "!{*,CTRLRIGHT} rm. i[1] 0 wait -1 fi # Reset curve if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} fi # Increase window size if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} fi # Decrease window size if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] {0,w},{0,h} fi # Reset window size if {*,r} w[] fi # Resize window if necessary. # Set/unset active point. if !{*,b} active=-1 # Unset active point if mouse button is released elif {*,x}>=0" && "{*,b}" && "$active==-1 # Find new active point [2] ({2*[{*,x},{*,y}]}) -[-2,-1] sqr. s. x +[-2,-1] # Compute distance vector to points nearest:=ym # Set nearest point if im<256 active=$nearest fi # Set it as active point, if near enough rm. fi rm[2] # Move active point. if {*,b}&1" && "{*,x}>=0" && "$active!=-1 =[1] {{*,x}*{0,w}/{*,w}},0,$active =[1] {{*,y}*{0,h}/{*,h}},1,$active # Delete nearest point. elif {*,b}&2" && "{*,x}>=0" && "{1,h}>3 l[1] { s y rm[$nearest] a y } wait -1 # Insert new active point. elif {*,b}&1" && "{*,x}>=0 xy=({{*,x}*{0,w}/{*,w}},{{*,y}*{0,h}/{*,h}}) # Point coordinates in the image basis +shift[1] 0,-1,0,0,2 +. [1] /. 2 # Compute center of segments $xy -[-2,-1] sqr. s. x +[-2,-1] # Compute distance vector to segments ns:=ym rm. # Get nearest segment l[1] { s y i[{$ns+1}] $xy a y } # Insert new point at right position active:=$ns+1 # Set new active point as newly inserted fi while {*}" && "!{*,ESC}" && "!{*,Q} # Render spline as a tertiary mask for output. +shift[1] 0,-1,0,0,2 +shift[1] 0,1,0,0,2 -[-2,-1] *. $roundness [0],[0],1,1,2 rm[0] repeat h#1 { spline. {0,@0-1},{1,@0-1},{0,@2-3},{1,@2-3},1,1 shift[0] 0,-1,0,0,2 shift[1] 0,-1,0,0,2 } flood. 0,0,0,0,0,1,0 # Exit properly. rm[0,1] w 0 #@cli x_starfield3d #@cli : Launch the 3D starfield demo. x_starfield3d : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"3D starfield"$n" ---------------------------------------\n ----\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n -----------------------------------------------------------" l[] { ('G\47MIC') s x x=0 N=$! repeat $N { 0 t. {$>,t},0,0,48,1,1 x$>=$x y$>=0 z$>:=-3200-150*$> x:=$x+w+8 } k[50%--1] expand xy,6 dilate_circ 5 b 0.5 expand z,1 isosurface3d 10% *3d 1,1,5 rv3d repeat $N { col3d[$>] ${-rgb} } 0 t. "Version "${-strver},0,0,48,1,1 rs. ,18 +f. 255 to_rgb. random3d 2500 col3d. 255 *3d. 320,200,1000 -3d. 160,100 l3d 0,0,-600 w[] ${"fitscreen 640,400,1,35%"},0,"[G"{`39`}"MIC] 3D Starfield" t0=0 t=0 do 320,200,1,3 # Starfield. l.. { s3d r[2] 3,{2,h/3},1,1,-1 s[2] x %[4] 1000 +/[4] 1000 *. -1 n. 0,2 c. 0,1 sqr. j.. . rm. a[2-4] x y a y } j3d. ..,50%,50%,-600,1,0,0,0,240 -3d.. 0,0,{min(12,$t0/10-4)} # Torus. torus3d 100,30 col3d. 255,64,255 +col3d. 64,64,255 r3d. 1,0,0,-90 +3d. 65,0,0 +3d[-2,-1] c3d. r3d. 1,1,0,{-6*$t} r3d. 0,0,1,{2*$t} j3d.. .,{($t-200)*2}%,50%,0,0.25,3,0,0 rm. # Letters. repeat $N { +r3d[$>] 1,{$>%4},1,{-${z$>}/2} j3d.. .,{90+${x$>}},{60+${y$>}},${z$>},1,4,0,0 rm. z$>:=tl=280+6*$<;$t}+20):-20*($t-tl) } # Version number. if $t<280 op:=max(0,min(1,($t-200)/20)) else op:=max(0,1-($t-280)/20) fi j. ...,{(w-{-3,w})/2},120,0,0,$op,[-4] w. wait 30 rm. t0+=1 t:=$t0%350 if !$t x=0 repeat 5 { z$>:=-3200-150*$> } fi while {*}" && "!{*,ESC}" && "!{*,Q} w[] 0 rm } #@cli x_tetris #@cli : Launch tetris game. x_tetris : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Tetris"$n" --------------------------------------------\n ----\n ---- This is a G\47MIC implementation of the "${g}"Tetris"$n" game.\n ----\n ---- "${c}"Arrow keys"$n" to move/rotate the triominos.\n ---- Key '"${c}"SPACE"$n"' to make the current triomino falling.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n ----------------------------------------------------------" rm # Init board and triominos shapes. 4,1,1,1,1,1,1,1 3,2,1,1,1,0,0,1,1,1 3,2,1,1,0,0,1,1,1,1 2,2,1,1,1,1,1,1 3,2,1,1,0,1,1,1,1,0 3,2,1,1,0,1,0,1,1,1 3,2,1,1,1,1,0,0,1,1 => m0,m4,m8,m12,m16,m20,m24 if u<0.25 # Enable extended set. i 2,1,1,1,1,1 i 2,2,1,1,1,1,0,1 i 3,1,1,1,1,1,1 i 1,1,1,1,1 i 3,2,1,1,1,1,1,1,0,1 i 3,3,1,1,1,1,1,1,0,1,1,1,1 => m28,m32,m36,m40,m44,m48 fi repeat $! { i:=4*$> l[m$i] { repeat 3 { +rotate[0] {90*($>+1)} => m{$i+$>+1} } } } N=$! # Render triomino colored sprites. 3,$N,1,1,'u(16,224)' r. 3,400% => colors (0,-1,0;1,0,-1;0,1,0) *. 120 => mask repeat $N { +r[m$>] 500%,500%,1,3 +correlate. [mask],0 r. 200%,200%,1,1,3 ri.. . *[-2,-1] c. 30%,100% +r[m$>] .,.,1,3 +replace_color. 0,0,1,1,1,{colors,@{3*$>}-{3*$>+2}} rv[-3,-1] +[-3,-1] c.. 0,255 channels. 0 *. 255 a[-2,-1] c => s$> } rm[colors,mask] fact:={s0,w}/{m0,w} # Generate board and background. W=12 H=20 $W,$H . => board,curr_board {$fact*$W},{$fact*$H},1,3 . => render,curr_render +channels. 0 => curr_render_mask +rows[render] 0,50% plasma. 1,2 noise. 20 b. y,40%,1 +mirror. y a[-2,-1] y ri. [render] n. 0,64 b. x,1 100%,100% noise. 0.5,2 ==. 1 b. 1 *. 300 +[-2,-1] c. 0,255 => background # Start game. time=$| score=0 fall_mode=0 gameover=0 n=-1 nn:=v(0,$N-1) do wait {$fall_mode?-1:-20} # In case of game over. if $gameover +j[background] [curr_render],0,0,0,0,0.7,[curr_render_mask],255 to. "Game\nOver!",22,30%,32,2,1,255 w. rm. continue fi # Check for completed lines and select new random triomino. if $n<0 l[board] { s y i=-1 repeat $! { if {$<,im} i=$<,$i fi } 0 rm[$i] a y score+=2^(narg($i)-1)-1 r $W,$H,1,1,0,0,0,1 => board } if narg($i)>1 l[render] { s y,$H 0 rm[$i] a y r {$fact*$W},{$fact*$H},1,3,0,0,0,1 => render } fi n=$nn nn:=v(0,$N-1) x:=$W/2 y=0 do_render=1 fall_mode=0 fi # Render board at current time. if $do_render rm[curr_board,curr_render,curr_render_mask] [board] => curr_board j[curr_board] [m$n],{$x-int({m$n,w}/2)},$y,0,0,1,[m$n] [render] => curr_render sh[s$n] 3 j[curr_render] [s$n],{$fact*($x-int({m$n,w}/2))},{$fact*$y},0,0,1,.,255 rm. +*[curr_board] 255 r. [curr_render],[curr_render] => curr_render_mask 0 t. "Score : "$score" Next :",4,0,25,1,200 r. 50%,50%,1,3,2 +!=. 0 *. 255 j[curr_render] ..,0,0,0,0,1,.,255 j[curr_render_mask] .,0,0,0,0,1,.,255 rm[-2,-1] +*[m$nn] 196 r. 300%,300%,1,3 j[curr_render,curr_render_mask] .,{{curr_render,w}-w-4},3,0,0,1,.,196 rm. do_render=0 fi +shift[background] 0,{round(-13*$|*1.04^$score)},0,0,2 j. [curr_render],0,0,0,0,1,[curr_render_mask],255 w. ${"fitscreen .,20%"},0,"[G"{`39`}"MIC] Tetris" rm. cursor[0] 0 # Manage user interactions. if {*,SPACE} fall_mode=1 fi if {*,ARROWUP}" || "{*,ARROWLEFT}" || "{*,ARROWRIGHT} an:={*,ARROWUP}?(n=$n+1;n%4?n:n-4):$n nx:=w2=int({m$an,w}/2);max(w2,min($x-{*,ARROWLEFT}+{*,ARROWRIGHT},$W-({m$an,w}%2)-w2)) +j[board] [m$an],{$nx-int({m$an,w}/2)},$y,0,0,-1,[m$an] if iM==1 x=$nx n=$an fi rm. do_render=1 fi if {*,ARROWDOWN}" || "$|-$time>0.9^int($score/2)" || "$fall_mode # Piece goes down. y+=1 +j[board] [m$n],{$x-int({m$n,w}/2)},$y,0,0,-1,[m$n] if iM>1" || "$y+{m$n,h}>$H if $y<=1 gameover=1 fi # Game over! j[board] [curr_board] j[render] [curr_render] n=-1 fi rm. time=$| do_render=1 fi while {*}" && "!{*,ESC}" && "!{*,Q} rm w 0 #@cli x_threshold #@cli : Threshold selected images interactively. x_threshold : e[^-1] "Threshold image"$_gmic_s" interactively." foreach { wsiz0=${"fitscreen ."} value=-1 w[] $wsiz0,0,"[G'MIC] "{n}" - Interactive threshold" 0 for {*}" && "!{*,ESC} { if [w#1,h#1]!=[{*,w,h}] # Generate image view rm[1] +slices[0] 50% r. {*,w,h},1,100%,1 w. fi mx,my,mb={*,x,y,b} if $mb" && "$mx>=0" && "$my>=0 value:="w>h?( dw1 = "{*,w}" - 1; val = (dw1 - "$mx")*100/dw1; ): ( dh1 = "{*,h}" - 1; val = (dh1 - "$my")*100/dh1; )" update_view=1 fi if $update_view if $value>=0 +ge[1] $value% *. 255 to. "Threshold: "{_round($value,0.1)}%,1%,1%,{max(13,3.5*h%)} w. rm. else w. fi update_view=0 fi wait if {*,r} update_view=1 fi if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,D} w[] {{*,w}*1.5},{{*,h}*1.5} wait -1 update_view=1 fi if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,C} w[] {{*,w}/1.5},{{*,h}/1.5} wait -1 update_view=1 fi if ({*,CTRLLEFT}" || "{*,CTRLRIGHT})" && "{*,R} w[] $wsiz0 wait -1 update_view=1 fi } w[] 0 rm. u $value% ge ${} } #@cli x_tictactoe #@cli : Launch tic-tac-toe game. x_tictactoe : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Tic-Tac-Toe game"$n" -----------------\n ----\n ---- Use "${c}"mouse"$n" to select positions of the\n ---- symbols. Close window to exit game.\n ----\n -----------------------------------------" # Allocate variables. message=0 # [-7] : State message. counter=0 # [-6] : Turn counter (0 to 8). player=0 # [-5] : Current player (0 or 1). state=0 # [-4] : Board state. tmp3=0 # [-3] : Temporary variable 3. tmp2=0 # [-2] : Temporary variable 2. tmp1=0 # [-1] : Temporary variable 1. _x_tictactoe2 # Generate board. w. -1,-1,0," " # Init display window. # Start main loop. do # Set state message depending on the current player. if $player message="Tic-Tac-Toe (O to play)" else message="Tic-Tac-Toe (X to play)" fi # Select position by the user. do # Enter event loop. w[] {w},{h},0,"[G"{`39`}"MIC] "$message wait # Wait for events and force window size if necessary. if !{*}" || "{*,ESC}" || "{*,Q} w[] 0 rm return fi # Quit properly if window has been closed. if {*,b}&1" && "{*,x}>20" && "{*,y}>20" && "{*,x}<400" && "{*,y}<400 # If mouse button pressed on the board area tmp3:=int(({*,x}-15)/130) # X of the selected position (0,1 or 2) tmp2:=int(({*,y}-15)/130) # Y of the selected position (0,1 or 2) tmp1:=4^($tmp2*3+$tmp3) # Get state code of the selected position if int($state/$tmp1)%4 tmp1=-1 fi # Check availability of position else tmp1=-1 fi # If no mouse button, do nothing but loop while $tmp1<0 # Go on until a valid position selected # Draw symbol on selected position and update board state. _x_tictactoe{$player%2} # Generate the symbol sprite and his mask j... ..,{"130*"$tmp3" + 15+u(-5,5)"},\ # Draw symbol (with some fuzzyness) {"130*"$tmp2" + 15+u(-5,5)"},0,0,1,. rm[-2--1] # Delete sprite and mask w. # Update display window state+=(1+$player)*$tmp1 # Update the board state # Check for a winning configuration. (21,1344,86016,4161,16644,66576,65793,4368;\ # The list of winning configurations 0,0,0,0,1,2,0,0;\ # Corresponding X coords for the stroke 0,1,2,0,0,0,0,0;\ # Corresponding Y coords for the stroke 3,3,3,4,4,4,5,6) # Corresponding index of the stroke sprite repeat w { # Start to check configurations tmp1={@$>} # Save the current configuration code if ($state&$tmp1)==$tmp1||($state&(2*$tmp1))==2*$tmp1 # If a winner has been found _x_tictactoe{i($>,3)} # Generate the stroke symbol and his mask j[-4] ..,{130*{-3,i($>,1)}+u(-5,5)},\ # And display it on the board {130*{-3,i($>,2)}+u(-5,5)},0,0,1,. rm[-2--1] if ($state&$tmp1)==$tmp1 w.. -1,-1,0,"Tic-Tac-Toe (X won!)" else w.. -1,-1,0,"Tic-Tac-Toe (O won!)" # Update display window fi do wait if {*} w[] {*,w},{*,h} fi while {*}" && "!{*,ESC}" && "!{*,Q} # Wait for the window to be closed rm w[] 0 return # And return properly fi } # Go on until all configurations checked rm. # Delete winning configuration data player:=($player+1)%2 # Select next player counter+=1 # Increment turn counter while $counter<9 # Loop to next move until all positions filled # Here, the game has been ended without winners. w[] -1,-1,0,0,"Tic-Tac-Toe (Tied game!)" # Change window title do wait if {*} w[] {*,w},{*,h} fi while {*}" && "!{*,ESC}" && "!{*,Q} # Wait for the window to be closed w[] 0 rm # Return properly # Generate Tic-Tac-Toe graphics. _x_tictactoe : # Apply a hand-drawing effect. spread. 4 b. 6,1,0 sharpen. 0.8 n. 0,1 __x_tictactoe : # Apply color to last image and generate corresponding opacity mask. +f. 1-i +n.. $2,255 +n... $3,255 n[-4] $1,255 a[-4,-2,-1] c _x_tictactoe0 : # Generate a 'X' and his mask. 128,128,1,1,1 line. 15%,15%,85%,85%,1,0 line. 15%,85%,85%,15%,1,0 erode. 12 _x_tictactoe deform. 4 __x_tictactoe 40,40,160 _x_tictactoe1 : # Generate a 'O' and his mask. 128,128,1,1,1 ellipse. 50%,50%,22%,22%,0,1,0 ellipse. 50%,50%,15%,15%,0,1,1 _x_tictactoe deform. 4 __x_tictactoe 160,40,160 _x_tictactoe2 : # Generate the board. 391,391,1,1,"!(x%130) || !(y%130)" r. 421,421,1,1,0,0,0.5,0.5 dilate. 3 _x_tictactoe f. 1-i 100%,100% noise. 10 b. 8,0 sharpen. 1.5 n. 220,255 *[-2,-1] to_rgb. _x_tictactoe3 : # Generate an horizontal stroke and his mask. 421,130,1,1,1 line. 10%,60%,90%,60%,1,0 erode. 6 _x_tictactoe rotate. {u(-6,6)},1,1,50%,50% __x_tictactoe 180,10,10 _x_tictactoe4 : # Generate a vertical stroke and his mask. _x_tictactoe3 transpose[-2--1] _x_tictactoe5 : # Generate a ++ diagonal stroke and his mask. 421,421,1,1,1 line. 10%,10%,90%,90%,1,0 erode. 6 _x_tictactoe __x_tictactoe 180,10,10 _x_tictactoe6 : # Generate a +- diagonal stroke and his mask. 421,421,1,1,1 line. 10%,90%,90%,10%,1,0 erode. 6 _x_tictactoe __x_tictactoe 180,10,10 #@cli x_warp : _nb_keypoints_xgrid>=2,_nb_keypoints_ygrid>=2,_nb_keypoints_contours>=0,\ # _preview_fidelity={ 0:coarsest | 1:coarse | 2:normal | 3:fine | 4:finest },\ # _[background_image],0<=_background_opacity<=1 #@cli : Launch the interactive image warper. #@cli : Default values: 'nb_keypoints_xgrid=nb_keypoints_ygrid=2', 'nb_keypoints_contours=0' and 'preview_fidelity=1'. x_warp : check "isint(${1=2},2) && isint(${2=$1},2) && isint(${3=0},0) && isint(${4=1},0,4) 0 && ${6=0.5}>=0 && $6<=1" skip "${5=}" check_display $0 if !$! error[0--3] "Command '$0': Requires at least one input image!" return fi use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Interactive image warper"$n" -----------------------------\n ----\n ---- "${c}"Left mouse button"$n": Add and move keypoint.\n ---- "${c}"Right mouse button"$n": Delete keypoint.\n ---- Key '"${c}"SPACE"$n"' or "${c}"middle mouse button"$n": Show/hide keypoints.\n ---- Key '"${c}"TAB"$n"': Change keypoint radius.\n ---- Key '"${c}"SHIFT"$n"': Toggle to original image.\n ---- Key '"${c}"R"$n"': Reset keypoints.\n ---- Keys '"${c}"ESC"$n"', '"${c}"ENTER"$n"' or '"${c}"Q"$n"': Process fullres and exit.\n ----\n -------------------------------------------------------------" if ${"is_image_arg $5"} pass$5 store. background fi radius_keypoints=4 foreach { nm={n} => img if narg($background) $background rs. {-2,[w,h]},3,1 store. rbackground fi # Start interactive window. selected_keypoint=-1 view_keypoints=1 do # Generate base image. if !narg($imgb) if {*} wdims=${"fitscreen "{*,d},{img,{*,d}*h/w}} else wdims=${"fitscreen "{img,[w,h,1]},128,60%} fi w[] $wdims,0,"[G'MIC] Interactive Warp" +to_color[img] r. $wdims,1,100%,2 n. 0,255 => imgb rmn warp,imgw,imgr fi # Generate set of keypoints. if !narg($keypoints) if narg($__x_warp_keypoints) ($__x_warp_keypoints) r. 1,{w/4},1,4,-1 else # Keypoints located on regular grid. nbp,nbq=$1,$2 1,{$nbp*$nbq},1,4,"const nbp = "$nbp"; const nbq = "$nbq"; p = y%nbp; q = int(y/nbp); x = p*100/(nbp - 1); y = q*100/(nbq - 1); [ x,y,x,y ]" # Keypoints located on contours. nbc=$3 if $nbc>0 +b[imgb] 0.5 gradient_norm. sqrt. {round([w,h]/4)} gaussian. 20% 1,$nbc,1,4,"> begin(ref(crop(#-1),gauss)); st = stats(#-2); iM = st[1]; xM = st[8]; yM = st[9]; img = vector(#w#-1*h#-1,-iM); draw(#-2,img,xM - w#-1/2,yM - h#-1/2,0,0,w#-1,h#-1,1,1,-1,gauss); nxyM = [ xM,yM ]*100/([w#-2,h#-2]-1); [ nxyM,nxyM ]" rm[-3,-2] a[-2,-1] y fi fi N0:=h => keypoints rmn warp,imgw,imgr fi # Generate warp field. if !narg($warp) subsamp:=arg(1+$4,8,6,4,2,1) +_x_warp_rbf[keypoints] {imgb,round([w,h]/$subsamp)} *. $subsamp r. {imgb,[w,h]},1,100%,3 +. '[x,y]' => warp rmn imgw,imgr fi # Generate warped image. if !narg($imgw) +warp[imgb] [warp],0,1,3 => imgw rmn imgr fi # Render visualization. if !narg($imgr) [imgw] if narg($rbackground) $rbackground ri. .. j.. .,0,0,0,0,$6 rm. fi => imgr if s==4 drgba. fi if $view_keypoints eval[keypoints] "* begin( col1 = [ 64,200,255 ]; col2 = [ 255,255,255 ]; fact = ([ w#"$imgw",h#"$imgw" ] - 1)/100; const radius1 = "$radius_keypoints"; const radius2 = radius1 + 2; const opacity = min(1,3/"($radius_keypoints-1)"); ); X = round((I)[0,2]*fact); ellipse(#-1,X,radius2,radius2,0,opacity,0); ellipse(#-1,X,radius1,radius1,0,opacity,y<"$N0"?col1:col2); I" fi w. fi # Manage user interaction wait mb={*,b} mxy:=[{*,x,y}]*100/([{*,w,h}]-1) mouse_over:={*,x}>=0 if $mouse_over" && "$mb" && "$selected_keypoint<0" && "h#$keypoints>0 # Determine selected keypoint selected_keypoint={keypoints,"dmin = inf; kmin = -1; fact = ([w#"$imgr",h#"$imgr"]-1)%; repeat (h,k, dist = norm(((I[k])[0,2] - ["$mxy"])*fact); dist=0 && dmin<"max(8,1.5*$radius_keypoints)"?kmin:-1"} fi if {*,-SPACE}" || "$mb==4 view_keypoints:=!$view_keypoints rmn imgr fi # Show/hide keypoints if {*,-R} rmn keypoints __x_warp_keypoints= fi # Reset keypoints if {*,-TAB} # Change keypoint radius radius_keypoints:=max(2,($radius_keypoints+2)%16) view_keypoints=1 rmn imgr fi if {*,SHIFTLEFT}" || "{*,SHIFTRIGHT} # View original if {imgb,s==4} +drgba[imgb] w. rm. else w[imgb] fi do wait while {*,SHIFTLEFT}" || "{*,SHIFTRIGHT} rmn imgr fi if {*,r} rmn imgb fi # Window resize if $mouse_over" && "$mb==1" && "$selected_keypoint<0 # Add keypoint wxy={warp,I([$mxy]*([w,h]-1)/100,1)*100/([w,h]-1)} ({"[ "$mxy,$wxy" ]"}) permute. zycx a[keypoints,-1] y selected_keypoint={keypoints,h-1} rmn warp elif $mouse_over" && "$mb==1" && "$selected_keypoint>=0 # Move keypoint ({"[ "$mxy" ]"}) permute. zycx j[keypoints] .,0,$selected_keypoint rm. rmn warp elif $mouse_over" && "$mb==2" && "$selected_keypoint>=0" && "h#$keypoints>4 # Remove keypoint 1,1,1,4,-1 j[keypoints] .,0,$selected_keypoint rm. discard[keypoints] -1 r[keypoints] 1,{keypoints,h/4},1,4,-1 N0-={$selected_keypoint<$N0} selected_keypoint=-1 rmn warp elif !$mb selected_keypoint=-1 fi while {*}" && "!{*,ESC}" && "!{*,Q}" && "!{*,ENTER} if !$< __x_warp_keypoints={keypoints,^} fi +drgba[imgw] to. "Processing fullres...",5,5,20,2 w. rm. k[img,keypoints] _x_warp_rbf[keypoints] {img,[w,h]} +. '[x,y]' warp[img] .,0,2,3 rm. => $nm } c 0,255 w 0 # Generate 2D warping field from set of keypoints, using RBF reconstruction. # $1,$2 = width,height _x_warp_rbf : if !h $1,$2,1,2,[x,y] return fi $1,$2,1,2,"* begin( fact = ([w,h] - 1)/100; xy(p) = (I[#0,p])[0,2]*fact; const N = h#0; ref(vector(#N*N),A); ref(vector(#N*2),B); repeat (N,p, repeat (N,q, A[q + N*p] = A[p + N*q] = norm(xy(p) - xy(q))); copy(B[2*p],(I[#0,p])[2,2]*fact - xy(p),2); ); W = solve(A,B,2); ); res = [ 0,0 ]; repeat (N,p,res += W[2*p,2]*(norm([x,y] - xy(p)))); res" k. #@cli x_waves #@cli : Launch the image waves demo. x_waves : check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Image waves"$n" --------------------------\n ----\n ---- "${c}"Left mouse button"$n" to drop balls.\n ---- "${c}"Right mouse button"$n" to rotate view.\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"CTRL+F"$n"' to switch fullscreen mode.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n ---------------------------------------------" if !$! l[] { # Generate fractal image 200,200 x:=-1.06-u*0.1 y:=-0.26-u*0.1 mandelbrot $x,$y,{$x+0.1},{$y+0.1},256 16,1,1,3,u r. 256,1,1,3,3 shift. 1 map[0] . rm. rs 100 +mirror y +mirror x + n 0,128 shape_fern {2*w},70%,25 rs. {-2,3*w/4} to_rgb. ri. ..,0,0,0.5,0.5 n. 0,196 +[-2,-1] c. 0,255 } else k[0] r[0] 100,100,1,3,2 fi i[0] (20;80;0^20;80;0^20;80;0) r[0] 400,300,1,3,3 water[0] 100,2 w[0] ${"fitscreen [0],35%"},0,"[G"{`39`}"MIC] Image Waves" w:=w elevation3d. 0 rv3d. sh. 8,{7+3*i[6]},0,0 r. 3,{h/3},1,1,-1 (0,1,0;1,0,1;0,1,0) /. 2 ball[] 20,200,255,128,1,0.7,3.5 0 $w,$w . l3d {$w/2},-200,-1000 sl3d 0.4 ss3d 0.8 f3d 500 time0=$| do +convolve. [3],1 -. ... rm... b. 0.8 -. {ia} # Update height map. r. 1,{$w*$w},1,1,-1 j[2] .,2,0 r. $w,$w,1,1,-1 # Set 3D object coordinates. [1] if {5,h} +l[5] { rows 0,2 nb:=w i[0] ('CImg3d') i[1] ($nb,$nb) transpose[2] (1,0;1,{$nb-1}) r. 2,$nb,1,1,3 round. 1,{4*$nb},1,1,1 y a y } [4] sprites3d.. .,1 rm. +3d[-2,-1] fi -3d. {$w/2},{$w/2} *3d. {0,0.9*max(w,h)/$w} # Center and scale 3D object. r3d. 0,0,1,{{*,b}&2?{*,x}*360/{*,w}:$|*30} r3d. 1,0,0,120 # Get rotated 3D object. +j3d[0] .,50%,65%,30,1,3,0,0 fps=${-fps} if $fps>0 to. $fps" fps",5,{h-22},16,2,0.2 fi w. if {*,CTRLLEFT}" && "{*,D} w[] {2.25*w},{2.25*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.25*w},{1.25*h} elif {*,CTRLLEFT}" && "{*,F} if !narg($is_fs) is_fs={*,w,h} fw:=min({*,u}*h/w,{*,v}*w/h) w[] $fw,{$fw*h/w},0,1 else w[] $is_fs,0,0 is_fs="" fi fi rm[-2,-1] wait 20 if {*,b}&1||($|-$time0)>1 ({u*$w};{u*$w};70;0) a[5,-1] x time0:=$|-u fi # Insert new ball. if {5,h} l[5,-1] { # Manage ball motion and collision. sh[0] 2,2,0,0 sh[0] 3,3,0,0 -.. . +. 0.2 rm[-2,-1] s[0] x repeat $!-1 { coords={$<,@0-1} if {$<,@2}=0 #@cli : Launch the fractal whirls demo. #@cli : Default values: 'opacity=0.2'. x_whirl : check "${1=0.2}>=0" check_display $0 use_vt100 g=$_vt100_g c=$_vt100_c n=$_vt100_n r=$_vt100_r e "\n ------ "${g}"Fractal whirls"$n" ----------------------------\n ----\n ---- Keys '"${c}"CTRL+D"$n"' to increase window size.\n ---- Keys '"${c}"CTRL+C"$n"' to reset window size.\n ---- Keys '"${c}"ESC"$n"' or '"${c}"Q"$n"' to exit.\n ----\n --------------------------------------------------" 5,5,1,3 256,256,1,3 [-1] w. ${"fitscreen 384,384,1,35%"},0,"[G"{`39`}"MIC] Fractal Whirls" tangle=0 tzoom=0 xc,yc:=(w-{-3,w})/2,(h-{-3,h})/2 do rand... 0,255 j.. [-3],$xc,$yc,0,0 f.. "*begin(R = rot(8*sin(-"$tangle")°)/(1.03+0.02*sin("$tzoom")); C=[w,h]/2); I((R*([x,y]-=C))+=C,0,0)" tangle+=0.001 tzoom+=0.02 j. [-2],0,0,0,0,$1 w. if {*,CTRLLEFT}" && "{*,D} w[] {3*w},{3*h} elif {*,CTRLLEFT}" && "{*,C} w[] {1.5*w},{1.5*h} fi wait 20 while {*}" && "!{*,ESC}" && "!{*,Q} rm[-3,-2] w[] 0 #------------------------------------- # # Define menu entries for # the G'MIC plug-in # #------------------------------------- gui_crop_preview : if narg($_preview_x0) z 0$_preview_x0,0$_preview_y0,0$_preview_x1,0$_preview_y1 fi gui_resize_preview : if narg($_preview_width) r $_preview_width,$_preview_height,100%,100%,2 fi gui_resize_preview_area : if narg($_preview_area_width) r $_preview_area_width,$_preview_area_height,100%,100%,2 fi gui_crop_resize_preview : gui_crop_preview gui_resize_preview # Function that always returns a valid preview size. gui_preview_wh : u {0$_preview_area_width?[0$_preview_area_width,0$_preview_area_height]:\ 0$_preview_width?[0$_preview_width,0$_preview_height]:\ min(w,h)>=256?[w,h]:[400,400]} # Function used for filters based on parallelization with spatial splitting. gui_parallel_overlap : apo "$1",$3,{$2?2^($2-1):0} # Function that renders a single preview image from multiple preview images. # Pre-cond : Number of image is $!>1. gui_preview : frame xy,1,0,0,0,255 montage B # Return name of a layer. gui_layer_name : u ${"_gui_merge_layers[0] name,[unnamed]"} # Return blending mode of a layer. gui_layer_mode : u ${"_gui_merge_layers[0] mode,alpha"} # Return opacity of a layer. gui_layer_opacity : u ${"_gui_merge_layers[0] opacity,100"} # Return (x,y) position of a layer. gui_layer_pos : u ${"_gui_merge_layers[0] pos,0,0"} # Set name of a layer. gui_set_layer_name : foreach { opacity=${-gui_layer_opacity} mode=${-gui_layer_mode} pos=${-gui_layer_pos} => "name($*),mode("$mode"),opacity("$opacity"),pos("$pos")" } # Set blending mode of a layer. gui_set_layer_mode : foreach { name=${-gui_layer_name} opacity=${-gui_layer_opacity} pos=${-gui_layer_pos} => "name("$name"),mode($1),opacity("$opacity"),pos("$pos")" } # Set opacity of a layer. gui_set_layer_opacity : foreach { name=${-gui_layer_name} mode=${-gui_layer_mode} pos=${-gui_layer_pos} => "name("$name"),mode("$mode"),opacity($1),pos("$pos")" } # Set position of a layer. gui_set_layer_pos : foreach { name=${-gui_layer_name} mode=${-gui_layer_mode} opacity=${-gui_layer_opacity} => "name("$name"),mode("$mode"),opacity("$opacity"),pos("{round("$1")}","{round("$2")}")" } # Flatten list of input layers according to their blending modes, opacities and positions, # set in each layer name by the G'MIC plug-in. gui_merge_layers : if !$! return fi mode0=${"_gui_merge_layers. mode,alpha"} opacity0=${"_gui_merge_layers. opacity,100"} pos0=${"_gui_merge_layers. pos,0,0"} if $opacity0<100" || "['$pos0']!='0,0' 100%,100%,1,4 fi wh0:=w,h wh=${-max_wh} r. $wh,1,100%,0 repeat $!-1 { l[-2,-1] { rv mode=${"_gui_merge_layers[1] mode,alpha"} opacity=${"_gui_merge_layers[1] opacity,100"} pos=${"_gui_merge_layers[1] pos,0,0"} to_a[1] r[1] $wh,1,100%,0 shift[1] ${u\ $pos},0,0 to_colormode[0,1] 0 blend $mode,{max(0,min(1,$opacity/100))} } } r $wh0,1,100%,0 _gui_merge_layers : u {`" str = ["{'{n}'}"]; const sstr = size(str); def = ['${2--1}']; ker = ['$1(']; const sker = size(ker); const N = max(size(str),size(def)); ref(vectorN(0),res); p = q = find(str,ker); p>=0?( q+=sker; r = find(str,'),',q); q = r>=0?r:(str[sstr-1]==_')'?sstr-1:-1); ); q>=0?copy(res,str[p + sker],q - p - sker):(def = ['${2--1}']; copy(res,def,size(def))); for (p = 0, p=0, res[p=q++] = 26); # Replace comma by quoted comma res"`} # gui_split_preview : "command",_split_type,_split_posx,_split_posy # 'split_type' can be { 0:no split | 1:forw. horiz. | 2:forw. vert. | 3:back. horiz. | 4:back. vert. | # 5:dupl. left | 6:dupl. top | 7:dupl. bottom | 8:dupl. right | 9:dupl. horiz.l | 10:dupl. vert. | # 11:checkered | 12:checkered inv. } # if 'posx' or 'posy' are equal to 'nan', splitting is done at the middle, and moving is not possible. # 'compute_entire_image' can be { 0:no | 1:yes }. gui_split_preview : check "isint(${2=0},0,12) && ${5=0}>=0" skip "${3=nan},${4=nan}" __split_preview="$1" m "_split_preview : run $__split_preview k[0]" is_movable:=!isnan($3)" && "!isnan($4) posx,posy:=$is_movable?cut([$3,$4],0,100):[50,50] pw,ph=${-gui_preview_wh} foreach { is_failed=0 +l { apply_timeout _split_preview,0$_preview_timeout onfail if 0$_is_timeout gui_timeout_preview else gui_error_preview ${} fi is_failed=1 } if $is_failed" || "!$2 k. else drgba rs $pw,$ph,2 r {[max(w#0,w#1),max(h#0,h#1)]},1,100%,0,0,0.5,0.5 posx,posy:=round([$posx,$posy]*([w,h]-1)%) if $2==1" || "$2==3 # Forward/backward horizontal 1,[0],1,1,'y>=$posy?($2==1):($2==3)' r. [0],[0],1,1 j[0] [1],0,0,0,0,1,. elif $2==2" || "$2==4 # Forward/backward vertical [0],1,1,1,'x>=$posx?($2==2):($2==4)' r. [0],[0],1,1 j[0] [1],0,0,0,0,1,. elif $2==5 # Duplicate top j[0] [1],0,$posy elif $2==6 # Duplicate left j[0] [1],$posx elif $2==7 # Duplicate bottom j[0] [1],0,{$posy-h} elif $2==8 # Duplicate right j[0] [1],{$posx-w} elif $2==9 # Duplicate horizontal if !$posy k. elif $posy>=h k.. else r[0] 100%,$posy,1,100%,0,0,0,0.5 r[1] 100%,{h-$posy},1,100%,0,0,0,0.5 a y fi elif $2==10 # Duplicate vertical if !$posx k. elif $posx>=h k.. else r[0] $posx,100%,1,100%,0,0,0.5 r[1] {w-$posx},100%,1,100%,0,0,0.5 a x fi elif $2==11 # Checkered 1,[0],1,1,'y>=$posy' [0],1,1,1,'x>=$posx' r[-2,-1] [0],[0],1,1 xor[-2,-1] j[0] [1],0,0,0,0,1,. elif $2==12 # Checkered reverse 1,[0],1,1,'y<=$posy' [0],1,1,1,'x>=$posx' r[-2,-1] [0],[0],1,1 xor[-2,-1] j[0] [1],0,0,0,0,1,. fi k[0] dir=0 if isin($2,1,3,5,7,9,11,12) dir+=1 line 0,$posy,100%,$posy,0.75,0xF0F0F0F0,255 line 0,$posy,100%,$posy,0.75,0x0F0F0F0F,0 if $is_movable coords:=p=[$posx,$posy];[p+[-10,-1],p+[10,-1],p+[0,-11]] polygon 3,$coords,0.7,255 polygon 3,$coords,0.7,0xFFFFFFFF,0 coords:=p=[$posx,$posy];[p+[-10,1],p+[10,1],p+[0,11]] polygon 3,$coords,0.7,255 polygon 3,$coords,0.7,0xFFFFFFFF,0 fi fi if isin($2,2,4,6,8,10,11,12) dir+=2 line $posx,0,$posx,100%,0.75,0xF0F0F0F0,255 line $posx,0,$posx,100%,0.75,0x0F0F0F0F,0 if $2!=9" && "$is_movable coords:=p=[$posx,$posy];[p+[-1,-10],p+[-1,10],p+[-11,0]] polygon 3,$coords,0.7,255 polygon 3,$coords,0.7,0xFFFFFFFF,0 coords:=p=[$posx,$posy];[p+[1,-10],p+[1,10],p+[11,0]] polygon 3,$coords,0.7,255 polygon 3,$coords,0.7,0xFFFFFFFF,0 fi fi l[] { 0 +t. "After",0,0,20,1,255 t.. "Before",0,0,20,1,255 autocrop 0 z[0] {0,[-1,-1,w,h]} z[1] {1,[-1,-1,w,h]} if isin($2,3,4,7,8,12) rv fi +dilate[-2,-1] 3 /[-2,-1] 255 r[-4,-3] 100%,100%,1,3 } if {-4,"const c1 = "$posx">w+2; const c2 = "$posy">h+2; arg("$dir",c2,c1,c1&&c2)"} j[0] [-4],2,2,0,0,1,.. fi if {-3,"const c1 = "$posx"h+2); arg("$dir",c2,c1,c1&&c2)"} j[0] [-3],{[w#0-3-w,isin($2,11,12)?2:h#0-3-h]},0,0,1,. fi k[0] fi } um _split_preview # Command to display text on a the G'MIC plug-in preview. # $1 : header message # $2 : header font size. # $3 : main message # $4 : main font size. gui_print_preview : skip "${1=},${3=}" check "${2=32}>=0 && ${4=20}>=0" # Resize image to output resolution. if $! k[0] fi drgba siz:=0$_preview_area_width?[0$_preview_area_width,0$_preview_area_height]:\ 0$_preview_width?[0$_preview_width,0$_preview_height]:\ [${fitscreen\ {[max(w,1),max(h,1),1]},400,800}] sizw:=arg(1,$siz)-8 if $! rs $siz,3,1 drgba else $siz,1,3,128 fi (1;0.5^1;0.5^0;1) (0,0.5,0;0.5,1,0.5;0,0.5,0) *. 0.65 ri[-2,-1] ...,3 * c 0,255 # Render title. l[] { if ['"$1"']!=0 msg="$1" else msg=" " fi 0 t. $msg,0,0,$2,1,255 i.. 100%,100%,1,3 fc.. 255,200,120 a[-2,-1] c rs. {min(w,arg(1,$sizw)-8)} r. 100%,140%,1,100%,0,0 onfail rm } # Render message. l[] { if ['"$3"']!=0 msg="$3" else msg=" " fi 0 t. $msg,0,0,$4,1,255 i.. 100%,100%,1,3,255 a c ('$msg') is_err:="crop(0,4)=='*** '" rm. if $is_err x:="ref(crop(0,0,0,3,32,h,1,1),T); for (c = 32, c$sizw { x:="const c0 = "$sizw"-1; const c02 = c0/2; for (c = c0, c>=c02, --c, max(crop(#-1,c-2,0,0,3,5,h,1,1))<=0?break()); c=c02, --c, max(crop(#-1,c,0,0,3,1,h,1,1))<=0?break()); c "mode("$mode"),opacity("$opacity"),pos("{arg(1,$coords)}","{arg(2,$coords)}"),name("$nm")" } fx_logo : skip "${1= }" rm[^0] w,h=${-gui_preview_wh} ({'"$1"'}) c. 0,127 r. 15,1,1,1,0,2 r. {w/3},1,1,3,-1 n. 0,255 100%,1,1,1,lerp(0,$w-1,x/(w-1)) rv[-2,-1] a[-2,-1] c rbf. $h transpose. r. $w,$h,1,3,3 blend alpha,0.5 to "$1",0.5~,0.5~,10% #@gui ____About #--------------------- #@gui ♥ Support Us ! ♥ : _none_,fx_support_us #@gui : note = note{"
"} #@gui : note = note{"
is proposed to you by
"} #@gui : note = note("
   David Tschumperlé     and     #@gui :    Sébastien Fourey
") #@gui : url = link{"( IMAGE Team / GREYC Laboratory - CNRS UMR 6072 )","https://www.greyc.fr/?page_id=443&lang=en"} #@gui : note = note{"as well as many other friendly and wonderful project contributors you can meet on the \ # G'MIC forum!"} #@gui : note = note{"\nIf you appreciate what we do on G'MIC and want to help us maintaining and \ # developing this piece of software, please consider \ # making a donation!\n\n"} #@gui : note = note{
} #@gui : url = link("Go to the donation page","https://libreart.info/en/projects/gmic") #@gui : sep = separator() #@gui : note = note{"\nG'MIC officially collaborates with LILA ("Libre comme l'Art"), #@gui : a French non-profit organization, which promotes Arts and Artists as well as access to technics and #@gui : knowledge for everyone.\n #@gui : LILA collects donations to help developing G'MIC."} #@gui : note = note{"Author: David Tschumperlé.      Latest Update: 2019/03/13.") fx_support_us : if $!>0 k[0] fi gui_print_preview "" l[] { filename=${-path_tmp}gmic_donations.png need_update:="Y = date(0); M = date(1); D = date(2); date_current = Y*365 + M*31 + D; Y = date(0,'"{/$filename}"'); M = date(1,'"{/$filename}"'); D = date(2,'"{/$filename}"'); date_file = Y*365 + M*31 + D; date_current - date_file>=15" if $need_update i https://gmic.eu/img/donations_latest_months.png o. $filename else i $filename fi 0 t. "Latest donations to the G'MIC project:",0,0,24,1,1 rows. 0,120% *. 255 channels. -3,0 rv a y,0.5 drgba 200 frame xy,4,200 frame xy,2,0 to_rgba onfail rm } if w>w#0" || "h>h#0 rs. {-2,[w,h]} fi j.. .,{([w#0,h#0]-[w,h])/2},0,0,0.85 rm. #@gui About G'MIC : _none_, _none_ #@gui : note = note{"
"} #@gui : note = note{"
is proposed to you by
"} #@gui : note = note("
   David Tschumperlé     and     #@gui :    Sébastien Fourey
") #@gui : url = link{"( IMAGE Team / GREYC Laboratory - CNRS UMR 6072 )","https://www.greyc.fr/?page_id=443&lang=en"} #@gui : note = note{"as well as many other friendly and wonderful project contributors you can meet on the \ # G'MIC forum!"} #@gui : note = note{"\n #@gui : This plug-in is based on our open-source libraries libgmic and #@gui : CImg (C++ Template Image Processing Library), #@gui : available at:"} #@gui : note = note(
  https://gmic.eu     and     #@gui :   \ #http://cimg.eu
) #@gui : note = note{"\n #@gui : If you appreciate G'MIC, you are welcome to send us a nice postcard from your place, at:\n\n #@gui : David Tschumperlé,\n Laboratoire GREYC (CNRS UMR 6072), Equipe Image,\n #@gui : 6 Bd du Maréchal Juin,\n 14050 Caen Cedex / France."} #@gui : note = note{"You may also consider making a donation!"} #@gui Download External Data : gui_download_all_data, gui_no_preview(1) #@gui : note = note{"This filter will download all external data files used by some filters of the G'MIC #@gui : plug-in (Color Presets, Light Leaks, Grain, Denoise, etc.), #@gui : and will install them as persistent files on your hard drive. After this operation, you won't need a permanent #@gui : internet connection anymore in order to use some of the G'MIC filters."} #@gui : note = note() #@gui : note = note{"Warning: A lot of data will be downloaded. #@gui : This can take a long time !"} #@gui : sep = separator() #@gui : Force re-Download from Scratch = _bool(0) #@gui : sep = separator() #@gui : note = note{"Alternative (manual) method:\nIf, for any reasons, #@gui : your plug-in is unable to retrieve data from the Internet, you can download all #@gui : those data files manually (as a single .zip file) at this address :"} #@gui : url = link{"https://gmic.eu/gmic_all_data.zip"} #@gui : note = note{"You must then decompress all files contained in this archive at the following location:\n #@gui : - for Unix-like systems : $HOME/.cache/gmic/\n #@gui : - for Windows systems : %APPDATA%/gmic/ #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/16/04.") gui_download_all_data : # Color Presets. l[] { input_cached gmic_cluts.gmz rm onfail rm } # Light Leaks. l[] { input_cached gmic_lightleaks.gmz rm onfail rm } # Denoising CNN. l[] { input_cached gmic_denoise_cnn.gmz rm onfail rm } # Grain. _filenames_grain=${-_fx_simulate_grain} _url_grain=https://gmic.eu/data_film_presets _prefix_grain=grain_ _ext_grain=cimgz # Sample images. _filenames_sample=${-__sample} _url_sample=https://gmic.eu/img _prefix_sample=sample_ _ext_sample=png # Demo thumbnails. _filenames_demos=gmic_demos _url_demos=https://gmic.eu/img _prefix_demos= _ext_demos=cimgz # Manage downloads. _n=0 _N:=narg($_filenames_grain,$_filenames_logo,$_filenames_sample) progress 0 _gui_download_all_data[] grain,$1 _gui_download_all_data[] logo,$1 _gui_download_all_data[] sample,$1 _gui_download_all_data[] demos,$1 progress 100 _gui_download_all_data : repeat narg(${_filenames_$1}) { filename=${_prefix_$1}${arg0\ $>,${_filenames_$1}}.${_ext_$1} e "Download "$filename if $2" || "!isfile(['{/${-path_cache}$filename}']) l[] { ${_url_$1}/$filename o ${-path_cache}$filename rm onfail } fi progress {100*$_n/$_N} _n+=1 } #@gui Gmicky - Roddy : fx_gmicky, fx_gmicky_preview #@gui : Mascot Image = choice{"Gmicky (by Deevad)","Gmicky (by Mahvin)","Gmicky & Wilber (by Mahvin)", #@gui : "Roddy (by Mahvin)"} #@gui : sep = separator() #@gui : note = note{"Gmicky is the name of the G'MIC mascot. #@gui : He is a small and cute tiger who knows how to do magic. #@gui : Gmicky is a tiger, i.e. fast, agile and elegant, just as the G'MIC code is :). #@gui : As many magicians, Gmicky knows lot of gimmicks, #@gui : and he is a direct and friendly companion of #@gui : the ImageMagick's wizard, or the GraphicMagick's frog."} #@gui : note = note{"Roddy is another mascot designed specifically for the #@gui : Artistic / Rodilius filter of G'MIC.\n"} #@gui : note = note{"Gmicky and Roddy have been both created and drawn by "} #@gui : url = link("Mahvelous Mahvin","http://www.mahvin.com/") #@gui : note = note{"and"} #@gui : url = link{"David Revoy (Deevad)","http://www.davidrevoy.com/"} fx_gmicky : rm if !$1 sp gmicky => "name(Gmicky)" elif $1==1 sp gmicky_mahvin => "name(Gmicky)" elif $1==2 sp gmicky_wilber => "name(Gmicky & Wilber)" else sp roddy => "name(Roddy)" fi fx_gmicky_preview : fx_gmicky $* rs $_preview_area_width,$_preview_area_height,2 #@gui Privacy Notice : _none_, _none_ #@gui : note = note{"This plugin may download up-to-date filter definitions from the #@gui : gmic.eu server.\n\n #@gui : It is the case when first launched after a fresh installation, and periodically #@gui : with a frequency which can be set in the settings dialog. #@gui : The user should be aware that the following information may be retrieved #@gui : from the server logs: IP address of the client; date and time of the request; #@gui : as well as a short string, supplied through the HTTP protocol "User Agent" header #@gui : field, which describes the full plugin version as shown in the window title #@gui : (e.g. "G'MIC-Qt for GIMP 2.10 - Linux 64 bits - 3.3.6").\n\n #@gui : Note that this information may solely be used for purely anonymous #@gui : statistical purposes. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: Sébastien Fourey.      Latest Update: 2018/03/01.") #@gui Release Notes : _none_, _none_ #@gui : note = note{" #@gui : - 2009/01/13 : version 1.3.0 (initial plug-in release).\n #@gui : - 2010/09/03 : version 1.4.0.\n #@gui : - 2011/07/07 : version 1.5.0.\n #@gui : - 2014/08/20 : version 1.6.0.\n #@gui : - 2016/03/25 : version 1.7.0.\n #@gui : - 2017/05/29 : version 2.0.0.\n #@gui : - 2017/10/09 : version 2.1.0.\n #@gui : - 2018/02/15 : version 2.2.0.\n #@gui : - 2018/06/21 : version 2.3.0.\n #@gui : - 2018/10/04 : version 2.4.0.\n #@gui : - 2019/03/15 : version 2.5.0.\n #@gui : - 2019/04/29 : version 2.6.0.\n #@gui : - 2019/08/14 : version 2.7.0.\n #@gui : - 2019/12/04 : version 2.8.0.\n #@gui : - 2020/03/28 : version 2.9.0.\n #@gui : - 2021/12/09 : version 3.0.0.\n #@gui : - 2022/04/06 : version 3.1.0.\n #@gui : - 2023/01/16 : version 3.2.0.\n #@gui : - 2023/09/04 : version 3.3.0.\n #@gui : - 2024/03/12 : version 3.3.5 (Current stable).\n #@gui : - 2024/03/13 : version 3.3.6_pre (Current pre-release).
\n #@gui : "} #@gui : sep = separator() #@gui : url = link{"View changelog for upcoming major version (3.4)","https://discuss.pixls.us/t/on-the-road-to-3-4"} #@gui : url = link{"View changelog for latest major version (3.3)","https://discuss.pixls.us/t/release-of-gmic-3-3"} #@gui What's New? : _none_,fx_whatsnew_preview #@gui : note = note("Here you'll find a list of filter additions and deletions in the plug-in, since your last visit. #@gui : When you have seen what's new, press the Got It! button to reset the list of changes.") #@gui : sep = separator() #@gui : nochange = value(0)_0+ #@gui : note = note("\nThere have been no changes since your last visit.\n\n") #@gui : New Filters = text(1,"")_0 #@gui : Removed Filters = text(1,"")_0 #@gui : Got it! = button(0.5)_0+ #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé. #@gui :       Latest Update: 2021/01/18.") fx_whatsnew_preview : skip "$*" file=${-path_cache}whatsnew.txt l[] { if !isfile(['{/$_path_rc/update$_version.gmic}']) update fi parse_gui whatsnew if isfile(['{/$file}']) it $file date={date([0,1,2],['{/$file}']):/} else 0 date={date([0,1,2]):/} fi utf82html.. utf82html. if $4 ot.. $file newfilters,delfilters= is_changes=0 else # Check for changes. newfilters,delfilters= +- is_changes:=max(abs(im),abs(iM))!=0 rm. if !w # First call to the filter rm. s -,10 repeat $! { newfilters.=" - "{$>,t}{`10`} } elif $is_changes # Estimate changes eval " str0 = str1 = vector256(); for (s0 = s1 = 0, s0Arrays & Tiles
#------------------------------- #@gui Array [Faded] : fx_array_fade, fx_array_fade_preview(1) #@gui : X-Tiles = int(2,1,10) #@gui : Y-Tiles = int(2,1,10) #@gui : X-Offset (%) = float(0,0,100) #@gui : Y-Offset (%) = float(0,0,100) #@gui : Fade Start (%) = float(80,1,100) #@gui : Fade End (%) = float(90,1,100) #@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axes") #@gui : Size = _choice("Shrink", "Expand", "Repeat [Memory Consuming!]") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_array_fade : if $7&1 mirror x fi if $7>1 mirror y fi array_fade $1,$2,$5,$6,$8 shift -$3%,-$4%,0,0,2 fx_array_fade_preview : fx_array_fade $1,$2,$3,$4,$5,$6,$7,0 #@gui Array [Mirrored] : fx_array_mirror, fx_array_mirror_preview(1) #@gui : Iterations = int(1,1,10) #@gui : X-Offset (%) = float(0,0,100) #@gui : Y-Offset (%) = float(0,0,100) #@gui : Array Mode = choice(2,"X-Axis","Y-Axis","XY-Axes","2XY-Axes") #@gui : Initialization = choice("Original","Mirror X","Mirror Y","Rotate 90 deg.","Rotate 180 deg.","Rotate 270 deg.") #@gui : Expand Size = _bool(false) #@gui : Crop (%) = int(0,0,100) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_array_mirror : skip ${7=0} if $5==1 mirror x elif $5==2 mirror y elif $5==3 rotate 90 elif $5==4 rotate 180 elif $5==5 rotate 270 fi if $7 if !$4 columns 0,{100-$7}% elif $4==1 rows 0,{100-$7}% elif $4==2 z 0,0,{100-$7}%,{100-$7}% elif $4==3 z {$7/2}%,{$7/2}%,{100-$7/2}%,{100-$7/2}% fi fi shift -$2%,-$3%,0,0,2 array_mirror $1,$4,$6 fx_array_mirror_preview : fx_array_mirror $1,$2,$3,$4,$5,0,$7 #@gui Array [Random] : array_random, array_random(1) #@gui : Source X-Tiles = int(5,1,20) #@gui : Source Y-Tiles = int(5,1,20) #@gui : Destination X-Tiles = int(7,1,20) #@gui : Destination Y-Tiles = int(7,1,20) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") #@gui Array [Random Colors] : fx_array_color, fx_array_color(1) #@gui : X-Tiles = int(5,1,20) #@gui : Y-Tiles = int(5,1,20) #@gui : Opacity = float(0.5,0,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_array_color : repeat $! { l. { $1,$2,1,3 rand. 0,255 to_colormode. {-2,s} ri. .. *. $3 *.. {1-$3} +[-2,-1] } mv. 0 } #@gui Array [Regular] : fx_array, fx_array_preview(1) #@gui : X-Tiles = int(2,1,10) #@gui : Y-Tiles = int(2,1,10) #@gui : X-Offset (%) = float(0,0,100) #@gui : Y-Offset (%) = float(0,0,100) #@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axes") #@gui : Size = _choice("Shrink", "Expand", "Repeat [Memory Consuming!]") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_array : shift -$3%,-$4%,0,0,2 if $5&1 mirror x fi if $5>1 mirror y fi array $1,$2,$6 fx_array_preview : fx_array $1,$2,$3,$4,$5,0 #@gui Ascii Art : fx_asciiart, fx_asciiart_preview(0)+ #@gui : Charset = choice(5,"Custom","Binary Digits","Digits","Lowercase Letters","Uppercase Letters", #@gui : "Ascii","Card Suits","Math Symbols") #@gui : Custom Dictionary = text{" .oO0"} #@gui : Analysis Scale = int(16,8,103) #@gui : Analysis Smoothness = float(15,0,100) #@gui : Synthesis Scale = int(16,8,103) #@gui : Result Type = choice(2,"White on Black","Black on White","Colored on Black","Colored on Transparent") #@gui : sep = separator() #@gui : Gamma = float(0,-3,3) #@gui : Smoothness = float(0.2,0,5) #@gui : Colors = choice("Full Colors","2 Colors","3 Colors","4 Colors","8 Colors","12 Colors","16 Colors", #@gui : "Grayscale","2 Grays","3 Grays","4 Grays","8 Grays","12 Grays","16 Grays") #@gui : sep = separator() #@gui : Output Ascii File = _bool(0) #@gui : Output Folder = _folder() #@gui : Output Filename = _text("gmic_asciiart.txt") #@gui : sep = separator() #@gui : url = link("Click here for a detailed description of this filter.",\ # "http://www.gimpchat.com/viewtopic.php?f=28&t=10047") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/27/03.") fx_asciiart : skip "${10=},${11=},${12=}" foreach { to_rgb apply_gamma {10^$7} b $8% n 0,255 if !$1 dict="$2" elif $1==1 dict=" 01" elif $1==2 dict=" 0123456789" elif $1==3 dict=" abcdefghijklmnopqrstuvwxyz" elif $1==4 dict=" ABCDEFGHIJKLMNOPQRSTUVWXYZ" elif $1==5 dict=" !\042#$%&\047()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\133\\\135^_\140abcdefghijklmnopqrstu"\ "vwxyz\173|\174~" elif $1==6 dict=" \16\17\20\21" elif $1==7 dict=" \200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231" fi if $6==1 negate fi if $-3 +img2ascii $dict,$3,$4%,$5,"$-2"/"$-1" else +img2ascii $dict,$3,$4%,$5 fi wh=${} if !$6 k. n 0,255 elif $6==1 k. negate n 0,255 elif $6==2" || "$6==3 r[0] $wh,1,100%,1 if $9>=7 luminance[0] fi if $9%7 quantize[0] {arg($9%7,2,3,4,8,12,16)-1},1,0 fi r[0] [1],[1],1,100% *[0] [1] if $6==2 rm[1] else *[1] 255 a c fi fi } fx_asciiart_preview : foreach { w,h={w},{h} fx_asciiart $1,"$2",${3-9},0,foo,foo r $w,$h,1,100%,0,0,0.5,0.5 } #@gui Chessboard : fx_chessboard, fx_chessboard_preview(0) #@gui : First Size = int(64,1,512) #@gui : Second Size = int(64,1,512) #@gui : First Offset = int(0,0,512) #@gui : Second Offset = int(0,0,512) #@gui : Angle = float(0,0,180) #@gui : Opacity = float(0.5,0,1) #@gui : First Color = color(0,0,0,255) #@gui : Second Color = color(255,255,255,255) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_chessboard : to_rgba chessboard ${1-14} fx_chessboard_preview : gui_split_preview "fx_chessboard $*",${-3--1} #@gui Dices : fx_dices, fx_dices(0) #@gui : Resolution = float(2,1,10) #@gui : Size = int(24,8,64) #@gui : Color Model = choice(1,"Black Dices","White Dices","Dices with Colored Numbers","Dices with Colored Sides") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/27/06.") fx_dices : # Create dice patterns. repeat 6 { {2*$2},{2*$2} _dice$> } if $3%2 negate[-6--1] fi frame_round[-6--1] 0,0,30%,0.5,128,128,128,0 frame[-6--1] xy,1,128,128,128,0 rs[-6--1] ,$2 a[-6--1] x foreach[^-1] { pass. # Prepare input images. +luminance[0] rv[1,2] r[0,1] {100*$1/$2}%,{100*$1/$2}%,1,100%,2 quantize[1] 6,0 # Convert input image to dices pattern. *.. $2 channels.. 0,1 r.. {$2*100}%,{$2*100}% $2,$2,1,2,"c?y:x" r. ...,...,1,2,0,2 +[-3,-1] +warp. ..,0,0,1 rm... if $3<2 rm[0] mv. 0 else r[0] [2],[2],1,100% rv[0,-1] blend[0,-1] multiply fi rm. } rm. _dice0 : ellipse. 50%,50%,5.2%,5.2%,0,1,255 _dice1 : ellipse. 25%,25%,5.2%,5.2%,0,1,255 ellipse. 75%,75%,5.2%,5.2%,0,1,255 _dice2 : _dice1 _dice0 _dice3 : _dice1 ellipse. 25%,75%,5.2%,5.2%,0,1,255 ellipse. 75%,25%,5.2%,5.2%,0,1,255 _dice4 : _dice3 _dice0 _dice5 : _dice3 ellipse. 25%,50%,5.2%,5.2%,0,1,255 ellipse. 75%,50%,5.2%,5.2%,0,1,255 #@gui Drawn Montage : fx_drawn_montage, fx_drawn_montage_preview(1) : * #@gui : Layer = choice("1st","2nd","3rd","4th","5th","6th","7th","8th","9th","10th","11th","12th", #@gui : "13th","14th","15th","16th") #@gui : Associated Color = color(0,0,0) #@gui : Zoom = float(-10,0,10) #@gui : X-Centering (%) = float(50,0,100) #@gui : Y-Centering (%) = float(50,0,100) #@gui : Angle = choice("0 deg.","90 deg.","180 deg.","270 deg.") #@gui : Pargs = value(-1) #@gui : Args0 = value(0:0:0:0:50:50:0) #@gui : Args1 = value(0:0:0:0:50:50:0) #@gui : Args2 = value(0:0:0:0:50:50:0) #@gui : Args3 = value(0:0:0:0:50:50:0) #@gui : Args4 = value(0:0:0:0:50:50:0) #@gui : Args5 = value(0:0:0:0:50:50:0) #@gui : Args6 = value(0:0:0:0:50:50:0) #@gui : Args7 = value(0:0:0:0:50:50:0) #@gui : Args8 = value(0:0:0:0:50:50:0) #@gui : Args9 = value(0:0:0:0:50:50:0) #@gui : Args10 = value(0:0:0:0:50:50:0) #@gui : Args11 = value(0:0:0:0:50:50:0) #@gui : Args12 = value(0:0:0:0:50:50:0) #@gui : Args13 = value(0:0:0:0:50:50:0) #@gui : Args14 = value(0:0:0:0:50:50:0) #@gui : Args15 = value(0:0:0:0:50:50:0) #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter requires a top layer containing the desired montage layout defined as free-form shapes #@gui : of different colors. You can then assign each layer to a layout color to create the montage. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/01/29.") _fx_drawn_montage : nb_layers:=min(16,$!-1) if !$nb_layers error="A top layout layer is missing for this filter to make it work properly!" if $-1 gui_warning_preview $error return else error $error fi fi # Manage parameter changes. pargs,args0,args1,args2,args3,args4,args5,args6,args7,args8,args9,args10,args11,args12,args13,args14,args15=${9-25} if $1!=$pargs # Get back saved parameters for selected layer ('${args$1}') replace. {':'},{','} arg_R,arg_G,arg_B,arg_zoom,arg_xcenter,arg_ycenter,arg_angle=${u\ {t}} rm. else # Set new parameters for selected #L arg_R,arg_G,arg_B,arg_zoom,arg_xcenter,arg_ycenter,arg_angle=${2-8} fi args$1=$arg_R:$arg_G:$arg_B:$arg_zoom:$arg_xcenter:$arg_ycenter:$arg_angle status=\{$1\}\{$arg_R,$arg_G,$arg_B\}\{$arg_zoom\}\{$arg_xcenter\}\{$arg_ycenter\}\{$arg_angle\}\{$1\}\{$args0\}\ \{$args1\}\{$args2\}\{$args3\}\{$args4\}\{$args5\}\{$args6\}\{$args7\}\{$args8\}\{$args9\}\{$args10\}\ \{$args11\}\{$args12\}\{$args13\}\{$args14\}\{$args15\} # Read parameters for all layers. c= repeat $nb_layers { ('${args$>}') replace. {':'},{','} R$>,G$>,B$>,zoom$>,xcenter$>,ycenter$>,angle$>=${u\ {t}} cols=$cols$c${R$>},${G$>},${B$>} c=, rm. rotate[{1+$>}] {90*${angle$>}} } # Get bounding boxes for each associated color in layout layer, as well as labeled layout layer. to_rgb[0] to_rgba[^0] $nb_layers,1,1,4,[inf,inf,-inf,-inf] [0],[0] f[0] " begin(colors = [ "$cols" ]); repeat ("$nb_layers",l, I==colors[3*l,3]?( if (xi(#-2,l,0,0,2), i(#-2,l,0,0,2) = x); if (y>i(#-2,l,0,0,3), i(#-2,l,0,0,3) = y); i(#-1,x,y) = l + 1; ); ); I" thumbnail=0 boundaries=0 if $-1 # Render thumbnail of current layer, for preview. l:=1+$1 if $l<=$nb_layers if {$l,w>h} +rs[$l] {(0$_preview_area_width?0$_preview_area_width:400)/6} else +rs[$l] ,{(0$_preview_area_height?0$_preview_area_height:400)/6} fi frame. 1,1,0,0,0,255 drgba. to. "#"$l,0,-2,13 to_rgba. mv. -3 thumbnail=1 fi # Render region boundaries of layout layer, for preview. +f. "const boundaries = 1; ref(crop(x - 2,y - 2,5,5),V); if (min(V)==max(V),0,i)" (0,0,0,$cols) r. 3,{$nb_layers+1},1,1,-1 1,100%,1,1,y?255:0 a[-2,-1] x permute. yzcx map.. . rm. repeat $nb_layers { xmin,ymin,xmax,ymax={-3,I[$>]} if !isinf($xmin)" && "!isinf($ymin)" && "!isinf($xmax)" && "!isinf($ymax) xc$>,yc$>:=0.5*([$xmin,$ymin]+[$xmax,$ymax]-6)*100/[w,h] else xc$>,yc$>=-1024 fi } mv. -3 boundaries=1 fi # Render montage. if iM>0 # Resize layers to fit proposed layout. repeat $nb_layers { l:=$>+1 xmin,ymin,xmax,ymax={-2,I[$>]} if !isinf($xmin)" && "!isinf($ymin)" && "!isinf($xmax)" && "!isinf($ymax) dx,dy:=$xmax-$xmin+1,$ymax-$ymin+1 nw,nh={$l,round(min(w/$dx,h/$dy)*[$dx,$dy]/(1+3*(${zoom$>}/10)^2))} r[$l] $nw,$nh,1,100%,0,0,{(100-${xcenter$>})%},{(100-${ycenter$>})%} # Centered crop r[$l] $dx,$dy,1,100%,6 # Resize else r[$l] 1,1 # Save a bit of memory! fi } # Draw image pixels over layout layer. to_rgba[0] f[0] " l = i#-1; l1 = l - 1; l<=0?I:( xmin = i(#-2,l1,0,0,0); ymin = i(#-2,l1,0,0,1); rx = x - xmin; ry = y - ymin; crop(#l,rx,ry,0,0,1,1,1,4); )" fi rm[-2,-1] # Manage preview. if $boundaries blend[0,-1] alpha fi # Add shape boundaries. if $thumbnail # Add current layer thumbnail over preview drgba[0] rs[0] ${-gui_preview_wh} j[0] .,3,3,0,0,0.75 rm. fi if $-1 repeat $nb_layers { to "#"{1+$>},${xc$>}%,${yc$>}%,13,1,0.75 } fi k[0] c 0,255 u $status fx_drawn_montage : _fx_drawn_montage $*,0 fx_drawn_montage_preview : _fx_drawn_montage $*,1 #@gui Extract Objects : fx_extract_objects, fx_extract_objects_preview(1) #@gui : Background Point (%) = point(0,0) #@gui : sep = separator() #@gui : Color Tolerance = int(20,0,256) #@gui : Opacity Threshold (%) = int(50,0,100) #@gui : Minimal Area = float(0.3,0,5) #@gui : Connectivity = choice("Low","High") #@gui : Output As = _choice(0,"Crop","Segmentation") #@gui : sep = separator() #@gui : Preview Guides = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/23/02.") #@gui : url = link("Filter explained here","http://gimpchat.com/viewtopic.php?f=28&t=7905") fx_extract_objects : if $5 min_area=$5% else min_area=6 fi foreach { to_rgba nm=${-gui_layer_name} w,h={w},{h} x,y:=$1%*(w-1),$2%*(h-1) color:=I($x,$y) if !$7 # Output: Crop. +replace_color $3,0,$color,0,0,0,0 autocrop_components. $4%,$min_area,$6,2 repeat w { +z[0] {1,i($>,0)},{1,i($>,1)},{1,i($>,3)},{1,i($>,4)} => pos({1,i($>,0)}\,{1,i($>,1)})\,name($nm" "[$>]) } rm[0,1] elif $7==1 # Output: Segmentation. replace_color $3,0,$color,0,0,0,0 +autocrop_components[0] $4%,$min_area,$6,2 autocrop_components[0] $4%,$min_area,$6,1 repeat w { =>[$>] pos({i($>,0)}\,{i($>,1)})\,name($nm" "[$>]) } rm. fi $w,$h,1,4 fc. $color => name($nm" [background]") } fx_extract_objects_preview : x0,y0=${1,2} if $5 min_area=$5% else min_area=5 fi foreach { to_rgba x:=$x0%*(w-1) y:=$y0%*(h-1) color:=I($x,$y) +replace_color $3,0,$color,0,0,0,0 autocrop_components. $4%,$min_area,$6,2 repeat w { xycoords={1,i($>,0)},{1,i($>,1)},{1,i($>,3)},{1,i($>,4)} rectangle[0] $xycoords,0.3,0,0,255,255 rectangle[0] $xycoords,1,0xFFFFFFFF,0,0,0,255 } drgba[0] to[0] {w}" objects",2,2,13,2,0.3,255,255,255,255 k[0] if $8 line 0,$y0%,100%,$y0%,0.5,0xF0F0F0F0,255 line 0,$y0%,100%,$y0%,0.5,0x0F0F0F0F,0 line $x0%,0,$x0%,100%,0.5,0xF0F0F0F0,255 line $x0%,0,$x0%,100%,0.5,0x0F0F0F0F,0 fi circle $x,$y,3,1,0,255,0 circle $x,$y,3,1,0xFFFFFFFF,0 } #@gui Grid [Cartesian] : fx_imagegrid, fx_imagegrid(0) #@gui : X-Size = int(10,2,512) #@gui : Y-Size = int(10,2,512) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_imagegrid : imagegrid $1,$2 #@gui Grid [Hexagonal] : fx_imagegrid_hexagonal, fx_imagegrid_hexagonal(1) #@gui : Resolution = int(32,1,128) #@gui : Outline = float(0.1,0,0.5) #@gui : Anti-Aliasing = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/12/01.") fx_imagegrid_hexagonal : foreach { if $3 r 200%,200%,1,100% fi imagegrid_hexagonal $1,$2 if $3 r 50%,50%,1,100%,2 fi } #@gui Grid [Triangular] : fx_imagegrid_triangular, fx_imagegrid_triangular(0) #@gui : Pattern Width = int(10,8,128) #@gui : Pattern Height = int(18,8,128) #@gui : Pattern Type = choice(0,"Horizontal","Vertical","Crossed","Cube","Decreasing","Increasing") #@gui : Outline Color = color(255,255,255,128) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/08/07.") fx_imagegrid_triangular : foreach { split_opacity l[0] { to_rgb imagegrid_triangular ${1-3},{$7/255},${4-6} } a c } #@gui Loose Photos : fx_loose_photos, fx_loose_photos_preview(1) #@gui : note = note("Photo geometry:") #@gui : Density (%) = float(60,0,100) #@gui : Maximal Size (%) = float(40,0,100) #@gui : Minimal Size (% of Max) = float(50,0,100) #@gui : Maximal Ratio (%) = float(100,0,100) #@gui : Minimal Ratio (% of Max) = float(50,0,100) #@gui : Maximal Angle (deg.) = float(360,0,360) #@gui : Minimal Angle (% of Max) = float(0,0,100) #@gui : Frame Size (%) = float(2,0,20) #@gui : Frame Color = color(255,255,255) #@gui : sep = separator() #@gui : note = note("Photo content:") #@gui : Rotation Probability (%) = float(50,0,100) #@gui : Maximal Angle (deg.) = float(25,0,360) #@gui : Minimal Angle (% of Max) = float(0,0,100) #@gui : Background = color(0,0,0,0) #@gui : Background Image (%) = float(0,0,100) #@gui : sep = separator() #@gui : note = note("Shadow:") #@gui : Opacity (%) = float(50,0,100) #@gui : X-Shift (%) = float(1,-10,10) #@gui : Y-Shift (%) = float(1,-10,10) #@gui : Smoothness (%) = float(1,0,5) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/09/07.") fx_loose_photos : photo_density,photo_max_size,photo_min_size,photo_max_ratio,photo_min_ratio,photo_max_angle,photo_min_angle,\ frame_size,frame_R,frame_G,frame_B,image_rot_prob,image_max_angle,image_min_angle,\ background_R,background_G,background_B,background_A,background_image,\ shadow_opacity,shadow_shift_x,shadow_shift_y,shadow_smoothness=${1-23} foreach { nm={n} to_rgba if narg($_is_preview) srand {date(4)} fi frame:=$frame_size%*max(w,h)*$photo_max_size% # Generate random frame positions and sizes. 100%,100% noise_poissondisk. {lerp(100,5,($photo_density%)^0.25)}% 0 eval.. "i?( const Mwh = max(w,h); const M_size = Mwh*$photo_max_size%; const m_size = M_size*$photo_min_size%; width = u(m_size,M_size); const M_ratio = $photo_max_ratio%; const m_ratio = M_ratio*$photo_min_ratio%; ratio = u(m_ratio,M_ratio); height = width*ratio; u<0.5?swap(width,height); const M_angle = $photo_max_angle; const m_angle = M_angle*$photo_min_angle%; angle = u(m_angle,M_angle); da_push([u, x, y, width, height, angle]) )" da_freeze. rm.. sort. +,y channels. 1,100% => coords [0],[0],1,4 => canvas repeat h#$coords { x,y,w,h,ang={coords,I[$>]} plane3d $w,$h,1,1 col3d. -1 plane3d {[$w,$h]+2*$frame},1,1 col3d. 255 c3d[-2,-1] +3d. 0,0,0.1 +3d[-2,-1] r3d. 0,0,1,$ang +l. { s3d k[2] r. 3,{h/3},1,1,-1 z. 0,1 s x xm,ym,xM,yM:=floor([im#0,im#1]),ceil([iM#0,iM#1]) rm } {2*([$xM-$xm,$yM-$ym]+1)},1,1,-2 j3d. ..,50%,50%,-100,1,2,0,0,200 rm.. r. 100%,100%,1,4 f. "const boundary = 3; const x0 = int($x - w/4); const y0 = int($y - h/4); i>=0?[[i0*$frame_R,i1*$frame_G,i2*$frame_B]/255,i3]: i==-2?[0,0,0,0]: I(#0,x0 + x/2,y0 + y/2)" rs. 50% if u<$image_rot_prob% rotate. {"const M_angle = $image_max_angle; const m_angle = M_angle*$image_min_angle%; (u<0.5?-1:1)*u(m_angle,M_angle)"} fi x0,y0,x1,y1:=" const x0 = floor($x - w/2); const y0 = floor($y - h/2); const x1 = x0 + w - 1; const y1 = y0 + h - 1; [ x0,y0,x1,y1 ]" if $shadow_opacity>0 nx0,ny0,nx1,ny1:=$x0,$y0,$x1,$y1 shift_x,shift_y={0,round([$shadow_shift_x,$shadow_shift_y]*max(w,h)%)} sigma:=$shadow_smoothness if $shift_x>0 nx1+=$shift_x else nx0+=$shift_x fi if $shift_y>0 ny1+=$shift_y else ny0+=$shift_y fi if $sigma nx0,ny0,nx1,ny1+=3.5*$sigma*[-1,-1,1,1] fi z. {p=$nx0-$x0;q=$ny0-$y0;[p,q,p+$nx1-$nx0,q+$ny1-$ny0]} sh. 100% +b. $sigma% shift. $shift_x,$shift_y n. 0,{$shadow_opacity*255%} max[-2,-1] rm. else nx0,ny0=$x0,$y0 fi +channels. 100% sh.. 100% f. 255 rm. j[canvas] ..,$nx0,$ny0,0,0,1,.,255 rm[-2,-1] } k[0,canvas] => $nm i[0] 100%,100%,1,4 fc[0] $background_R,$background_G,$background_B,$background_A if $background_image sh[1] 100% *. {$background_image%} rm. else rm[1] fi blend alpha } fx_loose_photos_preview : _is_preview=1 fx_loose_photos $* #@gui Make Seamless [Diffusion] : fx_make_seamless, fx_make_seamless_preview(1) #@gui : Equalize Light = float(0,0,100) #@gui : sep = separator() #@gui : Preview Original = bool(0) #@gui : Tiled Preview = choice(3,"None","2x1","1x2","2x2","3x3","4x4") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: This filter helps in converting your input pattern as a seamless #@gui : (a.k.a periodic) texture."} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/24/02.") fx_make_seamless : foreach { if $1 +b {20.5-$1/50}% -[0] [1] fc. ${average_vectors.} + fi } periodize_poisson c 0,255 fx_make_seamless_preview : u,v:=arg($3,2,1,2,3,4),arg($3,1,2,2,3,4) gui_split_preview "if !$2 fx_make_seamless $* fi if $3 array "$u","$v" fi",${-3--1} #@gui Make Seamless [Patch-Based] : fx_frame_seamless, fx_frame_seamless_preview(0) #@gui : Frame Size = int(32,0,256) #@gui : Patch Size = int(9,3,64) #@gui : Blend Size = int(0,0,64) #@gui : Frame Type = choice(1,"Inner","Outer") #@gui : Equalize Light = float(100,0,100) #@gui : sep = separator() #@gui : Preview Original = bool(0) #@gui : Tiled Preview = choice(3,"None","2x1","1x2","2x2","3x3","4x4") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: This filter helps in converting your input pattern as a seamless #@gui : (a.k.a periodic) texture."} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/15/12.") fx_frame_seamless : foreach { if $5 +b {20.5-$5/50}% -[0] [1] fc. ${average_vectors.} + fi } frame_seamless ${1-4} c 0,255 fx_frame_seamless_preview : u,v:=arg($7,2,1,2,3,4),arg($7,1,2,2,3,4) gui_split_preview "if !$6 fx_frame_seamless $* fi if $7 array "$u","$v" fi",${-3--1} #@gui Ministeck : fx_ministeck, fx_ministeck_preview(1) #@gui : Number of Colors = int(8,2,24) #@gui : Resolution (px) = int(64,16,256) #@gui : Piece Size (px) = int(8,1,64) #@gui : Piece Complexity = int(2,1,10) #@gui : Relief Amplitude = float(100,0,256) #@gui : Relief Size = float(0.3,0,1) #@gui : Add 1px Outline = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/14/01.") fx_ministeck : foreach { if w>h rs {min($2,w)} else rs ,{min($2,h)} fi split_opacity l[0] { +colormap. $1 index.. . [0],[0],1,1 rand[2] 0,1 dilate[2] $4 +[0,2] r[0] $3""00%,$3""00% +g[0] xy,1 !=[-2,-1] 0 +f[0] "i(x+1,y+1)-i(x,y)" !=[-3--1] 0 -|[-3--1] z[0,-1] 0,0,{w-2},{h-2} if $7 . fi +shift. 1,1 *.. -1 +[-2,-1] b. {$6*$3/5} n. -$5,$5 map[0] [1] rm[1] +[0,-1] if $7 ==[1] 0 * fi } r. [0],[0],1,100% a c } c 0,255 fx_ministeck_preview : foreach { w,h={w},{h} fx_ministeck $* r $w,$h,1,100%,0,0,0.5,0.5 } #@gui Montage : fx_montage, fx_montage_preview(1) : * #@gui : Montage Type = choice("Auto","Custom Layout","Horizontal","Vertical","Horizontal Array","Vertical Array") #@gui : Custom Layout = text{"V(H(0,1),H(2,V(3,4)))"} #@gui : Merging Mode = choice(1,"Aligned","Scaled") #@gui : Centering / Scale = float(0.5,0,1) #@gui : Padding (px) = int(0,0,128) #@gui : sep = separator() #@gui : Frame (px) = int(0,0,128) #@gui : Frame Color = color(0,0,0,255) #@gui : sep = separator() #@gui : Angle = float(0,0,360) #@gui : Angle Variations = float(0,0,180) #@gui : sep = separator() #@gui : Cycle Layers = int(0,-255,255) #@gui : Revert Layer Order = bool() #@gui : Output As = _choice("Single Layer","Multiple Layers") #@gui : sep = separator() #@gui : note = note{"Instructions:\n #@gui : - Don't forget to set the Input layers... option on the left if you have multiple input layers #@gui : for your montage.\n #@gui : - The Custom layout parameter is only active when Montage type is set to Custom layout. #@gui : This is basically a string containing expressions such as:\n #@gui : \n . H(a,b) or V(a,b) stand respectively for an horizontal and vertical merge of two #@gui : blocks a and b. #@gui : \n . R(a), stands for a 90-deg. rotated version of a block a. Use RR(a) and #@gui : RRR(a) for resp. 180-deg and 270-deg. rotations. #@gui : \n . M(a), stands for a X-mirrored version of a block a. Use MRR(a) for a Y-mirrored #@gui : version of a.\n\n #@gui : - A block a can be a layer index or a nested montage expression itself.\n #@gui : - Layer indices start from 0 (top layer) and are treated periodically. #@gui : "} #@gui : url = link("Click here for a tutorial","https://patdavid.net/2014/05/gmic-montage.html") #@gui : url = link("+ video tutorial","http://www.youtube.com/watch?v=iM42vx22gwg") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/22/12.") fx_montage : skip "${2=A}" if !$! return fi code0=X code1="$2" code2=H code3=V code4=A code5=B if $3==1" && "$4<0.5 r {max(10,$4*200)}%,{max(10,$4*200)}%,1,100%,2 fi to_rgba if $14 rv fi if $13%$! mv[{$13%$!}--1] 0 fi if $11" || "$12 repeat $! { rotate[$>] {$11+u(-$12,$12)},1,0 } fi montage ${code$1},{!$3?$4:2+max(0,$4-0.5)},$15,\ "if $""7%2 mirror x fi if $""8%2 mirror y fi "\ "rotate {90*$""6} "\ "if $5||$6 "\ "r {max(1,$""4-2*($5+$6))},{max(1,$""5-2*($5+$6))},1,100%,2 "\ "frame $6,$6,${7-10} "\ "r {w+2*$5},{h+2*$5},1,100%,0,0,0.5,0.5 "\ "else r $""4,$""5,1,100%,2 fi " if $15 gui_autocrop_layers fi gui_set_layer_name "[Montage]" fx_montage_preview : skip "${2=A}" if !$! return fi w,h={w},{h} if $3==1" && "$4<0.5 r {max(10,$4*200)}%,{max(10,$4*200)}%,1,100%,2 fi drgba code0=X code1="$2" code2=H code3=V code4=A code5=B to_rgba if $14 rv fi if $13%$! mv[{$13%$!}--1] 0 fi if $11" || "$12 repeat $! { rotate[$>] {$11+u(-$12,$12)},1,0 } fi montage ${code$1},{!$3?$4:2+max(0,$4-0.5)},0,\ "if $""7%2 mirror x fi if $""8%2 mirror y fi rotate {90*$""6} if $5||$6 r {max(1,$""4-2*($5+$6))},{max(1,$""5-2*($5+$6))},1,100%,2 fs:=min(53,max(w,h)/3) frame $6,$6,${7-10} r {w+2*$5},{h+2*$5},1,100%,0,0,0.5,0.5 0 t. \\\#$""1,0,0,$fs,1,255 expand. xy,3 [-1]x3 a[-4--2] c dilate. {3+2*$fs/20} a.. .,c j[0] [1],{5+$5+$6},{$5+$6},0,0,1,[2],255 k[0] else r $""4,$""5,1,100%,2 fs:=min(53,max(w,h)/3) 0 t. \\\#$""1,0,0,$fs,1,255 expand. xy,3 [-1]x3 a[-4--2] c dilate. {3+2*$fs/20} a.. .,c j[0] [1],5,0,0,0,1,[2],255 k[0] fi " nw,nh:=w,h rs $w,{$h-16},2,1 drgba i[0] 100%,15,1,3,240 t[0] "Estimated size : "{round(100*$nw/$w)}%" x "{round(100*$nh/$h)}%,2,0,16 r[0] 100%,16,1,3,0 a y #@gui Puzzle : fx_puzzle, fx_puzzle_preview(1) #@gui : note = note("Pattern parameters:") #@gui : X-Tiles = int(5,2,32) #@gui : Y-Tiles = int(5,2,32) #@gui : Curvature = float(0.5,0,1.5) #@gui : Connectors Centering = float(0,0,1) #@gui : Connectors Variability = float(0,0,2) #@gui : sep = separator() #@gui : note = note("Blending parameters:") #@gui : Relief Smoothness = float(0.3,0,3) #@gui : Relief Contrast = float(100,0,255) #@gui : Outline Smoothness = float(0.2,0,3) #@gui : Outline Contrast = float(255,0,255) #@gui : sep = separator() #@gui : note = note("Recomposition parameters:") #@gui : Scale = float(100,0,150) #@gui : Scale Variations = float(0,0,100) #@gui : Angle = float(0,-180,180) #@gui : Angle Variations = float(0,0,180) #@gui : Shuffle Pieces = bool(0) #@gui : Additional Outline = bool(0) #@gui : Output Each Piece on a Different Layer = _bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/06/01.") fx_puzzle : foreach { w,h={w},{h} to_rgb puzzle $w,$h,$1,$2,$3,$4,$5 +b. $6%,0 g. xy +[-2,-1] n. -$7,$7 +[0,-1] # Relief. +b. $8%,0 n. 0,1 *. -1 +. 1 n. {(255-$9)/255},1 *[0,-1] c 0,255 # Outline. if $10!=100||$11||$12||$13||$14||$15||$16 # Decompose puzzle into set of pieces. +-. 1 label_fg. 0 +area_fg. 0,0 <. 50% -|... . ==. 0 *[-2,-1] distance.. 0 *.. -1 watershed. .. rm.. label. 0,0 repeat iM+1 { +==[1] $> coords=${autocrop_coords.\ 0} +z[0] $coords z.. $coords rv[-2,-1] *.. . *. 255 a[-2,-1] c x$>,y$>:=arg(1,$coords)+round(w/2),arg(2,$coords)+round(h/2) } rm[0,1] # Recompose puzzle. if $14 sort_list +,u fi if $16 # One piece by layer. foreach { rs ,{max(0.1,$10+$11*v)}% rotate {$12+$13*v} if $15 expand xy,1 fi cx,cy:=round([w,h]/2) sh 100% if $15 dilate. 3 fi i[0] $w,$h,1,4 j[0] ..,{${x$>}-$cx},{${y$>}-$cy},0,0,1,.,255 rm[-2,-1] } else # All pieces on the same layer. i[0] $w,$h,1,{s} repeat $!-1 { rs. ,{max(0.1,$10+$11*v)}% rotate. {$12+$13*v} if $15 expand. xy,1 fi cx,cy:=round([w,h]/2) sh. 100% if $15 dilate. 3 fi j[0] ..,{${x$<}-$cx},{${y$<}-$cy},0,0,1,.,255 rm[-2,-1] } fi else rm. fi } fx_puzzle_preview : fx_puzzle ${1-15},0 #@gui Shuffle Patches : fx_shuffle_patches, fx_shuffle_patches_preview(0)* #@gui : Mode = choice("Shuffle","Rotate","Shuffle & Rotate") #@gui : Reconstruct From = choice("Colors","Gradients","Laplacians") #@gui : Patch Size = int(64,4,512) #@gui : Overlap (%) = float(0,0,50) #@gui : Overlap Std (%) = float(20,0,100) #@gui : Random Seed = int(0,0,65535) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2022/08/08.") fx_shuffle_patches_preview : fx_shuffle_patches $* gui_crop_resize_preview fx_shuffle_patches : mode,recons,patch_size,overlap,overlap_std,seed=${1-6} foreach { nm={n} w,h={w},{h} s c foreach { avg$>,std$>:=ia,id } a c # Shuffle & rotate patches. ps:=min(w,h,$patch_size) if $recons==1 g xy,1 a c elif $recons==2 laplacian fi img2patches $ps,$overlap%,3 s z srand $seed if isin($mode,0,2) sort_list +,u fi if $mode>0 foreach { rotate {90*v(3)} } fi a z patches2img $w,$h,$overlap%,$overlap_std% if $recons==1 s c,2 g.. x,-1,2 g. y,-1,2 + ilaplacian 0 elif $recons==2 ilaplacian 0 fi # Renormalize result. s c foreach { - {ia} / {max(1e-8,id)} * ${std$>} + ${avg$>} } a c c 0,255 } #@gui Taquin : fx_taquin, fx_taquin(1) #@gui : X-Tiles = int(7,1,20) #@gui : Y-Tiles = int(7,1,20) #@gui : Remove Tile = choice("None","First","Last","Random") #@gui : sep = separator() #@gui : Relief = float(50,0,255) #@gui : Border Thickness (%) = float(5,0,100) #@gui : Border Outline = int(0,0,16) #@gui : Ouline Color = color(0,0,0,255) #@gui : sep = separator() #@gui : Random Seed = int(0,0,65535) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/13/01.") fx_taquin : to_a foreach { srand $11 taquin $1,$2,$3,$4,$5%,$6,${7-10} } #@gui Tileable Rotation : fx_rotate_tileable, fx_rotate_tileable_preview(1) #@gui : Angle = float(45,0,360) #@gui : Maximum Size Factor = int(8,0,20) #@gui : Array Mode = choice(0,"None","x-axis","y-axis","xy-axes","2xy-axes") #@gui : sep = separator() #@gui : note = note("Note: This filter implements the tileable rotation technique described by #@gui : Peter Yu, at:") #@gui : url = link("[Peter Yu] Create rotated tileable patterns", #@gui : "http://www.peteryu.ca/tutorials/gimp/rotate_tileable_patterns") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/26/05.") fx_rotate_tileable : if $3 array_mirror 1,{$3-1},1 fi rotate_tileable $1,{!$3?$2:$2/2} fx_rotate_tileable_preview : l { fx_rotate_tileable $* onfail gui_warning_preview "Invalid image size" } #@gui Tiled Rotation : fx_rotate_tiles, fx_rotate_tiles(1) #@gui : X-Tiles = int(5,1,80) #@gui : Y-Tiles = int(5,1,80) #@gui : Angle = float(15,0,360) #@gui : X-Shadow = float(3,-20,20) #@gui : Y-Shadow = float(3,-20,20) #@gui : Smoothness = float(1.8,0,5) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_rotate_tiles : to_rgba rotate_tiles $3,$1,$2 drop_shadow $4%,$5%,$6% #@gui Tiled Normalization : fx_normalize_tiles, fx_normalize_tiles(1) #@gui : X-Tiles = int(25,1,80) #@gui : Y-Tiles = int(25,1,80) #@gui : Minimal Value = float(0,0,255) #@gui : Maximal Value = float(255,0,255) #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") _fx_normalize_tiles : repeat $! { l. { split_tiles $1,$2 n $3,$4 append_tiles $1,$2 } mv. 0 } fx_normalize_tiles : ac "_fx_normalize_tiles ${1-4}",$-1 #@gui Tiled Random Shifts : fx_shift_tiles, fx_shift_tiles(1) #@gui : X-Tiles = int(10,1,30) #@gui : Y-Tiles = int(10,1,30) #@gui : Amplitude = float(10,0,100) #@gui : Opacity = float(1,0,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_shift_tiles : to_rgba shift_tiles $1,$2,$3 if $4<1 repeat $! { s. c *. $4 a[-4--1] c mv. 0 } fi #@gui Tiled Parameterization : fx_parameterize_tiles, fx_parameterize_tiles(1) #@gui : X-Tiles = int(10,1,30) #@gui : Y-Tiles = int(10,1,30) #@gui : Fitting Function = choice("Linear","Quadratic") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_parameterize_tiles : if $3 quadratize_tiles $1,$2 else linearize_tiles $1,$2 fi c 0,255 #@gui Tiled Isolation : fx_isolate_tiles, fx_isolate_tiles(0) #@gui : X-Size = float(10,0,100) #@gui : Y-Size = float(10,0,100) #@gui : X-Border = float(5,0,100) #@gui : Y-Border = float(5,0,100) #@gui : Keep Tiles Square = bool(1) #@gui : Keep Borders Square = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/13/04.") fx_isolate_tiles : foreach { to_rgba if $5 sx,sy:=round(min(w,h)*max($1,$2)/100) else sx,sy:=round(w*$1/100),round(h*$2/100) fi if $6 bx:=max($3,$4) by=$bx else bx,by=$3,$4 fi s x,-$sx foreach { s y,-$sy r 100%,{100+$by}%,1,100%,0,0,0.5,0.5 a y } r {100+$bx}%,100%,1,100%,0,0,0.5,0.5 a x } #@gui ____Artistic #------------------------- #@gui Bokeh : fx_bokeh, fx_bokeh_preview(1) #@gui : Number of Scales = int(3,1,10) #@gui : Shape = choice(8,"Triangle","Square","Diamond","Pentagon","Hexagon","Octogon","Decagon","Star","Circular") #@gui : Random Seed = int(0,0,65535) #@gui : sep = separator() #@gui : note = note{"Starting parameters:"} #@gui : Density = int(30,1,256) #@gui : Radius (%) = float(8,0,50) #@gui : Outline (%) = float(4,0,100) #@gui : Inner Shade = float(0.3,0,1) #@gui : Smoothness = float(0.2,0,8) #@gui : Color = color(210,210,80,160) #@gui : Color Dispersion = float(0.7,0,1) #@gui : sep = separator() #@gui : note = note{"Ending parameters:"} #@gui : Density = int(30,1,256) #@gui : Radius (%) = float(20,0,50) #@gui : Outline (%) = float(20,0,100) #@gui : Inner Shade = float(1,0,1) #@gui : Smoothness = float(2,0,8) #@gui : Color = color(170,130,20,110) #@gui : Color Dispersion = float(0.15,0,1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/02/07.") fx_bokeh : _shape=$2 srand $3 foreach { nm=${-gui_layer_name} pos=${-gui_layer_pos} 100%,100%,1,3 (${4-13};${14-23}) if $1>2 r. 100%,$1,1,1,3 fi repeat $1 { +rows. $> _fx_bokeh... {^} rm. } rm. => "name("$nm"),opacity(100),mode(screen),pos("$pos")" rv if !0$_output_mode gui_merge_layers elif 0$_output_mode<2 rm. fi } _fx_bokeh0 : shape_star $1,3 # Triangle. _fx_bokeh1 : $1,$1,1,1,1 # Square. _fx_bokeh2 : shape_diamond $1 # Diamond. _fx_bokeh3 : shape_polygon $1,5,45 # Pentagon. _fx_bokeh4 : shape_polygon $1,6,45 # Hexagon. _fx_bokeh5 : shape_polygon $1,8,45 # Octogon. _fx_bokeh6 : shape_polygon $1,10,45 # Decagon. _fx_bokeh7 : shape_star $1,5 # Star. _fx_bokeh8 : shape_circle $1 # Circle. fx_bokeh_preview : gui_split_preview "_output_mode=0 fx_bokeh $*",${-3--1} # [Internal] Draw one step of bokeh with specified geometry on last image. # $1=density, $2=size(in %), $3=outline(in %), $4=shade (in [0,1]), $5=smoothness (in %), # $6,$7,$8,$9=RGBA (in [0,255]), $10 = color dispersion (in [0,1]) _fx_bokeh : radius1:=r=max(w,h)*$2%;r+1-(r%2) radius2:=r=$radius1-($radius1*$3%);r+1-(r%2) random3d $1 *3d. {-2,w},{-2,h},0 _fx_bokeh$_shape $radius1 if $radius2>=1 _fx_bokeh$_shape $radius2 ri. ..,0,0,0.5,0.5 *. {max(0,min(1,1-$4))} -[-2,-1] fi sigma={-3,$5%*w} r. {w+5*$sigma},{h+5*$sigma},1,1,0,0,0.5,0.5 b. $sigma,0 n. 0,255 sprites3d[1] [2],1 rm[2] l. { s3d r.. 3,{-2,h/3},1,1,-1 s.. x d:=$10*255 rand[-4] {$6-$d},{$6+$d} rand... {$7-$d},{$7+$d} rand.. {$8-$d},{$8+$d} a[-4--2] x c.. 0,255 y a y } j3d[0] [1],0,0,0,{$9/255},1,0,0 rm[1] #@gui Brushify : fx_brushify, fx_brushify_preview(0) #@gui : note = note("Brush parameters:") #@gui : Shape = choice(7,"Bottom layer","Top layer","Rectangle","Diamond","Pentagon","Hexagon","Octogon", #@gui : "Ellipse","Gaussian","Star","Heart") #@gui : Ratio = float(0.25,0,1) #@gui : Number of Sizes = int(4,1,16) #@gui : Maximal Size = int(64,1,128) #@gui : Minimal Size (%)= float(25,0,100) #@gui : Number of Orientations = int(12,1,24) #@gui : Fuzzyness = float(0,0,10) #@gui : Smoothness = float(2,0,10) #@gui : Light Type = choice(4,"None","Flat","Darken","Lighten","Full") #@gui : Light Strength = float(0.2,0,1) #@gui : Opacity = float(0.5,0,1) #@gui : sep = separator() #@gui : note = note("Painting parameters:") #@gui : Density (%) = float(30,0,100) #@gui : Contour Coherence = float(1,0,1) #@gui : Orientation Coherence = float(1,0,1) #@gui : Gradient Smoothness = float(1,0,10) #@gui : Structure Smoothness = float(5,0,10) #@gui : Primary Angle = float(0,-180,180) #@gui : Angle Dispersion = float(0.2,0,1) #@gui : sep = separator() #@gui : Preview Brush = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/22/04.") fx_brushify : _fx_brushify $* s0=0--3 s1=1--2 s2=0--2 l[${s{!$1?0:$1==1?1:2}},-1] { brushify[^-1] .,$3,{$5%},$6,$9,$10,$11,$12%,$13,$14,$15,$16,$17,$18 rm. } _fx_brushify : # Insert brush at the end of the list N:=0.9*$4 if !$1 +autocrop. i.. 100%,100%,1,3,1 blend[-2,-1] alpha rs. $N,$N,3 elif $1==1 +autocrop[0] i.. 100%,100%,1,3,1 blend[-2,-1] alpha rs. $N,$N,3 elif $1==2 $4,$4 rectangle. 10%,10%,90%,90%,1,1 elif $1==3 shape_diamond. $N elif $1==4 shape_polygon $N,5 elif $1==5 shape_polygon $N,6 elif $1==6 shape_polygon $N,8 elif $1==7 shape_circle. $N elif $1==8 $4,$4 gaussian. 30%,30%,0 elif $1==9 shape_star $N elif $1==10 shape_heart $N fi norm. r. 100%,{max(0.01,100*$2)}%,1,1,2 r. $4,$4,1,1,0,0,0.5,0.5 spread. $7 b. $8% n. 0,1 fx_brushify_preview : if $1<2" && "$!<2 gui_error_preview "When a custom brush (bottom or top layer) is specified, at least two layers are required for this filter to work.In this case, don't forget to set the 'Input layers' option!" return fi fx_brushify $* if $19 _fx_brushify $* if !$1 rm.. elif $1==1 rm[0] fi rs. {0,max(1,w/5)},{0,max(1,h/5)},2 n. 0,255 frame. 3,3,0 frame. 1,1,255 frame. 1,1,0 to_rgb. to. "Brush",4,2,13,2,1,255,255,0 to_a. j[^-1] .,2,2 rm. else if !$1 rm. elif $1==1 rm[0] fi fi #@gui Cartoon : cartoon, fx_cartoon_preview(0) #@gui : Smoothness = float(3,0,10) #@gui : Sharpening = float(200,0,400) #@gui : Edge Threshold = float(20,1,30) #@gui : Edge Thickness = float(0.25,0,1) #@gui : Color Strength = float(1.5,0,3) #@gui : Color Quantization = int(8,2,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_cartoon_preview : gui_split_preview "cartoon $*",${-3--1} #@gui Circle Abstraction : fx_circle_abstraction, fx_circle_abstraction_preview(1) #@gui : Number of Colors = int(8,2,16) #@gui : Density = int(5,1,100) #@gui : Opacity = float(0.8,0,1) #@gui : Smoothness = float(0,0,4) #@gui : Filled Circles = bool(1) #@gui : Fill Transparent Holes = bool(1) #@gui : Normalize Colors = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/16/06.") fx_circle_abstraction : foreach { b $4% +colormap $1 index[0] [1],0,0 [0],[0],1,4,0 repeat $1 { rprogress {$>*100/$1} +==[0] $> skeleton3d. 2,2,0,1,0 if {@7} # Process only non empty objects. s3d. l[-6--1] { r[2] 3,{2,h/3},1,1,-1 1,{2,h/2*$2%},1,1,1 r. 1,{2,h/2},1,1,4 r. 3,200% *[2,-1] y } a[-6--1] y col3d. {1,I($>)} [0],[0],1,4,0 j3d. ..,0,0,0,1,{1+$5},0,0 sh. 3 col3d... 255 j3d. ...,0,0,0,$3,{1+$5},0,0 rm. rm.. blend[2,-1] alpha fi } k[2] if $6 +channels 3 <. 1 inpaint[0] [1],0,1 rm. channels. 0,2 fi if $7 n 0,255 fi rprogress 100 } fx_circle_abstraction_preview : gui_split_preview "fx_circle_abstraction $*",${-3--1} #@gui Cubism : fx_cubism, fx_cubism_preview(1) #@gui : Iterations = int(2,0,10) #@gui : Density = float(50,0,200) #@gui : Thickness = float(10,0,50) #@gui : Angle = float(90,0,360) #@gui : Opacity = float(0.7,0.01,1) #@gui : Smoothness = float(0,0,5) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/05/06.") fx_cubism : repeat $1 { cubism ${2--1} } fx_cubism_preview : gui_split_preview "fx_cubism $*",${-3--1} #@gui Cutout : fx_cutout, fx_cutout_preview(1) #@gui : Number of Levels = int(4,2,32) #@gui : Edge Simplicity = float(0.5,0,3) #@gui : Edge Fidelity = int(4,0,10) #@gui : Normalize = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé and Garagecoder #@gui :       Latest Update: 2014/03/06.") fx_cutout : foreach { split_opacity l[0] { median {10-$3} quantize $1 +luminance. round. area. 0,1 med:=ic rm. inpaint_holes {$med*$2%},0,1 if $4 n 0,255 fi } a c } fx_cutout_preview : gui_split_preview "fx_cutout $*",${-3--1} #@gui Doodle : fx_doodle, gui_no_preview(0) #@gui : Precision (%) = float(30,0,100) #@gui : Smoothness = float(2,0,10) #@gui : Coherence = float(2,0,10) #@gui : Contour Threshold = float(1.5,0,10) #@gui : Spacing = int(2,0,20) #@gui : Minimal Stroke Length = float(70,0,255) #@gui : Preview Progression While Running = _bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/07/08.") fx_doodle : skip "$*" foreach { b $2 n 0,255 structuretensors b $3 100%,100%,1,1,100 if $7 w. ${"fitscreen ."},"[G'MIC] Doodle" fi _stopflag=0 eval " const dt = 0.5; nb_strokes = nb_consecutive_fails = 0; while (nb_consecutive_fails<500*$1%, run('+store. canvas'); P0 = u([w,h] - 1); length = 0; repeat (2,dir, P = P0; oiP = iP = round(P); C = I(#0,P); E = eig([ C[0], C[1], C[1], C[2] ]); oV = V = (dir?1:-1)*E[4,2]; while (E[0]>$4^2 && (i(iP)>$5 || oiP==iP), i(iP) = 0; oiP = iP; P+=dt*V; iP = round(P); C = I(#0,P,1); E = eig([ C[0], C[1], C[1], C[2] ]); V = E[4,2]; dot(oV,V)<0?(V*=-1); oV = V; iP!=oiP?++length; ); ); length<$6?(run('rm. $canvas'); ++nb_consecutive_fails):( !(++nb_strokes%15)?run('distance. 0'); nb_consecutive_fails = 0; $7 && !(nb_strokes%30)?( nb_consecutive_fails = run('if !{*} u inf else +neq. 0 r. {*,w,h},1,1,2 w. rm. u 0 fi') ); ) )" k. != 0 * 255 } #@gui Diffusion Tensors : fx_diffusiontensors, fx_diffusiontensors_preview(0) #@gui : Resolution (%) = float(10,0,20) #@gui : Size = float(5,0,16) #@gui : Color Mode = choice(3,"Monochrome","Grayscale","Orientation","Color") #@gui : Outline = int(1,0,16) #@gui : sep = separator() #@gui : Sharpness = float(0.15,0,1) #@gui : Anisotropy = float(1,0,1) #@gui : Gradient Smoothness = float(0,0,10) #@gui : Tensor Smoothness = float(3,0,10) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/19/10.") fx_diffusiontensors : foreach { wh:=w,h +diffusiontensors ${5-8} rs. {max(1,w*$1%)} dt. {round(125/max(1,$1))},$2,{$3<3?$3:0},$4 if $3==3 remove_opacity.. r.. .,.,1,100%,3 blend[0] [1],shapeaverage0 dilate. {1+2*$4} a c else k. fi r. $wh,1,100%,2 } fx_diffusiontensors_preview : gui_split_preview "fx_diffusiontensors $*",${-3--1} #@gui Ellipsionism : fx_ellipsionism, fx_ellipsionism_preview(0) #@gui : Primary Radius = float(20,1,100) #@gui : Secondary Radius = float(10,1,100) #@gui : Smoothness = float(0.5,0,10) #@gui : Opacity = float(0.7,0,1) #@gui : Outline = float(3,1,3) #@gui : Density = float(0.5,0.1,2) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_ellipsionism : ellipsionism ${^0} fx_ellipsionism_preview : gui_split_preview "fx_ellipsionism $*",${-3--1} #@gui Felt Pen : fx_feltpen, fx_feltpen_preview(0) #@gui : Amplitude = float(300,0,4000) #@gui : Density = float(50,0,100) #@gui : Smoothness = float(1,0,10) #@gui : Opacity = float(0.1,0,1) #@gui : Edge = float(20,0,100) #@gui : Thickness = int(5,2,32) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/25/10.") fx_feltpen : foreach { +fx_hardsketchbw ${1-5},0,0 blend hardlight erode_oct $6 } fx_feltpen_preview : gui_split_preview "fx_feltpen $*",${-3--1} #@gui Fractalize : fractalize, fractalize(1) #@gui : Detail Level = float(0.8,0,1) #@gui : sep = separator() #@gui : note = note("Note: This filter uses lot of random values to generate its result, #@gui : so running it twice will give you different results !") #@gui : sep = separator() #@gui : url = link("Click here for a detailed description of this filter.",\ # "http://www.gimpchat.com/viewtopic.php?f=28&t=10036") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/25/04.") #@gui Ghost : fx_ghost, fx_ghost_preview(0) #@gui : note = note("Ghost Effect:") #@gui : Amplitude = float(200,0,1000) #@gui : Smoothness = float(2,0,10) #@gui : Coherence = float(2,0,10) #@gui : Gamma = float(1,-3,3) #@gui : sep = separator() #@gui : note = note("Normalization:") #@gui : Amplitude = float(3,0,10) #@gui : Radius = float(16,1,64) #@gui : Invert = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/01/30.") fx_ghost : foreach { split_opacity l[0] { diffusiontensors. 1,1,$2,$3 eigen. compose_channels.. + sqrt.. n.. 1,100 ^.. $4 n.. 0,$1 100%,100% eval.. "* ca = i(x,y,0,0); sa = i(x,y,0,1); N = i(#-3,x,y); x0 = x + N*ca; y0 = y + N*sa; x1 = x - N*ca; y1 = y - N*sa; polygon(#-1,2,x0,y0,x1,y1,-1,1); I" k. normalize_local $5,$6 n 0,255 if $7 negate fi } a c } fx_ghost_preview : gui_split_preview "fx_ghost $*",${-3--1} #@gui Hard Sketch : fx_hardsketchbw, fx_hardsketchbw_preview(0) #@gui : Amplitude = float(300,0,4000) #@gui : Density = float(50,0,100) #@gui : Smoothness = float(1,0,10) #@gui : Opacity = float(0.1,0,1) #@gui : Edge = float(20,0,100) #@gui : Fast Approximation = bool(0) #@gui : Color Model = choice(4,"Black on white","White on black","Black on transparent white", #@gui : "White on transparent black","Color on white") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_hardsketchbw : b $3 if $7==4 foreach { +hardsketchbw $1,$2,$4,$5,$6 blend hardlight } return fi hardsketchbw $1,$2,$4,$5,$6 if $7&1 negate fi if $7==2 r 100%,100%,1,4 repeat $! { sh[$>] 3 *. -2 +. {2*255} c. 0,255 rm. } elif $7==3 r 100%,100%,1,4 repeat $! { sh[$>] 3 *. 2 c. 0,255 rm. } fi fx_hardsketchbw_preview : gui_split_preview "fx_hardsketchbw $*",${-3--1} #@gui Highlight Bloom : fx_highlight_bloom, fx_highlight_bloom_preview(0) #@gui : Details Strength (%) = float(90,0,400) #@gui : Details Scale = float(60,0,255) #@gui : Smoothness = float(60,0,255) #@gui : Highlight (%) = int(30,0,100) #@gui : Contrast (%) = float(20,0,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/24/10.") #@gui : sep = separator() #@gui : note = note("This effect has been inspired by:") #@gui : url = link("This tutorial by Sebastien Guyader and Patrick David", #@gui : "https://pixls.us/articles/highlight-bloom-and-photoillustration-look/") fx_highlight_bloom : foreach { split_opacity l[0] { +smooth $2,0.3,0.8,1,2 -.. . amp=$3 do smooth. {min(50,$amp)},0.1,1,1,2 amp-=50 while $amp>0 +retinex. 16,lab,0,15 j.. .,0,0,0,0,{$4%} rm. ac. "normalize_local {$5%},5",lab_l *.. {$1%} + c 0,255 } a c } fx_highlight_bloom_preview : gui_split_preview "fx_highlight_bloom $*",${-3--1} #@gui Hope Poster : fx_poster_hope, fx_poster_hope_preview(0)+ #@gui : Gamma = float(0,-3,3) #@gui : Smoothness = float(3,0,20) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/07/11.") fx_poster_hope : foreach { split_opacity l[0] { apply_gamma {10^$1} poster_hope $2 } a c } fx_poster_hope_preview : gui_split_preview "fx_poster_hope $*",${-3--1} #@gui Hough Sketch : fx_houghsketchbw, fx_houghsketchbw_preview(0) #@gui : Smoothness = float(1.25,0,10) #@gui : Density (%) = float(10,0,100) #@gui : Radius = int(5,0,30) #@gui : Threshold = float(80,0,100) #@gui : Opacity = float(0.1,0,1) #@gui : Color Model = choice(4,"Black on white","White on black","Black on transparent white", #@gui : "White on transparent black","Color on white") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/05/29.") fx_houghsketchbw : b $1 n 0,255 if $6==4 foreach { +houghsketchbw ${2-5} blend hardlight } return fi houghsketchbw ${2-5} if $6&1 negate fi if $6==2 r 100%,100%,1,4 repeat $! { sh[$>] 3 *. -2 +. {2*255} c. 0,255 rm. } elif $6==3 r 100%,100%,1,4 repeat $! { sh[$>] 3 *. 2 c. 0,255 rm. } fi fx_houghsketchbw_preview : gui_split_preview "fx_houghsketchbw $*",${-3--1} #@gui Kuwahara : fx_kuwahara, fx_kuwahara_preview(0) #@gui : Iterations = int(2,1,20) #@gui : Radius = int(5,1,30) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/31/05.") fx_kuwahara : ac "repeat $1 { kuwahara $2 }",$3,$4 fx_kuwahara_preview : gui_split_preview "fx_kuwahara $*",${-3--1} #@gui Linify : fx_linify, fx_linify_preview(0) #@gui : Density = float(40,0,100) #@gui : Spreading = float(2,0,10) #@gui : Resolution (%) = float(40,0,100) #@gui : Line Opacity = float(10,0,30) #@gui : Line Precision = int(24,1,128) #@gui : Color Mode = choice(0,"Subtractive","Additive") #@gui : sep = separator() #@gui : Preview Progression While Running = _bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note:\n\n #@gui : - This filter is our own implementation of the nice algorithm proposed on the webpage #@gui : http://linify.me.\n #@gui : - This is a quite resource-demanding filter, so please be patient when running it.\n #@gui : - It actually renders better when applied on small images (<1024). #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/11/21.") fx_linify : foreach { if $7 _debug=1 w ${-fitscreen\ {[w,h]}},0,"[Preview] G'MIC: Linify" fi linify $1,$2,$3%,$4,$5,$6 } fx_linify_preview : gui_split_preview "fx_linify ${1-6},0",${-3--1} #@gui Lylejk's Painting : fx_lylejk_painting, fx_lylejk_painting_preview(0) #@gui : Iterations = int(10,1,20) #@gui : Abstraction = int(2,1,20) #@gui : Radius = int(4,1,30) #@gui : Canvas = float(10,0,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: Lyle Kroll and David Tschumperlé. #@gui :       Latest Update: 2015/23/02.") #@gui : url = link("Filter Explained here","http://www.gimpchat.com/viewtopic.php?f=10&t=2624") fx_lylejk_painting : foreach { nm={n} +l { repeat $1 { b 0.75 unsharp 0.75,10.49 c 0,255 mv. 0 } } smooth. 300,0.26,1,0,7 . rv[-3--1] blend[-2,-1] lighten,0.5 blend[-2,-1] grainmerge,1 fx_kuwahara. $2,$3,0,0 texturize_canvas. $4,4 => $nm } fx_lylejk_painting_preview : gui_split_preview "fx_lylejk_painting $*",${-3--1} #@gui Paint With Brush : fx_paint_with_brush, fx_paint_with_brush_preview(0) #@gui : Predefined style = choice("Default","Felt Spots","Colored Edges","Circles","Dreamy","Fuzzy", #@gui : "Whirls","Smooth") #@gui : previous_style = value(-1) #@gui : sep = separator() #@gui : note = note("Global Settings:") #@gui : Painting Order = choice(1,"Random","Coarse to Fine","Fine to Coarse") #@gui : Number of Iterations = int(16,1,128) #@gui : Precision (%) = float(30,0,100) #@gui : Details (%) = float(100,0,100) #@gui : Background (%) = float(100,0,100) #@gui : Sharpness (%) = float(10,0,100) #@gui : Anisotropy (%) = float(80,0,100) #@gui : Smoothness = float(0.5,0,8) #@gui : Coherence = float(3,0,16) #@gui : Twist Angle (°) = int(45,-1,360) #@gui : note = note{"(-1 means 'Random Angle')."} #@gui : Twist strength (%) = int(0,-1,100) #@gui : note = note{"(-1 means 'Angle Shift')."} #@gui : Init Canvas = choice(6,"Black","Gray","White","Self","Blur","Kuwahara","Vector Painting") #@gui : sep = separator() #@gui : note = note("Brush for Details:") #@gui : Brush Diameter (px) = float(2,0,100) #@gui : Stroke Length (px) = float(10,0,100) #@gui : Hue Randomness (%) = float(0,0,100) #@gui : Saturation Randomness (%) = float(0,0,100) #@gui : Value Randomness (%) = float(0,0,100) #@gui : Opacity (%) = float(60,0,100) #@gui : sep = separator() #@gui : note = note("Brush for Background:") #@gui : Brush Diameter (px) = float(20,0,100) #@gui : Stroke Length (px) = float(1,0,100) #@gui : Hue Randomness (%) = float(0,0,100) #@gui : Saturation Randomness (%) = float(0,0,100) #@gui : Value Randomness (%) = float(0,0,100) #@gui : Opacity (%) = float(30,0,100) #@gui : sep = separator() #@gui : note = note("Brush Dynamics:") #@gui : Brush Diameter = float(15,0,255) #@gui : Stroke Length = float(15,0,255) #@gui : Hue Randomness = float(15,0,255) #@gui : Saturation Randomness = float(15,0,255) #@gui : Value Randomness = float(15,0,255) #@gui : Opacity = float(15,0,255) #@gui : Spatial Step = float(1,0,3) #@gui : Angular Step (°) = float(45,0,90) #@gui : sep = separator() #@gui : Preview Progression While Running = _bool(0) #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/08/30.") fx_paint_with_brush : predefined_style,previous_style,\ painting_order,nb_iterations,precision,details_threshold,background_threshold,\ sharpness,anisotropy,smoothness,coherence,twist_angle,twist_strength,init_canvas,\ brush_diameter_details,stroke_length_details,\ hue_randomness_details,saturation_randomness_details,value_randomness_details,opacity_details,\ brush_diameter_background,stroke_length_background,\ hue_randomness_background,saturation_randomness_background,value_randomness_background,opacity_background,\ brush_diameter_dynamics,stroke_length_dynamics,\ hue_randomness_dynamics,saturation_randomness_dynamics,value_randomness_dynamics,opacity_dynamics,\ spatial_step,angular_step,preview_progression,preview_type,preview_splitx,preview_splity=${1-38} # Define parameters for predefined styles. style0=1,16,50,100,100,10,80,0.5,3,0,0,6,2,10,0,0,0,60,20,1,0,0,0,30,15,15,15,15,15,15,1,45 style1=1,16,4,100,100,1,30,0.5,3,0,0,2,3,1,0,0,0,80,10,1,0,0,0,30,8,15,15,15,15,15,1,45 style2=2,16,30,100,20,10,100,0.5,3,45,0,4,4,10,80,0,0,60,20,1,0,0,0,30,10,15,15,15,15,15,1,45 style3=1,16,6.6,100,100,0,0,1.7,3,0,0,2,3,1,0,0,50,80,30,1,0,0,50,20,15,15,15,15,15,15,1,45 style4=1,16,50,100,80,5,80,0.5,1.5,45,0,4,1,20,0,0,0,10,5,1,0,0,0,30,15,15,15,15,15,15,1,45 style5=0,16,10,100,100,10,80,0.5,3,135,50,6,1,20,0,0,0,90,3,10,0,0,0,30,0,15,15,15,15,15,1,45 style6=1,16,30,100,100,0,95,0.5,8,-1,-1,2,2,30,0,0,0,100,20,1,0,0,0,30,0,0,0,0,0,0,1,45 style7=1,16,50,100,100,10,80,0.5,3,0,0,6,1,10,0,0,0,6,20,1,0,0,0,100,17.34,15,15,15,15,20,1,45 if $predefined_style!=$previous_style" && "$previous_style>=0 painting_order,nb_iterations,precision,details_threshold,background_threshold,\ sharpness,anisotropy,smoothness,coherence,twist_angle,twist_strength,init_canvas,\ brush_diameter_details,stroke_length_details,\ hue_randomness_details,saturation_randomness_details,value_randomness_details,opacity_details,\ brush_diameter_background,stroke_length_background,\ hue_randomness_background,saturation_randomness_background,value_randomness_background,opacity_background,\ brush_diameter_dynamics,stroke_length_dynamics,\ hue_randomness_dynamics,saturation_randomness_dynamics,value_randomness_dynamics,opacity_dynamics,\ spatial_step,angular_step=${style$predefined_style} fi # Process images. foreach { to_color # Determine painting geometry. +diffusiontensors {[$sharpness,$anisotropy]%},$smoothness,$coherence if $twist_strength>0 # Attract to twist angle if $twist_angle>=0 f. "begin(Id = eye(2)); T = [ i0,i1,i1,i2 ]; eig = eig(T); const ang = $twist_angle°; U = lerp(eig[2,2],[ cos(ang),sin(ang) ],$twist_strength%); U/=max(1e-8,norm(U)); UUt = mul(U,U,2); T = eig[0]*UUt + eig[1]*(Id - UUt); [ T[0],T[1],T[3] ]" else 100%,100%,1,2 rand. -1,1 b. $coherence orientation. f.. "begin(Id = eye(2)); T = [ i0,i1,i1,i2 ]; eig = eig(T); U = lerp(eig[2,2],I(#-1),$twist_strength%); UUt = mul(U,U,2); T = eig[0]*UUt + eig[1]*(Id - UUt); [ T[0],T[1],T[3] ]" rm. fi elif $twist_strength<0 # Shift with twist angle if $twist_angle>=0 f. "R = rot($twist_angle°); Rt = transpose(R,2); T = mul(mul(Rt,[ i0,i1,i1,i2 ],2),R,2); [ T[0],T[1],T[3] ]" else 100%,100%,1,2 rand. -1,1 b. $coherence orientation. f.. "ang = atan2(i(#-1,x,y,0,1),i(#-1,x,y,0,0)); R = rot(ang); Rt = transpose(R,2); T = mul(mul(Rt,[ i0,i1,i1,i2 ],2),R,2); [ T[0],T[1],T[3] ]" rm. fi fi # Compute contour map and sequence of brush strokes. +gradient_norm.. 100%,100%,1,2,[x,y] a[-2,-1] c r. {wh},1,1,3,-1 sort. +,x r. {max(0.01,$precision)}%,1,1,3 z. {(100-$background_threshold)*$details_threshold%}%,$details_threshold% if !$painting_order 100% rand. 0,100 rv[-2,-1] a[-2,-1] c sort. +,x channels. 1,100% elif $painting_order==2 mirror. x fi # Initialize canvas. arg0 $init_canvas,"+f[0] 0","+f[0] 128","+f[0] 255","[0]","+b[0] 1%","+kuwahara[0] 10","+fx_vector_painting[0] 9" run ${} # Paint brush strokes. ind=0 repeat inf { t=$> progress {$ind*100/w#2} 100%,100%,1,{s#0+1} nb_strokes:=max(1,w#2/$nb_iterations) $nb_strokes,1,1,1,:${-math_lib}" begin(color_random = vector(#s#0)); lambda(tau) = min(1,edge/(1e-8+tau)); const brush_diameter_background = $brush_diameter_background; const brush_diameter_details = $brush_diameter_details; const brush_diameter_dynamics = $brush_diameter_dynamics; const stroke_length_background = $stroke_length_background; const stroke_length_details = $stroke_length_details; const stroke_length_dynamics = $stroke_length_dynamics; const opacity_background = 255*$opacity_background%; const opacity_details = 255*$opacity_details%; const opacity_dynamics = $opacity_dynamics; const hue_randomness_background = $hue_randomness_background%; const hue_randomness_details = $hue_randomness_details%; const hue_randomness_dynamics = $hue_randomness_dynamics; const saturation_randomness_background = $saturation_randomness_background%; const saturation_randomness_details = $saturation_randomness_details%; const saturation_randomness_dynamics = $saturation_randomness_dynamics; const value_randomness_background = $value_randomness_background%; const value_randomness_details = $value_randomness_details%; const value_randomness_dynamics = $value_randomness_dynamics; const spatial_step = max($spatial_step,0.1); const angular_step = max($angular_step,1); stroke = $ind + x; stroke=w#2 break fi } k. } # Set style parameters if new style has been selected. if $predefined_style!=$previous_style u "{"$predefined_style"}{"$predefined_style"}"\ "{"$painting_order"}{"$nb_iterations"}{"$precision"}{"$details_threshold"}{"$background_threshold"}"\ "{"$sharpness"}{"$anisotropy"}{"$smoothness"}{"$coherence"}{"$twist_angle"}{"$twist_strength"}{"$init_canvas"}"\ "{"$brush_diameter_details"}{"$stroke_length_details"}"\ "{"$hue_randomness_details"}{"$saturation_randomness_details"}{"$value_randomness_details"}{"$opacity_details"}"\ "{"$brush_diameter_background"}{"$stroke_length_background"}"\ "{"$hue_randomness_background"}{"$saturation_randomness_background"}{"$value_randomness_background"}"\ "{"$opacity_background"}"\ "{"$brush_diameter_dynamics"}{"$stroke_length_dynamics"}"\ "{"$hue_randomness_dynamics"}{"$saturation_randomness_dynamics"}{"$value_randomness_dynamics"}"\ "{"$opacity_dynamics"}"\ "{"$spatial_step"}{"$angular_step"}{"$preview_progression"}{"$preview_type"}{"$preview_splitx,$preview_splity"}" fi fx_paint_with_brush_preview : _is_preview=1 gui_split_preview "fx_paint_with_brush $*",${-3--1} #@gui Painting : fx_painting, fx_painting_preview(0)+ #@gui : Abstraction = int(5,1,10) #@gui : Details Scale = float(2.5,0,5) #@gui : Color = float(1.5,0,4) #@gui : Smoothness = float(50,0,1000) #@gui : Sharpen Shades = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: Lyle Kroll, Angelo Lama and David Tschumperlé. #@gui :       Latest Update: 2011/02/28.") fx_painting : skip ${4=0},${5=0} foreach { to_colormode {max(3,s)} split_opacity rv repeat $1 { fx_normalize_local. 10,6,5,20,1,11 } fx_smooth_anisotropic. {100*$2},0.2,1,$2,{2*$2},0.8,90,2,0,1,1,2,1,16 fx_mix_lab. 1,0,0,$3,0,0.5,$3,0,0.5,0,2,0 if $5 fx_segment_watershed. 10,1,0 fi smooth. $4,0,1,1,1 rv a c } fx_painting_preview : gui_split_preview "fx_painting $*",${-3--1} #@gui Pen Drawing : fx_pen_drawing, fx_pen_drawing_preview(0)+ #@gui : Amplitude = float(10,0,30) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_pen_drawing : drawing $1 fx_pen_drawing_preview : gui_split_preview "fx_pen_drawing $*",${-3--1} #@gui Polygonize [Delaunay] : fx_polygonize_delaunay, fx_polygonize_delaunay_preview(0) #@gui : Density (%) = float(40,0,100) #@gui : Edges = float(5,0,100) #@gui : Boundaries (%) = float(75,0,100) #@gui : Smoothness = float(0.5,0,8) #@gui : Filling = choice(3,"Black","White","Random","Average","Linear") #@gui : Outline (%) = float(50,0,100) #@gui : Outline Color = color(0,0,0,255) #@gui : Anti-Aliasing = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/06/05.") fx_polygonize_delaunay : foreach { to_rgba wh:=w,h if $11 r 150%,150%,1,100%,3 fi # Compute Delaunay triangulation. mM:=im,iM b $4 n $mM +to_rgb. gradient_norm. ge. $2 f. "!x || !y || x==w-1 || y==h-1?(u^0.25<$3%):(u^0.25<$1%?i:0)" {is+1},1,1,2 f.. ">begin(p = 0); i?(I[#-1,++p] = [x,y];p):0" delaunay.. 0 # Render filling. if $5==4 # Linear +f[0] 0 f... "*i?( p = I(#1); P0 = I[#2,p[0]]; P1 = I[#2,p[1]]; P2 = I[#2,p[2]]; W = solve([P0[0],P1[0],P2[0],P0[1],P1[1],P2[1],1,1,1],[x,y,1]); I(#-1) = cut(W[0]*I(#0,P0) + W[1]*I(#0,P1) + W[2]*I(#0,P2),0,255); );I" elif $5==3 # Average +f[0] 0 f... "*i?( p = I(#1); P0 = I[#2,p[0]]; P1 = I[#2,p[1]]; P2 = I[#2,p[2]]; I(#-1) = (I(#0,P0) + I(#0,P1) + I(#0,P2))/3; );I" elif $5==2 # Random +norm[1] label_fg. 0 {iM+1},1,1,3 rand. 0,255 to_rgba. point. 0 map.. . rm. else # Black or white +norm[1] !=. 0 *. 255 channels. -3,0 if $5 sh. 0,2 f. 255 rm. fi fi rm[0,2] # Render outlines. if $6 norm[0] f[0] "i!=j(1) || i!=j(0,1)" # thinning[0] 1 +fc. ${7-10} j.. .,0,0,0,0,{$6%},... rm. fi k. if $11 r $wh,1,100%,2 fi } fx_polygonize_delaunay_preview : gui_split_preview "fx_polygonize_delaunay $*",${-3--1} #@gui Polygonize [Energy] : fx_polygonize, fx_polygonize_preview(0) #@gui : Amplitude = int(300,0,2000) #@gui : Smoothness = float(10,0,100) #@gui : Minimal Area = float(10,0,100) #@gui : X-Resolution = float(10,1,256) #@gui : Y-Resolution = float(10,1,256) #@gui : Outline Color = color(0,0,0,255) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : url = link("Click here for a detailed description of this filter.",\ # "http://www.gimpchat.com/viewtopic.php?f=28&t=9174") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/02/12.") fx_polygonize : polygonize $1,$2,{$3^2},$4,$5 if $9 foreach { # has outline. +norm g. xy,1 !=[-2,-1] 0 -|[-2,-1] r. 100%,100%,1,4 replace_color. 0,0,1,1,1,1,$6,$7,$8,$9 blend alpha } fi fx_polygonize_preview : gui_split_preview "fx_polygonize $*",${-3--1} #@gui Poster Edges : fx_poster_edges, fx_poster_edges_preview(0) #@gui : Image Smoothness = float(20,0,100) #@gui : Edge Threshold = float(60,0,100) #@gui : Edge Shade = float(5,0,30) #@gui : Edge Thickness = float(0,0,5) #@gui : Edge Antialiasing = float(10,0,100) #@gui : Posterization Level = int(0,0,15) #@gui : Posterization Antialiasing = float(0,0,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : url = link("Click here for a detailed description of this filter.",\ # "http://www.davidrevoy.com/article147/gmic-new-filter-poster-edges") #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé and David Revoy. #@gui :       Latest Update: 2012/30/11.") fx_poster_edges : if $1 bilateral 10,$1 fi poster_edges ${2-7} fx_poster_edges_preview : gui_split_preview "fx_poster_edges $*",${-3--1} #@gui Posterize : fx_posterize, fx_posterize_preview(0) #@gui : Smoothness = float(150,0,800) #@gui : Edges (%) = float(30,0,100) #@gui : Paint = float(1,0,10) #@gui : Colors = int(12,2,256) #@gui : Minimal Area = int(0,0,64) #@gui : Outline (%) = float(0,0,100) #@gui : Normalize Colors = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/25/10.") fx_posterize : foreach { split_opacity l[0] { . amp=$1 do smooth. {min(50,$amp)},{$2%},1,$3,{2*$3} amp-=50 while $amp>0 bilateral.. .,5,10 rm. srgb2rgb +colormap $4,0 rgb2srgb index.. .,0,0 if $5 +area.. 0,0 <. $5 inpaint... .,0,3 rm. fi map.. . rm. if $6 +norm (0,1,0;1,1,1;0,1,0) +dilate.. . rm.. -[-2,-1] !=. 0 100%,100%,1,{0,s} j... .,0,0,0,0,{$6%},.. rm[-2,-1] fi if $7 n 0,255 fi } a c } fx_posterize_preview : gui_split_preview "fx_posterize $*",${-3--1} #@gui Quadtree Variations : fx_quadtree, fx_quadtree_preview(1) #@gui : Mode = choice("Squares","Sierpinksi Design","Ellipse Painting") #@gui : Precision = int(1024,2,4096) #@gui : Homogeneity = float(0.5,0,2) #@gui : Outline = int(0,0,4) #@gui : sep = separator() #@gui : note = note{"For 'Ellipse painting' only:"} #@gui : Primary Radius = float(3,0,5) #@gui : Secondary Radius = float(1.5,0,5) #@gui : Anisotropy = float(1,0,4) #@gui : Only Leafs = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/15/06.") fx_quadtree : mode,precision,homogeneity,outline,radius1,radius2,anisotropy,only_leafs=${1-8} m "_qt : sh 3 u {w>1&&h>1?iv*(w*h)^"$homogeneity":-0.1} k[0]" # Function used to split quads foreach { to_rgb # Decompose image with quadtree. keep_only_leafs:=$mode!=2" || "$only_leafs +norm. a c _qt ({d=max(w,h);[0,0,d-1,d-1]},0,${}) repeat $precision { +columns. 100% n:=yM rm. x0,y0,x1,y1,level:=crop(0,$n,5,1) xc,yc:=round([$x1+$x0,$y1+$y0]/2) xc1,yc1:=[$xc,$yc]-1 level1:=$level+1 B0=$x0,$y0,$xc1,$yc1 +crop[0] $B0,3 _qt. B0.=,$level1,${} rm. B1=$xc,$y0,$x1,$yc1 +crop[0] $B1,3 _qt. B1.=,$level1,${} rm. B2=$x0,$yc,$xc1,$y1 +crop[0] $B2,3 _qt. B2.=,$level1,${} rm. B3=$xc,$yc,$x1,$y1 +crop[0] $B3,3 _qt. B3.=,$level1,${} rm. if $keep_only_leafs sh. $n,$n,0,0 f. $B0 rm. ($B1;$B2;$B3) a[-2,-1] y # Insert new leafs else =. -1,5,$n # Mark as not terminal leaf anymore ($B0;$B1;$B2;$B3) a[-2,-1] y # Insert new leafs else fi } shift. 2,0,0,0,2 sort. +,y levelmax:=i(0,h-1) shift. -2,0,0,0,2 # Sort by quadtree level order # Estimate data average in each quad. channels[0] 0,2 +structuretensors[0] a[0,-1] c r. {w+6},100%,1,1,0 repeat h { x0,y0,x1,y1:=crop(0,$>,4,1) +crop[0] $x0,$y0,$x1,$y1,3 r. 1,1,1,100%,2 y. x j.. .,{-2,w-6},$> rm. } # Synthetize image. permute. cyzx channels[0] 0,3 f[0] 0 if !$mode # Squares f. "> rectangle(ind,P0,P1,opacity,color) = ( _P0 = P0; _P2 = P1; _P1 = [ _P2[0], _P0[1] ]; _P3 = [ _P0[0], _P2[1] ]; polygon(#ind,4,_P0,_P1,_P2,_P3,opacity,color); ); begin(colo = [ 0,0,0,255 ]); P = I; Xpp = P[0,2]; Xnn = P[2,2]; col = [ P[6,3],255 ]; if ("$outline"<=0, rectangle(#0,Xpp,Xnn,1,col); _(else), rectangle(#0,Xpp,Xnn,1,colo); rectangle(#0,Xpp + "$outline",Xnn - "$outline",1,col); ); I" elif $mode==1 # Sierpinski design f. "> P = I; Xpp = P[0,2]; Xnn = P[2,2]; Xnp = [ Xnn[0],Xpp[1] ]; Xpn = [ Xpp[0],Xnn[1] ]; col1 = [ P[6,3],255 ]; col2 = [ P[6,3],64 ]; T = [ P[9],P[10],P[10],P[11] ]; eig = eig(T); angle = atan2(eig[5],eig[4])*180/pi; if ((angle>=0 && angle<90) || angle<-90, polygon(#0,3,Xpp,Xnp,Xnn,1,col1); polygon(#0,3,Xpp,Xnn,Xpn,1,col2); _(else), polygon(#0,3,Xnp,Xnn,Xpn,1,col1); polygon(#0,3,Xnp,Xpp,Xpn,1,col2) ); I" else # Ellipse painting f. "> begin(colo = [ 0,0,0,255 ]); P = I; Xpp = P[0,2]; Xnn = P[2,2]; Xcc = (Xpp + Xnn)/2; R = (Xnn[0] - Xpp[0])/2; col = [ P[6,3],255 ]; r = "$radius2"*R; R*="$radius1"; T = [ P[9],P[10],P[10],P[11] ]; eig = eig(T); anisotropy = (1 + eig[1])/(1 + eig[0]); r*=anisotropy^"$anisotropy"; angle = atan2(eig[5],eig[4])*180/pi; if ("$outline">0, ellipse(#0,Xcc,R,r,angle°,1,colo)); ellipse(#0,Xcc,R - "$outline",r - "$outline",angle°,1,col); I" if !$outline sh[0] 100% if !im solidify[0] 10% fi fi fi k[0] } um _qt fx_quadtree_preview : gui_split_preview "fx_quadtree $*",${-3--1} #@gui Rodilius : fx_rodilius, fx_rodilius_preview(1) #@gui : Amplitude = float(10,0,30) #@gui : Thickness = float(10,0,100) #@gui : Sharpness = float(300,0,1000) #@gui : Orientations = int(5,2,36) #@gui : Offset = float(30,0,180) #@gui : Smoothness = int(0,0,5) #@gui : Color Mode = choice(1,"Darker","Lighter") #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=RC07VUpzwGc") #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé and Rod/GimpChat. #@gui :       Latest Update: 2013/05/03.") fx_rodilius : ac "rodilius ${1-5,7} repeat $6 { smooth 10,0,1,1,1,0.8,45 sharpen 30 } c 0,255",$8,$9 fx_rodilius_preview : gui_split_preview "fx_rodilius $*",${-3--1} #@gui Shapeism : fx_shapeism, fx_shapeism_preview(0)+ #@gui : Shape = choice(2,"Squares","Triangles","Circles","Diamond","Hexagon","Octagon","Stars","Custom") #@gui : Branches = int(7,3,16) #@gui : Thickness = float(0.38,0,1) #@gui : Angle = float(0,0,360) #@gui : note = note("Note: Parameters Branches, Thickness and Angle are #@gui : used only for Custom shapes.") #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : Scales = int(5,1,16) #@gui : Maximal Size = int(32,1,256) #@gui : Minimal Size = int(8,1,256) #@gui : Allow Angle = choice(3,"0 deg.","180 deg.","90 deg.","Any") #@gui : Spacing = int(1,-5,5) #@gui : Precision = int(5,1,10) #@gui : Edges = float(0.5,0,2) #@gui : Smoothness = float(1,0,10) #@gui : Background = color(0,0,0,255) #@gui : sep = separator() #@gui : url = link("Click here for a detailed description of this filter.",\ # "http://gimpchat.com/viewtopic.php?f=28&t=7500&sid=5b483979826903b8f8fc8fdaf1767dae") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/11/06.") fx_shapeism : foreach { to_rgb +gradient_norm b. $13% ^. $12 quantize. $6,0,0 100%,100%,1,2 repeat $6 { # Create map of possible locations. +channels[2] 100% +>[1] $> !=.. 0 -|[-2,-1] a[2,-1] c # Create shape at given scale. size:=$6<=1?$7:$7+($8-$7)*$>/($6-1) if $size<1 break fi if $5 {2*$size},{2*$size} _fx_shapeism$1. ${2-4} rs. ,$size else $size,$size _fx_shapeism$1. ${2-4} fi +!=. 0 expand[-2,-1] xy,1 n[-2,-1] 0,1 if $10<1 dilate. 3 fi . a[-3--1] c # Pack sprites for given scale rprogress "pack_sprites[-2,-1] 1,100,$9,$10,$11",{$>*100/$6},{($>+1)*100/$6} channels. 0,1 } rprogress 97 rm[1] channels. 0 +!=. 0 blend[0,-1] shapeaverage0 *[1] 255 a c i[0] 100%,100%,1,4 fc[0] $14,$15,$16,$17 blend alpha rprogress 100 } fx_shapeism_preview : gui_print_preview "" 50%,50% _fx_shapeism$1. ${2-4} frame. 1,1,0 >=. 50% n. 0,255 r. 100%,100%,1,4 ri. [0],0,0,0.5,0.5 -| _fx_shapeism0 : # Square f 255 skip $* _fx_shapeism1 : # Triangle polygon 3,50%,0,0,100%,100%,100%,1,1 skip $* _fx_shapeism2 : # Circle shape_circle {w} rm.. skip $* _fx_shapeism3 : # Diamond shape_diamond {w} rm.. skip $* _fx_shapeism4 : # Hexagon star3d 3,1 *3d. {0,min(w,h)/2} j3d[0] .,50%,50%,0,1,2,0 k[0] skip $* _fx_shapeism5 : # Octogon star3d 4,1 *3d. {0,min(w,h)/2} j3d[0] .,50%,50%,0,1,2,0 k[0] skip $* _fx_shapeism6 : # Star star3d 5 *3d. {0,min(w,h)/2} j3d[0] .,50%,50%,0,1,2,0 k[0] skip $* _fx_shapeism7 : # Custom star3d $1,$2 *3d. {0,min(w,h)/2} r3d. 0,0,1,$3 j3d[0] .,50%,50%,0,1,2,0 k[0] #@gui Sharp Abstract : fx_sharp_abstract, fx_sharp_abstract_preview(0) #@gui : Spatial Scale = float(4,0,32) #@gui : Value Scale = float(10,0,16) #@gui : Precision = float(0.5,0,2) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/09.") fx_sharp_abstract : ac "rolling_guidance ${1-3}",$4 fx_sharp_abstract_preview : gui_split_preview "fx_sharp_abstract $*",${-3--1} #@gui Sketch : fx_sketchbw, fx_sketchbw_preview(0) #@gui : Number of Orientations = int(3,1,16) #@gui : Starting Angle = float(45,0,180) #@gui : Angle Range = float(180,0,180) #@gui : Stroke Length = float(30,0,1000) #@gui : Contour Threshold = float(1.75,0,10) #@gui : Opacity = float(0.02,0,0.3) #@gui : Background Intensity = float(0.5,0,2) #@gui : Density = float(0.75,0,5) #@gui : Sharpness = float(0.1,0,1) #@gui : Anisotropy = float(0.7,0,1) #@gui : Smoothness = float(3,0,10) #@gui : Coherence = float(6,0,10) #@gui : Boost Stroke = bool(0) #@gui : Curved Stroke = bool(1) #@gui : Color Model = choice(4,"Black on white","White on black","Black on transparent white", #@gui : "White on transparent black","Color on white") #@gui : Random Seed = int(0,0,65535) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/05/11.") fx_sketchbw : skip ${16=0} srand $16 if $15==4 foreach { +sketchbw ${1-14} blend hardlight } return fi sketchbw ${1-14} if $15&1 negate fi if $15==2 r 100%,100%,1,4 repeat $! { sh[$>] 3 *. -2 +. {2*255} c. 0,255 rm. } elif $15==3 r 100%,100%,1,4 repeat $! { sh[$>] 3 *. 2 c. 0,255 rm. } fi fx_sketchbw_preview : gui_split_preview "fx_sketchbw $*",${-3--1} #@gui Smooth Abstract : fx_smooth_abstract, fx_smooth_abstract_preview(0) #@gui : Smoothness (%) = float(75,0,100) #@gui : Regularization = choice(0,"Isotropic","Delaunay-Guided","Edge-Oriented") #@gui : Regularization Iterations = int(20,0,100) #@gui : Geometry = float(1,0,5) #@gui : Details = float(30,0,50) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/06/04.") fx_smooth_abstract : foreach { split_opacity l[0] { srgb2rgb mM:=im,iM +b $4 n. $mM gradient_norm. <=. {50-$5} inpaint_pde[0] [1],$1%,$2,$3 rm. rgb2srgb c 0,255 } a c } fx_smooth_abstract_preview : gui_split_preview "fx_smooth_abstract $*",${-3--1} #@gui Stringify : fx_stringify, fx_stringify_preview(0) #@gui : Smoothness = int(2,0,10) #@gui : Levels = int(32,2,64) #@gui : Sampling rate (%) = float(20,0,100) #@gui : Link = choice(1,"2 points","3 points","4 points","All points") #@gui : Offset (%) = float(100,0,100) #@gui : Threshold = int(32,0,1024) #@gui : Opacity (%) = float(20,0,100) #@gui : Normalization (%) = float(25,0,100) #@gui : Fill holes = bool(0) #@gui : Background color = color(0,0,0) #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2024/03/07.") fx_stringify_preview : gui_split_preview "fx_stringify $*",${-3--1} fx_stringify : smoothness,levels,sampling,link,offset,threshold,opacity,normalization,fill_holes,bgr,bgg,bgb=${1-12} foreach { split_opacity l[0] { to_rgb repeat $smoothness { smooth 60,0,1,1,1 progress {lerp(0,25,($>+1)/($>+$<+1))} } +colormap. $levels,0 round. index.. . [0],[0],1,3 fc. $bgr,$bgg,$bgb # Extract all closed components and sort them by decreasing size. repeat w#1 { ind=$> color:="I[#1,$>]" +==[0] $> N:=$!-1 edgels. , foreach[$N--1] { if h<=$threshold rm fi } if $!>$N =>[$N--1] {``$color} fi progress {lerp(25,75,($>+1)/($>+$<+1))} } sort_list[3--1] -,h # Draw colored lines. repeat $!-3 { p:=$>+3 color={$p,n} +sh[$p] 100% f. "arg0((j[1,2]-i)%4,0,1,0,-1)" is_hole:=is!=4 rm. if !$is_hole" || "$fill_holes channels[$p] 0,1 rs[$p] ,$sampling%,1 eval[$p] ":begin(color = [ "$color" ]; const offset = h/($link + 2)*$offset%; ret = I); P = I; $link==3?(repeat (h,k,polygon(#2,2,P,I[k],$opacity%,color))):( # All-points link Q = J[offset,2]; !$link?polygon(#2,2,P,Q,$opacity%,color):( # 2-point link R = J[2*offset,2]; $link==1?polygon(#2,-3,P,Q,R,$opacity%,0xFFFFFFFF,color):( # 3-points link S = J[3*offset,2]; polygon(#2,-4,P,R,Q,S,$opacity%,0xFFFFFFFF,color) # 4-points link ); ); ); ret" fi progress {lerp(75,100,($>+1)/($>+$<+1))} } k[2] normalize_local {$normalization/10} } a c } #@gui Stylize : fx_stylize,fx_stylize_preview #@gui : Style = choice{0,"Custom Style (Top Layer)","Custom Style (Bottom Layer)", #@gui : "Braque: Landscape near Antwerp", #@gui : "Braque: Le Viaduc à l'Estaque", #@gui : "Braque: Little Bay at La Ciotat", #@gui : "Braque: The Mandola", #@gui : "Christine Garner: Black Colour Pencil", #@gui : "Christine Garner: Colour Pencil Sepia", #@gui : "Christine Garner: Dark Coloured Pencil", #@gui : "Christine Garner: Pencil", #@gui : "Christine Garner: Sketching Pastel", #@gui : "Christine Garner: Willow Charcoal", #@gui : "Delaunay: Windows Open Simultaneously", #@gui : "Delaunay: Portrait de Metzinger", #@gui : "Hokusai: The Great Wave", #@gui : "Kandinsky: Squares with Concentric Circles", #@gui : "Kandinsky: Yellow-Red-Blue", #@gui : "Klee: Death and Fire", #@gui : "Klee: In the Style of Kairouan", #@gui : "Klee: Oriental Pleasure Garden Anagoria", #@gui : "Klee: Polyphony 2", #@gui : "Klee: Red waistcoat", #@gui : "Klimt: The Kiss", #@gui : "Mondrian: Composition in Red-Yellow-Blue", #@gui : "Mondrian: Evening; Red Tree", #@gui : "Mondrian: Gray Tree", #@gui : "Monet: San Giorgio Maggiore at Dusk", #@gui : "Monet: Water-Lily Pond", #@gui : "Monet: Wheatstacks - End of Summer", #@gui : "Munch: The Scream", #@gui : "Picabia: Udnie", #@gui : "Picasso: Les Demoiselles d'Avignon", #@gui : "Picasso: Seated Woman", #@gui : "Picasso: The Reservoir - Horta de Ebro", #@gui : "Pollock: Convergence", #@gui : "Pollock: Summertime Number 9A", #@gui : "Van Gogh: Almond Blossom", #@gui : "Van Gogh: Irises", #@gui : "Van Gogh: The Starry Night", #@gui : "Van Gogh: Wheat Field with Crows"} #@gui : Scale Style to Fit Target Resolution = choice(5,"No rescaling","10%","20%","30%","50%","75%","100%","150%", #@gui : "200%","250%","300%") #@gui : Style Variations = choice("None","All XY-flips","All 90° rotations","All 45° rotations") #@gui : Preview Progression While Running = bool(1) #@gui : sep = separator(), note = note{"Style/Target Parameters:"} #@gui : Fidelity to Target (Finest) = float(0.5,0,5) #@gui : Fidelity to Target (Coarsest) = float(2,0,5) #@gui : Fidelity Smoothness (Finest) = float(3,0,5) #@gui : Fidelity Smoothness (Coarsest) = float(0.5,0,5) #@gui : Fidelity Chromaticity = float(0.1,0,1) #@gui : sep = separator(), note = note{"Image Matching Parameters:"} #@gui : Match Colors With = choice(3,"Nothing","Gamma Balance","Histogram Transfer","PCA transfer") #@gui : Colorspace = choice{3,"sRGB","Linear RGB","YCbCr","YCbCr (Luma/Chroma),"YCbCr (Luma Only)", #@gui : "YCbCr (Chroma Only)","Lab","Lab (Luma/Chroma)","Lab (Luma Only)","Lab (Chroma Only)"} #@gui : Keep Color Channels = choice{"All","Luminance Only (YCbCr)","Luminance Only (Lab)","Chrominances Only (CbCr)", #@gui : "Chrominances Only (ab)"} #@gui : Smoothness = float(0.7,0,5) #@gui : Also Match Gradients = float(1,0,5) #@gui : sep = separator(), note = note{"Advanced Parameters:"} #@gui : Init. Type = choice("Best Match","Identity","Randomized") #@gui : Init. Resolution = choice(1,"8px","16px","32px","64px","128px","256px") #@gui : Init. With High Gradients Only = float(0,0,100) #@gui : Patch Size for Analysis = int(5,2,16) #@gui : Patch Size for Synthesis = int(5,2,16) #@gui : Patch Size for Synthesis (Final) = int(7,2,16) #@gui : Number of Matches (Finest) = int(1,0,10) #@gui : Number of Matches (Coarsest) = int(30,0,200) #@gui : Penalize Patch Repetitions = int(2,0,300) #@gui : Matching Precision (Smaller is Faster) = float(2,0,10) #@gui : Scale Factor = float(1.85,1.1,4) #@gui : Skip Finest Scales = int(0,0,3) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/03/23.") fx_stylize : init_resolution:=arg(1+$16,8,16,32,64,128,256) # Define image matching function colorspace=${"arg0 $11,all,lrgb,ycbcr,ycbcr,ycbcr_y,ycbcr_cbcr,lab,lab,lab_l,lab_ab"} match_colors="s c,-3 mv[1] 3 b[-2,-1] 1% negate[-2,-1] n[-2,-1] 0,1" luma,chroma= if $11==3" || "$11==7 # Chroma/Luma conversion for YCbCr or Lab luma=_${"arg0 ($11==7),y,l"} chroma=_${"arg0 ($11==7),cbcr,ab"} fi if $10==1 match_colors=$match_colors" ac[0,1] \"balance_gamma ,\","$colorspace$luma if narg($chroma) match_colors=$match_colors" ac[0,1] \"balance_gamma ,\","$colorspace$chroma fi elif $10==2 match_colors=$match_colors" match_histogram[0] [1],256,"$colorspace$luma if narg($chroma) match_colors=$match_colors" match_histogram[0] [1],256,"$colorspace$chroma fi elif $10==3 match_colors=$match_colors" match_pca[0] [1],"$colorspace$luma" c[0] 0,255" if narg($chroma) match_colors=$match_colors" match_pca[0] [1],"$colorspace$chroma" c[0] 0,255" fi fi if $12==1 match_colors=$match_colors" rgb2ycbcr[0,1] sh[0,1] 1,2 f[-2,-1] 128 rm[-2,-1] ycbcr2rgb[0,1]" elif $12==2 match_colors=$match_colors" srgb2lab[0,1] sh[0,1] 1,2 f[-2,-1] 0 rm[-2,-1] lab2srgb[0,1]" elif $12==3 match_colors=$match_colors" rgb2ycbcr[0,1] sh[0,1] 0 f[-2,-1] 128 rm[-2,-1] ycbcr2rgb[0,1]" elif $12==4 match_colors=$match_colors" srgb2lab[0,1] sh[0,1] 0 f[-2,-1] 50 rm[-2,-1] lab2srgb[0,1]" fi match_colors=$match_colors\ " b[0,1] xy,$13 foreach[0,1] { s c n 0,255 a c } *[-2,-1] {$14*255} a[0,-2] c a[1,-1] c" patch_penalization=$23 # Insert style image at the end of the list. if $1<2 # Custom style (top or bottom layer) if $!<2 error "At least two layers are required in this mode." fi ind_first:=!$1 ind_style:=$1?-1:0 N:=$!-1 sh[$ind_style] else ind_first=0 N=$! ind_style= _fx_stylize {$1-2} fi # Process layers. is_window=0 repeat $N { l[{$ind_first+$>},-1] { nm={0,n} if $2 +rs[1] {0,[w,h]*arg($2,0.1,0.2,0.3,0.5,0.75,1,1.5,2,2.5,3)},2,2 fi if $3==1 . +mirror. x +mirror[-2,-1] y a[-4--1] z elif $3==2 +l. { r {u=max(w,h);[u,u]},1,100%,0,3,0.5,0.5 repeat 3 { +rotate[0] {90*$>},1,3,50%,50% } a z } elif $3==3 +l. { r {u=max(w,h);[u,u]},1,100%,0,3,0.5,0.5 repeat 7 { +rotate[0] {45*$>},1,3,50%,50% } a z } fi if $4 wsiz=${"fitscreen "{0,[w,h]}} w[0] $wsiz is_window={*} fi stylize[0] .,${5-9},$15,$init_resolution,${17-22},$patch_penalization,${24-26},$match_colors k[0,1] =>[0] $nm if $is_window" && "!{*} break fi } } rm. # Remove style image if 0$_output_mode if narg($ind_style) rm[$ind_style] fi # Do not duplicate style layer if output mode -> new layers if $is_window" && "!{*} rm fi # In case user closed the window, does not output new layers. fi fx_stylize_preview : if $1<2 # Custom style (top or bottom layer) if $!<2 gui_warning_preview "At least two layers are required when specifying a custom style." return fi ind_first:=!$1 ind_style:=$1?-1:0 N:=$!-1 sh[$ind_style] else ind_first=0 N=$! ind_style= _fx_stylize {$1-2} fi repeat $N { l[{$ind_first+$>},-1] { gui_no_preview[0] , +rs[1] {0,[w,h]*2/3},2 frame. 1,1,255 frame. 1,1,0,0,0,255 j[0] .,1%,30 rm. to[0] "Style:",1%,0,24 } } rm. if narg($ind_style) rm[$ind_style] fi _fx_stylize : # Download pre-defined style image if isnum($1) name=${"arg0 $1",\ "landscapenearantwerp,leviaducalestaque,littlebayatlaciotat,themandola",\ "blackcolourpencil,colourpencilsepia,darkcolouredpencil,pencil,sketchingpastel,willowcharcoal",\ "windowsopensimultaneously,portraitdemetzinger,greatwave,squareswithconcentriccircles,yellowredblue",\ "deathandfire,inthestyleofkairouan,orientalpleasuregardenanagoria,polyphony2,redwaistcoat,thekiss",\ "compositionredyellowblue,redtree,graytree,sangiorgiomaggioreatdusk,waterlilypond,wheatstacksendofsummer",\ "scream,udnie,lesdemoisellesdavignon,seatedwoman,reservoirhortadeebro,convergence,summertime9a",\ "almondblossom,irises,starrynight,wheatfieldwithcrows"} else name="$1" fi input_cached img/style_$name.png #@gui Vector Painting : fx_vector_painting, fx_vector_painting_preview(1) #@gui : Details = float(9,0,10) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/08/25.") fx_vector_painting : foreach { split_opacity l[0] { +luminance b. {10-$1}%,1,1 f. "dmax = -1; nmax = 0; for (n = 0, ++n<=8, p = arg(n,-1,0,1,-1,1,-1,0,1); q = arg(n,-1,-1,-1,0,0,1,1,1); d = (j(p,q,0,0,0,1) - i)^2; d>dmax?(dmax = d; nmax = n):nmax; )" blend shapeaverage } a c } fx_vector_painting_preview : gui_split_preview "fx_vector_painting $*",${-3--1} #@gui Warhol : warhol, warhol(1) #@gui : X-Tiles = int(3,1,10) #@gui : Y-Tiles = int(3,1,10) #@gui : Smoothness = float(2,0,10) #@gui : Color = float(40,0,60) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") #@gui Whirl Drawing : fx_draw_whirl, fx_draw_whirl_preview(0) #@gui : Amplitude = float(20,0,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_draw_whirl : foreach { split_opacity draw_whirl[0] $* a c } fx_draw_whirl_preview : gui_split_preview "fx_draw_whirl $*",${-3--1} #@gui ____Black & White #------------------------------- #@gui Black & White : fx_blackandwhite, fx_blackandwhite_preview(1)+ #@gui : Red Level = float(0.299,0,1) #@gui : Red Smoothness = float(0,0,10) #@gui : Green Level = float(0.587,0,1) #@gui : Green Smoothness = float(0,0,10) #@gui : Blue Level = float(0.114,0,1) #@gui : Blue Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : sep = separator() #@gui : Grain (Shadows) = float(0,0,200) #@gui : Grain (Midtones) = float(0,0,200) #@gui : Grain (Highlights) = float(0,0,200) #@gui : Grain Tone Fading = float(2,0,10) #@gui : Grain Scale = float(0,0,3) #@gui : Grain Type = choice("Gaussian","Uniform","Salt and Pepper","Poisson") #@gui : sep = separator() #@gui : Local Contrast = float(0,0,60) #@gui : Radius = int(16,1,512) #@gui : Contrast Smoothness = float(4,0,10) #@gui : sep = separator() #@gui : Pseudo-Gray Dithering = int(0,0,5) #@gui : Use Maximum Tones = bool(false) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/20/02.") fx_blackandwhite : repeat $! { l. { split_opacity rv to_rgb. s. c # Isolate opacity *... $1 b... $2% # Red level + smoothness *.. $3 b.. $4% # Green level + smoothness *. $5 b. $6% # Blue level + smoothness +[-3--1] /. {$1+$3+$5} c. 0,255 # (R,G,B) -> B&W adjust_colors ${7-11},0,255 if $12||$13||$14 100%,100% [-1]x2 # Create noise for shadows, midtones and highlights. noise... 100,$17 b... $16% n... -$12,$12 # Scaled grain on shadows. noise.. 100,$17 b.. $16% n.. -$13,$13 # Scaled grain on midtones. noise. 100,$17 b. $16% n. -$14,$14 # Scaled grain on highlights. +tones[-4] 3 b[-3--1] $15% # Get smoothed tones. *[-6,-3] *[-4,-2] *[-2,-1] # Get noisy tones. +[-4--1] c. 0,255 # Compose them with the B&W image. fi rv a c } mv. 0 } # Re-compose opacity and loop to next image. if $18 normalize_local $18,$19,$20,2%,1,0,255 fi if $22 n 0,255 fi if $21 to_pseudogray $21,1 fi fx_blackandwhite_preview : gui_split_preview "fx_blackandwhite $*",${-3--1} #@gui B&W Stencil : fx_stencilbw, fx_stencilbw_preview(0) #@gui : Threshold = float(10,0,30) #@gui : Smoothness = float(10,0,30) #@gui : Hue = float(0,0,360) #@gui : Saturation = float(0,0,1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_stencilbw : stencilbw $1,$2 if $3||$4 foreach { split_opacity /[0] 255 i[0] 100%,100%,1,1,$4 i[0] 100%,100%,1,1,$3 a[0-2] c hsv2rgb[0] a c } fi fx_stencilbw_preview : gui_split_preview "fx_stencilbw $*",${-3--1} #@gui Charcoal : fx_charcoal, fx_charcoal_preview(0) #@gui : Granularity = int(65,0,800) #@gui : Lowlights Crossover Point = int(70,0,255) #@gui : Highlights Crossover Point = int(170,0,255) #@gui : Boost Contrast = bool(0) #@gui : Resize Image for Optimum Effect = bool(1) #@gui : Add Chalk Highlights = bool(0) #@gui : Minimal Highlights = int(50,0,255) #@gui : Maximal Highlights = int(70,0,255) #@gui : Background Color = color(255,255,255) #@gui : Foreground Color = color(0,0,0) #@gui : Invert Background / Foreground = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/17/03.") #@gui : note = note("Inspired from the Charcoal script by micomicon :") #@gui : url = link("http://registry.gimp.org/node/25078") fx_charcoal : foreach { split_opacity l[0] { compose_channels max w,h={w},{h} if $5 r. 150%,150%,1,1,6 fi if $4 equalize. n. 0,255 fi sharpen {$1*3} cut 0,255 if $6 +ir $7,$8 fi # Add highlights layer if required. ir[0] $2,$3 if !$15 ==[0] 0 fi -| +*[0] $10 +*[0] $11 *[0] $9 a[-3--1] c replace_color 0,0,0,0,0,$12,$13,$14 r $w,$h,1,100%,2 } a c } fx_charcoal_preview : gui_split_preview "fx_charcoal $*",${-3--1} #@gui Colorize [Interactive] : fx_colorize_interactive, fx_colorize_interactive_preview #@gui : Input Type = _choice("B&W Photograph","Lineart") #@gui : Output Type = _choice{"Colorized Image (1 Layer)","Colors Only (1 Layer)","Image + Colors (2 Layers)", #@gui : "Image + Colors (Multi-Layers)"} #@gui : View Resolution = _choice{1,"Small (Faster)","Medium","High (Slower)","Very High (Even Slower)"} #@gui : 1st Additional Palette (.gpl) = _filein() #@gui : 2nd Additional Palette (.gpl) = _filein() #@gui : Image to Grab Color from (.png) = _filein() #@gui : sep = separator() #@gui : note = note{"Description:\n #@gui : This filter allows to quickly colorize a B&W image or lineart. #@gui : Click on the Apply or OK buttons below to open the G'MIC interactive window and #@gui : start adding color control points. When you're done, exit the interactive window: your colored #@gui : result will be transferred back to the host software.\n\n #@gui : If you are not satisfied with the result, Undo it (CTRL+Z), and click on Apply once #@gui : again to modify your control points defined previously. #@gui : To clear all control points, click on the Reset button above. #@gui : "} #@gui : Clear Control Points = button(0.5) #@gui : Last Image Size = value(0,0) #@gui : Control Points = value(-1) #@gui : sep = separator() #@gui : note = note{"Interactions:\n #@gui : Use the following actions in the interactive window to manage your colorization :\n\n #@gui : - Left mouse button creates a new color control point (or move an existing one).\n #@gui : - Right mouse button or key X over a control point deletes it.\n #@gui : - Right mouse button or key P anywhere else picks a color from the image.\n #@gui : - Mouse wheel, or keys CTRL+arrows up/down zoom view in/out.\n #@gui : - CTRL+mouse wheel, SHIFT+wheel or arrow keys move image in zoomed view.\n #@gui : - Key SPACE updates the extrapolated color field.\n #@gui : - Key TAB toggles markers view modes.\n #@gui : - Key BACKSPACE deletes the last control point added.\n #@gui : - Key PAGE UP increases image contrast.\n #@gui : - Key PAGE DOWN decreases image contrast.\n #@gui : - Key R enters/exits color replace mode.\n #@gui : - Keys CTRL+D increase window size.\n #@gui : - Keys CTRL+C decrease window size.\n #@gui : - Keys CTRL+R resets window size.\n #@gui : - Keys ESC, Q or ENTER exit the interactive window. #@gui : "} #@gui : sep = separator() #@gui : note = note("You can find more information on how to use this filter here :") #@gui : url = link("David Revoy's G'MIC Colorization Page", #@gui : "http://www.davidrevoy.com/article240/gmic-line-art-colorization") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/12/07.") fx_colorize_interactive : skip "${4=},${5=},${6=}" N=$! nm={n} resolution:=arg(1+$3,512,1024,2048,0) => "[G"{`39`}"MIC] Colorize" if [$9][0]==-1" || "[$8]!=[w,h] _gui_control_points= else _gui_control_points=$9 fi N=$! arg_palette1=0 l[] { 0 => "$4" ext={x} rm if same(['$ext'],'gpl',-1,0) input_gpl "$4" arg_palette1=1 fi onfail rm } if $arg_palette1 arg_palette1=[{$!-1}] fi arg_palette2=0 l[] { 0 => "$5" ext={x} rm if same(['$ext'],'gpl',-1,0) input_gpl "$5" arg_palette2=1 fi onfail rm } if $arg_palette2 arg_palette2=[{$!-1}] fi arg_grabber=0 l[] { 0 => "$6" ext={x} rm i "$6" to_rgb arg_grabber=1 onfail rm } if $arg_grabber arg_grabber=[{$!-1}] fi repeat $N { status=${x_colorize[$>]\ $1,$resolution,$2,$arg_palette1,$arg_palette2,$arg_grabber} } k[0-{$N-1}] if $2==1 foreach { channels {s-3},{s-1} } # Output : colors only (1 layer). elif $2>=2 foreach { +channels {s-3},{s-1} channels.. 0,{0,s-4} } # Output : Lineart + Colors (2 layers). if $2>=3 split_colors[1] 0,256,8 fi # Split colors into layers. fi =>[^] $nm if !narg($status) status=-1 fi u \{$1\}\{$2\}\{$3\}\{"$4"\}\{"$5"\}\{"$6"\}\{0\}\{{w},{h}\}\{$status\} fx_colorize_interactive_preview : skip "${4=},${5=},${6=}" if $7 # Clear control points. gui_print_preview "No preview\n available",,"(Control points cleared)" u \{$1\}\{$2\}\{$3\}\{"$4"\}\{"$5"\}\{"$6"\}\{0\}\{{w},{h}\}\{-1\} else gui_no_preview , fi #@gui Colorize [Photographs] : fx_recolorize, fx_recolorize_preview(1) #@gui : Smoothness = int(2,0,6) #@gui : Anisotropy = float(0.2,0,1) #@gui : Output Mode = choice("Merge Brightness / Colors","Split Brightness / Colors") #@gui : sep = separator() #@gui : note = note{"Note: This filter needs two layers to work properly. #@gui : The bottom layer must be a B&W image, while the top layer contains color patches that will #@gui : be extrapolated in a smart way (edge-directed) to fill the entire image. At the end, #@gui : you get a completely recolored image."} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/16/01.") fx_recolorize : repeat int($!/2) { if $3 s:=$>,$>+1 else s:=2*$>,2*$>+1 fi l[$s] rv[0,1] { channels[0] 0 to_rgb.. # Convert to pure gray. to_rgba. split_opacity. !=. 0 # Retrieve mask of color patches. srgb2rgb[-3,-2] rgb2lab8[-3,-2] channels... 0 channels.. 1,2 # Now, list is [0]=lightness / [1]=chroma / [2]=mask. +.. 1 *.. . +gradient_norm... *. -1 watershed... . rm. -.. 1 # Get first estimate for the color interpolation. +diffusiontensors... $2,1,0.5,0.5 ==.. 0 *. .. rm.. smooth.. .,{$1*80},0.8,60 rm. a[-2,-1] c lab82rgb. rgb2srgb. if $3 rgb2hsv. s. c i[2] 100%,100%,1,1,1 i[3] 100%,100%,1,2,0 a[0-2] c a[^0] c hsv2rgb rv fi } } fx_recolorize_preview : fx_recolorize $* a x #@gui Colorize [with Colormap] : fx_bwrecolorize, fx_bwrecolorize_preview #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Normalize Input = bool(0) #@gui : sep = separator() #@gui : Gradient Preset = choice("User-Defined","Black to White","White to Black","Sepia","Solarize") #@gui : Interpolation Type = choice(1,"Nearest","Linear","Cubic","Lanczos") #@gui : Preserve Initial Brightness = bool(0) #@gui : sep = separator() #@gui : note = note("User-defined gradient :") #@gui : Number of Tones = int(5,2,8) #@gui : 1st Tone = color(0,0,0,255) #@gui : 2nd Tone = color(43,25,55,255) #@gui : 3rd Tone = color(158,137,189,255) #@gui : 4th Tone = color(224,191,228,255) #@gui : 5th Tone = color(255,255,255,255) #@gui : 6th Tone = color(255,255,255,255) #@gui : 7th Tone = color(255,255,255,255) #@gui : 8th Tone = color(255,255,255,255) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_bwrecolorize : remove_opacity if $4 n 0,255 fi if !$5 # User-defined gradient (${9--2}) r. 4,$8,1,1,-1 permute. yzcx elif $5==1 # Black to white (0,255^0,255^0,255^255,255) elif $5==2 # White to black (255,0^255,0^255,0^255,255) elif $5==3 # Sepia (0,44,115,143,196,244^0,20,84,119,184,235^0,5,44,73,144,200^255,255,255,255,255,255) else # Solarize (0,359^1,1^1,1^255,255) r. 256,1,1,4,3 sh. 0,2 hsv2rgb. rm. fi if !$6 r. 256,1,1,4,1 elif $6==1 r. 256,1,1,4,3 elif $6==2 r. 256,1,1,4,5 c. 0,255 else r. 256,1,1,4,6 fi if $7==1 sh. 0,2 rgb2hsv. sh. 2 f. x/w hsv2rgb.. rm[-2,-1] fi l[^-1] { luminance adjust_colors ${1-3} } map[^-1] . rm. fx_bwrecolorize_preview : gui_split_preview "fx_bwrecolorize ${^0}",${-3--1} #@gui Colorize Lineart [Auto-Fill] : fx_autofill_lineart, fx_autofill_lineart_preview(0) #@gui : Contour Threshold (%) = float(90,0,100) #@gui : Contour Normalization = bool(1) #@gui : Minimal Region Area = int(8,0,256) #@gui : Tolerance to Gaps = int(0,0,10) #@gui : Preview Type = choice("Lineart + Colors","Colors Only") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/12/11.") fx_autofill_lineart : foreach { nm=${-gui_layer_name} # Format input lineart to expected format. is_alpha:=s==2||s==4 if $is_alpha sh 100% is_alpha:=im<128&&iM>128 rm. fi # Check is alpha-channel contains lineart if $is_alpha +channels 100% negate. else +norm fi n. 0,255 l. { # Start multi-scale filling. repeat 1+$4 { fact:=2^-$< nw={0,max(1,round(w*$fact))} nh={0,max(1,round(h*$fact))} +r[0] $nw,$nh,1,1,2 if $2 normalize_local. , fi +>=. {min(99.5,$1)}% if $colors scale2x[colors] r[colors] .,1 *[colors,-1] rv[-2,-1] fi label_fg. 0,1 b.. 0.8 watershed. .. rm.. => colors } # Inpaint regions that are too small. +area. 0,1 label_maxarea:="P=[xM,yM];i(#-2,P)-1" if $3>1 >. {$3*sqrt($3)} *[-2,-1] b.. 0.8 watershed. .. else rm. fi rm.. # Colorize regions. N:=iM if $N -. 1 srand 0 {iM+1},1,1,3,">[j(-1) + u(135,225),u(0,0.7),u(0.4,0.9)]" hsv2rgb. round. point. $label_maxarea,0,0,1,255 map.. . rm. fi } # Format output layers. if !$is_alpha gui_set_layer_mode.. multiply fi gui_set_layer_name. $nm" [colors]" } fx_autofill_lineart_preview : foreach { fx_autofill_lineart ${1-4} if $5 k. else rv blend multiply fi } #@gui Colorize Lineart [Smart Coloring] : fx_colorize_lineart_smart, fx_colorize_lineart_smart_preview(0) #@gui : Colorize Mode = choice("Generate Random-Colors Layer","Extrapolate Color Spots on Transparent Top Layer", #@gui : "Auto-Clean Bottom Color Layer") #@gui : sep = separator() #@gui : note = note{"Global geometry parameters:"} #@gui : Contour Detection (%) = float(95,0,100) #@gui : Discard Contour Guides = bool(0) #@gui : note = note{"Add strokes with a saturated color having value 255 (e.g. pure red) on your lineart #@gui : allows to guide the colorization algorithm with virtual contours."} #@gui : Output Region Delimiters = _bool(0) #@gui : sep = separator() #@gui : note = note{"For Random colors mode only:"} #@gui : Make Hue Depends on Region Size = float(1,0,1) #@gui : Maximal Color Saturation = int(24,0,255) #@gui : Minimal Color Intensity = int(200,0,255) #@gui : sep = separator() #@gui : note = note{"For color spots mode only:"} #@gui : Color Shading (%) = int(0,0,100) #@gui : sep = separator() #@gui : note = note{"Connection parameters:"} #@gui : End Point Rate (%) = float(75,0,100) #@gui : End Point Connectivity = int(2,1,5) #@gui : Spline Max Length (px) = float(60,0,256) #@gui : Segment Max Length (px) = float(20,0,256) #@gui : Spline Max Angle (deg) = float(90,0,180) #@gui : Spline Roundness = float(1,0,2) #@gui : Minimal Region Area = float(10,0,100) #@gui : Allow Self Intersections = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice(0,"Colored geometry","Colored regions","Colored lineart") #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé, Sébastien Fourey and #@gui : David Revoy.      Latest Update: 2018/11/09.") fx_colorize_lineart_smart : _fx_colorize_lineart_smart $*,-1 round fx_colorize_lineart_smart_preview : if $1==1" && "$!<2 gui_warning_preview "A top layer with color spots is missing, for this colorization mode." return elif $1==2" && "$!<2 gui_warning_preview "A bottom color layer is missing, for this colorization mode." return fi _fx_colorize_lineart_smart ${1-3},0,${5--1} _fx_colorize_lineart_smart : if $1==1" && "$!<2 error "A top layer with color strokes is missing, for this colorization mode." elif $1==2" && "$!<2 error "A bottom color layer is missing, for this colorization mode." fi min_color_area:=$15^2 repeat $1?1:$! { inds=${arg0\ !!$1,$<,0--1} l[$inds] { ind_lineart:=$1==1 nm=${gui_layer_name[$ind_lineart]} =>[$ind_lineart] lineart if $!>1 ind_colors:=$1!=1 nmc={$ind_colors,n} =>[$ind_colors] colors fi if $!>=3 rm[2--1] fi # Delete old color and added contour layers if any # Constrain input lineart to be a binary, single channel image. [lineart] is_alpha:=s==2||s==4 if $is_alpha sh. 100% is_alpha:=iM-im>64 rm. fi if $is_alpha channels. 100% else luminance. negate. fi >. {255*(1-$2%)} => strokes # Retrieve closed binary shape. _keep_keycoords:=!$-1 [strokes] close_binary. ${9-14},$min_color_area,$16 => new_strokes if !$-1 # Render estimated geometry +negate[strokes] *. 255 to_rgb. +-[new_strokes] [strokes] dilate. 2 100%,100%,1,3,[0,128,255] j... .,0,0,0,0,1,.. rm[-2,-1] if narg($keycoords) f[keycoords] "ellipse(#-1,(I)[0,2],3,3,0,1,[255,0,0]);I" rm[keycoords] fi => geometry fi # Label color regions and inpaint strokes. if $1==1 # Mode: Color spots-guided colorization to_rgba[$colors] [colors],[colors] f[colors] "i(#-1) = A<255?0:norm(R,G+0.3,B+0.6);I" label_fg. 0 {1+iM},1,1,{colors,s+1} f.. ">I[#-1,i]+=[ I(#"$colors"),1 ];I" s. c,{-s+1} /[-2,-1] +l[new_strokes] { * -1 + 1 +b 1% b.. 1 min } watershed... .,0 rm. map.. . rm. if !$8 rm[strokes] # No color shading else # Shade colors j[strokes] [new_strokes] distance[strokes] 0 *[strokes] -1 eq[new_strokes] 0 label_fg[new_strokes] 0,0 if $min_color_area +area_fg[new_strokes] 0,0 >. $min_color_area *[new_strokes,-1] fi watershed[new_strokes] [strokes],0 rm[strokes] srgb2rgb. guided. [new_strokes],{1+$8/5},0 rgb2srgb. fi rm[new_strokes] => new_colors elif $1==2 # Mode: Clean color layer j[strokes] [new_strokes] distance[strokes] 0 *[strokes] -1 eq[new_strokes] 0 label_fg[new_strokes] 0,0 if $min_color_area +area_fg[new_strokes] 0,0 xy_bg:=xM,yM >. $min_color_area *[new_strokes,-1] fi watershed[new_strokes] [strokes],0 rm[strokes] to_color[colors] sh[colors] 0,2 rgb2hsv8. rm. blend[colors,new_strokes] shapemedian sh[colors] 0,2 hsv82rgb. rm. =>[colors] new_colors else # Mode: Random colorization j[strokes] [new_strokes] distance[strokes] 0 *[strokes] -1 eq[new_strokes] 0 label_fg[new_strokes] 0,0 if $min_color_area +area_fg[new_strokes] 0,0 xy_bg:=xM,yM >. $min_color_area *[new_strokes,-1] fi watershed[new_strokes] [strokes],0 rm[strokes] label[new_strokes] 0,0 +histogram[new_strokes] {new_strokes,[iM+1,0,iM]} equalize. 1024 n. 0,240 ind_bg:=xM channels. 0,2 srand 0 f. "[i,u(1,max(3,$6))/255,u(min(252,$7),255)/255]" . sh. 0 rand. 0,360 rm. hsi2rgb[-2,-1] *.. $5 *. {1-$5} +[-2,-1] round. point. $ind_bg,0,0,1,255 point. 0,0,0,1,255 map[new_strokes] . rm. =>[new_strokes] new_colors fi # Final rendering ind_lineart=$lineart ind_colors=$new_colors if $4 # Output region delimiters 100%,100%,1,1,"const boundary = 1; J(#"$new_colors",1)!=I(#"$new_colors") || J(#"$new_colors",0,1)!=I(#"$new_colors")" *. 255 channels. -3,0 sh. 0,2 fc. 255,0,0 rm. gui_set_layer_name. $nm" [region delimiters]" mv. {$lineart+1} fi if !narg($nmc) gui_set_layer_name[$ind_colors] $nm" [colors]" fi gui_set_layer_name[$ind_lineart] $nm if $-1==-1 # Rendering : Lineart and color layers if !$is_alpha gui_set_layer_mode[$ind_lineart] multiply fi if $3 l[{$1==1}] { # Discard contour guides if $is_alpha 100%,100%,1,3,255 blend. [0],alpha,1 rgb2hsv. channels. 2 *. 255 negate. channels.. 0,{0,s-2} s={0,s} luminance[0] to_colormode[0] $s a c else s:=s to_rgb rgb2hsv channels 2 * 255 to_colormode $s fi } fi elif !$-1 # Preview : Colored geometry if $is_alpha channels[$ind_lineart] 100% negate[$ind_lineart] fi to_rgb[$ind_lineart] n[$ind_lineart] 180,255 blend[$ind_lineart,$ind_colors] multiply +select_color[geometry] 0,255,255,255 ==. 0 j[$ind_lineart] [geometry],0,0,0,0,1,. k[$ind_lineart] elif $-1==1 # Preview : Colored regions k[$ind_colors] else # Preview : Colored lineart if $is_alpha channels[$ind_lineart] 100% negate[$ind_lineart] fi to_rgb[$ind_lineart] if $3 l[$ind_lineart] { s:=s to_rgb rgb2hsv channels 2 * 255 to_colormode $s } fi blend[$ind_lineart,$ind_colors] multiply k[$ind_lineart] fi } } #@gui Colorize Lineart [Propagation] : fx_colorize_lineart, fx_colorize_lineart_preview(1) #@gui : note = note("Layers ordering:") #@gui : Input Layers = choice{0,"Color Spots + Lineart","Lineart + Color Spots", #@gui : "Color Spots + Extrapolated Colors + Lineart","Lineart + Color Spots + Extrapolated Colors"} #@gui : Output Layers = _choice{1,"Single (Merged)","Extrapolated Colors + Lineart", #@gui : "Lineart + Extrapolated Colors","Color Spots + Extrapolated Colors + Lineart", #@gui : "Lineart + Color Spots + Extrapolated Colors"} #@gui : Extrapolate Colors As = choice("One Layer","Two Layers","Three Layers","Four Layers","Five Layers", #@gui : "Six Layers","Seven Layers","Eight Layers","Nine Layers","Ten Layers","One Layer per Single Color", #@gui : "One Layer per Single Region") #@gui : sep = separator() #@gui : Smoothness = float(0.05,0,1) #@gui : sep = separator() #@gui : note = note{"Note: You probably need to select All for the Input layers option #@gui : on the left.\n #@gui : Color Spots = your layer with color indications.\n #@gui : Lineart = your layer with line-art (B&W or transparent).\n #@gui : Extrapolated Colors = the G'MIC generated layer with flat colors.\n\n #@gui : Warnings: #@gui : \n - Do not rely too much on the preview, it is probably not accurate ! #@gui : \n - Activate option Extrapolate color as one layer per single color/region only if you have #@gui : a lot of available memory ! #@gui : "} #@gui : sep = separator() #@gui : url = link("Click here for a detailed description of this filter.",\ # "http://www.gimpchat.com/viewtopic.php?f=28&t=7567") #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé, Timothée Giet and David Revoy. #@gui :       Latest Update: 2013/19/06.") fx_colorize_lineart : if $!<2 return fi if $1<2 selection=0,1 else selection=0,1,2 fi l[$selection] { # Format input layers. if !$1 # Color strokes + drawing elif $1==1 rv # Drawing + color strokes elif $1==2 rm[1] # Color strokes + extrapolated colors + drawing elif $1==3 rm[2] rv # Drawing + color strokes + extrapolated colors. fi # Here we have only 'color strokes + drawing' -> process. +to_rgba[0] split_opacity. +.. 1 !=. 0 *[-2,-1] # Map of color labels to spread. +norm[1] n. 0,1 +histogram. 2,0,1 if i(0)>i(1) *.. -1 +.. 1 fi rm. # Determine color model of the drawing. b. $4% watershed.. . rm. # Priority map. -. 1 # Here we have 'color strokes + drawing + extrapolated colors'. # Format output layers. if !$2 rm[0] rv blend[0,1] multiply ind=-1 elif $2==1 rm[0] rv ind=0 elif $2==2 rm[0] ind=1 elif $2==3 rv[1,2] ind=1 elif $2==4 rv[0,1] ind=2 fi # Separate extrapolated colors as multiple layers. if $3" && "$ind>=0 l[$ind] { +mix_channels (65536,256,1) if $3==10 do # Split by colors. iM={1,iM} if $iM>=0 +==[1] $iM area:=is replace[1] $iM,-1 +r. 100%,100%,1,3 *. [0] rv[-2,-1] *. 255 a[-2,-1] c => $area fi while $iM>=0 else # Split by disconnected regions. label. if $3<10 %. {$3+1} fi repeat iM+1 { +==[1] $< area:=is +r. 100%,100%,1,[0] *. [0] rv[-2,-1] *. 255 a[-2,-1] c => $area } fi rm[0,1] sort_list +,n } fi } fx_colorize_lineart_preview : fx_colorize_lineart $1,0,$3,$4 #@gui Dithering : fx_ditheredbw, fx_ditheredbw_preview(0) #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue = float(0,0,360) #@gui : Saturation (%) = float(0,0,100) #@gui : Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_ditheredbw : foreach { split_opacity l[0] { luminance adjust_colors ${1-3} b $6 ditheredbw if $4" || "$5 / 255 i[0] 100%,100%,1,2 fc[0] $4,{$5%} a c hsv2rgb fi } a c } fx_ditheredbw_preview : gui_split_preview "fx_ditheredbw $*",${-3--1} #@gui Engrave : fx_engrave, fx_engrave_preview(0) #@gui : note = note("Black & White foreground:") #@gui : Radius = float(0.5,0,2) #@gui : Density = float(50,0,200) #@gui : Edges = float(0,0,10) #@gui : Coherence = float(8,0,40) #@gui : Threshold (%) = float(40,0,100) #@gui : Minimal Area = int(0,-256,256) #@gui : Flat Regions Removal = float(0,0,10) #@gui : sep = separator() #@gui : note = note("Color background:") #@gui : Add Color Background = bool() #@gui : Quantization = float(10,0,40) #@gui : Shading = int(1,0,5) #@gui : Hue = float(0,-180,180) #@gui : Saturation (%) = float(0,-100,100) #@gui : Lightness (%) = float(0,-100,100) #@gui : sep = separator() #@gui : Anti-Aliasing = choice(1,"Disabled","x1.5","x2","x3") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: Lyle Kroll and David Tschumperlé. #@gui :       Latest Update: 03/13/2015.") fx_engrave : f:=arg(1+$14,1,1.5,2,3) r:=$f*(0.2+$1) foreach { nm=${-gui_layer_name} pos=${-gui_layer_pos} if $8 [0] fi # Keep copy for color background. l[0] { split_opacity l[0] { wh:=w,h norm if $14 r {100*$f}%,{100*$f}%,1,1,3 fi if $7>0 [0] fi # Keep copy for flat regions removal. l[0] { amount:=(0.5+$2)^2 repeat 5 { b $r unsharp $r,{1+$2} c 0,255 } smooth 100,0.1,1,{$f*$3},{$f*$4} >= {100-$5}% } if $7>0 # Flat region removal. gradient_norm[1] b[1] $3 <[1] $7 max[0,1] fi if $6<0 area_fg 0,0 > {$f*$6*$6} elif $6>0 == 0 area_fg 0,0 > {$f*$6*$6} == 0 fi * 255 if $14 r $wh,1,1,2 fi } a c } # Process color background. if $!>1 l[1] { split_opacity l[0] { f:=arg(1+$14,1,1.5,2,3) if $14 r {100*$f}%,{100*$f}%,1,100%,3 fi b {$f*$9} segment_watershed 5 if $14 r $wh,1,100%,2 fi repeat $10 { guided 10,{$10*80} } rgb2hsv s c +... $11 +.. {$12%} +. $13% a c hsv2rgb } a c } =>[0] "mode(darken),name("$nm"),pos("$pos")" =>[1] "name("$nm" [colors]),pos("$pos")" fi } fx_engrave_preview : foreach { gui_split_preview "=> foo fx_engrave $* gui_merge_layers",${-3--1} } #@gui Filaments : fx_filaments, fx_filaments(0)+ #@gui : Density (%) = float(50,0,100) #@gui : Length (%) = float(50,0,100) #@gui : Contour (%) = float(75,0,100) #@gui : Distortion (%) = float(30,0,100) #@gui : Smoothness = float(10,0,100) #@gui : Rotation (deg.) = float(0,-180,180) #@gui : Local normalization = bool(1) #@gui : sep = separator() #@gui : Throw From Left = bool(1) #@gui : Throw From Right = bool(1) #@gui : Throw From Above = bool(1) #@gui : Throw From Below = bool(1) #@gui : sep = separator() #@gui : Opacity (%) = float(50,0,100) #@gui : Color model = choice("White on Black","Black on White","White on Transparent","Black on Transparent") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/12/18.") fx_filaments : density,length,contour,distortion,smoothness,angle,normalize,is_left,is_right,is_above,is_below,opacity,\ colormodel=${1-13} foreach { lightness. b. {$smoothness/10} g. xy a[-2,-1] c 100%,100%,1,3,"P = I(#-1); n = norm(P); a = atan2(P[1],P[0]) + (90 + $angle)°; [ 1/(1 + n)^0.25,cos(a),sin(a) ]" rm.. sh. 0 n. {max(1,100-$contour)%},1 rm. 100%,100% if $density" && "$length {$density*(w+h)/10},1,1,1,"* begin( const distortion = $distortion/1000; const dmax = 2*($length%)*norm(w#0,h#0); const opacity = ($opacity/10)%; const is_none = !($is_left || $is_right || $is_above || $is_below); const is_left = is_none || $is_left; const is_right = is_none || $is_right; const is_above = is_none || $is_above; const is_below = is_none || $is_below; const nb_throw_directions = sum(is_left,is_right,is_above,is_below); throw_directions = vector(#nb_throw_directions); k = 0; is_left?(throw_directions[k++] = 0); is_right?(throw_directions[k++] = 1); is_above?(throw_directions[k++] = 2); is_below?(throw_directions[k] = 3); ); # Set initial particle direction. case = throw_directions[v(nb_throw_directions -1)]; case==0?(x = 0; y = u(h#0)%h#0; dx = 1; dy = 0.5*u(-1,1)): case==1?(x = w#0 - 1; y = u(h#0)%h#0; dx = -1; dy = 0.5*u(-1,1)): case==2?(x = u(w#0)%w#0; y = 0; dx = 0.5*u(-1,1); dy = 1): case==3?(x = u(w#0)%w#0; y = h#0 - 1; dx = 0.5*u(-1,1); dy = -1); n = norm(dx,dy); dx/=n; dy/=n; # Find path of the particle through the image. repeat (dmax,dl, !(inrange(x,0,w#0-1) && inrange(y,0,h#0-1))?break(); polygon(#1,1,x,y,opacity,255); u = i(#0,x,y,0,1,1); v = i(#0,x,y,0,2,1); dx*u + dy*v<0?(u = -u; v = -v); dx = lerp(dx,u,distortion); dy = lerp(dy,v,distortion); n = norm(dx,dy)/i(#0,x,y,0,0,1); dx/=n; dy/=n; x+=dx; y+=dy; )" fi k[1] if $normalize normalize_local. , fi if $colormodel==1 *. -1 +. 255 c. 0,255 elif $colormodel==2 i[0] 100%,100%,1,1,255 a c elif $colormodel==3 i[0] 100%,100% a c fi } #@gui Freaky B&W : fx_freaky_bw, fx_freaky_bw_preview #@gui : Strength (%) = float(90,0,100) #@gui : Oddness (%) = float(20,0,100) #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/30/09.") fx_freaky_bw : foreach { split_opacity l[0] { to_rgb # Estimate gradient field of B&W result. +expand xy,1 channels. 0,4 f. ">if (c!=4,i, Rx = i(x+1,y,0,0) - i(x,y,0,0); Ry = i(x,y+1,0,0) - i(x,y,0,0); Rn = Rx^2 + Ry^2; Gx = i(x+1,y,0,1) - i(x,y,0,1); Gy = i(x,y+1,0,1) - i(x,y,0,1); Gn = Gx^2 + Gy^2; Bx = i(x+1,y,0,2) - i(x,y,0,2); By = i(x,y+1,0,2) - i(x,y,0,2); Bn = Bx^2 + By^2; n = 1e-5 + max(Rn,Gn,Bn)^"{$2%}"; val = 0; Rn>=Gn && Rn>=Bn?(i(x,y,0,3) = Rx/n; val=Ry/n): Gn>=Rn && Gn>=Bn?(i(x,y,0,3) = Gx/n; val=Gy/n): (i(x,y,0,3) = Bx/n; val=By/n); val )" channels. 3,4 luminance[0] ia={0,ia} # Estimate laplacian of final image and invert it. s. c f.. "i - i(x-1,y,0,0)" f. "i - i(x,y-1,0,0)" +[-2,-1] ilaplacian. 0 shrink. xy,1 +. $ia n. 0,255 # Merge result with original color image. j[0] [1],0,0,0,0,{$1%} rm. adjust_colors ${3-5} } a c } fx_freaky_bw_preview : gui_split_preview "fx_freaky_bw $*",${-3--1} #@gui Ink Wash : fx_ink_wash, fx_ink_wash(0) #@gui : note = note("Ink wash controls") #@gui : Size = float(0.14,0,4) #@gui : Amplitude = float(23,0,200) #@gui : sep = separator() #@gui : note = note("Check if you wish visual control on this step") #@gui : Skip All Other Steps = bool(false) #@gui : note = note ("UNcheck to reactivate the other controls") #@gui : sep = separator() #@gui : Smoother Sharpness = float(0.5,0,2) #@gui : Smoother Edge Protection = float(0.54,0,1) #@gui : Smoother Softness = float(2.25,0,10) #@gui : sep = separator() #@gui : Stretch Contrast = choice("None","Automatic","Automatic & Contrast Mask","Manual Controls") #@gui : note = note ("To activate the sliders below chose 'Manual Controls'") #@gui : sep = separator() #@gui : LN Amplitude = float(2,0,60) #@gui : LN Size = float(6,0,64) #@gui : LN Neightborhood-Smoothness = float(5,0,40) #@gui : LN Average-Smoothness = float(20,0,40) #@gui : sep = separator() #@gui : note = note("Author: PhotoComiX. #@gui :       Latest Update: 2011/05/04.") #@gui : url = link(0,"Forum thread about the filter discussion","http://gimpchat.com/viewtopic.php?f=10&t=914") fx_ink_wash : foreach { split_opacity l[0] { fx_pencilbw. $1,$2,0,0,0 if $3==1 continue elif !$3 fx_smooth_anisotropic. 60,$4,$5,$6,1.1,0.8,30,2,0,1,1,0,1,16 fi if $7==1 normalize_local. 2,6,5,24,1,0,255 elif $7==2 normalize_local. 2,6,5,24,1,0,255 fx_contrast_swm 2,0,0.512 elif $7==3 fx_normalize_local. $8,$9,$10,$11,1,3,0 fi } a c } #@gui Pencil : fx_pencilbw, fx_pencilbw_preview(0) #@gui : Size = float(0.3,0,5) #@gui : Amplitude = float(60,0,200) #@gui : Hue = float(0,0,360) #@gui : Saturation = float(0,0,1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/05/03.") fx_pencilbw : pencilbw $1,$2 if $3" || "$4 foreach { split_opacity /[0] 255 i[0] 100%,100%,1,1,$4 i[0] 100%,100%,1,1,$3 a[0-2] c hsv2rgb[0] a c } fi fx_pencilbw_preview : gui_split_preview "fx_pencilbw $*",${-3--1} #@gui Pencil Portrait : fx_pencil_portraitbw, fx_pencil_portraitbw_preview(0) #@gui : Stroke Length = float(30,0,500) #@gui : Stroke Angle = float(120,0,180) #@gui : Contour Threshold = float(1,0,10) #@gui : Opacity = float(0.5,0,1) #@gui : Color = color(144,79,21) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: Jamac4k and David Tschumperlé. #@gui :       Latest Update: 2015/29/06.") fx_pencil_portraitbw : foreach { split_opacity l[0] { +b 2% +blend divide rm.. luminance. fx_ink_wash.. 0,167,0,0.5,0.54,2.25,0,2,6,5,20 +fx_hardsketchbw. 80,32,1.89,0.21,31.46,0,0 +fx_sketchbw.. 1,$2,180,$1,$3,0.03,0,0.6,0.1,0.6,0.25,1,0,1,0 blend[0,1] darken blend[0,1] multiply,0.5 blend[0,1] lighten,$4 normalize_local , to_rgb +fc ${5-7} blend softlight } a c } fx_pencil_portraitbw_preview : gui_split_preview "fx_pencil_portraitbw $*",${-3--1} #@gui Stamp : fx_stamp, fx_stamp_preview(0) #@gui : Auto-Threshold = bool(1) #@gui : Threshold = int(50,0,100) #@gui : Smoothness = float(0,0,10) #@gui : Sharpening = float(0,0,30) #@gui : Grain = float(0,0,100) #@gui : Negative = bool() #@gui : Anti-Aliasing = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: Antaron, Mahvin and David Tschumperlé. #@gui :      Latest Update: 2015/16/03.") fx_stamp : foreach { split_opacity l[0] { wh:=w,h norm if $7 r 150%,150%,1,1,3 fi noise $5 if $1 otsu 256 else >= $2% fi b {($7?1.5:1)*$3},0 sharpen $4 n 0,255 apply_curve 1,0,0,101,33,170,229,255,255 if $7 r $wh,1,1,2 fi if $6 negate fi } a c } fx_stamp_preview : gui_split_preview "fx_stamp $*",${-3--1} #@gui ____Colors #---------------------- #@gui Abstraction : fx_color_abstraction, fx_color_abstraction_preview(0) #@gui : Smoothness = float(1,0,10) #@gui : Levels = int(10,2,100) #@gui : Contrast = float(0.2,0.01,1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/10/19.") fx_color_abstraction : foreach { split_opacity l[0] { to_rgb b $1 s c quantize $2,1,0 area 0 ^ $3 n 0,255 } a c } fx_color_abstraction_preview : gui_split_preview "fx_color_abstraction $*",${-3--1} #@gui Apply External CLUT : fx_apply_haldclut, fx_apply_haldclut_preview(1)+ #@gui : Specify HaldCLUT As = choice(2,"Top Layer","Bottom Layer","Filename") #@gui : HaldCLUT Filename = filein() #@gui : note = note("Note: Do not forget to set the Input layers option if you select #@gui : Top layer or Bottom layer.") #@gui : sep = separator() #@gui : Strength (%) = float(100,0,100) #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : Normalize Colors = choice("None","Pre-Normalize","Post-Normalize","Both") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/02/08.") fx_apply_haldclut : skip "${2=}" mode=$1 filename="$2" strength,brightness,contrast,gamma,hue,saturation,normalize=${3-9} if $mode<2 # CLUT as a layer if $!<2 gui_warning_preview "Input layer with HaldCLUT is missing" return fi ind_clut:=$mode?$!-1:0 else # CLUT as a file l { 0 => "$2" ext={x} rm. if lowercase(['$ext'])=='cube' input_cube "$2" else i "$2" fi ind_clut:=$!-1 onfail gui_warning_preview "Specified HaldCLUT filename not found" return } fi if {$ind_clut,iM>512} /[$ind_clut] 255 fi # Possibly a 16bits HaldCLUT. if $normalize==1" || "$normalize==3 # Pre-normalization foreach { if $>!=$ind_clut split_opacity balance_gamma[0] , a c fi } fi repeat $! { if $>!=$ind_clut +map_clut[$>] [$ind_clut] j[$>] .,0,0,0,0,{$strength%} rm. fi } rm[$ind_clut] adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255 if $normalize==2" || "$normalize==3 # Post-normalization foreach { split_opacity n[0] 0,255 a c } fi fx_apply_haldclut_preview : skip "${2=}" if $1<2 gui_warning_preview "No preview available in this mode" return fi gui_split_preview "fx_apply_haldclut $1,\"$2\",${3--2}",${-3--1} #@gui Apply From CLUT Set : apply_from_clut_set, apply_from_clut_set_preview(1)+ #@gui : CLUT Set (.gmz File) = file("") #@gui : Index = int(1,1,1024) #@gui : sep = separator() #@gui : Strength (%) = float(100,0,100) #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : Normalize Colors = choice("None","Pre-Normalize","Post-Normalize","Both") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2022/05/08.") apply_from_clut_set : skip "${1=}" m "_title : ('\"$""*\"') replace_str. \"_\",\" \" replace_str. \"iii\",\"III\" f. \"(!y || j(0,-1)==32) && i>=_'a' && i<=_'z'?uppercase(i):i\" u {t} rm." is_file_error=0 l[] { "$1" nb_cluts,ind:=l,($2-1)%l k[$ind] onfail rm is_file_error=1 } if $is_file_error gui_error_preview "Unable to load specified CLUT set" else _title {n} clut_name=${} decompress_clut. {0$_is_preview?17:33} store. _clut if 0$_is_preview foreach { gui_split_preview "_apply_from_clut_set \"$1\",${2--1}",${-3--1} } to "#"{$ind+1}"/"$nb_cluts":\n"$clut_name,0.01~,1~,5% else foreach { _apply_from_clut_set $"*" } fi fi apply_from_clut_set_preview : skip "${1=}" _is_preview=1 k[0] apply_from_clut_set $"*" _apply_from_clut_set : skip "${1=}" if isin($9,1,3) split_opacity n[0] 0,255 a c fi $_clut +map_clut.. . rm.. j.. .,0,0,0,0,{$3%} rm. adjust_colors ${4-8},0,255 if isin($9,2,3) split_opacity n[0] 0,255 a c fi #@gui Basic Adjustments : fx_adjust_colors, fx_adjust_colors_preview #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/16/06.") fx_adjust_colors : adjust_colors ${1-5},0,255 fx_adjust_colors_preview : gui_split_preview "fx_adjust_colors $*",${-3--1} #@gui Boost Chromaticity : fx_boost_chroma, fx_boost_chroma_preview(1) #@gui : Amplitude (%) = float(50,0,100) #@gui : Color Space = choice{"YCbCr (Distinct)","YCbCr (Mixed)","Lab (Distinct)","Lab (Mixed)"} #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/19/07.") fx_boost_chroma : foreach { split_opacity l[0] { +to_rgb if $2>=2 srgb2rgb rgb2lab. if $2==2 sh. 1 sh.. 2 equalize[-2,-1] rm[-2,-1] else sh. 1,2 equalize. rm. fi lab2rgb. rgb2srgb else rgb2ycbcr. if !$2 sh. 1 sh.. 2 equalize[-2,-1] rm[-2,-1] else sh. 1,2 equalize. rm. fi ycbcr2rgb. fi j.. .,0,0,0,0,{$1%} rm. } a c } fx_boost_chroma_preview : gui_split_preview "fx_boost_chroma $*",${-3--1} #@gui Boost-Fade : fx_boost_fade, fx_boost_fade_preview #@gui : Amplitude = float(5,0,10) #@gui : Chromaticity From = choice("YCbCr","Lab") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/11/26.") fx_boost_fade : foreach { 100%,100%,1,3 rand. 0,1 b. {10-10*($1/10)^0.5} n. 0,255 to_colormode 0 a z ac "s z match_histogram.. . rm.",${"arg0 $2,ycbcr_cbcr,lab_ab"} } fx_boost_fade_preview : gui_split_preview "fx_boost_fade $1,$2",${-3--1} #@gui Channel Processing : fx_channel_processing, fx_channel_processing_preview(1) #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Smoothness = float(0,0,10) #@gui : Value Action = choice("None","Cut","Cut & Normalize","Normalize","Threshold") #@gui : Low Value = float(0,0,100) #@gui : High Value = float(100,0,100) #@gui : Quantization = int(256,1,256) #@gui : Equalization = bool(0) #@gui : Negation = bool(0) #@gui : sep = separator() #@gui : Tones Range = choice("All tones","Shadows","Mid-Tones","Highlights") #@gui : Tones Smoothness = float(2,0,10) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") _fx_channel_processing : adjust_colors ${1-3} b. $4% if $5==1 c. $6%,$7% elif $5==2 c. $6%,$7% n. 0,255 elif $5==3 n. $6%,$7% elif $5==4 ir. $6%,$7% *. 255 fi if $8!=256 quantize. $8,1,0 fi if $9 equalize. fi if $10 negate. fi fx_channel_processing : repeat $! { l. { split_opacity rv to_rgb. fx_start_mix $11,$12 ac. "_fx_channel_processing $1,$2,$3,$4,$5,$6,$7,$8,$9,$10",$13,1 fx_end_mix $11 if $!!=3 rv a c fi } mv. 0 } fx_channel_processing_preview : gui_split_preview "fx_channel_processing $*",${-3--1} #@gui Channels to Layers : fx_channels2layers, fx_channels2layers_preview #@gui : Colorspace = choice("RGB","CMY","HSV") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/15/07.") fx_channels2layers : foreach { nm=${-gui_layer_name} to_rgb if !$1 # RGB s[0] c r[0] 100%,100%,1,3,0,0,0,0,0,0 =>[0] "name("$nm" [red]),mode(add)" r[1] 100%,100%,1,3,0,0,0,0,0,0.5 =>[1] "name("$nm" [green]),mode(add)" r[2] 100%,100%,1,3,0,0,0,0,0,1 =>[2] "name("$nm" [blue]),mode(add)" elif $1==1 # CMY rgb2cmy[0] -[0] 255 s[0] c r[0] 100%,100%,1,3,0,0,0,0,0,0 =>[0] "name("$nm" [cyan]),mode(difference)" r[1] 100%,100%,1,3,0,0,0,0,0,0.5 =>[1] "name("$nm" [magenta]),mode(difference)" r[2] 100%,100%,1,3,0,0,0,0,0,1 =>[2] "name("$nm" [yellow]),mode(difference)" +[0-2] 255 i[0] 100%,100%,1,3,255 =>[0] "name("$nm" [base]),mode(difference)" else # HSV rgb2hsv[0] s[0] c,-2 r[0] 100%,100%,1,3,0,0 sh[0] 2 f. 1 rm. =>[0] "name("$nm" [color]),mode(normal)" r[1] 100%,100%,1,3,0,0,0,0,0,1 =>[1] "name("$nm" [value]),mode(value)" hsv2rgb[0,1] rv[0,1] fi } fx_channels2layers_preview : foreach { fx_channels2layers $* foreach { to "#"{1+$>},1,1,43,7,1,255 } frame xy,1,0 frame xy,3,255 append_tiles , } #@gui Color Balance : fx_balance_gamma, fx_balance_gamma_preview #@gui : Neutral Color = color(128,128,128) #@gui : Stretch Colors = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/01/07.") fx_balance_gamma : foreach { split_opacity if $!>1 +!=. 0 *[0,-1] fi l[0] { balance_gamma ${1-3} if $4 n 0,255 fi } a c } fx_balance_gamma_preview : gui_split_preview "fx_balance_gamma $*",${-3--1} #@gui Color Blindness : colorblind, fx_colorblind_preview #@gui : Blindness Type = choice("Protanopia","Protanomaly","Deuteranopia","Deuteranomaly","Tritanopia", #@gui : "Tritanomaly","Achromatopsia","Achromatomaly") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter simulates different types of colorblindness vision. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/04.") fx_colorblind_preview : gui_split_preview "colorblind $*",${-3--1} #@gui Color Presets : fx_color_presets, fx_color_presets_preview(1)+ #@gui : LUTs Pack = choice{21,"Abigail Gonzalez (21)","Alex Jordan (81)","Berat (10)","Cinematic (8)", #@gui : "Cinematic Travel (29)","Creative Pack (33)","EditingCorp (60)","Eric Ellerbrock (14)", #@gui : "FilterGrade Cinematic (8)","Hollywood Movies (74)","InAvision (15)","J.T. Semple (14)","Kyler Holland (10)", #@gui : "Lutify.Me (7)","Michael Ezra (2)","Moviz (48)","Ohad Peretz (7)","Olivio Sarikas (19)","ON1 Photography (90)", #@gui : "PictureFX (25)","Pixelmator (45)","PIXLS.US (31)","Purple11 (12)","RocketStock (35)","Shamoon Abbasi (25)", #@gui : "SmallHD Movie Look (7)","Todd Blankenship (13)","Youssef Hossam (5)","Others (69)"} ##### Abigail Gonzales #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Blade Runner","Blue House","Blue Ice","Caribe","Cinema","Cinema 2","Cinema 3","Cinema 4","Cinema 5", #@gui : "Cinema Noir","Cinematic for Flog","Day4Nite","Eterna for Flog","Filmic","Fuji HDR", #@gui : "Golden Gate","Matrix","Monochrome 1","Monochrome 2","Old West","Science Fiction"}_0 ##### Alex Jordan #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Action Magenta 01","Action Red 01","Adventure 1453","Aggressive Highlights Recovery 5", #@gui : "Bleech Bypass Green","Bleech Bypass Yellow 01","Blue Dark","Blue Shadows 01","Bright Green 01", #@gui : "Brownish","Colorful 0209","Conflict 01","Contrast with Highlights Protection","Contrasty Afternoon", #@gui : "Contrasty Green","Cross Process CP 130","Cross Process CP 14","Cross Process CP 15", #@gui : "Cross Process CP 16","Cross Process CP 18","Cross Process CP 3","Cross Process CP 4", #@gui : "Cross Process CP 6","Dark Green 02","Dark Green 1","Dark Place 01","Dream 1","Dream 85", #@gui : "Faded Retro 01","Faded Retro 02","Film 0987","Film 9879","Film Highlight Contrast","Flat 30", #@gui : "Green 2025","Green Action","Green Afternoon","Green Conflict","Green Day 01","Green Day 02", #@gui : "Green G09","Green Indoor","Green Light","Harsh Day","Harsh Sunset","Highlights Protection", #@gui : "Indoor Blue","Low Contrast Blue","Low Key 01","Magenta Day","Magenta Day 01","Magenta Dream", #@gui : "Memories","Moonlight 01","Mostly Blue","Muted 01","Night 01","Only Red","Only Red and Blue", #@gui : "Operation Yellow","Orange Dark 4","Orange Dark 7","Orange Dark Look","Orange Underexposed", #@gui : "Protect Highlights 01","Red Afternoon 01","Red Day 01","Red Dream 01","Retro Brown 01", #@gui : "Retro Magenta 01","Retro Yellow 01","Saturated Blue","Smart Contrast","Subtle Blue", #@gui : "Subtle Green","Yellow 55B","Yellow Film 01"}_0 ##### Berat #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Brown BM","Cine Blue","Cine BM4k","Golden Time","Green and Orange","Monochrome","Sevsuz","Sunlight Love", #@gui : "Western","Western Lut 2"}_0 ##### Cinematic #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Deep","Dimension","Enchanted","Flavin","Frosted","Shine","Ultra Water","Wipe"}_0 ##### Cinematic Travel #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Blue Cold Fade","Bright Teal Orange","Bright Warm","Clear Teal Fade","Cold Clear Blue","Cold Clear Blue 1", #@gui : "Deep Blue","Deep Dark Warm","Deep High Contrast","Deep Teal Fade","Deep Warm Fade","Faded Green", #@gui : "Greenish Contrasty","Greenish Fade","Greenish Fade 1","Hard Teal Orange","Neutral Teal Orange", #@gui : "Neutral Warm Fade","Smooth Clear","Smooth Green Orange","Smooth Teal Orange","Teal Fade","Very Warm Greenish", #@gui : "Warm Dark Contrasty","Warm Fade","Warm Fade 1","Warm Neutral","Warm Sunset Red","Warm Teal"}_0 ##### Creative Pack #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Anime","Bleach Bypass 1","Bleach Bypass 2","Bleach Bypass 3","Bleach Bypass 4","Candle Light", #@gui : "Color Negative","Crisp Warm","Crip Winter","Drop Blues","Edgy Ember","Fall Colors","Foggy Night", #@gui : "Futuristic Bleak 1","Futuristic Bleak 2","Futuristic Bleak 3","Futuristic Bleak 4","Horror Blue", #@gui : "Late Sunset","Moonlight","Night From Day","Red Blue Yellow","Smokey","Soft Warming","Teal Magenta Gold", #@gui : "Teal Orange","Teal Orange 1","Teal Orange 2","Teal Orange 3","Tension Green 1","Tension Green 2", #@gui : "Tension Green 3","Tension Green 4"}_0 #### EditingCorp #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Ampio","Asistas","Atusa","Basuco","Beati","Bisogno","Boyado","Calidum","Colore","Convold","Cosa","Culor", #@gui : "Dimmer","Ensaya","Falua","Farkling","Fatos","Fezzle","Filo","Foresta","Huesio","Husmes","Huyan","Ideo", #@gui : "Jarklin","Lavark","Levex","Litore","Loro","Lotta","Maesky","Mercato","Molti","Motus","Mucca","Nigrum","Onda", #@gui : "Padre","Partia","Perso","Picola","Randas","Satid","Scala","Scrittle","Seges","Selor","Sensum","Sino","Soldi", #@gui : "Strano","Stringa","Tirare","Tutto","Upglow","Valize","Valsky","Vita","Vubes","Wavefire"}_0 ##### Eric Ellerbrock #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Avalanche","Black Star","Helios","Hydracore","Hypnosis","Killstreak","Nemesis","Night Blade 4", #@gui : "Paladin","Seringe 4","Serpent","Terra 4","Victory","Yellowstone"}_0 ##### FilterGrade Cinematic #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Cine Basic","Cine Bright","Cine Cold","Cine Drama","Cine Teal Orange 1","Cine Teal Orange 2", #@gui : "Cine Vibrant","Cine Warm"}_0 ##### Hollywood Movies #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "12 Years a Slave","1917","Ad Astra","Aladdin","Ant-Man","Aquaman","Avengers Endgame","Baby Driver", #@gui : "Bad Boys for Life","Beauty and the Beast","Black Panther","Bohemian Rhapsody","Bombshell","Captain Marvel", #@gui : "City of God","Creed 2","Doctor Strange","Dunkirk","Fight Club","Ford v Ferrari","Green Book","Greyhound", #@gui : "Inception","I Tonya","Jojo Rabbit","Joker ","Jumanji The Next Level","Jurassic World Fallen Kingdom", #@gui : "Justice League","Kingsman The Golden Circle","Knives Out","La La Land","Little Women","Logan", #@gui : "Mad Max Fury Road","Marriage Story","Moonlight","Mother!","No Time to Die","Once Upon a Time in Hollywood", #@gui : "Parasite","Pirates of the Caribbean","Rocketman","Separation","Sicario","Spider-Man Far From Home", #@gui : "Spotlight","Star Wars The Rise of Skywalker","Sully","TENET","The Darkest Hour","The Dark Knight", #@gui : "The Gentelmen","The Grand Budapest Hotel","The Hurt Locker","The Irishman","The Lighthouse","The Lobster", #@gui : "The Martian","The Revenant","The Shape of Water","The Social Network","The Two Popes","The Way Back", #@gui : "Thor Ragnarok","Top Gun Maverick","Uncut Gems","Underwater","Venom","War for the Planet of the Apes", #@gui : "Wolf of Wall Street","Wonder Woman","X-Men Dark Phoenix","Zombieland Double Tap"}_0 ##### InAvision #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "7Drk21","BC Darkum","Brown Mobster","Cold Ice","Dark Man X","Film GB-19","Formula B","Gremerta", #@gui : "Hitman","J. Wick 21","London Nights","Louetta","Nightlife","VFB 21","Vintage Mob"}_0 ##### J.T. Semple #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Bright Green","Crisp Romance","Crushin","Frosted Beach Picnic","Just Peachy","Late Afternoon Wanderlust", #@gui : "Lush Green Summer","Magenta Coffee","Minimalist Caffeination","Mystic Purple Sunset","Nostalgia Honey", #@gui : "Spring Morning","Toasted Garden","Winter Lighthouse"}_0 ##### Kyler Holland #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "KH 1","KH 2","KH 3","KH 4","KH 5","KH 6","KH 7","KH 8","KH 9","KH 10"}_0 ##### Lutify.Me #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Hackmanite","Herderite","Heulandite","Hiddenite","Hilutite","Howlite","Hypersthene"}_0 ##### Michael Ezra #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Deep Skin Tones 2","Deep Skin Tones 3"}_0 ##### Moviz #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Moviz 1","Moviz 2","Moviz 3","Moviz 4","Moviz 5","Moviz 6","Moviz 7","Moviz 8","Moviz 9","Moviz 10", #@gui : "Moviz 11","Moviz 12","Moviz 13","Moviz 14","Moviz 15","Moviz 16","Moviz 17","Moviz 18","Moviz 19","Moviz 20", #@gui : "Moviz 21","Moviz 22","Moviz 23","Moviz 24","Moviz 25","Moviz 26","Moviz 27","Moviz 28","Moviz 29","Moviz 30", #@gui : "Moviz 31","Moviz 32","Moviz 33","Moviz 34","Moviz 35","Moviz 36","Moviz 37","Moviz 38","Moviz 39","Moviz 40", #@gui : "Moviz 41","Moviz 42","Moviz 43","Moviz 44","Moviz 45","Moviz 46","Moviz 47","Moviz 48"}_0 ##### Ohad Peretz #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Cold Simplicity 2","D and O 1","Retro Summer 3","Subtle Yellow","Teal Moonlight","True Colors 8", #@gui : "Vintage Warmth 1"}_0 #### Olivio Sarikas #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Analog Film 1","Atomic Pink","Beach Aqua Orange","Beach Faded Analog","BW but Yellow","City Dust", #@gui : "Dark Orange Teal","Day to Night King's Blue","DuoTone Blue Red","Faded Pink-ish","Flat Blue Moon", #@gui : "Honey Light","Infrared - Dust Pink","Neutral Pump","Shade King's Ink","Sunset Aqua Orange", #@gui : "Sunset Intense Violet Blue","Sunset Violet Mood","Violet Taste"}_0 ##### ON1 Photography #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "2-Strip Process","Aqua","Aqua and Orange Dark","Berlin Sky","Blues", #@gui : "Black & White-1","Black & White-2","Black & White-3","Black & White-4","Black & White-5", #@gui : "Black & White-6","Black & White-7","Black & White-8","Black & White-9","Black & White-10","Chrome 01", #@gui : "Cinematic-1","Cinematic-2","Cinematic-3","Cinematic-4","Cinematic-5","Cinematic-6","Cinematic-7", #@gui : "Cinematic-8","Cinematic-9","Cinematic-10","Classic Teal and Orange","Earth Tone Boost","Fade to Green", #@gui : "Film Print 01","Film Print 02","French Comedy","Green Blues","Green Yellow","Landscape-1","Landscape-2", #@gui : "Landscape-3","Landscape-4","Landscape-5","Landscape-6","Landscape-7","Landscape-8","Landscape-9", #@gui : "Landscape-10","Lifestyle & Commercial-1","Lifestyle & Commercial-2","Lifestyle & Commercial-3", #@gui : "Lifestyle & Commercial-4","Lifestyle & Commercial-5","Lifestyle & Commercial-6","Lifestyle & Commercial-7", #@gui : "Lifestyle & Commercial-8","Lifestyle & Commercial-9","Lifestyle & Commercial-10","Moody-1","Moody-2", #@gui : "Moody-3","Moody-4","Moody-5","Moody-6","Moody-7","Moody-8","Moody-9","Moody-10","Nature & Wildlife-1", #@gui : "Nature & Wildlife-2","Nature & Wildlife-3","Nature & Wildlife-4","Nature & Wildlife-5","Nature & Wildlife-6", #@gui : "Nature & Wildlife-7","Nature & Wildlife-8","Nature & Wildlife-9","Nature & Wildlife-10","Oranges","Portrait-1", #@gui : "Portrait-2","Portrait-3","Portrait-4","Portrait-5","Portrait-6","Portrait-7","Portrait-8","Portrait-9", #@gui : "Portrait10","Purple","Reds","Reds Oranges Yellows","Studio Skin Tone Shaper","Vintage Chrome"}_0 ##### Picture FX #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "AnalogFX - Anno 1870 Color","AnalogFX - Old Style I","AnalogFX - Old Style II","AnalogFX - Old Style III", #@gui : "AnalogFX - Sepia Color","AnalogFX - Soft Sepia I","AnalogFX - Soft Sepia II", #@gui : "PictureFX - Faux Infrared B&W1","PictureFX - Faux Infrared Color P2","PictureFX - Faux Infrared Color P3", #@gui : "PictureFX - Faux Infrared R0a","PictureFX - Faux Infrared R0b","PictureFX - Faux Infrared YP1", #@gui : "GoldFX - Bright Spring Breeze","GoldFX - Bright Summer Heat","GoldFX - Hot Summer Heat", #@gui : "GoldFX - Perfect Sunset 01min","GoldFX - Perfect Sunset 05min","GoldFX - Perfect Sunset 10min", #@gui : "GoldFX - Spring Breeze","GoldFX - Summer Heat", #@gui : "TechnicalFX - Backlight Filter","ZilverFX - B&W Solarization","ZilverFX - InfraRed", #@gui : "ZilverFX - Vintage B&W"}_0 ##### Pixelmator #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Black & White 01","Black & White 02","Black & White 03","Black & White 04","Black & White 05",\ # "Black & White 06", #@gui : "Cinematic 01","Cinematic 02","Cinematic 03","Cinematic 04","Cinematic 05","Cinematic 06","Cinematic 07", #@gui : "Classic Films 01","Classic Films 02","Classic Films 03","Classic Films 04","Classic Films 05", #@gui : "Landscape 01","Landscape 02","Landscape 03","Landscape 04","Landscape 05", #@gui : "Modern Films 01","Modern Films 02","Modern Films 03","Modern Films 04","Modern Films 05","Modern Films 06",\ # "Modern Films 07", #@gui : "Night 01","Night 02","Night 03","Night 04","Night 05", #@gui : "Urban 01","Urban 02","Urban 03","Urban 04","Urban 05", #@gui : "Vintage 01","Vintage 02","Vintage 03","Vintage 04","Vintage 05"}_0 ##### PIXLS.US #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Amstragram","Amstragram+","Autumn","Cinematic Lady Bird","Cinematic Mexico","Dark Blues in Sunlight", #@gui : "Delicatessen","Expired 69","Faded Look","Faded Print","Hypressen","Magenta Yellow","Metropolis", #@gui : "Modern Film","Newspaper","Night Spy","Progressen","Prussian Blue","Seventies Magazine","Street", #@gui : "Sweet Bubblegum","Sweet Gelatto","Taiga","Tarraco","Unknown","Uzbek Bukhara","Uzbek Marriage", #@gui : "Uzbek Samarcande","Velvetia","Warm Vintage","Whiter Whites"}_2 ##### Purple11 #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Going for a Walk","Good Morning","Nah","Once Upon a Time","Passing By","Serenity", #@gui : "Smooth Sailing","Undeniable","Undeniable 2","Urban Cowboy","We'll See","You Can Do It"}_0 ##### RocketStock #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Arabica 12","Ava 614","Azrael 93","Bourbon 64","Byers 11","Chemical 168","Clayton 33","Clouseau 54", #@gui : "Cobi 3","Contrail 35","Cubicle 99","Django 25","Domingo 145","Faded 47","Folger 50","Fusion 88", #@gui : "Hyla 68","Korben 214","Lenox 340","Lucky 64","McKinnon 75","Milo 5","Neon 770","Paladin 1875","Pasadena 21", #@gui : "Pitaya 15","Reeve 38","Remy 24","Sprocket 231","Teigen 28","Trent 18","Tweed 71","Vireo 37","Zed 32", #@gui : "Zeke 39"}_0 ##### Shamoon Abbasi #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "City 7","Coffee 44","Date 39","Day for Night","Denoise Simple 40","Desert Gold 37","Directions 23", #@gui : "Drop Green Tint 14","Elegance 38","Golden Night Softner 43","Golden Sony 37","Green 15","Happyness 133", #@gui : "HLG 1","Industrial 33","Morning 6","Morroco 16","Night King 141","Rest 33","Shadow King 39","Spy 29", #@gui : "Thriller 2","Turkiest 42","Vintage 163","Wooden Gold 20"}_0 ##### SmallHD Movie Look #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Apocalypse This Very Moment","B-Boyz 2","Bob Ford","Life Giving Tree","Moonrise","Saving Private Damon", #@gui : "The Matrices"}_0 ##### Todd Blankenship #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Blue Architecture","Blue Hour","Cold Chrome","Crisp Autumn","Dark And Somber","Hard Boost", #@gui : "Long Beach Morning","Lush Green","Magic Hour","Natural Boost","Orange And Blue","Soft Black And White", #@gui : "Waves"}_0 ##### Youssef Hossam #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Cinematic Forest","City","Darkness","Hallowen Dark","Sea"}_0 ##### Others #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "60's","60's (faded)","60's (faded alt)","Alien green","Black & White","Bleach bypass","Blue mono", #@gui : "Cinematic-01","Cinematic-02","Cinematic-03", #@gui : "Color (rich)","Faded","Faded (alt)","Faded (analog)","Faded (extreme)","Faded (vivid)","Expired (fade)", #@gui : "Expired (polaroid)","Extreme","Fade","Faux infrared","Golden","Golden (bright)","Golden (fade)", #@gui : "Golden (mono)","Golden (vibrant)","Green mono","Hong Kong","Instant-C","K-Tone Vintage Kodachrome", #@gui : "Light (blown)","Lomo","Mono tinted","Muted fade", #@gui : "Mute shift","Natural (vivid)","Nostalgic","Orange tone","Pink fade","Purple","Retro","Rotate (muted)", #@gui : "Rotate (vibrant)","Rotated","Rotated (crush)","Smooth crome-ish","Smooth fade","Soft fade","Solarize color", #@gui : "Solarized color2","Summer","Summer (alt)","Sunny","Sunny (alt)","Sunny (warm)","Sunny (rich)","Super warm", #@gui : "Super warm (rich)","Sutro FX","Vibrant","Vibrant (alien)","Vibrant (contrast)","Vibrant (crome-ish)", #@gui : "Vintage","Vintage (alt)","Vintage (brighter)","Warm","Warm (highlight)","Warm (yellow)"}_0 #@gui : Thumbnail Size = int(512,0,1024)_1 #@gui : sep = separator() #@gui : Strength (%) = float(100,0,100) #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : Normalize Colors = choice("None","Pre-Normalize","Post-Normalize","Both") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = value(0)_2+ #@gui : sep = separator() #@gui : note = note("Note: The color LUTs proposed in this category come from:\n") #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # Abigail Gonzalez - FreshLUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # Alex Jordan - FreshLUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
Berat - FreshLUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
Free Cinematic LUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
&n\ # bsp; 30 Cinematic Travel Color
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  RawTherapee Film Simulation
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # EditingCorp - 60 Free Hand-Picked LUTs For Cinematic Color Grading
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # Eric Ellerbrock - FreshLUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  FilterGrade Free Cinematic LUTs Pack
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
Hollywood LUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # InAvision - FreshLUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # J.T. Semple - FreshLUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # Kyler Holland 10 Free CLUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  Lutify.Me Free LUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
Michael Ezra\ #
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
Moviz LUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
Ohad Peretz - FreshLUTs
"} #@gui : sep = value(0)_0+ #@gui : #@gui : note = note{"
  \ # Olivio Sarikas - Free LUT Packs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # ON1 Free Photography LUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
PictureFX \ # - A Free HaldCLUT Set
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # Pixelmator Free LUTs
"} #@gui : sep = value(0)_2+ #@gui : note = note{"
  \ # PIXLS.US Contributors
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # Purple11 - Free LUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  RocketStock 35 Free LUTs for Color Grading
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  \ # Shamoon Abbasi - FreshLUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  SmallHD Free \ # Movie Look Pack
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
  Todd \ # Blankenship - Free LUTs
"} #@gui : sep = value(0)_0+ #@gui : note = note{"
Youssef Hossam
"} #@gui : sep = value(0)_2+ #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé. #@gui :         Latest Update: 2023/06/01.") fx_color_presets : category=${arg0\ $1,abigailgonzalez,alexjordan,berat,cinematic,cinematic_travel,creative,editingcorp,ericellerbrock,\ filtergrade,hollywood,inavision,jtsemple,kylerholland,lutifyme,michaelezra,moviz,ohadperetz,\ oliviosarikas,on1,picturefx,pixelmator,pixlsus,purple11,rocketstock,shamoonabbasi,smallhd,\ toddblankenship,youssefhossam,others} presets=${-_fx_cluts_$category} index:=arg(1+$1,${2-30}) thumbsize,strength,brightness,contrast,gamma,hue,saturation,normalize=${31-38} if $normalize==1" || "$normalize==3 # Pre-normalization foreach { split_opacity balance_gamma[0] , a c } fi if $index>=2 # Apply CLUT path_clut=${-path_cache} name=${arg0\ $index-2,$presets} clut $name,{0$_is_preview" && "!isfile(['{/${path_clut}clut_$name.cimgz}'])?17:48} repeat $!-1 { if $strength<100 +map_clut[$>] . j[$>] .,0,0,0,0,{$strength%} rm. else map_clut[$>] . fi } rm. adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255 if $normalize==2" || "$normalize==3 foreach { split_opacity n[0] 0,255 a c } fi # Post-normalization elif $index==1 # "None" adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255 if $normalize==2" || "$normalize==3 foreach { split_opacity n[0] 0,255 a c } fi # Post-normalization else # Collage foreach { if max(w,h)>$thumbsize rs $thumbsize,$thumbsize,2 fi } N=$! +to "Original",1%,1%,7.5%,2 Np:=narg($presets) repeat $Np { clut_name=${arg0\ $>,$presets} clut $clut_name mv. $N repeat $N { if $strength<100 [$>] +map_clut. [$N] j.. .,0,0,0,0,{$strength%} rm. else +map_clut[$>] [$N] fi adjust_colors. $brightness,$contrast,$gamma,$hue,$saturation,0,255 if $normalize==2" || "$normalize==3 l. { split_opacity n[0] 0,255 a c } fi # Post-normalization strcapitalize $clut_name clut_name=${} to. $clut_name,0.01~,0.01~,7.5%,2 } rm[$N] progress {$>*100/($Np-1)} } k[$N--1] frame xy,1,0,0,0,255 - 128 append_tiles {s=floor(sqrt($!));w>h?[s,0]:[0,s]} + 128 fi fx_color_presets_preview : _is_preview=1 index:=arg(1+$1,${2-30}) if !$index gui_warning_preview "Preview disabled in 'Collage' mode" else gui_split_preview "fx_color_presets $*",${39-41} fi u "{$1}{$2}_"{2*!$1}\ "{$3}_"{2*($1==1)}\ "{$4}_"{2*($1==2)}\ "{$5}_"{2*($1==3)}\ "{$6}_"{2*($1==4)}\ "{$7}_"{2*($1==5)}\ "{$8}_"{2*($1==6)}\ "{$9}_"{2*($1==7)}\ "{$10}_"{2*($1==8)}\ "{$11}_"{2*($1==9)}\ "{$12}_"{2*($1==10)}\ "{$13}_"{2*($1==11)}\ "{$14}_"{2*($1==12)}\ "{$15}_"{2*($1==13)}\ "{$16}_"{2*($1==14)}\ "{$17}_"{2*($1==15)}\ "{$18}_"{2*($1==16)}\ "{$19}_"{2*($1==17)}\ "{$20}_"{2*($1==18)}\ "{$21}_"{2*($1==19)}\ "{$22}_"{2*($1==20)}\ "{$23}_"{2*($1==21)}\ "{$24}_"{2*($1==22)}\ "{$25}_"{2*($1==23)}\ "{$26}_"{2*($1==24)}\ "{$27}_"{2*($1==25)}\ "{$28}_"{2*($1==26)}\ "{$29}_"{2*($1==27)}\ "{$30}_"{2*($1==28)}\ "{$31}_"{1+!$index}\ "{$32}{$33}{$34}{$35}{$36}{$37}{$38}{$39}{$40,$41}"\ "{0}_"{2*($1!=28)}\ "{0}_"{2*!$1}\ "{0}_"{2*($1==1)}\ "{0}_"{2*($1==2)}\ "{0}_"{2*($1==3)}\ "{0}_"{2*($1==4)}\ "{0}_"{2*($1==5)}\ "{0}_"{2*($1==6)}\ "{0}_"{2*($1==7)}\ "{0}_"{2*($1==8)}\ "{0}_"{2*($1==9)}\ "{0}_"{2*($1==10)}\ "{0}_"{2*($1==11)}\ "{0}_"{2*($1==12)}\ "{0}_"{2*($1==13)}\ "{0}_"{2*($1==14)}\ "{0}_"{2*($1==15)}\ "{0}_"{2*($1==16)}\ "{0}_"{2*($1==17)}\ "{0}_"{2*($1==18)}\ "{0}_"{2*($1==19)}\ "{0}_"{2*($1==20)}\ "{0}_"{2*($1==21)}\ "{0}_"{2*($1==22)}\ "{0}_"{2*($1==23)}\ "{0}_"{2*($1==24)}\ "{0}_"{2*($1==25)}\ "{0}_"{2*($1==26)}\ "{0}_"{2*($1==27)}\ "{0}" _fx_cluts_abigailgonzalez : u blade_runner,blue_house,blue_ice,caribe,cinema,cinema_2,cinema_3,\ cinema_4,cinema_5,cinema_noir,cinematic_for_flog,day_4nite,eterna_for_flog,filmic,\ fuji_hdr,goldengate,matrix,monochrome_1,monochrome_2,old_west,science_fiction _fx_cluts_alexjordan : u action_magenta_01,action_red_01,adventure_1453,agressive_highligjtes_recovery_5,bleech_bypass_green,\ bleech_bypass_yellow_01,blue_dark,blue_shadows_01,bright_green_01,brownish,colorful_0209,conflict_01,\ contrast_with_highlights_protection,contrasty_afternoon,contrasty_green,cross_process_cp_130,cross_process_cp_14,\ cross_process_cp_15,cross_process_cp_16,cross_process_cp_18,cross_process_cp_3,cross_process_cp_4,\ cross_process_cp_6,dark_green_02,dark_green_1,dark_place_01,dream_1,dream_85,faded_retro_01,faded_retro_02,\ film_0987,film_9879,film_high_contrast,flat_30,green_2025,green_action,green_afternoon,\ green_conflict,green_day_01,green_day_02,green_g_09,green_indoor,green_light,harsh_day,harsh_sunset,\ highlights_protection,indoor_blue,low_contrast_blue,low_key_01,magenta_day,magenta_day_01,magenta_dream,\ memories,moonlight_01,mostly_blue,muted_01,night_01,only_red,only_red_and_blue,operation_yellow,orange_dark_4,\ orange_dark_7,orange_dark_look,orange_underexposed,protect_highlights_01,red_afternoon_01,red_day_01,red_dream_01,\ retro_brown_01,retro_magenta_01,retro_yellow_01,saturated_blue,smart_contrast,subtle_blue,subtle_green,yellow_55b,\ yellow_film_01 _fx_cluts_berat : u brownbm,cineblue,cinebm4k,goldentime,green_and_orange,monochrome,sevsuz,sunlightlove,western,westernlut2 _fx_cluts_cinematic : u deep,dimension,enchanted,flavin,frosted,shine,ultra_water,wipe _fx_cluts_cinematic_travel : u blue_cold_fade,bright_teal_orange,bright_warm,clear_teal_fade,cold_clear_blue,cold_clear_blue_1,deep_blue,\ deep_dark_warm,deep_high_contrast,deep_teal_fade,deep_warm_fade,faded_green,greenish_contrasty,greenish_fade,\ greenish_fade_1,hard_teal_orange,neutral_teal_orange,neutral_warm_fade,smooth_clear,smooth_green_orange,\ smooth_teal_orange,teal_fade,very_warm_greenish,warm_dark_contrasty,warm_fade,warm_fade_1,warm_neutral,\ warm_sunset_red,warm_teal _fx_cluts_creative : u anime,bleachbypass_1,bleachbypass_2,bleachbypass_3,bleachbypass_4,candlelight,colornegative,crispwarm,crispwinter,\ dropblues,edgyember,fallcolors,foggynight,futuristicbleak_1,futuristicbleak_2,futuristicbleak_3,futuristicbleak_4,\ horrorblue,latesunset,moonlight,nightfromday,redblueyellow,smokey,softwarming,tealmagentagold,tealorange,\ tealorange_1,tealorange_2,tealorange_3,tensiongreen_1,tensiongreen_2,tensiongreen_3,tensiongreen_4 _fx_cluts_editingcorp : u ampio,asistas,atusa,basuco,beati,bisogno,boyado,calidum,colore,convold,cosa,culor,dimmer,ensaya,falua,farkling,\ fatos,fezzle,filo,foresta,huesio,husmes,huyan,ideo,jarklin,lavark,levex,litore,loro,lotta,maesky,mercato,molti,\ motus,mucca,nigrum,onda,padre,partia,perso,picola,randas,satid,scala,scrittle,seges,selor,sensum,sino,soldi,strano,\ stringa,tirare,tutto,upglow,valize,valsky,vita,vubes,wavefire _fx_cluts_ericellerbrock : u avalanche,black_star,helios,hydracore,hypnosis,killstreak,nemesis,night_blade_4,paladin,seringe_4,serpent,terra_4,\ victory,yellowstone _fx_cluts_filtergrade : u fgcinebasic,fgcinebright,fgcinecold,fgcinedrama,fgcinetealorange_1,fgcinetealorange_2,fgcinevibrant,fgcinewarm _fx_cluts_hollywood : u 12_years_a_slave,1917,ad_astra,aladdin,ant-man,aquaman,avengers_endgame,baby_driver,bad_boys_for_life,\ beauty_and_the_beast,black_panther,bohemian_rhapsody,bombshell,captain_marvel,city_of_god,creed_2,doctor_strange,\ dunkirk,fight_club,ford_v_ferrari,green_book,greyhound,i_tonya,inception,jojo_rabbit,joker,jumanji_the_next_level,\ jurassic_world_fallen_kingdom,justice_league,kingsman_the_golden_circle,knives_out,la_la_land,little_women,logan,\ mad_max_fury_road,marriage_story,moonlight_2,mother!,no_time_to_die,once_upon_a_time_in_hollywood,parasite,\ pirates_of_the_caribbean,rocketman,separation,sicario,spider-man_far_from_home,spotlight,\ star_wars_the_rise_of_skywalker,sully,tenet,the_dark_knight,the_darkest_hour,the_gentelmen,\ the_grand_budapest_hotel,the_hurt_locker,the_irishman,the_lighthouse,the_lobster,the_martian,the_revenant,\ the_shape_of_water,the_social_network,the_two_popes,the_way_back,thor_ragnarok,top_gun_maverick,uncut_gems,\ underwater,venom,war_for_the_planet_of_the_apes,wolf_of_wall_street,wonder_woman,x-men_dark_phoenix,\ zombieland_double_tap _fx_cluts_inavision : u 7drk_21,bc_darkum,brown_mobster,cold_ice,dark_man_x,film_gb-19,formula_b,gremerta,hitman,jwick_21,london_nights,\ louetta,nightlife,vfb_21,vintage_mob _fx_cluts_jtsemple : u brightgreen,crispromance,crushin,frostedbeachpicnic,justpeachy,lateafternoonwanderlust,lushgreensummer,\ magentacoffee,minimalistcaffeination,mysticpurplesunset,nostalgiahoney,springmorning,toastedgarden,\ winterlighthouse _fx_cluts_kylerholland : u kh1,kh2,kh3,kh4,kh5,kh6,kh7,kh8,kh9,kh10 _fx_cluts_lutifyme : u hackmanite,herderite,heulandite,hiddenite,hilutite,howlite,hypersthene _fx_cluts_michaelezra : u deepskintones2,deepskintones3 _fx_cluts_moviz : u moviz_1,moviz_2,moviz_3,moviz_4,moviz_5,moviz_6,moviz_7,moviz_8,moviz_9,moviz_10,\ moviz_11,moviz_12,moviz_13,moviz_14,moviz_15,moviz_16,moviz_17,moviz_18,moviz_19,moviz_20,\ moviz_21,moviz_22,moviz_23,moviz_24,moviz_25,moviz_26,moviz_27,moviz_28,moviz_29,moviz_30,\ moviz_31,moviz_32,moviz_33,moviz_34,moviz_35,moviz_36,moviz_37,moviz_38,moviz_39,moviz_40,\ moviz_41,moviz_42,moviz_43,moviz_44,moviz_45,moviz_46,moviz_47,moviz_48 _fx_cluts_ohadperetz : u cold_simplicity_2,d_o_1,retro_summer_3,subtle_yellow,teal_moonlight,true_colors_8,vintage_warmth_1 _fx_cluts_oliviosarikas : u analog_film_1,atomic_pink,beach_aqua_orange,beach_faded_analog,bw_but_yellow,city_dust,dark_orange_teal,\ day_to_night_kings_blue,duotone_blue_red,faded_pink-ish,flat_blue_moon,honey_light,infrared_-_dust_pink,neutral_pump,\ shade_kings_ink,sunset_aqua_orange,sunset_intense_violet_blue,sunset_violet_mood,violet_taste _fx_cluts_on1 : u 2-strip-process,aqua,aqua_and_orange_dark,berlin_sky,blues,\ bw_1,bw_2,bw_3,bw_4,bw_5,bw_6,bw_7,bw_8,bw_9,bw_10,chrome_01,\ cinematic-1,cinematic-2,cinematic-3,cinematic-4,cinematic-5,cinematic-6,cinematic-7,cinematic-8,\ cinematic-9,cinematic-10,\ classic_teal_and_orange,earth_tone_boost,fade_to_green,film_print_01,film_print_02,french_comedy,green_blues,\ green_yellow,\ landscape_1,landscape_2,landscape_3,landscape_4,landscape_5,landscape_6,landscape_7,landscape_8,landscape_9,\ landscape_10,\ lc_1,lc_2,lc_3,lc_4,lc_5,lc_6,lc_7,lc_8,lc_9,lc_10,\ moody_1,moody_2,moody_3,moody_4,moody_5,moody_6,moody_7,moody_8,moody_9,moody_10,\ nw-1,nw-2,nw-3,nw-4,nw-5,nw-6,nw-7,nw-8,nw-9,nw-10,oranges,\ portrait_1,portrait_2,portrait_3,portrait_4,portrait_5,portrait_6,portrait_7,portrait_8,portrait_9,portrait_10,\ purple_2,reds,reds_oranges_yellows,studio_skin_tone_shaper,vintage_chrome _fx_cluts_picturefx : u analogfx_anno_1870_color,analogfx_old_style_i,analogfx_old_style_ii,analogfx_old_style_iii,\ analogfx_sepia_color,analogfx_soft_sepia_i,analogfx_soft_sepia_ii,\ faux_infrared_bw_1,faux_infrared_color_p2,faux_infrared_color_p3,faux_infrared_color_r0a,\ faux_infrared_color_r0b,faux_infrared_color_yp1,\ goldfx_bright_spring_breeze,goldfx_bright_summer_heat,goldfx_hot_summer_heat,\ goldfx_perfect_sunset_01min,goldfx_perfect_sunset_05min,goldfx_perfect_sunset_10min,\ goldfx_spring_breeze,goldfx_summer_heat,technicalfx_backlight_filter,\ zilverfx_bw_solarization,zilverfx_infrared,zilverfx_vintage_bw _fx_cluts_pixelmator : u black_white_01,black_white_02,black_white_03,black_white_04,black_white_05,black_white_06,\ pmcinematic_01,pmcinematic_02,pmcinematic_03,pmcinematic_04,pmcinematic_05,pmcinematic_06,pmcinematic_07,\ classic_films_01,classic_films_02,classic_films_03,classic_films_04,classic_films_05,\ landscape_01,landscape_02,landscape_03,landscape_04,landscape_05,\ modern_films_01,modern_films_02,modern_films_03,modern_films_04,modern_films_05,modern_films_06,modern_films_07,\ pmnight_01,pmnight_02,pmnight_03,pmnight_04,pmnight_05,\ urban_01,urban_02,urban_03,urban_04,urban_05,\ vintage_01,vintage_02,vintage_03,vintage_04,vintage_05 _fx_cluts_pixlsus : u amstragram,amstragram+,autumn,cinematic_lady_bird,cinematic_mexico,dark_blues_in_sunlight,delicatessen,expired_69,\ fadedlook,faded_print,hypressen,magenta_yellow,metropolis,modern_film,newspaper,night_spy,progressen,prussian_blue,\ seventies_magazine,street,sweet_bubblegum,sweet_gelatto,taiga,tarraco,unknown,uzbek_bukhara,\ uzbek_marriage,uzbek_samarcande,velvetia,warm_vintage,whiter_whites _fx_cluts_purple11 : u good_morning,going_for_a_walk,nah,once_upon_a_time,serenity,passing_by,smooth_sailing,undeniable,undeniable2,\ urban_cowboy,well_see,you_can_do_it _fx_cluts_rocketstock : u arabica_12,ava_614,azrael_93,bourbon_64,byers_11,chemical_168,clayton_33,clouseau_54,cobi_3,contrail_35,\ cubicle_99,django_25,domingo_145,\ faded_47,folger_50,fusion_88,hyla_68,korben_214,lenox_340,lucky_64,mckinnon_75,milo_5,neon_770,paladin_1875,\ pasadena_21,pitaya_15,reeve_38,remy_24,sprocket_231,teigen_28,trent_18,tweed_71,vireo_37,zed_32,zeke_39 _fx_cluts_shamoonabbasi : u city_7,coffee_44,date_39,day_for_night,denoiser_simple_40,desert_gold_37,directions_23,drop_green_tint_14,\ elegance_38,golden_night_softner_43,golden_sony_37,green_15,happyness_133,hlg_1_1,industrial_33,morning_6,\ morroco_16,night_king_141,rest_33,shadow_king_39,spy_29,thriller_2,turkiest_42,vintage_163,wooden_gold_20 _fx_cluts_smallhd : u apocalypse_this_very_moment,bboyz_2,bob_ford,life_giving_tree,moonrise,saving_private_damon,the_matrices _fx_cluts_others : u 60s,60s_faded,60s_faded_alt,alien_green,black_and_white,bleach_bypass,blue_mono,\ cinematic_01,cinematic_02,cinematic_03,\ color_rich,faded,faded_alt,faded_analog,faded_extreme,faded_vivid,expired_fade,expired_polaroid,extreme,fade,\ faux_infrared,golden,golden_bright,golden_fade,golden_mono,golden_vibrant,green_mono,hong_kong,instantc,\ k_tone_vintage_kodachrome,light_blown,lomo,mono_tinted,\ muted_fade,mute_shift,natural_vivid,nostalgic,orange_tone,pink_fade,purple,retro,rotate_muted,\ rotate_vibrant,rotated,rotated_crush,\ smooth_cromeish,smooth_fade,soft_fade,solarized_color,solarized_color_2,summer,summer_alt,sunny,sunny_alt,\ sunny_warm,\ sunny_rich,super_warm,super_warm_rich,sutro_fx,vibrant,vibrant_alien,vibrant_contrast,vibrant_cromeish,\ vintage,vintage_alt,vintage_brighter,warm,warm_highlight,warm_yellow _fx_cluts_toddblankenship : u bluearchitecture,bluehour,coldchrome,crispautumn,darkandsomber,hardboost,longbeachmorning,lushgreen,\ magichour,naturalboost,orangeandblue,softblackandwhite,waves _fx_cluts_youssefhossam : u cinematic_forest,city,darkness,hallowen_dark,sea #@gui CLUT from After - Before Layers : fx_clut_from_ab, fx_clut_from_ab_preview #@gui : Output Mode = choice("Replace Layer with CLUT","Insert New CLUT Layer","Save CLUT as .cube or .png File") #@gui : Output CLUT Resolution = choice{4,16,25,36,49,64,81,100,121,144,169,225,256}_2 #@gui : CLUT format = choice("Hald CLUT","RGB LUT") #@gui : sep = separator() #@gui : Output Folder = _folder()_1- #@gui : Output Filename = _text("output.cube")_1+ #@gui : sep = separator() #@gui : Influence of Color Samples (%) = float(50,0,100)_2 #@gui : sep = separator() #@gui : note = note{"What is this filter for?\n\n #@gui : This filter requires at least two input layers to work properly.\n #@gui : It assumes you have an input top layer A and a base layer B such that A and #@gui : B both represent the same image but with only color variations #@gui : (typically A has been obtained from B using the color curves tool).\n\n #@gui : This filter is then able to estimate and outputs a color HaldCLUT H so that applying H #@gui : on the base layer B gives back A.\n\n #@gui : This is useful when you have a color transformation between two images, that you want to recover and #@gui : re-apply on a bunch of other images. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.       #@gui : Latest Update: 2019/08/27.") fx_clut_from_ab : skip "${4=},${5=}" if $!<2 error "At least two input layers are needed to run this filter." fi repeat $!-1 { l[$<,-1] { nm=${gui_layer_name..} i[0] {a=0$_is_preview?32:64;[a,a,a]},4 f[1] ">I(#0,round(I(#2)*(w#0-1)/255))+=[R,G,B,1]; I" l[0] { s c,-3 +max. 1 /[-3,-1] ==. 0 inpaint_pde.. .,75%,1 distance. 0 *. {-1/(1+$6)} exp. f.. "f = i(#-1); f*I + (1-f)*[x,y,z]*255/(w-1)" rm. S:=arg(1+$2,16,25,36,49,64,81,100,121,144,169,225,256) if $S!=w r. $S,$S,$S,3,3 fi c 0,255 } if $1==2 # Output as a file is_png:=str=lowercase(['"$5"']);find(str,'.png')==size(str)-4 is_cube:=str=lowercase(['"$5"']);find(str,'.cube')==size(str)-5 if !$is_png" && "!$is_cube error "Filename extension must be '.cube' or '.png'." fi if $is_png r[0] {0,r=round(whd^0.5);[r,r]},1,3,-1 o[0] "$4/$5" else if {0,w>32} rs3d[0] 32 fi output_cube[0] "$4/$5" fi rm[0] else l[0] { if $3 s z append_tiles , # RGB LUT else r[0] {0,r=round(whd^0.5);[r,r]},1,3,-1 # Hald CLUT fi } if !$1 rm[1] fi # Replace Layer with CLUT =>[0] "name(CLUT to '"$nm"')" fi if 0$_output_mode k[0] fi } } fx_clut_from_ab_preview : skip "${4=},${5=}" if $!<2 gui_warning_preview "At least two input layers are needed to run this filter." return fi _is_preview,_output_mode=1 fx_clut_from_ab 0,4,0,0,0,$6 foreach { +r {a=round(cbrt(wh));[a,a,a]},3,-1 +dclut. {0,round(0.75*w)},24 if $3 rm[0] l[0] { s z append_tiles , } else rm.. fi blend[-2,-1] alpha } file_attr:=$1==2?2:1 u "{$1}{$2}{$3}{$4}_"$file_attr"{$5}_"$file_attr"{$6}" #@gui Colorful Blobs : fx_colorful_blobs, fx_colorful_blobs_preview #@gui : Colorspace = choice(1,"sRGB","Linear RGB","Lab") #@gui : Background Color = color(200,200,200,0) #@gui : Display Blob Controls = bool(1) #@gui : sep = separator() #@gui : Blob 1 = point(25,25,1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 1 Color = color(255,0,0) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob2 = point(75,25,1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 2 Color = color(0,255,0) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob 3 = point(50,75,1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 3 Color = color(0,0,255) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob 4 = point(5,90,-1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 4 Color = color(255,255,0) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob 5 = point(5,90,-1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 5 Color = color(255,0,255) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob 6 = point(5,90,-1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 6 Color = color(0,255,255) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob 7 = point(5,90,-1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 7 Color = color(255,255,255) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob 8 = point(5,90,-1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 8 Color = color(0,0,0) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob 9 = point(5,90,-1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 9 Color = color(255,128,64) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob 10 = point(5,90,-1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 10 Color = color(255,64,128) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob 11 = point(5,90,-1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 11 Color = color(128,64,255) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : Blob 12 = point(5,90,-1,1,0,0,0,0,5) #@gui : Radius = point(50,50,0,1,0,0,0,0,5)_0 #@gui : Blob 12 Color = color(64,128,255) #@gui : Previous = value(-1,-1,-1,-1) #@gui : sep = separator() #@gui : note = note("This filter can be used to create custom palettes with given color shades. #@gui : It has been inspired by #@gui : #@gui : Adobe's Playful Palette.") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/08/26.") fx_colorful_blobs : N=12 colorspace,bgR,bgG,bgB,bgA,display_controls,\ x0,y0,rx0,ry0,R0,G0,B0,p_x0,p_y0,p_rx0,p_ry0,\ x1,y1,rx1,ry1,R1,G1,B1,p_x1,p_y1,p_rx1,p_ry1,\ x2,y2,rx2,ry2,R2,G2,B2,p_x2,p_y2,p_rx2,p_ry2,\ x3,y3,rx3,ry3,R3,G3,B3,p_x3,p_y3,p_rx3,p_ry3,\ x4,y4,rx4,ry4,R4,G4,B4,p_x4,p_y4,p_rx4,p_ry4,\ x5,y5,rx5,ry5,R5,G5,B5,p_x5,p_y5,p_rx5,p_ry5,\ x6,y6,rx6,ry6,R6,G6,B6,p_x6,p_y6,p_rx6,p_ry6,\ x7,y7,rx7,ry7,R7,G7,B7,p_x7,p_y7,p_rx7,p_ry7,\ x8,y8,rx8,ry8,R8,G8,B8,p_x8,p_y8,p_rx8,p_ry8,\ x9,y9,rx9,ry9,R9,G9,B9,p_x9,p_y9,p_rx9,p_ry9,\ x10,y10,rx10,ry10,R10,G10,B10,p_x10,p_y10,p_rx10,p_ry10,\ x11,y11,rx11,ry11,R11,G11,B11,p_x11,p_y11,p_rx11,p_ry11=$* if !0$_is_preview display_controls=0 fi if $1==1 srgb2cs=srgb2rgb cs2srgb=rgb2srgb elif $1==2 srgb2cs=srgb2lab cs2srgb=lab2srgb fi {0,s=min(w,h);[s,s]},1,4 k. 100%,100%,1,1,1e-8 repeat $N { # If center point has been moved -> Update radius point. rx$>,ry$>:="P = ["${x$>},${y$>}"]; R = ["${rx$>},${ry$>}"]; oP = ["${p_x$>},${p_y$>}"]; oP==[-1,-1]?P + [10,0]:P!=oP?R + P - oP:R" if !isnan(${x$>}) x,y,rx,ry,R,G,B:="const w1 = (w - 1)%; const h1 = (h -1)%; "\ round([${x$>}*w1,${y$>}*h1,${rx$>}*w1,${ry$>}*h1,${R$>},${G$>},${B$>}]) r:=max(1,round(norm($x-$rx,$y-$ry))) if $1 ($R^$G^$B) $srgb2cs. R,G,B={^} rm. fi f. "* pexp(x) = x<2?(res = 1; px = x^2; res+=-1.17282*px; px*=x; res+=0.683221*px; px*=x; res+=-0.110353*px):0; const r = 1.2*"($r)"; dist = norm(x-"$x",y-"$y")/r; w = pexp(dist); j(#0,0,0,0) += w*"$R"; j(#0,0,0,0,1) += w*"$G"; j(#0,0,0,0,2) += w*"$B"; i + w; " fi } sh[0] 0,2 /. [1] if $1 $cs2srgb. fi f[0] "*begin(bg = [ "$bgR,$bgG,$bgB,$bgA" ]); i(#1)<0.5?bg:[R,G,B,255]" k[0] repeat $N { if !isnan(${x$>})" && "$display_controls x,y,rx,ry,R,G,B:="const w1 = (w - 1)%; const h1 = (h -1)%; "\ round([${x$>}*w1,${y$>}*h1,${rx$>}*w1,${ry$>}*h1,${R$>},${G$>},${B$>}]) circle $x,$y,3,0.85,0xFFFFFFFF,{v=avg(crop($x-3,$y-3,7,7))>128?0:255;[v,v,v,255]} rectangle {"const x = "$rx"; const y = "$ry"; [x-2,y-2,x+2,y+2]"},0.85,0xFFFFFFFF,\ {v=avg(crop($rx-3,$ry-3,7,7))>128?0:255;[v,v,v,255]} line $x,$y,$rx,$ry,0.5,0xF0F0F0F0,255 line $x,$y,$rx,$ry,0.5,0x0F0F0F0F,0,0,0,255 fi } if 0$_is_preview u \{$colorspace\}\{$bgR,$bgG,$bgB,$bgA\}\{$display_controls\}\ \{$x0,$y0\}\{$rx0,$ry0\}\{$R0,$G0,$B0\}\{$x0,$y0,$rx0,$ry0\}\ \{$x1,$y1\}\{$rx1,$ry1\}\{$R1,$G1,$B1\}\{$x1,$y1,$rx1,$ry1\}\ \{$x2,$y2\}\{$rx2,$ry2\}\{$R2,$G2,$B2\}\{$x2,$y2,$rx2,$ry2\}\ \{$x3,$y3\}\{$rx3,$ry3\}\{$R3,$G3,$B3\}\{$x3,$y3,$rx3,$ry3\}\ \{$x4,$y4\}\{$rx4,$ry4\}\{$R4,$G4,$B4\}\{$x4,$y4,$rx4,$ry4\}\ \{$x5,$y5\}\{$rx5,$ry5\}\{$R5,$G5,$B5\}\{$x5,$y5,$rx5,$ry5\}\ \{$x6,$y6\}\{$rx6,$ry6\}\{$R6,$G6,$B6\}\{$x6,$y6,$rx6,$ry6\}\ \{$x7,$y7\}\{$rx7,$ry7\}\{$R7,$G7,$B7\}\{$x7,$y7,$rx7,$ry7\}\ \{$x8,$y8\}\{$rx8,$ry8\}\{$R8,$G8,$B8\}\{$x8,$y8,$rx8,$ry8\}\ \{$x9,$y9\}\{$rx9,$ry9\}\{$R9,$G9,$B9\}\{$x9,$y9,$rx9,$ry9\}\ \{$x10,$y10\}\{$rx10,$ry10\}\{$R10,$G10,$B10\}\{$x10,$y10,$rx10,$ry10\}\ \{$x11,$y11\}\{$rx11,$ry11\}\{$R11,$G11,$B11\}\{$x11,$y11,$rx11,$ry11\} fi fx_colorful_blobs_preview : _is_preview=1 rm {s=min(${-gui_preview_wh})/2;[s,s]},1,1 fx_colorful_blobs $* #@gui Colormap : fx_colormap,fx_colormap_preview #@gui : Colormap = choice{2,"Adaptive","Custom","Standard (256)","HSV (256)","Lines (256)","Hot (256)", #@gui : "Cool (256)","Jet (256)","Flag (256)","Cube (256)"} #@gui : Dithering = float(1,0,1) #@gui : sep = separator() #@gui : Number of Tones = int(32,2,256)_0 #@gui : Number of Colors = int(8,2,8)_0 #@gui : 1st Color = color(0,0,0)_0 #@gui : 2nd Color = color(255,255,255)_0 #@gui : 3rd Color = color(255,0,0)_0 #@gui : 4th Color = color(0,255,0)_0 #@gui : 5th Color = color(0,0,255)_0 #@gui : 6th Color = color(255,255,0)_0 #@gui : 7th Color = color(255,0,255)_0 #@gui : 8th Color = color(0,255,255)_0+ #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/27/12.") fx_colormap : foreach { split_opacity to_rgb[0] if $1>=2 # Pre-defined colormap. index[0] {$1-2},$2,1 elif $1==1 # Custom colormap. (${5-28}) z. 0,{3*$4-1} r. 3,{w/3},1,1,-1 permute. yzcx r. $3,1,1,3,3 index[0] .,$2,1 rm. else # Adaptive colormap. autoindex[0] $3,$2,{$3<=32} fi a c } fx_colormap_preview : gui_split_preview "fx_colormap $*",${-3--1} is_ad,is_cu:=2*[isbool($1),$1==1] u "{$1}{$2}"\ "{$3}_"$is_ad\ "{$4}_"$is_cu\ "{${5-7}}_"$is_cu\ "{${8-10}}_"$is_cu\ "{${11-13}}_"$is_cu\ "{${14-16}}_"$is_cu\ "{${17-19}}_"$is_cu\ "{${20-22}}_"$is_cu\ "{${23-25}}_"$is_cu\ "{${26-28}}_"$is_cu\ "{$29}{$30,$31}" #@gui Color Mask [Interactive] : fx_mask_color, gui_no_preview #@gui : Color Metric = _choice(13,"RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","Linear RGB [All]", #@gui : "Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [red chrominance]", #@gui : "YCbCr [green chrominance]","Lab [all]","Lab [lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [all]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]", #@gui : "HSV [all]","HSV [hue]","HSV [saturation]","HSV [value]","HSI [all]","HSI [intensity]","HSL [all]", #@gui : "HSL [lightness]","CMYK [cyan]","CMYK [magenta]","CMYK [yellow]","CMYK [key]","YIQ [luma]","YIQ [chromas]") #@gui : Spatial Tolerance = _float(10,0,100) #@gui : Color Tolerance = _float(5,0,100) #@gui : sep = separator() #@gui : Output Mode = _choice(0,"Masked image","Color mask") #@gui : sep = separator() #@gui : note = note{"Note: This filter is CPU consuming, so use it at least with 4+ cores #@gui : (or reduce the size of the interactive window to speed up computation)."} #@gui : note = note{"Interactions:\n #@gui : Use the following actions in the interactive window to build your color mask :\n\n #@gui : - Left mouse button make the color pointed by the mouse wanted for the mask.\n #@gui : - Right mouse button make the color pointed by the mouse unwanted for the mask.\n #@gui : - Middle mouse button or key R resets color mask.\n #@gui : - Key SPACE or TAB toggles view modes (half/full-masked RGB or color mask).\n #@gui : - Keys CTRL+D increase window size.\n #@gui : - Keys CTRL+C decrease window size.\n #@gui : - Keys CTRL+R resets window size.\n #@gui : - Keys ESC, Q or ENTER exit the interactive window. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 01/20/2017.") fx_mask_color : cs=rgb,rgb_r,rgb_g,rgb_b,lrgb,lrgb_r,lrgb_g,lrgb_b,ycbcr_y,ycbcr_cbcr,ycbcr_cb,ycbcr_cr,ycbcr_cg,\ lab,lab_l,lab_ab,lab_a,lab_b,lch,lch_c,lch_h,hsv,hsv_h,hsv_s,hsv_v,hsi,hsi_i,hsl,hsl_l,\ cmyk_c,cmyk_m,cmyk_y,cmyk_k,yiq_y,yiq_iq foreach { to_rgb nm={n} => ${-gui_layer_name} +x_mask_color ${arg0\ $1,$cs},$2,$3 if $4==1 channels. 100% fi => $nm rv } #@gui Curves : fx_curves_interactive, fx_curves_interactive_preview #@gui : Colorspace = choice{"RGB","CMY","CMYK","HSI","HSL","HSV","Lab","Lch","YCbCr"} #@gui : Output Preset as a HaldCLUT Layer = _choice("Disable","Lowres CLUT","Highres CLUT") #@gui : Apply Transformation From = _choice("New Curves [Interactive]","Curves Previously Defined") #@gui : Colorspace = value(0) #@gui : Keypoints = value(0,0,100,100,-1,0,0,100,100,-1,0,0,100,100,-1,0,0,100,100,-1,0,0,100,100) #@gui : sep = separator() #@gui : note = note{"Description:\n #@gui : This filter allows to apply color curves on your images, in many different colorspaces. #@gui : Click on the Apply or OK buttons below to open the G'MIC interactive windows and #@gui : start building your color curves. #@gui : When you're done, exit the main image window: your modified result will be transferred back to the #@gui : host software.\n\n #@gui : Once you've set curves, you can save them by pressing the Add to faves button below the filter tree. #@gui : To clear control points for your curves, click on the Reset button above. #@gui : "} #@gui : sep = separator() #@gui : note = note{"Interactions:\n #@gui : Use the following actions in the interactive windows to manage your colorization :\n\n #@gui : - Left mouse button on a curve creates a new color control point (or move an existing one).\n #@gui : - Right mouse button on a control point deletes it.\n #@gui : - Left mouse button on the main image window shows the initial image until button is released.\n #@gui : - Right mouse button on the main image window adds a keypoint to all curves from picked color.\n #@gui : - Key R on a curve resets it.\n #@gui : - Keys CTRL+D increase window size.\n #@gui : - Keys CTRL+C decrease window size.\n #@gui : - Keys CTRL+R resets window size.\n #@gui : - Keys ESC, Q or ENTER close the current window. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 09/28/2014.") fx_curves_interactive : => "Color curves" repeat 5 { __xcc_C$>=0,0,100,100 } if $4==$1 l[] { ($5) s -,-1 repeat $! { __xcc_C$>={$>,^} } rm } fi if $3 # Apply transformation from previously defined curves _xcc_colorbase=${arg\ {$4+1},rgb,cmy,cmyk,hsi,hsl,hsv,lab,lch,ycbcr} x_color_curves last else # Run interactive curve builder x_color_curves ${arg\ {$1+1},rgb,cmy,cmyk,hsi,hsl,hsv,lab,lch,ycbcr} u "{$1}{$2}{$3}{$1}{"$__xcc_C0,-1,$__xcc_C1,-1,$__xcc_C2,-1,$__xcc_C3,-1,$__xcc_C4"}" fi if $2 # Add HaldCLUT layer (0,255) (0;255) (0/255) r[-3--1] 2,2,2 a[-3--1] c if $2==2 r. 256,256,256,3,3 r. 4096,4096,1,3,-1 # High-res HaldCLUT else r. 64,64,64,3,3 r. 512,512,1,3,-1 # Low-res HaldCLUT fi x_color_curves. last fi fx_curves_interactive_preview : fx_curves_interactive $1,0,1,$4,"$5" #@gui Customize CLUT : fx_customize_clut,fx_customize_clut_preview(1)+ #@gui : Keypoint Influence (%) = float(100,0,100) #@gui : Lock Uniform Sampling = choice{0,"None","8 Keypoints (RGB Corners)","27 Keypoints","64 Keypoints", #@gui : "125 Keypoints","216 Keypoints","343 Keypoints"}, #@gui : Spatial Regularization = int(10,0,30) #@gui : sep = separator() #@gui : note = note("Global correction:") #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : Post-Normalize = bool(0) #@gui : sep = separator() #@gui : Output Corresponding CLUT = _choice("Disable","512x512 Layer","4096x4096 Layer") #@gui : Preview Type = choice{8,"Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Horizontal","Duplicate Vertical","HaldCLUT","3D CLUT (Fast)","3D CLUT (Precise)"} #@gui : CLUT Opacity = float(0.5,0,1) #@gui : sep = separator() #@gui : note = note("Color correspondences:") #@gui : Action #1 = choice(1,"Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #1 = color(0,0,0), Target Color #1 = color(0,0,0) #@gui : sep = separator() #@gui : Action #2 = choice(1,"Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #2 = color(255,255,255), Target Color #2 = color(255,196,128) #@gui : sep = separator() #@gui : Action #3 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #3 = color(0,0,0), Target Color #3 = color(0,0,0) #@gui : sep = separator() #@gui : Action #4 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #4 = color(0,0,0), Target Color #4 = color(0,0,0) #@gui : sep = separator() #@gui : Action #5 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #5 = color(0,0,0), Target Color #5 = color(0,0,0) #@gui : sep = separator() #@gui : Action #6 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #6 = color(0,0,0), Target Color #6 = color(0,0,0) #@gui : sep = separator() #@gui : Action #7 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #7 = color(0,0,0), Target Color #7 = color(0,0,0) #@gui : sep = separator() #@gui : Action #8 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #8 = color(0,0,0), Target Color #8 = color(0,0,0) #@gui : sep = separator() #@gui : Action #9 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #9 = color(0,0,0), Target Color #9 = color(0,0,0) #@gui : sep = separator() #@gui : Action #10 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #10 = color(0,0,0), Target Color #10 = color(0,0,0) #@gui : sep = separator() #@gui : Action #11 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #11 = color(0,0,0), Target Color #11 = color(0,0,0) #@gui : sep = separator() #@gui : Action #12 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #12 = color(0,0,0), Target Color #12 = color(0,0,0) #@gui : sep = separator() #@gui : Action #13 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #13 = color(0,0,0), Target Color #13 = color(0,0,0) #@gui : sep = separator() #@gui : Action #14 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #14 = color(0,0,0), Target Color #14 = color(0,0,0) #@gui : sep = separator() #@gui : Action #15 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #15 = color(0,0,0), Target Color #15 = color(0,0,0) #@gui : sep = separator() #@gui : Action #16 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #16 = color(0,0,0), Target Color #16 = color(0,0,0) #@gui : sep = separator() #@gui : Action #17 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #17 = color(0,0,0), Target Color #17 = color(0,0,0) #@gui : sep = separator() #@gui : Action #18 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #18 = color(0,0,0), Target Color #18 = color(0,0,0) #@gui : sep = separator() #@gui : Action #19 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #19 = color(0,0,0), Target Color #19 = color(0,0,0) #@gui : sep = separator() #@gui : Action #20 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #20 = color(0,0,0), Target Color #20 = color(0,0,0) #@gui : sep = separator() #@gui : Action #21 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #21 = color(0,0,0), Target Color #21 = color(0,0,0) #@gui : sep = separator() #@gui : Action #22 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #22 = color(0,0,0), Target Color #22 = color(0,0,0) #@gui : sep = separator() #@gui : Action #23 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #23 = color(0,0,0), Target Color #23 = color(0,0,0) #@gui : sep = separator() #@gui : Action #24 = choice("Ignore","Lock Source","Replace Source by Target") #@gui : Source Color #24 = color(0,0,0), Target Color #24 = color(0,0,0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/14/06.") fx_customize_clut : # Build CLUT. if !narg($_N) N=64 else N=$_N fi N1:=$N-1 $N,$N,$N,4 if $2 # Lock uniform sampling uniform_distribution {(1+$2)^3},3 repeat w { point.. {round($N1*I[$>])},1,{255*I[$>]},1 } rm. fi $=arg # Add user-defined color correspondences repeat 24 { mode=${arg{13+7*$<}} if $mode sr=${arg{14+7*$<}} sg=${arg{15+7*$<}} sb=${arg{16+7*$<}} tr=${arg{17+7*$<}} tg=${arg{18+7*$<}} tb=${arg{19+7*$<}} xyz:=round(($N1/255)*[$sr,$sg,$sb]) point. $xyz,1,{$mode==2?[$tr,$tg,$tb]:[$sr,$sg,$sb]},1 fi } s c,-3 if $1<100 # Need to compute a weighting map. +distance. 1 if $1 ^. {1/(0.05+4*$1%)} else f. 0 fi n. 0,1 => influence mv. -3 fi ==. 0 inpaint_pde.. .,100%,1,20 rm. c. 0,255 if $influence 100%,100%,100%,3,[x,y,z] n. 0,255 j. ..,0,0,0,0,1,... rm[-3,-2] fi # Apply CLUT on input layers + global color corrections. if !$3 map_clut[^-1] . # w/o spatial regularization else repeat $!-1 { # w/ spatial regularization +luminance[$>] +map_clut[$>] .. -. [$>] repeat $3 { guided. ..,2,50 } +[$>,-1] rm. } fi adjust_colors ${4-8},0,255 if $9 foreach { split_opacity n[0] 0,255 a c } fi if $10 if $10==2 r. 256,256,256,3,5 c. 0,255 fi siz:=w^1.5 r. $siz,$siz,1,3,-1 mv. 0 else rm. fi fx_customize_clut_preview : if $11<7 gui_split_preview "fx_customize_clut ${1-9},0,0,${12--1}",$11 elif $11==7 # HaldCLUT preview rm fx_customize_clut ${1-9},1,0,${12--1} elif $11>=8 # 3D CLUT preview _N:=$11>=9?64:32 k[0] to_rgb w,h={w},{h} +fx_customize_clut ${1-9},1,0,${12--1} mv. 1 r. $_N,$_N,$_N,3,-1 pointcloud3d. o3d. $12 l[] { if $2 # Lock uniform sampling uniform_distribution {(1+$2)^3},3 repeat w { circle3d {0,round($_N*I[$>])},0.75 col3d. {0,255*I[$>]} } rm[0] fi $=arg # Add user-defined color correspondences repeat 24 { mode=${arg{13+7*$<}} if $mode sr=${arg{14+7*$<}} sg=${arg{15+7*$<}} sb=${arg{16+7*$<}} tr=${arg{17+7*$<}} tg=${arg{18+7*$<}} tb=${arg{19+7*$<}} xy:=round(($_N/255)*[$sr,$sg]) z:=round(($_N/255)*$sb)-0.1 circle3d $xy,$z,0.75 col3d. {$mode==2?[$tr,$tg,$tb]:[$sr,$sg,$sb]} fi } colorcube3d *3d. {$_N/255} o3d. 0.5 col3d. 0 p3d. 1 } +3d[2--1] pose3d. 5.10656,2.04904,2.723,-316.115,-0.0815767,4.97762,-3.59262,-41.7094,\ -3.40685,2.95212,4.16756,-118.811,0,0,203,1 # Try to find the best layout for displaying preview. if $w>$h # Landscape mode rs[0,1] {0,round(w/2)} to[0] "Before",2,0,13,1,0.75 to[1] "After",2,0,13,1,0.75 a[0,1] y r[0] 100%,$h,1,3,0 else # Portrait mode. rs[0,1] ,{0,round(h/2)} to[0] "Before",2,0,13,1,0.75 to[1] "After",2,0,13,1,0.75 a[0,1] x r[0] $w,100%,1,3,0 fi snapshot3d. {0,round(1.1*min(w,h))},1.2,64,64,64 autocrop. -. 64 r. {0,max(w,$w-w)},{0,max(h,$h-h)},1,3,0,0,0.5,0.5 +. 64 to. "RGB CLUT",2,0,13,1,0.75 a {`$w>$h?_'x':_'y'`} fi #@gui Decompose Channels : fx_decompose_channels, fx_decompose_channels_preview #@gui : Color Basis = choice(7,"RGB","HSV","HSL","HSI","YUV","YCbCr","XYZ","Lab","Lch","CMY","CMYK","YIQ") #@gui : Action = choice("Decompose","Recompose") #@gui : Output Multiple Layers = _bool(0) #@gui : Include Opacity Layer = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/19/07.") fx_decompose_channels : if !$2 # Decompose if $4 to_rgba else to_rgb fi foreach { nm={n} split_opacity _s3=A _s4=A _fx_decompose_channels$1[0] s[0] c if !$3 a x => $nm else nm=${-gui_layer_name} repeat $! { gui_set_layer_name[$>] {``$nm}" ["${_s$>}"]" } fi } else # Recompose channels 0 nbc:=3+($1==10) nb:=$nbc+$4 if $3 repeat int($!/$nb) { l[0-{$nb-1}] { a[0-{$nbc-1}] c _fx_recompose_channels$1[0] a c } mv. 0 } else foreach { s x,$nb a[0-{$nbc-1}] c _fx_recompose_channels$1[0] a c } fi fi fx_decompose_channels_preview : foreach { _s3=A _s4=A fx_decompose_channels $1,$2,1,$4 if !$2 fs:=round(min(w,h)*15%) repeat $! { to[$>] ${_s$>},5,3,$fs,{max(2,round($fs/15))} } to_rgba fi } append_tiles , _fx_decompose_channels0 : _s0=R _s1=G _s2=B _fx_decompose_channels1 : rgb2hsv8 _s0=H _s1=S _s2=V _fx_decompose_channels2 : rgb2hsl8 _s0=H _s1=S _s2=L _fx_decompose_channels3 : rgb2hsi8 _s0=H _s1=S _s2=I _fx_decompose_channels4 : rgb2yuv8 _s0=Y _s1=U _s2=V _fx_decompose_channels5 : rgb2ycbcr _s0=Y _s1=Cb _s2=Cr _fx_decompose_channels6 : rgb2xyz8 _s0=X _s1=Y _s2=Z _fx_decompose_channels7 : rgb2lab8 _s0=L _s1=a _s2=b _fx_decompose_channels8 : rgb2lch8 _s0=L _s1=c _s2=h _fx_decompose_channels9 : rgb2cmy _s0=C _s1=M _s2=Y _fx_decompose_channels10 : rgb2cmyk _s0=C _s1=M _s2=Y _s3=K _fx_decompose_channels11 : rgb2yiq8 _s0=Y _s1=I _s2=Q _fx_recompose_channels0 : _fx_recompose_channels1 : hsv82rgb _fx_recompose_channels2 : hsl82rgb _fx_recompose_channels3 : hsi82rgb _fx_recompose_channels4 : yuv82rgb _fx_recompose_channels5 : ycbcr2rgb _fx_recompose_channels6 : xyz82rgb _fx_recompose_channels7 : lab82rgb _fx_recompose_channels8 : lch82rgb _fx_recompose_channels9 : cmy2rgb _fx_recompose_channels10 : cmyk2rgb _fx_recompose_channels11 : yiq82rgb #@gui Detect Skin : fx_detect_skin, fx_detect_skin_preview(1) #@gui : Skin Estimation = choice(1,"Manual","Automatic") #@gui : sep = separator() #@gui : Tolerance = float(0.5,0,1) #@gui : Smoothness = float(0.5,0,5) #@gui : Threshold = float(1,0,10) #@gui : Pre-Normalize Image = bool(1) #@gui : sep = separator() #@gui : note = note("Manual estimation:\n #@gui : Use the sliders below to target as much skin pixels as you can.") #@gui : X-Coordinate = float(50,0,100) #@gui : Y-Coordinate = float(50,0,100) #@gui : Radius = float(5,0,25) #@gui : sep = separator() #@gui : Output Mode = choice(1,"Probability Map","Opaque Skin","Transparent Skin") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/03/01.") fx_detect_skin : to_rgb m "_fx_detect_skin : if $5 balance_gamma 128,128,128 fi if $1 detect_skin $2 else detect_skin $2,$6%,$7%,$8% fi M:=iM b $3% * {255*$M/iM} * $4 c 0,255" foreach { if $9 # Opaque/transparent skin. +_fx_detect_skin a c if $9>1 sh 100% *. -1 +. 255 rm. fi else _fx_detect_skin # Probability mask. fi } um _fx_detect_skin fx_detect_skin_preview : gui_split_preview "fx_detect_skin $*",${-3--1} to_rgba if !$1 circle $6%,$7%,$8%,0.3,0,255,0,255 circle $6%,$7%,$8%,1,0xFFFFFFFF,0,255,0,255 line {$6-0.25*$8}%,{$7-0.25*$8}%,{$6+0.25*$8}%,{$7+0.25*$8}%,1,255,255,0,255 line {$6+0.25*$8}%,{$7-0.25*$8}%,{$6-0.25*$8}%,{$7+0.25*$8}%,1,255,255,0,255 fi #@gui Equalize HSV : fx_hsv_equalizer, fx_hsv_equalizer_preview #@gui : Preview Bands = bool(false) #@gui : sep = separator() #@gui : Hue Band = float(180,0,360) #@gui : Band Width = float(40,1,360) #@gui : Hue Shift = float(0,-180,180) #@gui : Saturation Correction = float(0,-0.99,0.99) #@gui : Value Correction = float(0,-0.99,0.99) #@gui : sep = separator() #@gui : Hue Band = float(180,0,360) #@gui : Band Width = float(40,1,360) #@gui : Hue Shift = float(0,-180,180) #@gui : Saturation Correction = float(0,-0.99,0.99) #@gui : Value Correction = float(0,-0.99,0.99) #@gui : sep = separator() #@gui : Hue Band = float(180,0,360) #@gui : Band Width = float(40,1,360) #@gui : Hue Shift = float(0,-180,180) #@gui : Saturation Correction = float(0,-0.99,0.99) #@gui : Value Correction = float(0,-0.99,0.99) #@gui : sep = separator() #@gui : note = note("Author: Jérome Ferrari. #@gui :       Latest Update: 01/14/2011.") #@gui : url = link("Filter explained here","http://www.flickr.com/groups/gmic/discuss/72157625798533482") fx_hsv_equalizer : foreach { to_rgb rgb2hsv s c # From now on 0,1,2 are H,S,V #3 masks: +f[0] abs(i-$2)<$3/2|abs(i-$2-360)<$3/2|abs(i-$2+360)<$3/2 +f[0] abs(i-$7)<$8/2|abs(i-$7-360)<$8/2|abs(i-$7+360)<$8/2 +f[0] abs(i-$12)<$13/2|abs(i-$12-360)<$13/2|abs(i-$12+360)<$13/2 # From now on 3,4,5 are Masks +threshold[1,2] 0.01 *[-1,-2] [-1]x2 *[-1,3] *[-1,4] *[-1,5] #0 saturation and value not in mask # Hue shift: +*[3] $4 +*[4] $9 +*[5] $14 +[-1,-2,-3] +[-1,0] %[0] 360 # Saturation : if $5>=0 +*[3] -$5 else +*[3] {1/(1+$5)-1} fi +. 1 if $10>=0 +*[4] -$10 else +*[4] {1/(1+$10)-1} fi +. 1 if $15>=0 +*[5] -$15 else +*[5] {1/(1+$15)-1} fi +. 1 *[-1,-2,-3] ^[1,-1] # Value : if $6>=0 +*[3] -$6 else +*[3] {1/(1+$6)-1} fi +. 1 if $11>=0 +*[4] -$11 else +*[4] {1/(1+$11)-1} fi +. 1 if $16>=0 +*[5] -$16 else +*[5] {1/(1+$16)-1} fi +. 1 *[-1,-2,-3] ^[2,-1] # Reconstruction rm[3,4,5] a[0,1,2] c hsv2rgb } fx_hsv_equalizer_preview : l. { if !$1 fx_hsv_equalizer $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16 else to_rgb rgb2hsv s c (0,359) r. ..,{{0,h}/10},1,1,3 . f. 1 #create lower band j[0] [3],0,91% j[1] [4],0,91% j[2] [4],0,91% rm[-1,-2] #paste lower band +f[0] abs(i-$2)<$3/2|abs(i-$2-360)<$3/2|abs(i-$2+360)<$3/2 +f[0] abs(i-$7)<$8/2|abs(i-$7-360)<$8/2|abs(i-$7+360)<$8/2 +f[0] abs(i-$12)<$13/2|abs(i-$12-360)<$13/2|abs(i-$12+360)<$13/2 #masks -|[-3--1] +. 0.33 /. 1.33 #1 and 0.25 *[2,-1] a c hsv2rgb fi } #@gui Equalize HSI-HSL-HSV : fx_equalize_hsv, fx_equalize_hsv_preview(0)+ #@gui : Colorspace = choice(1,"HSI","HSL","HSV") #@gui : Opacity (%) = float(100,0,100) #@gui : Value Blending = float(0,0,64) #@gui : Color Blending = float(0,0,64) #@gui : sep = separator() #@gui : Preview Mapping = choice("None","Grey","Color") #@gui : sep = separator() #@gui : note = note("Black:") #@gui : Hue Offset = float(0,-180,180) #@gui : Saturation Offset = float(0,-1,1) #@gui : Value Offset = float(0,-1,1) #@gui : sep = separator() #@gui : note = note("Near black:") #@gui : Hue Offset = float(0,-180,180) #@gui : Saturation Offset = float(0,-1,1) #@gui : Value Offset = float(0,-1,1) #@gui : sep = separator() #@gui : note = note("Dark grey:") #@gui : Hue Offset = float(0,-180,180) #@gui : Saturation Offset = float(0,-1,1) #@gui : Value Offset = float(0,-1,1) #@gui : sep = separator() #@gui : note = note("Mi-dark grey:") #@gui : Hue Offset = float(0,-180,180) #@gui : Saturation Offset = float(0,-1,1) #@gui : Value Offset = float(0,-1,1) #@gui : sep = separator() #@gui : note = note("Middle grey:") #@gui : Hue Offset = float(0,-180,180) #@gui : Saturation Offset = float(0,-1,1) #@gui : Value Offset = float(0,-1,1) #@gui : sep = separator() #@gui : note = note("Mid-light grey:") #@gui : Hue Offset = float(0,-180,180) #@gui : Saturation Offset = float(0,-1,1) #@gui : Value Offset = float(0,-1,1) #@gui : sep = separator() #@gui : note = note("Light grey:") #@gui : Hue Offset = float(0,-180,180) #@gui : Saturation Offset = float(0,-1,1) #@gui : Value Offset = float(0,-1,1) #@gui : sep = separator() #@gui : note = note("Highlights:") #@gui : Hue Offset = float(0,-180,180) #@gui : Saturation Offset = float(0,-1,1) #@gui : Value Offset = float(0,-1,1) #@gui : sep = separator() #@gui : note = note("White:") #@gui : Hue Offset = float(0,-180,180) #@gui : Saturation Offset = float(0,-1,1) #@gui : Value Offset = float(0,-1,1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé and David Revoy. #@gui :       Latest Update: 2018/01/19.") fx_equalize_hsv : cs=${"arg0 $1,hsi,hsl,hsv"} foreach { split_opacity l[0] { to_rgb 9,1,1,4,\ {"V = [${6-30:3}]*pi/180; [cos(V),sin(V)]"},${7-31:3},\ {"V = [${8-32:3}]; const sV = size(V); repeat (sV,k, v0 = k/sV; v1 = (k+1)/sV; V[k]*=(V[k]>0?(1 - v0):v1); ); V"} r. 256,1,1,4,1 sh. 3 b. $3 rm. # Value smoothness f. "[ atan2(G,R)*180/pi,B,A,0 ]" channels. 0,2 +rgb2$cs.. +channels. 100% *. 256 round. map. ... +[-2,-1] rm.. +channels. 100% ${cs}2rgb.. if $4 # Spatial smoothness *. 255 bilateral.. .,$4,{2+$4} rgb2$cs.. /. 255 j.. .,0,0,0,2 ${cs}2rgb.. fi rm. blend alpha,{$2%} } a c } fx_equalize_hsv_preview : if $5 cs=${"arg0 $1,hsi,hsl,hsv"} rm {0.8*[${-gui_preview_wh}]},1,3,\ "$5==1? (H = S = 0; V = y/(h-1)): (H = x*360/(w-1); S = y/(h-1); V = y/(h-1)); [H,S,V]" ${cs}2rgb. fi gui_split_preview "fx_equalize_hsv $*",${-3--1} if $5 r. ${-gui_preview_wh},1,3,0,0,0.5,0.5 fi #@gui Mixer [CMYK] : fx_mix_cmyk, fx_mix_cmyk_preview(1)+ #@gui : Cyan Factor = float(1,0,4) #@gui : Cyan Shift = float(0,-255,255) #@gui : Cyan Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Magenta Factor = float(1,0,4) #@gui : Magenta Shift = float(0,-255,255) #@gui : Magenta Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Yellow Factor = float(1,0,4) #@gui : Yellow Shift = float(0,-255,255) #@gui : Yellow Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Key Factor = float(1,0,4) #@gui : Key Shift = float(0,-255,255) #@gui : Key Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Tones Range = choice("All tones","Shadows","Mid-Tones","Highlights") #@gui : Tones Smoothness = float(2,0,10) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/06.") fx_mix_cmyk : foreach { split_opacity rv to_rgb. fx_start_mix $13,$14 rgb2cmyk. s. c *[-4] $1 +[-4] $2 b[-4] $3% *... $4 +... $5 b... $6% *.. $7 +.. $8 b.. $9% *. $10 +. $11 b. $12% a[-4--1] c cmyk2rgb. fx_end_mix $13 if $!!=3 rv a c fi } fx_mix_cmyk_preview : gui_split_preview "fx_mix_cmyk $*",${-3--1} #@gui Mixer [HSV] : fx_mix_hsv, fx_mix_hsv_preview(1)+ #@gui : Hue Factor = float(1,0,4) #@gui : Hue Shift = float(0,-180,180) #@gui : Hue Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Saturation Factor = float(1,0,4) #@gui : Saturation Shift = float(0,-1,1) #@gui : Saturation Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Value Factor = float(1,0,4) #@gui : Value Shift = float(0,-1,1) #@gui : Value Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights") #@gui : Tones Smoothness = float(2,0,10) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/06.") fx_mix_hsv : foreach { split_opacity rv to_rgb. fx_start_mix $10,$11 rgb2hsv. s. c -[-2,-1] 0.5 *... $1 +... $2 b... $3% *.. $4 +.. $5 b.. $6% *. $7 +. $8 b. $9% %... 360 +[-2,-1] 0.5 c[-2,-1] 0,1 a[-3--1] c hsv2rgb. fx_end_mix $10 if $!!=3 rv a c fi } fx_mix_hsv_preview : gui_split_preview "fx_mix_hsv $*",${-3--1} #@gui Mixer [Lab] : fx_mix_lab, fx_mix_lab_preview(1)+ #@gui : Lightness Factor = float(1,0.5,1.5) #@gui : Lightness Shift = float(0,-50,50) #@gui : Lightness Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : A-Color Factor = float(1,0,4) #@gui : A-Color Shift = float(0,-20,20) #@gui : A-Color Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : B-Color Factor = float(1,0,4) #@gui : B-Color Shift = float(0,-20,20) #@gui : B-Color Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights") #@gui : Tones Smoothness = float(2,0,10) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/06.") fx_mix_lab : foreach { split_opacity to_rgb[0] gui_parallel_overlap[0] "_fx_mix_lab $*",0,{3*max($3,$6,$9)} a c } _fx_mix_lab : fx_start_mix $10,$11 rgb2lab. s. c *... $1 +... $2 b... $3% *.. $4 +.. $5 b.. $6% *. $7 +. $8 b. $9% a[-3--1] c lab2rgb. fx_end_mix $10 fx_mix_lab_preview : gui_split_preview "fx_mix_lab $*",${-3--1} #@gui Mixer [PCA] : fx_mix_pca, fx_mix_pca_preview(1)+ #@gui : Primary Factor = float(0,-1.5,1.5) #@gui : Primary Shift = float(0,-255,255) #@gui : Primary Twist = float(0,-180,180) #@gui : Primary Gamma = float(0,-100,100) #@gui : sep = separator() #@gui : Secondary Factor = float(0,-1.5,1.5) #@gui : Secondary Shift = float(0,-255,255) #@gui : Secondary Twist = float(0,-180,180) #@gui : Secondary Gamma = float(0,-100,100) #@gui : sep = separator() #@gui : Tertiary Factor = float(0,-1.5,1.5) #@gui : Tertiary Shift = float(0,-255,255) #@gui : Tertiary Twist = float(0,-180,180) #@gui : Tertiary Gamma = float(0,-100,100) #@gui : sep = separator() #@gui : Display Color Axes = bool(1) #@gui : Stats = value(-1,-1,-1,-1) #@gui : Avg Covariance = value(0,0,0,0,0,0,0,0,0,0,0,0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/07/18.") fx_mix_pca : foreach { split_opacity l[0] { to_rgb # Get image covariance (and remember it to speed up multiple calls of the filter). if [$14]==round(stats()[0,4],0.1) _avg:=[$15][0,3] C:=[$15][3,9] status= else +rs 256,256,2 C=${"covariance_vectors. _avg"} rm. __status="{$1}{$2}{$3}{$4}"\ "{$5}{$6}{$7}{$8}"\ "{$9}{$10}{$11}{$12}"\ "${13}"\ "{"{round(stats()[0,4],0.1)}"}"\ "{"$_avg,$C"}"\ "{$16}{${17,18}}" fi # Find value ranges for gamma correction. if "$4 || $8 || $12" +l { f "begin(avg = ["$_avg"]; eig = eig(["$C"]); Pt = eig[3,9]); Pt*(I-avg)" s c repeat $! { vmax$>={$>,1.1*max(abs(im),abs(iM))} } rm } else vmax0,vmax1,vmax2=1 fi # Modify image values. f "begin( do_gamma(val,vmax,gamma) = (vmax*sign(val)*(abs(val)/vmax)^gamma); const gamma0 = 10^-($4%); const gamma1 = 10^-($8%); const gamma2 = 10^-($12%); const vmax0 = "$vmax0"; const vmax1 = "$vmax1"; const vmax2 = "$vmax2"; avg = ["$_avg"]; eig = eig(["$C"]); for (k = 3, k<12, k+=3, eig[k]<0?copy(eig[k],eig[k,3]*=-1,3)); Pt = eig[3,9]; P = transpose(Pt,3); T = mul(P,diag(10^[$1,$5,$9]),3); R1 = rot(eig[3,3],$3°); R2 = rot(eig[6,3],$7°); R3 = rot(eig[9,3],$11°); T = mul(R1,mul(R2,mul(R3,T,3),3),3); avg_shift = avg + $2*eig[3,3] + $6*eig[6,3] + $10*eig[9,3]; if ("0$_is_preview", L = [ 2,5,10]*sqrt(1e-5 + eig[0,3]); run('__cols=',v2s(round([ avg - L[0]*eig[3,3], avg + L[0]*eig[3,3], avg - L[1]*eig[6,3], avg + L[1]*eig[6,3], avg - L[2]*eig[9,3], avg + L[2]*eig[9,3] ]))); ); ); nI = Pt*(I - avg); ($4 || $8 || $12)?( nI[0] = do_gamma(nI[0],vmax0,gamma0); nI[1] = do_gamma(nI[1],vmax1,gamma1); nI[2] = do_gamma(nI[2],vmax2,gamma2); ); avg_shift + T*nI" c 0,255 } a c } u $__status fx_mix_pca_preview : _is_preview=1 __status= foreach { gui_split_preview "fx_mix_pca ${1-13},\"$14\",\"$15\",${16-18}",${-3--1} if $13 rs ${-gui_preview_wh},1 ($__cols) r. 3,6,1,1,-1 permute. yzcx s. x,3 r[-3--1] {w#0/2},13,1,3,3 c[-3--1] 0,255 frame[-3--1] xy,1,0 to[0] Primary,4,2,13,1 j[0] ...,64,4 to[0] Secondary,4,17,13,1 j[0] ..,64,19 to[0] Tertiary,4,32,13,1 j[0] .,64,34 k[0] fi } u $__status #@gui Mixer [RGB] : fx_mix_rgb, fx_mix_rgb_preview(1)+ #@gui : Red Factor = float(1,0,4) #@gui : Red Shift = float(0,-255,255) #@gui : Red Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Green Factor = float(1,0,4) #@gui : Green Shift = float(0,-255,255) #@gui : Green Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Blue Factor = float(1,0,4) #@gui : Blue Shift = float(0,-255,255) #@gui : Blue Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights") #@gui : Tones Smoothness = float(2,0,10) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/06.") fx_start_mix : if $1==1 +tones. 3 +[-2,-1] b[-2,-1] $2% ri[-2,-1] ... *. ... mv... $! elif $1==2 +tones. 3 +[-3,-1] b[-2,-1] $2% ri[-2,-1] ... *.. ... mv... $! elif $1==3 +tones. 3 +[-3,-2] b[-2,-1] $2% ri[-2,-1] ... *.. ... mv... $! fi fx_end_mix : if $1==1 *[-3,-1] +[-2,-1] elif $1==2 *[-2,-1] +[-2,-1] elif $1==3 *[-2,-1] +[-2,-1] fi c 0,255 fx_mix_rgb : foreach { split_opacity rv to_rgb. fx_start_mix $10,$11 -. 128 s. c *... $1 +... $2 b... $3% *.. $4 +.. $5 b.. $6% *. $7 +. $8 b. $9% a[-3--1] c +. 128 c. 0,255 fx_end_mix $10 if $!!=3 rv a c fi } fx_mix_rgb_preview : gui_split_preview "fx_mix_rgb $*",${-3--1} #@gui Mixer [YCbCr] : fx_mix_ycbcr, fx_mix_ycbcr_preview(1)+ #@gui : Luminance Factor = float(1,0,4) #@gui : Luminance Shift = float(0,-255,255) #@gui : Luminance Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Blue Chroma Factor = float(1,0,4) #@gui : Blue Chroma Shift = float(0,-255,255) #@gui : Blue Chroma Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Red Chroma Factor = float(1,0,4) #@gui : Red Chroma Shift = float(0,-255,255) #@gui : Red Chroma Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Tones Range = choice("All Tones","Shadows","Mid-Tones","Highlights") #@gui : Tones Smoothness = float(2,0,10) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/06.") fx_mix_ycbcr : foreach { split_opacity rv to_rgb. fx_start_mix $10,$11 rgb2ycbcr. -. 128 s. c *... $1 +... $2 b... $3% *.. $4 +.. $5 b.. $6% *. $7 +. $8 b. $9% a[-3--1] c +. 128 c. 0,255 ycbcr2rgb. fx_end_mix $10 if $!!=3 rv a c fi } fx_mix_ycbcr_preview : gui_split_preview "fx_mix_ycbcr $*",${-3--1} #@gui Random Color Transformation : fx_random_color_transformation, fx_random_color_transformation_preview(1)+ #@gui : Seed = float(0,0,100000) #@gui : Randomize Seed = button() #@gui : First time = value(1) #@gui : sep = separator() #@gui : Amplitude (%) = float(100,0,100) #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/03/10.") fx_random_color_transformation : if $2" || "$3 srand __seed:=_v(100000) else __seed=$1 fi random_clut $__seed amplitude:=$4% repeat $!-1 { l[$>,-1] { +map_clut.. . j... .,0,0,0,0,$amplitude rm. } } rm. adjust_colors ${5-9} fx_random_color_transformation_preview : gui_split_preview "fx_random_color_transformation ${1--2}",${-3--1} to "Seed: \#"$__seed,5,5,5%,2 u "{"$__seed"}{0}{0}{$4}{$5}{$6}{$7}{$8}{$9}{$10}{$11,$12}" #@gui Retinex : fx_retinex, fx_retinex_preview(0)+ #@gui : Strength (%) = float(75,0,100) #@gui : Value Offset = float(16,1,256) #@gui : Colorspace = choice(1,"HSI","HSV","Lab","Linear RGB","RGB","YCbCr") #@gui : Min Cut (%) = float(1,0,100) #@gui : Max Cut (%) = float(1,0,100) #@gui : Regularization = float(5,0,32) #@gui : sep = separator() #@gui : Low Scale = float(15,1,512) #@gui : Middle Scale = float(80,1,512) #@gui : High Scale = float(250,1,512) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Note: This filter implements the Multiscale Color Retinex algorithm, #@gui : as described in:") #@gui : url = link{"http://www.ipol.im/pub/art/2014/107/"} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/13/09.") fx_retinex : foreach { +retinex $2,${"arg0 $3,hsi,hsv,lab,lrgb,rgb,ycbcr"},$4,$5,${7--1} if $6 guided. ..,$6,$6 fi j[0] .,0,0,0,0,{$1%} rm. c 0,255 } fx_retinex_preview : gui_split_preview "fx_retinex $*",${-3--1} #@gui Retro Fade : fx_retrofade, fx_retrofade_preview #@gui : Iterations = int(20,1,64) #@gui : Colors = int(6,2,32) #@gui : Grain = float(40,1,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/25/10.") fx_retrofade : foreach { split_opacity l[0] { +f 0 repeat $1 { +noise[0] $3 c. 0,255 autoindex. $2,0,0 +[-2,-1] progress {$>*100/$1} } k. n 0,255 progress 100 } a c } fx_retrofade_preview : gui_split_preview "fx_retrofade $*",${-3--1} #@gui Select-Replace Color : fx_select_color, fx_select_color_preview(0) #@gui : Similarity Space = choice(0,"RGB[A]","RGB","YCbCr","Red","Green","Blue","Opacity","Luminance", #@gui : "Blue & Red Chrominances","Hue","Saturation") #@gui : Tolerance = float(20,0,100) #@gui : Smoothness = float(0,0,10) #@gui : Fill Holes = int(0,0,256) #@gui : Selected Color = color(255,255,255,255) #@gui : Output As = choice(0,"Selected Colors","Selected Mask","Rejected Colors","Rejected Mask","Replaced Color") #@gui : Replacement Color = color(255,0,0,255) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") _fx_select_color : if $1==1 to_rgb # RGB elif $1==2 to_rgb rgb2ycbcr # YCbCr elif $1==3 channels 0 # R elif $1==4 channels 1 # G elif $1==5 channels 2 # B elif $1==6 to_rgba channels 3 # Opacity elif $1==7 to_rgb rgb2ycbcr channels 0 # Luminance elif $1==8 to_rgb rgb2ycbcr channels 1,2 # B&R chrominances elif $1==9 to_rgb rgb2hsv channels 0 # Hue elif $1==10 to_rgb rgb2hsv channels 1 # Saturation fi fx_select_color : ($5^$6^$7^$8) _fx_select_color. $1 color={^} rm. foreach { to_rgba +_fx_select_color $1 select_color[1] $2%,$color if $4 +area. 0,0 <=. {round($4^1.5)} inpaint.. .,0,3 rm. fi # Fill holes. b[1] $3 n[1] 0,255 if !$9 sh[0] 100% &. [1] # Selected colors. elif $9==1 rm[0] # Selected mask. elif $9==2 -[1] 255 *[1] -1 sh[0] 100% &. [1] # Rejected colors. elif $9==3 rm[0] - 255 * -1 # Rejected mask. else # Replaced color. /[1] 255 +*[0,1] +*[1] $11 +*[1] $12 +*[1] $13 *[1] $10 a[1,-3--1] c -[1,2] + fi k[0] } fx_select_color_preview : gui_split_preview "fx_select_color $*",${-3--1} #@gui Selective Desaturation : fx_selective_desaturation, fx_selective_desaturation_preview(1) #@gui : Reference Color = color(255,255,255) #@gui : Desaturate = choice("Reference Color","All but Reference Color") #@gui : Strength = float(3,0,10) #@gui : Regularization = int(0,0,20) #@gui : Maximum Saturation = choice("From Input","From Reference Color","Maximum Value") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/15/07.") fx_selective_desaturation : foreach { to_color split_opacity l[0] { +fc $1,$2,$3 -[1] [0] norm[1] /[1] {1e-6+iM} if $4 *[1] -{max(0.01,$5)} +[1] 1 else >=[1] {5*$5}% fi c[1] 0,1 rgb2hsl[0] s[0] c mM:=im,iM repeat $6 { guided. [2],1,0.1 } n. $mM # Regularization step. if !$7 *[1,-1] elif $7==1 ($1^$2^$3) rgb2hsl. *[1] {i[1]} rm[-2,-1] else rv[1,-1] rm. fi a c hsl2rgb } a c } fx_selective_desaturation_preview : gui_split_preview "fx_selective_desaturation $*",${-3--1} #@gui Sepia : fx_sepia, fx_sepia_preview(1)+ #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_sepia : sepia adjust_colors ${1-3},0,0,0,255 fx_sepia_preview : gui_split_preview "fx_sepia $*",${-3--1} #@gui Simulate Film : fx_simulate_film, fx_simulate_film_preview(1)+ #@gui : Category = choice{"Black & White (25)","Instant [Consumer] (54)","Instant [Pro] (68)","Fuji XTrans III (15)", #@gui : "Negative [Color] (13)","Negative [New] (39)","Negative [Old] (44)","Print Films (12)","Slide [Color] (26)"} ##### Black & White #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Agfa APX 100","Agfa APX 25","Fuji Neopan 1600","Fuji Neopan Acros 100","Ilford Delta 100", #@gui : "Ilford Delta 3200","Ilford Delta 400","Ilford FP4 Plus 125","Ilford HP5 Plus 400","Ilford HPS 800", #@gui : "Ilford Pan F Plus 50","Ilford XP2","Kodak BW 400 CN","Kodak HIE (HS Infra)","Kodak T-Max 100", #@gui : "Kodak T-Max 3200","Kodak T-Max 400","Kodak Tri-X 400","Polaroid 664","Polaroid 667","Polaroid 672", #@gui : "Rollei IR 400","Rollei Ortho 25","Rollei Retro 100 Tonal","Rollei Retro 80s"}_2 ##### Instant [Consumer] #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Polaroid PX-100UV+ Cold --","Polaroid PX-100UV+ Cold -","Polaroid PX-100UV+ Cold", #@gui : "Polaroid PX-100UV+ Cold +","Polaroid PX-100UV+ Cold ++","Polaroid PX-100UV+ Cold +++", #@gui : "Polaroid PX-100UV+ Warm --","Polaroid PX-100UV+ Warm -","Polaroid PX-100UV+ Warm", #@gui : "Polaroid PX-100UV+ Warm +","Polaroid PX-100UV+ Warm ++","Polaroid PX-100UV+ Warm +++", #@gui : "Polaroid PX-680 --","Polaroid PX-680 -","Polaroid PX-680","Polaroid PX-680 +","Polaroid PX-680 ++", #@gui : "Polaroid PX-680 Cold --","Polaroid PX-680 Cold -","Polaroid PX-680 Cold","Polaroid PX-680 Cold +", #@gui : "Polaroid PX-680 Cold ++","Polaroid PX-680 Cold ++a","Polaroid PX-680 Warm --","Polaroid PX-680 Warm -", #@gui : "Polaroid PX-680 Warm","Polaroid PX-680 Warm +","Polaroid PX-680 Warm ++","Polaroid PX-70 --", #@gui : "Polaroid PX-70 -","Polaroid PX-70","Polaroid PX-70 +","Polaroid PX-70 ++","Polaroid PX-70 +++", #@gui : "Polaroid PX-70 Cold --","Polaroid PX-70 Cold -","Polaroid PX-70 Cold","Polaroid PX-70 Cold +", #@gui : "Polaroid PX-70 Cold ++","Polaroid PX-70 Warm --","Polaroid PX-70 Warm -","Polaroid PX-70 Warm", #@gui : "Polaroid PX-70 Warm +","Polaroid PX-70 Warm ++","Polaroid Time Zero (Expired) ---", #@gui : "Polaroid Time Zero (Expired) --","Polaroid Time Zero (Expired) -","Polaroid Time Zero (Expired)", #@gui : "Polaroid Time Zero (Expired) +","Polaroid Time Zero (Expired) ++","Polaroid Time Zero (Expired) Cold ---", #@gui : "Polaroid Time Zero (Expired) Cold --","Polaroid Time Zero (Expired) Cold -", #@gui : "Polaroid Time Zero (Expired) Cold"}_0 ##### Instant [Pro] #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Fuji FP-100c --","Fuji FP-100c -","Fuji FP-100c","Fuji FP-100c (alt)","Fuji FP-100c +","Fuji FP-100c ++", #@gui : "Fuji FP-100c ++a","Fuji FP-100c +++", #@gui : "Fuji FP-100c Cool --","Fuji FP-100c Cool -","Fuji FP-100c Cool","Fuji FP-100c Cool +","Fuji FP-100c Cool ++", #@gui : "Fuji FP-100c Negative --","Fuji FP-100c Negative -","Fuji FP-100c Negative","Fuji FP-100c Negative +", #@gui : "Fuji FP-100c Negative ++","Fuji FP-100c Negative ++a","Fuji FP-100c Negative +++", #@gui : "Fuji FP-3000b --","Fuji FP-3000b -","Fuji FP-3000b","Fuji FP-3000b +","Fuji FP-3000b ++","Fuji FP-3000b +++", #@gui : "Fuji FP-3000b HC","Fuji FP-3000b Negative --","Fuji FP-3000b Negative -","Fuji FP-3000b Negative", #@gui : "Fuji FP-3000b Negative +","Fuji FP-3000b Negative ++","Fuji FP-3000b Negative +++", #@gui : "Fuji FP-3000b Negative Early","Polaroid 665 --","Polaroid 665 -","Polaroid 665","Polaroid 665 +", #@gui : "Polaroid 665 ++","Polaroid 665 Negative -","Polaroid 665 Negative","Polaroid 665 Negative +", #@gui : "Polaroid 665 Negative HC","Polaroid 669 --","Polaroid 669 -","Polaroid 669","Polaroid 669 +", #@gui : "Polaroid 669 ++","Polaroid 669 +++","Polaroid 669 Cold --","Polaroid 669 Cold -","Polaroid 669 Cold", #@gui : "Polaroid 669 Cold +","Polaroid 690 --","Polaroid 690 -","Polaroid 690","Polaroid 690 +","Polaroid 690 ++", #@gui : "Polaroid 690 Cold --","Polaroid 690 Cold -","Polaroid 690 Cold","Polaroid 690 Cold +","Polaroid 690 Cold ++", #@gui : "Polaroid 690 Warm --","Polaroid 690 Warm -","Polaroid 690 Warm","Polaroid 690 Warm +","Polaroid 690 Warm ++"}_0 #### Fuji XTrans III #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Acros","Acros+G","Acros+R","Acros+Ye","Astia","Classic Chrome","Mono","Mono+G","Mono+R","Mono+Ye", #@gui : "Pro Neg Hi","Pro Neg Std","Provia","Sepia","Velvia"}_0 ##### Negative [Color] #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Agfa Ultra Color 100","Agfa Vista 200","Fuji Superia 200","Fuji Superia HG 1600","Fuji Superia Reala 100", #@gui : "Fuji Superia X-Tra 800","Kodak Ektar 100","Kodak Elite 100 XPRO","Kodak Elite Color 200", #@gui : "Kodak Elite Color 400","Kodak Portra 160 NC","Kodak Portra 160 VC","Lomography Redscale 100"}_0 ##### Negative [New] #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Fuji 160C -","Fuji 160C","Fuji 160C +","Fuji 160C ++", #@gui : "Fuji 400H -","Fuji 400H","Fuji 400H +","Fuji 400H ++", #@gui : "Fuji 800Z -","Fuji 800Z","Fuji 800Z +","Fuji 800Z ++", #@gui : "Fuji Ilford HP5 -","Fuji Ilford HP5","Fuji Ilford HP5 +","Fuji Ilford HP5 ++", #@gui : "Kodak Portra 160 -","Kodak Portra 160","Kodak Portra 160 +","Kodak Portra 160 ++", #@gui : "Kodak Portra 400 -","Kodak Portra 400","Kodak Portra 400 +","Kodak Portra 400 ++", #@gui : "Kodak Portra 800 -","Kodak Portra 800","Kodak Portra 800 +","Kodak Portra 800 ++","Kodak Portra 800 HC", #@gui : "Kodak T-MAX 3200 -","Kodak T-MAX 3200","Kodak T-MAX 3200 +","Kodak T-MAX 3200 ++","Kodak T-MAX 3200 (alt)", #@gui : "Kodak TRI-X 400 -","Kodak TRI-X 400","Kodak TRI-X 400 +","Kodak TRI-X 400 ++","Kodak TRI-X 400 (alt)"}_0 ##### Negative [Old] #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Fuji Ilford Delta 3200 -","Fuji Ilford Delta 3200","Fuji Ilford Delta 3200 +","Fuji Ilford Delta 3200 ++", #@gui : "Fuji Neopan 1600 -","Fuji Neopan 1600","Fuji Neopan 1600 +","Fuji Neopan 1600 ++", #@gui : "Fuji Superia 100 -","Fuji Superia 100","Fuji Superia 100 +","Fuji Superia 100 ++", #@gui : "Fuji Superia 400 -","Fuji Superia 400","Fuji Superia 400 +","Fuji Superia 400 ++", #@gui : "Fuji Superia 800 -","Fuji Superia 800","Fuji Superia 800 +","Fuji Superia 800 ++", #@gui : "Fuji Superia 1600 -","Fuji Superia 1600","Fuji Superia 1600 +","Fuji Superia 1600 ++", #@gui : "Kodak Portra 160 NC -","Kodak Portra 160 NC","Kodak Portra 160 NC +","Kodak Portra 160 NC ++", #@gui : "Kodak Portra 160 VC -","Kodak Portra 160 VC","Kodak Portra 160 VC +","Kodak Portra 160 VC ++", #@gui : "Kodak Portra 400 NC -","Kodak Portra 400 NC","Kodak Portra 400 NC +","Kodak Portra 400 NC ++", #@gui : "Kodak Portra 400 UC -","Kodak Portra 400 UC","Kodak Portra 400 UC +","Kodak Portra 400 UC ++", #@gui : "Kodak Portra 400 VC -","Kodak Portra 400 VC","Kodak Portra 400 VC +","Kodak Portra 400 VC ++"}_0 ##### Print Films #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Fuji 3510 (Constlclip)","Fuji 3510 (Constlmap)","Fuji 3510 (Cuspclip)", #@gui : "Fuji 3513 (Constlclip)","Fuji 3513 (Constlmap)","Fuji 3513 (Cuspclip)", #@gui : "Kodak 2383 (Constlclip)","Kodak 2383 (Constlmap)","Kodak 2383 (Cuspclip)", #@gui : "Kodak 2393 (Constlclip)","Kodak 2393 (Constlmap)","Kodak 2393 (Cuspclip)"}_0 #### Slide [Color] #@gui : Preset = choice{1,"All [Collage]","None", #@gui : "Agfa Precisa 100","Fuji Astia 100F","Fuji FP 100C","Fuji Provia 100F","Fuji Provia 400F","Fuji Provia 400X", #@gui : "Fuji Sensia 100","Fuji Superia 200 XPRO","Fuji Velvia 50","Generic Fuji Astia 100","Generic Fuji Provia 100", #@gui : "Generic Fuji Velvia 100","Generic Kodachrome 64","Generic Kodak Ektachrome 100 VS", #@gui : "Kodak E-100 GX Ektachrome 100","Kodak Ektachrome 100 VS","Kodak Elite Chrome 200","Kodak Elite Chrome 400", #@gui : "Kodak Elite ExtraColor 100","Kodak Kodachrome 200","Kodak Kodachrome 25","Kodak Kodachrome 64", #@gui : "Lomography X-Pro Slide 200", #@gui : "Polaroid 669","Polaroid 690","Polaroid Polachrome"}_0 #@gui : Thumbnail Size = int(512,0,1024)_1 #@gui : sep = separator() #@gui : Strength (%) = float(100,0,100) #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : Normalize Colors = choice("None","Pre-Normalize","Post-Normalize","Both") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Note: The color LUTs proposed in this filter come from #@gui : various free sources :") #@gui : note = note{"* #@gui : RawTherapee Film Simulation."} #@gui : note = note{"* #@gui : Pat David Film Emulation. #@gui : "} #@gui : note = note{"* #@gui : Fuji Film Simulation Profiles."} #@gui : note = note{"* #@gui : Print Film LUTs For Download. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2019/02/27.") fx_simulate_film : category=${arg0\ $1,bw,instant_consumer,instant_pro,fujixtransiii,negative_color,negative_new,negative_old,\ print,colorslide} presets=${-_fx_cluts_$category} index:=arg(1+$1,${2-10}) thumbsize,strength,brightness,contrast,gamma,hue,saturation,normalize=${11-18} if $normalize==1" || "$normalize==3 # Pre-normalization foreach { split_opacity balance_gamma[0] , a c } fi if $index>=2 # Apply CLUT path_clut=${-path_cache} name=${arg0\ $index-2,$presets} clut $name,{0$_is_preview" && "!isfile(['{/${path_clut}clut_$name.cimgz}'])?17:48} repeat $!-1 { if $strength<100 +map_clut[$>] . j[$>] .,0,0,0,0,{$strength%} rm. else map_clut[$>] . fi } rm. adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255 if $normalize==2" || "$normalize==3 foreach { split_opacity n[0] 0,255 a c } fi # Post-normalization elif $index==1 # "None" adjust_colors $brightness,$contrast,$gamma,$hue,$saturation,0,255 if $normalize==2" || "$normalize==3 foreach { split_opacity n[0] 0,255 a c } fi # Post-normalization else # Collage foreach { if max(w,h)>$thumbsize rs $thumbsize,$thumbsize,2 fi } N=$! +to "Original",1%,1%,7.5%,2,0.5 Np:=narg($presets) repeat $Np { clut_name=${arg0\ $>,$presets} clut $clut_name mv. $N repeat $N { if $strength<100 [$>] +map_clut. [$N] j.. .,0,0,0,0,{$strength%} rm. else +map_clut[$>] [$N] fi adjust_colors. $brightness,$contrast,$gamma,$hue,$saturation,0,255 if $normalize==2" || "$normalize==3 l. { split_opacity n[0] 0,255 a c } fi # Post-normalization strcapitalize $clut_name clut_name=${} to. $clut_name,1%,1%,7.5%,2,0.5 } rm[$N] progress {$>*100/($Np-1)} } k[$N--1] frame xy,1,0,0,0,255 - 128 append_tiles {s=floor(sqrt($!));w>h?[s,0]:[0,s]} + 128 fi fx_simulate_film_preview : _is_preview=1 index:=arg(1+$1,${2-10}) if !$index gui_warning_preview "Preview disabled in 'Collage' mode" else gui_split_preview "fx_simulate_film $*",${19-21} fi u "{$1}{$2}_"{2*!$1}\ "{$3}_"{2*($1==1)}\ "{$4}_"{2*($1==2)}\ "{$5}_"{2*($1==3)}\ "{$6}_"{2*($1==4)}\ "{$7}_"{2*($1==5)}\ "{$8}_"{2*($1==6)}\ "{$9}_"{2*($1==7)}\ "{$10}_"{2*($1==8)}\ "{$11}_"{1+!$index}\ "{$12}{$13}{$14}{$15}{$16}{$17}{$18}{$19}{$20,$21}" _fx_cluts_bw : u agfa_apx_100,agfa_apx_25,fuji_neopan_1600,fuji_neopan_acros_100,ilford_delta_100,ilford_delta_3200,\ ilford_delta_400,ilford_fp_4_plus_125,\ ilford_hp_5_plus_400,ilford_hps_800,ilford_pan_f_plus_50,ilford_xp_2,kodak_bw_400_cn,kodak_hie_hs_infra,\ kodak_t-max_100,kodak_t-max_3200,\ kodak_t-max_400,kodak_tri-x_400,polaroid_664,polaroid_667,polaroid_672,rollei_ir_400,rollei_ortho_25,\ rollei_retro_100_tonal,rollei_retro_80s _fx_cluts_instant_consumer : u polaroid_px-100uv+_cold_--,polaroid_px-100uv+_cold_-,polaroid_px-100uv+_cold,polaroid_px-100uv+_cold_+,\ polaroid_px-100uv+_cold_++,polaroid_px-100uv+_cold_+++,\ polaroid_px-100uv+_warm_--,polaroid_px-100uv+_warm_-,polaroid_px-100uv+_warm,polaroid_px-100uv+_warm_+,\ polaroid_px-100uv+_warm_++,polaroid_px-100uv+_warm_+++,\ polaroid_px-680_--,polaroid_px-680_-,polaroid_px-680,polaroid_px-680_+,polaroid_px-680_++,\ polaroid_px-680_cold_--,polaroid_px-680_cold_-,polaroid_px-680_cold,polaroid_px-680_cold_+,\ polaroid_px-680_cold_++,polaroid_px-680_cold_++_alt,\ polaroid_px-680_warm_--,polaroid_px-680_warm_-,polaroid_px-680_warm,polaroid_px-680_warm_+,polaroid_px-680_warm_++,\ polaroid_px-70_--,polaroid_px-70_-,polaroid_px-70,polaroid_px-70_+,polaroid_px-70_++,polaroid_px-70_+++,\ polaroid_px-70_cold_--,polaroid_px-70_cold_-,polaroid_px-70_cold,polaroid_px-70_cold_+,polaroid_px-70_cold_++,\ polaroid_px-70_warm_--,polaroid_px-70_warm_-,polaroid_px-70_warm,polaroid_px-70_warm_+,polaroid_px-70_warm_++,\ polaroid_time_zero_expired_---,polaroid_time_zero_expired_--,polaroid_time_zero_expired_-,\ polaroid_time_zero_expired,polaroid_time_zero_expired_+,polaroid_time_zero_expired_++,\ polaroid_time_zero_expired_cold_---,polaroid_time_zero_expired_cold_--,polaroid_time_zero_expired_cold_-,\ polaroid_time_zero_expired_cold _fx_cluts_instant_pro : u fuji_fp-100c_--,fuji_fp-100c_-,fuji_fp-100c,fuji_fp-100c_alt,fuji_fp-100c_+,fuji_fp-100c_++,fuji_fp-100c_++_alt,\ fuji_fp-100c_+++,\ fuji_fp-100c_cool_--,fuji_fp-100c_cool_-,fuji_fp-100c_cool,fuji_fp-100c_cool_+,fuji_fp-100c_cool_++,\ fuji_fp-100c_negative_--,fuji_fp-100c_negative_-,fuji_fp-100c_negative,fuji_fp-100c_negative_+,\ fuji_fp-100c_negative_++,fuji_fp-100c_negative_++_alt,fuji_fp-100c_negative_+++,\ fuji_fp-3000b_--,fuji_fp-3000b_-,fuji_fp-3000b,fuji_fp-3000b_+,fuji_fp-3000b_++,fuji_fp-3000b_+++,fuji_fp-3000b_hc,\ fuji_fp-3000b_negative_--,fuji_fp-3000b_negative_-,fuji_fp-3000b_negative,fuji_fp-3000b_negative_+,\ fuji_fp-3000b_negative_++,fuji_fp-3000b_negative_+++,fuji_fp-3000b_negative_early,\ polaroid_665_--,polaroid_665_-,polaroid_665,polaroid_665_+,polaroid_665_++,\ polaroid_665_negative_-,polaroid_665_negative,polaroid_665_negative_+,polaroid_665_negative_hc,\ polaroid_669_--,polaroid_669_-,polaroid_669,polaroid_669_+,polaroid_669_++,polaroid_669_+++,\ polaroid_669_cold_--,polaroid_669_cold_-,polaroid_669_cold,polaroid_669_cold_+,\ polaroid_690_--,polaroid_690_-,polaroid_690,polaroid_690_+,polaroid_690_++,\ polaroid_690_cold_--,polaroid_690_cold_-,polaroid_690_cold,polaroid_690_cold_+,polaroid_690_cold_++,\ polaroid_690_warm_--,polaroid_690_warm_-,polaroid_690_warm,polaroid_690_warm_+,polaroid_690_warm_++ _fx_cluts_fujixtransiii : u fuji_xtrans_iii_acros,fuji_xtrans_iii_acros+g,fuji_xtrans_iii_acros+r,fuji_xtrans_iii_acros+ye,\ fuji_xtrans_iii_astia,\ fuji_xtrans_iii_classic_chrome,fuji_xtrans_iii_mono,fuji_xtrans_iii_mono+g,fuji_xtrans_iii_mono+r,\ fuji_xtrans_iii_mono+ye,\ fuji_xtrans_iii_pro_neg_hi,fuji_xtrans_iii_pro_neg_std,fuji_xtrans_iii_provia,fuji_xtrans_iii_sepia,\ fuji_xtrans_iii_velvia _fx_cluts_negative_color : u agfa_ultra_color_100,agfa_vista_200,fuji_superia_200,fuji_superia_hg_1600,fuji_superia_reala_100,\ fuji_superia_x-tra_800,kodak_ektar_100,\ kodak_elite_100_xpro,kodak_elite_color_200,kodak_elite_color_400,kodak_portra_160_nc,kodak_portra_160_vc,\ lomography_redscale_100 _fx_cluts_negative_new : u fuji_160c_-,fuji_160c,fuji_160c_+,fuji_160c_++,\ fuji_400h_-,fuji_400h,fuji_400h_+,fuji_400h_++,\ fuji_800z_-,fuji_800z,fuji_800z_+,fuji_800z_++,\ ilford_hp_5_-,ilford_hp_5,ilford_hp_5_+,ilford_hp_5_++,\ kodak_portra_160_-,kodak_portra_160,kodak_portra_160_+,kodak_portra_160_++,\ kodak_portra_400_-,kodak_portra_400,kodak_portra_400_+,kodak_portra_400_++,\ kodak_portra_800_-,kodak_portra_800,kodak_portra_800_+,kodak_portra_800_++,kodak_portra_800_hc,\ kodak_tmax_3200_-,kodak_tmax_3200,kodak_tmax_3200_+,kodak_tmax_3200_++,kodak_tmax_3200_alt,\ kodak_tri-x_400_-,kodak_tri-x_400,kodak_tri-x_400_+,kodak_tri-x_400_++,kodak_tri-x_400_alt _fx_cluts_negative_old : u ilford_delta_3200_-,ilford_delta_3200,ilford_delta_3200_+,ilford_delta_3200_++,\ fuji_neopan_1600_-,fuji_neopan_1600,fuji_neopan_1600_+,fuji_neopan_1600_++,\ fuji_superia_100_-,fuji_superia_100,fuji_superia_100_+,fuji_superia_100_++,\ fuji_superia_400_-,fuji_superia_400,fuji_superia_400_+,fuji_superia_400_++,\ fuji_superia_800_-,fuji_superia_800,fuji_superia_800_+,fuji_superia_800_++,\ fuji_superia_1600_-,fuji_superia_1600,fuji_superia_1600_+,fuji_superia_1600_++,\ kodak_portra_160_nc_-,kodak_portra_160_nc,kodak_portra_160_nc_+,kodak_portra_160_nc_++,\ kodak_portra_160_vc_-,kodak_portra_160_vc,kodak_portra_160_vc_+,kodak_portra_160_vc_++,\ kodak_portra_400_nc_-,kodak_portra_400_nc,kodak_portra_400_nc_+,kodak_portra_400_nc_++,\ kodak_portra_400_uc_-,kodak_portra_400_uc,kodak_portra_400_uc_+,kodak_portra_400_uc_++,\ kodak_portra_400_vc_-,kodak_portra_400_vc,kodak_portra_400_vc_+,kodak_portra_400_vc_++ _fx_cluts_print : u fuji_3510_constlclip,fuji_3510_constlmap,fuji_3510_cuspclip,\ fuji_3513_constlclip,fuji_3513_constlmap,fuji_3513_cuspclip,\ kodak_2383_constlclip,kodak_2383_constlmap,kodak_2383_cuspclip,\ kodak_2393_constlclip,kodak_2393_constlmap,kodak_2393_cuspclip _fx_cluts_colorslide : u agfa_precisa_100,fuji_astia_100f,fuji_fp_100c,fuji_provia_100f,fuji_provia_400f,fuji_provia_400x,fuji_sensia_100,\ fuji_superia_200_xpro,fuji_velvia_50,fuji_astia_100_generic,fuji_provia_100_generic,fuji_velvia_100_generic,\ kodak_kodachrome_64_generic,kodak_ektachrome_100_vs_generic,kodak_e-100_gx_ektachrome_100,kodak_ektachrome_100_vs,\ kodak_elite_chrome_200,\ kodak_elite_chrome_400,kodak_elite_extracolor_100,kodak_kodachrome_200,kodak_kodachrome_25,kodak_kodachrome_64,\ lomography_x-pro_slide_200,\ polaroid_669,polaroid_690,polaroid_polachrome #@gui Transfer Colors [Variational] : fx_transfer_rgb, fx_transfer_rgb_preview(1)+ : * #@gui : Regularization = int(8,0,32) #@gui : Preserve Luminance = float(0.2,0,1) #@gui : Precision = _choice(1,"Low","Normal","High","Very High") #@gui : Reference Colors = choice("Bottom Layer","Top Layer") #@gui : Add User-Defined Constraints (Interactive) = _bool(0) #@gui : sep = separator() #@gui : Preview_ref_point = point(1,1,0,0,255,255,255,128,4)_0 #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Instructions:\n #@gui : - This filter transfers the colors of one layer to all the others.\n #@gui : - Don't forget to set the Input layers... option on the left to manage your input layers.\n #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/04/04.") fx_transfer_rgb : to_rgb ref:=$4?0:-1 match_rgb[^$ref] [$ref],0.25,$1,$2,{2^(4+$3)},$5,0 c 0,255 fx_transfer_rgb_preview : if $!<2 gui_print_preview "Warning:",,"This filter requires at least two input layers to work properly." return fi ref:=$4?0:-1 +store[$ref] _fx_trgb_ref gui_split_preview[^$ref] "$_fx_trgb_ref fx_transfer_rgb $1,$2,0,0,0 rm.",${-3--1} _fx_trgb_ref= mv[$ref] $! repeat $!-1 { l[$>,-1] { rs[0] $_preview_area_width,$_preview_area_height,3 rs. {0,[w,h]/3},3 to. Reference,2,2,13,1,1,255 frame. 2,2,255 frame. 1,1,0 j[0] .,$6%,$7% rm. } } #@gui Transfer Colors [Histogram] : fx_transfer_histogram, fx_transfer_histogram_preview(1)+ : * #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Reference Colors = choice("Bottom Layer","Top Layer") #@gui : sep = separator() #@gui : Preview_ref_point = point(1,1,0,0,255,255,255,128,4)_0 #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter needs at least two layers to work properly. Set the Input layers option to handle #@gui : multiple input layers. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/01/13.") fx_transfer_histogram : to_rgb ref:=$2?0:-1 match_histogram[^$ref] [$ref],256,$1 c 0,255 fx_transfer_histogram_preview : if $!<2 gui_print_preview "Warning:",,"This filter requires at least two input layers to work properly." return fi ref:=$2?0:-1 +store[$ref] _fx_trgb_ref gui_split_preview[^$ref] "$_fx_trgb_ref fx_transfer_histogram $1,0 rm.",${-3--1} _fx_trgb_ref= mv[$ref] $! repeat $!-1 { l[$>,-1] { rs[0] $_preview_area_width,$_preview_area_height,3 rs. {0,[w,h]/3},3 to. Reference,2,2,13,1,1,255 frame. 2,2,255 frame. 1,1,0 j[0] .,$3%,$4% rm. } } #@gui Transfer Colors [PCA] : fx_transfer_pca, fx_transfer_pca_preview(1)+ : * #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Reference Colors = choice("Bottom Layer","Top Layer") #@gui : sep = separator() #@gui : Preview_ref_point = point(1,1,0,0,255,255,255,128,4)_0 #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter needs at least two layers to work properly. Set the Input layers option to handle #@gui : multiple input layers. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/01/13.") fx_transfer_pca : to_rgb ref:=$2?0:-1 match_pca[^$ref] [$ref],$1 c 0,255 fx_transfer_pca_preview : if $!<2 gui_print_preview "Warning:",,"This filter requires at least two input layers to work properly." return fi ref:=$2?0:-1 +store[$ref] _fx_trgb_ref gui_split_preview[^$ref] "$_fx_trgb_ref fx_transfer_pca $1,0 rm.",${-3--1} _fx_trgb_ref= mv[$ref] $! repeat $!-1 { l[$>,-1] { rs[0] $_preview_area_width,$_preview_area_height,3 rs. {0,[w,h]/3},3 to. Reference,2,2,13,1,1,255 frame. 2,2,255 frame. 1,1,0 j[0] .,$3%,$4% rm. } } #@gui Tune HSV Colors : fx_tune_hsv, fx_tune_hsv_preview(1) #@gui : Dark = choice(2,"Ignore","Lock","Remap") #@gui : Dark Color = color(0,0,0) #@gui : Target Hue (%) = float(100,0,100) #@gui : Target Saturation (%) = float(100,0,100) #@gui : Target Value (%) = float(100,0,100) #@gui : sep = separator() #@gui : Light = choice(2,"Ignore","Lock","Remap") #@gui : Light Color = color(255,255,255) #@gui : Target Hue (%) = float(100,0,100) #@gui : Target Saturation (%) = float(100,0,100) #@gui : Target Value (%) = float(100,0,100) #@gui : sep = separator() #@gui : Average = choice(1,"Ignore","Lock","Remap") #@gui : Average Color = color(128,128,128)_0 #@gui : Target Hue (%) = float(0,0,100)_0 #@gui : Target Saturation (%) = float(50,0,100)_0 #@gui : Target Value (%) = float(100,0,100)_0 #@gui : sep = separator() #@gui : Red = choice("Ignore","Lock","Remap") #@gui : Red Color = color(255,0,0)_0 #@gui : Target Hue (%) = float(100,0,100)_0 #@gui : Target Saturation (%) = float(12.5,0,100)_0 #@gui : Target Value (%) = float(0,0,100)_0 #@gui : sep = separator() #@gui : Yellow = choice("Ignore","Lock","Remap") #@gui : Yellow Color = color(255,255,0)_0 #@gui : Target Hue (%) = float(100,0,100)_0 #@gui : Target Saturation (%) = float(12.5,0,100)_0 #@gui : Target Value (%) = float(0,0,100)_0 #@gui : sep = separator() #@gui : Green = choice("Ignore","Lock","Remap") #@gui : Green Color = color(0,255,0)_0 #@gui : Target Hue (%) = float(100,0,100)_0 #@gui : Target Saturation (%) = float(12.5,0,100)_0 #@gui : Target Value (%) = float(0,0,100)_0 #@gui : sep = separator() #@gui : Cyan = choice("Ignore","Lock","Remap") #@gui : Cyan Color = color(0,255,255)_0 #@gui : Target Hue (%) = float(100,0,100)_0 #@gui : Target Saturation (%) = float(12.5,0,100)_0 #@gui : Target Value (%) = float(0,0,100)_0 #@gui : sep = separator() #@gui : Blue = choice("Ignore","Lock","Remap") #@gui : Blue Color = color(0,0,255)_0 #@gui : Target Hue (%) = float(100,0,100)_0 #@gui : Target Saturation (%) = float(12.5,0,100)_0 #@gui : Target Value (%) = float(0,0,100)_0 #@gui : sep = separator() #@gui : Magenta = choice("Ignore","Lock","Remap") #@gui : Magenta Color = color(255,0,255)_0 #@gui : Target Hue (%) = float(100,0,100)_0 #@gui : Target Saturation (%) = float(12.5,0,100)_0 #@gui : Target Value (%) = float(0,0,100)_0 #@gui : sep = separator() #@gui : Preview color mapping = choice(2,"None","Center","Top Left","Top Right","Bottom Left","Bottom Right") #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/12/17.") _fx_tune_hsv : mode_dark,Rdark,Gdark,Bdark,Hdark,Sdark,Vdark,\ mode_light,Rlight,Glight,Blight,Hlight,Slight,Vlight,\ mode_avg,Ravg,Gavg,Bavg,Havg,Savg,Vavg,\ mode_red,Rred,Gred,Bred,Hred,Sred,Vred,\ mode_yellow,Ryellow,Gyellow,Byellow,Hyellow,Syellow,Vyellow,\ mode_green,Rgreen,Ggreen,Bgreen,Hgreen,Sgreen,Vgreen,\ mode_cyan,Rcyan,Gcyan,Bcyan,Hcyan,Scyan,Vcyan,\ mode_blue,Rblue,Gblue,Bblue,Hblue,Sblue,Vblue,\ mode_magenta,Rmagenta,Gmagenta,Bmagenta,Hmagenta,Smagenta,Vmagenta,\ preview_mapping=${1-64} all_colors=dark,light,avg,red,yellow,green,cyan,blue,magenta foreach { split_opacity l[0] { to_rgb # Define list of considered colors. colors,sep= repeat narg($all_colors) { color=${arg0\ $>,$all_colors} if ${mode_$color} colors.=$sep$color sep=, fi } # Find darkest, lightest and average colors. +srgb2lab channels. 0 darklight:="I(#-2,xm,ym),I(#-2,xM,yM)" rm. dark:=[$darklight][0,3] light:=[$darklight][3,3] +r. 1,1,1,3,2 avg={^} rm. # Find the nearest existing colors to "pure" colors. eval. "> begin( red = yellow = green = cyan = blue = magenta = [0,0,0]; best_red = best_yellow = best_green = best_cyan = best_blue = best_magenta = inf; test_color(color,tR,tG,tB) = ( val = norm([tR,tG,tB]-[R,G,B]); valAuthor: David Tschumperlé.      Latest Update: 2010/29/12.") fx_custom_transform : to_rgba repeat $! { f. "$1" s. c a[-4--2] c f.. "$2" s.. c f[-4] "$3" f... "$4" f.. "$5" f. "$6" if !$7 a[-4--1] c c. 0,255 elif $7==1 a[-4--2] c n.. 0,255 c. 0,255 a[-2,-1] c else a[-4--1] c n. 0,255 fi mv. 0 } #@gui Vibrance : fx_vibrance, fx_vibrance_preview(1)+ #@gui : Strength = float(0.5,-1,3) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: Age / Pixls.us.      Latest Update: 2022/06/28.") fx_vibrance : foreach { split_opacity l[0] { to_rgb f "const vibrance = 1 + $1; vib(val) = ( v = val/255; vv = (v - lum)*vibrance + lum; (vv*(1 - sat) + v*sat)*255 ); min = min(R,G,B)/255; lum = (R*0.2 + G*0.7 + B*0.1)/255; sat = cut(1 - min/lum,0,1); [ vib(R),vib(G),vib(B) ]" c 0,255 } a c } fx_vibrance_preview : gui_split_preview "fx_vibrance $*",${-3--1} #@gui ____Contours #------------------------ #@gui Convolve : fx_convolve, fx_convolve_preview(0) #@gui : Kernel = choice("Custom","Average 3x3","Average 5x5","Average 7x7","Average 9x9","Prewitt-X","Prewitt-Y", #@gui : "Sobel-X","Sobel-Y","Rotinv-X","Rotinv-Y","Laplacian","Robert Cross 1","Robert Cross 2","Impulses 5x5", #@gui : "Impulses 7x7","Impulses 9x9") #@gui : Boundary = choice(1,"Dirichlet","Neumann") #@gui : sep = separator() #@gui : note = note("Note: If parameter Kernel is set to Custom, it uses the custom #@gui : convolution kernel defined below. Use commas and semicolons as separators for res. matrix columns and rows. #@gui : ") #@gui : Custom Kernel = text("0,1,0;1,-4,1;0,1,0") #@gui : sep = separator() #@gui : note = note("Note: Kernel multiplier is useful only when parameter Value range is set #@gui : to Cut.") #@gui : Value Range = choice(1,"Cut","Normalize") #@gui : Kernel Multiplier = float(1,0,50) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/06/06.") fx_convolve : skip "${3=1}" ac "_fx_convolve $1,$2,\"$3\",${4--5}",$-4 _fx_convolve : if $1 _fx_convolve$1[] else ($3) fi if !$4 *. $5 fi convolve[0--2] .,$2 if $4 n 0,255 else c 0,255 fi rm. _fx_convolve1 : 3,3 f 1 normalize_sum # Average 3x3 _fx_convolve2 : 5,5 f 1 normalize_sum # Average 5x5 _fx_convolve3 : 7,7 f 1 normalize_sum # Average 7x7 _fx_convolve4 : 9,9 f 1 normalize_sum # Average 9x9 _fx_convolve5 : (1,0,-1;1,0,-1;1,0,-1) # Prewitt-X _fx_convolve6 : (1,1,1;0,0,0;-1,-1,-1) # Prewitt-Y _fx_convolve7 : (1,0,-1;2,0,-2;1,0,-1) # Sobel-X _fx_convolve8 : (1,2,1;0,0,0;-1,-2,-1) # Sobel-Y _fx_convolve9 : a:=0.25*(2-sqrt(2)) b:=0.5*(sqrt(2)-1) ($a,0,-$a;$b,0,-$b;$a,0,-$a) # Rotinv-X _fx_convolve10 : a:=0.25*(2-sqrt(2)) b:=0.5*(sqrt(2)-1) ($a,$b,$a;0,0,0;-$a,-$b,-$a) # Rotinv-Y _fx_convolve11 : (0,1,0;1,-4,1;0,1,0) # Laplacian _fx_convolve12 : (1,0;0,-1) # Robert Cross1 _fx_convolve13 : (0,1;-1,0) # Robert Cross2 _fx_convolve14 : 3,3 f 1 r 7,7,1,1,4,0,0.5,0.5 autocrop normalize_sum # Impulse 5x5 _fx_convolve15 : 3,3 f 1 r 9,9,1,1,4,0,0.5,0.5 autocrop normalize_sum # Impulse 7x7 _fx_convolve16 : 3,3 f 1 r 11,11,1,1,4,0,0.5,0.5 autocrop normalize_sum # Impulse 9x9 fx_convolve_preview : skip "${3=1}" gui_split_preview "fx_convolve $1,$2,\"$3\",${4--1}",${-3--1} #@gui Curvature : fx_curvature, fx_curvature_preview(0) #@gui : Smoothness = float(2,0,10) #@gui : Min Threshold = float(0,0,100) #@gui : Max Threshold = float(100,0,100) #@gui : Absolute Value = bool(0) #@gui : Negative Colors = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_curvature : foreach { split_opacity l[0] { b $1 iee if $4 abs fi c $2%,$3% if $5 negate fi n 0,255 } a c } fx_curvature_preview : gui_split_preview "fx_curvature ${^0}",${-3--1} #@gui Difference of Gaussians : fx_dog, fx_dog_preview(1)* #@gui : 1st Variance = float(1.4,0,5) #@gui : 2nd Variance = float(1.5,0,5) #@gui : Threshold = float(0,0,49) #@gui : Negative Colors = bool(0) #@gui : Monochrome = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_dog : dog $1%,$2% if $5 norm fi c $3%,{100-$3}% if $4 negate fi n 0,255 fx_dog_preview : gui_split_preview "gui_crop_preview fx_dog ${^0} gui_resize_preview",${-3--1} #@gui Distance Transform : fx_distance, fx_distance_preview(0) #@gui : Value = int(128,0,255) #@gui : Metric = choice(2,"Chebyshev","Manhattan","Euclidean","Squared-Euclidean") #@gui : Normalization = choice(2,"Cut","Normalize","Modulo") #@gui : Modulo Value = int(32,1,255) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/07/04.") fx_distance : foreach { split_opacity l[0] { distance $1,$2 if !$3 c 0,255 elif $3==1 n 0,255 else % $4 n 0,255 fi } a c } fx_distance_preview : gui_split_preview "fx_distance ${^0}",${-3--1} #@gui Edges : fx_edges, fx_edges_preview(0) #@gui : Smoothness = float(0,0,10) #@gui : Threshold = float(15,0,50) #@gui : Negative Colors = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_edges : to_rgb b $1% edges $2% if $3 negate fi n 0,255 fx_edges_preview : gui_split_preview "fx_edges ${^0}",${-3--1} #@gui Edges (Canny) : fx_canny, fx_canny_preview(0) #@gui : Sigma = float(5,0,20) #@gui : Lower Threshold = float(0.05,0,1) #@gui : Upper Threshold = float(0.15,0,1) #@gui : Monochrome = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: Garagecoder and David Tschumperlé.      \ # Latest Update: 2023/12/04.") fx_canny : if $4 norm fi foreach { split_opacity canny[0] $1,{[max($2,1e-6),max($3,1e-6)]} *[0] 255 a c } fx_canny_preview : gui_split_preview "fx_canny $*",${-3--1} #@gui Edges Offsets : fx_edge_offsets, fx_edge_offsets_preview(0) #@gui : Smoothness = float(0,0,10) #@gui : Threshold = float(15,0,50) #@gui : Scale = int(4,0,32) #@gui : Thickness = int(1,0,16) #@gui : Negative Colors = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_edge_offsets : repeat $! { os:=s b. $1% gradient_norm. >=. $2% skeleton. 0 distance. 1 round. 1 %. $3 >=. {max(1,$3-$4)} if !$5 negate. fi n. 0,255 to_colormode. $os mv. 0 } fx_edge_offsets_preview : gui_split_preview "fx_edge_offsets ${^0}",${-3--1} #@gui Extract Foreground [Interactive] : fx_extract_foreground, gui_no_preview #@gui : Feathering = _float(0,0,4) #@gui : Dilation = int(0,-32,32) #@gui : Output Mode = choice{3,"RGBA Image (Full-Transparency / 1 Layer)","RGBA Image (Updatable / 1 Layer)", #@gui : "RGB Image + Binary Mask (2 Layers)","RGBA Foreground + Background (2 Layers)"} #@gui : View Resolution = _choice{1,"Small (Faster)","Medium","High (Slower)","Very High (Even Slower)"} #@gui : sep = separator() #@gui : note = note{"Description:\n #@gui : This filter allows to quickly extract foreground objects from background in opaque RGB images. #@gui : Click on the Apply or OK buttons below to open the interactive window and start adding #@gui : foreground and background control points. When you're done, exit the interactive window: your extracted #@gui : foreground will be transferred back to the host software.\n\n #@gui : If you are not satisfied with the result, click on Apply once again to modify your control points #@gui : defined previously. To remove all control points, click on the Reset button above. #@gui : "} #@gui : Last Image Size = value(0,0) #@gui : Control Points = value(-1) #@gui : sep = separator() #@gui : note = note{"Interactions:\n #@gui : Use the following actions in the interactive window to build your extraction mask :\n\n #@gui : - Left mouse button or key F create a new foreground control point #@gui : (or move an existing one).\n #@gui : - Right mouse button or key B create a new background control point #@gui : (or move an existing one).\n #@gui : - Mouse wheel, or keys CTRL+arrows UP/DOWN zoom view in/out.\n #@gui : - Key SPACE updates the extraction mask.\n #@gui : - Key TAB toggles background view modes.\n #@gui : - Key M toggles marker view modes.\n #@gui : - Key BACKSPACE deletes the last control point added.\n #@gui : - Key PAGE UP increases background opacity.\n #@gui : - Key PAGE DOWN decreases background opacity.\n #@gui : - Keys CTRL+D increase window size.\n #@gui : - Keys CTRL+C decrease window size.\n #@gui : - Keys CTRL+R reset window size.\n #@gui : - Keys ESC, Q or ENTER exit the interactive window. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/29/09.") fx_extract_foreground : if !$! return fi resolution:=arg(1+$3,512,1024,2048,0) foreach { nm=${-gui_layer_name} initial_pos=${-gui_layer_pos} => "[G"{`39`}"MIC] Interactive Foreground Extraction" if [$6][0]==-1" || "[$5]!=[w,h] _gui_control_points= else _gui_control_points=$6 fi status=${x_segment\ $resolution} sh 3 b. $1% if $2>0 dilate. {1+2*$2} elif $2<0 erode. {1-2*$2} fi rm. if $3==1 sh 3 max. 1 rm. elif $3==2 s c,-3 r. 100%,100%,1,4 rv =>[0] "name(Mask)" =>[1] "name("$nm")" elif $3==3 . sh.. 0,2 +channels... 3,3 >=. 3 *[-2,-1] rm. sh. 0,2 +channels.. 3,3 <=. {255-3} *[-2,-1] rm. sh. 3 *. -1 +. 255 rm. gui_autocrop_layers[0] pos0=${gui_layer_pos[0]} pos1=${gui_layer_pos[1]} =>[0] "name("$nm" [foreground]),pos("{``{[$pos0]+[$initial_pos]}}")" =>[1] "name("$nm" [background]),pos("{``{[$pos1]+[$initial_pos]}}")" fi } if narg($status)>=4 u \{$1\}\{$2\}\{$3\}\{$4\}\{{w},{h}\}\{$status\} else u "" fi #@gui Gradient Norm : fx_gradient_norm, fx_gradient_norm_preview(0) #@gui : Smoothness = float(0,0,10) #@gui : Linearity = float(0.5,0,1.5) #@gui : Min Threshold = float(0,0,100) #@gui : Max Threshold = float(100,0,100) #@gui : Negative Colors = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_gradient_norm : b $1 gradient_norm ^ $2 c $3%,$4% if $5 negate fi n 0,255 fx_gradient_norm_preview : gui_split_preview "fx_gradient_norm ${^0}",${-3--1} #@gui Gradient RGB : fx_gradient2rgb, fx_gradient2rgb_preview(0) #@gui : Smoothness = float(0,0,10) #@gui : Min Threshold = float(0,0,100) #@gui : Max Threshold = float(100,0,100) #@gui : Orientation Only = bool(0) #@gui : Negative Colors = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_gradient2rgb : b $1 gradient2rgb $4 c $2%,$3% if $5 negate fi n 0,255 fx_gradient2rgb_preview : gui_split_preview "fx_gradient2rgb ${^0}",${-3--1} #@gui Isophotes : fx_isophotes, fx_isophotes_preview(0) #@gui : Levels = int(8,1,256) #@gui : Smoothness = float(0,0,5) #@gui : Filling = choice(1,"Transparent","Colors") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_isophotes : if $3 topographic_map $1,$2 else b $2 isophotes $1 fi fx_isophotes_preview : gui_split_preview "fx_isophotes ${^0}",${-3--1} #@gui Laplacian : fx_laplacian, fx_laplacian_preview(0) #@gui : Smoothness = float(0,0,10) #@gui : Min Threshold = float(0,0,100) #@gui : Max Threshold = float(100,0,100) #@gui : Absolute Value = bool(0) #@gui : Negative Colors = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_laplacian : b $1 laplacian if $4 abs fi c $2%,$3% if $5 negate fi n 0,255 fx_laplacian_preview : gui_split_preview "fx_laplacian ${^0}",${-3--1} #@gui Local Orientation : fx_local_orientation, fx_local_orientation_preview(1) #@gui : Smoothness = float(0,0,5) #@gui : Min Threshold = float(0,0,100) #@gui : Max Threshold = float(100,0,100) #@gui : Negative Colors = bool(0) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") _fx_local_orientation : foreach { split_opacity l[0] { b $1% gradient_orientation 2 complex2polar rm[0--1:2] c $2%,$3% if $4 negate fi n 0,255 } a c } fx_local_orientation : ac "_fx_local_orientation $1,$2,$3,$4",$5,2 fx_local_orientation_preview : gui_split_preview "fx_local_orientation ${^0}",${-3--1} #@gui Morphological Filter : fx_morphological, fx_morphological_preview(0) #@gui : Action = choice{"Erosion","Dilation","Opening","Closing","Original - Erosion","Dilation - Original", #@gui : "Original - Opening","Closing - Original","Original - (Opening + Closing)/2","Closing - Opening"} #@gui : Kernel = choice(0,"Square","Octagonal","Circular","Custom") #@gui : Size = int(5,2,60) #@gui : note = note("Parameter Size is inactive for Custom kernel.") #@gui : Custom Kernel = text("1,0,1; 0,1,0; 1,0,1") #@gui : Negative = bool() #@gui : Process Transparency = bool(0) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Stretch") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/22/06.") fx_morphological : ac "_fx_morphological ${1-3},\"$4\",${5-6}",$7,$8 fx_morphological_preview : gui_split_preview "fx_morphological ${1-3},\"$4\",${5--1}",${-3--1} if $2==3 ('"$4"') f. "(i>=_'0' && i<=_'9') || i==_',' || i==_';'?i:-1" discard. -1 ({t}) rs. {0,max(24,w/6)},{0,max(24,h/6)},1 >. 0 *. 255 to_rgba. frame. 1,1,0,0,0,0,255 frame. 1,1,255 frame. 1,1,0,0,0,0,255 j[^-1] .,2,2,0,0,0.75 rm. else fi _fx_morphological : ('"$4"') f. "(i>=_'0' && i<=_'9') || i==_',' || i==_';'?i:-1" discard. -1 ckernel={t} rm. if !$2 m "my_erode: erode $""1" m "my_dilate: dilate $""1" m "my_opening : opening $""1" m "my_closing : closing $""1" elif $2==1 m "my_erode: erode_oct $""1" m "my_dilate: dilate_oct $""1" m "my_opening : opening_circ $""1" m "my_closing : closing_circ $""1" elif $2==2 m "my_erode: erode_circ $""1" m "my_dilate: dilate_circ $""1" m "my_opening : opening_circ $""1" m "my_closing : closing_circ $""1" else m "my_erode : skip $""1 ("$ckernel") erode[^-1] . rm." m "my_dilate : skip $""1 ("$ckernel") dilate[^-1] . rm." m "my_opening : skip $""1 ("$ckernel") opening[^-1] . rm." m "my_closing : skip $""1 ("$ckernel") closing[^-1] . rm." fi # Erosion if !$1 m "my_action : my_erode $3" # Dilation elif $1==1 m "my_action : my_dilate $3" # Opening elif $1==2 m "my_action : my_opening $3" # Closing elif $1==3 m "my_action : my_closing $3" # Original - Erosion elif $1==4 m "my_action : +my_erode $3 -" # Dilation - Original elif $1==5 m "my_action : +my_dilate $3 rv -" # Original - Opening elif $1==6 m "my_action : +my_opening $3 -" # Closing - Original elif $1==7 m "my_action : +my_closing $3 rv -" # Original - (Opening + Closing)/2 elif $1==8 m "my_action : +my_opening $3 +my_closing.. $3 +[-2,-1] /. 2 -" # Closing - Opening else m "my_action : +my_opening $3 my_closing.. $3 -" fi foreach { if !$6 split_opacity fi my_action[0] a c } if $5 foreach { split_opacity negate[0] a c } fi um my_erode,my_dilate,my_action #@gui Segmentation : fx_segment_watershed, fx_segment_watershed_preview(0) #@gui : Edge Threshold = float(2,0,15) #@gui : Smoothness = float(1,0,5) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_segment_watershed : skip ${4=1} ac "b $2 segment_watershed $1",$3,$4 fx_segment_watershed_preview : gui_split_preview "fx_segment_watershed ${^0}",${-3--1} #@gui Skeleton : fx_skeleton, fx_skeleton_preview(1) #@gui : Method = choice{"Distance (Fast)","Thinning (Slow)"} #@gui : Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/07/04.") fx_skeleton : remove_opacity b $2% >= 50% if $1 thinning 1 else distance 0 sharpen 1e10 >= 100% repeat $! { +erode[$>] 2 -[$>,-1] } fi * 255 fx_skeleton_preview : gui_split_preview "fx_skeleton ${^0}",${-3--1} #@gui Super-Pixels : fx_superpixels, fx_superpixels_preview(0) #@gui : Size = int(16,4,64) #@gui : Regularity = float(10,0,128) #@gui : Iterations = int(5,1,16) #@gui : Colors = choice(1,"Random","Average") #@gui : Border Opacity = float(1,0,1) #@gui : Border Thickness (px) = int(1,1,16) #@gui : Border Color = color(0,0,0,255) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/11/16.") fx_superpixels : foreach { +srgb2lab slic. ${1-3} if $4 +blend shapeaverage else +map. 2,2 fi if $5 (0,1,0;1,1,1;0,1,0) +dilate[1] . -[1,-1] neq[1] 0 rm. if $6 dilate_circ[1] $6 fi [0],[0],1,4 fc. ${7-10} to_rgba.. j.. .,0,0,0,0,$5,... k.. else k. fi } fx_superpixels_preview : gui_split_preview "fx_superpixels ${^0}",${-3--1} #@gui Thin Edges : fx_thin_edges, fx_thin_edges_preview(0) #@gui : Smoothness = float(0,0,10) #@gui : Threshold = float(15,0,50) #@gui : Negative Colors = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_thin_edges : b $1% gradient_norm >= $2% thinning 1 if !$3 negate fi n 0,255 fx_thin_edges_preview : gui_split_preview "fx_thin_edges ${^0}",${-3--1} #@gui ____Deformations #---------------------------- #@gui Breaks : fx_breaks,fx_breaks(0) #@gui : Type = choice("Flat","Relief") #@gui : Amplitude = float(30,0,300) #@gui : Frequency (%) = float(30,0,100) #@gui : Smoothness = float(0.5,0,10) #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/09/09.") fx_breaks : foreach { if !$5 to_a fi 100%,100%,1,1,"u<($3%)^6" if $1 # Wavy distance. 1 sharpen. 100000 neq. 0 distance. 1 b. $4 g. xy a[-2,-1] c else # Flat delaunay. 0 label_fg. 0,1 {1+iM},1,1,2 rand. -30,30 point. 0 map.. . rm. fi n. -$2,$2 warp[0] .,1,1,$5 rm. } #@gui Cartesian Transform : fx_custom_deformation, fx_custom_deformation(1) #@gui : X-Warping = text{"(w*a%)*cos(b*y/h)"} #@gui : Y-Warping = text{"(h*c%)*cos(d*x/w)"} #@gui : Relative Warping = bool(1) #@gui : Interpolation = choice(1,"Nearest Neighbor","Linear") #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Note:The parameters below can be used in the warping formulas:") #@gui : a = float(10,-100,100) #@gui : b = float(10,-100,100) #@gui : c = float(10,-100,100) #@gui : d = float(10,-100,100) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/10/26.") fx_custom_deformation : if !$5 to_a fi str="const a = $6; const b = $7; const c = $8; const d = $9;" repeat $! { +norm. . f.. $str"($1)" f. $str"($2)" a[-2,-1] c warp.. .,$3,$4,$5,1 rm. mv. 0 } #@gui Circle Transform : fx_circle_transform, fx_circle_transform_preview(1) #@gui : Center (%) = point(50,50,0,1) #@gui : Radius = point(75,50,0,1) #@gui : X-Scale = float(-2,-16,16) #@gui : Y-Scale = float(-2,-16,16) #@gui : Symmetry = choice("None","Inside","Outside") #@gui : Interpolation = choice(1,"Nearest Neighbor","Linear") #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : Preview Reference Circle = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/08/01.") fx_circle_transform : foreach { to_rgba r:=dx=($3-$1)*(w-1)%;dy=($4-$2)*(h-1)%;norm(dx,dy) if !$7 cond="i(X,Y,z,c,$8,$9)" elif $7==1 cond="N<$r?i(X,Y,z,c,$8,$9):i" else cond="N>$r?i(X,Y,z,c,$8,$9):i" fi f "U = x - w*$1%; V = y - h*$2%; N = sqrt(U*U + V*V); Nr = N - "$r"; X = x + $5*Nr*U/N; Y = y + $6*Nr*V/N; "$cond } fx_circle_transform_preview : fx_circle_transform $* if $10 rs ${-gui_preview_wh},1 foreach { x0,y0:=[$1,$2]*([w,h]-1)% r:=dx=($3-$1)*(w-1)%;dy=($4-$2)*(h-1)%;norm(dx,dy) circle $x0,$y0,{$r-1},1,0xFFFFFFFF,0,0,0,255 circle $x0,$y0,{$r+1},1,0xFFFFFFFF,0,0,0,255 circle $x0,$y0,$r,1,0xFFFFFFFF,0,255,0,255 } fi #@gui Conformal Maps : fx_conformal_maps, fx_conformal_maps_preview(1) #@gui : Mapping = choice{8,"Custom Formula","z","(z+1)/(z-1)","cos(z)","sin(z)","tan(z)","exp(z)","log(z)", #@gui : "Dipole: 1/(4*z^2-1)","Star: -5*(z^3/3-z/4)/2"} #@gui : Exponent (Real) = float(1,-16,16) #@gui : Exponent (Imaginary) = float(0,-16,16) #@gui : Custom Formula = text{1,"((1.1 + i*z/6)/(1.04 - i*z/6))^6.2"} #@gui : sep = separator() #@gui : Zoom = float(0,-4,4) #@gui : Angle = float(0,-180,180) #@gui : Aspect Ratio = float(0,-1,1) #@gui : X-Shift = float(0,-5,5) #@gui : Y-Shift = float(0,-5,5) #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : Anti-Aliasing = int(0,0,3) #@gui : sep = separator() #@gui : Specify Different Output Size = _bool(0) #@gui : Output Width = _text("1024") #@gui : Output Height = _text("1024") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/15/02.") fx_conformal_maps : to_a expr0="$4" expr1="z" expr2="(z+1)/(z-1)" expr3="cos(z)" expr4="sin(z)" expr5="tan(z)" expr6="exp(z)" expr7="log(z)" expr8="1/(4*z^2-1)" expr9="-5*(z^3/3-z/4)/2" ('${expr$1}') replace_str. "*","**" replace_str. "/","//" replace_str. "^","^^" replace_str. "exp(","cexp(" replace_str. "log(","clog(" replace_str. "cos(","ccos(" replace_str. "sin(","csin(" replace_str. "tan(","ctan(" expr={t} rm. foreach { wh:=$12?[$13,$14]:[w,h] {(1+$11)*[$wh]},1,100% f. "begin( ccos(z) = (iz = [ -z[1],z[0] ]; (cexp(iz) + cexp(-iz)/2)); csin(z) = (iz = [ -z[1],z[0] ]; (cexp(iz) - cexp(-iz)/2)); ctan(z) = csin(z)//ccos(z); boundary = $10; interpolation = 1; const f = max(w,h); const f0 = max(w#0,h#0); i = [0,1]; ); z = (2*[ x,y ] - [ w,h ])/f; z = rot(-$6°)*z; z-= [ $8, $9 ]; z/=[ 10^($5 + $7), 10^$5 ]; z = ("$expr"); if ($1, z = z^^[$2,$3]); z = rot($6°)*z; z = 0.5*(f0*z + [w#0,h#0]); I(#0,z)" r. $wh,1,100%,2 rm.. } fx_conformal_maps_preview : fx_conformal_maps ${1-3},"$4",${5-11},0,0,0 #@gui Crease : fx_crease,fx_crease(0) #@gui : Amplitude = float(30,0,300) #@gui : Frequency (%) = float(10,0,100) #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/01/22.") fx_crease : foreach { if !$3 to_a fi 2,2,1,2,{"const w1 = w#-1-1; const h1 = h#-1 - 1; [ 0,w1,0,w1,0,0,h1,h1 ];"} r. {0,D=$2*[w,h]%;[max(D[0],1),max(D[1],1)]},1,2,3 noise. $1,1 r. ..,..,1,2,3 warp.. .,0,1,$3 rm. } #@gui Distort Lens : fx_distort_lens, fx_distort_lens(1) #@gui : Amplitude = float(0.1,-1,1) #@gui : Aspect Ratio = float(0,-2,2) #@gui : Zoom = float(0,-4,4) #@gui : Center (%) = point(50,50,0,1) #@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/18/02.") fx_distort_lens : if !$6 to_a fi undistort ${1-3},$4%,$5%,$6 #@gui Drop Water : fx_drop_water, fx_drop_water_preview(1) #@gui : note = note("Shape geometry:") #@gui : Shapes = choice("Procedural","Opaque Regions on Top Layer") #@gui : Density = float(20,0,100) #@gui : Radius = float(2,0,5) #@gui : Variability = float(80,0,100) #@gui : Random Seed = int(0,0,16384) #@gui : note = note("Parameters Density, Radius, Variability and Random seed #@gui : are used only in Procedural shapes mode.") #@gui : sep = separator() #@gui : note = note("Light parameters:") #@gui : Refraction = float(3,0,20) #@gui : Light Angle = float(35,0,360) #@gui : Specular Size = float(10,0,100) #@gui : Specular Intensity = float(1,0,1) #@gui : Specular Centering = float(0.5,0,1) #@gui : sep = separator() #@gui : note = note("Shadow parameters:") #@gui : Shadow Size = float(0.25,0,3) #@gui : Shadow Intensity = float(0.5,0,1) #@gui : Shadow Smoothness = float(0.75,0,3) #@gui : Diffuse Shadow = float(0.05,0,3) #@gui : sep = separator() #@gui : Smoothness = float(0.15,0,3) #@gui : Output as Separate Layers = _bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/21/07.") fx_drop_water : N:=$!-$1 if $N<=0 error "At least two layers are required in this mode." fi repeat $N { l[{$!-$>-1}] { nm0={n} nm=${-gui_layer_name} => img # Create binary shapes (i.e. opacity map). srand $5 if $1 # Shape from top layer. pass[0] 0 to_a. channels. 100% >=. 50% r. [0],[0],1,1,0,0,0.5,0.5 else # Procedural shape. 100%,100% rmin,rmax:=max(0.1,$3*(1-$4%)),max(0.1,$3) repeat 10 { 100%,100% random3d {max(1,$2)} *3d. {-2,w},{-2,h},0 j3d.. .,0,0,0,1,1,0,0 rm. b. {$rmin+($rmax-$rmin)*$>/9}%,0,1 j.. .,0,0,0,0,0.5 rm. } >=. 10% fi => shape # Create elevation map. +b[shape] 1% n. 0,30 => elevation # Warp image. g[elevation] xy a[-2,-1] c => grad +*[grad] {grad,$6*max(w,h)/100} *. [shape] b. $15% +warp[img] .,1,1,1 rm.. => refraction # Compute specular spots. +*[grad] -1 100%,100%,1,1,1 a[-2,-1] c orientation. # 3D normal map. a:=$7*pi/180 ca:=-cos($a) sa:=-sin($a) mix_channels. ({(1-$10)*$ca},{(1-$10)*$sa},1) c. {100-$8}%,100% n. 0,1 *. [shape] => spots # Compute ambiant light (gradient). mix_channels[grad] ($ca,$sa) n[grad] 0,1 *[grad] [shape] # Drop shadow. +shift[shape] {-$11*$ca}%,{-$11*$sa}%,0,0,1 -. [shape] >=. 1 b. $13% n. 0,1 => shadow b[shape] $14% n. 0,1 # Add diffuse shadow around each drop. # Prepare layers for output. =>[img] name($nm) *[shadow] 255 channels[shadow] -1,0 mv[shadow] 1 =>[shadow] "name("$nm" [shadow]),mode(alpha),opacity("{$12*100}")" to_a[refraction] sh[refraction] 100% +b[shape] $15% *[-2,-1] rm. mv[refraction] 2 =>[refraction] "name("$nm" [refraction]),mode(alpha)" channels[spots] -1,0 sh[spots] 0 f. 1 rm. *[spots] 255 =>[spots] "name("$nm" [specular spots]),mode(alpha),opacity("{$9*100}")" rv[shape,grad] a[grad,shape] c *[grad] 255 b[grad] $15% =>[grad] "name("$nm" [gradient]),mode(grainmerge)" rv if !$16 gui_merge_layers => $nm0 fi } } if $1 rm[0] fi fx_drop_water_preview : N:=$!-$1 if $N<=0 gui_warning_preview "At least two layers are required in this mode." return fi if $1 repeat $N { l[{$!-$>-1}] { pass[0] 0 mv. 0 fx_drop_water $* gui_merge_layers } } rm[0] else foreach { fx_drop_water $* gui_merge_layers } fi #@gui Equirectangular to Nadir-Zenith : fx_equirectangular2nadirzenith, fx_equirectangular2nadirzenith(1) #@gui : Mode = choice{"to Nadir / Zenith","to Equirectangular"} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/29/12.") fx_equirectangular2nadirzenith : if $1 nadirzenith2equirectangular else equirectangular2nadirzenith fi #@gui Euclidean - Polar : fx_euclidean2polar, fx_euclidean2polar(1) #@gui : Center (%) = point(50,50,0,1) #@gui : Stretch Factor = float(1,0.1,10) #@gui : Boundary = choice(1,"Transparent","Nearest","Periodic","Mirror") #@gui : Inverse Transform = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_euclidean2polar : if !$4 to_a fi if $5 polar2euclidean $1%,$2%,$3,$4 else euclidean2polar $1%,$2%,$3,$4 fi #@gui Fish-Eye : fisheye, fisheye(1) #@gui : Center (%) = point(50,50,0,1,255) #@gui : Radius = float(70,0,100) #@gui : Amplitude = float(1,0,2) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") #@gui Flower : fx_flower, fx_flower_preview(1) #@gui : Center (%) = point(50,50,0,1) #@gui : Amplitude / Angle = point(75,50,0,1) #@gui : Petals = int(6,2,20) #@gui : Offset (%) = float(0,0,100) #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_flower : if !$7 to_a fi amplitude,angle:=dx=$3-$1;dy=$4-$2;[norm(dx,dy),-atan2(dy,dx)*180/pi] flower $amplitude,$5,$6%,$angle,$1%,$2%,$7 fx_flower_preview : fx_flower $* line $1%,$2%,$3%,$4%,1,0xF0F0F0F0,0 line $1%,$2%,$3%,$4%,1,0x0F0F0F0F,255 #@gui Kaleidoscope [Blended] : fx_rotoidoscope, fx_rotoidoscope(1) #@gui : Center (%) = point(50,50) #@gui : Angular Tiles = int(10,1,72) #@gui : Smoothness = float(0.5,0,5) #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_rotoidoscope : if !$5 to_a fi rotoidoscope $1%,$2%,$3,$4%,$5 #@gui Kaleidoscope [Polar] : fx_kaleidoscope, fx_kaleidoscope(1) #@gui : Center (%) = point(50,50) #@gui : X-Offset (%) = float(0,0,100) #@gui : Y-Offset (%) = float(0,0,100) #@gui : Radius Cut = float(100,0,100) #@gui : Angle Cut = float(10,0,100) #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_kaleidoscope : if !$7 to_a fi shift $3%,$4%,0,0,2 kaleidoscope $1%,$2%,$5,$6,$7 #@gui Kaleidoscope [Symmetry] : fx_symmetrizoscope, fx_symmetrizoscope(1) #@gui : Iterations = int(4,1,32) #@gui : Angle = float(0,0,360) #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : Symmetry Sides = choice("Backward","Forward","Swap") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/07/01.") fx_symmetrizoscope : if !$3 to_a fi repeat $1 { ang:=$2+180*$>/max(1,$1-1) symmetrize 50%,50%,$ang,$3,0,{$4!=2?$4:$>%2} } #@gui Morph [Interactive] : fx_morph_interactive, fx_morph_interactive_preview : + #@gui : Number of Frames = int(16,3,1024) #@gui : Preview Precision = choice{2,"Coarsest (faster)","Coarse","Normal","Fine","Finest (slower)"} #@gui : Keypoints = value(-1) #@gui : sep = separator() #@gui : note = note{"Instructions:"} #@gui : note = note{" #@gui : Use mouse buttons to add/move/remove correspondence keypoints over the interactive window that will appear, #@gui : in order to create the morphing.\n\n #@gui : Source/target window:\n\n #@gui : - Left mouse button: Add new keypoint on current image and move it on the other one.\n #@gui : - Right mouse button: Add/move keypoint on current image.\n #@gui : - Key DELETE or middle mouse button: Delete keypoint.\n #@gui : - Key SPACE or mouse wheel: Toggle source/target.\n\n #@gui : In-between window:\n\n #@gui : - Mouse wheel: Change morphing time, from 0 to 1.\n #@gui : - Left mouse button: Reset morphing time to 0.5.\n\n #@gui : Both windows:\n\n #@gui : - Key TAB: Change keypoint radius.\n #@gui : - Key ENTER: Play/stop in-between animation.\n #@gui : - Key R: Reset keypoints.\n #@gui : - Key K: Show/hide keypoints.\n #@gui : - Keys ESC or Q: Process fullres and exit. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2019/04/16.") fx_morph_interactive : if [$3][0]!=-1 __x_morph_keypoints=$3 fi rv x_morph $1,$2 repeat $! { gui_set_layer_name[$>] "Morphing ""#"$> gui_set_layer_pos[$>] 0,0 } rv u "{$1}{$2}{"$__x_morph_keypoints"}" fx_morph_interactive_preview : if $!<2 gui_warning_preview "This filter requires at least two input layers!" return fi rs ${-max_wh},3 + n 0,255 if [$3][0]!=-1 gui_warning_preview "No preview available\n\nKeypoints from previous\nrun have been saved" else gui_warning_preview "No preview available" fi #@gui Perspective : fx_warp_perspective, fx_warp_perspective(1) #@gui : X-Angle = float(1.73,-4,4) #@gui : Y-Angle = float(0,-4,4) #@gui : Zoom = float(1,0.1,4) #@gui : Center (%) = point(50,50,0,1,255) #@gui : X-Offset = float(0,0,100) #@gui : Y-Offset = float(0,0,100) #@gui : Boundary = choice(2,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_warp_perspective : if !$8 to_a fi shift $6%,$7%,0,0,2 warp_perspective $1,$2,$3,$4,$5,$8 #@gui Poincaré Disk : fx_poincare_disk, fx_poincare_disk(1) #@gui : P-Value = int(5,3,16) #@gui : Q-Value = int(6,3,32) #@gui : Size (%) = float(100,0,200) #@gui : Iterations = int(20,0,40) #@gui : Angle (deg.) = float(0,-180,180) #@gui : Tiling = choice(1,"Triangular","Polygonal") #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : Filling = choice(1,"Binary","Image") #@gui : Shift = point(50,50,0,0,255,128,0,200,1%) #@gui : Zoom = float(0,-10,10) #@gui : Angle (deg.) = float(0,-180,180) #@gui : Outline (px) = int(2,0,8) #@gui : Outline Color = color(0,0,0) #@gui : First Color = color(0,0,0)_0 #@gui : Second Color = color(255,255,255)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2024/01/13.") fx_poincare_disk : p,q,size,nb_iter,angle,tiling,antialiasing,filling,shiftx,shifty,zoom,iangle,\ outline,Ro,Go,Bo,R0,G0,B0,R1,G1,B1=${1-22} foreach { to_a if $antialiasing r 200%,200%,1,100%,3 fi poincare_disk {max(w,h)},$p,$q,$angle,$tiling,$nb_iter,{e=max(1,$size)%;[-1/e,-1/e,1/e,1/e]} s. c,-2 if $filling # Image filling eval[1] ">begin(m = inf; M = -inf); i0!=-2?(m = min(m,i0,i1); M = max(M,i0,i1)); end(set('m',m); set('M',M))" f[1] " begin( const m = $m; const dM = $M - m; const mwh = min(w#0,h#0); const shiftx = ($shiftx - 50)*mwh%; const shifty = ($shifty - 50)*mwh%; const zoom = 1.25^-$zoom; R = rot(-$iangle°); ); i0==-2?[-1,-1]: U = R*[ i0,i1 ]; [ w#0 - mwh - shiftx + (U[0]*zoom - m)*mwh/dM, h#0 - mwh - shifty + (U[1]*zoom - m)*mwh/dM ]" warp[0] [1],0,1,3 if $outline +f. "const boundary = 1; v = i; v==j(1) && v==j(0,1)" erode. {$outline*($antialiasing?2:1)} sh[0] 0,{0,s-2} f. "i(#-2)?I:[ $Ro,$Go,$Bo ]" rm[-2,-1] fi else # Binary filling rm[0] if $tiling +%. 2 else +%. 3 ==. 2 fi ($R0,$R1^$G0,$G1^$B0,$B1) map.. . rm. to_a. mv. 0 fi # Manage alpha-channel !=. -2 sh[0] 100% *. .. k[0] if $antialiasing r 50%,50%,1,100%,2 fi } v_image,v_binary:="v = $filling?2:0; [ v,2-v ]" u "{"$p"}{"$q"}{"$size"}{"$nb_iter"}{"$angle"}{"$tiling"}{"$antialiasing"}{"$filling"}"\ "{"$shiftx,$shifty"}_"$v_image"{"$zoom"}_"$v_image"{"$iangle"}_"$v_image\ "{"$outline"}_"$v_image"{"$Ro,$Go,$Bo"}_"$v_image\ "{"$R0,$G0,$B0"}_"$v_binary"{"$R1,$G1,$B1"}_"$v_binary #@gui Polar Transform : fx_transform_polar, fx_transform_polar(1) #@gui : Preset = choice("Custom Transform","Inverse Radius","Swap Radius / Angle") #@gui : Center (%) = point(50,50,0,1) #@gui : Radius = text{"r + R/10*cos(a*5)"} #@gui : Angle = text{"a"} #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_transform_polar : if !$6 to_a fi if !$1 transform_polar "$4","$5",$2%,$3%,$6 elif $1==1 transform_polar R-r,a,$2%,$3%,$6 else transform_polar a*R/(2*pi),r*2*pi/R,$2%,$3%,$6 fi #@gui Quadrangle : fx_quadrangle, fx_quadrangle_preview(1) #@gui : Top-Left Vertex (%) = point(5,5,0,1,255,0,0) #@gui : Top-Right Vertex (%) = point(95,25,0,1,0,255,0) #@gui : Bottom-Right Vertex (%) = point(60,95,0,1,64,128,255) #@gui : Bottom-Left Vertex (%) = point(40,95,0,1,255,255,0) #@gui : Interpolation = choice(1,"Nearest Neighbor","Linear") #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : Preview Type = choice(1,"Input","Output","Both") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/10/11.") fx_quadrangle : at_quadrangle $1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,${9-10} fx_quadrangle_preview : foreach { if !$10 to_a fi if $11 +fx_quadrangle $* rs. {0,[w,h]},3,1 fi polygon[{$11==1}] 4,$1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,0.25,255 if $11>=2 circle[0] $1%,$2%,4,1,0 circle[0] $1%,$2%,3,1,255,0,0 circle[0] $3%,$4%,4,1,0 circle[0] $3%,$4%,3,1,0,255,0 circle[0] $5%,$6%,4,1,0 circle[0] $5%,$6%,3,1,64,128,255 circle[0] $7%,$8%,4,1,0 circle[0] $7%,$8%,3,1,255,255,0 elif $11>0 rm[0] fi if $!==2 drgba to[0] Quadrangle to[1] Result frame xy,1,0 +a x a[0,1] y rs ${-gui_preview_wh},3 k[{max(w#0,h#0)<=max(w#1,h#1)}] fi } #@gui Raindrops : raindrops, raindrops(0) #@gui : Amplitude = float(80,0,300) #@gui : Density = float(0.1,0,1) #@gui : Wavelength = float(1,0,2) #@gui : Merging Steps = int(0,0,20) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/28/11.") #@gui Random : deform, deform(0) #@gui : Amplitude = float(10,0,100) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") #@gui Ripple : ripple, ripple(0) #@gui : Amplitude = float(10,0,100) #@gui : Bandwidth = float(20,1,300) #@gui : Shape = choice(2,"Block","Triangle","Sine","Sine+","Random") #@gui : Angle = float(0,0,360) #@gui : Offset = float(0,0,500) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/23/08.") #@gui Reflection : fx_reflect, fx_reflect(1) #@gui : Height = float(50,0,100) #@gui : Attenuation = float(1,0.1,4) #@gui : Color = color(110,160,190,64) #@gui : Waves Amplitude = float(0,0,100) #@gui : Waves Smoothness = float(1.5,0,4) #@gui : X-Angle = float(0,-10,10) #@gui : Y-Angle = float(-3.30,-10,10) #@gui : Focale = float(7,0,10) #@gui : Zoom = float(1.5,1,5) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_reflect : repeat $! { to_rgba. +rows. {100-$1}%,100% mirror. y water. $7,$8 s. c f[-4] "(i*(255-$6) + $6*$3)/255" f... "(i*(255-$6) + $6*$4)/255" f.. "(i*(255-$6) + $6*$5)/255" a[-4--1] c *. '(h^$2-y^$2)/h^$2' a[-2,-1] y 100%,100%,100%,1,$11*$12*(x/w-0.5) 100%,100%,100%,1,$11*$12*(y/h-0.5) 100%,100%,100%,1,"$10*(x/w-0.5) + $9*(y/h-0.5) + $11" /... . +... 0.5 *... {-3,w} /[-2,-1] +. 0.5 *. {h} a[-2,-1] c warp.. .,0,1,0 rm. mv. 0 } autocrop 0,0,0,0 #@gui Seamcarve : fx_seamcarve, fx_seamcarve_preview(1) #@gui : Width (%) = float(85,0,200) #@gui : Height (%) = float(100,0,200) #@gui : Maximal Seams per Iteration (%) = float(15,0,100) #@gui : Use Top Layer as a Priority Mask = bool(0) #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : note = note{"Note: #@gui : You can define a transparent top layer that will help the seam-carving algorithm to preserve or force #@gui : removing image structures:\n #@gui : \n - Draw areas in red to force removing them. #@gui : \n - Draw areas in green to preserve them. #@gui : \n - Don't forget also to set the Input layers... parameter to input both layers to the filter. #@gui : "} #@gui : sep = separator() #@gui : note = note("Authors: Garagecoder and David Tschumperlé. #@gui :       Latest Update: 2014/02/06.") fx_seamcarve : if $4 if $!<2 error "Priority mask (top layer) is missing!" fi _fx_seamcarve fi seamcarve $1%,$2%,$4,$5,$3% if $4 repeat $! { channels[$>] 0,{$>,s-2} } fi c 0,255 fx_seamcarve_preview : if $4 if $!<2 to_rgb to "Priority mask (top layer) is missing!",5,5,18,2 return fi _fx_seamcarve fi foreach { w,h={w},{h} seamcarve $1%,$2%,$4,$5,{max($3,10)}% if $4 channels 0,{s-2} fi to_rgba r $w,$h,1,100%,0,0,0.5,0.5 } c 0,255 _fx_seamcarve : mv[0] $! l. { s c k[0,1] >[1] [0] !=[0] 0 -[0] [1] *[0] -1 + * 256 } repeat $!-1 { a[$>] .,c } rm. #@gui Sphere : fx_map_sphere, fx_map_sphere_preview(1) #@gui : Width = _int(512,1,4096) #@gui : Height = _int(512,1,4096) #@gui : Radius = float(90,0,400) #@gui : Dilation = float(0.5,0,1) #@gui : Angle = float(0,-50,50) #@gui : Border Smoothness = float(0,0,200) #@gui : Border Width = float(20,0,100) #@gui : Orientation = choice("0 deg.","90 deg.","180 deg.","270 deg.") #@gui : Background = choice("Transparent","Mean Color") #@gui : Fading = float(0,0,100) #@gui : Fading Shape = float(0.5,0,3) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/07/11.") fx_map_sphere : rotate {$8*90} if $6 repeat $! { shift. {round(w/2)},0,0,0,2 +columns. {(1-$7/100)*w/2},{(1+$7/100)*w/2} 100% gaussian. {0.1*w},{h},0 100% 100% a[-3--1] c r. ..,..,1,3 smooth.. .,$6,5,0 rm. j.. .,{(1-$7/100)*{-2,w}/2} rm. shift. -{round(w/2)},0,0,0,2 mv. 0 } fi shift $5%,0,0,0,2 to_rgba if $9 repeat $! { +rows[$>] 0 r. 1,1,1,4,2 RGBA$>={^} r. [$>],[$>],1,4 -[$>,-1] } fi map_sphere $1,$2,$3,$4,$10,$11 if $9 repeat $! { (${RGBA$>}) y. c r. [$>],[$>],1,4 +[$>,-1] } fi fx_map_sphere_preview : fx_map_sphere {w},{h},${3--1} #@gui Spherize : fx_spherize, fx_spherize_preview(1) #@gui : Radius (%) = float(50,0,300) #@gui : Strength = float(1,-10,10) #@gui : Smoothness (%) = float(0,0,4) #@gui : Center (%) = point(50,50,0,1,255,255,255,170,10) #@gui : Ratio = float(0,-2,2) #@gui : Angle = float(0,-90,90) #@gui : Interpolation = choice(2,"Nearest Neighbor","Linear","Cubic") #@gui : Preview Grid = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/10/03.") fx_spherize : ratio:=10^$6 spherize $1%,$2,$3%,$4%,$5%,$ratio,$7,$8 cut 0,255 fx_spherize_preview : cx,cy=${4,5} if $9 grid 5%,5%,50%,50%,0.6,255 fi fx_spherize ${1-3},$cx,$cy,${6--1} #@gui Square to Circle : fx_square_circle, fx_square_circle #@gui : Mode = choice(0,"Square to Circle","Circle to Square") #@gui : Interpolation = choice(1,"Nearest Neighbor","Linear") #@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : X-Factor (%) = float(0,-100,100) #@gui : Y-Factor (%) = float(0,-100,100) #@gui : X-Offset (%) = float(0,-300,300) #@gui : Y-Offset (%) = float(0,-300,300) #@gui : sep = separator() #@gui : note = note("This filter implements the mapping functions described in this page, #@gui : by C. Fong:") #@gui : url = link("http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/10/30.") fx_square_circle : mode,interp,boundary,factx,facty,offx,offy=${1-7} if !$boundary to_a fi base="const interpolation = "$interp"; const boundary = "$boundary"; const offx = "$offx"%; const offy = "$offy"%; const factx = 10^-("$factx"%); const facty = 10^-("$facty"%); const w2 = int(w/2); const h2 = int(h/2);" if !$mode # Square to circle f $base" const tst = 2*sqrt(2); U = (2*x/(w-1) - 1)*factx + offx; V = (2*y/(h-1) - 1)*facty + offy; U2 = U^2; V2 = V^2; U2mV2 = U2 - V2; X = 0.5*(sqrt(max(0,2 + tst*U + U2mV2)) - sqrt(max(0,2 - tst*U + U2mV2))); Y = 0.5*(sqrt(max(0,2 + tst*V - U2mV2)) - sqrt(max(0,2 - tst*V - U2mV2))); (X+=1)*=w2 - 0.5; (Y+=1)*=h2 - 0.5; I(X,Y)" else # Circle to square f $base" X = (2*x/(w-1) - 1)*factx + offx; Y = (2*y/(h-1) - 1)*facty + offy; U = X*sqrt(abs(1 - 0.5*Y^2)); V = Y*sqrt(abs(1 - 0.5*X^2)); (U+=1)*=w2 - 0.5; (V+=1)*=h2 - 0.5; I(U,V)" fi #@gui Square to Circle [alt] : fx_square_circle_alt,fx_square_circle_alt_preview(1) #@gui : Mode = choice("Square to Circle","Circle to Square") #@gui : Center = point(50,50,0,1) #@gui : Strength (%) = float(100,0,100) #@gui : Zoom (%) = float(0,-100,100) #@gui : Angle (deg.) = float(0,-180,180) #@gui : Interpolation = choice(1,"Nearest Neighbor","Linear") #@gui : Boundary = choice("Transparent","Nearest","Periodic","Mirror") #@gui : Adapt to Image Ratio = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/12/24.") fx_square_circle_alt : mode,centerx,centery,strength,zoom,angle,interpolation,boundary,ratio=${1-9} if !$boundary to_a fi f "const cx = w*$centerx%; const cy = h*$centery%; const strength = $strength%; const zoom = 10^($zoom%); const angle = $angle°; const interpolation = $interpolation; const boundary = $boundary; const fx = $ratio?w:1; const fy = $ratio?h:1; x = (x - cx)/fx; y = (y - cy)/fy; t = atan2(y,x); r = norm(x,y); phi = (t + pi/4)%(pi/2) - pi/4; nr = ($mode?r*cos(phi):r/cos(phi))/zoom; nr = lerp(r,nr,strength); t+=angle; nx = cx + fx*nr*cos(t); ny = cy + fy*nr*sin(t); i(nx,ny)" fx_square_circle_alt_preview : gui_split_preview "fx_square_circle_alt $*",${-3--1} #@gui Stereographic Projection : fx_project_stereographic, fx_project_stereographic_preview(1) #@gui : Transform = choice("Direct","Inverse") #@gui : Center (%) = point(50,50,0,1,255,255,255,170) #@gui : Radius / Angle = point(50,75,0,1,255,0,255,170) #@gui : Horizon Leveling (deg) = float(0,-10,10) #@gui : Left / Right Blur (%) = float(0,0,20) #@gui : Dilation = float(0,-2,2) #@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axis") #@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror") #@gui : Last Center = value(50,50) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/07/04.") fx_project_stereographic : is_inverse,centerx,centery,radangx,radangy,rechor,lrblur,dilation,mirror,boundary,ocenterx,ocentery=${1-11} if $centerx!=$ocenterx" || "$centery!=$ocentery deltax,deltay:=[$radangx,$radangy]-[$ocenterx,$ocentery] radangx,radangy:=[$centerx,$centery]+[$deltax,$deltay] fi status=\{$is_inverse\}\{$centerx,$centery\}\{$radangx,$radangy\}\{$rechor\}\{$lrblur\}\ \{$dilation\}\{$mirror\}\{$boundary\}\{$centerx,$centery\} # So that preview line does not hide left/right frontier: nradangx,nradangy:=[$centerx,$centery]+rot(-90°)*([$radangx,$radangy]-[$centerx,$centery]) init="const boundary = "$is_inverse?$boundary:2"; const interpolation = 1; const dilation = 2^"$dilation"; const centerx = "$centerx"%*(W-1); const centery = "$centery"%*(H-1); const radangx = "$nradangx"%*(W-1) - centerx; const radangy = "$nradangy"%*(H-1) - centery; const R = sqrt(radangx^2 + radangy^2); const theta0 = atan2(radangy,radangx); const pi2 = 2*pi;" m "_fx_project_stereographic_mirror : if !$""1 mirror y elif $""1==1 mirror xy elif $""1==3 mirror x fi" foreach { if !$boundary to_a fi if $rechor rotate $rechor,1,3 fi if $lrblur 100%,1,1,1,!x||x==w-1 shift {round(w/2)},0,0,0,2 b. x,$lrblur% n. 0,1 +b.. x,$lrblur% r.. .,.,1,1 j... .,0,0,0,0,1,.. k[0] shift {-round(w/2)},0,0,0,2 fi if $is_inverse 100%,50%,1,100%,"* const W = w#0; const H = h#0; "$init" theta = theta0 + x*pi2/w; phi = (y/h - 0.5)*pi; z = R*sin(phi); rho = ((R + z)/(R - z))^(0.5/dilation)*R; X = centerx + rho*cos(theta); Y = centery + rho*sin(theta); I(#0,X,Y)" _fx_project_stereographic_mirror $mirror else _fx_project_stereographic_mirror $mirror {u=0$_is_preview?min(w,h):max(w,h);[u,u,1,s]},"* const W = w; const H = h; "$init" X = x - centerx; Y = y - centery; theta = atan2(Y,X); beta = ((X^2 + Y^2)/R^2)^dilation; z = R*(beta - 1)/(beta + 1); phi = asin(z/R); theta = ((theta - theta0)*w#0/pi2)%w#0; phi = (h#0*(phi/pi + 0.5))%h#0; I(#0,theta,phi)" fi k. } um _fx_project_stereographic if 0$_is_preview line $centerx%,$centery%,$radangx%,$radangy%,0.75,0xF0F0F0F0,255,255,255,255 line $centerx%,$centery%,$radangx%,$radangy%,0.75,0x0F0F0F0F,0,0,0,255 fi u $status fx_project_stereographic_preview : _is_preview=1 fx_project_stereographic $"*" #@gui Symmetrize : fx_symmetrize, fx_symmetrize_preview(1) #@gui : Point 1 = point(50,50,0,1,0,255,0,170,10) #@gui : Point 2 = point(50,75,-1,1,255,255,0,170,10) #@gui : Angle = float(0,-180,180) #@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror") #@gui : Type = choice("Symmetry","Antisymmetry") #@gui : Swap Sides = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/06/11.") fx_symmetrize : if !$6 to_a fi angle:=isnan($3)?$5:atan2($4-$2,$3-$1)*180/pi symmetrize $1%,$2%,$angle,${6-8} fx_symmetrize_preview : fx_symmetrize $* rs ${-gui_preview_wh},1 u,v:=angle=isnan($3)?$5*pi/180:atan2($4-$2,$3-$1);[cos(angle),sin(angle)] foreach { x0,y0,x1,y1:=V=[$u,$v];([${1,2},${1,2}]+10000*[V,-V])*([w,h,w,h]-1)% line $x0,$y0,$x1,$y1,1,0x0F0F0F0F,0,0,0,255 line $x0,$y0,$x1,$y1,1,0xF0F0F0F0,255 } #@gui Textured Glass : fx_textured_glass, fx_textured_glass_preview(0) #@gui : X-Amplitude = float(40,0,400) #@gui : Y-Amplitude = float(40,0,400) #@gui : X-Smoothness = float(1,0,5) #@gui : Y-Smoothness = float(1,0,5) #@gui : Edge Attenuation = float(0,0,1) #@gui : Edge Influence = float(2,0,10) #@gui : Noise Scale = int(0,0,16) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/21/11.") fx_textured_glass : foreach { 100%,100%,1,1 if $7 plasma. 1,1,$7 else rand. 0,1 fi g. xy if $5 +gradient_norm... +. 1 b. $6 ^. -$5 *... . *[-2,-1] fi b[-2,-1] x,$3 b[-2,-1] y,$4 *.. {-2,$1/max(abs(im),abs(iM))} *. {$2/max(abs(im),abs(iM))} a[-2,-1] c warp.. .,1,1,1 rm. } fx_textured_glass_preview : gui_split_preview "fx_textured_glass $*",${-3--1} #@gui Twirl : fx_twirl, fx_twirl(1) #@gui : Amplitude = float(1,-5,5) #@gui : Center (%) = point(50,50,0,1) #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_twirl : if !$4 to_a fi twirl $1,$2%,$3%,$4 #@gui Warp [Interactive] : fx_warp_interactive, fx_warp_interactive_preview(1) #@gui : Preview Precision = choice{1,"Coarsest (faster)","Coarse","Normal","Fine","Finest (slower)"} #@gui : sep = separator() #@gui : note = note{"Pre-defined keypoints"} #@gui : Regular Grid = int(2,2,10) #@gui : Contours = int(0,0,32) #@gui : Keypoints = value(-1) #@gui : sep = separator() #@gui : note = note{"Instructions:"} #@gui : note = note{" #@gui : Use mouse to add/move/delete keypoints over the interactive window that will appear, #@gui : in order to create the deformation map.\n\n #@gui : - Left mouse button: Add and move keypoint.\n #@gui : - Right mouse button: Delete keypoint.\n #@gui : - Key SPACE or middle mouse button: Show/hide keypoints.\n #@gui : - Key TAB: Change keypoint radius.\n #@gui : - Key SHIFT: Toggle to original image.\n #@gui : - Key R: Reset keypoints.\n #@gui : - Keys ESC, ENTER or Q: Process fullres and exit. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2019/04/08.") fx_warp_interactive : if [$4][0]!=-1 __x_warp_keypoints=$4 fi x_warp $2,$2,$3,$1 u "{$1}{$2}{$3}{"$__x_warp_keypoints"}" fx_warp_interactive_preview : if [$4][0]!=-1 __x_warp_keypoints=$4 fi foreach { rs $_preview_area_width,$_preview_area_height,3 to_color drgba if narg($__x_warp_keypoints) ($__x_warp_keypoints) r. 1,{w/4},1,4,-1 else # Keypoints located on regular grid. nbp,nbq=$2,$2 1,{$nbp*$nbq},1,4,"const nbp = "$nbp"; const nbq = "$nbq"; p = y%nbp; q = int(y/nbp); x = p*100/(nbp - 1); y = q*100/(nbq - 1); [ x,y,x,y ]" # Keypoints located on contours. nbc=$3 if $nbc>0 +b[0] 0.5 gradient_norm. sqrt. {round([w,h]/4)} gaussian. 20% 1,$nbc,1,4,"> begin(ref(crop(#-1),gauss)); st = stats(#-2); iM = st[1]; xM = st[8]; yM = st[9]; img = vector(#w#-1*h#-1,-iM); draw(#-2,img,xM - w#-1/2,yM - h#-1/2,0,0,w#-1,h#-1,1,1,-1,gauss); nxyM = [ xM,yM ]*100/([w#-2,h#-2]-1); [ nxyM,nxyM ]" rm[-3,-2] a[-2,-1] y fi fi +_x_warp_rbf. {0,round([w,h]/4)} *. 4 r. [0],[0],1,100%,3 +. '[x,y]' warp[0] .,0,1,3 rm. eval. "* begin( col1 = [ 64,200,255 ]; const radius1 = 3; const radius2 = radius1 + 2; fact = ([ w#0,h#0 ] - 1)% ); X = (I)[0,2]*fact; ellipse(#0,X,radius2,radius2,0,1,0); ellipse(#0,X,radius1,radius1,0,1,col1); I" rm. if narg($__x_warp_keypoints) 0 t. "Keypoints from previous\nrun have been saved",0,0,24,1,255 frame. 5,5,0 +dilate_circ. 5 a[-2,-1] c blend alpha fi } #@gui Water : water, water(0) #@gui : Amplitude = float(30,0,300) #@gui : Smoothness = float(1.5,0,4) #@gui : Angle = float(45,0,180) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/07/10.") #@gui Wave : wave, wave(1) #@gui : Amplitude = float(10,0,30) #@gui : Frequency = float(0.4,0,2) #@gui : Center (%) = point(50,50,0,1,255,255,255,170,10) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") #@gui Wind : fx_wind, fx_wind_preview(0) #@gui : Amplitude = int(20,0,500) #@gui : Angle = float(0,0,360) #@gui : Attenuation = float(0.7,0,1) #@gui : Threshold = float(20,0,100) #@gui : Mode = choice(1,"Darker","Brighter") #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/05/26.") fx_wind : if !$5 negate fi ac "wind ${1-4}",$6,$7 if !$5 negate fi fx_wind_preview : gui_split_preview "fx_wind $*",${-3--1} #@gui Zoom : fx_zoom, fx_zoom(1) #@gui : Factor = float(2,0.01,10) #@gui : Center (%) = point(50,50,0,1,255) #@gui : Boundary = choice(0,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_zoom : if !$4" && "$1<1 to_a fi zoom $1,{$2%},{$3%},0,$4 #@gui ____Degradations #----------------------------- #@gui Add Grain : fx_simulate_grain, fx_simulate_grain_preview(0) #@gui : Preset = choice{"Orwo NP20-GDR","Kodak TMAX 400","Kodak TMAX 3200","Kodak TRI-X 1600","Unknown"} #@gui : Blend Mode = choice(1,"Alpha","Grain Merge","Hard Light","Overlay","Soft Light","Grain Only") #@gui : Opacity = float(0.2,0,1) #@gui : Scale = float(100,30,800) #@gui : Sharpness = float(0,0,512) #@gui : Colored Grain = bool() #@gui : sep = separator() #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Grain Alone = bool() #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/02/08.") fx_simulate_grain : __fx_simulate_grain ${arg\ {1+$1},${-_fx_simulate_grain}},${2-11},0,0 _fx_simulate_grain : u orwo_np20,kodak_tmax400,kodak_tmax3200,kodak_trix1600,unknown fx_simulate_grain_preview : gui_split_preview "_fx_simulate_grain_preview $*",$-2 _fx_simulate_grain_preview : __fx_simulate_grain ${arg\ {1+$1},${-_fx_simulate_grain}},${2-13} __fx_simulate_grain : bm0=alpha bm1=grainmerge bm2=hardlight bm3=overlay bm4=softlight bm5=alpha input_cached data_film_presets/grain_$1.cimgz r. $4%,$4%,1,1,6 if $4>100 b. 1 fi sharpen. $5 c. 0,255 repeat $!-1 { l[$>,-1] { split_opacity[0] +syntexturize. {0,[w,h]} if $6 +syntexturize.. {w},{h} +syntexturize... {w},{h} a[-3--1] c fi c. 0,255 adjust_colors. ${7-11} if $13 k[0,-1] rv else blend[0,-1] ${bm$2},{$2<=4?$3:1} fi a[^-1] c } } rm. #@gui Blur [Angular] : fx_blur_angular, fx_blur_angular_preview(1) #@gui : Amplitude (%) = float(2,0,20) #@gui : Center (%) = point(50,50,0,1) #@gui : Sharpness = float(0,0,500) #@gui : Preview Guides = bool(1) #@gui : sep = separator() #@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/16/01.") fx_blur_angular : ac "blur_angular $1%,$2%,$3% sharpen $4",$6,$7 fx_blur_angular_preview : fx_blur_angular $* if $5 line 0,$3%,100%,$3%,0.5,0xF0F0F0F0,255 line 0,$3%,100%,$3%,0.5,0x0F0F0F0F,0 line $2%,0,$2%,100%,0.5,0xF0F0F0F0,255 line $2%,0,$2%,100%,0.5,0x0F0F0F0F,0 fi #@gui Blur [Bloom] : fx_blur_bloom, fx_blur_bloom_preview(0) #@gui : Amplitude = float(1,0,10) #@gui : Ratio = float(2,0,5) #@gui : Iterations = int(5,0,100) #@gui : Operator = choice("Add","Max","Min") #@gui : Kernel = choice(1,"Deriche","Gaussian","Box","Triangle","Quadratic") #@gui : Normalize Scales = bool(0) #@gui : Anisotropy = float(0,0,1) #@gui : Angle = float(0,-180,180) #@gui : note = note("Parameter Angle is only active when Anisotropy>0") #@gui : sep = separator() #@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/03/02.") fx_blur_bloom : op=${"arg0 $4,+,max,min"} if !$7 ac "blur_bloom ${1-3},"$op",${5-6},xy",$9 else wh:=w,h rotate $8,2,1 ac "blur_bloom ${1-3},"$op",${5-6},x blur_bloom {$1*(1-$7)},${2-3},"$op",${5-6},y",$9 rotate {-$8},2,1 r $wh,1,100%,0,0,0.5,0.5 c 0,255 fi fx_blur_bloom_preview : gui_split_preview "fx_blur_bloom $*",${-3--1} #@gui Blur [Depth-of-Field] : fx_blur_dof, fx_blur_dof_preview(1) #@gui : Blur Amplitude = float(3,0,20) #@gui : Blur Precision = int(16,2,64) #@gui : Depth-of-Field Type = choice{"Gaussian","User-Defined (Bottom Layer)"} #@gui : Invert Blur = bool(0) #@gui : sep = separator() #@gui : note = note("Gaussian depth-of-field:") #@gui : Center (%) = point(50,50,0,0,255) #@gui : First Radius = float(30,0,200) #@gui : Second Radius = float(30,0,200) #@gui : Angle = float(0,0,180) #@gui : Sharpness = float(1,0,8) #@gui : Preview Guides = bool(1) #@gui : sep = separator() #@gui : note = note("User-defined depth-of-field:") #@gui : Gamma = float(0,-2,2) #@gui : note = note("You can specify your own depth-of-field image, as a bottom layer image #@gui : whose luminance encodes the depth for each pixel. #@gui : Don't forget to modify the Input layers combo-box to make this layer active for the filter.") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/25/02.") fx_blur_dof : _$0 ${1-10},0,$12 fx_blur_dof_preview : _fx_blur_dof $* _fx_blur_dof : if !$3 # Gaussian DOF. foreach { if $11 drgba fi split_opacity l[0] { rmax:=(w*w+h*h)^0.5 R:=$7*$rmax/100 r:=$8*$rmax/100 t:=$9*pi/180 u:=cos($t) v:=sin($t) l1:=($rmax/(1e-8+$R))^2 l2:=($rmax/(1e-8+$r))^2 a:=$l1*($u)^2+$l2*($v)^2 b:=$u*$v*($l1-$l2) c:=$l1*($v)^2+$l2*($u)^2 100%,100%,1,1,'X=(x-$5*w/100)/max(w,h);Y=(y-$6*h/100)/max(w,h);f=$a*X*X+2*$b*X*Y+$c*Y*Y;exp(-f^$10/2.5)' -[1] 1 *[1] -$1 ms,Ms:=im,iM if $11 # With preview of guides. +isoline3d[1] {0.1*$1} col3d. 255,255,0 +isoline3d[1] {0.5*$1} col3d. 255,128,0 +3d[-2--1] __fx_dof_blur[0,1] $2,$ms,$Ms,$4 [0],[0],1,3 j3d. ..,0,0,0,1,1,0,0 rm.. circle. $5%,$6%,3,1,255,255,255 +compose_channels. + !=. 0 dilate. 3 j[0] ..,0,0,0,0,0.5,.,1 rm[-2,-1] else __fx_dof_blur[0,1] $2,$ms,$Ms,$4 # Without preview. fi } if $11 k[0] fi a c } elif $!>1 # User-defined DOF (as bottom layer). luminance. n. 0,1 ^. {10^$12} repeat $!-1 { +r. {$>,w},{$>,h},1,1,3 l[$>,-1] { split_opacity[0] __fx_dof_blur[0,-1] $2,0,$1,$4 a c } } rm. else drgba to "Depth-of-field (bottom layer) is missing !",2,2,13,2,1,255 fi # Render DOF blur (generic) # [0] = Input image # [1] = continuous DOF field (with same size as [0]). # $1 = nb quantization levels. # $2 = minimal blur level. # $3 = maximal blur level. # $4 = invert blur. __fx_dof_blur : n[1] 0,{$1-1} round[1] [0],[0],1,{0,s+1} s=0 repeat $1 { +==[1] {$4?$<:$>} b. 2% j.. [0],0,0,0,0,-1,.,1 j.. .,0,0,0,100%,-1 rm. ns:=$2+($3-$2)*($>+1)/($1-1) b[0] {sqrt($ns^2-$s^2)}% s=$ns } s. c,{-s+1} /[-2,-1] rm[0,1] #@gui Blur [Gaussian] : fx_gaussian_blur, fx_gaussian_blur_preview(0) #@gui : XY-Amplitude = float(3,0,20) #@gui : X-Amplitude = float(0,0,20) #@gui : Y-Amplitude = float(0,0,20) #@gui : Boundary = choice(1,"Black","Nearest") #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") _fx_gaussian_blur : b $1,$4 if $2>0 repeat $! { l. { s y b $2,$4 a y } mv. 0 } fi if $3>0 repeat $! { l. { s x b $3,$4 a x } mv. 0 } fi fx_gaussian_blur : ac "_fx_gaussian_blur $1,$2,$3,$4",$5,$6 fx_gaussian_blur_preview : gui_split_preview "fx_gaussian_blur $*",${-3--1} #@gui Blur [Glow] : fx_glow, fx_glow_preview(0) #@gui : Amplitude = float(6,0,20) #@gui : sep = separator() #@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_glow : ac "glow $1",$2,$3 fx_glow_preview : gui_split_preview "fx_glow $*",${-3--1} #@gui Blur [Linear] : fx_blur_linear, fx_blur_linear_preview(1) #@gui : Tangent Radius (%) = float(10,0,25) #@gui : Orthogonal Radius (%) = float(0.5,0,25) #@gui : Angle = float(0,0,180) #@gui : Sharpness = float(0,0,500) #@gui : Boundary = choice(1,"Black","Nearest") #@gui : sep = separator() #@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_blur_linear : ac "blur_linear $1%,$2%,$3,$5 sharpen $4",$6,$7 fx_blur_linear_preview : gui_split_preview "fx_blur_linear $*",${-3--1} #@gui Blur [Motion] : fx_blur_motion, fx_blur_motion_preview(0) #@gui : Strength = float(1,0,5) #@gui : sep = separator() #@gui : Control Points = choice("2","3","4","5") #@gui : Previous Control Points = value(-1) #@gui : Point0 = point(50,50,0,0,255,255,255,0,12)_0 #@gui : Point1 = point(50,50,0,0,255,255,255,0,11)_0 #@gui : Point2 = point(50,50,0,0,255,255,255,0,10)_0 #@gui : Point3 = point(50,50,0,0,255,255,255,0,9)_0 #@gui : Point4 = point(50,50,0,0,255,255,255,0,8)_0 #@gui : Centering = bool(1) #@gui : Weight Decay (%) = float(75,0,100) #@gui : Subsampling = int(0,0,20) #@gui : note = note("Set Subsampling to 0 to disable subsampling.") #@gui : sep = separator() #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2024/03/27.") fx_blur_motion : # Generate convolution kernel. _fx_blur_motion_preview_args $* if $nb_ptsm2==0 ($x0,$x1^$y0,$y1^1,0) elif $nb_ptsm2==1 ($x0,$x1,$x2^$y0,$y1,$y2^1,0.5,0) elif $nb_ptsm2==2 ($x0,$x1,$x2,$x3^$y0,$y1,$y2,$y3^1,0.75,0.25,0) else ($x0,$x1,$x2,$x3,$x4^$y0,$y1,$y2,$y3,$y4^1,0.75,0.5,0.25,0) fi size:=max(8,round(100*$strength)) r. {4*$size},1,1,3,5 if $subsampling r. $subsampling,1,1,3,2 fi sh. 2 n. {1-$wdecay%},1 rm. sh. 0,1 *. $strength rm. $size,$size eval.. "i(#-1,i0,i1) = i2" rm.. normalize_sum. if $centering autocrop. 0 fi if !$boundary to_a[^-1] fi convolve_fft[^-1] .,$boundary rm. c 0,255 fx_blur_motion_preview : _fx_blur_motion_preview_args $* palette[] hot z. {100-$wdecay}%,100% store. palette foreach { # Apply effect. fx_blur_motion $* rs ${-gui_preview_wh},1 # Draw spline curve. if $nb_ptsm2==0 ($x0,$x1^$y0,$y1^1,0) elif $nb_ptsm2==1 ($x0,$x1,$x2^$y0,$y1,$y2^1,0.5,0) elif $nb_ptsm2==2 ($x0,$x1,$x2,$x3^$y0,$y1,$y2,$y3^1,0.75,0.25,0) else ($x0,$x1,$x2,$x3,$x4^$y0,$y1,$y2,$y3,$y4^1,0.75,0.5,0.25,0) fi r. {max(w#0,h#0)},1,1,3,5 $palette eval.. ">begin(pP = round([i0*(w#0-1)%,i1*(h#0-1)%])); x>0?( P = round([i0*(w#0-1)%,i1*(h#0-1)%]); col = [ I(#-1,i2*(w#-1-1),0,0,1),255 ]; polygon(#0,4,pP - [1,0],pP + [1,0],P + [1,0],P - [1,0],1,col); polygon(#0,4,pP - [0,1],pP + [0,1],P + [0,1],P - [0,1],1,col); pP = P; ); I" rm[-2,-1] # Draw control points. $palette (0,1) r. {$nb_ptsm2+2},1,1,1,3 n. 0,{-2,w-1} warp.. .,0,1 rm. _$0_circle[0] $x0,$y0,12,{I[-1,2]} _$0_circle[0] $x1,$y1,11,{I[-2,2]} if $nb_ptsm2>0 _$0_circle[0] $x2,$y2,10,{I[-3,2]} fi if $nb_ptsm2>1 _$0_circle[0] $x3,$y3,9,{I[-4,2]} fi if $nb_ptsm2>2 _$0_circle[0] $x4,$y4,8,{I[-5,2]} fi rm. } u \{$strength\}\{$nb_ptsm2\}\{$nb_ptsm2\}\ \{$x0,$y0\}\{$x1,$y1\}\{$x2,$y2\}\{$x3,$y3\}\{$x4,$y4\}\ \{$centering\}\{$wdecay\}\{$subsampling\}\{$boundary\} _fx_blur_motion_preview_args : strength,nb_ptsm2,p_nb_ptsm2,x0,y0,x1,y1,x2,y2,x3,y3,x4,y4,centering,wdecay,subsampling,boundary=${1-17} if $nb_ptsm2!=$p_nb_ptsm2 if $nb_ptsm2==0 x0,y0,x1,y1,x2,y2,x3,y3,x4,y4=30,30,70,70,-1,-1,-1,-1,-1,-1 elif $nb_ptsm2==1 x0,y0,x1,y1,x2,y2,x3,y3,x4,y4=30,30,30,70,70,70,-1,-1,-1,-1 elif $nb_ptsm2==2 x0,y0,x1,y1,x2,y2,x3,y3,x4,y4=30,30,70,30,30,70,70,70,-1,-1 else x0,y0,x1,y1,x2,y2,x3,y3,x4,y4=50,30,50,40,30,50,70,60,30,70 fi fi _fx_blur_motion_preview_circle : opac:="inrange($1,0,100) && inrange($2,0,100)?1:0.5" x,y:=cut([$1,$2],0,100) circle $x%,$y%,$3,$opac,0,0,0,255 circle $x%,$y%,{$3-3},$opac,${4-6},255 #@gui Blur [Multidirectional] : fx_blur_multidirectional, fx_blur_multidirectional_preview(0) #@gui : Number of Orientations = int(5,1,16) #@gui : Reference Angle (deg.) = float(0,0,360) #@gui : Angle Range (deg.) = float(360,0,360) #@gui : sep = separator() #@gui : Smoothness = float(150,0,1024) #@gui : Kernel type = choice(0,"Mono-Directional","Bi-Directional") #@gui : Boundary conditions = choice(1,"Dirichlet","Neumann","Periodic","Mirror") #@gui : Sharpness = float(0,0,1000) #@gui : Blend Mode = choice{2,"Min","Max","Average","Edges-0.5 (beware: memory-consuming!)", #@gui : "Edges-1 (beware: memory-consuming!)","Edges-2 (beware: memory-consuming!)", #@gui : "Median (beware: memory-consuming!)"} #@gui : Boost Contrast = float(2,0,32) #@gui : sep = separator() #@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/09/11.") _fx_blur_multidirectional : nb_orientations,\ angle_ref,\ angle_range,\ smoothness,\ kernel,\ boundary_conditions,\ sharpness,\ blend_mode,\ contrast=${1-9} foreach { # If no median blend, blend step by step in an accumulator to reduce memory usage. if $blend_mode<3 +f {$blend_mode?0:inf} fi repeat $nb_orientations { angle:=$angle_ref+$angle_range*($>/$nb_orientations-0.5) # Kernel definition. $smoothness,1 gaussian. 20%,0.1 if !$kernel f. "x>w/2?i:0" fi rotate. $angle,1 # /. {is} # Blur and blend. +convolve_fft[0] .,$boundary_conditions rm.. n. 0,255 sharpen. $sharpness if $blend_mode<3 ${arg0\ $blend_mode,min,max,+}[1,-1] fi } rm[0] if $blend_mode==6 blend_median elif $blend_mode>2 blend_edges {arg($blend_mode-2,0.5,1,2)} fi n 0,255 ac "normalize_local "$contrast,hsl_l } fx_blur_multidirectional : ac "_fx_blur_multidirectional ${1-9}",$10 fx_blur_multidirectional_preview : gui_split_preview "fx_blur_multidirectional $*",${-3--1} #@gui Blur [Radial] : fx_blur_radial, fx_blur_radial_preview(1) #@gui : Amplitude = float(3,0,20) #@gui : Center (%) = point(50,50,0,1) #@gui : Sharpness = float(0,0,500) #@gui : Preview Guides = bool(1) #@gui : sep = separator() #@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/16/01.") fx_blur_radial : ac "blur_radial $1%,$2%,$3% sharpen $4",$6,$7 fx_blur_radial_preview : fx_blur_radial $* if $5 line 0,$3%,100%,$3%,0.5,0xF0F0F0F0,255 line 0,$3%,100%,$3%,0.5,0x0F0F0F0F,0 line $2%,0,$2%,100%,0.5,0xF0F0F0F0,255 line $2%,0,$2%,100%,0.5,0x0F0F0F0F,0 fi #@gui Chromatic Aberrations : fx_chromatic_aberrations, fx_chromatic_aberrations_preview(0) #@gui : Primary Color = color(255,0,0) #@gui : Deformation Type = choice("Shift","Radial","Angular","Random") #@gui : X-Amplitude = float(2,-32,32) #@gui : Y-Amplitude = float(2,-32,32) #@gui : Smoothness = float(0,0,10) #@gui : Attenuation Near Center (%) = float(50,-100,100) #@gui : Attenuation Decay = float(1,0,8) #@gui : sep = separator() #@gui : Secondary Color = color(0,255,0) #@gui : Deformation Type = choice("Shift","Radial","Angular","Random") #@gui : X-Amplitude = float(0,-32,32) #@gui : Y-Amplitude = float(0,-32,32) #@gui : Smoothness = float(0,0,10) #@gui : Attenuation Near Center (%) = float(0,-100,100) #@gui : Attenuation Decay = float(1,0,8) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/08/10.") fx_chromatic_aberrations : U1:=u=[${1-3}];u/max(1e-8,norm(u)) U2:=u=[${10-12}];u/max(1e-8,norm(u)) foreach { to_color split_opacity l[0] { # Compute image decomposition : [0] = residual, [1] = weights for primary/secondary colors. 100%,100%,1,2,"*begin(U1 = ["$U1"]; U2 = ["$U2"]); V = I(#0); d1 = dot(V,U1); V-=d1*U1; d2 = dot(V,U2); V-=d2*U2; I(#0) = V; [ d1,d2 ]" s. c # Shift weights. _fx_chromatic_aberrations.. ${4-9} _fx_chromatic_aberrations. ${13-18} # Reconstruct color image. a[-2,-1] c +[0] '"*begin(U1 = ["$U1"]; U2 = ["$U2"]); i(#1,x,y,0,0)*U1 + i(#1,x,y,0,1)*U2"' rm. } a c } _fx_chromatic_aberrations : if !$1 # Shift 100%,100%,1,2,[$2,$3] elif $1==1 # Radial 100%,100%,1,2," ang = atan2(y - h/2, x - w/2); U = [ $2*cos(ang), $3*sin(ang) ]" elif $1==2 # Angular 100%,100%,1,2," ang = atan2(x - w/2, -y + h/2); U = [ $2*cos(ang), $3*sin(ang) ]" else # Random 100%,100%,1,2,g s. c n.. 0,$1 n. 0,$2 a[-2,-1] c fi if $4 # Smoothness s. c m,M:=im#-2,iM#-2 b.. $4 n.. $m,$M m,M:=im,iM b. $4 n. $m,$M a[-2,-1] c fi if $5 # Center/Borders attenuation 100%,100% =. 1,50%,50% distance. 1 n. 0,1.4142 c. 0,1 if $5>0 ^. {0.1+$6} n. {1-$5%},1 else negate. 1 ^. $6 n. {1+$5%},1 fi *[-2,-1] fi warp.. .,1,1,1 rm. fx_chromatic_aberrations_preview : gui_split_preview "fx_chromatic_aberrations $*",${-3--1} #@gui CRT Scanlines : fx_crt_scanlines, fx_crt_scanlines_preview(0) #@gui : Upscale Factor = int(4,1,16) #@gui : Neighborhood size = int(16,8,64) #@gui : sep = separator() #@gui : Bloom Shape = choice(1,"Square","Triangular","Sine","Gaussian") #@gui : Bloom Threshold = float(0,-1,1) #@gui : Use Luma For Bloom = bool(1) #@gui : sep = separator() #@gui : Fuzz Shape = choice(1,"Square","Triangular","Sine","Gaussian") #@gui : sep = separator() #@gui : Normalize Image = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : Preview Zoom (%) = float(0,0,100) #@gui : sep = separator() #@gui : note = note("Authors: Romain Hérault and David Tschumperlé.\ #       Latest Update: 2024/01/10.") fx_crt_scanlines : upscale_factor,nsize,bloom_shape,bloom_threshold,bloom_use_luma,fuzz_shape,is_normalize:=${1-7} foreach { nm={n} split_opacity l[0] { # Retrieve image statistics. if $is_normalize to_rgb s c repeat $! { avg$>,std$>:=ia#$>,id#$> } a c fi / 255 {[w,h]*$upscale_factor},1,100% eval.. ": begin( const epsilon = $bloom_threshold; const M = $upscale_factor; const N = $nsize; window_weights(type,spectrum) = ( unref(formula,W); formula = type==0?'1': # Square type==1?'const h2 = h/2; t = (y=1 - epsilon,N,N,1,1,N,N,1,3,1); ):( mask = (P + Wb)>=1 - epsilon; ); mask*=Wf; P*=mask; # Downsize patch back to MxM and draw to resulting image. out = resize(P,N,N,1,3,M,M,1,3,2); draw(#-1,out,x*M,y*M,0,0,M,M,1,3)" k. # Normalize colors. if $is_normalize s c repeat $! { f[$>] ${avg$>}" + (i - ia)/id*"${std$>} } a c else * 255 fi c 0,255 } if $!>1 r. ..,..,1,1 a c fi => $nm } fx_crt_scanlines_preview : if $-1 r. {101-$-1}%,{101-$-1}%,1,100%,0,0,0.5,0.5 fi gui_split_preview "fx_crt_scanlines $*",${-4--2} #@gui CRT Phosphors : fx_crt_phosphors,fx_crt_phosphors_preview(0) #@gui : Phosphor Type = choice("Phosphor-1","Phosphor-2","Phosphor-3") #@gui : Upscale Factor = choice(3,"x1","x2","x3","x4","x5","x6","x7","x8") #@gui : Rendering Precision = choice{"Low (Faster)","High (Slower)"} #@gui : Smoothness (%) = float(0,0,10) #@gui : Neighborhhod Size (px) = int(4,1,16) #@gui : Stride (%) = float(50,0,100) #@gui : sep = separator() #@gui : Adaptive Pattern = bool(1) #@gui : Use Luma for Adaptive Pattern = bool(0) #@gui : Transpose Pattern = bool(0) #@gui : Average Over Pattern = bool(0) #@gui : Normalize Image = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : Preview Zoom (%) = float(0,0,100) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/12/26.") fx_crt_phosphors : type,upscale_factor,is_precise,smoothness,nsize,stride,is_adaptive,use_luma,\ is_transpose,is_average,is_normalize=${1-11} upscale_factor+=1 foreach { nm={n} split_opacity l[0] { # Retrieve image statistics. if $is_normalize to_rgb s c repeat $! { avg$>,std$>:=ia#$>,id#$> } a c fi # Algorithm parameters. M=$nsize # Size of input image neighborhood S:=max(1,int($M*$stride%)) # Stride Q:=$upscale_factor*$M # Size of output image neighborhood # Create CRT pattern. if $type==0 4,12,1,3,[255,60,0] +fc. 0,200,0 +fc. 0,60,255 r[-3--1] 5,15,1,3,0,0 shift.. 0,5,0,0,2 shift. 0,10,0,0,2 a[-3--1] x elif $type==1 15,15,1,3 circle. 5,5,4,-1,255,0,0 circle. 10,5,4,-1,0,255,0 circle. 7,10,4,-1,0,0,255 else (218,65,87;229,255,125;141,247,211;68,36,213) permute. yzcx r. 200%,4,1,3,0,2 f. "i*lerp(0.35,1,y/(h-1))" +mirror. y a[-2,-1] y fi if $is_transpose transpose. fi if $smoothness b. $smoothness%,2 fi # Add dynamics to the CRT pattern. if $is_adaptive l. { w:=w if $w>8 r 300%,300% +l { repeat 5 { 3,3,1,1,1 +dilate.. .,0 rm.. } r $w,$w,1,3,2 b 1,2 n 0,255 a z } l.. { repeat 5 { 3,3,1,1,1 +erode.. .,0 rm.. } rv rm. r $w,$w,1,3,2 b 1,2 n 0,255 a z } a z else r 100%,100%,15,3 fi rgb2hsl f "[ i0, cut(i1*lerp(1,0.5,z/(d-1)),0,1), i2 ]" hsl2rgb if $type==1 shape_circle. {w} *[-2,-1] fi } fi /. 255 # Apply CRT pattern to image. W,H={0,ceil([w,h]*$Q/$M)} $W,$H,1,3 $W,$H,1,1 {0,ceil([w,h]/$S)},1,3,": begin( const M = $M; const M2 = int(M/2); const P = w#1; const Q = $Q; iP = vector(#P^2); iS = expr('x = x - w/2; y = y - h/2; gauss(norm(x,y),0.3*$Q,0)',Q,Q); # Gaussian weights for patch accumulation ); xM = x*$S; yM = y*$S; iM = crop(#0,xM,yM,0,c,M,M,1,1); riMy = resize(iM,M,M,1,1,M,P,1,1,1); riM = resize(riMy,M,P,1,1,P,P,1,1,3); $use_luma?( xl = xM + M2; yl = yM + M2; luma = 0.22248840*i(#0,xl,yl,0,0) + 0.71690369*i(#0,xl,yl,0,1) + 0.06060791*i(#0,xl,yl,0,2); ind = int(luma*d#1/256); ):( icM = iM[M2*(M + 1)]; ind = int(icM*d#1/256); ); p = int((xM%M)*P/M); q = int((yM%M)*P/M); $is_precise?( # High-precision rendering off = 0; repeat (P,n, repeat (P,m, $use_luma?( xl = xM + m*M/P; yl = yM + n*M/P; luma = 0.22248840*i(#0,xl,yl,0,0,1) + 0.71690369*i(#0,xl,yl,0,1,1) + 0.06060791*i(#0,xl,yl,0,2,1); ind = int(luma*d#1/256); ):( ind = int(riM[off]*d#1/256); ); iP[off++] = i(#1,p + m, q + n,ind,c,1,2); ); ) ):( # Low-precision rendering iP = crop(#1,p,q,ind,c,P,P,1,1,2); ); $is_average?(iP*=avg(riM)):(iP*=riM); iQ = resize(iP,P,P,1,1,Q,Q,1,1,2); xQ = int(xM*Q/M); yQ = int(yM*Q/M); draw(#-2,iQ,xQ,yQ,0,c,Q,Q,1,1,-1,iS); !c?draw(#-1,iS,xQ,yQ,0,0,Q,Q,1,1,-1)" rm. max. 1e-6 /[-2,-1] # Renormalize output with input statistics. k. if $is_normalize s c repeat $! { f[$>] ${avg$>}" + (i - ia)/id*"${std$>} } a c fi c 0,255 } if $!>1 r. ..,..,1,1 a c fi => $nm } fx_crt_phosphors_preview : if $-1 r. {101-$-1}%,{101-$-1}%,1,100%,0,0,0.5,0.5 fi gui_split_preview "fx_crt_phosphors ${1-11}",${-4--2} #@gui Dirty : fx_dirty, fx_dirty_preview(0) #@gui : Amplitude = float(30,0,100) #@gui : Monochrome = bool(1) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/24/11.") fx_dirty : ac "_fx_dirty ${1-2}",$3,$4 fx_dirty_preview : gui_split_preview "fx_dirty ${1--2}",${-3--1} _fx_dirty : foreach { dct 100%,100%,1,{$2?1:s} noise. $1,2 ==. 0 point. 0,0,0,1,1 * idct c 0,255 } #@gui Flip & Rotate Blocks : fx_flip_blocks,fx_flip_blocks_preview(0) #@gui : X-Size (px) = int(4,1,128) #@gui : Y-Size (px) = int(4,1,128) #@gui : Flip = choice(3,"None","X-axis","Y-axis","XY-axes") #@gui : Rotate = choice(1,"-90 deg.","0 deg.","90 deg.") #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/01/09.") fx_flip_blocks : foreach { if $5 ac "_fx_flip_blocks ${1-4}",$5 else _fx_flip_blocks ${1-4} fi } _fx_flip_blocks : if ($3%2)" && "$1>1 s x,-$1 mirror x a x fi if ($3>1)" && "$2>1 s y,-$2 mirror y a y fi if $4!=1" && "$1>1" && "$2>1 s y,-$2 N=$! s x,-$1 M:=$!/$N ap "rotate {($4-1)*90}" append_tiles $M,$N fi fx_flip_blocks_preview : gui_split_preview "fx_flip_blocks $*",${-3--1} #@gui Huffman Glitches : fx_huffman_glitches,fx_huffman_glitches_preview(1) #@gui : Noise Level (%) = float(30,0,100) #@gui : Split Mode = choice("None","Horizontal Blocs","Vertical Blocs","Patches") #@gui : Bloc Size (%) = int(25,0,100)_1 #@gui : Patch Overlap (%) = float(0,0,50)_1 #@gui : Color Space = choice("RGB","CMYK","HCY","HSI","HSL","HSV","Jzazbz","Lab","Lch","OKLab","YCbCr","YIQ") #@gui : Quantization = int(0,0,64) #@gui : note = note{"(Set to 0 to turn quantization off)"} #@gui : sep = separator() #@gui : Random Seed = int(0,0,65536) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Beware:\n\n\ # - The preview does not perfectly reflect what the resulting image will be. \ # It just gives an idea on the kind of effects the filter produces with the specified parameters.\n\n\ # - The filter is slow to compute on large images.\ # ") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/04/26.") fx_huffman_glitches : noise_level,split_mode,split_size,patch_overlap,colorspace,quantization,seed=${1-7} foreach { W,H,siz={w},{h},{whds} cspace=${"arg0 "$colorspace",rgb,cmyk,hcy,hsi,hsl,hsv,jzazbz,lab,lch,oklab,ycbcr,yiq"} if $colorspace rgb2$cspace fi s c foreach { im$>,iM$>:=im,iM } n 0,255 a c round if $quantization +colormap $quantization,0,0 index.. .,0,0 store. colormap fi huffman_tree mv. 0 # Decompose image into blocs. bloc_size:=max(1,round($split_size*max(w,h)%)) if $split_mode==1 s. y,{-$bloc_size} elif $split_mode==2 s. x,{-$bloc_size} elif $split_mode==3 img2patches. $bloc_size,$patch_overlap%,2 s. z fi compress_huffman[^0] [0] # Add bit-inversion noise in compressed Huffman codes. eval "srand($seed); repeat($siz*($noise_level/100)^6, k = v(1,l - 1); off = v(4,whds#k - 1); bit = v(0,7); val = i[#k,off]; mask = 1<},${iM$>} } a c if $colorspace ${cspace}2rgb c 0,255 fi } fx_huffman_glitches_preview : gui_split_preview "fx_huffman_glitches $*",${-3--1} u "{$1}{$2}{$3}_"{$2?2:1}"{$4}_"{$2==3?2:1}"{$5}{$6}{$7}{$8}{$9,$10}" #@gui JPEG Artefacts : fx_jpeg_artefacts, fx_jpeg_artefacts_preview(0) #@gui : note = note("This filter simulates the JPEG compression artifacts, #@gui : using DCT quantization on 8x8 blocks.") #@gui : Quality (%) = int(50,1,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2024/02/21.") fx_jpeg_artefacts : # Input all 8x8 DCT and iDCT kernels. # (Generated with: l[] { 8,8,64,1,"y*w+x==z" s z +ap idct a[-64--1] z ap[^-1] dct a[0--2] z => idct,dct }) base642img[] \ "MSBmbG9hdCBsaXR0bGVfZW5kaWFuCjggOCA2NCAxICMyMjIyCnic5Vs9q51FEC60kKtWMYpaCirYKCoI767iR6UoSangtdM/EAM2Fgab2FhaWdgIUXO"\ "qVO8sXEHBVDFia6kgCBaCQSyu+5w8c3nv5p55Bg+3uSmGs19nP2Z2Zt752P39/bJ/G8PHF5+qD1y4v77+yNWyeulegPWy9bbW+9pJ7z9XH6s/f3KxdL"\ "BeRr2hjDb0nfR+4OZyxwlw8UbHS4f1nehtBTg66f3kAyOU261OfDTiA/ehEV91ga8T208+cDlg5JNKPiknvR8ykfKwLuRlo7y0k96PegS8MxsB80RAn"\ "ROBRYC9R0AZtxF45o1w+cXz9fHvztTnP7y7nv7z5dLBern1NvTht37/75P17Qt/lOsPPQqwXrbeth6D/6Hv3F2fl2/++WXqMPfyegzmwZz439e9755f"\ "HwbMvTxjHqwFQBlt6MMYjMV/8F/MgbkwJ+bGGlgLa2Jt7AF7wRjMg/+hD3vG3nmGxr0Y5/S+euPGe/XVJ/ZKB+vlBkAZbeh77ctX6re/f1E6WC83AMp"\ "oQ9+Zd++oV6+8WTpYLzcAymhD30+fPlee/e2zqcPcywZAGW3oO6J+aPw437jeuJ9xv+N5xvMCD/d1vABPL3QcdVjjpbcV4rb+2PGK8jsdxx3QjrZCvJ"\ "evOl2A3/c7jTqs8dvbJtKkgK7A/eU+rsMEGoDOpJehjDb0YQzG4j+kpWEuzIm5sQbWwpqkc8NesCfsDXvEXrFn3oGGs+BMOBvOiLPizPgP+fzgW3Gsu"\ "87wb4mxTj53fixjfeDxo+qb2suC/2+Z1+ub9uX1TefyOundSG/HlxFfjfRupHclvo34dno30tvpNZNeRnob6V1I75n0nknvQnr7fZl4XybSu5Lexvs2"\ "8b45vSvp7fe18L4W0ruS3n7fC+97JV808oWRLyr5ppCvGvnKyFeVfFfIl418aeTLSr4t5GsjX8/ka+f76Yj6ofHjfON6437G/Y7nGc/rMtZlJfVBoz5"\ "weXrAS9QHjfrAKI+N8tj1gVEfuDyfKc+N+sCoD2bqg4n6oPia1AeuTwr1SaU+KNQHE/VRoT5yfVBd9lCfVeoz1wfVdZPbSJvAv6ECsAjc5vq/oOZX+1"\ "Pncx3nusp1zkJ3lL1+p37gXcad7GXb490CoIw212MYi//4XcNcmNPvNdbCmn4vo341v9qfOt8HH71V/7rzSulgvYx6Qxlt3vfg36cBxnZDGW3+v6F+a"\ "Pw4n1pv7Ffzq/2p9YCb6x0nwMXZjpcOa7z3trUcAW6f6TgFLv07BDjvbRNwDEAZbf5dgrH4zx5lEebCnJgba2AtrOlyK+pX86v9qfO5LeQ20lgfeHzk"\ "+RK0L30ut8ybrav51f7UOsRHIz6cHkZ6NOLTiM9Cesykh5EehfQw0mMiPQrpWUnPRnoU0qOqfjW/2p86H/nA5YDzbCWfFPKR85mRz5zvyxH1Q+PH+dR"\ "6Y7+aX+1PrUd52yhvG+Vlo7w0ylujvDXKW6O8nSlvC+VtobwulNcT5W2lvHV5Xynvi+pX86v9qfMp+x6+ogiUfU0fVAQlAuWfgF8rAuUfUPb9Kdrnl/"\ "p32E7/VuswXaJ93vvKKrCvV7Qhdm/aCQf2H77bd29+m69tMpTR5vYfxuI/+O8q8E9g7VP0L2BP2Bv2eIn+hVP85o/8A9QxZW9h37ucQd+1fkee7veow"\ "9TLBYAy2q5Rr0T29dmF/U55d2C/uz4a6ofGK//EuJ9xv+N5xvMu7XvgfHew74Ez4BV4Bo6BW+B1h/a529erhX29WtjXmBPf9+g732ncYU1XfN9jLQDK"\ "aEMfxmAs/rNL2w5zrRb+idXCP4E9YC/YE/aGPWKvO/QvLP0DXO+Qf0DZ9+Rz5ycb68q+Hnh85HkL2m3B/xvt+E378rryD+wG9j1wRXqXnYV9zvuAvsl"\ "5dLSvSa9KelfS20jvifQupHcjvQvvy7z87+oI/4TLFtLbdhb+Bd4H3Nd5N/APYL29hb3s37P+nYA+8lUhX03kKyPfzcq+9m+Qswv7fcH3dkT90Hjlnx"\ "j3M+53PM94XmXfU55OlKeF+qBQH0xL+xo0WQ32NeV5oTx3fVDcN+Q+HuqDRn1g1Afz0j+BuVeDf4L6aKY+MuoDoz6YlX8gESO3LWHbGP2xrp+InzfGS"\ "w58kxjLeEpN9G8bvz/W9RPx823j7dvG7491/UT8fB07Zbx0rZsYT/VvZ9W/bfz+WNdPxMu3jbNvG68/1vUT8XMjvmxBr0J8lkT/tvH7Y10/ET/fNt6+"\ "bfz+WNdPxM+N8tS/Pz3uZQt5GvVvG78/1vUT9nccP9f2t8ohCO3zhP8h9A8o/0PC/g7t+4T9HfoXlH2e8D+E/gHlf0jY36F9n7C/Q/+Css8T/ofQP6D"\ "8Dwn7O7TvE/Z36F9Q9nnC/xD6B5T/IWF/h/Z9wv4O/QvKPk/4H0L/gPI/JOzv0L5P2N+hf0HZ5wn/Q+gfUP6HhP0d2vcJ+zv0Lyj7POF/CP0Dyv+QsL"\ "9D+z5hf4f+BWWfJ/wPoX9A+R8S8XMVo1f5Ayp+r/IHwvnV/tT5EvFzFb9X+QMqfq/yB8L5E/kD4fkS8XMVv1f5Ayp+r/IHVL6Ayh8I10vEz1X8XuUPq"\ "Pi9yh8I50/kD4TnS8TPVbxe5Q+o+L3KH1B5Aip/IFwnET9X8XuVP6Di9yp/IJw/kT8Qni8RP1fxe5U/oOL3Kn9A5Quo/IFwvUT8XMXvVf6Ait+r/IFw"\ "/kT+QHg+lZ+fyL8P7XvlP1D2u/IPJN4fhO8LVH5+Iv8+tO9V/n3i/UHoH0i8PwjfF6j8/ET+ffg+QPkPEu8Pwvz+xPuD8H2Bys9P5N+H7wNU/n3i/UG"\ "Y3594fxC+L1D5+Yn8+9C+V/4DZb8r/0Di/UH4vkDl5yfy78P3ASr/PvH+IMzvT7w/CN8XqPz8RP59+D5A+Q8S7w/C/P7E+4PwfYHKz0/k34f2vcq/T7"\ "w/CP0DifcH4fuC/wBiGjbi" base642img[] \ "MSBmbG9hdCBsaXR0bGVfZW5kaWFuCjggOCA2NCAxICMyOTE3CnicjVsxjybFEV0JB4iDyHdYhhAJIznBAiSS6ZbBkb3YS4glHOI/4ENyQuDTOsAJIRG"\ "BEyP7di+6cKo5yZY4J95DmxKChIREYMln5ODo1/OqXd9KU1XZzX37Tc+rqqlX9aq+o6Ojevr+T+rvlh/VH9x6uh716189d79c/un9cue1pwo+O//pu/"\ "Xhw9/WF/5+Mv62vnet/vzHn5Qb37xe8D189vs//Lr+4i8/G/d5853H6r+/d7d89sGrBffE9/DZP/734jjjN7e+Lp989efy4Jnny6NHj4o9H9e/NOfj3"\ "zgP98T3cP3u4x+V+3ffKufffr7g7/AsOA/3xDU+++F/bpRXvvxwwT3wnHgWnIdrfA+fPfnFswvw4lx87/y1p6Rft/48rT9Pe+O5+4Jnw/PjjO9/8zqu"\ "W7dH65hbee8a/m5gw/kPPnhV8D181u3RTt55rOHewI1n+9czz0u/bv1+rX+nvX3rawFePf+sn9+vxZ4PvMANTH/79vO1X0u3R+v2aDcf/0jwPdwbeF/"\ "68sO1X0t/VunPI5/efUuAF7iB99oXz679Wro9pNtDbvf7Af8bm73HucDf7VDUH8BfNn8LcONvuh2KxgP+7mTztwA3nrvboWg84B5vb/4W4Ab+boei8Q"\ "D89nzgPzPnA//Nzd8DN/B3OywaD8D/6ebvgRv36XZYNB6A//bm7xW4gb/bYdF4AP4723mtx72YeBjPA/w3NrwN55t4GPbAc3624R0+MfEw7IF7Pdjwt"\ "v68YuJh2AP49XxgM/Ewzgf+8w1v63Fv42HYA/hf2fAK/s/Ew7AH7v3khlf6fVYTD8MexC/EX4i/8fxK/EL8lfgb8VfiF+KvxN+IvxK/EH8h/kb8lfiF"\ "+Avxz/OJfyX+QvxC/JX4V+IvxC/EX4h/Jf6F+IX4C+Nf9DzGvzD+GuN/4mX8C+O/Mf4nXsa/MP4b418UL+NfGP+N8T/PZ/zP8xn/Ey/jf2X8C+N/4mX"\ "8r4x/YfyvipfxvzL+xfgb+a8w/1Xmn2L8jfxXmP8q85/1N/JfYf6rzH/V+Bv5rzD/VeY/62/kv8L8N883/kb+W5j/KvOf9Tfy38L8V5j/ivE38t/C/F"\ "eY/0b+Z74ddiD/CflHyH9N/U3+a+Q/If819Tf5r5H/hPzX1N/kPyH/Cflvnk/+m+eT/5r6m/wn5L+V/Cfqb/KfkP9W8p+ov8l/K/lvPQr4X/n7+Ap/3"\ "yN/R/yLd+k6+eain4/rv/bzX+7PhvMt3xG/EP/I1X/sn93p9v9vtzHsiGvaX2j/ybe0f6P9x5m4F95fvKO0n8B+/+w2OiP/efyv+Rp4Lwx/H5O/I/7F"\ "PfE94H2ixx/eseusRz7u8Qe8mu8Y/7MeQvwDr+ZbfK9f126P2u1R+f41zbd8/2Y9hvcPeIEbeG9v70/B9/AZ3p+I/5W/7xn+7niKxkPEv8D/8ebvBc+"\ "Pez/BfHyxxb8w/xXmv8L8N84CfubfAtzAj3jSeMDfMf8W5t/K/DvsAvw3N38P3MCPfKjxEPG/8vfxFf5We0T8C/zkn9LjfjHxMOwB/OS/Sv7TeBj2AP"\ "4bG95Rd5t4GPbAc5N/K/lX42HYA/jPNryV/KnxMOwR8b/y9/EV/ib+kH+JfyH+lfgL8QvxF+IX4q/E34i/EH8j/kr8jfgL8Tfir8TfiH8hfiH+Qvwt4"\ "n/l73uGvy82eww7RPzL+F8UL+N/YfwXxn9RvIz/wvivjP+Jl/FfGP+V8T/xMv4L478y/idexv/C+C8R/xt/I/9N/mb+C/nX+Bv5b2X+E+a/1fhb2P9M"\ "PoRfjL8HjzD/NeY/629h/zX5GBiNv0e/w/zXmP8k4n/l7+Mr/E3+k4h/yX9F/U3+W8h/i6132P8K+9/xXpD/qvqb/FfJfwf1Fvvvxv572Jj8V9Xf5L9"\ "C/lsi/rd8YftvxNPZ1n9PviN+If5RY0f8HfG/5Uvar9B+o3/P6g979UvE/9pvAS/jZ9YT7L9F8x3jf9ZDiP+IvyP+B17Nl3x/Zj2F9yerP+zVLxH/a/"\ "+NeGH+EOYP7b+F+a8w/xXmv2GXiL8j/sezMH+uzJ/C/DnsktUf9uqXiP+Bn/xx0H+rPfB35L9C/tN4GPaI+Dvi/3PTv5M/NR6GPbL6w179EvE/8a8nV"\ "/pv4q/EvxC/vg+F+CXi74j/z03/zvqpaD19nfpjRn/Yq18i/tf+W/Ey/teT//ffwvq3sP4trH+HHSL+jvif8b8qXsb/yviXrP6wV79E/G/8Xdg/TD5h"\ "/63+FvY/kw+RFyL+jvjf+Hth/zT5FHkhqz/s1S8R/9t6wfbf5D/t94r6m/xXyH9LxN8R/9t6if1zYf88+ves/rBXv0T8H+nnmf5b9WrbfyNXnG/1n6v"\ "/R/ODqP6wej31m4P6I+L/SD/P9t94JupXc56B9yfS/6P5QVR/nJr6g/rdnOdo/+fxf6SfZ/tvvBPULwv1yxEPkf4fzQ+i+uPU1B/Ubyv122b4b5f/I/"\ "08039Tvz7ov9Uekf4fzQ+i+uOU9cdD1h8mHoY9Iv6P9PNM/835xUH/Tfw6/9jV/6P5QVR/EH8h/nZq6g/khYj/I/08238rXsb/wvgvkf4fzQ+i+uPU1"\ "B+c31XO75rWPx7/R/p5tv9m/lutno28EOn/0fwgqj+Mv4Xz26nnq/7j8X+kn2f6b51X2P6b/LdE+n80P4jqDzuv4fz+oP5I8P/kO+IX4l8Nfo//3fl9"\ "gv8n3xF/I34x+D3+d/cXEvwvmu8Y/7MeMvHv8b87v0/wf9N8x/if9ZCJf4//3f2FBP8L89/C/FeY/8TkP4//3fl9gv8b819h/qvMf83kP4//3f2FBP9"\ "P/Z78p/GwGP7z+N+d3yf4v5H/KvlP46EY/vP4391fSPD/1O9Z/4jWw6b+8fjfnd8n+L+x/qmsf5rWw6b+8fjf3V9I8L+w/l1Y/xbWv2LqX4//3fl9gv"\ "8b69/C+rey/m2m/vX4391fSPC/+ntl/zP50PQ/Hv+78/sE/6u/hf3P5EPT/3j87+4vJPh/1jvsf4X972r6X4//3fl9gv9nvcP+t7H/FdP/evzv7i8k9"\ "H93fp7Q/6feTf3jgH8T+r+7P5DQ/6febeuHe6wfEvq/Oz9P6P+Tf6l/zXmI0b88/d/dH0jo/031zgtTPxyzfkjo/+78PKH/T/6l/tmof1ajf3r6v7s/"\ "kND/G/XPWT9Q/2yqfwb6vzs/T+j/lfp3o/49+dfo357+7+4PJPT/Rv37oH5QeyT0f3d+ntD/K+cfjfOPyb9m/uHp/+7+QEL/b5x/HNQPxB/2/9H8PKH"\ "/T/7l/Ktx/lXN/MvT/939gYT+3zj/mvUD519N51+B/u/OzxP6/+Rfzj+nHm7mn57+7+4PJPR/9bdcmPqB+S+c/0fz84T+P+cdnH8f8G9C/3f3BxL6/5"\ "x32PqB/Bf2/xF/Wr7b2f9z5/eJ/T+3fsjoD97+QmL/z+XPy3j/z53fJ/b/3Pohqz/s7S8k9v9c/ryM9//c+X1i/8+tH7L6w97+QmL/z+XPy3j/z53fJ"\ "/b/3Pohoz94+wuJ/T+XPy/j/T93fp/Y/3Prh4z+4O0vJPb/XP68jPf/3Pl9Yv/PrR+y+sPe/kJi/8/lT+Pvvf0/d36f2P9z64es/rC3v5DY/3P509Y7"\ "O/t/7vw+sf/n1g8Z/cHbX4j4P5qfZ/vvPf0+0v+j/YGo/ojmF4nf/7nz82z/vaffR/p/tD8Q1R/R/CLx+z93fp7tv/f0+0j/j/YHovojml8kfv/nzs+"\ "z/feefh/p/9H+QFR/RPOLxO//3Pl5tv/e0+8j/T/aH4jqj2h+kfj9nzs/z/bfe/p9pP9H+wNR/RHNLxK//3Pn59n+e0+/j/T/aH8gqj+i+UXi93/u/D"\ "zbf+/p95H+H+0PRPVHNL/4DgTd2gQ=" => dct,idct # Input DCT quantization matrix. (16,11,10,16,24,40,51,61;12,12,14,19,26,58,60,55;14,13,16,24,40,57,69,56;14,17,22,29,51,87,80,62;\ 18,22,37,56,68,109,103,77;24,35,55,64,81,104,113,92;49,64,78,87,103,121,120,101;72,92,95,98,112,100,103,99) f. "const S = $1<50?5000/$1:200-2*$1; max(1,round((S*i+50)/100,1,-1))" => Q # Process images. foreach[0--4] { split_opacity pass[-3--1] l[0,-3--1] { # Decompose image into luma/chroma l[0] { w,h={w},{h} to_rgb r {w+(-w%16)},{h+(-h%16)},1,100%,0,3 rgb2ycbcr s c r[-2,-1] 50%,50%,1,1,2 round. } repeat 3 { # Compute DCT of image with 8x8 blocks. [$>] f[$>] "begin(boundary = 2; ref(vector64(),res)); if (!(x%8) && !(y%8), ref(crop(x,y,8,8),src); repeat (8,l, repeat (8,k, off = k + 8*l; res[off] = sum(src*crop(#"$dct",0,0,off,8,8,1)))); draw(#-1,res,x,y,8,8); ); i" round. # Compute DCT quantization. +r[Q] {$>,w},100%,1,1,0,2 /.. . round.. *[-2,-1] # Compute iDCT of 8x8 blocks. f. "begin(boundary = 2; ref(vector64(),res)); if (!(x%8) && !(y%8), ref(crop(x,y,8,8),src); repeat (8,l, repeat (8,k, off = k + 8*l; res[off] = sum(src*crop(#"$idct",0,0,off,8,8,1)))); draw(#"$>",res,x,y,8,8); ); i" rm. round[$>] } l[^3--1] { r ${-max_wh},1,1,1 a c ycbcr2rgb round. r $w,$h,1,3,0 } } rm[-3--1] a c } rm[dct,idct,Q] fx_jpeg_artefacts_preview : gui_split_preview "fx_jpeg_artefacts $*",${-3--1} #@gui Lomo : fx_lomo, fx_lomo_preview(1) #@gui : Vignette Size = float(20,0,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: Jérome Boulanger and David Tschumperlé. #@gui :       Latest Update: 2012/06/06.") fx_lomo : foreach { split_opacity l[0] { to_rgb +gaussian {100-$1}%,{100-$1}% n. 0,1 * s c f[0] "255*atan((i-128)/128)" f[1] "255*tan((i-128)/128)" f[2] "255*atan((i-128)/255)" a c sharpen 1 normalize 0,255 } a c } fx_lomo_preview : gui_split_preview "fx_lomo $*",${-3--1} #@gui Mess with Bits : fx_mess_with_bits, fx_mess_with_bits_preview #@gui : note = note("Input processing:") #@gui : Pre-Normalize = bool(1) #@gui : Smoothness (%) = float(15,0,100) #@gui : Multiplier = int(1,1,256) #@gui : sep = separator() #@gui : note = note("Output processing:") #@gui : Reversing = choice{1,"None","Reverse bits","Reverse bytes"} #@gui : Bit Masking (Start) = int(0,0,15) #@gui : Bit Masking (End) = int(15,0,15) #@gui : Opacity (%) = float(100,0,100) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2019/01/16.") fx_mess_with_bits : ac "_fx_mess_with_bits ${1-7}",$8 _fx_mess_with_bits : foreach { +l { b {($2/100)^2*100}% if $1 n 0,255 fi * $3 round # -> Here, images are 'ushort'-valued. if $4==1 f "for (k = res = 0, k<15, ++k, res|=((i>>k)&1)<<(15-k))" elif $4==2 f "res = ((i>>8)&255) | ((i&255)<<8)" fi f "begin( mask = (65535>>(15-max($5,$6))) & (65535<Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_noise : ac "_fx_noise $1,$2",$3,$4 _fx_noise : foreach { split_opacity noise[0] $1,$2 a c } fx_noise_preview : gui_split_preview "fx_noise $*",${-3--1} #@gui Noise [Gradient] : fx_noise_gradient,fx_noise_gradient_preview(0) #@gui : Strength = float(100,0,255) #@gui : Smoothness[%] = float(0,0,100) #@gui : Noise Type = choice("Gaussian","Uniform") #@gui : sep = separator() #@gui : Channel(s) = choice(2,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/12/06.") _fx_noise_gradient : foreach { avg=${average_vectors.} g[0] xy,1,2 foreach { +f 0 noise. {$3?$1:$1/2},$3 b. {lerp(0,10,($2%)^2)}% + } g[0] x,-1,2 g[1] y,-1,2 + ilaplacian 0 +fc $avg + c 0,255 } fx_noise_gradient : ac "_fx_noise_gradient $*",$-4,1 fx_noise_gradient_preview : gui_split_preview "fx_noise_gradient $*",${-3--1} #@gui Noise [Perlin] : fx_noise_perlin, fx_noise_perlin_preview(1) #@gui : Random Seed = int(0,0,65536) #@gui : sep = separator() #@gui : note = note("1st scale:") #@gui : Amplitude = float(100,0,512) #@gui : Scale (%) = float(8,0,100) #@gui : X/Y-Ratio = float(0,-4,4) #@gui : sep = separator() #@gui : note = note("2nd scale:") #@gui : Amplitude = float(0,0,512) #@gui : Scale (%) = float(4,0,100) #@gui : X/Y-Ratio = float(0,-4,4) #@gui : sep = separator() #@gui : note = note("3rd scale:") #@gui : Amplitude = float(0,0,512) #@gui : Scale (%) = float(2,0,100) #@gui : X/Y-Ratio = float(0,-4,4) #@gui : sep = separator() #@gui : note = note("4th scale:") #@gui : Amplitude = float(0,0,512) #@gui : Scale (%) = float(1,0,100) #@gui : X/Y-Ratio = float(0,-4,4) #@gui : sep = separator() #@gui : Channel(s) = choice(2,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2019/01/24.") _fx_noise_perlin : seedx,seedy:=[$1>>8,$1&255] foreach { if $2 .,.,1,. noise_perlin. {r=2^$4;s=max(w,h)*$3%;[max(1,s/r),max(1,s*r)]},1,$seedx,$seedy *. $2 + fi if $5 .,.,1,. noise_perlin. {r=2^$7;s=max(w,h)*$6%;[max(1,s/r),max(1,s*r)]},1,$seedx,$seedy *. $5 + fi if $8 .,.,1,. noise_perlin. {r=2^$10;s=max(w,h)*$9%;[max(1,s/r),max(1,s*r)]},1,$seedx,$seedy *. $8 + fi if $11 .,.,1,. noise_perlin. {r=2^$13;s=max(w,h)*$12%;[max(1,s/r),max(1,s*r)]},1,$seedx,$seedy *. $11 + fi } fx_noise_perlin : ac "_fx_noise_perlin $*",$-4,1 fx_noise_perlin_preview : gui_split_preview "fx_noise_perlin $*",${-3--1} #@gui Noise [Spread] : fx_spread, fx_spread_preview(0) #@gui : X-Variations = float(4,0,20) #@gui : Y-Variations = float(4,0,20) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_spread : ac "spread $1,$2",$3,$4 fx_spread_preview : gui_split_preview "fx_spread $*",${-3--1} #@gui Old-Movie Stripes : fx_stripes_y, fx_stripes_y_preview(1) #@gui : Frequency = float(10,0,100) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_stripes_y : ac "stripes_y $1",$2,$3 fx_stripes_y_preview : gui_split_preview "fx_stripes_y $*",${-3--1} #@gui Oldschool 8bits : fx_8bits, fx_8bits_preview(0) #@gui : Scale = float(25,1,100) #@gui : Dithering = float(800,0,10000) #@gui : Levels = int(16,2,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/02/11.") fx_8bits : foreach { split_opacity l[0] { w,h={w},{h} r $1%,$1%,1,100%,2 +luminance sharpen. $2 otsu. 256 blend[-2,-1] shapeaverage0 l. { s c quantize $3,1,1 a c } r. $w,$h,1,100%,1 } a c } fx_8bits_preview : gui_split_preview "fx_8bits $*",${-3--1} #@gui Pixel Sort : fx_pixelsort, fx_pixelsort_preview(1)+ #@gui : note = note{"Sorting parameters:"} #@gui : Order = choice(1,"Decreasing","Increasing") #@gui : Axis = choice("X-axis","Y-axis","X-axis Then Y-axis","Y-axis Then X-axis") #@gui : Sorting Criterion = choice("Red","Green","Blue","Intensity","Luminance","Lightness","Hue", #@gui : "Saturation","Minimum","Maximum","Random") #@gui : sep = separator() #@gui : note = note{"Masking parameters:"} #@gui : Mask By = choice(1,"Bottom Layer","Criterion","Contours","Random") #@gui : Lower Mask Threshold (%) = float(0,0,100) #@gui : Higher Mask Threshold (%) = float(100,0,100) #@gui : Mask Smoothness (%) = float(0,0,5) #@gui : Invert Mask = bool(0) #@gui : Preview Mask = bool(0) #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter implements one version of the algorithm described here : #@gui : "} #@gui : url = link("http://satyarth.me/articles/pixel-sorting/") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/10/29.") fx_pixelsort : _fx_pixelsort ${1-8},0 _fx_pixelsort : foreach[0-{$4?-1:-2}] { if !$3 +to_rgb channels. 0 # Red elif $3==1 +to_rgb channels. 1 # Green elif $3==2 +to_rgb channels. 2 # Blue elif $3==3 +compose_channels + # Intensity elif $3==4 +luminance # Luminance elif $3==5 +to_rgb rgb2hsl. channels. 2 # Lightness elif $3==6 +to_rgb rgb2hsl. channels. 0 # Hue elif $3==7 +to_rgb rgb2hsl. channels. 1 # Saturation elif $3==8 +compose_channels min # Minimum elif $3==9 +compose_channels max # Maximum else 100%,100%,1,1 rand. 0,100 # Random fi if !$4 pass. 0 norm. elif $4==1 . elif $4==2 +gradient_norm[0] else +rand. 0,100 fi b. $7% ir. $5%,{$6+0.01}% if $8 ==. 0 fi if $9 k. * 255 else pixelsort[0] {`$1?_'+':_'-'`},{`!$2?'x':$2==1?'y':$2==2?'xy':'yx'`},[1],[2] k[0] fi } fx_pixelsort_preview : _fx_pixelsort $* #@gui Rain & Snow : fx_rain, fx_rain_preview(0) #@gui : Angle = float(65,-180,180) #@gui : Speed = float(10,0,50) #@gui : Density (%) = float(50,0,100) #@gui : Radius = float(0.1,0,3) #@gui : Gamma = float(1,0,2) #@gui : Opacity = float(1,0,1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2024/02/21.") fx_rain : foreach { nm=${-gui_layer_name} 100%,100% l. { noise 300 c 0,255 b 1,0 c {100-$3}%,100% +>= 40% blend shapeaverage0 blur_linear $2,$4,$1 max n 0,255 apply_gamma $5 } i.. 100%,100%,1,3,255 a[-2,-1] c => "name("$nm"),mode(alpha),opacity("{$6*100}")" rv } fx_rain_preview : gui_split_preview "foreach { fx_rain $* rv blend screen,$6 }",${-3--1} #@gui Random Shade Stripes : fx_shade_stripes, fx_shade_stripes_preview(1) #@gui : Frequency = float(30,1,100) #@gui : Orientation = choice(1,"Horizontal","Vertical") #@gui : Darkness = float(0.8,0,3) #@gui : Lightness = float(1.3,0,3) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_shade_stripes : ac "shade_stripes $1,$2,$3,$4",$5,$6 fx_shade_stripes_preview : gui_split_preview "fx_shade_stripes $*",${-3--1} #@gui Rebuild From Similar Blocks : fx_rebuild_from_similar_blocks, fx_rebuild_from_similar_blocks(1) #@gui : Block Size (%) = float(5,2,50) #@gui : sep = separator() #@gui : Regularization factor = float(10,0,20) #@gui : Luminance factor = float(0.75,0,3) #@gui : Norm type = choice(1,"L1","L2") #@gui : sep = separator() #@gui : note = note{"This filter subdivides the image into blocks, and replace each block by the most similar block #@gui : found in the other blocks."} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/09/17.") fx_rebuild_from_similar_blocks : foreach { split_opacity l[0] { to_rgb w0,h0,S:=w,h,round(min(w,h)*$1%) # S: Block size # Split image into regular blocks. r {ceil([w,h]/$S)*$S},1,100%,0,3,0.5,0.5 M,N:=[w,h]/$S s yx,-$S a z # Original RGB blocks # +mirror x +mirror y a z # + all xy mirrors # repeat 3 { +rotate. 90 } a z # + all 90° rotations +l { b xy,$2% rgb2ycbcr sh. 0 *. $3 rm. } # Blocks to compare, in YCbCr space # For each block, find most similar block. $M,$N,1,1,-1 f. ": i<0?( ind = x + y*w; ref(crop(#1,0,0,ind,w#1,h#1,1),S); kmin = 0; dmin = inf; repeat (wh,k, k!=ind?( d = norm"{1+$4}"(S - crop(#1,0,0,k,w#1,h#1,1)); dAuthor: David Tschumperlé.      Latest Update: 2014/19/11.") fx_scanlines : ac "scanlines ${1-5}",$6,$7 fx_scanlines_preview : gui_split_preview "fx_scanlines $*",${-3--1} #@gui Self Glitching : fx_self_glitching, fx_self_glitching_preview(1) #@gui : Multiplier = float(0,-5,5) #@gui : Bias = float(0,-255,255) #@gui : Negate = bool(0) #@gui : Operator = choice("Add","Mul","And","Or","Xor","Pow","Reverse Pow","Mod","Reverse Mod") #@gui : Shift Point = point(50,50,0,1) #@gui : Boundary = choice(3,"Zero","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/08/19.") fx_self_glitching : ac "_fx_self_glitching ${1-7}",$8 _fx_self_glitching : f "begin( shift = ([w,h]-1)*(50-[$5,$6])%; const sign = $3?-1:1; const boundary = $7; ); val = sign*((2^$1)*j(shift) + $2); (!$4?(val + i): $4==1?(val * i): $4==2?(val & i): $4==3?(val | i): $4==4?xor(val,i): $4==5?(val^i): $4==6?(i^val): $4==7?(val%i): (i%val) )%256; " fx_self_glitching_preview : gui_split_preview "fx_self_glitching $*",${-3--1} #@gui Streak : fx_streak,fx_streak(1) #@gui : Mask Color = color(255,0,0,255) #@gui : Step (%) = float(0,0,30) #@gui : Angle = float(0,0,360) #@gui : Propagation = choice(3,"Backward","Forward","Bidirectional [Sharp]","Bidirectional [Smooth]") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/12/22.") fx_streak : foreach { to_rgba if !$4 R,G,B,A=0 else R,G,B,A=${1-4} fi +select_color 0,$R,$G,$B,$A if $7==3 srgb2rgb.. fi f.. " const step = max(1,$5%*min(w,h)); const angle = $6*pi/180; const dx = step*cos(angle); const dy = step*sin(angle); if (!i(#-1),I, ixf = xf = x; iyf = yf = y; lf = 0; if ($7>=1, while (i(#-1,ixf=round(xf),iyf=round(yf)), ++lf; xf-=dx; yf-=dy)); # Forward ixb = xb = x; iyb = yb = y; lb = 0; if ($7!=1, while (i(#-1,ixb=round(xb),iyb=round(yb)), ++lb; xb+=dx; yb+=dy)); # Backward !$7?I(ixb,iyb): $7==1?I(ixf,iyf): $7==2?(lfAuthor: David Tschumperlé.      Latest Update: 2023/09/16.") fx_watermark_visible : skip "${1= }" watermark_visible "$1",$2,${"font ${3-5}"},${6-7} #@gui Warp by Intensity : fx_warp_by_intensity, fx_warp_by_intensity_preview(0) #@gui : X-Factor = float(0.04,-6,6) #@gui : Y-Factor = float(0.04,-6,6) #@gui : sep = separator() #@gui : X-Offset = float(128,0,255) #@gui : Y-Offset = float(128,0,255) #@gui : sep = separator() #@gui : Correlated Channels = bool(0) #@gui : Interpolation = choice(1,"Nearest Neighbor","Linear") #@gui : Boundary = choice(3,"Transparent","Nearest","Periodic","Mirror") #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/02/09.") fx_warp_by_intensity : if !$7 to_a fi ac "_fx_warp_by_intensity ${1-7}",$8 _fx_warp_by_intensity : if $5 f "ni = norm(R,G,B); J((ni-$3)*$1,(ni-$4)*$2,0,$6,$7)" else f "j((i-$3)*$1,(i-$4)*$2,0,0,$6,$7)" fi fx_warp_by_intensity_preview : gui_split_preview "fx_warp_by_intensity $*",${-3--1} #@gui ____Details #------------------------ #@gui Details Equalizer : fx_equalize_details, fx_equalize_details_preview(0) #@gui : Base Scale = float(5,0,15) #@gui : Detail Scale = float(0.5,0,5) #@gui : note = note("Coarse scale:") #@gui : Threshold = float(0,0,10) #@gui : Smoothness = float(0,0,10) #@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion") #@gui : Gain = float(0,-4,4) #@gui : sep = separator() #@gui : note = note("Medium scale:") #@gui : Threshold = float(0,0,10) #@gui : Smoothness = float(0,0,10) #@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion") #@gui : Gain = float(0,-4,4) #@gui : sep = separator() #@gui : note = note("Small scale:") #@gui : Threshold = float(0,0,10) #@gui : Smoothness = float(0,0,10) #@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion") #@gui : Gain = float(0,-4,4) #@gui : sep = separator() #@gui : note = note("Fine scale:") #@gui : Threshold = float(0,0,10) #@gui : Smoothness = float(0,0,10) #@gui : Smoothness Type = choice(2,"Gaussian","Bilateral","Diffusion") #@gui : Gain = float(0,-4,4) #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(32,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: Jérome Boulanger and David Tschumperlé. #@gui :       Latest Update: 2015/11/11.") _fx_equalize_details : foreach { split_details 5,{max(0.1,$1)},{max(0.1,$2)} __fx_equalize_details[1] ${3-6},8 __fx_equalize_details[2] ${7-10},4 __fx_equalize_details[3] ${11-14},2 __fx_equalize_details[4] ${15-18},1 + c 0,255 } __fx_equalize_details : threshold $1,1 if !$3 b {$2*$5/2} elif $3==1 if $2>0 m,M:=im,iM n. 0,255 repeat int($2/5) { bilateral 15,{5*$5} } bilateral 15,{($2%5)*$5} *. {($M-$m)/255} +. $m fi else smooth {$2*50},0.2,0.8,$5,$5 fi * {10^$4} fx_equalize_details : ac "gui_parallel_overlap \"_fx_equalize_details ${1-18}\",$21,$22",$19,$20 fx_equalize_details_preview : gui_split_preview "fx_equalize_details $*",${-3--1} #@gui Equalize Local Histograms : fx_equalize_local_histograms, fx_equalize_local_histograms_preview(0) #@gui : Strength (%) = float(75,0,100) #@gui : Mode = choice(2,"Raw","Hard","Soft") #@gui : Radius = int(4,1,16) #@gui : Sigma = float(100,0,256) #@gui : Regularization = float(8,0,32) #@gui : Reduce Halos = bool(1) #@gui : sep = separator() #@gui : Channel(s) = choice(16,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/01/31.") fx_equalize_local_histograms : b0="normal" b1="overlay" b2="softlight" foreach { +ac "_fx_equalize_local_histograms ${1-6}",$7,1 blend ${b$2},{$1%} } _fx_equalize_local_histograms : +n 0,511 round. f. " begin( const boundary = 1; const N = $3; const sigma = ($6?1:-1)*(0.1+$4); ref(vector512(),weights); repeat (size(weights),k, # Pre-compute exponentials weights[k] = sigma>=0?exp(-sqr(k/sigma)):1 - exp(-sqr(k/sigma)) ); ); ref(vector512(0),bins); repeat (s,c, ref(crop(x - N,y - N,0,c,2*N + 1,2*N + 1,1,1),V); repeat (size(V),k, # Compute local weighted histogram val = V[k]; diff = abs(val - V[size(V)/2]); bins[val]+=weights[diff]; ); ); sum = 0; repeat (size(bins),k, sum+=bins[k]; bins[k] = sum; ); bins/=max(1e-5,sum); P = I; size(P)==1?(P = bins[P[0]]; 0): size(P)==2?(P = [ bins[P[0]], bins[P[1]] ]; 0): size(P)==3?(P = [ bins[P[0]], bins[P[1]], bins[P[2]] ]; 0): size(P)==4?(P = [ bins[P[0]], bins[P[1]], bins[P[2]], bins[P[3]] ]; 0); P" n. 0,255 if $5 norm.. bilateral. ..,$5,{2+$5} fi k. fx_equalize_local_histograms_preview : gui_split_preview "fx_equalize_local_histograms $*",${-3--1} #@gui Freaky Details : fx_freaky_details, fx_freaky_details_preview(0) #@gui : Amplitude = int(2,1,5) #@gui : Scale = float(10,0,100) #@gui : Iterations = int(1,1,4) #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé and Patrick David. #@gui :       Latest Update: 2013/27/02.") #@gui : sep = separator() #@gui : note = note("This effect has been done following:") #@gui : url = link("This tutorial from Patrick David", #@gui : "https://patdavid.net/2013/02/calvin-hollywood-freaky-details-in-gimp.html") fx_freaky_details : foreach { split_opacity l[0] { repeat $3 { . +-. 255 *. -1 repeat $1 { bilateral. $2,{1.5*$2} } blend[-2,-1] vividlight blend overlay } } a c } n 0,255 fx_freaky_details_preview : gui_split_preview "fx_freaky_details $*",${-3--1} #@gui Local Normalization : fx_normalize_local, fx_normalize_local_preview(0) #@gui : Amplitude = float(2,0,60) #@gui : Radius = int(6,1,64) #@gui : Neighborhood Smoothness = float(5,0,40) #@gui : Average Smoothness = float(20,0,40) #@gui : Constrain Values = bool(1) #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_normalize_local : ac "normalize_local $1,$2,$3,$4,$5,0,255",$6 fx_normalize_local_preview : gui_split_preview "fx_normalize_local $*",${-3--1} #@gui Local Processing : fx_local_processing, fx_local_processing_preview(1) #@gui : Action = choice("Normalize","Equalize") #@gui : Strength (%) = float(75,0,100) #@gui : Neighborhood Size (%) = float(10,1,100) #@gui : Overlap (%) = float(50,0,75) #@gui : Regularization (%) = float(20,0,100) #@gui : Process Channels Individually = bool(0) #@gui : sep = separator() #@gui : Channel(s) = choice(7,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/02/28.") fx_local_processing : com0="n 0,255" com1="equalize 256,0,255 n 0,255" com=${com$1} if $6 com="s c "$com" a c" fi foreach { size:=round(max(8,max(w,h)*$3%)) +ac "at \""$com"\","$size","$size",1,$4%,$4%",$7 if $5 +norm[0] bilateral[1] .,{$5/20}%,{2+$5/4} rm. fi blend alpha,{$2%} } fx_local_processing_preview : gui_split_preview "fx_local_processing $*",${-3--1} #@gui Magic Details : fx_magic_details,fx_magic_details_preview(0) #@gui : Amplitude = float(6,0,30) #@gui : Spatial Scale = float(3,0,10) #@gui : Value Scale = float(15,0,20) #@gui : Edges = float(-0.5,-3,3) #@gui : Smoothness = float(2,0,20) #@gui : sep = separator() #@gui : Channel(s) = choice(27,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/01/10.") fx_magic_details : ac "_fx_magic_details ${1-5}",$6,1 _fx_magic_details : foreach { +bilateral $2,$3 +gradient_norm.. +. 1 ^. {$4>=0?3.1-$4:-3.1-$4} b. $5 n. 1,{1+$1} -... .. *[-3,-1] + c 0,255 } fx_magic_details_preview : gui_split_preview "fx_magic_details $*",${-3--1} #@gui Mighty Details : fx_mighty_details, fx_mighty_details_preview(0) #@gui : Amplitude = float(25,0,100) #@gui : Details Amount = float(1,0,2) #@gui : Details Scale = float(25,1,100) #@gui : Details Smoothness = int(1,0,10) #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/08/08.") _fx_mighty_details : +smooth $3,0,1,0.5,0.5 -[1] [0] +abs. sign.. M:=iM ^. {2-$2} *. {$M/iM} *[-2,-1] +diffusiontensors[0] 0,1,0.5,0.5 repeat $4 { smooth[1] [2],20 } *[1] {-$1/5} + fx_mighty_details : ac "_fx_mighty_details ${1-4}",$5,1 n 0,255 fx_mighty_details_preview : gui_split_preview "fx_mighty_details $*",${-3--1} #@gui Sharpen [Alpha] : fx_sharpen_alpha,fx_sharpen_alpha_preview(0) #@gui : Amplitude (%) = float(100,0,400) #@gui : Number of Scales = int(5,1,10) #@gui : Anisotropy (%) = float(50,0,100) #@gui : Minimize Alpha (%) = float(100,0,100) #@gui : sep = separator() #@gui : Channel(s) = choice(0,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/10/14.") fx_sharpen_alpha : ac "sharpen_alpha {$1%},$2,{$3%},{$4%}",$5,1 fx_sharpen_alpha_preview : gui_split_preview "fx_sharpen_alpha $*",${-3--1} #@gui Sharpen [Deblur] : fx_deblur, fx_deblur_preview(0) #@gui : Radius = float(2,0,20) #@gui : Iterations = int(10,0,100) #@gui : Time Step = float(20,0,50) #@gui : Smoothness = float(0.1,0,10) #@gui : Regularization = choice(1,"Tikhonov","Mean Curvature","Total Variation") #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_deblur : ac "gui_parallel_overlap \"deblur ${1-5} c 0,255\",$7,$8",$6,1 fx_deblur_preview : gui_split_preview "fx_deblur $*",${-3--1} #@gui Sharpen [Gold-Meinel] : fx_unsharp_goldmeinel, fx_unsharp_goldmeinel_preview(0) #@gui : Sigma = float(1,0.5,10) #@gui : Iterations = int(5,1,15) #@gui : Acceleration = float(1,1,3) #@gui : Blur = choice(1,"Exponential","Gaussian") #@gui : Cut = bool(true) #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: Jérôme Boulanger.      Latest Update: 2013/29/03.") fx_unsharp_goldmeinel: ac "gui_parallel_overlap \"_fx_unsharp_goldmeinel $*\",$7,$8",$6,1 _fx_unsharp_goldmeinel : deblur_goldmeinel $* if $5 c 0,255 else n 0,255 fi fx_unsharp_goldmeinel_preview: gui_split_preview "fx_unsharp_goldmeinel $*",${-3--1} #@gui Sharpen [Inverse Diffusion] : fx_sharpen_inversediff, fx_sharpen_inversediff_preview(0) #@gui : Amplitude = float(50,1,300) #@gui : Iterations = int(2,1,10) #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_sharpen_inversediff : ac "repeat $2 { sharpen $1 c 0,255 }",$3,1 fx_sharpen_inversediff_preview : gui_split_preview "fx_sharpen_inversediff $*",${-3--1} #@gui Sharpen [Multiscale] : fx_sharpen_multiscale, fx_sharpen_multiscale_preview(0) #@gui : Strength (%) = float(15,0,100) #@gui : Regularity (%) = float(20,0,100) #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/01/14.") fx_sharpen_multiscale : ac "_fx_sharpen_multiscale $1,$2",$3 fx_sharpen_multiscale_preview : gui_split_preview "fx_sharpen_multiscale $*",${-3--1} _fx_sharpen_multiscale : foreach { N:=max(1,int(log2(min(w,h))-2)) +l { repeat $N { +r. 50%,50%,1,100%,2 +r. ..,..,1,100%,5 -[-3,-1] } } # Decompose guided[0] 4,100 # Smooth guide image # Process each scale. repeat $!-1 { l[0,{$>+1}] { +ri[0] [1],2 +equalize.. 1024 bilateral. ..,{2*$2%},100 j[1] .,0,0,0,0,{$1%} k[0,1] } } rm[0] repeat $!-1 { r. ..,..,1,100%,5 +[-2,-1] } # Recompose c 0,255 } #@gui Sharpen [Octave Sharpening] : fx_unsharp_octave, fx_unsharp_octave_preview(0) #@gui : Scales = int(4,1,10) #@gui : Maximal Radius = float(5,0,20) #@gui : Amount = float(3,0,10) #@gui : Threshold = float(0,0,255) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice(1,"Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_unsharp_octave : ac "gui_parallel_overlap \"unsharp_octave $1,$2,$3,$4\",$6,$7",$5,1 fx_unsharp_octave_preview : gui_split_preview "fx_unsharp_octave $*",${-3--1} #@gui Sharpen [Richardson-Lucy] : fx_unsharp_richardsonlucy, fx_unsharp_richardsonlucy_preview #@gui : Sigma = float(1,0.5,10) #@gui : Iterations = int(10,1,100) #@gui : Blur = choice(1,"Exponential","Gaussian") #@gui : Cut = bool(true) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: Jérôme Boulanger.      Latest Update: 2013/29/03.") fx_unsharp_richardsonlucy : deblur_richardsonlucy $* if $4 c 0,255 else n 0,255 fi fx_unsharp_richardsonlucy_preview : gui_split_preview "fx_unsharp_richardsonlucy $*",${-3--1} #@gui Sharpen [Shock Filters] : fx_sharpen_shock, fx_sharpen_shock_preview(0) #@gui : Amplitude = float(150,1,400) #@gui : Edge Threshold = float(0.1,0,0.7) #@gui : Gradient Smoothness = float(0.8,0,10) #@gui : Tensor Smoothness = float(1.1,0,10) #@gui : Iterations = int(1,1,10) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_sharpen_shock : ac "repeat $5 { sharpen $1,$2,$3,$4 c 0,255 }",$6,1 fx_sharpen_shock_preview : gui_split_preview "fx_sharpen_shock $*",${-3--1} #@gui Sharpen [Texture] : fx_sharpen_texture, fx_sharpen_texture_preview(0) #@gui : Strength = float(1,0,4) #@gui : Radius = float(4,0,32) #@gui : sep = separator() #@gui : Channel(s) = choice(16,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/09.") fx_sharpen_texture : ac "_fx_sharpen_texture ${1-2}",$3,1 _fx_sharpen_texture : foreach { +rolling_guidance $2,5,0.5 -. [0] *. $1 - c 0,255 } fx_sharpen_texture_preview : gui_split_preview "fx_sharpen_texture $*",${-3--1} #@gui Sharpen [Unsharp Mask] : fx_unsharp, fx_unsharp_preview(0) #@gui : Sharpening Type = choice(1,"Gaussian","Bilateral") #@gui : Spatial Radius = float(1.25,0,20) #@gui : Bilateral Radius = float(10,0,60) #@gui : Amount = float(2,0,10) #@gui : Threshold = float(0,0,20) #@gui : Darkness Level = float(1,0,4) #@gui : Lightness Level = float(1,0,4) #@gui : Iterations = int(1,1,10) #@gui : Negative Effect = bool(0) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : note = note{"\n\nNote: #@gui : This filter is inspired by the original GIMP Unsharp Mask filter, with additional parameters. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") _fx_unsharp : repeat $! { repeat $8 { if !$1 +b. $2 else +bilateral. $2,$3 fi -. .. *. -$4 +norm. >=. $5% ri. .. *[-2,-1] if $9 *. -1 fi +c. 0,100% c.. -100%,0 *.. $6 *. $7 +[-2,-1] +[-2,-1] c. 0,255 } mv. 0 } fx_unsharp : ac "_fx_unsharp $1,$2,$3,$4,$5,$6,$7,$8,$9",$10,1 fx_unsharp_preview : gui_split_preview "fx_unsharp $*",${-3--1} #@gui Split Details [Alpha] : fx_split_details_alpha, fx_split_details_alpha_preview(0) #@gui : Number of Levels = int(0,0,16) #@gui : note = note("Set Number of Levels to 0 for automatic setting.") #@gui : sep = separator() #@gui : Set Scales = choice("Auto","User-defined") #@gui : Base Scale = float(10,0,30)_1 #@gui : Details Scale = float(1,0,20)_1 #@gui : sep = separator() #@gui : Preview Without Alpha = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/10/06.") fx_split_details_alpha : remove_opacity foreach { nb_scales_max:=int(log2(min(w,h))) nb_scales:=$1?$1:$nb_scales_max repeat $nb_scales-1 { sigma:=$2?lerp($4,$3,$>/($>+$<)):0.25+2^$> +_fx_split_details_alpha_blur. $sigma sub_alpha.. . } } _fx_split_details_alpha_blur : if $1>=0.1 b. $1 else if $1>=0.05 (1,4,7,4,1;4,16,26,16,4;7,26,41,26,7;4,16,26,16,4;1,4,7,4,1) else (1,2,1;2,4,2;1,2,1) fi normalize_sum. convolve.. . rm. fi fx_split_details_alpha_preview : foreach { fx_split_details_alpha ${1-4} if $5 remove_opacity[^-1] else to_rgba. fi N:=int(sqrt($!)) N:=round($!/$N,1,1) rs ,{100/$N}% foreach { 0 text. "#"{1+$>}" ",1,1,24,1,255 +dilate. 5 to_rgba[1] j[0] [1],2,0,0,0,1,[2],255 k[0] } to_rgba frame xy,1,0 frame xy,3,255 append_tiles , } u "{$1}{$2}{$3}_"{$2?2:1}"{$4}_"{$2?2:1}"{$5}" #@gui Split Details [Gaussian] : fx_split_details_gaussian, fx_split_details_gaussian_preview(0) #@gui : Number of Scales = int(6,3,12) #@gui : Base Scale = float(10,0,200) #@gui : Details Scale = float(1,0,20) #@gui : sep = separator() #@gui : Sharpen Details in Preview = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/22/01.") fx_split_details_gaussian : remove_opacity foreach { nm=${-gui_layer_name} pos=${-gui_layer_pos} split_details $1,$2,$3 +[^0] 128 c[^0] 0,255 round repeat $!-1 { =>[{1+$>}] "mode(grainmerge), name"($nm" [scale ""#"{1+$>}"]), pos("$pos")" } =>[0] "name"($nm" [residual]), pos("$pos")" rv } fx_split_details_gaussian_preview : foreach { fx_split_details_gaussian $* if $4 equalize[^-1] 256 fi n[^-1] 0,255 N:=int(sqrt($!)) N:=round($!/$N,1,1) rs ,{100/$N}% foreach { 0 text. "#"{1+$>}" ",1,1,24,1,255 +dilate. 5 to_rgba[1] j[0] [1],2,0,0,0,1,[2],255 k[0] } to_rgba frame xy,1,0 frame xy,3,255 append_tiles , } #@gui Split Details [Wavelets] : fx_split_details_wavelets, fx_split_details_wavelets_preview(0) #@gui : Number of Scales = int(6,2,12) #@gui : Add Alpha Channels to Detail Scale Layers = _bool(0) #@gui : sep = separator() #@gui : Sharpen Details in Preview = bool(0) #@gui : sep = separator() #@gui : note = note{"Note: This filter decomposes an image into several detail scales, #@gui : using wavelet atrous. #@gui : It should provide similar results to the #@gui : Wavelet Decompose Plug-in #@gui : (by Marco Rossini). #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/23/03.") fx_split_details_wavelets : remove_opacity foreach { nm=${-gui_layer_name} pos=${-gui_layer_pos} split_details $1,0,0 rv +[^-1] 128 c[^-1] 0,255 round if $2 to_a[^-1] fi repeat $!-1 { =>[$>] "mode(grainmerge), name"($nm" [scale ""#"{1+$>}"]), pos("$pos")" } => "name"($nm" [residual]), pos("$pos")" } fx_split_details_wavelets_preview : foreach { fx_split_details_wavelets $1,0 if $3 equalize[^-1] 256 fi n[^-1] 0,255 N:=int(sqrt($!)) N:=round($!/$N,1,1) rs ,{100/$N}% foreach { 0 text. "#"{1+$>}" ",1,1,24,1,255 +dilate. 5 to_rgba[1] j[0] [1],2,0,0,0,1,[2],255 k[0] } to_rgba frame xy,1,0 frame xy,3,255 append_tiles , } #@gui Tone Mapping : fx_map_tones, fx_map_tones_preview(0) #@gui : Threshold = float(0.5,0,1) #@gui : Gamma = float(0.7,0,1) #@gui : Smoothness = float(0.1,0,10) #@gui : Iterations = int(30,0,500) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_map_tones : ac "map_tones ${1-4}",$5,1 n 0,255 fx_map_tones_preview : gui_split_preview "fx_map_tones $*",${-3--1} #@gui Tone Mapping [Fast] : fx_map_tones_fast, fx_map_tones_fast_preview(0) #@gui : Radius = float(3,0,20) #@gui : Power = float(0.5,0,1) #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: Paul Nasca and David Tschumperlé. #@gui :       Latest Update: 2011/10/06.") fx_map_tones_fast : ac "map_tones_fast $1,$2",$3,2 fx_map_tones_fast_preview : gui_split_preview "fx_map_tones_fast ${^0}",${-3--1} #@gui ____Frames #---------------------- #@gui Droste : fx_droste, fx_droste_preview(1) #@gui : note = note("Upper-left coordinates :") #@gui : Point #0 = point(20,20,0,1,255,0,0) #@gui : sep = separator() #@gui : note = note("Upper-right coordinates :") #@gui : Point #1 = point(80,20,0,1,255,0,255) #@gui : sep = separator() #@gui : note = note("Lower-right coordinates :") #@gui : Point #2 = point(80,80,0,1,0,128,255) #@gui : sep = separator() #@gui : note = note("Lower-left coordinates :") #@gui : Point #3 = point(20,80,0,1,0,255,255) #@gui : sep = separator() #@gui : Iterations = int(1,1,10) #@gui : X-Shift = float(0,-100,100) #@gui : Y-Shift = float(0,-100,100) #@gui : Angle = float(0,0,360) #@gui : Zoom = float(1,0.1,5) #@gui : Mirror = choice("None","X-Axis","Y-Axis","XY-Axes") #@gui : Boundary = choice(1,"Transparent","Nearest","Periodic","Mirror") #@gui : Drawing Mode = choice{"Replace","Replace (Sharpest)","Behind","Below"} #@gui : View Outlines Only = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/11/06.") fx_droste : repeat $! { if $16==1 100%,100%,1,1,'x' 100%,100%,1,1,'y' a[-2,-1] c fi repeat $9 { x0,y0,x1,y1,x2,y2,x3,y3:=round([\ $1*w/100,$2*h/100,$3*w/100,$4*h/100,\ $5*w/100,$6*h/100,$7*w/100,$8*h/100]) 100%,100%,1,2,-32767 polygon. 4,$x0,$y0,$x1,$y1,$x2,$y2,$x3,$y3,1,-65535 sh. 0 f. "i==-65535?( x03 = "$x0"+(y-"$y0")/("$y3"-"$y0")*("$x3"-"$x0"); x12 = "$x1"+(y-"$y1")/("$y2"-"$y1")*("$x2"-"$x1"); (x-x03)/(x12-x03)*(w-1)):i" rm. sh. 1 f. "i==-65535?( y01 = "$y0"+(x-"$x0")/("$x1"-"$x0")*("$y1"-"$y0"); y32 = "$y3"+(x-"$x3")/("$x2"-"$x3")*("$y2"-"$y3"); (y-y01)/(y32-y01)*(h-1)):i" rm. xshift,yshift,alpha:=w*$10/100,h*$11/100,-$12*pi/180 ca,sa,w2,h2:=cos($alpha)/$13,sin($alpha)/$13,w/2,h/2 f. i==-32767?i:(X=i(x,y,0,0)-$w2;Y=i(x,y,0,1)-$h2;!c?$w2-$xshift+X*$ca-Y*$sa:$h2-$yshift+X*$sa+Y*$ca) if !$14 sh. 0 f. i==-32767?x:i rm. sh. 1 f. i==-32767?y:i rm. elif $14==1 sh. 0 f. i==-32767?x:w-1-i rm. sh. 1 f. i==-32767?y:i rm. elif $14==2 sh. 0 f. i==-32767?x:i rm. sh. 1 f. i==-32767?y:h-1-i rm. else sh. 0 f. i==-32767?x:w-1-i rm. sh. 1 f. i==-32767?y:h-1-i rm. fi if $16<2 warp.. .,0,{!$16},$15 rm. else +warp.. .,0,1,$15 rm.. if $16==3 rv[-2,-1] fi blend[-2,-1] alpha fi } if $16==1 warp.. .,0,1,1 rm. fi mv. 0 } fx_droste_preview : if !$17 fx_droste $* else polygon 4,$1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,0.3,0,0,0,255 fi polygon 4,$1%,$2%,$3%,$4%,$5%,$6%,$7%,$8%,1,0xFFFFFFFF,0,0,0,255 #@gui Frame [Blur] : fx_frame_blur, fx_frame_blur(1) #@gui : Horizontal Size (%) = float(30,0,100) #@gui : Vertical Size (%) = float(30,0,100) #@gui : sep = separator() #@gui : Crop = float(0,0,100) #@gui : Blur = float(5,0,10) #@gui : Roundness = float(0,0,1) #@gui : Apply Color Balance = bool(0) #@gui : Balance Color = color(128,128,128) #@gui : Normalization = choice("None","Stretch","Equalize") #@gui : sep = separator() #@gui : Outline Size = float(5,0,50) #@gui : Outline Color = color(255,255,255) #@gui : X-Shadow = float(2,-10,10) #@gui : Y-Shadow = float(2,-10,10) #@gui : Shadow Smoothness = float(1,0,5) #@gui : Shadow Contrast = float(0,0,100) #@gui : X-Centering = float(0.5,0,1) #@gui : Y-Centering = float(0.5,0,1) #@gui : Angle = float(0,-180,180) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/19/01.") fx_frame_blur : foreach { to_rgb sx:=$1%*max(w,h) sy:=$2%*max(w,h) +r {w+$sx},{h+$sy},1,100%,3 b[1] $4% if $6 balance_gamma[1] ${7-9} fi if $10==1 n[1] 0,255 elif $10==2 n[1] 0,255 equalize[1] 256 fi rv z[1] {$3/2}%,{$3/2}%,{100-$3/2}%,{100-$3/2}% to_rgba[1] if $5 r:=1+1/$5 sh[1] 100% f. 1-(abs(x/w-0.5)^$r+abs(y/h-0.5)^$r)^(1/$r) v:=min(i(w/2,0),i(w-1,h/2),i(w/2,h-1),i(0,h/2)) c. $v,{$v+0.5/max(w,h)} n. 0,255 rm. fi s:=$11%*max(w,h) r[1] {w+$s},{h+$s},1,4,0,0,0.5,0.5 i[1] 100%,100%,1,3 fc[1] ${12-14} blend[1,2] alpha to_a. if $5 sh[1] 100% f. 1-(abs(x/w-0.5)^$r+abs(y/h-0.5)^$r)^(1/$r) v:=min(i(w/2,0),i(w-1,h/2),i(w/2,h-1),i(0,h/2)) c. $v,{$v+0.5/max(w,h)} n. 0,255 rm. fi rotate[1] $21,1,0 r[1] [0],[0],1,4,0,0,$19,$20 +channels[1] 100% b. $17%,0 c. 0,{max(1,100-$18)}% n. 0,255 shift. {round(w*$15%)},{round(h*$16%)},0,0,0 /. -255 +. 1 *[0,-1] blend alpha } #@gui Frame [Cube] : frame_cube, frame_cube(1) #@gui : Depth = float(3,0,30) #@gui : X-Center = float(0,-2,2) #@gui : Y-Center = float(0,-2,2) #@gui : Left Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY") #@gui : Right Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY") #@gui : Upper Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY") #@gui : Lower Side Orientation = choice("Normal","Mirror-X","Mirror-Y","Mirror-XY") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé, Angelo Lama. #@gui :       Latest Update: 2012/29/01.") #@gui Frame [Fuzzy] : fx_frame_fuzzy, fx_frame_fuzzy(0) #@gui : Horizontal Size (%) = float(5,0,100) #@gui : Vertical Size (%) = float(5,0,100) #@gui : Fuzzyness = float(10,0,40) #@gui : Smoothness = float(1,0,5) #@gui : Color = color(255,255,255,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_frame_fuzzy : foreach { sx:=$1%*max(w,h)/2 sy:=$2%*max(w,h)/2 frame_fuzzy $sx,$sy,${3-8} } #@gui Frame [Mirror] : fx_frame_mirror, fx_frame_mirror_preview(1) #@gui : note = note("Frame size:") #@gui : Horizontal (%) = float(10,0,100) #@gui : Vertical (%) = float(10,0,100) #@gui : sep = separator() #@gui : note = note("Image alignment:") #@gui : Horizontal (%) = float(50,0,100) #@gui : Vertical (%) = float(50,0,100) #@gui : sep = separator() #@gui : note = note("Frame dilation/shrinking:") #@gui : Left = float(0,-5,5) #@gui : Right = float(0,-5,5) #@gui : Up = float(0,-5,5) #@gui : Bottom = float(0,-5,5) #@gui : sep = separator() #@gui : Preview Opacity (%) = float(0.75,0,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/08/20.") fx_frame_mirror : foreach { {100+2*$1}%,{100+2*$2}%,1,100%," const boundary = 3; const offx = (w - w#-1)*$3%; const offy = (h - h#-1)*$4%; const f_left = 2^$5; const f_right = 2^$6; const f_up = 2^$7; const f_bottom = 2^$8; x = x - offx; y = y - offy; x<0?(x*=-f_left): x>=w#-1?(x = w#-1 - 1 - f_right*(x - w#-1)); y<0?(y*=-f_up): y>=h#-1?(y = h#-1 - 1 - f_bottom*(y - h#-1)); I(#-1,x,y)" k. } fx_frame_mirror_preview : foreach { ws,hs:=w,h fx_frame_mirror $* wd,hd:=w,h rs $_preview_area_width,$_preview_area_height wp,hp:=w,h ws,hs:=[$ws,$hs]*[$wp,$hp]/[$wd,$hd] coords:=off=([$wp,$hp]-[$ws,$hs])*[$3,$4]%;[off,off+[$ws,$hs]-1] split_opacity 100%,100%,1,1,$9 rectangle. $coords,1,1 *[0,-1] a c rectangle $coords,0.75,0xF0F0F0F0,255 rectangle $coords,0.75,0x0F0F0F0F,0,0,0,255 } #@gui Frame [Painting] : fx_frame_painting, fx_frame_painting_preview(1) #@gui : Size (%) = float(10,0,100) #@gui : Contrast = float(0.4,0,1) #@gui : Smoothness = float(6,0,30) #@gui : Color = color(225,200,120) #@gui : sep = separator() #@gui : Vignette Size = float(2,0,50) #@gui : Vignette Contrast = float(400,0,1000) #@gui : sep = separator() #@gui : Defects Contrast = float(50,0,512) #@gui : Defects Density = float(10,0,100) #@gui : Defects Size = float(1,0,10) #@gui : Defects Smoothness = float(0.5,0,20) #@gui : sep = separator() #@gui : Serial Number = int(123456,0,1000000) #@gui : Frame as a New Layer = _bool(false) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/07/06.") fx_frame_painting : if $14 repeat $! { 100%,100%,1,4 frame_painting. $1%,$2,$3%,${4-6},$7%,${8-13} rv[-2,-1] to_a. r. ..,..,1,4,0,0,0.5,0.5 mv[-2,-1] 0 } else frame_painting $1%,$2,$3%,${4-6},$7%,${8-13} fi fx_frame_painting_preview : frame_painting $1%,$2,$3%,${4-6},$7%,${8-13} #@gui Frame [Pattern] : fx_frame_pattern, fx_frame_pattern_preview(1) #@gui : Tiles = int(10,3,30) #@gui : Pattern = choice(1,"Top Layer","Self Image") #@gui : Iterations = int(1,1,10) #@gui : Constrain Image Size = _bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/01/08.") fx_frame_pattern : if $2" || "$!==1 repeat $3 { frame_pattern $1,$4 } else repeat $3 { frame_pattern[^0] $1,[0],$4 } fi fx_frame_pattern_preview : fx_frame_pattern ${1-3},1 #@gui Frame [Regular] : fx_frame, fx_frame(1) #@gui : note = note("Crop parameters :") #@gui : X-Start (%) = int(0,0,100) #@gui : X-End (%) = int(100,0,100) #@gui : Y-Start (%) = int(0,0,100) #@gui : Y-End (%) = int(100,0,100) #@gui : sep = separator() #@gui : note = note("Frame parameters :") #@gui : Width (%) = int(10,0,100) #@gui : Height (%) = int(10,0,100) #@gui : Color = color(0,0,0,255) #@gui : Outline Size = int(1,0,100) #@gui : Outline Color = color(255,255,255,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_frame : to_rgba repeat $! { z. $1%,$3%,$2%,$4% frame. $11,$11,${12-15} sx,sy:=[$5%,$6%]*max(w,h) frame. $sx,$sy,${7-10} mv. 0 } #@gui Frame [Round] : fx_frame_round, fx_frame_round(1) #@gui : X-Size (%) = float(10,0,100) #@gui : Y-Size (%) = float(10,0,100) #@gui : Radius (%) = float(20,0,100) #@gui : Smoothness = float(0.1,0,15) #@gui : Color = color(0,0,0,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_frame_round : to_rgba frame_round $1%,$2%,$3%,${4-7} #@gui Frame [Smooth] : fx_frame_smooth, fx_frame_smooth(1) #@gui : Width (%) = int(10,0,100) #@gui : Height (%) = int(10,0,100) #@gui : Roundness = float(0.25,0,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/25/04.") fx_frame_smooth : foreach { sx,sy:=[$1%,$2%]*max(w,h) 100%,100%,1,1,0 if $3 r:=1+1/$3 f. 1-(abs(x/w-0.5)^$r+abs(y/h-0.5)^$r)^(1/$r) v:=min(i(w/2,0),i(w-1,h/2),i(w/2,h-1),i(0,h/2)) <=. $v fi frame $sx,$sy,1 inpaint_pde[0] [1],100%,1,15 rm. } c 0,255 #@gui Old Photograph : fx_old_photo, fx_old_photo(1) #@gui : Vignette Strength = float(200,0,255) #@gui : Vignette Min Radius = float(50,0,100) #@gui : Vignette Max Radius = float(85,0,100) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_old_photo : vignette ${1-3} old_photo #@gui Polaroid : fx_polaroid, fx_polaroid(1) #@gui : Frame Size = int(10,0,400) #@gui : Bottom Size = int(20,0,400) #@gui : X-Shadow = float(0,-20,20) #@gui : Y-Shadow = float(0,-20,20) #@gui : Smoothness = float(3,0,5) #@gui : X-Curvature = float(0,0,1) #@gui : Y-Curvature = float(0,0,1) #@gui : Angle = float(20,-180,180) #@gui : Vignette Strength = float(50,0,255) #@gui : Vignette Min Radius = float(70,0,100) #@gui : Vignette Max Radius = float(95,0,100) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/06.") fx_polaroid : vignette ${8-10} polaroid $1,$2 drop_shadow $3%,$4%,$5%,$6,$7 rotate $8,1,0 #@gui Tunnel : fx_tunnel, fx_tunnel(1) #@gui : Depth = int(4,1,100) #@gui : Factor = float(80,1,99) #@gui : Center (%) = point(50,50) #@gui : Opacity = float(0.2,0,1) #@gui : Angle = float(0,-90,90) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/22/11.") fx_tunnel : tunnel $1,$2%,{[${3,4}]%},${5-6} #@gui Vignette : fx_vignette, fx_vignette #@gui : Strength = float(70,0,255) #@gui : Min Radius = float(70,0,100) #@gui : Max Radius = float(95,0,100) #@gui : Color = color(0,0,0,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/24/10.") fx_vignette : foreach { to_rgb to_rgba split_opacity =. 0 vignette. ${1-3} a c +fc ${4-7} rv blend alpha } #@gui ____Frequencies #---------------------------- #@gui Bandpass : fx_bandpass, fx_bandpass_preview(0) #@gui : Low Frequency = float(0,0,100) #@gui : High Frequency = float(100,0,100) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice(2,"None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_bandpass : foreach { split_opacity ac[0] "bandpass $1%,$2%",$3,$4 a c } fx_bandpass_preview : gui_split_preview "fx_bandpass $*",${-3--1} #@gui Fourier Analysis : fx_display_fft, fx_display_fft(1)* #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_display_fft : to_rgb display_fft #@gui Fourier Transform : fx_fourier_transform, fx_fourier_transform_preview(1)* : * #@gui : Discard Transparency = bool(1) #@gui : sep = separator() #@gui : note = note{"Note: Apply this filter once to get the direct FFT, #@gui : and once again to get the reverse transform."} #@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=3137dDa6P4s") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2022/04/26.") fx_fourier_transform : if $1 remove_opacity fi ind=0 _preview_error=0 for $ind<$! { gui_layer_name[$ind] nm=${} p:="s=['"$nm"'];find(s,' [FFT Magnitude] | ',size(s)-1,-1)" if $p>=0 if $ind+1>=$! gui_resize_preview_area[$ind] gui_error_preview[$ind] "Two input layers are required. Please set 'Input Layers' mode accordingly." ind=$! _preview_error=1 else l[$ind,{$ind+1}] { # Inverse transform ('$nm') +z. {$p+19},100% a0,b0=${u\ {t}} rm. z. 0,{$p-1} nm={t} rm. /.. $a0 +.. $b0 # Denormalize magnitude /. {255/(2*pi)} -. {pi} # Denormalize phase exp.. -.. 1 ifftpolar c 0,255 gui_set_layer_name $nm ind+=1 } fi else l[$ind] { # Direct transform fftpolar +.. 1 log.. a0,b0={0,_[255/(iM-im),im]} -.. $b0 *.. $a0 # Normalize magnitude +. {pi} *. {255/(2*pi)} # Normalize phase c 0,255 gui_set_layer_name.. $nm" [FFT Magnitude] | "$a0,$b0 gui_set_layer_name. $nm" [FFT Phase]" ind+=2 } fi } fx_fourier_transform_preview : fx_fourier_transform $* k[0] if !$_preview_error gui_resize_preview fi #@gui Fourier Transform [Old] : fx_fourier_old,fx_fourier_old_preview(1)* : * #@gui :Magnitude / Phase=choice{0,"One Layer (Horizontal)","One Layer (Vertical)","Two Layers"} #@gui :Discard Transparency=bool(1) #@gui :_=separator() #@gui :_=note("Note: Apply this filter once to get the direct FFT, and once again to get the reverse \ # transform.") #@gui :_=link("Click here for a video tutorial","http://www.youtube.com/watch?v=3137dDa6P4s") #@gui :_=separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/06/16.") fx_fourier_old : skip ${2=0} if $2 remove_opacity fi magic="GMICFFT" i=0 for $i<$! { ni:=$i+1 nm={$i,n} is_ifft=0 +columns[$i] 100% l. { mag,m0,M0,m1,M1=${u\ {t}} if ['$mag']=='$magic' is_ifft=1 fi onfail } rm. if !$is_ifft +rows[$i] 100% l. { mag,m0,M0,m1,M1=${u\ {t}} if ['$mag']=='$magic' is_ifft=2 fi onfail } rm. fi if !$is_ifft +rows[$i] 100% l. { mag,m0,M0=${u\ {t}} if ['$mag']=='$magic' is_ifft=3 fi onfail } rm. if $is_ifft==3 is_ifft=0 +rows[$ni] 100% l. { mag,m1,M1=${u\ {t}} if ['$mag']=='$magic' is_ifft=3 fi onfail } rm. fi fi if !$is_ifft l[$i] { fftpolar +.. 1 log.. m0,M0,m1,M1:=im#0,iM#0,im#1,iM#1 n[-2,-1] 0,255 if !$1 ({'$magic,$m0,$M0,$m1,$M1'},0) y. a x elif $1==1 ({'$magic,$m0,$M0,$m1,$M1'},0) a y else ({'$magic,$m0,$M0'},0) a[-3,-1] y ({'$magic,$m1,$M1'},0) a[-2,-1] y fi => $nm } else if $is_ifft==1 columns[$i] 0,{$i,w-2} s[$i] x,2 elif $is_ifft==2 rows[$i] 0,{$i,h-2} s[$i] y,2 else rows[$i,$ni] 0,{$i,h-2} fi l[$i,{$i+1}] { n[0] $m0,$M0 n[1] $m1,$M1 exp[0] -[0] 1 ifftpolar c 0,255 } fi i+=$1<2" || "$is_ifft>2?1:2 } fx_fourier_old_preview : if $2 remove_opacity fi dfft #@gui Fourier Watermark : fx_watermark_fourier, _none_ #@gui : Text = text{"(c) G'MIC"} #@gui : Size = int(53,13,128) #@gui : sep = separator() #@gui : note = note("Note: To make the watermark visible afterwards, use the #@gui : 'Fourier Analysis' filter. ") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_watermark_fourier : watermark_fourier "$1",$2 round c 0,255 #@gui ____Layers #----------------------- #@gui Align Layers : fx_align_layers, fx_align_layers_preview : * #@gui : Alignment Type = choice(0,"Rigid","Non-Rigid") #@gui : Smoothness = float(0.7,0,1) #@gui : Scales = choice(0,"Auto","1","2","3","4","5","6","7","8") #@gui : Revert Layers = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/01/11.") fx_align_layers : to_colormode 0 r ${-max_wh},1,100%,0,0,0.5,0.5 if ${4=0} _fx_revert_layers fi remove_opacity if $1 register_nonrigid[^-1] .,$2,0.1,$3 else register_rigid[^-1] .,$2 fi fx_align_layers_preview : fx_align_layers $1,$2,0 blend_edges 0.1 _fx_revert_layers : repeat int($!/2) { rv[{2*$>},{2*$>+1}] } #@gui Blend [Average All] : fx_blend_average_all, fx_blend_average_all : * #@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab") #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter takes multiple layers as input and average them. Set the Input layers option #@gui : to handle multiple input layers. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/11/08.") fx_blend_average_all : if $! to_rgba N=$! r ${-max_wh},1,100%,0,0,0.5,0.5 _gb_fwd $1 + / $N _gb_bwd $1 fi _gb_fwd : to_color if $1==1 foreach { sh 0,2 srgb2rgb. rm. } elif $1==2 foreach { sh 0,2 srgb2rgb. rgb2lab. rm. } fi _gb_bwd : to_color if $1==1 foreach { sh 0,2 rgb2srgb. rm. } elif $1==2 foreach { sh 0,2 lab2rgb. rgb2srgb. rm. } fi #@gui Blend [Edges] : fx_blend_edges, fx_blend_edges(0) : * #@gui : Opacity = float(1,0,1) #@gui : Smoothness = float(0.8,0,5) #@gui : Revert Layers = bool(0) #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter needs two layers to work properly. Set the Input layers option to handle #@gui : multiple input layers. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/21/01.") fx_blend_edges : repeat int($!/2) { l[$>,{$>+1}] { if $3 rv fi +blend_edges[-2,-1] $2 rm... blend[-2,-1] alpha,$1 } } #@gui Blend [Fade] : fx_blend_fade, fx_blend_fade(1) : + #@gui : Preset = choice{1,"Custom","Linear","Circular","Wave","Keftales"} #@gui : Offset = float(0,-1,1) #@gui : Thinness = float(0,0,10) #@gui : Sharpness = float(5,1,20) #@gui : Sharpest = bool(0) #@gui : Revert Layers = bool(0) #@gui : Colorspace = choice("sRGB","Linear RGB","Lab") #@gui : note = note{\n #@gui : The parameters below are used in most presets. #@gui : } #@gui : 1st Parameter = float(0,-1,1) #@gui : 2nd Parameter = float(0,-1,1) #@gui : 3rd Parameter = float(0,-1,1) #@gui : note = note{\n #@gui : The formula below is used for the Custom preset. #@gui : } #@gui : Formula = text{"cos(4*pi*x/w) * sin(4*pi*y/h)"} #@gui : note = note{"Note: #@gui : This filter needs two layers to work properly. Set the Input layers option to handle #@gui : multiple input layers. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/21/01.") fx_blend_fade : if $!==1 return fi to_colormode 4 _gb_fwd $7 if !$1 [0],[0],1,1,"$11" else _fx_blend_fade$1 $8,$9,$10 r. [0],[0],1,1,3 fi n. {-($!-2)*$3},{($!-2)*(1+$3)} -. {$2*(1+$3)*($!-2)} c. 0,{$!-2} if $6 rv[^-1] fi if $5 round. 1 else roundify. $4 fi blend_fade[^-1] . rm. _gb_bwd $7 c 0,255 _fx_blend_fade1 : [0],[0],1,1,"a=$1*pi/2; x*cos(a) + y*sin(a)" _fx_blend_fade2 : [0],[0],1,1,0 =. 1,{($1+1)*50}%,{($2+1)*50}% distance. 1 _fx_blend_fade3 : [0],[0],1,1,0 =. 1,{($1+1)*50}%,{($2+1)*50}% distance. 1 *. {0.01+$3/2} cos. _fx_blend_fade4 : [0],[0],1,1,"((x-w*($1+0.5))*(y-h*($2+0.5)))%(0.2*w*h*(1.001+$3))" #@gui Blend [Median] : fx_blend_median, fx_blend_median(0) : * #@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab") #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter needs at least two layers to work properly. Set the Input layers option to handle #@gui : multiple input layers. #@gui : "} #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé and Iain Fergusson. #@gui :       Latest Update: 2014/16/12.") fx_blend_median : _gb_fwd $1 blend_median _gb_bwd $1 #@gui Blend [Seamless] : fx_blend_seamless, fx_blend_seamless_preview(1) : * #@gui : Mixed Mode = bool(0) #@gui : Inner Fading = float(0,0,100) #@gui : Outer Fading = float(25,0,100) #@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab") #@gui : sep = separator() #@gui : Output as Separate Layers = _bool(0) #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter needs at least two layers to work properly. Set the Input layers option to handle #@gui : multiple input layers. #@gui : "} #@gui : sep = separator() #@gui : url = link("Click here for a detailed description of this filter.",\ # "http://gimpchat.com/viewtopic.php?f=28&t=10204") #@gui : url = link("+ Video tutorial 1","http://www.youtube.com/watch?v=Nu-S1HmOCgE") #@gui : url = link("+ Video tutorial 2","http://www.youtube.com/watch?v=zsHgQY6025I") #@gui : url = link("+ Video tutorial 3","http://www.youtube.com/watch?v=2e6FikWMkaQ") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/04/05.") fx_blend_seamless : rv _gb_fwd $4 to_a[^0] r[^0] [0],[0],1,100%,0 repeat $! { pos=${gui_layer_pos[$>]} shift[$>] ${u\ $pos},0,0 } if $5 # Output as separate layers +blend_seamless $1,$2%,$3% remove_opacity[0,-1] k[0,-1] rv sub_alpha[0] [1],1 else blend_seamless $1,$2%,$3% # Output as a single layer. fi _gb_bwd $4 fx_blend_seamless_preview : fx_blend_seamless ${1-4},0 #@gui Blend [Standard] : fx_blend, fx_blend_preview : * #@gui : Mode = choice{6,"Add","Alpha","And","Average","Blue","Burn","Custom formula","Darken","Difference", #@gui : "Divide","Dodge","Edges","Exclusion","Freeze","Grain Extract","Grain Merge","Green","Hard Light", #@gui : "Hard Mix","Hue","Interpolation","Lighten","Lightness","Linear Burn","Linear Light","Luminance", #@gui : "Multiply","Negation","Or","Overlay","Pin Light","Red","Reflect","Saturation", #@gui : "Shape Area Max","Shape Area Max0","Shape Area Min","Shape Area Min0","Shape Average","Shape Average0", #@gui : "Shape Median","Shape Median0","Shape Min","Shape Min0","Shape Max","Shape Max0","Shape Prevalent", #@gui : "Soft Burn","Soft Dodge","Soft Light","Screen","Stamp","Subtract","Value","Vivid Light","Xor"} #@gui : Process As = choice("Two-by-Two","Upper Layer is the Top Layer for All Blends", #@gui : "Lower Layer is the Bottom Layer for All Blends") #@gui : Opacity (%) = float(100,0,100) #@gui : Preview All Outputs = bool(1) #@gui : sep = separator() #@gui : Custom Formula = text{"1/2 - 1/4*cos(pi*a) - 1/4*cos(pi*b)"} #@gui : note = note{"Note: In custom formulas, a and b respectively stand for #@gui : the values of the base layer and the blend layer, #@gui : and are defined in value range [0,1]."} #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter needs at least two layers to work properly. Do not forget to set the Input layers option #@gui : below to handle multiple input layers. #@gui : "} #@gui : url = link("Reference page for G'MIC blending modes", #@gui : "https://github.com/GreycLab/gmic-community/wiki/Blending-modes") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/03/08.") fx_blend : mode=${arg0\ $1,add,alpha,and,average,blue,burn,custom_formula,darken,difference,\ divide,dodge,edges,exclusion,freeze,grainextract,grainmerge,green,hardlight,\ hardmix,hue,interpolation,lighten,lightness,linearburn,linearlight,luminance,\ multiply,negation,or,overlay,pinlight,red,reflect,saturation,\ shapeareamax,shapeareamax0,shapeareamin,shapeareamin0,\ shapeaverage,shapeaverage0,shapemedian,shapemedian0,\ shapemin,shapemin0,shapemax,shapemax0,shapeprevalent,\ softburn,softdodge,softlight,screen,stamp,subtract,value,\ vividlight,xor} m "_blend_custom_formula : f. \"a = i#0/255; b = i#1/255; 255*cut(($5),0,1)\"" if !$2 repeat int($!/2) { l[$>,{$>+1}] { rv blend $mode,{$3%} } } # Two-by-two. elif $2==1" && "$!>1 blend[^0] [0],$mode,{$3%},0 rm[0] # Top layer is top for all blends. elif $2==2" && "$!>1 blend[^-1] .,$mode,{$3%},1 rm. # Bottom layer is bottom for all blends. fi um _blend_custom_formula fx_blend_preview : fx_blend $"*" if $4 append_tiles , fi #@gui Colors to Layers : fx_split_colors, fx_split_colors_preview(1) #@gui : Color Tolerance = float(50,0,256) #@gui : Maximum Number of Output Layers = int(16,2,256) #@gui : Minimal Area (%) = float(1,0,100) #@gui : Autocrop Output Layers = bool() #@gui : sep = separator() #@gui : note = note{"Note: This filter decomposes an image into several layers each with #@gui : a single color + a residual layer (if any). #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/11/03.") fx_split_colors : skip ${2=0} to_rgb foreach { nm=${-gui_layer_name} pos=${-gui_layer_pos} min_area:=max(1,w*h*$3%) split_colors $1,$2,$min_area =>[^] "name("$nm"),pos("$pos")" if $4 gui_autocrop_layers fi } fx_split_colors_preview : foreach { +fx_split_colors ${1-4} drgba foreach { to ${arg\ {1+!!$>},"Original","#"$>},1,1,43,7,1,255 } frame xy,1,0 frame xy,3,255 to_rgba append_tiles , } #@gui Fade Layers : fx_fade_layers, fx_fade_layers_preview : + #@gui : Inter-Frames = _int(10,2,100) #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter needs at least two layers to work properly. Set the Input layers option to handle #@gui : multiple input layers. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/04/08.") fx_fade_layers : if $!<2 return fi to_colormode 0 r ${-max_wh},1,100%,0,0,0.5,0.5 a z r 100%,100%,{(d-1)*$1+1},100%,3 s z fx_fade_layers_preview : if $!<2 return fi to_colormode 0 r ${-max_wh},1,100%,0,0,0.5,0.5 k[0,1] + / 2 #@gui Layers to Tiles : append_tiles, fx_append_tiles_preview(1) : * #@gui : X-Tiles = int(0,0,256) #@gui : Y-Tiles = int(0,0,256) #@gui : note = note("For both parameters, 0 means automatic.") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_append_tiles_preview : frame xy,1,0,0,0,255 append_tiles $1,$2 #@gui Morph Layers : fx_morph_layers, gui_no_preview : * #@gui : Inter-Frames = _int(10,2,100) #@gui : Smoothness = _float(0.2,0,2) #@gui : Precision = _float(0.1,0,2) #@gui : Revert Layers = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_morph_layers : if ${4=0} _fx_revert_layers fi to_rgb morph $1,$2,$3 #@gui Multiscale Operator : fx_apply_multiscale, fx_apply_multiscale_preview(1) #@gui : Number of Scales = int(4,2,16) #@gui : sep = separator() #@gui : Starting Scale (%) = float(25,0,400) #@gui : Ending Scale (%) = float(100,0,400) #@gui : Non-Linearity = float(0,-1,1) #@gui : Rescaling = choice(3,"Block","Linear","Cubic","Lanczsos") #@gui : sep = separator() #@gui : X-Centering = float(0.5,0,1) #@gui : Y-Centering = float(0.5,0,1) #@gui : Angle = float(0,-180,180) #@gui : sep = separator() #@gui : Enable Interpolated Motion = bool(0) #@gui : Ending X-Centering = float(0.5,0,1) #@gui : Ending Y-Centering = float(0.5,0,1) #@gui : Ending Angle = float(0,-180,180) #@gui : sep = separator() #@gui : G'MIC Operator = text("") #@gui : Return Scaling = choice("None","Block","Linear","Cubic","Lanczos") #@gui : Lock Return Scaling to Source Layer = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/30/03.") fx_apply_multiscale : skip "${13=}" foreach { w0,h0:=w,h apply_scales "$13",$1,$2%,$3%,{10^$4},{arg(1+$5,1,3,5,6)} if $8" || "($9" && "$8!=$12) to_a N=$! repeat $! { angle:=$9?$8+($12-$8)*$>/max($N-1,1):$8 rotate[$>] $angle } fi if $14 if $15 siz=$w0,$h0 else siz=${-max_wh} fi r $siz,1,100%,{arg($14,1,3,5,6)} c 0,255 fi w=${-max_w} h=${-max_h} N=$! repeat $! { cx:=$9?$6+($10-$6)*$>/max($N-1,1):$6 cy:=$9?$7+($11-$7)*$>/max($N-1,1):$7 gui_set_layer_pos[$>] {$>,($w-w)*$cx},{$>,($h-h)*$cy} } } fx_apply_multiscale_preview : foreach { fx_apply_multiscale $"*" N:=int(sqrt($!)) N:=round($!/$N,1,1) rs ,{100/$N}% to_rgba max_wh=${-max_wh} N=$! foreach { cx:=$9?$6+($10-$6)*$>/max($N-1,1):$6 cy:=$9?$7+($11-$7)*$>/max($N-1,1):$7 r $max_wh,1,100%,0,0,$cx,$cy 0 text. "#"{1+$>}" ",1,1,24,1,255 +dilate. 5 to_rgba[1] j[0] [1],2,0,0,0,1,[2],255 k[0] } frame xy,1,0 frame xy,3,255 append_tiles , } #@gui Pack : fx_pack, fx_pack_preview(1) : * #@gui : Order By = choice(2,"Width","Height","Maximum Dimension","Area","Name") #@gui : Tends to Be Square = bool(1) #@gui : Force Transparency = bool(1) #@gui : Add Image Label = bool(0) #@gui : Font Height (px) = float(16,0,64)_0 #@gui : Font Colors = choice(1,"White on black","Black on white")_0 #@gui : sep = separator() #@gui : Output Coordinates File = _bool(0) #@gui : Output Folder = _folder() #@gui : sep = separator() #@gui : note = note{"This filter tries to pack all input layers into a single image, while trying to #@gui : minimize the empty areas. #@gui : This problem being NP-hard, the algorithm finds (of course) a non-optimal, but often acceptable #@gui : solution to this packing problem."} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2019/03/20.") fx_pack : skip "${8=}" if $4 to_rgba foreach { nm0={n} gui_layer_name nm=${} 0 t. {``$nm},3,0,$5,1,1 frame. 1,1,0 if $6 *. -1 fi n. 0,255 to_rgba. r. {[w+2,h+1,1,4]},0,0,0,1 rv a y,{w#0>w?0.5:0} => $nm0 } fi if $3 to_a fi repeat $! { gui_layer_name[$>] nm$>=${} =>[$>] {`lowercase(['${nm$>}'])`} } c0="w" c1="h" c2="max(w,h)" c3="w*h" c4="n" pack $2,${c$1} coords=${} if $7 repeat 256 { filename "$8/gmic_pack.txt",$> filename=${} if isfile(['{/$filename}']) else break fi } if !narg($filename) filename="$8/gmic_pack.txt" fi l[] { repeat narg($coords)/2 { x,y:=arg(1+2*$>,$coords),arg(2+2*$>,$coords) ('"Image ""#"{1+$>}" ("${nm$>}"): "$x,$y\n') } a x ot $filename rm } fi => "name(G'MIC packing),pos(0,0),mode(normal)" if $4 autocrop fi fx_pack_preview : skip "${8=}" if !$! return fi w,h={w},{h} filled=0 repeat $! { filled={$>,$filled+w*h} } fx_pack $1,$2,$3,$4,$5,$6,0 area:=wh to_rgba rs $w,$h i[0] $w,16,1,4,255 t[0] "Filled: "{round(100*$filled/$area)}%,3,1,14,1,0,0,0,255 a y,0.5 u "{$1}{$2}{$3}{$4}"\ "{$5}_"{2*$4}\ "{$6}_"{2*$4}\ "{$7}{$8}" #@gui Spatial Blend Multi-Layers : fx_smbl, fx_sbml_preview(1) : * #@gui : P0 = point(0,50,0,0,255,0,0,255,10)_0 #@gui : P1 = point(25,50,0,0,255,111,64,255,7)_0 #@gui : P2 = point(50,50,0,0,255,191,127,255,7)_0 #@gui : P3 = point(75,50,0,0,255,238,191,255,7)_0 #@gui : P4 = point(100,50,0,0,255,255,255,255,10)_0 #@gui : pP0 = value(0,50) #@gui : pP4 = value(100,50) #@gui : Sigma (%) = float(5,0,50) #@gui : Reverser order = bool() #@gui : sep = separator() #@gui : note = note{"Note: This filter requires several input layers. It creates a spatial blending of all \ # layers, oriented along the specified axis shown in the preview..\n\n\ # For N input frames, the colored dots corresponds respectively to frames \ # \#1 (Red), \#N/4 (Dark Orange), \#N/2 (Orange), 3*\#N/4 (Yellow) and \ # \#N (White).\n\n\ # Move the dots on the preview widget to modify the geometry of the spatial blending."} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2024/03/11.") fx_smbl : if $!<1 return fi if $14 rv fi # Compute position of indices on segment. d1,d2,d3:=" F = [ w,h ]%; P0 = [ $1,$2 ]*F; P1 = [ $3,$4 ]*F; P2 = [ $5,$6 ]*F; P3 = [ $7,$8 ]*F; P4 = [ $9,$10 ]*F; P0P4 = P4 - P0; U = unitnorm(P0P4); n = norm(P0P4); round([ dot(P1 - P0,U), dot(P2 - P0,U), dot(P3 - P0,U) ]*1023/n)" function1d 1,0,0,$d1,0.25,$d2,0.5,$d3,0.75,1023,1 n. -0.5,{$!-1.5} # Spatially blend all frames together. [0],[0],1,3," begin(const N = ($!) - 1; const sigma = max($13*N%,0.05)); # Find float-valued index. F = [ w,h ]%; P0 = [ $1,$2 ]*F; P4 = [ $9,$10 ]*F; P0P4 = P4 - P0; U = unitnorm(P0P4); n = norm(P0P4); z = i(#-1,dot([x,y] - P0,U)/n*w#-1,0,0,0,1,1); res = sum_weights = 0; repeat (N,k, weight = gauss(k - z,sigma); res+=weight*i(#k,x,y,0,c); sum_weights+=weight; ); res/sum_weights" k. fx_sbml_preview : # Ensure control points stay ordered on the line. x0,y0,x1,y1,x2,y2,x3,y3,x4,y4:=" F = [ w,h ]%; P0 = [ $1,$2 ]*F; P1 = [ $3,$4 ]*F; P2 = [ $5,$6 ]*F; P3 = [ $7,$8 ]*F; P4 = [ $9,$10 ]*F; pP0 = [ $11 ]*F; pP4 = [ $12 ]*F; P0P4 = P4 - P0; pP0!=P0 || pP4!=P4?( # Extremities have changed pn = norm(pP4 - pP0); P1 = P0 + norm(P1 - pP0)*P0P4/pn; P2 = P0 + norm(P2 - pP0)*P0P4/pn; P3 = P0 + norm(P3 - pP0)*P0P4/pn; ); U = unitnorm(P0P4); n = norm(P0P4); nmin = 0.01*n; nmax = 0.99*n; d1 = cut(dot(P1 - P0,U),nmin,nmax); d2 = cut(dot(P2 - P0,U),nmin,nmax); d3 = cut(dot(P3 - P0,U),nmin,nmax); nP1 = P0 + kth(1,d1,d2,d3)*U; nP2 = P0 + kth(2,d1,d2,d3)*U; nP3 = P0 + kth(3,d1,d2,d3)*U; [ P0/F,nP1/F,nP2/F,nP3/F,P4/F ]" fx_smbl $x0,$y0,$x1,$y1,$x2,$y2,$x3,$y3,$x4,$y4,"0","0",${13--1} line $x0%,$y0%,$x4%,$y4%,1,0x55555555,0 line $x0%,$y0%,$x4%,$y4%,1,0xCCCCCCCC,255 u "{"$x0,$y0"}{"$x1,$y1"}{"$x2,$y2"}{"$x3,$y3"}{"$x4,$y4"}{$1,$2}{$9,$10}{$13}{$14}" #@gui Stroke : fx_stroke, fx_stroke_preview(0) #@gui : Thickness (px) = int(3,1,256) #@gui : Threshold (%) = float(50,0,100) #@gui : Smoothness (px) = float(0,0,10) #@gui : Shape = choice(2,"Square","Diamond","Round") #@gui : Direction = choice(1,"Inward","Outward") #@gui : sep = separator() #@gui : Zoom (%) = float(100,1,300) #@gui : X-Shift (px) = int(0,-256,256) #@gui : Y-Shift (px) = int(0,-256,256) #@gui : sep = separator() #@gui : Starting Color = color(255,255,255,255) #@gui : Ending Color = color(255,255,255,255) #@gui : Inside Color = color(0,0,0,0) #@gui : Outside Color = color(0,0,0,0) #@gui : sep = separator() #@gui : Output Stroke Layer On = choice(1,"Bottom","Top") #@gui : Keep Original Image Size = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/24/06.") fx_stroke : to_a foreach { nm={n} if !$26" && "$5 expand xy,$1 is_frame1=0 else expand xy,1 is_frame1=1 fi split_opacity +l. { b $3 if $6>=100 shift $7,$8 if $6!=100 wh:=w,h r $6%,$6%,1,1,3 r $wh,1,1,0,0,0.5,0.5 fi else if $6!=100 wh:=w,h r $6%,$6%,1,1,3 r $wh,1,1,0,0,0.5,0.5 fi shift $7,$8 fi > {99.99-min(99.99,$2)}% distance $5,$4 ($9^$10^$11^$12) if $1>1 ($13^$14^$15^$16) a[-2,-1] x r. $1,1,1,4,3 c. 0,255 fi i.. ($21^$22^$23^$24) ($17^$18^$19^$20) if $5 rv[-3,-1] fi a[-3--1] x map.. .,1 rm. } a[0,1] c if $is_frame1 shrink xy,1 fi if $25 rv fi =>[^] $nm } fx_stroke_preview : foreach { fx_stroke $* => foo gui_merge_layers } #@gui Tiles to Layers : fx_tiles2layers, fx_tiles2layers_preview(1) #@gui : X-Tiles = int(3,1,100) #@gui : Y-Tiles = int(3,1,100) #@gui : Force Tiles to Have Same Size = _bool(false) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_tiles2layers : split_tiles $1,$2,$3 fx_tiles2layers_preview : split_tiles $1,$2,$3 to_rgba frame xy,1,0,0,0,255 frame xy,3,0,0,0,0 append_tiles , #@gui Tones to Layers : fx_tones2layers, fx_tones2layers_preview(0) #@gui : Number of Tones = int(3,2,10) #@gui : Start of Mid-Tones = int(85,0,255) #@gui : End of Mid-Tones = int(170,0,255) #@gui : Smoothness = float(0.5,0,5) #@gui : Alpha = choice("Binary","Scalar") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/05/04.") fx_tones2layers : sval=$2 eval:=max($2,$3) remove_opacity foreach { nm={n} +luminance rv repeat $1-1 { [1] val0:=$sval+($eval-$sval)*$>/($1-2) val1:=$sval+($eval-$sval)*($>+1)/($1-2)-1 +ir[0] $val0,$val1 if $5 *. [0] b. $4% n. 0,255 # Scalar alpha. else b. $4% n. 0,255 # Binary alpha. fi a[-2,-1] c } rm[0] rv =>[^] $nm } fx_tones2layers_preview : fx_tones2layers $* rv r {100/$!}%,{100/$!}%,1,100%,2 to_rgba frame xy,1,0,0,0,255 frame xy,3,0,0,0,0 append_tiles , #@gui ____Lights & Shadows #--------------------------------- #@gui Burn : fx_burn, fx_burn_preview(1) #@gui : Amplitude = float(0.5,0,1) #@gui : Scale = float(30,1,100) #@gui : Smoothness = float(1,0,4) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : Value Action = choice("None","Cut","Normalize") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/24/11.") _fx_burn : foreach { w,h={w},{h} +norm fx_fourier_old. 2 +rows. 0,{$h-1} r. $2%,$2%,1,100%,0,0,0.5,0.5 b. $3% j.. .,{($w-w)/2},{($h-h)/2} rm. fx_fourier_old[-2,-1] 2 blend overlay,$1 } fx_burn : ac "_fx_burn ${1-3}",$4,$5 fx_burn_preview : gui_split_preview "fx_burn ${^0}",${-3--1} #@gui Contrast Swiss Mask : fx_contrast_swm , fx_contrast_swm(0) #@gui : sep = separator() #@gui : Blur the Mask = float(2,0.5,10) #@gui : sep = separator() #@gui : note = note ("Contrast Mask need the negative of the mask") #@gui : Skip to Use the Mask to Boost = bool(false) #@gui : note = note ("Uncheck for Contrast Mask,Check for Contrast Boost") #@gui : sep = separator() #@gui : note = note("Merge the Mask") #@gui : Intensity = float(1,0,1) #@gui : sep = separator() #@gui : note = note("Author: PhotoComiX.      Latest Update: 2011/01/01.") #@gui : url = link("Filter explained here","http://www.gimpchat.com/viewtopic.php?f=9&t=864") fx_contrast_swm : foreach { split_opacity l[0] { +luminance to_rgb b $1 if !$2 negate[1] fi rv blend hardlight,$3 } a c } #@gui Drop Shadow : fx_drop_shadow, fx_drop_shadow_preview(1) #@gui : X-Offset = float(3,-20,20) #@gui : Y-Offset = float(3,-20,20) #@gui : Smoothness = float(1.8,0,5) #@gui : X-Curvature = float(0,-2,2) #@gui : Y-curvature = float(0,-2,2) #@gui : Color = color(0,0,0) #@gui : Expand size = bool(1) #@gui : Output as separate layers = _bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/09/02.") fx_drop_shadow : foreach { drop_shadow $1%,$2%,$3%,$4,$5,$9,1 to_rgba.. sh.. 0,2 fc. ${6-8} rm. if $10 rv else blend alpha fi } fx_drop_shadow_preview : fx_drop_shadow ${1-9},0 #@gui Drop Shadow 3D : fx_drop_shadow3d, fx_drop_shadow3d_preview(1) #@gui : X-Angle = float(0,-90,90) #@gui : Y-Angle = float(0,-90,90) #@gui : Z-Angle = float(0,-90,90) #@gui : Zoom = float(0,-100,100) #@gui : X-Offset = float(1,-50,50) #@gui : Y-Offset = float(1,-50,50) #@gui : Perspective = float(2,0,10) #@gui : Smoothness = float(0.5,0,5) #@gui : Color = color(0,0,0,200) #@gui : Preview Only Shadow = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/02/07.") fx_drop_shadow3d : foreach { +_fx_drop_shadow3d $* } fx_drop_shadow3d_preview : foreach { if $13 _fx_drop_shadow3d $* else +_fx_drop_shadow3d $* rv blend alpha fi } _fx_drop_shadow3d : point3d 0,0,1 r3d. 1,0,0,$1 r3d. 0,1,0,$2 r3d. 0,0,1,$3 u,v,w:=i(0,8),i(0,9),i(0,10) rm. to_a channels 100% if im==iM return fi +f "X = x/w-0.5; Y = y/h-0.5; A = ($7-$4*$7/100)*$w/(X*$u+Y*$v+$7*$w); A<0?1e8:A" +*. 'y/h-0.5' *.. 'x/w-0.5' +.. {0.5-$5/100} +. {0.5-$6/100} *.. {w} *. {h} a[-2,-1] c warp[0] .,0,1,0 rm. b $8% n 0,$12 i.. ($9^$10^$11) r.. .,.,1,3 a[-2,-1] c #@gui Equalize Light : fx_equalize_light, fx_equalize_light_preview(1) #@gui : Amount (%) = float(75,0,100) #@gui : Mode = choice("Preserve range","Preserve covariance") #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/03/23.") fx_equalize_light : ac "_fx_equalize_light $1,$2",$3,1 _fx_equalize_light : foreach { split_opacity l[0] { . +b. {max(0.1,100-$1)}% -[-2,-1] if $2 match_pca. .. else n. ..,.. fi rm.. } a c } fx_equalize_light_preview : gui_split_preview "fx_equalize_light $*",${-3--1} #@gui Equalize Shadow : fx_equalize_shadow, fx_equalize_shadow_preview(1) #@gui : Amplitude = float(1,0,1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: Francois Grassard and David Tschumperlé. #@gui :       Latest Update: 2021/03/23.") fx_equalize_shadow : foreach { +negate blend softlight,$1 } fx_equalize_shadow_preview : gui_split_preview "fx_equalize_shadow $1",${-3--1} #@gui Guided Light Rays : fx_guided_lightrays,fx_guided_lightrays_preview(1) : + #@gui : Amplitude (%) = float(10,0,100) #@gui : Ray Length = float(2,0,2) #@gui : Mode = choice("Boundary","Dense") #@gui : Density (%) = float(80,0,100) #@gui : Smoothness (%) = float(0.1,0,5) #@gui : Threshold (%) = float(50,0,100) #@gui : Light Position = point(50,50,0,1,255,255,0,-128,1%) #@gui : Light Color = color(255,255,255) #@gui : Blend Mode = choice(7,"Add","Alpha","Grain Merge","Hard Light","Lighten","Lightness", #@gui : "Luminance","Overlay","Soft Light","Value") #@gui : Opacity (%) = float(100,0,100) #@gui : sep = separator() #@gui : Preview Light Shape = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/04/06.") fx_guided_lightrays : bmode=${"arg0 $12,add,alpha,grainmerge,hardlight,lighten,lchlightness,\ luminance,overlay,softlight,value"} if $3 cond="i" else cond="i && !(j(-1) && j(1) && j(0,-1) && j(0,1))" fi if 0$_is_preview" && "$14 i[1] [0] gui_set_layer_opacity[1] 50 fi l[0] { # Process top layer only (light shape). if s==2" || "s==4 channels 100% else compose_channels max fi ge $6% # Draw lightrays. 100%,100% eval.. "* const L = $2<1?$2:$2^5; const Xl = $7<0?3*$7:$7>100?100+3*($7-100):$7; const Yl = $8<0?3*$8:$8>100?100+3*($8-100):$8; const xl = Xl*(w - 1)/100; const yl = Yl*(h - 1)/100; "$cond" && u<=$4%?( u = x - xl; v = y - yl; polygon(#-1,2,xl,yl,xl + L*u,yl + L*v,-1,255); )" equalize. 65536,1,{iM} n. 0,1 power:=10^(-$1%) ^. {max(1e-2,$power)} b. $5% n. 0,255 i[-2] (${9-11}:cyzx) r.. .,.,1,3 a[-2,-1] c k. gui_set_layer_mode $bmode gui_set_layer_opacity $13 } if 0$_is_preview gui_merge_layers elif 0$_output_mode k[0] fi fx_guided_lightrays_preview : _is_preview=1 fx_guided_lightrays $* #@gui Illuminate 2D Shape : fx_illuminate_shape2d,fx_illuminate_shape2d_preview(1)+ #@gui : note = note("Input / Output:) #@gui : Input Type = choice{"Single Opaque Shapes Over Transp. BG","Multiple Colored Shapes Over Transp. BG", #@gui : "Bump Map","Normal Map"} #@gui : Output Type = choice{"Illumination","Bump Map","Normal Map"} #@gui : Input Guide Color = color(255,0,0,255) #@gui : Keep Base Layer as Input Background = bool(1) #@gui : Keep Transparency in Output = bool(1) #@gui : sep = separator() #@gui : note = note("Shape:) #@gui : Minimal Shape Area = int(4,1,100) #@gui : note = note{"Parameter Minimal shape area is only active in Multiple colored shapes #@gui : input mode."} #@gui : Preview Detected Shapes = bool(0) #@gui : Erosion / Dilation = float(0,-10,10) #@gui : Smoothness = float(3,0,6) #@gui : Bump Factor = float(1,-5,5) #@gui : Std / Max Weight = float(0.5,0,1) #@gui : Resolution = choice{4,"Full (Slower)","2048","1024","512","256","128","64 (Faster)"} #@gui : sep = separator() #@gui : note = note("Illumination:) #@gui : Blending Mode = choice(10,"Normal","Lighten","Screen","Dodge","Add","Darken","Multiply","Burn","Overlay", #@gui : "Soft Light","Hard Light","Grain Merge") #@gui : Opacity (%) = float(75,0,100) #@gui : Ambient (%) = float(30,-100,100) #@gui : Diffuse (%) = float(40,0,200) #@gui : Specular (%) = float(40,0,300) #@gui : Shininess = float(80,0,100) #@gui : Smoothness = float(0.2,0,5) #@gui : Flatness = float(1,0,3) #@gui : Linearity = float(0,-100,100) #@gui : Levels = int(0,0,16) #@gui : Light-X = float(2,-20,20) #@gui : Light-Y = float(-2,-20,20) #@gui : Light-Z = float(2,0,20) #@gui : Normalize Illumination = bool(0) #@gui : sep = separator() #@gui : Open Interactive Preview = button() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : sep = separator() #@gui : note = note{"Note: This filter automatically adds illumination to an opaque shape defined #@gui : over a transparent background."} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/05/18.") fx_illuminate_shape2d : input_type,\ output_type,\ keep_input_bg,\ preview_shapes,\ blending_mode,\ opacity=$1,$2,$7,$10,$16,$17 blending_mode=${arg0\ $blending_mode,normal,lighten,screen,dodge,add,darken,multiply,burn,overlay,\ softlight,hardlight,grainmerge} keep_input_bg&=$!>1 if $output_type # Output : bumpmap or normalmap repeat $!-$keep_input_bg { __fx_illuminate_shape2d[$>] $* } else # Output : illumination repeat $!-$keep_input_bg { if $keep_input_bg sel=$>,-1 else sel=$> fi l[$sel] { if !$keep_input_bg" && "$input_type>=2 # From bumpmap/normalmap w/o background __fx_illuminate_shape2d $* elif $keep_input_bg" && "$input_type>=2 # From bumpmap/normalmap w/ background __fx_illuminate_shape2d[0] $* elif !$keep_input_bg" && "$input_type<=1 # From shape w/o background +__fx_illuminate_shape2d $* rv else # From shape w/ background __fx_illuminate_shape2d[0] $* fi if !$preview_shapes" || "0$_is_preview!=1 gui_set_layer_mode[0] $blending_mode gui_set_layer_opacity[0] $opacity if $!>1" && "(!0$_output_mode" || "0$_is_preview==1) if $keep_input_bg" && "!0$_is_preview . fi gui_merge_layers[0,1] else k[0] fi fi } } fi fx_illuminate_shape2d_preview : _is_preview=1 input_type,\ keep_input_bg,\ preview_interactive=$1,$7,$-2 keep_input_bg&=$!>1 if $preview_interactive fx_illuminate_shape2d_preview_interactive $* fi if $keep_input_bg repeat $!-1 { l[$>,-1] { fx_illuminate_shape2d $* rv to_colormode 0 a z gui_split_preview "slices 50%,100%",$-1 } } else gui_split_preview "fx_illuminate_shape2d $*",$-1 fi fx_illuminate_shape2d_preview_interactive : _output_mode=0 input_type,\ keep_input_bg=$1,$7 keep_input_bg&=$!>1 repeat $!-$keep_input_bg { if $keep_input_bg sel=$>,-1 else sel=$> fi +l[$sel] { to_rgba +__fx_illuminate_shape2d[0] $1,2,${3-6},0,1,""$9,0,${11-15},""${16-29},""0,0 if $!>2 rm[0] elif $input_type>=2 sh[0] 0,2 f. 128 rm. fi siz=${fitscreen\ {[w,h,1]},256,640} wsiz0=${fitscreen\ $siz,1,30%,100%} wsiz=$wsiz0 r $siz,1,100%,3 s. c,-3 !=. 0 l.. { - 128 / 127 s c,-2 / } a[-2,-1] c # Gradient map rv s. c,-3 (64,96;96,64) r. 24,24 r. ..,..,1,3,0,2 30,30,1,1 circle. 50%,50%,15%,1,1 b. 4 n. 0,1 100%,100%,1,3,[255,255,0] => normal,rgb,alpha,background,light_alpha,light_rgb w[] $wsiz,0,0,{rgb,([{*,u},{*,v}]-[$wsiz])/2},"[G'MIC] Illuminate 2D Shape" cursor 0 x0,y0,ox,oy,ob,olightz=-1 lightz=2 clicked=0 do x,y,b,mw={rgb,[{*,x},{*,y}]*[w,h]/[{*,w},{*,h}]},{*,b},{*,-o} lightz:=cut($lightz-0.3*sign($mw)+($y0>=0?3*($y-$y0)/h),0.1,4) if $x<0 x,y={rgb,ang=$|;(1+[cos(1.4*ang),sin(0.85*ang)])*[w,h]/2} fi if !$b" || "($b&1) if $b" && "!$clicked x0,y0=$x,$y elif !$b x0,y0=-1 fi lightx,lighty={rgb,3.5*(2*[$x/w,$y/h]-1)} if [$ox,$oy,$ob,$olightz]!=[$x,$y,$b,$lightz] +fx_illuminate_shape2d[normal,rgb] 4,0,${3-6},1,1,""$9,0,${11-15},""${16-25},$lightx,$lighty,$lightz,$29,""0,0 +j[background] .,0,0,0,0,1,[alpha],255 rm.. +rs[light_alpha,light_rgb] {light_rgb,8+$lightz*(w-8)} j... .,{[$x,$y]-[w,h]/2},0,0,1,.. rm[-2,-1] r. $wsiz,1,100% to. "Light: ("{``{_round([$lightx,$lighty,$lightz],0.1)}}")",2,2,16 w. rm. wait 20 else wait fi clicked=$b elif $b&2 +j[background] [rgb],0,0,0,0,1,[alpha],255 +rs[light_alpha,light_rgb] {light_rgb,8+$lightz*(w-8)} j... .,{[$x,$y]-[w,h]/2},0,0,1,.. rm[-2,-1] w. rm. wait fi if {*,CTRLLEFT}" && "{*,-D} w[] {1.5*[{*,w},{*,h}]} wsiz={*,w,h} elif {*,CTRLLEFT}" && "{*,-C} w[] {0.75*[{*,w},{*,h}]} wsiz={*,w,h} elif {*,CTRLLEFT}" && "{*,-R} w[] $wsiz0 fi ox,oy,ob=$x,$y,$b while {*}" && "!{*,ESC}" && "!{*,Q} w 0 rm } } __fx_illuminate_shape2d : # Input selection must contains a single image. Output is a single image. input_type,\ output_type,\ gR,gG,gB,gA,\ keep_input_bg,\ keep_output_transparency,\ min_shape_area,\ preview_shapes,\ dilation,\ shape_smoothness,\ bump_factor,\ weight_avg_max,\ resolution,\ blending_mode,\ opacity,\ ambient,\ diffuse,\ specular,\ shininess,\ light_smoothness,\ flatness,\ linearity,\ levels,\ lightx,\ lighty,\ lightz,\ normalize_illumination,\ preview_interactive,\ preview_mode=${1-31} # Generate 2D binary shape and corresponding bumpmap nm={n} if !$input_type # Single opaque shape to_rgba +channels. 100% >. 0 . select_color... 0,$gR,$gG,$gB,$gA mv... $! -[-2,-1] elif $input_type==1 # Multiple colored shapes to_rgba +channels. 100% >. 0 *[-2,-1] if $min_shape_area>1 +quantize_area. {$min_shape_area^2} fi s. c,-{s-1} >. 0 rv[-2,-1] if s>1 f. "begin(A = resize([ 0,(s-1)/s ],s,3));I+A" norm. round. 0.01 fi label. 0,0 f. "j(1)!=i || j(0,1)!=i" thinning. 1 ==. 0 *. .. select_color... 0,$gR,$gG,$gB,$gA mv... $! -[-2,-1] elif $input_type==2 # Bump map to_a s c,-{s-1} >. 0 *.. . rv s. c S:=$!-1 +[^0] /. $S elif $input_type==3 # Normal map +channels 100% >. 0 *.. . rv f. "I==vector4(0)?[128,128,255,255]:I" channels. 0,2 else # Gradient map (hidden mode used by interactive preview) +channels 100% rv fi if 0$_is_preview" && "$preview_shapes if $input_type==3 k[0] else k. fi +dilate. 3 label_fg.. 0 srand 0 {-2,iM+1},1,1,3,'!x?[0,0,0]:x==1?[255,255,255]:u([255,255,255])' map... . rm. *. 255 a c return fi if $input_type<=1 shape2bump. {arg($resolution,2048,1024,512,256,128,64)},$weight_avg_max,{$dilation%*max(w,h)},\ $shape_smoothness fi if $input_type<=2 if $input_type==2" && "$shape_smoothness mM:=im,iM guided. ..,$shape_smoothness%,100 n. $mM fi *. $bump_factor fi # Generate output. if $output_type==1 # Output as a bump map if $input_type<=2 if $keep_output_transparency k[-2,-1] n 0,255 rv a c # With transparency else k. n 0,255 # Without transparency fi else rm gui_error_preview "Cannot convert a normal map to a bump map." return fi elif $output_type==2 # Output as a normal map if $input_type<=2 round 0.0001 bump2normal. f. "i(#-2)?I:[128,128,255]" fi if $keep_output_transparency k[-2,-1] rv *. 255 a c # With transparency else k. # Without transparency fi else # Output as illumination layer (phong model) if $input_type<=2 g. xy a[-2,-1] c elif $input_type==3 -. 128 /. 127 s. c,-2 /[-2,-1] fi f. "* begin( const flatness = "$flatness"; # Surface flatness const ka = "$ambient"%; # Ambient const kd = "$diffuse"%; # Diffuse const ks = "$specular"%; # Specular const alpha = "$shininess"; # Specularity const m1 = max(1,"$lightz"); const mwh1 = max(w,h) - 1; light = [ "m1*$lightx,m1*$lighty,-$lightz" ]; # Light position camera = [ 0,0,-"$lightz" ]; # Camera position ); res = i#0?( P = [ 2*x/mwh1 - 1,2*y/mwh1 - 1,0 ]; L = light - P; L/=norm(L); V = camera - P; V/=norm(V); N = -[ i0,i1,flatness ]; N/=norm(N); R = 2*dot(N,L)*N - L; res = ka + kd*dot(L,N) + ks*max(dot(R,V),0)^alpha; ):0; [ res,0 ]" channels. 0 *. 255 c. 0,255 if $light_smoothness" || "$linearity mM:=im,iM if $light_smoothness b. $light_smoothness% fi if $linearity n. 0,1 ^. {10^-($linearity%)} fi n. $mM fi if $levels quantize. $levels,1,1 fi if $normalize_illumination n. 0,255 fi rv[-2,-1] *. 255 a[-2,-1] c =>[^] $nm if !$keep_output_transparency remove_opacity. fi fi =>[^] $nm #@gui Light Glow : fx_lightglow, fx_lightglow_preview(0) #@gui : Density = float(30,0,100) #@gui : Amplitude = float(0.5,0,2) #@gui : Mode = choice(8,"Burn","Dodge","Freeze","Grain Merge","Hard Light","Interpolation","Lighten","Multiply", #@gui : "Overlay","Reflect","Soft Light","Stamp","Value") #@gui : Opacity = float(0.8,0,1) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/21/02.") _fx_lightglow : mode=${arg0\ $3,burn,dodge,freeze,grainmerge,hardlight,interpolation,lighten,multiply,overlay,reflect,\ softlight,stamp,value} repeat $! { +gradient_norm. >=. {100-$1}% distance. 1 ^. $2 *. -1 n. 0,255 blend $mode,$4 mv. 0 } fx_lightglow : ac "_fx_lightglow ${1-4}",$5 fx_lightglow_preview : gui_split_preview "fx_lightglow $*",${-3--1} #@gui Light Leaks : fx_light_leaks, fx_light_leaks_preview(1) #@gui : Leak Type = int(0,0,70) #@gui : Angle = float(0,-180,180) #@gui : X-Scale = float(1,1,10) #@gui : Y-Scale = float(1,1,10) #@gui : Hue = float(0,-180,180) #@gui : Opacity (%) = float(85,0,100) #@gui : Blend Mode = choice(2,"Normal","Lighten","Screen","Dodge","Add","Darken","Multiply","Burn","Overlay", #@gui : "Soft Light","Hard Light","Difference","Subtract","Grain Extract","Grain Merge","Divide","Hue","Saturation", #@gui : "Value") #@gui : Output as Separate Layers = _bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"This filter uses the free light leaks dataset available at :"} #@gui : url = link{"Lomo Light Leaks","http://www.photoshoptutorials.ws/downloads/mockups-graphics/lomo-light-leaks/"} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2024/02/27.") fx_light_leaks : l[] { input_cached gmic_lightleaks.gmz k[$1] if i(0,0,2)==1" && "i(0,1,2)==1 decompress_from_keypoints. , else rbf. 512,512,0,0,511,511 fi } mode=${arg0\ $7,normal,lighten,screen,dodge,add,darken,multiply,burn,overlay,softlight,hardlight,difference,\ subtract,grainextract,grainmerge,divide,hue,saturation,value} foreach[^-1] { pass. +r. {0,[w,h]},1,100%,5 rm.. rotate. $2,1,1,50%,50% if $3>1" || "$4>1 f. "w2 = w/2; h2 = h/2; X = x - w2; Y = y - h2; i(w2 + X/$3,h2 + Y/$4,0,c,1,0)" fi c. 0,255 if $5 rgb2hsv. sh. 0 +. $5 rm. hsv2rgb. fi if $8 nm=${gui_layer_name..} => {``name($nm),opacity($6),mode($mode)} rv else blend $mode,$6% fi } rm. fx_light_leaks_preview : gui_split_preview "fx_light_leaks ${1--5},0",${-3--1} #@gui Light Patch : fx_light_patch, fx_light_patch(0) #@gui : Density = int(5,2,30) #@gui : Darkness = float(0.7,0,1) #@gui : Lightness = float(2.5,1,4) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_light_patch : foreach { split_opacity ac[0] "light_patch $1,$2,$3",$4 a c } #@gui Light Rays : fx_lightrays, fx_lightrays(1) #@gui : Density = float(30,0,100) #@gui : Center (%) = point(50,50,0,1) #@gui : Length = float(1,0,1) #@gui : Attenuation = float(0.5,0,1) #@gui : Transparency = bool(0) #@gui : Color = color(255,255,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/09/01.") fx_lightrays : skip ${6=255},${7=$6},${8=$6} foreach { lightrays $1,$2%,$3%,$4,$5 if $6 channels -3,0 sh. 0,2 fc. ${7-9} rm. # Render with transparency else 100%,100%,1,3 fc. ${7-9} rv n. 0,1 * fi } #@gui Pop Shadows : fx_pop_shadows, fx_pop_shadows_preview(1) #@gui : Strength = float(0.75,0,1) #@gui : Scale = float(5,0,20) #@gui : Post-Normalize = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Authors: Morgan Hardwood and David Tschumperlé. #@gui :       Latest Update: 2017/03/05.") fx_pop_shadows : foreach { split_opacity l[0] { .x2 luminance.. negate.. imM:=im#-2,iM#-2 b.. $2% n.. $imM blend[0,1] overlay,$1 max if $3 n 0,255 fi } a c } fx_pop_shadows_preview : gui_split_preview "fx_pop_shadows $*",${-3--1} #@gui Relief Light : fx_light_relief, fx_light_relief(1) #@gui : Ambient Lightness = float(0.3,0,5) #@gui : Specular Lightness = float(0.2,0,2) #@gui : Specular Size = float(0.2,0,1) #@gui : Darkness = float(0,0,1) #@gui : Light Smoothness = float(1,0,5) #@gui : XY-Light = point(50,50,0,1,255,255,128,200,10) #@gui : Z-Light = float(5,0,20) #@gui : Z-Scale = float(0.5,0,3) #@gui : Opacity as Heightmap = bool(0) #@gui : Image Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_light_relief : b $11% light_relief ${1-5},{[$6,$7]%},${8-10} #@gui Shadow Patch : fx_shadow_patch, fx_shadow_patch(1) #@gui : Opacity = float(0.7,0,1) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_shadow_patch : foreach { split_opacity ac[0] "shadow_patch $1",$2 a c } #@gui Slice Luminosity : fx_slice_luminosity, fx_slice_luminosity_preview #@gui : Luminosity Type = choice(1,"Average RGB","Luminance","Lightness","Value") #@gui : Output As = _choice(1,"Mask","Masked Image") #@gui : Preview Type = choice(2,"Mask","Mask + Background","Image","Image + Background") #@gui : sep = separator() #@gui : note = note{"Slice 1 (shadows):"} #@gui : Activate Slice 1 = bool(1) #@gui : Starting Value = int(0,0,255) #@gui : Ending Value = int(64,0,255) #@gui : Starting Feathering = int(0,0,255) #@gui : Ending Feathering = int(0,0,255) #@gui : sep = separator() #@gui : note = note{"Slice 2 (low midtones):"} #@gui : Activate Slice 2 = bool(1) #@gui : Starting Value = int(64,0,255) #@gui : Ending Value = int(128,0,255) #@gui : Starting Feathering = int(0,0,255) #@gui : Ending Feathering = int(0,0,255) #@gui : sep = separator() #@gui : note = note{"Slice 3 (high midtones):"} #@gui : Activate Slice 3 = bool() #@gui : Starting Value = int(128,0,255) #@gui : Ending Value = int(192,0,255) #@gui : Starting Feathering = int(0,0,255) #@gui : Ending Feathering = int(0,0,255) #@gui : sep = separator() #@gui : note = note{"Slice 4 (highlights):"} #@gui : Activate Slice 4 = bool() #@gui : Starting Value = int(192,0,255) #@gui : Ending Value = int(255,0,255) #@gui : Starting Feathering = float(0,0,255) #@gui : Ending Feathering = float(0,0,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/22/09.") fx_slice_luminosity : remove_opacity foreach { to_rgb _fx_slice_luminosity $* if $2 i[0] [0] a[-2,-1] c fi rv } fx_slice_luminosity_preview : remove_opacity foreach { to_rgb _fx_slice_luminosity $* if !$3 rm[0] channels {s-1} elif $3==1 100%,100%,1,1,128 a[0,-1] c r. 100%,100%,1,4 blend alpha elif $3==2 a c else +. 96 c. 0,255 a c fi } _fx_slice_luminosity : if !$1 +compose_channels + /. 3 elif $1==1 +luminance elif $1==2 +srgb2lab8. channels. 0 else +compose_channels max fi if $4 +apply_curve[1] 0,{$5-$7-0.1},0,$5,255,$6,255,{$6+$8+0.1},0,512,0 fi if $9 +apply_curve[1] 0,{$10-$12-0.1},0,$10,255,$9,255,{$11+$13+0.1},0,512,0 fi if $14 +apply_curve[1] 0,{$15-$17-0.1},0,$15,255,$16,255,{$16+$18+0.1},0,512,0 fi if $19 +apply_curve[1] 0,{$20-$22-0.1},0,$20,255,$21,255,{$21+$23+0.1},0,512,0 fi rm[1] max[^0] #@gui ____Patterns #------------------------ #@gui Bayer Filter : rgb2bayer, rgb2bayer(0) #@gui : Starting Pattern = choice(0,"Red-Green","Blue-Green","Green-Red","Green-Blue") #@gui : Keep Colors = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") #@gui Box Fitting : fx_boxfitting, fx_boxfitting(0) #@gui : Minimal Size = int(3,1,32) #@gui : Maximal Size = int(0,0,32) #@gui : note = note("Note: Set Maximal size to 0 to allow any size #@gui : for the squares.") #@gui : Initial Density = float(0.25,0,1) #@gui : Minimal Spacing = int(2,1,16) #@gui : Transparency = bool(0) #@gui : sep = separator() #@gui : note = note("Note: This filter has been highly inspired by the work of Jared Tarbell, #@gui : described on the page:") #@gui : url = link("http://www.complexification.net/gallery/machines/boxFittingImg/") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/06/06.") fx_boxfitting : boxfitting ${1-4} if $5 to_rgba replace_color 0,0,0,0,0,255,0,0,0,0 fi #@gui Camouflage : fx_camouflage, fx_camouflage #@gui : Scale = int(9,2,12) #@gui : Levels = int(12,2,32) #@gui : Coherence = float(100,0,1000) #@gui : Color 1 = color(30,46,33) #@gui : Color 2 = color(75,90,65) #@gui : Color 3 = color(179,189,117) #@gui : Color 4 = color(255,246,158) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/26/10.") fx_camouflage : foreach { split_opacity l[0] { channels 0 r {w+16},{h+16},1,1,0 rand 0,16 amp=$3 do smooth {min(50,$amp)},0,1 amp-=50 while $amp>0 shrink. xy,8 n 1,$2 round repeat $1 { +area 0,0 <. {1+2^$>} inpaint[0] [1],0,3 rm. } +colormap 0 n.. 0,{w-1} 4,1,1,3,"col=[${4-15}];col[3*x,3]" r. ..,..,1,3,3 rm.. map.. . rm. } a c } #@gui Canvas : fx_canvas, fx_canvas_preview(0) #@gui : note = note{"First direction :"} #@gui : Amplitude = float(70,0,300) #@gui : Angle = float(45,0,180) #@gui : Sharpness = float(400,0,2000) #@gui : note = note{"\nSecond direction : "} #@gui : Activate Second Direction = bool(true) #@gui : Amplitude = float(70,0,300) #@gui : Angle = float(135,0,180) #@gui : Sharpness = float(400,0,2000) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_canvas : repeat $! { l. { if $4 ({cos($2*pi/180)}^{sin($2*pi/180)}) vector2tensor. r. ..,.. +smooth.. .,$1 rm.. sharpen. $3 c. 0,255 ({cos($6*pi/180)}^{sin($6*pi/180)}) vector2tensor. r. ..,.. smooth... .,$5 rm. sharpen.. $7 c.. 0,255 +[-2,-1] /. 2 else ({cos($2*pi/180)}^{sin($2*pi/180)}) vector2tensor. r. ..,.. smooth.. .,$1 rm. sharpen. $3 c. 0,255 fi } mv. 0 } fx_canvas_preview : gui_split_preview "fx_canvas $*",${-3--1} #@gui Canvas Texture : texturize_canvas, texturize_canvas(0) #@gui : Amplitude = float(20,0,256) #@gui : Fibrousness = float(3,0,20) #@gui : Emboss = float(0.6,0,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") #@gui Cracks : fx_cracks, fx_cracks_preview(0) #@gui : Density (%) = float(30,0,100) #@gui : Relief = bool(true) #@gui : Color = color(255,255,255,128) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/07.") fx_cracks : ac "cracks $1,$2,{$6/255},${3-5},255",$7 fx_cracks_preview : gui_split_preview "fx_cracks $*",${-3--1} #@gui Crystal : fx_crystal, fx_crystal_preview(0) #@gui : Density = float(50,0,100) #@gui : Smoothness = float(0.2,0,2) #@gui : Edges = float(20,0,100) #@gui : Fast Fill = bool(1) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/19/01.") fx_crystal : foreach { split_opacity l[0] { s:=s +gradient_norm >=. {(100-$3)/5} remove_pixels. {100-max(0.1,$1*$3%)}%,{is} * +norm if $4 ==. 0 inpaint_pde.. .,{lerp(0,100,$2/2)}%,1 rm. else !=. 0 a c do sigma:=0.5*(1+$2)^$> +b. $sigma sh[0,-1] $s max. .. rm[-2,-1] f. "W = i(x,y,z,$s); W<0.001 || W>=1?0:c<$s?i/W:1" if !iM rm[1] break fi sh. $s j[0] [1],0,0,0,0,1,[2] k[0] while 1 channels 0,{$s-1} fi } a c } fx_crystal_preview : gui_split_preview "fx_crystal $*",${-3--1} #@gui Crystal Background : fx_crystal_background, fx_crystal_background #@gui : Iterations = int(10,1,32) #@gui : Density (%) = float(25,0,100) #@gui : Random Seed = int(0,0,65535) #@gui : Opacity (%) = float(100,0,100) #@gui : Color = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/18/10.") fx_crystal_background : foreach { split_opacity l[0] { N:=2*max(3,round(ispercentage($2)?4*wh*$2:$2)) if $5 col="u([255,255,255])" else col="u(255)" fi srand $3 M:=max(w,h) 2,$N repeat $1 { rand. {-$M/2},{3*$M/2} polygon.. $N,{^},{-$4%},{$col} } rm. n 0,255 } a c } #@gui Halftone : fx_halftone, fx_halftone_preview(0) #@gui : note = note("Image parameters :") #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Smoothness = float(0,0,10) #@gui : sep = separator() #@gui : note = note("Halftone parameters :") #@gui : Number of Tones = int(5,2,32) #@gui : Size for Dark Tones = int(8,2,256) #@gui : Size for Bright Tones = int(8,2,256) #@gui : Shape = choice{5,"Square","Diamond","Circle","Square (Inv.)","Diamond (Inv.)","Circle (Inv.)"} #@gui : Smoothness = float(0.1,0,32) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/23/07.") fx_halftone : adjust_colors ${1-3},0,0,0,255 b $4 foreach { split_opacity halftone[0] ${5-9} a c } fx_halftone_preview : gui_split_preview "fx_halftone $*",${-3--1} #@gui Halftone [Generic] : fx_generic_halftone,fx_generic_halftone_preview(1)* #@gui : note = note("Global Settings:") #@gui : Background = choice("Black","White") #@gui : Base shape = choice(1,"Square","Disc") #@gui : Max Radius (%) = float(100,0,300) #@gui : Smoothness (%) = float(10,0,100) #@gui : Antialiasing = choice(1,"None","x1.25","x1.5","x2","x2.5") #@gui : Black & White = bool() #@gui : sep = separator() #@gui : note = note("Halftoning Shape:") #@gui : Shape = choice{4,"Custom (Top Layer)","Dots","Lines","Self","Spiral"} #@gui : Layout = choice("Regular","Semi-Regular","Random")_0 #@gui : Density (%) = float(75,0,100) #@gui : Smoothness (%) = float(1,0,100)_0 #@gui : Angle = float(0,0,360)_0 #@gui : sep = separator() #@gui : note = note{"Note: #@gui : When adding a top layer that defines a custom halftoning shape, #@gui : it is recommended to use 1px wide strokes, #@gui : for a better (and faster) result. #@gui : "} #@gui : Old parameters = value("-1") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé. #@gui :       Latest Update: 2022/11/07.") fx_generic_halftone : background,base_shape,max_radius,smoothness,antialiasing,bw_only,shape,\ shape_layout,shape_density,shape_smoothness,shape_angle=${1-11} afact:=arg0($antialiasing,1,1.25,1.5,2,2.5) if $shape sel=^ else sel=^0 fi foreach[$sel] { # For each image b {$smoothness/50}% if $bw_only luminance n 0,1 else n 0,1 s c fi # Generate halftoning shape. ws,hs:=round([w,h]*$afact) delta:=max(2,round($afact*(100-$shape_density))) # Inter-shape distance if !$shape # Custom (top layer) pass[0] 0 if {s==2||s==4} channels. 100% else l. { s c + } fi r. {0,[w,h]},1,1,0,0 r. $ws,$hs,1,1,3 b. $shape_smoothness ge. 0.5 (0,1,0;1,1,1;0,1,0) +dilate.. . rm.. rv[-2,-1] -[-2,-1] if $shape_density distance. 1 round. mod. $delta ==. 0 fi elif $shape==1 # Dots if !$shape_layout # Layout: Regular {round([$ws,$hs]/$delta)},1,1,1 r. $ws,$hs,1,1,4,0,0.5,0.5 elif $shape_layout==1 # Layout: Semi-Regular $ws,$hs noise_poissondisk $delta else # Layout: Random $ws,$hs,1,1,u<0.5*($shape_density%)^4 fi elif $shape==2 # Lines $ws,$hs,1,1,"const ang = $shape_angle°; const ca = cos(ang); const sa = sin(ang); !(round(sa*x-ca*y)%$delta)" elif $shape==3 # Self ++ b. {$shape_smoothness/50}% r. $ws,$hs,1,1,3 otsu. 256 (0,1,0;1,1,1;0,1,0) +dilate.. . rm.. rv[-2,-1] -[-2,-1] distance. 1 round. mod. $delta ==. 0 elif $shape==4 # Spiral $ws,$hs eval " # Create spiral const w2 = w/2; const h2 = h/2; const dt = 0.1°; const A = max(6,$delta)/(2*pi); const tmax = sqrt(w^2+h^2)/2/A; for (t = 0, t<=tmax, t+=dt, tdt = t + dt; ct = cos(t); st = sin(t); ctdt = cos(tdt); stdt = sin(tdt); r = A*t; xt0 = w2 + r*cos(t); yt0 = h2 + r*sin(t); xt1 = w2 + r*cos(tdt); yt1 = h2 + r*sin(tdt); polygon(2,xt0,yt0,xt1,yt1,1,1); )" fi +distance. 1,0 max_patch. 5 +==.. 0 mul[-2,-1] distance. 1,0 *[-2,-1] # Generate halftone image. foreach[^-1] { # For each image channel pass. 1 100%,100%,1,1,{$background?255:0} eval.. "* const col = !$background; i?( lum = i(#0,round(x*w#0/w),round(y*h#0/h)); $background?(lum = 1 - lum); R = round(i*lum*$max_radius%); $base_shape==1?( ellipse(#-1,x,y,R,R,0,1,col); ):( x0 = x - R; y0 = y - R; x1 = x + R; y1 = y + R; polygon(#-1,4,x0,y0,x1,y0,x1,y1,x0,y1,1,col); ) )" r. {0,[w,h]},1,1,2 k. } rm. a c if $antialiasing sqr fi n 0,255 } fx_generic_halftone_preview : background,base_shape,max_radius,smoothness,antialiasing,bw_only,shape,\ shape_layout,shape_density,shape_smoothness,shape_angle=${1-11} if !$shape" && "$!<2 gui_error_preview "Missing top layer with halftoning shape." _persistent= return fi if "['${1--2}']==['$-1'] && "narg($_persistent) # Avoid preview recomputation if parameters didn't change rm $_persistent else fx_generic_halftone $* if !$shape rm[0] fi +store _persistent fi gui_crop_resize_preview u "{$1}{$2}{$3}{$4}{$5}{$6}{$7}"\ "{"$shape_layout"}"_{$shape==1?2:0}\ "{"$shape_density"}"\ "{"$shape_smoothness"}"_{isin($shape,0,3)?2:0}\ "{"$shape_angle"}"_{isin($shape,2)?2:0}\ "{${1--2}}" #@gui Hearts : fx_hearts, fx_hearts_preview(0) #@gui : Density = float(2,0,30) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_hearts : ac "hearts $1",$2 fx_hearts_preview : gui_split_preview "fx_hearts $*",${-3--1} #@gui Lava : fx_lava, fx_lava_preview(0) #@gui : Perturbation = int(8,0,15) #@gui : Smoothness = float(5,0,100) #@gui : Scale = float(3,0,20) #@gui : Sharpness = float(0,0,1000) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/26/11.") fx_lava : foreach { split_opacity l[0] { norm 100%,100% plasma. 1,1,{16-$1} smooth. $2,0,1,$3,$3,0.8,90 * gradient_norm n 0,255 equalize map 3 sharpen $4 } a c } fx_lava_preview : gui_split_preview "fx_lava $*",${-3--1} #@gui Marble : fx_marble, fx_marble #@gui : Image Weight = float(.5,0,30) #@gui : Pattern Weight = float(1,0,30) #@gui : Pattern Angle = float(0,0,360) #@gui : Amplitude = float(0,0,1000) #@gui : Sharpness = float(.4,0,5) #@gui : Anisotropy = float(.6,0,1) #@gui : Alpha = float(.6,0,20) #@gui : Sigma = float(1.1,0,20) #@gui : Cut Low = float(0,0,100) #@gui : Cut High = float(100,0,100) #@gui : sep = separator() #@gui : note = note("Author: Preben Soeberg.      Latest Update: 2010/29/12.") fx_marble : foreach { split_opacity marble[0] {$1/10},{$2/10},$3,$4,$5,$6,$7,$8,$9%,$10% a c } #@gui Maze : fx_maze, fx_maze #@gui : Cell Size = int(24,1,256) #@gui : Thickness = int(1,1,10) #@gui : Masking = choice("None","Render on Dark Areas","Render on White Areas") #@gui : Preserve Image Dimension = bool(1) #@gui : Maze Type = choice("Dark Walls","White Walls") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/02/09.") fx_maze : foreach { w,h={w},{h} r. {100/$1}%,{100/$1}%,1,100%,2 if !$3 f. 1 elif $3==1 negate. fi maze_mask. $1 dilate. $2 *. 255 if !$5 negate. fi if $4 r. $w,$h,100%,100% fi } #@gui Mineral Mosaic : fx_mineral_mosaic,fx_mineral_mosaic(0) #@gui : Density = float(1,0,3) #@gui : Area = float(2,0,32) #@gui : Smoothness = float(1,0,10) #@gui : Shade Strength = float(100,0,255) #@gui : Shade Angle = float(0,0,360) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/01/02.") fx_mineral_mosaic : foreach { to_rgb +b $3 segment_watershed. $1 +norm. area. 0 +<=. {$2^2} inpaint.. . rm. label. +f[0] "!c?x:y" rv[-2,-1] +blend[-2,-1] shapeaverage,1,1 -[-3,-1] rm[0,-2] channels. 0,1 alpha:=$5*pi/180 sh. 0 *. {cos($alpha)} rm. sh. 1 *. {sin($alpha)} rm. compose_channels. + normalize_local. 1000 n. -$4,$4 + c 0,255 } #@gui Mosaic : fx_mosaic, fx_mosaic_preview(0) #@gui : Density (%) = float(50,0,100) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/19/07.") fx_mosaic : ac "foreach { split_opacity mosaic[0] $1 a c }",$2 fx_mosaic_preview : gui_split_preview "fx_mosaic $*",${-3--1} #@gui Op Art : fx_shapes,fx_shapes_preview(0) #@gui : Shape = choice{1,"Custom Layers","Circles","Squares","Diamonds","Triangles","Horizontal Stripes", #@gui : "Vertical Stripes","Balls","Hearts","Stars","Arrows","Truchet","Circles (Outline)","Squares (Outline)", #@gui : "Diamonds (Outline)","Triangles (Outline)","Hearts (Outline)","Stars (Outline)","Arrows (Outline)"} #@gui : Number of Scales = int(16,2,24) #@gui : Resolution = float(10,1,50) #@gui : Zoom Factor = _int(2,1,8) #@gui : Minimal Size = float(5,0,150) #@gui : Maximal Size = float(90,0,150) #@gui : Stencil Type = choice(0,"Black & White","RGB","Color") #@gui : Allow Angle = choice("0 deg.","90 deg.","180 deg.") #@gui : Negative = bool(1) #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : note = note{"Note: #@gui : If you set the parameter Shape to Custom layers, the different shapes used to map #@gui : the pixel intensities will be defined as #@gui : the Number of scales top layers of your image. Don't forget to set also Input layers to #@gui : All to be sure #@gui : these layers are passed to the filter. #@gui : "} #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/16/12.") fx_shapes : if $1 # Pre-defined shapes. remove_opacity foreach { if !$7 _fx_shapes $* * 255 elif $7==1 split_opacity to_rgb[0] s[0] c repeat 3 { _fx_shapes[$>] $* } *[0-2] 255 a[0-2] c a c else +_fx_shapes $* r[0] $3%,$3% r[0] [1],[1] * fi } else # Custom shapes. if $!<=$2 error[] "Command '$0': Some layers are missing in 'Custom layers' mode ("{$2+1}" expected at least, "\ $!" provided)." fi to_colormode[0-{$2-1}] ${max_s[0-{$2-1}]} remove_opacity[$2--1] repeat $!-$2 { l[0-{$2-1},{$2+$>}] { norm. w,h={w},{h} r. $3%,$3%,1,1,2 s:=$4*max(round($w/w),round($h/h)) r0,r1:=$s*[$5,$6]% repeat $2 { r:=round($r0+$>*($r1-$r0)/($2-1)) if $r +r[$>] $r,$r,1,100%,3 else 1,1 fi } r[-$2--1] $s,$s,1,100%,0,0,0.5,0.5 map_sprites[$2--1] $2,$8 } } rm[0-{$2-1}] fi fx_shapes_preview : if $1 foreach { w,h={w},{h} gui_split_preview "fx_shapes ${1-3},1,${5--2}",$-1 r $w,$h,1,100%,0,0,0.5,0.5 } else if $!>$2 repeat $!-$2 { l[0-{$2-1},{$2+$>}] { w,h={w},{h} +fx_shapes ${1-3},1,${5--2} rm.. r. $w,$h,1,100%,0,0,0.5,0.5 } } rm[0-{$2-1}] else gui_warning_preview "Missing input layers!" fi fi _fx_shapes : norm w,h={w},{h} r $3%,$3%,1,1,2 s:=(1+$10)*$4*max(round($w/w),round($h/h)) r0,r1:=$s*[$5,$6]% repeat $2 { r:=round($r0+$>*($r1-$r0)/($2-1)) if $r _fx_shapes{$1-1}[] $r,$s else 1,1 fi } r[-$2--1] $s,$s,1,1,0,0,0.5,0.5 if $9 rv[-$2--1] *[-$2--1] -1 +[-$2--1] 1 fi map_sprites $2,$8 if $10 r 50%,50%,1,1,2 fi _fx_shapes0 : shape_circle $1 _fx_shapes1 : $1,$1,1,1,1 _fx_shapes2 : $1,$1,1,1 = 1,50%,50% distance 1,1 < {$1/2} _fx_shapes3 : $2,$2,1,1,'x+y<=2*$1-1' _fx_shapes4 : $2,$1,1,1,1 _fx_shapes5 : $1,$2,1,1,1 _fx_shapes6 : ball $1,200 n 0,1 _fx_shapes7 : shape_heart 65 r $1,$1,1,1,2 >= 50% _fx_shapes8 : shape_star $1 _fx_shapes9 : arrow3d 0,0,0,1,0,0,15%,40%,30% col3d 1 *3d $1 c3d $2,$2 j3d. ..,50%,50%,0,1,2,0,0 rm.. +mirror y max _fx_shapes10 : S:=$2+1-($2%2) $S,$S,1,1,"X=x/(w-1);Y=y/(h-1);r=abs(0.5-sqrt(X^2+Y^2));a=atan2(y,x);r<0.1-0.17*(0.5-$1/$2)*sin(2*a)" +mirror xy max _fx_shapes11 : _fx_shapes0 $* expand xy,1 +erode 3 - _fx_shapes12 : _fx_shapes1 $* expand xy,1 +erode 3 - _fx_shapes13 : _fx_shapes2 $* expand xy,1 +erode 3 - _fx_shapes14 : _fx_shapes3 $* expand xy,1 +erode 3 - _fx_shapes15 : _fx_shapes7 $* expand xy,1 +erode 3 - _fx_shapes16 : _fx_shapes8 $* expand xy,1 +erode 3 - _fx_shapes17 : _fx_shapes9 $* expand xy,1 +erode 3 - #@gui Pack Ellipses : fx_pack_ellipses, fx_pack_ellipses_preview(0) #@gui : Min Radius (px) = float(3,1,256) #@gui : Max Radius (px) = float(20,1,256) #@gui : Radius Dilation/Erosion (px)= float(0,-10,10) #@gui : Min Isotropy (%) = float(30,0,100) #@gui : Max Isotropy (%) = float(100,0,100) #@gui : Isotropy Quantization = int(6,0,16) #@gui : Orientation = choice{1,"Isotropic (Circles Only)","Anisotropic (Along Contours)",\ # "Anisotropic (Orthogonal to Contours)"} #@gui : Region Analysis Size = int(3,0,10) #@gui : Background Color = color(0,0,0,255) #@gui : Render Resolution = _choice("x1","x2","x3","x4","x5","x6","x7","x8") #@gui : Preserve Image Size = _bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/03/29.") fx_pack_ellipses : radius_min,radius_max,radius_dilation,isotropy_min,isotropy_max,isotropy_levels,orientation_ellipse,\ region_analysis,bgR,bgG,bgB,bgA,render_factor,preserve_size=${1-14} isotropy_min,isotropy_max:="sort([ $isotropy_min,$isotropy_max ])/100" radius_min,radius_max:="sort([ $radius_min,$radius_max ])" render_factor+=1 foreach { progress -1 to_rgb # Init images. nm={n} => img 1,1,1,3 => set +structuretensors[img] 1 b. 1% eigen. f.. "l0 = sqrt(i0); l1 = sqrt(i1); [1 - (l1 - l0)/l1,0]" channels.. 0 if $isotropy_levels quantize.. $isotropy_levels,0,-1 fi n.. $isotropy_min,$isotropy_max =>.. isotropy if !$orientation_ellipse f[isotropy] 1 . elif $orientation_ellipse==1 +f. "[ atan2(-i0,i1),0 ]" else +f. "[ atan2(i1,i0),0 ]" fi channels. 0 => angles if $region_analysis +norm[img] b. 0.5% slic. {30*$region_analysis},0 g. xy,1 a[-2,-1] c norm. ge. 5% else 100%,100% fi => contours +==[contours] 0 => mask # Pack ellipses. nb_attempts=0 repeat inf { x0,y0,x1,y1:=" const N = 4*$radius_max; wm1 = w#$mask - 1; hm1 = h#$mask - 1; Pc = v([wm1,hm1]); P0 = Pc - N/2; P1 = Pc + N/2; [ cut(P0[0],0,wm1), cut(P0[1],0,hm1), cut(P1[0],0,wm1), cut(P1[1],0,hm1) ]" +z[mask] $x0,$y0,$x1,$y1 r. {[w,h]+2},1,1,0,0,0.5,0.5 distance. 0 x,y,r:=$x0+xM-1,$y0+yM-1,min($radius_max,iM-1.25) rm. if $r>$radius_min r2={isotropy,max(1,$r*i($x,$y))} ellipse[mask] $x,$y,$r,$r2,{angles,rad2deg(i($x,$y))},1,0 eval "da_push(#$set,[$x,$y,$r])" nb_attempts=0 else if narg($contours) +[mask] [contours] rm[contours] else nb_attempts+=1 if $nb_attempts>10 break fi fi fi if !($>%100) progress {mask,cut(400*(1-ia)^3,0$progress,100)} fi } rm[mask] # Render result. da_freeze[set] W,H={img,[w,h]} {img,round($render_factor*[w,h])} => mask eval[set] " const fact = $render_factor; r1 = fact*(i2 + $radius_dilation); r2 = max(1,r1*i(#$isotropy,i0,i1)); ellipse(#$mask,round(fact*i0),round(fact*i1),r1,r2,i(#$angles,i0,i1),1,y + 1); I" r[img] {mask,[w,h]},1,100%,3 blend[img] [mask],shapeaverage0 !=[mask] 0 *[mask] 255 a[img,mask] c if $preserve_size r[img] $W,$H,1,100%,2 fi k[img] 100%,100%,100%,4 fc. $bgR,$bgG,$bgB,$bgA rv blend alpha => $nm progress 100 } fx_pack_ellipses_preview : gui_split_preview "fx_pack_ellipses ${1-12},1,0" #@gui Pack Sprites : fx_pack_sprites, gui_no_preview #@gui : Number of Scales = int(5,1,16) #@gui : Minimal Scale (%) = float(25,1,100) #@gui : Allow Angle = choice(3,"0 deg.","180 deg.","90 deg.","Any") #@gui : Spacing = int(1,-16,16) #@gui : Precision = int(7,1,32) #@gui : sep = separator() #@gui : Masking = choice("No Masking","Mask as Bottom Layer") #@gui : Width = int(512,32,2048) #@gui : Height = int(512,32,2048) #@gui : note = note("Notes:\n - Parameters Width and Height are considered only when #@gui : No masking mode is selected.\n #@gui : - Set different sprites on different layers to pack multiple sprites at the same time.") #@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=bpg7CGH7vCM") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/24/06.") fx_pack_sprites : if $6 # With mask. if $!<2 error[] "Command '$0': Masking requires at least two input layers ! (please check that 'Input Layers' is correctly set)." fi foreach[^-1] { to_rgba split_opacity +!=[1] 0 *[0] . a c autocrop 0 } remove_empty[0--2] +channels. 100% channels. -4,0 mv. 0 pack_sprites[0--2] ${1-5} else # No masking foreach { to_rgba split_opacity +!=[1] 0 *[0] . a c autocrop 0 } remove_empty i[0] $7,$8,1,5 pack_sprites ${1-5} fi channels[0] 0,{0,s-2} #@gui Paper Texture : fx_paper, fx_paper_preview(0) #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_paper : ac "texturize_paper",$1 fx_paper_preview : gui_split_preview "fx_paper $*",${-3--1} #@gui Pills : fx_pills, fx_pills_preview #@gui : Mode = choice("Gray","RGB") #@gui : sep = separator() #@gui : note = note("Gray Settings:") #@gui : Cycles = float(4,0,32)_2- #@gui : Angle = float(0,0,360)_2 #@gui : sep = separator() #@gui : note = note("Red Settings:") #@gui : Cycles = float(4,0,32)_0- #@gui : Angle = float(0,0,360)_0 #@gui : sep = separator() #@gui : note = note("Green Settings:") #@gui : Cycles = float(4,0,32)_0- #@gui : Angle = float(0,0,360)_0 #@gui : sep = separator() #@gui : note = note("Blue Settings:") #@gui : Cycles = float(4,0,32)_0- #@gui : Angle = float(0,0,360)_0 #@gui : sep = separator() #@gui : note = note{"Note: This filter evaluates the awesome math formula proposed by Miloslav Číž #@gui : (a.k.a. DrummyFish) on #@gui : this Wikipedia page. #@gui : "} #@gui : sep = separator() #@gui : note = note("Authors: Miloslav Číž and David Tschumperlé. #@gui :       Latest Update: 2022/11/08.") fx_pills : $=arg n:=$1?4:2 foreach { w,h={w},{h} rm repeat $1?3:1 { $w,$h,1,1,"begin( const cyc = "${arg$n}"*pi; const ang = "${arg{1+$n}}"°; const M = max(w,h); R = rot(ang); ); x0 = lerp(-cyc,cyc,x/M); y0 = lerp(-cyc,cyc,y/M); x = x0*R[0] + y0*R[1]; y = x0*R[2] + y0*R[3]; sqrt(abs(sin(x + cos(y + sin(x + cos(y)))) * sin(y + cos(x + sin(y + cos(x))))))" n+=2 } n 0,255 a c } fx_pills_preview : fx_pills $* u "{$1}"\ "{$2}_"{$1?0:2}"{$3}_"{$1?0:2}\ "{$4}_"{$1?2:0}"{$5}_"{$1?2:0}"{$6}_"{$1?2:0}"{$7}_"{$1?2:0}"{$8}_"{$1?2:0}"{$9}_"{$1?2:0} #@gui Plaid : fx_plaid_texture,fx_plaid_texture(1) #@gui : Line = float(50,0,100) #@gui : Number of Angles = int(2,1,8) #@gui : Starting Angle = float(0,0,360) #@gui : Angle Range = float(90,0,360) #@gui : Smoothness = float(1,0,5) #@gui : Sharpen = float(300,0,1000) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/16/05.") fx_plaid_texture : foreach { w,h,s={w},{h},{s} rows $1% b $5% sharpen $6 r $w,$h,1,$s,2 +rotate[0] $3,1,2,50%,50% repeat $2-1 { +rotate[0] {$3+$4*($>+1)/($2-1)},1,2,50%,50% +[-2,-1] } rm[0] / $2 } #@gui Polka Dots : fx_polka_dots, fx_polka_dots(1) #@gui : Size = float(80,0,100) #@gui : Density = float(20,0.1,100) #@gui : First Offset = float(50,0,100) #@gui : Second Offset = float(50,0,100) #@gui : Angle = float(0,0,180) #@gui : Aliasing = float(0.5,0.1,1) #@gui : Shading = float(0.1,0.1,1) #@gui : Opacity = float(1,0,1) #@gui : Color = color(255,0,0,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_polka_dots : to_rgba polka_dots {$1*$2/100},${2--1} #@gui Random Color Ellipses : fx_color_ellipses, fx_color_ellipses(1) #@gui : Density = int(400,0,3000) #@gui : Radius = float(8,0,30) #@gui : Opacity = float(0.1,0.01,0.5) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_color_ellipses : color_ellipses $1,$2,$3 #@gui Random Pattern : fx_random_pattern, fx_random_pattern_preview(1) #@gui : Size = _int(1024,16,8192) #@gui : Min Detail Level = float(2,0,20) #@gui : Seed = float(4038,0,100000) #@gui : Randomize Seed = button() #@gui : sep = separator() #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/10/08.") _fx_random_pattern : if $4 srand _seed:=_v(100000) else _seed=$3 fi srand $_seed random_pattern $1,$1,$2 adjust_colors. ${5-9} mv. 0 fx_random_pattern : if 0$_output_mode rm fi _fx_random_pattern $* fx_random_pattern_preview : _fx_random_pattern {max($_preview_area_width,$_preview_area_height)},${2--1} k[0] rs $_preview_area_width,$_preview_area_height,2,1 to "Seed: \#"$_seed,5,5,5%,2 u "{$1}{$2}{"{$4?$_seed:$3}"}{0}{$5}{$6}{$7}{$8}{$9}" #@gui Random Rectangles : fx_random_rectangles, fx_random_rectangles(0) #@gui : Density (%) = float(10,0,100) #@gui : Consistency (%) = float(100,0,100) #@gui : Outline (px) = int(0,1,16) #@gui : Outline Color = color(0,0,0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2023/02/05.") fx_random_rectangles : density,consistency,outline,oR,oG,oB=${1-6} foreach { split_opacity l[0] { (0,0,{[w,h]-1};1) permute. zycx eval " const consistency = $consistency%; repeat(ceil(sqrt(wh#0)*5*$density%), best_p = best_area = 0; siz = da_size(); repeat(round(siz/50,5,1),k, p = v(da_size() - 1); P = I[p]; P0 = P[0,2]; P1 = P[2,2]; dP = P1 - P0; area = prod(dP); area>best_area && (!k || udP[1]?( # Horizontal split x = round(lerp(P0[0],P1[0],u(0.1,0.9))); I[best_p] = [ P0,x,P1[1] ]; ++xAuthor: David Tschumperlé.      Latest Update: 2024/01/14.") fx_reaction_diffusion : foreach { channels 0,{$3<2?0:2} rand 0,1 repeat $1 { blur {lerp(0,20,($2/19)^2)}%,3 sharpen 10000 n 0,1 } * 255 if $3%2 +norm !=. 0 *. 255 a c fi } #@gui Resynthetize Texture [FFT] : syntexturize, fx_syntexturize_preview(1) #@gui : Width = _int(1024,32,8192) #@gui : Height = _int(1024,32,8192) #@gui : Equalize Light = float(0,0,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: This filter tries to re-synthetize a micro-texture #@gui : (given as the input image) onto an output (seamless) image with an arbitrary size. #@gui : It uses a phase randomization technique, as described in:"} #@gui : url = link("Micro-Texture Synthesis by Phase Randomization","http://www.ipol.im/pub/art/2011/ggm_rpn/") #@gui : note = note("This filter is based on the work of Bruno Galerne, Yann Gousseau and #@gui : Jean-Michel Morel.") #@gui : sep = separator() #@gui : url = link("Click here for a detailed description of this filter.",\ # "http://gimpchat.com/viewtopic.php?f=28&t=10141") #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé and Jérome Boulanger. #@gui :       Latest Update: 2014/09/04.") fx_syntexturize : foreach { if $3 +b {20.5-$3/50}% -[0] [1] fc. ${average_vectors.} + c 0,255 fi syntexturize $1,$2 } fx_syntexturize_preview : gui_split_preview "fx_syntexturize 100%,100%,$3",${-3--1} #@gui Resynthetize Texture [Patch-Based] : syntexturize_matchpatch, fx_syntexturize_matchpatch_preview(1) #@gui : Width = _int(512,32,8192) #@gui : Height = _int(512,32,8192) #@gui : Number of Scales = int(0,0,16) #@gui : Patch Size = int(7,1,32) #@gui : Blending Size = int(5,1,24) #@gui : Precision = float(1,0,5) #@gui : Equalize Light = float(0,0,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: This filter tries to re-synthetize an input texture image onto a #@gui : bigger output image (with an arbitrary size). #@gui : Beware, this filter is quite slow to compute!"} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/22/10.") _fx_syntexturize_matchpatch_preview : foreach { if $7 +b {20.5-$7/50}% -[0] [1] fc. ${average_vectors.} + c 0,255 fi w,h={w},{h} syntexturize_matchpatch 100%,100%,${3--1} to_rgba r $w,$h,1,4,0,0,0.5,0.5 } fx_syntexturize_matchpatch_preview : gui_split_preview "_fx_syntexturize_matchpatch_preview ${1--2}",${-3--1} #@gui Rorschach : fx_rorschach, fx_rorschach #@gui : Scale = float(3,0,10) #@gui : Mirror = choice(1,"None","X-Axis","Y-Axis","XY-Axes") #@gui : Stencil Type = choice(2,"Black & White","RGB","Color") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/12/03.") fx_rorschach : foreach { remove_opacity if !$3 norm rorschach $1%,$2 * 255 elif $3==1 to_rgb rorschach $1%,$2 * 255 else +norm rorschach. $1%,$2 blend shapeaverage0 fi } #@gui Satin : fx_satin, fx_satin(1) #@gui : Iterations = int(20,4,128) #@gui : Smoothness (%) = float(1,0,5) #@gui : Seed = int(0,0,65535) #@gui : sep = separator() #@gui : Dark Color = color(0,0,0,255) #@gui : Light Color = color(255,255,255,255) #@gui : Stretch Contrast = bool(0) #@gui : sep = separator() #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(-50,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : sep = separator() #@gui : note = note{"This filter has been inspired by #@gui : this tutorial #@gui : from DeviantArt user fence-post."} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/11/27.") fx_satin : ($4,$8^$5,$9^$6,$10^$7,$11) srgb2rgb. r. 256,1,1,4,3 rgb2srgb. foreach[^-1] { srand $3 channels 0 f 0 repeat $1 { 100%,100%,1,1,"begin( A = u([0,0],[w,h]-1); B = u([0,0],[w,h]-1); N = [0,-1,1,0]*(B - A); D = A + N; C = B + N; abc = solve([A,1,B,1,C,1,D,1],[0,255,255,0]); ); dot(abc,[x,y,1])" c. 0,255 -- abs } b $2% gradient_norm negate n 0,255 if $12 normalize_local , fi pass. 1 map.. . rm. sh. 0,2 adjust_colors. ${13-17} rm. } rm. #@gui Seamless Turbulence : fx_seamless_turbulence, fx_seamless_turbulence(0) #@gui : Amplitude = float(15,0,30) #@gui : Smoothness = float(20,0,40) #@gui : Orientation = float(0,0,180) #@gui : Deviation = float(1,0,1) #@gui : Contrast = float(3,0,4) #@gui : Color Rendering = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/02/04.") fx_seamless_turbulence : foreach { wh:=w,h rm $wh,1,{$6?3:1} rand. 0,255 $wh rand. {$3*pi/180-$4*10*pi},{$3*pi/180+$4*10*pi} +sin. cos.. a[-2,-1] c r[-2,-1] 130%,130%,1,100%,0,2,0.5,0.5 b. $2 orientation. vector2tensor. smooth.. .,$1,0.5,20 rm. r. $wh,1,100%,0,0,0.5,0.5 if $5!=1 ia:=ia - $ia * $5 + $ia fi } c 0,255 n 0,255 #@gui Shock Waves : fx_shockwaves, fx_shockwaves_preview #@gui : Amplitude = float(10,0,100) #@gui : Low Frequency = float(10,0,100) #@gui : Frequency Range = float(20,0,100) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/01/12.") _fx_shockwaves : dct 100%,100%,1,1,1 circle. 0,0,{$2+$3}%,1,{$1+1} circle. 0,0,$2%,1,1 * idct c 0,255 fx_shockwaves : ac "_fx_shockwaves ${1-3}",$4 fx_shockwaves_preview : gui_split_preview "fx_shockwaves $*",${-3--1} #@gui Sponge : fx_sponge, fx_sponge_preview(0) #@gui : Size = int(13,3,21) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_sponge : ac "sponge $1",$2 fx_sponge_preview : gui_split_preview "fx_sponge $*",${-3--1} #@gui Stained Glass : fx_stained_glass, fx_stained_glass_preview(0) #@gui : Edges = float(20,0,100) #@gui : Shading = float(0.1,0,0.5) #@gui : Thin Separators = bool(1) #@gui : sep = separator() #@gui : Equalize = bool(1) #@gui : Colors = float(1,0,3) #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/18/03.") fx_stained_glass : foreach { split_opacity l[0] { to_rgb stained_glass $1,$2,$3 n 0,255 if $4 equalize. fi rgb2lab. sh. 1,2 *. $5 rm. lab2rgb. adjust_colors. ${6-8} } a c } fx_stained_glass_preview : gui_split_preview "fx_stained_glass $*",${-3--1} #@gui Stars : fx_stars, fx_stars(0) #@gui : Density = float(10,0,200) #@gui : Depth = float(0,0,5) #@gui : Size = int(32,8,128) #@gui : Branches = int(5,3,16) #@gui : Thickness = float(0.38,0.1,1) #@gui : Smoothness = float(0,0,10) #@gui : Color = color(255,255,100,200) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/01/10.") fx_stars : foreach { split_opacity stars[0] $1%,$2,$3,$4,$5,$6%,${7-9},{$10/255} a c } #@gui Stencil : fx_stencil, fx_stencil_preview(0) #@gui : Radius = float(3,0,10) #@gui : Smoothness = float(0,0,30) #@gui : Iterations = int(8,1,100) #@gui : Aliasing = float(0,0,5) #@gui : Stencil Type = choice(2,"Black & White","RGB","Color") #@gui : Transparency = bool(0) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_stencil : if !$5 norm stencil $1,$2,$3 elif $5==1 stencil $1,$2,$3 else repeat $! { +norm. stencil. $1,$2,$3 >=. 50% blend[-2,-1] shapeaverage0 mv. 0 } fi if $6 to_rgba replace_color 0,0,0,0,0,255,0,0,0,0 fi if $4 smooth {30*$4},0,1,1 fi fx_stencil_preview : gui_split_preview "fx_stencil $*",${-3--1} #@gui Tetris : fx_tetris, fx_tetris(0) #@gui : Scale = int(10,1,20) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_tetris : tetris $1 #@gui Triangular Pattern : fx_triangular_pattern,fx_triangular_pattern(1)+ #@gui : Random Seed = int(43,0,65535) #@gui : sep = separator() #@gui : Depth = int(7,0,16) #@gui : Split Type-1 = int(4,0,20) #@gui : Split Type-2 = int(4,0,20) #@gui : Split Type-3 = int(4,0,20) #@gui : Split Type-4 = int(0,0,20) #@gui : Split Type-5 = int(0,0,20) #@gui : Holes Probability (Type-5) (%) = float(0,0,100) #@gui : sep = separator() #@gui : Filling opacity (%) = float(100,0,100) #@gui : Outline Color = color(0,0,0,160) #@gui : Anti-aliasing = choice(1,"None","x1.5","x2","x3","x4") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/09/29.") fx_triangular_pattern : if !$! 1024,1024,1,3 fi W,H={0,[w,h]} f:=arg0($14,1,1.5,2,3,4)*100 r[0] $f%,$f%,1,100% 1,1,1,6 eval " # Init parameters for random generator. srand($1); const pn1 = $3; const pn2 = $4 + pn1; const pn3 = $5 + pn2; const pn4 = $6 + pn3; const pn5 = $7 + pn4; # Create set of subdivided triangles. C = [ w#0,h#0 ]/2; da_push([ C,0,h#0-1,0,0 ], [ C,w#0-1,0,0,0 ], [ C,w#0-1,0,w#0-1,h#0-1 ], [ C,0,h#0-1,w#0-1,h#0-1 ]); repeat($2, n = v(pn5 - 1); n = nAuthor: David Tschumperlé.      Latest Update: 2011/26/10.") fx_truchet : foreach { 100%,100% truchet $1,$2,$4 rm.. if $5==1 * -1 elif $5==2 i[0] 100%,100%,1,1,1 elif $5==3 i[0] 100%,100% elif $5==4 * -1 i[0] 100%,100% elif $5==5 * -1 i[0] 100%,100%,1,1,-1 elif $5==6 label 0,1 {iM+1},1,1,3 rand. 0,255 map.. . rm. fi a c b $3 n 0,255 } #@gui Voronoi : fx_voronoi, fx_voronoi_preview(0) #@gui : Threshold = float(160,0,255) #@gui : Threshold on = choice(1,"Pixel values","Gradient values") #@gui : Smoothness = float(0.5,0,10) #@gui : Subsampling (%) = float(50,0,100) #@gui : sep = separator() #@gui : Flat color = choice(3,"Black","White","Transparent","Image") #@gui : Outline thickness = int(1,0,8) #@gui : Outline color = color(0,0,0,100) #@gui : Centers radius = int(2,0,10) #@gui : Centers color = color(255,255,255,40) #@gui : sep = separator() #@gui : Anti-aliasing = choice{1,"x1 (none)","x1.5","x2","x2.5"} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/04/30.") fx_voronoi : foreach { wh:=w,h f:=arg(1+$16,1,1.5,2,2.5)*100 r $f%,$f%,1,100%,3 if $2 +gradient_norm t:=(255-$1)/4 else +s c med[^0] t:=255-$1 fi if $3 mM:=im,iM b. $3 n. $mM fi >. $t f. "u<($4%)^4?i:0" label_fg. 0,1 voronoi. # Flat. if $5<3 1,1,1,4,"!$5?[0,0,0,255]:$5==1?[255,255,255,255]:$5==2?[128,128,128,0]" r. [0],[0],1,4 rv[0,-1] rm. else blend[0] .,shapeaverage fi # Outline. if $6" && "$10 +f. "const boundary=1; i!=j(1) || i!=j(0,1)" dilate. $6 1,1,1,4,"[${7-9},255]" r. [0],[0],1,4 j[0] .,0,0,0,0,{$10/255},.. rm[-2,-1] fi # Centers. if $11" && "$15 1,{iM+1},1,3 eval.. "I[#-1,i]+=[x,y,1]" s. c,-2 /[-2,-1] rm.. eval. "const r = ($11-1)*arg(1+$16,1,1.5,2,2.5); ellipse(#0,i0,i1,r,r,0,$15/255,[${12-14},255])" fi rm. r $wh,1,100%,2 } fx_voronoi_preview : fx_voronoi ${1-15},{min(1,$16)} #@gui Weave : weave, weave(1) #@gui : Density = int(6,1,32) #@gui : Thickness = float(65,0,100) #@gui : Shadow = float(0,0,100) #@gui : Shading = float(0.5,0,3) #@gui : Fibers Amplitude = float(0,0,255) #@gui : Fibers Smoothness = float(0,0,10) #@gui : Angle = choice("0 deg.","22.5 deg.","45 deg.","67.5 deg.") #@gui : X-Curvature = float(0,-1,1) #@gui : Y-Curvature = float(0,-1,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/18/01.") #@gui Whirls : fx_whirls, fx_whirls_preview(0) #@gui : Density = int(7,3,20) #@gui : Smoothness = float(2,0,10) #@gui : Darkness = float(0.2,0,1) #@gui : Lightness = float(1.8,1,3) #@gui : sep = separator() #@gui : Channel(s) = choice(11,"All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_whirls : ac "whirls $1,$2,$3,$4",$5 fx_whirls_preview : gui_split_preview "fx_whirls ${1-5}",${-3--1} #@gui ____Repair #----------------------- #@gui Bayer Reconstruction : bayer2rgb, gui_no_preview #@gui : G/M Smoothness = _float(6,0,20) #@gui : R/B Smoothness (Principal) = _float(6,0,20) #@gui : R/B Smoothness (Secondary) = _float(4,0,20) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") #@gui Deinterlace : deinterlace, fx_deinterlace_preview(0) #@gui : Algorithm = choice("Standard","Motion-Compensated") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_deinterlace : deinterlace 0 skip ${^0} fx_deinterlace_preview : gui_split_preview "fx_deinterlace $*",${-3--1} #@gui Inpaint [Holes] : fx_inpaint_holes, fx_inpaint_holes(0) #@gui : Maximal Area = float(4,1,512) #@gui : Tolerance = float(20,0,255) #@gui : Connectivity = choice(1,"Low","High") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/27/05.") fx_inpaint_holes : inpaint_holes {$1^1.5},$2,$3 #@gui Inpaint [Morphological] : fx_inpaint_morpho, fx_inpaint_morpho_preview(1) #@gui : Mask Color = _color(255,0,0,255) #@gui : Mask Dilation = _int(0,0,32) #@gui : sep = separator() #@gui : note = note{"Note: It is strongly suggested to apply this filter only on a selection #@gui : around the region to inpaint, to save computation time!"} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/25/11.") fx_inpaint_morpho : foreach { R=$1 G=$2 B=$3 A=$4 if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color. +round select_color. 0,{round([$R,$G,$B,$A])} if $5 dilate. {1+2*$5} fi inpaint_morpho.. [1] rm. } fx_inpaint_morpho_preview : fx_inpaint_morpho ${1-4},{1+$5} #@gui Inpaint [Multi-Scale] : fx_inpaint_matchpatch, fx_inpaint_matchpatch_preview(1) #@gui : Number of Scales = int(0,0,16) #@gui : note = note{"(Set Number of scales to 0 for automatic scale detection)"} #@gui : Patch Size = int(9,1,64) #@gui : Number of Iterations per Scale = int(10,1,100) #@gui : Blend Size = int(5,0,32) #@gui : Allow Outer Blending = bool(1) #@gui : Mask Color = color(255,0,0,255) #@gui : Mask Dilation = int(0,0,32) #@gui : sep = separator() #@gui : Preview Progression While Running = _bool(0) #@gui : sep = separator() #@gui : note = note{"Note: Preview and final result may strongly differ."} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/25/11.") fx_inpaint_matchpatch : foreach { nm={n} R=$6 G=$7 B=$8 A=$9 if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color. +round select_color. 0,{round([$R,$G,$B,$A])} if $10 dilate. {1+2*$10} fi if $11 visu_size=${fitscreen[]" "{0,[w,h,1]},25%,50%} w1.. $visu_size,0,"[Preview] G'MIC: Inpaint [multi-scale]" fi srand 0 inpaint_matchpatch.. [1],${1-5} rm. => $nm } fx_inpaint_matchpatch_preview : fx_inpaint_matchpatch ${1-9},{1+$10},0 #@gui Inpaint [Patch-Based] : fx_inpaint_patch, fx_inpaint_patch_preview #@gui : Patch Size = _int(7,1,64) #@gui : Lookup Size = _float(16,1,32) #@gui : Lookup Factor = _float(0.1,0,1) #@gui : Blend Size = _float(1.2,0,5) #@gui : Blend Threshold = _float(0,0,1) #@gui : Blend Decay = _float(0.05,0,0.5) #@gui : Blend Scales = _int(10,1,20) #@gui : Allow Outer Blending = _bool(1) #@gui : Mask Color = _color(255,0,0,255) #@gui : Mask Dilation = _int(0,0,32) #@gui : Process by Blocks of Size = _choice("100%","75%","50%","25%","10%","5%","2%","1%") #@gui : sep = separator() #@gui : note = note("A quick tutorial on how to use this filter can be found here:") #@gui : url = link("G'MIC Inpainting tutorial on Patrick David's blog.", #@gui : "https://patdavid.net/2014/02/getting-around-in-gimp-gmic-inpainting.html") #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé and Maxime Daisy. #@gui :       Latest Update: 2015/25/11.") _fx_inpaint_patch : foreach { R=$9 G=$10 B=$11 A=$12 if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color. +round select_color. 0,{round([$R,$G,$B,$A])} if $13 dilate. {1+2*$13} fi inpaint.. [1],$1,{$1*$2},$3,1,{$4*$1},${5-8} rm. } fx_inpaint_patch : foreach { if $14 bs:=max(16,min(w,h)*arg(1+$14,100,75,50,25,10,5,2,1)%) at "_fx_inpaint_patch $*",$bs,$bs,1,25%,25%,0,2 else _fx_inpaint_patch $* fi } fx_inpaint_patch_preview : fx_inpaint_patch ${1-12},{1+$13},0 #@gui Inpaint [Transport-Diffusion] : fx_inpaint_pde, fx_inpaint_pde_preview(1) #@gui : Smoothness (%) = float(75,0,100) #@gui : Regularization = choice(1,"Isotropic","Delaunay-Guided","Edge-Oriented") #@gui : Regularization Iterations = int(20,0,100) #@gui : Mask Color = _color(255,0,0,255) #@gui : Mask Dilation = _int(0,0,32) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/10/04.") fx_inpaint_pde : foreach { R=$4 G=$5 B=$6 A=$7 if !$A" && "(s==2" || "s==4) split_opacity +!=. 0 *[0,-1] a c R=0 G=0 B=0 fi # Purely transparent color. +select_color 0,$R,$G,$B,$A if $8 dilate. {1+2*$8} fi inpaint_pde.. [1],$1%,$2,$3 rm. } c 0,255 fx_inpaint_pde_preview : fx_inpaint_pde ${1-7},{1+$8} #@gui Red-Eye Attenuation : red_eye, red_eye #@gui : Threshold = float(75,0,100) #@gui : Smoothness = float(3.5,0,20) #@gui : Factor = float(0.1,0,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") #@gui Remove Hot Pixels : fx_remove_hotpixels, fx_remove_hotpixels_preview(0) #@gui : Mask Size = int(3,3,20) #@gui : Threshold = float(10,0,200) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: Jérome Boulanger.      Latest Update: 2010/29/12.") fx_remove_hotpixels : remove_hotpixels $1,$2 fx_remove_hotpixels_preview : gui_split_preview "fx_remove_hotpixels $*",${-3--1} #@gui Solidify : fx_solidify_td, fx_solidify_td_preview(1) #@gui : Smoothness (%) = float(75,0,100) #@gui : Regularization = choice(1,"Isotropic","Delaunay-Guided","Edge-Oriented") #@gui : Regularization Iterations = int(20,0,100) #@gui : Dilation / Erosion = int(0,-20,20) #@gui : Colorspace = choice(1,"sRGB","Linear RGB") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter reconstructs transparent regions of an image using a transport-diffusion algorithm. #@gui : Useful only for images having an alpha-channel. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/07/04.") fx_solidify_td : foreach { to_rgba sh 0,{s-2} if $5 srgb2rgb. fi rm. if $4 . sh. 100% if $4>0 erode. {1+2*$4} else dilate. {1-2*$4} fi rm. solidify. $1%,$2,$3 rv blend alpha else solidify $1%,$2,$3 fi if $5 rgb2srgb. fi } fx_solidify_td_preview : gui_split_preview "fx_solidify_td $*",${-3--1} #@gui Smooth [Anisotropic] : fx_smooth_anisotropic, fx_smooth_anisotropic_preview(0) #@gui : Amplitude = float(60,0,1000) #@gui : Sharpness = float(0.7,0,2) #@gui : Anisotropy = float(0.3,0,1) #@gui : Gradient Smoothness = float(0.6,0,10) #@gui : Tensor Smoothness = float(1.1,0,10) #@gui : Spatial Precision = float(0.8,0.1,2) #@gui : Angular Precision = float(30,1,180) #@gui : Value Precision = float(2,0.1,5) #@gui : Interpolation = choice(0,"Nearest Neighbor","Linear","Runge-Kutta") #@gui : Fast Approximation = bool(1) #@gui : Iterations = int(1,1,10) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/08/27.") fx_smooth_anisotropic : ac "repeat $11 { smooth $1,$2,$3,$4,$5,$6,$7,$8,$9,$10 }",$12 c 0,255 fx_smooth_anisotropic_preview : gui_split_preview "fx_smooth_anisotropic $*",${-3--1} #@gui Smooth [Antialias] : fx_smooth_antialias, fx_smooth_antialias_preview(0) #@gui : Amplitude = float(5,0,100) #@gui : Edge Threshold (%) = float(10,0,100) #@gui : Smoothness = float(0.8,0,5) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/11/13.") fx_smooth_antialias : foreach { +diffusiontensors 0,1,1,$3,$3 +gradient_norm.. >=. $2% *[-2,-1] smooth.. .,{$1^1/3},0.5,120,2,1 rm. } fx_smooth_antialias_preview : gui_split_preview "fx_smooth_antialias $*",${-3--1} #@gui Smooth [Bilateral] : fx_smooth_bilateral, fx_smooth_bilateral_preview(0) #@gui : Spatial Variance = float(10,0,100) #@gui : Value Variance = float(7,0,100) #@gui : Iterations = int(2,1,10) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/27/08.") fx_smooth_bilateral : skip ${5=0},${6=0} ac "repeat $3 { bilateral $1,$2 }",$4 fx_smooth_bilateral_preview : gui_split_preview "fx_smooth_bilateral $*",${-3--1} #@gui Denoise : fx_denoise,fx_denoise_preview(0) #@gui : Noise type = choice{"Soft","Heavy","Heavy (Faster)","Poisson + Gaussian","Poisson + Gaussian (v2)"} #@gui : Iterations = int(1,1,5) #@gui : sep = separator() #@gui : Update Neural Network = button() #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{" #@gui : Note: This filter uses a convolutional neural network (CNN) #@gui : to denoise images. This filter does not take advantage of GPU computing, so expect it to be quite slow #@gui : if you don't have many CPU cores available. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/11/06.") fx_denoise : if $3 delete ${-path_cache}gmic_denoise_cnn.gmz fi repeat $2 { denoise_cnn $1,64 progress {round(($>+1)/$2*100)} } fx_denoise_preview : rs 280,280,0,2 gui_split_preview "fx_denoise $*",${-3--1} #@gui Smooth [Guided] : fx_smooth_guided, fx_smooth_guided_preview(0) #@gui : Guide As = choice("Self","Top Layer","Bottom Layer") #@gui : Radius = int(5,1,100) #@gui : Smoothness = float(30,0,512) #@gui : Iterations = int(1,1,10) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2019/10/02.") fx_smooth_guided : skip ${6=0},${7=0} if $1" && "!narg($_guide) if $!<2 gui_warning_preview "Missing guide layer" return fi store[{$1==1?0:-1}] _guide fi if !$1 # Self-guide ac "repeat $4 { guided $2,$3 }",$5 elif $1==1 # Guide as top layer ac "$_guide r. ..,..,1,100%,0,0,0.5,0.5 repeat $4 { guided[0] [1],$2,$3 } rm.",$5 if !narg($_is_preview) $_guide mv. 0 fi else # Guide as bottom layer ac "$_guide r. ..,..,1,100%,0,0,0.5,0.5 repeat $4 { guided[0] [1],$2,$3 } rm.",$5 if !narg($_is_preview) $_guide fi fi fx_smooth_guided_preview : if $1" && "!narg($_guide) if $!<2 gui_warning_preview "Missing guide layer" return fi store[{$1==1?0:-1}] _guide fi _is_preview=1 gui_split_preview "fx_smooth_guided $*",${-3--1} #@gui Smooth [Diffusion] : fx_smooth_diffusion, fx_smooth_diffusion_preview(0) #@gui : Sharpness = float(0.7,0,2) #@gui : Anisotropy = float(0.3,0,1) #@gui : Gradient Smoothness = float(0.6,0,10) #@gui : Tensor Smoothness = float(1.1,0,10) #@gui : Time Step = float(15,5,50) #@gui : Iterations = int(8,1,100) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/27/08.") fx_smooth_diffusion : ac "gui_parallel_overlap \"smooth $6,$1,$2,$3,$4,$5,0 c 0,255\",$8,$9",$7 fx_smooth_diffusion_preview : gui_split_preview "fx_smooth_diffusion $*",${-3--1} #@gui Smooth [Mean-Curvature] : fx_smooth_meancurvature, fx_smooth_meancurvature_preview(0) #@gui : Time Step = float(30,5,50) #@gui : Iterations = int(4,1,30) #@gui : Keep Iterations as Different Layers = bool(false) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/27/08.") fx_smooth_meancurvature : ac "gui_parallel_overlap \"meancurvature_flow $2,$1,$3 c 0,255\",$5,$6",$4 fx_smooth_meancurvature_preview : gui_split_preview "fx_smooth_meancurvature $*",${-3--1} #@gui Smooth [Median] : fx_smooth_median, fx_smooth_median_preview(0) #@gui : Radius = int(3,1,20) #@gui : Threshold = float(255,0,255) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_smooth_median : ac "median $1,$2",$3 fx_smooth_median_preview : gui_split_preview "fx_smooth_median $*",${-3--1} #@gui Smooth [NL-Means] : fx_smooth_nlmeans, fx_smooth_nlmeans_preview(0) #@gui : Patch Size = float(4,0.5,10) #@gui : Spatial Bandwidth = int(4,3,13) #@gui : Tonal Bandwidth = float(10,1,50) #@gui : Patch Measure = choice(3,"Linf-Norm","L1-Norm","L2-Norm","Luminance","Lightness","RGB") #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: Jérôme Boulanger.      Latest Update: 2015/01/07.") fx_smooth_nlmeans: foreach { if s==1 nlmeans $1,$2,$3,-_fx_smooth_nlmeans$4 # Handle separately gray scale images. else ac "gui_parallel_overlap \"nlmeans $1,$2,$3,-_fx_smooth_nlmeans$4\",$6,$7",$5 fi } _fx_smooth_nlmeans0 : s c abs max _fx_smooth_nlmeans1 : s c abs + _fx_smooth_nlmeans2 : norm _fx_smooth_nlmeans3 : if s>=3 channels 0,2 luminance else norm fi _fx_smooth_nlmeans4 : if s>=3 channels 0,2 srgb2rgb rgb2lab channels 0 else norm fi _fx_smooth_nlmeans5 : fx_smooth_nlmeans_preview: gui_split_preview "fx_smooth_nlmeans $*",${-3--1} #@gui Smooth [Patch-Based] : fx_smooth_patch, fx_smooth_patch_preview(0) #@gui : Spatial Variance = float(10,0.1,200) #@gui : Patch Variance = float(10,0.1,200) #@gui : Patch Size = int(3,2,21) #@gui : Lookup Size = int(5,2,21) #@gui : Patch Smoothness = float(0,0,4) #@gui : Fast Approximation = bool(1) #@gui : Iterations = int(1,1,10) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/27/08.") fx_smooth_patch : ac "gui_parallel_overlap \"repeat $7 { denoise $1,$2,$3,$4,$5,$6 } c 0,255\",$9,$10",$8 fx_smooth_patch_preview : gui_split_preview "fx_smooth_patch $*",${-3--1} #@gui Smooth [Patch-PCA] : fx_smooth_patchpca, fx_smooth_patchpca_preview(0) #@gui : Strength = float(4,0,16) #@gui : Patch Size = int(7,2,21) #@gui : Lookup Size = int(11,2,21) #@gui : Spatial Sampling = int(7,1,16) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: Beware, this filter uses a very computationally intensive algorithm to #@gui : denoise images. So, do not complain too much if you have less than 8 cores available for the computation :) #@gui : "} #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé and Jérome Boulanger. #@gui :       Latest Update: 2016/24/03.") fx_smooth_patchpca : ac "denoise_patchpca ${1-4} c 0,255",$5 fx_smooth_patchpca_preview : gui_split_preview "fx_smooth_patchpca $*",${-3--1} #@gui Smooth [Perona-Malik] : fx_smooth_peronamalik, fx_smooth_peronamalik_preview(0) #@gui : K-Factor = float(20,0,255) #@gui : Time Step = float(5,5,50) #@gui : Iterations = int(5,1,30) #@gui : Keep Iterations as Different Layers = bool(false) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/26/11.") fx_smooth_peronamalik : ac "gui_parallel_overlap \"peronamalik_flow $1,$3,$2,$4 c 0,255\",$6,$7",$5 fx_smooth_peronamalik_preview : gui_split_preview "fx_smooth_peronamalik $*",${-3--1} #@gui Smooth [Selective Gaussian] : fx_smooth_selective, fx_smooth_selective_preview(0) #@gui : Amplitude = float(5,0,20) #@gui : Edges = float(0.5,0,2) #@gui : Scales = int(5,1,10) #@gui : Iterations = int(1,1,10) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/27/08.") fx_smooth_selective : ac "gui_parallel_overlap \"repeat $4 { blur_selective $1,$2,$3 } c 0,255\",$6,$7",$5 fx_smooth_selective_preview : gui_split_preview "fx_smooth_selective $*",${-3--1} #@gui Smooth [Skin] : fx_smooth_skin, fx_smooth_skin_preview(1) #@gui : note = note("Step 1: Skin detection") #@gui : Skin Estimation = choice(2,"None","Manual","Automatic") #@gui : Tolerance = float(0.5,0,1) #@gui : Smoothness = float(1,0,5) #@gui : Threshold = float(1,0,10) #@gui : Pre-Normalize Image = bool(1) #@gui : X-Coordinate [Manual] = float(50,0,100) #@gui : Y-Coordinate [Manual] = float(50,0,100) #@gui : Radius [Manual] = float(5,0,25) #@gui : sep = separator() #@gui : note = note("Step 2: Medium scale smoothing") #@gui : Base Scale = float(2,0,10) #@gui : Fine Scale = float(0.2,0,0.8) #@gui : Smoothness = float(3,0,10) #@gui : Smoothness Type = choice(1,"Gaussian","Bilateral") #@gui : sep = separator() #@gui : note = note("Step 3: Details enhancement") #@gui : Gain = float(0.05,0,0.5) #@gui : sep = separator() #@gui : Preview Data = choice{5,"Skin Mask","Base Scale","Medium Scale (Original)","Medium Scale (Smoothed)", #@gui : "Fine Scale","Result Image"} #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : url = link("Click here for a video tutorial","http://www.youtube.com/watch?v=H8pQfq-ybCc") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/20/12.") fx_smooth_skin : foreach { split_opacity l[0] { to_rgb # Skin detection step. if $5 +balance_gamma 128,128,128 else [0] fi if !$1 channels. 0 f. 1 elif $1==2 detect_skin. $2 else detect_skin. $2,$6%,$7%,$8% fi M:=iM b. $3% *. {$M/iM} *. $4 c. 0,1 # Details smoothing step. split_details[0] 4,$9%,$10% +_fx_smooth_skin[2] $12,$11 j[2] .,0,0,0,0,1,.. rm[-2,-1] *. {10^$13} + c 0,255 } a c } _fx_smooth_skin : if !$1 b {$2/8}% else if $2>0 m,M:=im,iM n 0,255 repeat int($2/5) { bilateral 3%,{5*3} } bilateral 3%,{($2%5)*3} * {($M-$m)/255} + $m fi fi fx_smooth_skin_preview : if !$14 gui_split_preview "if $5 balance_gamma 128,128,128 fi if !$1 f 1 elif $1==2 detect_skin $2 "\ "else detect_skin $2,$6%,$7%,$8% fi M:=iM b $3% * {255*$M/iM} * $4 c 0,255",${-3--1} elif $14==1 gui_split_preview "b $9%",${-3--1} elif $14==2 gui_split_preview "split_details 4,$9%,$10% k.. n 0,255",${-3--1} elif $14==3 gui_split_preview "split_details 4,$9%,$10% k.. _fx_smooth_skin $12,$11 n 0,255",${-3--1} elif $14==4 gui_split_preview "split_details 4,$9%,$10% k. n 0,255",${-3--1} else gui_split_preview "fx_smooth_skin $*",${-3--1} fi if $1==1 to_rgb circle $6%,$7%,$8%,0.2,0,255,0 circle $6%,$7%,$8%,0.4,0xFFFFFFFF,0,255,0 line {$6-0.25*$8}%,{$7-0.25*$8}%,{$6+0.25*$8}%,{$7+0.25*$8}%,0.8,255,255,0 line {$6+0.25*$8}%,{$7-0.25*$8}%,{$6-0.25*$8}%,{$7+0.25*$8}%,0.8,255,255,0 fi #@gui Smooth [Thin Brush] : fx_smooth_anisotropic, fx_smooth_anisotropic(0) #@gui : Amplitude = float(60,0,1000) #@gui : Sharpness = float(0.9,0,2) #@gui : Anisotropy = float(0.64,0,1) #@gui : Gradient Smoothness = float(3.1,0,10) #@gui : Tensor Smoothness = float(1.10,0,10) #@gui : Spatial Precision = float(0.8,0.1,2) #@gui : Angular Precision = float(30,1,180) #@gui : Value Precision = float(2,0.1,5) #@gui : Interpolation = choice(0,"Nearest Neighbor","Linear","Runge-Kutta") #@gui : Fast Approximation = bool(1) #@gui : Iterations = int(1,1,10) #@gui : Channel(s) = choice("RGB","Luminance","Blue & Red chrominances","Blue chrominance","Red chrominance") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : note = note{"\nNote: This set of anisotropic smoothing parameters has been suggested #@gui : by PhotoComiX."} #@gui : sep = separator() #@gui : note = note("Author: PhotoComiX.      Latest Update: 2010/26/12.") #@gui Smooth [Total Variation] : fx_smooth_tv, fx_smooth_tv_preview(0) #@gui : Time Step = float(30,5,100) #@gui : Iterations = int(10,1,40) #@gui : Keep Iterations as Different Layers = bool(false) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/27/08.") fx_smooth_tv : ac "gui_parallel_overlap \"tv_flow $2,$1,$3 c 0,255\",$5,$6",$4 fx_smooth_tv_preview : gui_split_preview "fx_smooth_tv $*",${-3--1} #@gui Smooth [Wavelets] : fx_smooth_haar, fx_smooth_haar_preview(0) #@gui : Threshold = float(1,0,10) #@gui : Iterations = int(10,1,32) #@gui : Scales = int(10,2,10) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : Parallel Processing = choice("Auto","One Thread","Two Threads","Four Threads","Eight Threads", #@gui : "Sixteen Threads"), Spatial Overlap = int(24,0,256) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: Jérome Boulanger and David Tschumperlé. #@gui :       Latest Update: 2013/27/08.") fx_smooth_haar : remove_opacity ac "gui_parallel_overlap \"denoise_haar $1,$3,$2 c 0,255\",$5,$6",$4 fx_smooth_haar_preview : gui_split_preview "fx_smooth_haar $*",${-3--1} #@gui Upscale [Diffusion] : fx_upscale_smart, fx_upscale_smart_preview(0) #@gui : Width = text("200%") #@gui : Height = text("200%") #@gui : Smoothness = float(2,0,20) #@gui : Anisotropy = float(0.4,0,1) #@gui : Sharpness = float(50,0,100) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_upscale_smart : to_rgb upscale_smart $1,$2,1,$3,$4,$5 c 0,255 fx_upscale_smart_preview : repeat $! { +r. $1,$2,1,1,0 if w<{-2,w}" || "h<{-2,h} # Test for downscaling rm. /. 4 0 t. "Downscaling is\nnot allowed!",5,5,20,1,255 r. ..,..,1,1,0,0,0.5,0.5 -|[-2,-1] else z.. {50-50*{-2,w}/w}%,{50-50*{-2,h}/h}%,{50+50*{-2,w}/w}%,{50+50*{-2,h}/h}% rm. fx_upscale_smart. $1,$2,$3,$4,$5 c. 0,255 fi mv. 0 } #@gui Upscale [Scale2x] : fx_scalenx, fx_scalenx_preview(0) #@gui : Scaling Factor = choice("x 2","x 3","x 4","x 6","x 8","x 9","x 12","x 16","x 18","x 27") #@gui : Colorbase = choice(0,"RGB","YCbCr","Lab") #@gui : note = note{"\nNote: #@gui : This filter re-implements the scaling algorithm described at : #@gui : "} #@gui : url = link("http://scale2x.sourceforge.net") #@gui : note = note{" #@gui : This filter is useful for resizing images that have very few colors #@gui : (e.g. indexed images). It is generally useless for true colors images. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_scalenx : foreach { split_opacity if $2==1 rgb2ycbcr[0] round[0] elif $2==2 rgb2lab8[0] round[0] fi if !$1 scale2x elif $1==1 scale3x elif $1==2 scale2x scale2x elif $1==3 scale3x scale2x elif $1==4 scale2x scale2x scale2x elif $1==5 scale3x scale3x elif $1==6 scale3x scale2x scale2x elif $1==7 scale2x scale2x scale2x scale2x elif $1==8 scale3x scale3x scale2x elif $1==9 scale3x scale3x scale3x fi if $2==1 ycbcr2rgb[0] elif $2==2 lab82rgb[0] fi a c } fx_scalenx_preview : z 40%,40%,60%,60% fx_scalenx $1,$2 #@gui Upscale [DCCI2x]: fx_scale_dcci2x, fx_scale_dcci2x_preview(0) #@gui : note = note("Directional Cubic Convolution Interpolation"), sep = separator() #@gui : Threshold = float(1.15,1,2) #@gui : Exponent = int(5,1,6) #@gui : Extend 1px = _bool(0) #@gui : sep = separator() #@gui : note = note("Author: Garagecoder. Latest Update : 2015/11/07.") #@gui : note = note{"\nNote: #@gui : This filter re-implements the scaling algorithm described at : #@gui : "} #@gui : url = link("wikipedia.org","https://en.wikipedia.org/wiki/Directional_Cubic_Convolution_Interpolation") #@gui : note = note("The algorithm is intended for enlarging images while avoiding") #@gui : note = note("artifacts, e.g. staircase artifacts.") #@gui : note = note("\nThreshold controls edge[lower] to texture[higher] balance.") #@gui : note = note("Exponent controls texture edge sharpness[higher].") #@gui : note = note("Warning: highly experimental...") fx_scale_dcci2x : skip ${1=1.15},${2=5},${3=0} foreach { split_opacity scale_dcci2x ${1-3} a c c 0,255 } fx_scale_dcci2x_preview : z 25%,25%,75%,75% fx_scale_dcci2x $* #@gui ____Rendering #------------------------- # Generic function to render a 3D image, with usual rendering parameters : # $1 = Width # $2 = Height # $3 = Object size # $4 = X-angle # $5 = Y-angle # $6 = Z-angle # $7 = FOV # $8 = X-light # $9 = Y-light # $10 = Z-light # $11 = Specular lightness # $12 = Specular shininess # $13 = Rendering mode. # $14 = Antialiasing (0 | 1) fx_render3d : skip ${14=1} width,height:=(1+$14)*[$1,$2] n3d c3d m3d {round($13)} f3d:=0.5*max($width,$height)/tan($7*pi/360) f3d $f3d l3d {$8*$f3d},{$9*$f3d},{$10*$f3d} sl3d $11 ss3d $12 foreach { *3d {$3*max($width,$height)} r3d 0,0,1,{-$6} r3d 0,1,0,{-$5} r3d 1,0,0,{-$4} $width,$height,1,3,-1 j3d. ..,50%,50% rm.. to_rgba replace_color 0,0,-1,-1,-1,255,0,0,0,0 if $14 r $1,$2,1,100%,2 s c,-3 +. 1e-5 /[0] [1] *[0] 255 a c fi } #@gui 3D Blocks : fx_blocks3d, fx_blocks3d(1) #@gui : Resolution = int(32,1,128) #@gui : Smoothness = float(0,0,40) #@gui : Elevation = float(4,-10,10) #@gui : Size = float(1.5,0,3) #@gui : Angle = float(30,0,360) #@gui : Tilt = float(60,0,90) #@gui : FOV = float(45,1,90) #@gui : Centering (%) = point(50,50) #@gui : sep = separator() #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(-50,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : Use Light = bool(1) #@gui : Antialiasing = bool(1) #@gui : Outline Color = color(0,0,0,128) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/10/02.") fx_blocks3d : foreach { nm=${"-gui_layer_name"} W,H,M:=w,h,max(w,h) if w>h rs $1 else rs ,$1 fi w,h,m:=w,h,max(w,h) if $3>0 mirror y fi imageblocks3d $3,$2% -3d. {$w/2},{$h/2} f:=$4*$M/($m*(2-$16)) *3d $f,$f,{$f*abs($3*$1/100)} if $3>0 r3d 1,0,0,180 fi r3d 0,0,1,$5 r3d 1,0,0,-$6 # Render object. if $16 {2*$M},{2*$M},1,4,-1 else $M,$M,1,4,-1 fi f3d:=0.5*w/tan($7*pi/360) f3d $f3d l3d {$10*$f3d},{$11*$f3d},{$12*$f3d} sl3d $13 ss3d $14 j3d. [0],$8%,$9%,0,1,{$15?3:2},0,1 sh. 100% +. 1 *. 255 rm. # Render object outline if $20 .,.,1,3,-1 j3d. [0],$8%,$9%,0,1,3,0,1 rm[0] g. xy,1 +[-2,-1] norm. !=. 0 +r. 100%,100%,1,3 sh. 0 *. $17 rm. sh. 1 *. $18 rm. sh. 2 *. $19 rm. j[0] .,0,0,0,0,{$20/255},.. rm[-2,-1] else rm[0] fi replace_color 0,0,-1,-1,-1,0,0,0,0,0 if $16 r 50%,50%,1,4,2 fi c 0,255 => "name("$nm")" } #@gui 3D Colored Object : fx_coloredobject3d, fx_coloredobject3d_preview(1) #@gui : Type = choice{1,"Plane","Box","Pyramid","Ellipsoid","Torus","Gyroid","Weird","Cup"} #@gui : Color = color(128,128,128,255) #@gui : sep = separator() #@gui : Size-1 = float(0.5,0,3) #@gui : Size-2 = float(0.5,0,3) #@gui : Size-3 = float(0.5,0,3) #@gui : X-Angle = float(57,0,360) #@gui : Y-Angle = float(41,0,360) #@gui : Z-Angle = float(21,0,360) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong") #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/16/05.") _fx_coloredobject3d : to_rgb _fx_coloredobject3d$1$2 ${6-8} col3d. ${3-5} db3d 0 fx_coloredobject3d : _fx_coloredobject3d "_",${1-4,6-8} repeat $!-1 { +fx_render3d. {$>,w},{$>,h},$6,${9--1} sh. 3 *. {$5/255} rm. blend[$>,-1] alpha } rm. fx_coloredobject3d_preview : _fx_coloredobject3d "_preview_",${1-4,6-8} repeat $!-1 { +fx_render3d. {$>,w},{$>,h},$6,${9--1} sh. 3 *. {$5/255} rm. blend[$>,-1] alpha } rm. _fx_coloredobject3d_0 : plane3d 1 *3d. $1,$2,1 _fx_coloredobject3d_1 : box3d 1 *3d. $1,$2,$3 _fx_coloredobject3d_2 : pyramid3d 1,1 *3d. $1,$2,$3 _fx_coloredobject3d_3 : sphere3d 1 *3d. 1,{2*$2},{2*$3} _fx_coloredobject3d_4 : torus3d $1,{$2/2},100,50 *3d. $3,0.5,0.5 _fx_coloredobject3d_5 : gyroid3d 24 *3d. $1,$2,$3 _fx_coloredobject3d_6 : weird3d 32 *3d. $1,$2,$3 _fx_coloredobject3d_7 : cup3d 128 *3d. $1,$2,$3 _fx_coloredobject3d_preview_0 : plane3d 1 *3d. $1,$2,1 _fx_coloredobject3d_preview_1 : box3d 1 *3d. $1,$2,$3 _fx_coloredobject3d_preview_2 : pyramid3d 1,1 *3d. $1,$2,$3 _fx_coloredobject3d_preview_3 : sphere3d 1 *3d. 1,{2*$2},{2*$3} _fx_coloredobject3d_preview_4 : torus3d $1,{$2/2},100,50 *3d. $3,0.5,0.5 _fx_coloredobject3d_preview_5 : gyroid3d 8 *3d. $1,$2,$3 _fx_coloredobject3d_preview_6 : weird3d 12 *3d. $1,$2,$3 _fx_coloredobject3d_preview_7 : cup3d 64 *3d. $1,$2,$3 #@gui 3D Elevation : fx_elevation3d, fx_elevation3d_preview(1) #@gui : Factor = float(100,-1000,1000) #@gui : Smoothness = float(1,0,10) #@gui : sep = separator() #@gui : Width = _int(1024,8,4096) #@gui : Height = _int(1024,8,4096) #@gui : Size = float(0.8,0,3) #@gui : X-Angle = float(25,0,360) #@gui : Y-Angle = float(0,0,360) #@gui : Z-Angle = float(21,0,360) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : Rendering = choice(2,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong") #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : Top Layer Defines Object Texture = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/11/10.") _fx_elevation3d : if $3" && "$!>1 # Textured object repeat $!-1 { l[0,{1+$>}] { +ri.. .,3 n. 0,{abs($1)} *. {sign($1)} b. $2 elevation3d.. . rm. } } else foreach { +norm n. 0,{abs($1)} *. {sign($1)} b. $2 elevation3d.. . rm. } # Non-textured object fi db3d fx_elevation3d : _fx_elevation3d ${1-2},$17 s0,s1=^,^0 fx_render3d[${s$17}] ${3--1} fx_elevation3d_preview : fx_elevation3d ${1-2},{w},{h},${5--1} if $17 rm[0] fi #@gui 3D Extrusion : fx_extrude3d, fx_extrude3d_preview(1) #@gui : Depth = float(10,1,1024) #@gui : Resolution = int(512,1,1024) #@gui : Smoothness = float(0.6,0,3) #@gui : sep = separator() #@gui : Width = _int(1024,1,4096) #@gui : Height = _int(1024,1,4096) #@gui : Size = float(0.5,0,3) #@gui : X-Angle = float(57,0,360) #@gui : Y-Angle = float(41,0,360) #@gui : Z-Angle = float(21,0,360) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong") #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : Top Layer Defines Object Texture = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/11/10.") _fx_extrude3d : if $4" && "$!>1 repeat $!-1 { l[0,{$>+1}] { extrude3d. $1,$2,$3% t3d. .. } } # Textured object else extrude3d $1,$2,$3% # Non-textured object fi db3d 0 fx_extrude3d : _fx_extrude3d ${1-3},$18 s0,s1=^,^0 fx_render3d[${s$18}] ${4--1} fx_extrude3d_preview : fx_extrude3d ${1-3},{w},{h},${6--1} if $18 rm[0] fi #@gui 3D Image Object : fx_imageobject3d, fx_imageobject3d_preview(1) #@gui : Type = choice{1,"Plane","Cube","Pyramid","Sphere","Torus","Gyroid","Weird","Cup","Rubik"} #@gui : sep = separator() #@gui : Width = _int(1024,1,4096) #@gui : Height = _int(1024,1,4096) #@gui : Size = float(0.5,0,3) #@gui : X-Angle = float(57,0,360) #@gui : Y-Angle = float(41,0,360) #@gui : Z-Angle = float(21,0,360) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong") #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") _fx_imageobject3d : to_rgb foreach { _fx_imageobject3d$1$2 } db3d 0 fx_imageobject3d : _fx_imageobject3d "_",$1 fx_render3d ${2--1} fx_imageobject3d_preview : w,h={w},{h} _fx_imageobject3d "_preview_",$1 fx_render3d $w,$h,${4--1} _fx_imageobject3d_0 : imageplane3d _fx_imageobject3d_1 : imagecube3d _fx_imageobject3d_2 : imagepyramid3d _fx_imageobject3d_3 : imagesphere3d 128,64 _fx_imageobject3d_4 : torus3d 100,30,100,50 t3d. .. rm.. _fx_imageobject3d_5 : gyroid3d 24 t3d. .. rm.. _fx_imageobject3d_6 : weird3d 32 t3d. .. rm.. _fx_imageobject3d_7 : cup3d 128 t3d. .. rm.. _fx_imageobject3d_8 : imagerubik3d 5,5 _fx_imageobject3d_preview_0 : imageplane3d _fx_imageobject3d_preview_1 : imagecube3d _fx_imageobject3d_preview_2 : imagepyramid3d _fx_imageobject3d_preview_3 : imagesphere3d 64,32 _fx_imageobject3d_preview_4 : torus3d 100,30,100,50 t3d. .. rm.. _fx_imageobject3d_preview_5 : gyroid3d 8 c3d. n3d. t3d. .. rm.. _fx_imageobject3d_preview_6 : weird3d 12 t3d. .. rm.. _fx_imageobject3d_preview_7 : cup3d 64 t3d. .. rm.. _fx_imageobject3d_preview_8 : imagerubik3d 3,3,5,5 #@gui 3D Lathing : fx_lathing3d, fx_lathing3d_preview(1) #@gui : Resolution = int(76,1,1024) #@gui : Smoothness = float(2,0,5) #@gui : Max Angle = float(361,0,361) #@gui : sep = separator() #@gui : Width = _int(1024,1,4096) #@gui : Height = _int(1024,1,4096) #@gui : Size = float(0.5,0,3) #@gui : X-Angle = float(0,0,360) #@gui : Y-Angle = float(0,0,360) #@gui : Z-Angle = float(0,0,360) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong") #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : Top Layer Defines Object Texture = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") _fx_lathing3d : if $4" && "$!>1 repeat $!-1 { l[0,{$>+1}] { lathe3d. $1,$2%,$3 t3d. .. } } # Textured object else lathe3d $1,$2%,$3 # Non-textured object fi db3d 0 fx_lathing3d : _fx_lathing3d ${1-3},$18 s0,s1=^,^0 fx_render3d[${s$18}] ${4--1} => "pos(0,0),mode(alpha)" fx_lathing3d_preview : fx_lathing3d ${1-3},{w},{h},${6--1} if $18 rm[0] fi #@gui 3D Mesh : fx_mesh3d, fx_mesh3d_preview(1) : * #@gui : 3D Mesh Filename = file("") #@gui : note = note{"(supported file formats are: .obj, .off and .gmz)."} #@gui : Old File = value("0") #@gui : sep = separator() #@gui : X-Axis = point(80,50,0,1,255,0,128,164,1%)_0 #@gui : Y-Axis = point(50,70,0,1,128,255,0,164,1%)_0 #@gui : Scale = point(60,60,0,1,255,200,128,164,0.75%)_0 #@gui : Center = point(50,50,0,1,255,255,255,-100,2%)_0 #@gui : Old Center X = value(-1) #@gui : Old Center Y = value(-1) #@gui : Pre-Rotate / X = choice("0 deg.","45 deg.","90 deg.","135 deg.","180 deg.","225 deg.","270 deg.","315 deg.") #@gui : Pre-Rotate / Y = choice("0 deg.","45 deg.","90 deg.","135 deg.","180 deg.","225 deg.","270 deg.","315 deg.") #@gui : Pre-Rotate / Z = choice("0 deg.","45 deg.","90 deg.","135 deg.","180 deg.","225 deg.","270 deg.","315 deg.") #@gui : Rendering Mode = choice(5,"Bounding Box","Dots","Wireframe","Flat","Flat Shaded","Gouraud Shaded",\ # "Phong Shaded") #@gui : Face Orientation = choice("Normal","Reverted","Double-Sided") #@gui : Materials = choice(2,"Discard Colors","Discard Textures","Keep Textures") #@gui : Recovery Color = color(200,200,200) #@gui : Focale (%) = float(100,0,500) #@gui : Anti-aliasing = _choice(1,"None","x1.5","x2","x3","x4") #@gui : Custom Light = point(50,50,-1,1,255,255,0,-100,3%) #@gui : Reset 3D Position = button() #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé #@gui :       Latest Update: 2022/06/21.") fx_mesh3d : skip "${1=},${2=}" # Parse filter parameters. filename="$1" old_filename="$2" xx,xy,yx,yy,sx,sy,cx,cy,ocx,ocy,\ rotx,roty,rotz,rendering_mode,face_orientation,materials,\ recR,recG,recB,focale,antialias,lx,ly,reset_pos=${3-26} R=30 # Radii (in %) of the virtual ellipse show_bb:=!$rendering_mode if $reset_pos xx,xy,yx,yy,sx,sy,cx,cy,ocx,ocy:=50+$R,50,50,50+$R,60,60,50,50,-1,-1 fi if $! W,H=${-max_wh} else W,H=1024,1024 fi # Size of output layer 0 => $filename ext={x} rm. if e=['$ext'];['$filename']!=0" && "e!='obj'" && "e!='off'" && "e!='gmz' gui_error_preview "Specified filename '"$filename"' is invalid\n(extension '"$ext"' is not supported)." return fi # Get 2D vectors. if $ocx>0" && "[$cx,$cy]!=[$ocx,$ocy] ncx,ncy=$ocx,$ocy else ncx,ncy=$cx,$cy fi ux,uy,vx,vy,sx,sy:="C = [$ncx,$ncy]; [ [$xx,$xy] - C,[$yx,$yy] - C,[$sx,$sy] - C ]" # Compute 3D orientation. Ux,Uy,Uz,Vx,Vy,Vz,Wx,Wy,Wz:=" const R = $R; const R2 = 2*R; coords3d(x,y) = ( _t = atan2(y,x); _n = norm(x,y); _s = int(_n/R)%2?1:-1; _mn = _n%R2; _n = _mn0?_U/_nU:[ 0,0,-1 ]; ); U = coords3d($ux,$uy); V = coords3d($vx,$vy); W = cross(U,V); W/=norm(W); nV = cross(W,U); nV/=norm(nV); dot(nV,V)<0?(nV*=1); [ U,nV,W ]" # Update 2D vector [vx,vy]. vx,vy:="($Vz<0?1:2/norm($Vx,$Vy) - 1)*$R*[$Vx,$Vy]" # Load 3D mesh. l[] { if ['$filename']==['$old_filename']" && "narg($_persistent) $_persistent elif ['$filename']!=0 $filename +boundingbox3d. +store _persistent else # Default object torus3d 100,40,32,17 s3d r.. 3,{h#2/3},1,1,-1 f.. y%2?160-20*x:128+50*x y a y => "Default Torus" +boundingbox3d. +store _persistent fi if $show_bb k. else rm. fi } nbv,nbp:=i[6],i[7] obj_name={b} # Normalize and display. fact:=min($W,$H)*norm($sx,$sy)/$R n3d. c3d. *3d. $fact if $rotx r3d. 1,0,0,{45*$rotx} fi if $roty r3d. 0,1,0,{45*$roty} fi if $rotz r3d. 0,0,1,{45*$rotz} fi pose3d. $Ux,$Vx,$Wx,0,$Uy,$Vy,$Wy,0,$Uz,$Vz,$Wz,0 is_double:=$face_orientation==2 if $face_orientation==1 rv3d. fi foc:=$fact*$focale% if $materials<2 p3d. 2 if !$materials col3d. $recR,$recG,$recB fi fi nlx,nly,nlz:=isnan($lx)?[$W*$cx%,$H*$cy%,-5e8]:[$W*($cx+4*($lx-$cx))%,$H*($cy+4*($ly-$cy))%,-10*$foc] if 0$_is_preview j3d[0] .,$cx%,$cy%,0,1,{$rendering_mode>0?$rendering_mode-1:1},$is_double,1,$foc,$nlx,$nly,$nlz rm. else afact:=arg0($antialias,1,1.5,2,3,4) if $afact>1 foc*=$afact *3d. $afact fi {[$W,$H]*$afact},1,3,-0.1 j3d. ..,$cx%,$cy%,0,1,{$rendering_mode>0?$rendering_mode-1:1},$is_double,1,$foc,$nlx,$nly,$nlz rm.. to_rgba. replace_color. 0,0,-0.1,-0.1,-0.1,255,0,0,0,0 r. $W,$H,1,4,2 if 0$_output_mode>0 k. else blend alpha fi fi # Show user guides in preview. if 0$_is_preview line[0] $cx%,$cy%,{$cx+$ux}%,{$cy+$uy}%,1,255,0,0 line[0] $cx%,$cy%,{$cx+$vx}%,{$cy+$vy}%,1,0,255,0 line[0] $cx%,$cy%,{$cx+$sx}%,{$cy+$sy}%,1,0xF0F0F0F0,255,128,64 line[0] $cx%,$cy%,{$cx+$sx}%,{$cy+$sy}%,1,0x0F0F0F0F,128,64,255 to[0] "[ "$obj_name" ]\nVertices: "$nbv"\nPrimitives: "$nbp,0,0,4% fi =>[0] "name("$obj_name"),pos(0,0)" # Return update filter parameters. u "{"$filename"}{"$filename"}"\ "{"{[$cx+$ux,$cy+$uy]}"}{"{[$cx+$vx,$cy+$vy]}"}{"{[$cx+$sx,$cy+$sy]}"}{"$cx,$cy"}{"$cx"}{"$cy"}"\ "{"$rotx"}{"$roty"}{"$rotz"}{"$rendering_mode"}{"$face_orientation"}{"$materials"}"\ "{"$recR,$recG,$recB"}{"$focale"}{"$antialias"}{"$lx,$ly"}{"$reset_pos"}" fx_mesh3d_preview : skip "${1=},${2=}" if !$! $_preview_area_width,$_preview_area_height,1,4 else gui_merge_layers fi drgba _is_preview=1 fx_mesh3d $"*" #@gui 3D Random Objects : fx_random3d, fx_random3d(1) #@gui : Type = choice("Cube","Cone","Cylinder","Sphere","Torus") #@gui : Density = int(50,1,300) #@gui : Size = float(3,1,20) #@gui : Z-Range = float(100,0,300) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : Rendering = choice(3,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong") #@gui : Opacity = float(1,0,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_random3d : foreach { f3d:=0.5*max(w,h)/tan($5*pi/360) f3d $f3d l3d {$6*$f3d},{$7*$f3d},{$8*$f3d} sl3d $9 ss3d $10 to_rgb ({w},{h},{d},{s}) /. 2 repeat $2 { ({1,@0}) +. {1,@1} *. $3 /. 100 _fx_random3d$1 {^} rm.. r3d. 1,1,0,{u(0,360)} ({u(-1,1)}) *. {1,@0} ({u(-1,1)}) *. {1,@1} +3d... {-2,^},{^},{u(-$4,$4)} rm[-2,-1] col3d. {u(255)},{u(255)},{u(255)} } +3d[2--1] j3d[0] .,50%,50%,0,$12,$11,0,1 k[0] } _fx_random3d0 : box3d $1 _fx_random3d1 : ($1) /. 2 cone3d {^},$1 rm.. _fx_random3d2 : ($1) /. 2 cylinder3d {^},$1 rm.. _fx_random3d3 : sphere3d $1,2 _fx_random3d4 : ($1) /. 3 torus3d $1,{^} rm.. #@gui Ball : fx_ball, fx_ball_preview(0) #@gui : Radius = int(128,1,1024) #@gui : Specular Light = float(0.8,0,8) #@gui : Specular Size = float(1,0,8) #@gui : Shadow = float(1.5,0,4) #@gui : Color = color(255,0,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/27/11.") fx_ball : ball $1,${5-7},${2-4} if $!>1 mv. 0 =>[0] "name(Ball),pos("{``{0,0.5*([${-max_wh}]-[w,h])}}")" else =>[0] "name(Ball)" fi fx_ball_preview : fx_ball $* if $!>1 rv[-2,-1] blend[-2,-1] alpha fi #@gui Circle Art : fx_circle_art, fx_circle_art #@gui : Type = choice(1,"Random","Lissajous spiral") #@gui : Density = float(15,0,100) #@gui : Radius = float(0.5,0,1) #@gui : Modulo = int(8,2,16) #@gui : Anti-Aliasing = bool(1) #@gui : Random Colors = bool(1) #@gui : sep = separator() #@gui : note = note("Lissajous parameters:") #@gui : Curve Length = float(15,0,50) #@gui : Curve Angle = float(0,0,360) #@gui : Minimal Radius = float(0,-5,5) #@gui : Maximal Radius = float(0.5,-5,5) #@gui : X-Dispersion = float(1,0,4) #@gui : Y-Dispersion = float(1,0,4) #@gui : X-Factor = int(1,0,16) #@gui : Y-Factor = int(1,0,16) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/22/08.") fx_circle_art : if !$2 f 0 return fi # Generate object coordinates. if !$1 # Random {round(2*($2^1.5))} rand. -1,1 +rand. -1,1 +rand. -$3,$3 a[-3--1] y else # Spiral {max(1,round($2*$7))} t0:=$8*2*pi/180 rows. 0,2 f. "r = x/(w-1); t = 2*pi*x/$2; !y?(r^$11)*cos("$t0"+$13*t): y==1?(r^$12)*sin("$t0"+$14*t): max(0,$3*($9+($10-$9)*r))" fi # Convert to 3D object. l. { transpose s x,-1 h:=h i[0] ({'CImg3d'},{2*$h},$h) # Header. ++... . -[-4,-2] i .. i[-3,-1] 1,100% a[-6--1] x # Vertices. 1,$h,1,1,5 1,$h,1,1,2*y ++. 1 a[-3--1] x z. 0,5 # Primitives. 3,$h,1,1,1 1,$h,1,1,-1 y a y # Colors + Opacities. } # Render object on selected images. repeat $!-1 { l[$>,-1] { s={0,max(w,h)} rm[0] if $5 {2*$s},{2*$s} +*3d[0] $s # Anti-aliasing. else $s,$s +*3d[0] {$s/2} # No anti-aliasing. fi j3d[1] [2],50%,50%,0,1,2,0,0 rm[2] %. $4 if $6 i.. 100%,100%,1,3 rand.. 0,255 plasma.. 1,1 equalize.. 256 n.. 0,255 blend[-2,-1] shapeaverage fi rv } } rm. n 0,255 if $5 r 50%,50%,1,100%,2 fi #@gui Equation Plot [Parametric] : fx_equation_parametric, fx_equation_parametric #@gui : X(t) = text{"sin(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)"} #@gui : Y(t) = text{"cos(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)"} #@gui : Min-t = float(0,-1000,1000) #@gui : Max-t = float(100,-1000,1000) #@gui : Resolution = int(4096,2,32768) #@gui : Outline Opacity = float(1,0,1) #@gui : Dot Size = int(0,0,16) #@gui : Start Color = color(64,0,0) #@gui : End Color = color(128,0,0) #@gui : Colored Outline = bool(1) #@gui : Antialiasing = bool(1) #@gui : Decoration = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/13/11.") fx_equation_parametric : foreach { w,h={w},{h} rm $5,1,1,2,"t = $3 + x*($4-$3)/($5-1); !c?$1:$2" channels. 0,2 ($8,$11^$9,$12^$10,$13) r. {-2,w},1,1,3,3 a c display_parametric $w,$h,{$6+$14*1.001},$7,$15,$16 } #@gui Equation Plot [Y=f(X)] : fx_equation_plot, fx_equation_plot #@gui : F(X) = text{"X*c+10*cos(X+c+u)"} #@gui : X-Min = float(-10,-100,100) #@gui : X-Max = float(10,-100,100) #@gui : Resolution = int(100,2,1024) #@gui : Channels = int(3,1,32) #@gui : Plot Type = choice(2,"None","Lines","Splines","Bars") #@gui : Vertex Type = choice(0,"None","Points","Crosses 1","Crosses 2","Circles 1","Circles 2","Square 1","Square 2") #@gui : sep = separator() #@gui : note = note("Note : #@gui : Use variable X instead of x in the above equation to take care of the X-min/max settings. #@gui : Variable c refers to the current channel number. #@gui : Variable u refers to a uniformly distributed random value in [0,1]. #@gui : Reduce resolution to be able to view #@gui : separate graph vertices.") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_equation_plot : foreach { w,h={w},{h} rm $4,1,1,$5,"X=$2+($3-$2)*x/($4-1);$1" dg $w,$h,$6,$7,$2,$3 } #@gui Generate Random Portrait : fx_generate_random_portrait, fx_generate_random_portrait_preview(1)+ #@gui : Resolution (px) = _int(800,0,2048) #@gui : Update Portrait = button() #@gui : sep = separator() #@gui : note = note("This filter loads data from the website:") #@gui : url = link("https://thispersondoesnotexist.com/") #@gui : note = note("It means it doesn't perform any calculations on your machine and it requires an active #@gui : Internet connection.") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé. #@gui :       Latest Update: 2022/04/14.") fx_generate_random_portrait : file_tmp=${-path_tmp}fx_random_portrait.png if $! rm[0] fi if isfile(['{/$file_tmp}']) $file_tmp rs. $1 delete $file_tmp else +portrait[] $1 fi mv. 0 fx_generate_random_portrait_preview : file_tmp=${-path_tmp}fx_random_portrait.png if $! rm[0] fi +portrait[] $1 o. $file_tmp mv. 0 #@gui Gradient [Corners] : fx_corner_gradient, fx_corner_gradient #@gui : Color 1 (Up/Left Corner) = color(255,255,255,128) #@gui : Color 2 (Up/Right Corner) = color(255,0,0,255) #@gui : Color 3 (Bottom/Left Corner) = color(0,255,0,255) #@gui : Color 4 (Bottom/Right Corner) = color(0,0,255,255) #@gui : sep = separator() #@gui : Colorspace = choice(1,"sRGB","Linear RGB","Lab") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_corner_gradient : skip ${17=0} foreach { wh:=w,h rm ($1,$5;$9,$13^$2,$6;$10,$14^$3,$7;$11,$15^$4,$8;$12,$16) _gb_fwd $17 r. $wh,1,100%,3 _gb_bwd $17 } #@gui Gradient [Custom Shape] : fx_custom_gradient, fx_custom_gradient_preview(1) #@gui : note = note("Shape selection:") #@gui : Select By = choice("Auto","Dark Pixels","Bright Pixels","Opaque Pixels") #@gui : Smoothness = float(0,0,10) #@gui : Threshold = float(0,0,100) #@gui : Preview Shape = bool(1) #@gui : note = note("Note: Shapes with small strokes may lead to incorrect previews.") #@gui : sep = separator() #@gui : note = note("Gradient parameters:") #@gui : Number of Colors = int(4,2,10) #@gui : Cycles = float(1,1,16) #@gui : Offset = float(0,0,100) #@gui : Shading = float(128,1,256) #@gui : Inner Length = float(100,0,100) #@gui : Outer Length = float(100,0,100) #@gui : Spatial Metric = choice(2,"Chebyshev","Manhattan","Euclidean") #@gui : Color Metric = choice("RGB","HSV","Lab") #@gui : Shade Back to First Color = bool(1) #@gui : Preview Gradient = bool(0) #@gui : Save Gradient As = _text("") #@gui : sep = separator() #@gui : note = note("Color definitions:") #@gui : Colormap Type = choice(1,"Pre-Defined","User-Defined") #@gui : Pre-Defined Colormap = int(0,0,65535) #@gui : 1st Color = color(0,0,0,255) #@gui : 2nd Color = color(255,0,0,255) #@gui : 3rd Color = color(255,255,0,255) #@gui : 4th Color = color(255,255,255,255) #@gui : 5th Color = color(0,255,255,255) #@gui : 6th Color = color(0,255,0,255) #@gui : 7th Color = color(0,0,255,255) #@gui : 8th Color = color(128,128,128,255) #@gui : 9th Color = color(255,0,255,255) #@gui : 10th Color = color(0,0,0,0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2013/03/10.") fx_custom_gradient_preview : skip "${15=}" foreach { if $4 +_fx_custom_gradient1 ${1-14},"$15",${16--1} +erode. 3 -[-2,-1] +dilate. 5 a[-2,-1] c n. 0,255 fx_custom_gradient[0] ${1-14},"$15",${16--1},-1 blend alpha else fx_custom_gradient ${1-14},"$15",${16--1},-1 fi if $14 +_fx_custom_gradient0[] ${1-14},"$15",${16--1} r. {{0,w}-16},16,1,4 frame. 1,1,0 j[0] .,8,{0,h-25} rm. fi } fx_custom_gradient : skip "${15=}" _fx_custom_gradient0 ${1-14},"$15",${16--1} if $-1>=0" && "narg("$15") dir_ggr=${-path_gimp}gradients 0 => ${"normalize_filename \"$15\""} name_ggr={b} rm. output_ggr. $dir_ggr/$name_ggr.ggr,"$15" fi i.. (0^0^0^0) a[-2,-1] x repeat $!-1 { l[$>,-1] { _fx_custom_gradient1[0] ${1-14},"$15",${16--1} +distance[0] 1,$11 +distance[0] 0,$11 *. -1 +[0] 1 +[0,-2,-1] # Signed distance function. m,M={0,[$9%*im,$10%*iM]} -[0] $m *[0] {1,(w-2)/($M-$m)} +[0] 1 round[0] map[0] . } } rm. # Create colormap. _fx_custom_gradient0 : if $16 4,8,1,1,${18-56} permute. yzcx else 8,1,1,3 srand $17 rand. 0,255 to_rgba. fi z. 0,{$5-1} if $13 r. {200*$6}%,1,1,4,0,2 __fx_custom_gradient0. $12,$8 shift. {-round(w*0.5*$7%)},0,0,0,2 z. 0,{w/2-1} else __fx_custom_gradient0. $12,$8 r. {100*$6}%,1,1,4,0,2 shift. {-round(w*$7%)},0,0,0,2 fi __fx_custom_gradient0 : if $1==1 sh. 0,2 rgb2hsv. rm. elif $1==2 sh. 0,2 srgb2rgb. rgb2lab. rm. fi r. {$2*w},1,1,4,3 if $1==1 sh. 0,2 hsv2rgb. rm. elif $1==2 sh. 0,2 lab2rgb. rgb2srgb. rm. fi # Extract shape from image. _fx_custom_gradient1 : b $2% if !$1 # Auto-mode to_a split_opacity if iM>im+32 rm.. >=[0] {100-$3}% else rm. norm n 0,1 if ia>0.5 <=[0] $3% else >=[0] {100-$3}% fi fi elif $1==1 # Dark pixels remove_opacity norm <= $3% elif $1==2 # Bright pixels remove_opacity norm >= {100-$3}% else # Opaque pixels to_a channels 100% >= {100-$3}% fi #@gui Gradient [from Line] : fx_line_gradient, fx_line_gradient_preview(1) #@gui : Starting Point (%) = point(0,0,0,1,255,0,0) #@gui : Ending Point (%) = point(100,100,0,1,64,128,255) #@gui : Sampling = float(100,0,100) #@gui : Length = int(0,0,4096) #@gui : note = note("Note: Set length to 0 to release gradient length constraints.") #@gui : Sort Colors = choice("Don't Sort","By Red Component","By Green Component","By Blue Component", #@gui : "By Luminance","By Blue Chrominance","By Red Chrominance","By Lightness") #@gui : Reverse Gradient = bool(0) #@gui : sep = separator() #@gui : Preview Gradient = bool(1) #@gui : Save Gradient As = _text("") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/29/06.") fx_line_gradient : skip "${10=}" _fx_line_gradient $* if narg("$10") dir_ggr=${-path_gimp}gradients 0 => ${"normalize_filename \"$10\""} name_ggr={b} rm. output_ggr. $dir_ggr/$name_ggr.ggr,"$10" fi r 100%,64,1,100% fx_line_gradient_preview : foreach { to_rgba if $9 +_fx_line_gradient $* fi l[0] { line $1%,$2%,$3%,$4%,1,0xF0F0F0F0,255,255,255,255 line $1%,$2%,$3%,$4%,1,0x0F0F0F0F,0,0,0,255 } if $!>1 r. {{0,w}-32},32,1,4,1 frame. 1,1,0,0,0,255 j[0] [1],16,{{0,h}-48} rm. fi } _fx_line_gradient : at_line $1%,$2%,0,$3%,$4%,0 r {max(0.1,$5)}%,1,1,100%,1 m "feature1 : channels 0" m "feature2 : channels 1" m "feature3 : channels 2" m "feature4 : to_rgb luminance" m "feature5 : to_rgb rgb2ycbcr channels 1" m "feature6 : to_rgb rgb2ycbcr channels 2" m "feature7 : to_rgb srgb2rgb rgb2lab channels 0" if $7 foreach { +feature$7 rv a y sort +,x rows 1 } fi if $6 r $6,1,1,100%,3 fi if $8 mirror x fi #@gui Gradient [Linear] : fx_linear_gradient, fx_linear_gradient #@gui : Starting Color = color(0,0,0,255) #@gui : Ending Color = color(255,255,255,255) #@gui : Swap Colors = bool(0) #@gui : Angle = float(45,0,360) #@gui : Fade Start = float(0,0,100) #@gui : Fade End = float(100,0,100) #@gui : sep = separator() #@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_linear_gradient : skip ${13=0} foreach { wh:=w,h rm ($1^$2^$3^$4) ($5^$6^$7^$8) if $9 rv[-2,-1] fi r $wh _gb_fwd $13 fade_linear $10,$11,$12 _gb_bwd $13 } #@gui Gradient [Radial] : fx_radial_gradient, fx_radial_gradient #@gui : Starting Color = color(0,0,0,255) #@gui : Ending Color = color(255,255,255,255) #@gui : Swap Colors = bool(0) #@gui : Fade Start = float(0,0,100) #@gui : Fade End = float(100,0,100) #@gui : Center (%) = point(50,50,0,1,255) #@gui : sep = separator() #@gui : Colorspace = choice(0,"sRGB","Linear RGB","Lab") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/29/06.") fx_radial_gradient : skip ${14=0} foreach { wh:=w,h rm ($1^$2^$3^$4) ($5^$6^$7^$8) if $9 rv[-2,-1] fi r $wh _gb_fwd $14 100%,100% =. 1,$12%,$13% distance. 1 _fade $10,$11 _gb_bwd $14 } #@gui Gradient [Random] : fx_random_gradient, fx_random_gradient #@gui : Density = int(32,1,1024) #@gui : Seed = int(0,0,65535) #@gui : Smoothness = float(0,0,10) #@gui : Color Balance = color(128,128,128) #@gui : Opacity = float(1,0,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/08/04.") fx_random_gradient : foreach { to_rgba 100%,100% srand $2 eval "repeat ($1,n, x = v(w - 1); y = v(h - 1); i(x,y) = 1; i(#0,x,y,0,0) = v(255); i(#0,x,y,0,1) = v(255); i(#0,x,y,0,2) = v(255); i(#0,x,y,0,3) = $7*255 + (1-$7)*v(255); )" if $7!=1 sh.. 100% n. 0,255 rm. fi ==. 0 sh.. 0,2 srgb2rgb. rm. inpaint_pde.. [1],100%,1 rm. b $3% n 0,255 sh 0,2 rgb2srgb. balance_gamma. ${4-6} rm. } #@gui Hypotrochoid : fx_hypotrochoid, fx_hypotrochoid(1) #@gui : Periods = int(37,1,100) #@gui : Outer Radius (%) = float(100,0,300) #@gui : Inner Radius (%) = float(74,0,300) #@gui : Distance to center (%) = float(80,0,300) #@gui : Thickness (%) = float(0.5,0,5) #@gui : Color = color(255,255,255,255) #@gui : Anti-aliasing = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/01/25.") fx_hypotrochoid : {0,[w,h]*($10?1.5:1)} eval " const M = min(w,h)/2; const A = M*$2%; const B = A*$3%; const H = B*$4%; const S = M*$5%; const F = (A - B)/max(1e-5,B); const AmB = A - B; hypotrochoid(t) = (_t = t; round([ w/2 + AmB*cos(_t) + H*cos(F*_t), h/2 + AmB*sin(_t) - H*sin(F*_t) ])); oX = hypotrochoid(t); dt = 1; for (t = 0, t<$1*2*pi, do ( X = hypotrochoid(t + dt); dist = abs(X[0] - oX[0]) | abs(X[1] - oX[1]); !dist?(dt*=2): dist>1?(dt/=1.25): (t+=dt), dist!=1; ); S<1?(I(X) = $9):ellipse(X,S,S,0,1,$9); oX = X; )" r. [0],[0],1,1,2 channels. -3,0 sh. 0,2 fc. ${6-8} rm. blend[0,-1] alpha #@gui Lightning : fx_lightning, fx_lightning_preview #@gui : note = note{"Global parameters:"} #@gui : Number of Streaks = int(20,1,1024) #@gui : Size (%) = float(90,0,150) #@gui : Resolution = int(256,2,4096) #@gui : Randomness = float(3,0,16) #@gui : Smoothness = float(1.5,0,10) #@gui : Balance = float(0.75,0,1) #@gui : Color = color(255,255,255,255) #@gui : Seed = int(0,0,65535) #@gui : sep = separator() #@gui : note = note{"Initial streak:"} #@gui : XY-Coordinates (%) = point(50,5,0,1) #@gui : Angle (deg) = float(0,-180,180) #@gui : Thickness (px) = int(6,1,64) #@gui : Blur = float(0.2,0,3) #@gui : sep = separator() #@gui : note = note{"Auxiliary streaks:"} #@gui : Min Offset (%) = float(25,0,100) #@gui : Max Offset (%) = float(60,0,100) #@gui : Min Length (%) = float(95,0,200) #@gui : Max Length (%) = float(100,0,200) #@gui : Min Angle Deviation (deg) = float(30,0,180) #@gui : Max Angle Deviation (deg) = float(40,0,180) #@gui : Thickness Factor = float(-0.25,-1,1) #@gui : Blur Factor = float(-0.1,-1,1) #@gui : Opacity Factor = float(-0.20,-1,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/27/11.") fx_lightning : foreach { 100%,100% l. { fact:=max(w,h)/$3*$2% srand $11 repeat $1 { if $!<=1 i=0 new_level=1 new_length=$3 new_x=0 new_y=0 new_angle=$14 else i:=v(1,max(1,($!-1)*$6)) level={$i,@-2} angle={$i,@-1} nb_points={$i,i[6]} p:=round(($nb_points-2)*u($17%,$18%)) new_level:=$level+1 new_length:=max(2,round(($nb_points-$p)*u($19%,$20%))) new_x={$i,i[8+3*$p]} new_y={$i,i[9+3*$p]} new_angle:=$angle+u($21,$22)*(u>0.5?1:-1) fi _fx_lightning $new_length,$4,$5 r3d. 0,0,1,$new_angle +3d. $new_x,$new_y +*3d. $fact [0],[0] j3d. ..,$12%,$13%,0,1,1,0,0 rm.. dilation:=$15*($23>0?1.5:10)^($23*($new_level-1)) blur:=max(0,-1+(1+$16)*($24>0?2:5)^($24*($new_level-1))) opacity:=min(1,$10/255*(2^($25*($new_level-1)))) dilate. $dilation b. $blur% n. 0,1 *. $opacity max[0,-1] ($new_level;$new_angle) a[-2,-1] y progress {($>*100)/($1-1)} } k[0] * 255 i[0] 100%,100%,1,3 fc[0] ${7-9} a c } rv } fx_lightning_preview : foreach { fx_lightning $* rv blend alpha } _fx_lightning : l[] { ({'CImg3d'},$1,{$1-1}) 1,$1 noise. $2,1 cumulate. b. $3 shift. 0,1 1,100%,1,1,y 1,100% a[-3--1] x 1,{h-1},1,1,2 +f. y ++. 1 a[-3--1] x 4,100%,1,1,1 y a y } #@gui Lissajous : fx_lissajous, fx_lissajous(1) #@gui : Resolution = int(4096,2,8192) #@gui : sep = separator() #@gui : X-Size = float(0.9,0,2) #@gui : Y-Size = float(0.9,0,2) #@gui : Z-Size = float(3,1,10) #@gui : sep = separator() #@gui : X-Multiplier = float(8,0,32) #@gui : Y-Multiplier = float(7,0,32) #@gui : Z-Multiplier = float(0,0,32) #@gui : sep = separator() #@gui : X-Offset = float(0,0,1) #@gui : Y-Offset = float(0,0,1) #@gui : Z-Offset = float(0,0,1) #@gui : sep = separator() #@gui : X-Angle = float(0,0,360) #@gui : Y-Angle = float(0,0,360) #@gui : Z-Angle = float(0,0,360) #@gui : sep = separator() #@gui : Thickness = float(0,0,50) #@gui : Color = color(255,255,255,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/18/04.") fx_lissajous : foreach { to_rgba {w},{h} f3d {0.5*max(w,h)/tan($4*pi/360)} lissajous3d $1,$5,$8,$6,$9,$7,$10 r3d. 0,0,1,$13 r3d. 0,1,0,$12 r3d. 1,0,0,$11 *3d. {0.5*$2*{-2,w}},{0.5*$3*{-2,h}},{0.5*$4*max({-2,w},{-2,h})} col3d. 1 j3d.. .,50%,50%,0,1,1,0,0 rm. distance. 1 >. $14% *.. . ==. 0 r. 100%,100%,1,4 sh. 0 *. $15 rm. sh. 1 *. $16 rm. sh. 2 *. $17 rm. sh. 3 *. $18 rm. +[-2,-1] } #@gui Mandelbrot - Julia Sets : fx_mandelbrot, fx_mandelbrot_preview #@gui : X0 = value(-2) #@gui : Y0 = value(-2) #@gui : X1 = value(2) #@gui : Y1 = value(2) #@gui : note = note{"Fractal Type:"} #@gui : Fractal Set = choice("Mandelbrot","Julia") #@gui : Iterations = int(1024,16,65535) #@gui : X-Seed (Julia) = float(0.317,-2,2) #@gui : Y-Seed (Julia) = float(0.03,-2,2) #@gui : sep = separator() #@gui : note = note{"Colormap:"} #@gui : Number of Colors = int(16,2,2048) #@gui : Smoothness = int(8,1,256) #@gui : Seed = int(255,0,65536) #@gui : sep = separator() #@gui : note = note{"Navigation:"} #@gui : Zoom Center = point(50,50,0,0,255,255,255,200) #@gui : Zoom Factor = float(0.25,0,1) #@gui : Zoom In = button() #@gui : Center = button() #@gui : Zoom Out = button() #@gui : Display Coordinates = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/06/27.") fx_mandelbrot : if !narg($_size) _size:=max(w,h) fi rm $_size,$_size mandelbrot ${1-4},$6,{$5?[1,$7,$8]:[0,0,0]} srand $11 $9,1,1,3 rand. 0,255 r. {$9*$10},1,1,3,3 point. 0 map.. .,3 rm. fx_mandelbrot_preview : _size:=min(${-gui_preview_wh}) if "$15 || $16 || $17" x0,y0,x1,y1:="P0 = [${1,2}]; dP = [${3,4}] - P0; C = P0 + [${12,13}]%*dP; zfact = $14*($15?1:$16?0:-2); dC = 0.5*dP*(1 - 0.98*zfact); [C - dC,C + dC]" status=\{$x0\}\{$y0\}\{$x1\}\{$y1\}\{$5\}\{$6\}\{$7\}\{$8\}\{$9\}\{$10\}\{$11\}\ \{50,50\}\{$14\}\{0\}\{0\}\{0\}\{$18\} px,py=50 else x0,y0,x1,y1=${1-4} status= px,py=${12,13} fi fx_mandelbrot $x0,$y0,$x1,$y1,${5--1} x0r,y0r,x1r,y1r:="C = ["$px,$py"]%*w; dC = 0.5*w*(1 - 0.98*$14); round([C - dC, C + dC - 1])" rectangle $x0r,$y0r,$x1r,$y1r,0.7,0xF0F0F0F0,255,255,255,255 rectangle $x0r,$y0r,$x1r,$y1r,0.7,0x0F0F0F0F,0,0,0,255 if $18 to "Z0 = ( "{_$x0}" , "{_$y0}" )\nZ1 = ( "{_$x1}" , "{_$y1}" )",2,2,16 fi u $status #@gui Algorithm A : fx_memoakten_algorithm_a,fx_memoakten_algorithm_a_preview(1)* #@gui : Random Seed = int(0,0,65535) #@gui : Density of Initial Points (%) = float(20,0,100) #@gui : Rate of Primary Segments (%) = float(30,0,100) #@gui : Rate of Secondary Segments (%) = float(30,0,100) #@gui : Max Secondary Segments Occurences = int(2,1,16) #@gui : Secondary Segments Centering Constraint (%) = float(50,0,100) #@gui : Min Segment Length (%) = float(10,0,100) #@gui : Max Segment Length (%) = float(50,0,100) #@gui : Angle Constraint (%) = float(40,0,100) #@gui : Line Thickness (px) = int(3,1,16) #@gui : Density of Color Fill (%) = float(60,0,100) #@gui : Anti-aliasing = choice(1,"None","x1.5","x2") #@gui : sep = separator() #@gui : Foreground Color = color(0,0,0) #@gui : Background Color = color(255,255,255) #@gui : Fill Color 1 = color(255,0,0) #@gui : Fill Color 2 = color(255,128,0) #@gui : Fill Color 3 = color(255,255,0) #@gui : Fill Color 4 = color(0,0,0) #@gui : sep = separator() #@gui : note = note{"Note: This filter has been implemented after reading the following tweet from Memo Akten:"} #@gui : url = link("https://twitter.com/memotv/status/1556619064491102209") #@gui : note = note{"I (David) found the idea really nice so I tried to convert the algorithm into a G'MIC script. \ # The code is not optimized and probably very perfectible, but at least it works :)}" #@gui : sep = separator() #@gui : note = note{"Authors: Memo Akten (algorithm) and \ # David Tschumperlé (G'MIC conversion) #@gui :       Latest Update: 2022/08/09."} fx_memoakten_algorithm_a_preview : fx_memoakten_algorithm_a $* gui_crop_resize_preview fx_memoakten_algorithm_a : seed,point_density,rate_ps,rate_ss,max_occ,centering,msl,Msl,angt,thickness,color_density,antialias,\ Rf,Gf,Bf,Rb,Gb,Bb,R0,G0,B0,R1,G1,B1,R2,G2,B2,R3,G3,B3=${1-30} fa:=arg0($antialias,1,1.5,2) foreach { nm={n} W,H={w},{h} rm srand $seed # Extract non-overlapping points. $W,$H noise_poissondisk. {max(8,min(w,h)/max(2,$point_density))} 1,1,1,2 eval.. "begin(da_push(#1,[0,0],[$W - 1,0],[$W - 1,$H - 1],[0,$H - 1])); i?da_push(#1,[x,y])" rm.. # Draw random non-intersecting segments. 1,1,1,2 1,{"max(1,round($rate_ps*da_size(#0)%))"},1,1,">"${-math_lib}" begin( const mwh = min($W,$H); da_push(#1,[0,1],[1,2],[2,3],[3,0]); nb_pts = da_size(#0) ); do ( do (p0 = v(nb_pts - 1); p1 = v(nb_pts - 1), p0==p1); P0 = I[#0,p0]; P1 = I[#0,p1], !inrange(norm(P0 - P1),mwh*$msl%,mwh*$Msl%) ); is_valid = 1; repeat (da_size(#1),k, S = I[#1,k]; q0 = S[0]; q1 = S[1]; Q0 = I[#0,q0]; Q1 = I[#0,q1]; is_valid&=!do_intersect(P0[0],P0[1],P1[0],P1[1],Q0[0],Q0[1],Q1[0],Q1[1]); !is_valid?break(); ); is_valid?(da_push(#1,[p0,p1])); " rm. channels. 0,2 # Link segments together. 1,{"max(1,round($rate_ss*da_size(#1)))*100"},1,1,">"${-math_lib}" const cmin = lerp(0,0.5,$centering%); const cmax = lerp(1,0.5,$centering%); const mwh = min($W,$H); const angt = 1 - $angt%; nb_seg = da_size(#1); do (s0 = v(nb_seg - 1); s1 = v(nb_seg - 1), s0==s1 || i(#1,0,s0,0,2)>$max_occ || i(#1,0,s1,0,2)>$max_occ); S0 = I[#1,s0]; S1 = I[#1,s1]; P0 = I[#0,S0[0]]; P1 = I[#0,S0[1]]; dP = P1 - P0; nP = norm(dP); ndP = dP/nP; Q0 = I[#0,S1[0]]; Q1 = I[#0,S1[1]]; dQ = Q1 - Q0; nQ = norm(dQ); ndQ = dQ/nQ; R0 = round(lerp(P0,P1,u(cmin,cmax))); R1 = round(lerp(Q0,Q1,u(cmin,cmax))); dR = R1 - R0; nR = norm(dR); ndR = dR/nR; inrange(nR,mwh*$msl%,mwh*$Msl%) && abs(dot(ndR,ndP))y>=4?(S = I; polygon(#-1,2,$fa*I[#0,S[0]],$fa*I[#0,S[1]],1,0))" k. # Fill regions with random colors. srand $seed +label_fg. 0,0 1,{iM+1},1,6,[0,0,y,0,0,0] eval.. ">++i[#-1,i]" sh. 0 area_max:=iM rm. f. "[ i0*u(0.25,1),i0,y,0,0,0 ]" sort. +,y channels. 1,100% f. "begin(N = max(1,h*$color_density%)); y>N || i0<8 || i0>=$area_max?[i0,i1,$Rb,$Gb,$Bb]:( rand = u; rand<0.25?[i0,i1,$R0,$G0,$B0]: rand<0.5?[i0,i1,$R1,$G1,$B1]: rand<0.75?[i0,i1,$R2,$G2,$B2]: [i0,i1,$R3,$G3,$B3] )" channels. 1,100% sort. +,y channels. 1,100% point. 0,0,0,1,$Rf,$Gf,$Bf map.. . rm. # Draw thick segments. ==.. 0 if $thickness<=3 dilate[0] $thickness else dilate_circ[0] $thickness fi f. "i(#0)?[$Rf,$Gf,$Bf]:I" rm.. # Anti-aliased rendering. r $W,$H,1,3,2 => $nm } #@gui Neon Lightning : fx_neon_lightning, fx_neon_lightning(1) #@gui : Source (%) = point(50,50) #@gui : R0 = float(0,0,100) #@gui : Destination (%) = point(50,50) #@gui : R1 = float(100,0,100) #@gui : sep = separator() #@gui : Density = int(50,1,512) #@gui : Glow = float(0.7,0,5) #@gui : Thickness = float(3,0,20) #@gui : sep = separator() #@gui : Color = color(130,80,50) #@gui : Color Dispersion = float(0.25,0,1) #@gui : Transparency = float(0,0,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/30/06.") fx_neon_lightning : d:=$13*255 foreach { 100%,100%,1,4 rm[0] repeat $7 { x0:=max(0,min(w,$1+u(-$3,$3))) y0:=max(0,min(h,$2+u(-$3,$3))) x1:=max(0,min(w,$4+u(-$6,$6))) y1:=max(0,min(h,$5+u(-$6,$6))) u0,v0,u1,v1:=u([0,0,0,0],[100,100,100,100]) R:=max(0,min(255,u($10-$d,$10+$d))) G:=max(0,min(255,u($11-$d,$11+$d))) B:=max(0,min(255,u($12-$d,$12+$d))) spline $x0%,$y0%,$u0%,$v0%,$x1%,$y1%,$u1%,$v1%,1,$R,$G,$B,1 } s c,-3 b[0] 3% distance. 1 *. -1 c. -{$9+1e-5},0 n. 0,1 sqrt. +b. $8%,1 n. 0,1 sqrt. n[-2,-1] 0,255 max[-2,-1] . blend[0,1] value smooth 5,0,1,0.5,2,10,0 /. 255 ^. $14 *. 255 a c c 0,255 } #@gui Newton Fractal : fx_newton_fractal, fx_newton_fractal_preview #@gui : X0 = value(-2) #@gui : Y0 = value(-2) #@gui : X1 = value(2) #@gui : Y1 = value(2) #@gui : note = note{"Fractal Type:"} #@gui : Expression = choice(2,"Custom","z^^2 - 1","z^^3 - 1","z^^5 - 1","z^^6 + z^^3 - 1","z^^8 + 15*z^^4 - 1") #@gui : p(z) = text{"rot(35°)*z^^3 - z^^2 + 1"}_1 #@gui : p'(z) = text{"3*z^^2 - 2*z"}_1 #@gui : p''(z) = text{"6*z - 2"}_1 #@gui : Descent method = choice(1,"Secant","Newton","Householder") #@gui : Max iterations = int(200,16,1024) #@gui : Precision = float(2,0,12) #@gui : sep = separator() #@gui : note = note{"Rendering:"} #@gui : Coloring = choice(1,"By Custom Expression","By Iteration","By Value") # # Color by iteration # #@gui : Number of Colors = int(16,2,2048) #@gui : Smoothness = int(8,1,256) #@gui : Seed = int(255,0,65536) # # Color by value # #@gui : Colorspace = choice(2,"HSI","HSL","HSV")_0 #@gui : Hue min (%) = float(100,0,500)_0 #@gui : Hue max (%) = float(150,0,500)_0 #@gui : Lightness min (%) = float(20,0,500)_0 #@gui : Lightness max (%) = float(400,0,500)_0 # # Custom coloring # #@gui : Colorspace = choice(3,"RGB,"HSI","HSL","HSV","Lab")_0 #@gui : Pre-Process = choice(2,"None","Equalize","Normalize","Equalize and Normalize")_0+ #@gui : note = note{"Tips for Custom expressions:\n #@gui : - Variables i0,i1 stand for the real and imaginary parts of the iterated complex number.\n #@gui : - Variable i2 is the number of iterations required for convergence.\n #@gui : - Variable z is the complex number with value [ i0,i1 ].\n #@gui : - Functions p(z), dp(z) and d2p(z) are the expressions used for computing the fractal. #@gui : "} #@gui : Channel #1 = text{"carg(-z)"}_0 #@gui : Channel #2 = text{"(i0 + i1)/2"}_0 #@gui : Channel #3 = text{"10*(i2^0.4)"}_0 #@gui : Post-Process = choice(0,"None","Equalize","Normalize","Equalize and Normalize")_0 # # Basic color adujstment # #@gui : Brightness (%) = float(0,-100,100) #@gui : Contrast (%) = float(0,-100,100) #@gui : Gamma (%) = float(0,-100,100) #@gui : Hue (%) = float(0,-100,100) #@gui : Saturation (%) = float(0,-100,100) #@gui : Equalization (%) = float(0,0,100) #@gui : Anti-aliasing = choice(2,"x1","x1.5","x2","x2.5","x3","x3.5","4") #@gui : note = note{"Note: Anti-aliasing is applied on final rendering only, not on preview."} #@gui : antialias_note = value(0)_2- #@gui : sep = separator() # # Navigation # #@gui : note = note{"Navigation:"} #@gui : Zoom Center = point(50,50,0,0,255,255,255,200) #@gui : Zoom Factor = float(0.5,0,1) #@gui : Angle = float(0,-180,180) #@gui : Zoom In = button() #@gui : Center = button() #@gui : Zoom Out = button() #@gui : Reset View = button() #@gui : Display Coordinates on Preview Window = bool(1) #@gui : Preview subsampling = choice(2,"None","x1.5","x2","x2.5","x3","x3.5","x4") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2019/01/09.") fx_newton_fractal : skip "${6=},${7=},${8=},${22=},${25=},${28=}" foreach { if !narg($_size) _size:=max(w,h) fi rm antialias:=arg(1+$33,1,1.5,2,2.5,3,3.5,4) {$antialias*[$_size,$_size]} if $5==1 pz="z^^2 - 1" dpz="2*zn" d2pz="2" elif $5==2 pz="z^^3 - 1" dpz="3*z^^2" d2pz="6*z" elif $5==3 pz="z^^5 - 1" dpz="5*z^^4" d2pz="20*z^^3" elif $5==4 pz="z^^6 + z^^3 - 1" dpz="6*z^^5 + 3*z^^2" d2pz="30*z^^4 + 6*z" elif $5==5 pz="z^^8 + 15*z^^4 - 1" dpz="8*z^^7 + 60*z^^3" d2pz="56*z^^6 + 180*z^^2"; else pz="$6" dpz="$7" d2pz="$8" fi if !narg($pz) pz="[1,0]" fi if !narg($dpz) dpz="[1,0]" fi if !narg($d2pz) d2pz="[1,0]" fi newton_fractal ${1-4},$38,$9,$10,{10^-$11},$pz,$dpz,$d2pz if $12==1 # Color by iteration channels 100% srand $15 $13,1,1,3 rand. 0,255 r. {$13*$14},1,1,3,3 point. 0 map.. .,3 rm. elif $12==2 # Color by value f "[ atan2(i1,i0),1,i2 ]" s c n... {[$17,$18]*360%} n. {[$19,$20]%} c. 0,1 a c ${"arg0 $16,hsi,hsl,hsv"}2rgb else # Custom coloring if $22 # Pre-process values s c,-2 if $22&1 equalize 1024 fi if $22&2 /[-2] {-2,max(1e-5,abs(im),abs(iM))} n. 0,1 fi a c fi f "*begin( p(z) = ("$pz"); dp(z) = ("$dpz"); d2p(z) = ("$d2pz"); ); z = [ i0,i1 ]; [ (0;$23),(0;$24),(0;$25) ]" if $26 # Post-process values s c if $26&1 equalize 1024 fi if $26&2 normalize 0,1 fi a c fi * 255 mod 256 if $21 ${"arg $21,hsi8,hsl8,hsv8,lab8"}2rgb fi # Convert to RGB colors fi rs $_size if $32 ac "+equalize 1024 j.. .,0,0,0,0,{$32%} rm.",ycbcr_y fi adjust_colors ${27-31},0,0,0,255 } fx_newton_fractal_preview : skip "${6=},${7=},${8=},${22=},${25=},${28=}" is_custom_expression:=$5?1:2 is_color_by_custom:=$12?0:2 is_color_by_iter:=$12==1?2:0 is_color_by_value:=$12==2?2:0 _size0:=min(${-gui_preview_wh}) _size:=$_size0/arg(1+$44,1,1.5,2,2.5,3,3.5,4) angle=$38 if "$39 || $40 || $41" x0,y0,x1,y1:="P0 = [${1,2}]; dP = [${3,4}] - P0; M = P0 + 0.5*dP; C = P0 + [${35,36}]%*dP; C = M + rot(-$38°)*(C - M); zfact = $37*($39?1:$40?0:-2); dC = 0.5*dP*(1 - 0.98*zfact); [ C - dC, C + dC ]" px,py=50 elif $42 x0,y0,x1,y1=-2,-2,2,2 px,py=50 angle=0 else x0,y0,x1,y1=${1-4} px,py=${35,36} fi fx_newton_fractal $x0,$y0,$x1,$y1,$5,"$6","$7","$8",${9-22},"$23","$24","$25",${26-32},0,${34-37},$angle,${39--1} foreach { rs $_size0,,1 x0r,y0r,x1r,y1r:="C = [ "$px,$py" ]%*w; dC = 0.5*w*(1 - 0.98*$37); round([ C - dC, C + dC - 1 ])" rectangle $x0r,$y0r,$x1r,$y1r,0.7,0xF0F0F0F0,255,255,255,255 rectangle $x0r,$y0r,$x1r,$y1r,0.7,0x0F0F0F0F,0,0,0,255 if $43 to "Z0 = ( "{_$x0}" , "{_$y0}" )\nZ1 = ( "{_$x1}" , "{_$y1}" )",2,2,16 fi } u "{"$x0"}{"$y0"}{"$x1"}{"$y1"}{$5}"\ "{$6}_"$is_custom_expression\ "{$7}_"$is_custom_expression\ "{$8}_"$is_custom_expression\ "{$9}{$10}{$11}{$12}"\ "{$13}_"$is_color_by_iter\ "{$14}_"$is_color_by_iter\ "{$15}_"$is_color_by_iter\ "{$16}_"$is_color_by_value\ "{$17}_"$is_color_by_value\ "{$18}_"$is_color_by_value\ "{$19}_"$is_color_by_value\ "{$20}_"$is_color_by_value\ "{$21}_"$is_color_by_custom\ "{$22}_"$is_color_by_custom\ "{$23}_"$is_color_by_custom\ "{$24}_"$is_color_by_custom\ "{$25}_"$is_color_by_custom\ "{$26}_"$is_color_by_custom\ "{$27}{$28}{$29}{$30}{$31}{$32}{$33}"\ "{$34}"_{$33?2:0}\ "{"$px,$py"}{$37}{"$angle"}{0}{0}{0}{0}{$43}{$44}" #@gui Plasma : fx_plasma, fx_plasma(0) #@gui : Alpha = float(0.5,0,5) #@gui : Beta = float(0,0,100) #@gui : Scale = int(8,2,10) #@gui : Randomize = bool(0) #@gui : Transparency = bool(0) #@gui : Color Balance = color(128,128,128) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/20/03.") fx_plasma : skip ${4=0},${5=0} if $5 to_rgba else to_rgb fi if $4 rand 0,255 fi plasma $1,$2,$3 n 0,255 balance_gamma ${6-8} #@gui Quick Copyright : fx_quick_copyright, fx_quick_copyright_preview(1) #@gui : Text = text{"\\251 John Doe"} #@gui : Font = choice(22,"Acme","Arial","Arial Black","Black Ops One","BlackChancery","Cabin Sketch",\ # "Caprasimo","Carnevalee Freakshow","Cheese Burger","Cheque","Cheque-Black","Chlorinar","Comic Sans MS",\ # "Courier New","Creepster","Georgia","Impact","Lobster","Luckiest Guy","Macondo","MedievalSharp","Odin Rounded",\ # "Oswald","Palatino Linotype","Playfair Display","Roboto","Sacramento","Satisfy","Sofia","Tex Gyre Adventor",\ # "Times New Roman","Titan One","Verdana") #@gui : Size (%) = float(10,0,50) #@gui : Bold Face = bool(0) #@gui : Color = color(255,255,255) #@gui : Opacity (%) = float(90,0,100) #@gui : sep = separator() #@gui : Outline (%) = float(3,0,20) #@gui : Outline Color = color(0,0,0) #@gui : sep = separator() #@gui : Position = choice(4,"Up-Left","Up-Right","Center","Bottom-Left","Bottom-Right","Corners","Everywhere") #@gui : X-Offset (%) = float(1,0,50) #@gui : Y-Offset (%) = float(1,0,50) #@gui : Orientation (deg.) = float(-180,0,180) ##@gui : Orientation (deg.) = choice(2,"-90 deg.","-45 deg.","0 deg.","+45 deg.","+90 deg.","+135 deg.","+180 deg.") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2024/02/21.") fx_quick_copyright : if "['$1']==0 || !$3" return fi foreach { font_size:=ceil(min(w,h)*$3%) outline_size:=$9?max(1,$font_size*$9%):0 0 t. "$1",0,0,${"font $2,"$font_size",$4"},1,255 autocrop. if !$outline_size # No outline i.. 100%,100%,1,4 fc.. ${5-7},255 a[-2,-1] c else # Outline r. {[w,h]+2*$outline_size+1},1,100%,0,0,0.5,0.5 +dilate. {2*$outline_size+1} 100%,100%,1,3x2 fc. ${10-12} j.. .,0,0,0,0,1,[-3],255 fc. ${5-7} j.. .,0,0,0,0,1,[-4],255 rm[-4,-1] to_rgba. rv[-2,-1] a[-2,-1] c autocrop. fi rotate. $16 repeat $13<5?1:$13<6?4:5 { ind:=$13<5?$13:$13<6?($><2?$>:1+$>):$> dx,dy:="const e = $14%; const f = 1 - e; const g = $15%; const h = 1 - g; arg0("$ind",[e,g],[f,g],[0.5,0.5],[e,h],[f,h])" ja.. .,$dx~,$dy~,0,0,{$8%} } rm. } fx_quick_copyright_preview : fx_quick_copyright "$1",${2--1} outline_visibility:=$9?2:1 offset_visibility:=$13!=2?2:1 u "{$1}{$2}{$3}{$4}{${5-7}}{$8}{$9}"\ "{${10-12}}"_$outline_visibility\ "{$13}{$14}"_$offset_visibility"{$15}"_$offset_visibility\ "{$16}" #@gui Rainbow : fx_rainbow, fx_rainbow #@gui : Left Position = float(80,0,100) #@gui : Right Position = float(80,0,100) #@gui : Left Slope = float(175,0,400) #@gui : Right Slope = float(175,0,400) #@gui : Thinness = float(3,0.1,8) #@gui : Opacity = float(80,0,199) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_rainbow : foreach { 100%,100% spline. 0,$1%,100,{-$3}%,100%,$2%,100,$4%,1,1 flood. 0,0,0,0,0,1,1 flood. {w-1},0,0,0,0,1,1 distance. 0 c. 0,255 n. 0,{$5*255} palette rainbow +luminance. c. 0,{min(100,200-$6)}% n. 0,255 a[-2,-1] c map.. . rm. if $6<100 sh. 3 *. {$6/100} rm. fi blend alpha } #@gui Random Signature : fx_random_signature, fx_random_signature_preview(1) #@gui : Seed = int(16,0,65536) #@gui : Complexity = int(16,2,32) #@gui : Top-Left Corner = point(10,10,0,1,255,128,0,200,10)_0 #@gui : Bottom-Right Corner = point(90,90,0,1,255,128,0,200,10)_0 #@gui : Thickness (%) = float(1.5,0,10) #@gui : Tilt (deg.) = float(0,-180,180) #@gui : Tilt Strength (%) = float(75,0,100) #@gui : Color = color(0,0,0,255) #@gui : Anti-Alias = bool(1) #@gui : Change Seed = button() #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2024/01/04.") fx_random_signature : x0,y0,x1,y1:=min($3,$5),min($4,$6),max($3,$5),max($4,$6) dx,dy:=$x1-$x0,$y1-$y0 foreach { nm=${"-gui_layer_name"} srand $1 $2,1,1,4,"u(1024)" s. c,2 rbf[-2,-1] 1024 W,H={0,[max(1,$dx*(w-1)%),max(1,$dy*(h-1)%)]} thickness:=max($dx*w#0%,$dy*h#0%)*$7% thickness2:=round($thickness/2) n.. $thickness2,{$W-1-$thickness2} n. $thickness2,{$H-1-$thickness2} a[-2,-1] c {[$W,$H]*($14+1)} if $14 *.. 2 fi curve. ..,{$thickness*($14+1)},$8,{$9%},0,1,255 if $14 r. $W,$H,1,1,2 fi if $3>$5 mirror. x fi if $4>$6 mirror. y fi if inrange(0$_output_mode,1,2) # Output as new layer i[-2] 100%,100%,1,3 fc.. ${10-12} a[-2,-1] c x,y:=round([$x0*w#0%,$y0*h#0%]) => name($nm" [signature]")\,pos($x\,$y)\,opacity({round($13*100/255)}) k. else # Draw on current layer 100%,100%,1,{0,s} fc. ${10-12},255 j[0] .,$x0%,$y0%,0,0,{$13/255},..,255 k[0] fi } fx_random_signature_preview : _output_mode=0 seed=$1 if $15 seed:=v(65535) fi foreach { fx_random_signature $seed,${2--1} rectangle $3%,$4%,$5%,$6%,1,0x33333333,0 rectangle $3%,$4%,$5%,$6%,1,0xCCCCCCCC,255 } u "{"$seed"}{$2}{$3,$4}{$5,$6}{$7}{$8}{$9}{${10-13}}{$14}{0}" #@gui Shade Bobs : fx_shadebobs, fx_shadebobs #@gui : note = note("Bobs parameters :") #@gui : Density = int(50,1,200) #@gui : Radius = int(5,1,100) #@gui : Duration = int(200,1,500) #@gui : Velocity = float(1,0,10) #@gui : sep = separator() #@gui : note = note("Curve parameters :") #@gui : Rx = float(-1,-3,3) #@gui : Ry = float(2,-3,3) #@gui : Rz = float(1,-3,3) #@gui : Rt = float(0.8,-3,3) #@gui : Rcx = float(0,-3,3) #@gui : Colormap = choice(8,"Grayscale","Standard","HSV","Lines","Hot","Cool","Jet","Flag","Cube") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/18/04.") fx_shadebobs : channels 0 f 0 foreach { t=0 repeat $3 { repeat $1 { r:=$6+$5*cos(6*$7*$t)+(1-$5)*sin(6*$8*$t) a:=(360*sin($7*$t)+30*$6*$>)*pi/180 ax:=2*$>*pi/$1+$t cx:=(1+$9*cos($ax)+$r*cos($a))*w/2 cy:=(1+$9*sin($ax)+$r*sin($a))*h/2 ellipse. $cx,$cy,$2%,$2%,0,-1,1 } t+=$4% } } & 255 if $10 map {$10-1} fi #@gui Sine Curve : fx_sine_curve, fx_sine_curve_preview #@gui : note = note("Curve parameters:") #@gui : Preset = choice{1,"Default (Circle)","Alien Rasta","All Round","Carnivorous Plant","Cat Pad","Flower", #@gui : "Flower Cushion","Fly Karateka","Hearts","Moving Leaf","Radioactive Flower","Rosace","Spaceship", #@gui : "Transformer","Tubular Waves","Twisted Heart","Twisted Heart 2","Twisted Tunnel","Waterslide"} #@gui : Previous Preset = value(-1) #@gui : Resolution (%) = float(75,0,100) #@gui : Periods = float(1,0,3) #@gui : sep = separator() #@gui : Parameter Settings = choice(1,"Ratios","Multipliers","Offsets","Exponents","Signs","3D Angles") #@gui : note = note("Ratios:") #@gui : Xa/Xb = float(0.5,0,1)_0- #@gui : Ya/Yb = float(0.5,0,1)_0 #@gui : Za/Zb = float(0.5,0,1)_0 #@gui : note = note("Multipliers:") #@gui : Xa-Multiplier = int(1,0,1024)_2- #@gui : Ya-Multiplier = int(1,0,1024)_2 #@gui : Za-Multiplier = int(0,0,1024)_2 #@gui : Xb-Multiplier = int(800,0,1024)_2 #@gui : Yb-Multiplier = int(800,0,1024)_2 #@gui : Zb-Multiplier = int(1,0,1024)_2 #@gui : note = note("Offsets:") #@gui : Xa-Offset (deg.) = float(90,0,360)_0- #@gui : Ya-Offset (deg.) = float(0,0,360)_0 #@gui : Za-Offset (deg.) = float(0,0,360)_0 #@gui : Xb-Offset (deg.) = float(90,0,360)_0 #@gui : Yb-Offset (deg.) = float(0,0,360)_0 #@gui : Zb-Offset (deg.) = float(0,0,360)_0 #@gui : note = note("Exponents:") #@gui : Xa-Exponent = float(1,0,32)_0- #@gui : Ya-Exponent = float(1,0,32)_0 #@gui : Za-Exponent = float(1,0,32)_0 #@gui : Xb-Exponent = float(1,0,32)_0 #@gui : Yb-Exponent = float(1,0,32)_0 #@gui : Zb-Exponent = float(1,0,32)_0 #@gui : note = note("Signs:") #@gui : Xa-Sign = choice("Preserve","Invert","Negative","Positive")_0- #@gui : Ya-Sign = choice("Preserve","Invert","Negative","Positive")_0 #@gui : Za-Sign = choice("Preserve","Invert","Negative","Positive")_0 #@gui : Xb-Sign = choice("Preserve","Invert","Negative","Positive")_0 #@gui : Yb-Sign = choice("Preserve","Invert","Negative","Positive")_0 #@gui : Zb-Sign = choice("Preserve","Invert","Negative","Positive")_0 #@gui : note = note("3D Angles:") #@gui : X-Angle (deg.) = float(0,-180,180)_0- #@gui : Y-Angle (deg.) = float(0,-180,180)_0 #@gui : Z-Angle (deg.) = float(0,-180,180)_0 #@gui : Zoom = float(1,0,10)_0 #@gui : Focale = int(8,1,20)_0 #@gui : sep = separator() #@gui : note = note("Rendering parameters:") #@gui : Center = point(50,50,0,1,0,238,85,-170,10)_0 #@gui : Old X-Center = value(50) #@gui : Old Y-Center = value(50) #@gui : Radius = point(68,68,0,1,238,0,85,-170,10)_0 #@gui : Angle = point(75,50,0,1,238,85,0,-170,10)_0 #@gui : Old X-Angle = value(75) #@gui : Old Y-Angle = value(50) #@gui : Primary radius (%) = float(3,0,100) #@gui : Secondary radius (%) = float(2,0,100) #@gui : Opacity (%) = float(40,0,100) #@gui : Color = color(255,255,255) #@gui : Anti-aliasing = choice(2,"None","× 1.25","× 1.5","× 2","× 3") #@gui : sep = separator() #@gui : Preview background = choice(1,"Image","Black","White") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2021/03/04.") fx_sine_curve : # Get parameters as named variables. preset,previous_preset,\ resolution,periods,dp,\ ratx,raty,ratz,\ mxa,mya,mza,mxb,myb,mzb,\ oxa,oya,oza,oxb,oyb,ozb,\ pxa,pya,pza,pxb,pyb,pzb,\ sxa,sya,sza,sxb,syb,szb,\ rotx,roty,rotz,zoom,focale,\ xc,yc,prev_xc,prev_yc,xr,yr,xa,ya,prev_xa,prev_ya,\ radius1,radius2,opacity,\ colR,colG,colB,\ antialiasing,\ preview_background=${1-55} if !narg($_is_preview) _is_preview=0 fi if [$prev_xc,$prev_yc]!=[$xc,$yc] xr,yr,xa,ya,prev_xa,prev_ya+=d=[$xc,$yc]-[$prev_xc,$prev_yc];[d,d,d] fi if [$prev_xa,$prev_ya]!=[$xa,$ya] delta_a:=" a = [ "$xa" - "$xc", "$ya" - "$yc" ]; b = [ "$prev_xa" - "$xc", "$prev_ya" - "$yc" ]; (atan2(a[1],a[0]) - atan2(b[1],b[0]))*180/pi; " xr,yr:=[$xc,$yc]+rot($delta_a°)*[$xr-$xc,$yr-$yc] else delta_a=0 fi if [$colR,$colG,$colB]==[0,0,0]" && "$preview_background==1 colR,colG,colB=255 elif [$colR,$colG,$colB]==[255,255,255]" && "$preview_background==2 colR,colG,colB=0 fi # Manage presets. update_params=0 if $preset!=$previous_preset # Set default parameters for presets ('Default (Circle)'). periods=1 ratx,raty,ratz=0.5,0.5,0 mxa,mxb,mya,myb,mza,mzb=1,1,1,1,0,1 oxa,oxb,oya,oyb,oza,ozb=90,90,0,0,0,0 pxa,pxb,pya,pyb,pza,pzb=1 sxa,sxb,sya,syb,sza,szb=0 rotx,roty,rotz,zoom,focale=0,0,0,2,8 # Set specific values for each preset. # Default (circle) if !$preset ratx,raty,ratz=0 zoom=1 # Alien Rasta elif $preset==1 mxa,mxb,mya,myb=1,800,1,800 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 oxa,oxb,oya,oyb=90,90,0,60 # All Round elif $preset==2 mxa,mxb,mya,myb=1,200,1,150 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 # Carnivorous Plant elif $preset==3 mxa,mxb,mya,myb=9,512,1024,9 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 # Cat Pad elif $preset==4 mxa,mxb,mya,myb=80,1,80,1 pxa,pxb,pya,pyb=1,3,1,3 # Flower elif $preset==5 ratz=0.8 mza,mzb=7,1024 pza,pzb=1.6,2 rotz=45 zoom=1 focale=4 # Flower Cushion elif $preset==6 mxa,mxb,mya,myb=80,1,1,80 pxa,pxb,pya,pyb=1,3,1,3 # Fly Karateka elif $preset==7 mxa,mxb,mya,myb=150,1,1,100 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 # Hearts elif $preset==8 mxa,mxb,mya,myb=1,80,80,80 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 # Moving Leaf elif $preset==9 mxa,mxb,mya,myb=2,200,200,1 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 # Radioactive Flower elif $preset==10 mxa,mxb,mya,myb=1,800,1,800 pxa,pxb,pya,pyb=1,3,1,3 # Rosace elif $preset==11 mxa,mxb,mya,myb=1,10,1,10 # Spaceship elif $preset==12 mxa,mxb,mya,myb=1,400,1,200 pxa,pxb,pya,pyb=1,3,1,3 sxa,sxb,sya,syb=1,1,0,0 # Transformer elif $preset==13 mxa,mxb,mya,myb=1,800,800,2 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 # Tubular Waves elif $preset==14 mxa,mxb,mya,myb=1,30,1,60 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 # Twisted Heart elif $preset==15 mxa,mxb,mya,myb=500,1,1,500 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 # Twisted Heart 2 elif $preset==16 mxa,mxb,mya,myb=1,80,80,1 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 # Twisted Tunnel elif $preset==17 mxa,mxb,mya,myb=1,80,1,40 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 # Waterslide elif $preset==18 ratx=0.6 mxa,mxb,mya,myb=9,400,200,9 pxa,pxb,pya,pyb=1,3,1,4 sxa,sxb,sya,syb=0,1,0,2 fi fi # Change unit for some variables. W,H:=w#0?[w#0,h#0]:0$_is_preview" && "0$_preview_area_width?[0$_preview_area_width,0$_preview_area_height]:[1024,1024] nresolution:=max(1,round($periods*1000000*($resolution%)^2)) nfocale:=arg($focale,0.05,0.1,0.2,0.3,0.4,0.5,0.75,1,1.25,1.5,1.75,2,3,4,8,16,32,128,1024,16384) noxa,noxb,noya,noyb,noza,nozb:=[$oxa,$oxb,$oya,$oyb,$oza,$ozb]*pi/180 nantialiasing:=arg0($antialiasing,1,1.25,1.5,2,3) rW,rH:=[$W,$H]*$nantialiasing # Size of the non-antialiased rendering # Generate and render curve. l[] { # Compute curve coordinates. $nresolution,1,1,2,"* begin( const is_rot = "$rotx" || "$roty" || "$rotz"; ref(rot(1,0,0,"$rotx"°),Rx); ref(rot(0,1,0,"$roty"°),Ry); ref(rot(0,0,1,"$rotz"°),Rz); R = mul(Rz,mul(Ry,Rx,3),3); # 3D rotation # Variables to manage 2D rotation. const ang = atan2("$ya" - "$yc","$xa" - "$xc"); const cosa = cos(ang); const sina = sin(ang); # Variables to manage aspect ratio. const c = 35; const dxr0 = "$xr" - "$xc"; const dyr0 = "$yr" - "$yc"; const dxr = cosa*dxr0 + sina*dyr0; const dyr = -sina*dxr0 + cosa*dyr0; const _dx = abs(dxr)/c; const dx = 2.5*c*(_dx<1?_dx:_dx^3); const _dy = abs(dyr)/c; const dy = 2.5*c*(_dy<1?_dy:_dy^3); ); cpow(x,p,s) = ( ref(x,_x); (!s?sign(_x):s==1?-sign(_x):s==2?-1:1)*abs(_x)^p ); t = x/w*2*pi*"$periods"; X = lerp(cpow(sin("$mxa"*t + "$noxa"),"$pxa","$sxa"), cpow(sin("$mxb"*t + "$noxb"),"$pxb","$sxb"), "$ratx"); Y = lerp(cpow(sin("$mya"*t + "$noya"),"$pya","$sya"), cpow(sin("$myb"*t + "$noyb"),"$pyb","$syb"), "$raty"); Z = lerp(cpow(sin("$mza"*t + "$noza"),"$pza","$sza"), cpow(sin("$mzb"*t + "$nozb"),"$pzb","$szb"), "$ratz"); # Set aspect ratio and rotate. X*=dx%; Y*=dy%; is_rot?(P = R*[ X,Y,Z ]; X = P[0]; Y = P[1]; Z = P[2]); # 2D projection. X*="$nfocale"; Y*="$nfocale"; Z = max(1e-5,Z + 1 + "$nfocale"); pX = X/Z; pY = -Y/Z; # Normalize and get display coordinates. ang?(X = cosa*pX - sina*pY; pY = sina*pX + cosa*pY; pX = X); const ax = "$zoom*$rW"; const bx = "$xc*$rW"%; const ay = "$zoom*$rH"; const by = "$yc*$rH"%; [ ax*pX + bx, ay*pY + by ]" # Draw curve (as an alpha-channel). $rW,$rH eval.. "* const mwh = min(w#-1,h#-1)*5%; const r1 = max(0.01,mwh*"$radius1"%); const r2 = max(0.01,mwh*"$radius2"%); const Mr = max(r1,r2); const opacity = ("$opacity"%)^3; X = R; Y = G; Mr<0?( i(#-1,X,Y) = lerp(i(#-1,X,Y),1,opacity); ):( pX = i(x - 1,0,0,0); pY = i(x - 1,0,0,1); dX = X - pX; dY = Y - pY; ang = atan2(dY,dX)*180/pi; ellipse(#-1,X,Y,r1,r2,ang°,opacity,255); ); I" rm.. r. $W,$H,1,1,2 n 0,255 i[0] 100%,100%,1,3 fc[0] $colR,$colG,$colB a[-2,-1] c } if 0$_is_preview if $!==1 i[0] $W,$H,1,3 fi if $preview_background [0],[0] f. {$preview_background==1?0:255} to_rgb. rv[0,-1] rm. fi blend[0,-1] alpha line. $xc%,$yc%,$xr%,$yr%,0.75,0xF0F0F0F0,238,0,85 line. $xc%,$yc%,$xr%,$yr%,0.75,0x0F0F0F0F,255 line. $xc%,$yc%,$xa%,$ya%,0.75,0xF0F0F0F0,238,85,0 line. $xc%,$yc%,$xa%,$ya%,0.75,0x0F0F0F0F,0 fi mv. 0 if 0$_output_mode k[0] fi # Update parameter values. r,m,o,p,s,a:=d=$dp;[!d?2:0,d==1?2:0,d==2?2:0,d==3?2:0,d==4?2:0,d==5?2:0] u "{"$preset"}{"$preset"}{"$resolution"}{"$periods"}{"$dp"}"\ "{"$ratx"}_"$r"{"$raty"}_"$r"{"$ratz"}_"$r\ "{"$mxa"}_"$m"{"$mya"}_"$m"{"$mza"}_"$m"{"$mxb"}_"$m"{"$myb"}_"$m"{"$mzb"}_"$m\ "{"$oxa"}_"$o"{"$oya"}_"$o"{"$oza"}_"$o"{"$oxb"}_"$o"{"$oyb"}_"$o"{"$ozb"}_"$o\ "{"$pxa"}_"$p"{"$pya"}_"$p"{"$pza"}_"$p"{"$pxb"}_"$p"{"$pyb"}_"$p"{"$pzb"}_"$p\ "{"$sxa"}_"$s"{"$sya"}_"$s"{"$sza"}_"$s"{"$sxb"}_"$s"{"$syb"}_"$s"{"$szb"}_"$s\ "{"$rotx"}_"$a"{"$roty"}_"$a"{"$rotz"}_"$a"{"$zoom"}_"$a"{"$focale"}_"$a\ "{"$xc,$yc"}{"$xc"}{"$yc"}{"$xr,$yr"}{"$xa,$ya"}{"$xa"}{"$ya"}{"$radius1"}{"$radius2"}{"$opacity"}"\ "{"$colR,$colG,$colB"}{"$antialiasing"}"\ "{"$preview_background"}" fx_sine_curve_preview : _is_preview=1 fx_sine_curve $* k[0] #@gui Superformula : fx_superformula, fx_superformula(1) #@gui : Resolution = int(4096,2,8192) #@gui : sep = separator() #@gui : X-Size = float(0.9,0,2) #@gui : Y-Size = float(0.9,0,2) #@gui : sep = separator() #@gui : M = int(8,1,32) #@gui : N1 = float(1,-32,32) #@gui : N2 = float(5,-32,32) #@gui : N3 = float(8,-32,32) #@gui : sep = separator() #@gui : X-Angle = float(0,0,360) #@gui : Y-Angle = float(0,0,360) #@gui : Z-Angle = float(0,0,360) #@gui : sep = separator() #@gui : Thickness = float(3,0,50) #@gui : Color = color(128,255,128,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/18/04.") fx_superformula : foreach { to_rgba {w},{h} f3d {0.5*max(w,h)/tan($4*pi/360)} superformula3d $1,${4-7} r3d. 0,0,1,$10 r3d. 0,1,0,$9 r3d. 1,0,0,$8 *3d. {0.5*$2*{-2,w}},{0.5*$3*{-2,h}} col3d. 1 j3d.. .,50%,50%,0,1,1,0,0 rm. distance. 1 >. $11% *.. . ==. 0 r. 100%,100%,1,4 sh. 0 *. $12 rm. sh. 1 *. $13 rm. sh. 2 *. $14 rm. sh. 3 *. $15 rm. +[-2,-1] } #@gui Symmetric 2D Shape : fx_symmetric_shape2d, fx_symmetric_shape2d_preview(1) #@gui : Subdivisions = int(5,2,32) #@gui : Center = point(50,50,0,1,255,255,255,128) #@gui : Old Center = value(50,50) #@gui : Angle / Size = point(50,30,0,1,255,255,255,128) #@gui : Old Angle / Size = value(50,30) #@gui : sep = separator() #@gui : Control Point 1 = point(50,25,1,1,255,128,0,255,4) #@gui : Control Point 2 = point(56,42,1,1,255,128,0,255,4) #@gui : Control Point 3 = point(52,52,-1,1,255,128,0,255,4) #@gui : Control Point 4 = point(52,52,-1,1,255,128,0,255,4) #@gui : Control Point 5 = point(52,52,-1,1,255,128,0,255,4) #@gui : Control Point 6 = point(52,52,-1,1,255,128,0,255,4) #@gui : sep = separator() #@gui : Drawing Mode = choice(1,"Outlined","Filled") #@gui : Color = color(255,0,255) #@gui : Opacity (%) = float(100,0,100) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2019/06/17.") fx_symmetric_shape2d : if !narg($_is_preview) _is_preview=0 fi foreach { 100%,100%,1,4 _fx_symmetric_shape2d. $* rv if !0$_is_preview" && "0$_output_mode rm. fi } _fx_symmetric_shape2d : (${10-21}) f. "isnan(i)?-1024:i" discard. -1024 r. 2,{h/2},1,1,-1 permute. cyzx f. "x = R - $2; y = G - $3; [ atan2(y,x), norm(x,y) ]" l. { n:=h .x{$1-1} a y f "const pi2 = 2*pi; [ (R + int(y/"$n")*pi2/$1)%pi2, G ]" sort +,y } f. "[ $2*w#-2,$3*h#-2 ]/100 + [ G*cos(R), G*sin(R) ]*(min(w#-2,h#-2)-1)%" permute. cyzx coords={^} rm. if $22 polygon {narg($coords)/2},$coords,1,${23-25},{$26*255%} else polygon {narg($coords)/2},$coords,1,0xFFFFFFFF,${23-25},{$26*255%} fi fx_symmetric_shape2d_preview : _fx_symmetric_shape2d_preview $* _fx_symmetric_shape2d_preview : _is_preview=1 cx1,cy1,cx2,cy2,cx3,cy3,cx4,cy4,cx5,cy5,cx6,cy6=${10-21} angx,angy=$6,$7 if [$2,$3]!=[$4,$5]" || "[$angx,$angy]!=[$8,$9] # Center or angle has been modified dx,dy:=[$2-$4,$3-$5] repeat 6 { i:=1+$> cx$i,cy$i:=" const cx = "${cx$i}"; const cy = "${cy$i}"; dang = atan2($7 - $3,$6 - $2) - atan2($9 - $3,$8 - $2); dsca = norm($6 - $2,$7 - $3)/norm($9 - $3,$8 - $2); [ $2,$3 ] + dsca*rot(dang)*[ cx - $4, cy - $5 ]" } angx,angy:=[$6+$2-$4,$7+$3-$5] fi foreach { r {s=min(w,h);[s,s]},1,100%,0,0,0.5,0.5 fx_symmetric_shape2d ${1-9},$cx1,$cy1,$cx2,$cy2,$cx3,$cy3,$cx4,$cy4,$cx5,$cy5,$cx6,$cy6,${22-26} rv blend alpha eval " t0 = atan2($7 - $3,$6 - $2); repeat ($1,k, const pi2 = 2*pi; const xc = $2*(w-1)%; const yc = $3*(h-1)%; x = xc + (w+h)*cos(t0 + 2*pi*k/$1); y = yc + (w+h)*sin(t0 + 2*pi*k/$1); polygon(-2,xc,yc,x,y,0.35,0xF0F0F0F0,255); polygon(-2,xc,yc,x,y,0.35,0x0F0F0F0F,0); )" } u "{$1}"\ # Subdivisions "{$2,$3}"\ # Center "{$2,$3}"\ # Old Center "{"$angx,$angy"}"\ # Angle "{"$angx,$angy"}"\ # Old Angle "{"$cx1,$cy1"}"\ # Control point 1 "{"$cx2,$cy2"}"\ # Control point 2 "{"$cx3,$cy3"}"\ # Control point 3 "{"$cx4,$cy4"}"\ # Control point 4 "{"$cx5,$cy5"}"\ # Control point 5 "{"$cx6,$cy6"}"\ # Control point 6 "{$22}"\ # Drawing mode "{$23,$24,$25}"\ # Color "{$26}" # Opacity #@gui Tree : fx_tree, fx_tree_preview(1) #@gui : note = note("Global parameters:") #@gui : Recursion Depth = int(11,1,18) #@gui : Random Seed = int(10000,0,65535) #@gui : X-ratio = float(0,-1,1) #@gui : Y-ratio = float(0,-1,1) #@gui : note = note("Note: Set Random Seed to 0 to make it \ # random as well.") #@gui : sep = separator(), note = note("Trunk:") #@gui : Thickness (%) = float(15,0,100) #@gui : Base Thickness (%) = float(150,0,300) #@gui : Angle (deg.) = float(0,-90,90) #@gui : sep = separator(), note = note("Recursion:") #@gui : Avg Branching = float(2.15,1,6) #@gui : Std Branching = float(0.8,0,6) #@gui : Avg Left Angle (deg.) = float(-40,-90,90) #@gui : Avg Right Angle (deg.) = float(40,-90,90) #@gui : Std Angle (deg.) = float(10,0,90) #@gui : Avg Length Factor (%) = float(75,0,200) #@gui : Std Length Factor (%) = float(0,0,200) #@gui : Avg Thickness Factor (%) = float(70,0,200) #@gui : Std Thickness Factor (%) = float(20,0,200) #@gui : sep = separator(), note = note("Colors / Opacity:") #@gui : Trunk color = color(40,25,0,255) #@gui : Trunk opacity (%) = float(100,0,100) #@gui : Leaf color = color(70,140,60,255) #@gui : Leaf opacity (%) = float(100,0,100) #@gui : Color gamma = float(0.4,-2,2) #@gui : Opacity gamma = float(0.4,-2,2) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/03/24.") _fx_tree: recursion_depth,random_seed,xratio,yratio,\ trunk_thickness,base_thickness,trunk_angle,\ avg_branching,std_branching,avg_leftangle,avg_rightangle,std_angle,avg_length,std_length,avg_thickness,std_thickness,\ Rt,Gt,Bt,At,Ot,Rl,Gl,Bl,Al,Ol,gammaRGBA,gammaO=${1-28} W,H,S:=w,h,min(w,h) l[] { if $2 srand $2 fi # Init trunk. 1,1,1,9," const h_thickness = "$trunk_thickness"%/2; const hb_thickness = h_thickness*"$base_thickness"%; R = rot("$trunk_angle"°); C = [ 0.5,0 ]; P0 = C + R*[ -hb_thickness,0 ]; P1 = C + R*[ hb_thickness,0 ]; P2 = C + R*[ h_thickness,0.5 ]; P3 = C + R*[ -h_thickness,0.5 ]; [ P0,P1,P2,P3,0 ]" # Compute tree geometry. repeat $recursion_depth { 1,8,1,9 eval.. "> const dangle = "$avg_rightangle" - "$avg_leftangle"; ref(I,val); ref(val[0,2],P0); ref(val[2,2],P1); ref(val[4,2],P2); ref(val[6,2],P3); ndepth = val[8] + 1; # Median axis. A = (P0 + P1)/2; B = (P2 + P3)/2; AB = B - A; thickness = norm(B - P2); N = round(cut("$avg_branching" + u(-1,1)*"$std_branching",1,6)); Nm1 = N<=1?1:N - 1; repeat (N,n, ang = cut("$avg_leftangle" + dangle*n/Nm1 + u(-1,1)*"$std_angle",-90,90); len = cut("$avg_length" + u(-1,-1)*"$std_length",0,200); rot = rot(ang°); nB = B + len%*rot*AB; orth = (nB - B); orth/=norm(orth); orth = [ -orth[1],orth[0] ]; nthickness = thickness*("$avg_thickness" + u(-1,1)*"$std_thickness")%; Q0 = nB + nthickness*orth; Q1 = nB - nthickness*orth; da_push([ P3,P2,Q1,Q0,ndepth ]); ); end(resize(#-1,1,da_size(),1,s#-1,0)); val" } a y # Normalize coordinates to fit image size and aspect ratio. 1,100%,1,4,"[ i0#-1,i2#-1,i4#-1,i6#-1 ]" 1,100%,1,4,"[ i1#-2,i3#-2,i5#-2,i7#-2 ]" sx,sy:=10^[$xratio,$yratio] -.. 0.5 *.. {-2,$S*$sx/(2.1*max(abs(iM),abs(im)))} +.. {$W/2} *. {$S*$sy/(1.05*max(iM))} f... "round([ i0#-2,i0#-1,i1#-2,i1#-1,i2#-2,i2#-1,i3#-2,i3#-1,i8 ])" rm[-2,-1] # Make sure trunk starts at bottom when rotated. eval "ref(I,T); P0 = T[0,2]; P1 = T[2,2]; P2 = T[4,2]; P3 = T[6,2]; I[0] = [ lerp(P0,P2,-2),lerp(P1,P2,-2),P2,P3,0 ]" # Draw tree. $W,$H,1,4 eval.. "> begin( RGBAt = [ "$Rt,$Gt,$Bt,$At" ]; RGBAl = [ "$Rl,$Gl,$Bl,$Al" ]; const gRGBA = 10^"$gammaRGBA"; const gO = 10^"$gammaO"; ); ref(I,val); t = val[8]/"$recursion_depth"; # Between [0,1] RGBA = lerp(RGBAt,RGBAl,t^gRGBA); O = lerp("$Ot,$Ol",t^gO)%; polygon(#-1,4,val[0,8],O,RGBA); (i0==i2 && i1==i3) || (i4==i6 && i5==i7)?polygon(#-1,2,i0,i1,i4,i5,O,RGBA); val" mirror xy rm.. } fx_tree : _fx_tree. $* mv. 0 if 0$_output_mode k[0] fi fx_tree_preview : foreach { _fx_tree $* blend alpha } #@gui Turbulence : fx_turbulence, fx_turbulence #@gui : Radius = float(128,1,1024) #@gui : Octaves = int(6,1,12) #@gui : Damping per Octave = float(4,1,10) #@gui : Difference Mixing = float(0,-10,10) #@gui : Mode = choice("Turbulence","Turbulence 2","Fractal Noise","Fractured Clouds","Stardust","Pea Soup") #@gui : sep = separator() #@gui : note = note("Author: Preben Soeberg.      Latest Update: 2010/29/12.") fx_turbulence : remove_opacity turbulence ${^0} #@gui Twisted Rays : fx_twisted_rays, fx_twisted_rays #@gui : Center = point(50,50,0,1,0,255,0,128,10) #@gui : Branches = int(9,1,64) #@gui : Angle (%) = float(50,0,100) #@gui : Twist (%) = float(50,-400,400) #@gui : Perspective (%) = float(25,-100,100) #@gui : Color = color(255,255,255,255) #@gui : Anti-aliasing = bool(1) #@gui : sep = separator() #@gui : note = note("Authors: David Tschumperlé and \ # Frio . #@gui :       Latest Update: 2023/10/24.") fx_twisted_rays : xcenter,ycenter,branches,angle,twist,focale,R,G,B,A,is_antialias=${1-11} foreach { Mwh,offx,offy:="const Mwh = max(w,h); [ Mwh,(Mwh - w)/2,(Mwh - h)/2 ]" xc,yc:=[$xcenter,$ycenter]%*[w,h]+[$offx,$offy] +shape_rays $Mwh,$xc,$yc,$branches,$angle%,{$twist%},{$focale%},$is_antialias r. ..,..,1,100%,0,0,0.5,0.5 100%,100%,1,4 fc. $R,$G,$B,$A sh. 100% *. ... rm[-3,-1] blend alpha } #@gui ____Sequences #------------------------- # fx_animate_preview : _command,_parameters1,_parameters2,_compute_half={ 0 | 1 },_width>=0,_height>=0 # Generate a preview with start/end rendering of an animation. fx_animate_preview : skip ${4=1},${5=0},${6=$5} repeat $! { if $5 width=$5 else width:=w fi if $6 height=$6 else height:=h fi if $4 s. x,2 else . fi -$1.. $2 -$1. $3 # Assume this is a 1->1 filter. r[-2,-1] {max(w,{-2,w})},{max(h,{-2,h})},1,100%,3 if !$4 columns.. 0,50% columns. 50%,100% fi a[-2,-1] x r. $width,$height,1,100%,2 drgba. line. 50%,0,50%,100%,1,0,0,0,255 to. "Start",3,-1,13,2,1,255 to. "End",{w-24},{h-18},13,2,1,255 mv. 0 } #@gui 3D Elevation [Animated] : fx_animate_elevation3d, fx_animate_elevation3d_preview(1) #@gui : Frames = _int(10,2,100) #@gui : Output as Frames = _bool(1) #@gui : Output as Files = _bool(0) #@gui : Output Folder = _folder() #@gui : note = note{"\nGlobal parameters :"} #@gui : Factor = float(100,-1000,1000) #@gui : Smoothness = float(1,0,10) #@gui : Width = _int(1024,8,4096) #@gui : Height = _int(1024,8,4096) #@gui : Rendering = choice(2,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong") #@gui : note = note{"\nStarting parameters :"} #@gui : Size = float(0.8,0,3) #@gui : X-Angle = float(35,0,360) #@gui : Y-Angle = float(0,0,360) #@gui : Z-Angle = float(0,0,360) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : note = note{"\nEnding parameters :"} #@gui : Size = float(0.8,0,3) #@gui : X-Angle = float(35,0,1440) #@gui : Y-Angle = float(0,0,1440) #@gui : Z-Angle = float(360,0,1440) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_animate_elevation3d : skip "${4=}" if $3 filename="$4/gmic_elevation3d.png" else filename="" fi _fx_elevation3d ${5-6},0 animate fx_render3d,"${7-8},${10-19},$9",\ "${7-8},${20-29},$9",$1,$2,{``$filename} fx_animate_elevation3d_preview : skip "${4=}" w,h={w},{h} _fx_elevation3d ${5-6},0 fx_animate_preview fx_render3d,$w","$h",${10-19},$9",\ $w","$h",${20-29},$9",0,$w,$h #@gui 3D Extrusion [Animated] : fx_animate_extrude3d, fx_animate_extrude3d_preview(1) #@gui : Frames = _int(10,2,100) #@gui : Output as Frames = _bool(1) #@gui : Output as Files = _bool(0) #@gui : Output Folder = _folder() #@gui : note = note{"\nGlobal parameters :"} #@gui : Depth = float(10,1,256) #@gui : Resolution = int(512,1,1024) #@gui : Smoothness = float(0.6,0,3) #@gui : Width = _int(1024,8,4096) #@gui : Height = _int(1024,8,4096) #@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong") #@gui : note = note{"\nStarting parameters :"} #@gui : Size = float(0.8,0,3) #@gui : X-Angle = float(35,0,360) #@gui : Y-Angle = float(0,0,360) #@gui : Z-Angle = float(0,0,360) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : note = note{"\nEnding parameters :"} #@gui : Size = float(0.8,0,3) #@gui : X-Angle = float(35,0,1440) #@gui : Y-Angle = float(360,0,1440) #@gui : Z-Angle = float(0,0,1440) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_animate_extrude3d : skip "${4=}" if $3 filename="$4/gmic_extrude3d.png" else filename="" fi _fx_extrude3d ${5-7},0 animate fx_render3d,"${8-9},${11-20},$10",\ "${8-9},${21-30},$10",$1,$2,{``$filename} fx_animate_extrude3d_preview : skip "${4=}" w,h={w},{h} _fx_extrude3d ${5-7},0 fx_animate_preview fx_render3d,$w","$h",${11-20},$10",\ $w","$h",${21-30},$10",0,$w,$h #@gui 3D Image Object [Animated] : fx_animate_imageobject3d, fx_animate_imageobject3d_preview(1) #@gui : Frames = _int(10,2,100) #@gui : Output as Frames = _bool(1) #@gui : Output as Files = _bool(0) #@gui : Output Folder = _folder() #@gui : note = note{"\nGlobal parameters :"} #@gui : Type = choice{1,"Plane","Cube","Pyramid","Sphere","Torus","Gyroid","Weird","Cup","Rubik"} #@gui : Width = _int(1024,1,4096) #@gui : Height = _int(1024,1,4096) #@gui : Rendering = choice(4,"Dots","Wireframe","Flat","Flat-Shaded","Gouraud","Phong") #@gui : note = note{"\nStarting parameters :"} #@gui : Size = float(0.5,0,3) #@gui : X-Angle = float(57,0,360) #@gui : Y-Angle = float(41,0,360) #@gui : Z-Angle = float(21,0,360) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : note = note{"\nEnding parameters :"} #@gui : Size = float(0.5,0,3) #@gui : X-Angle = float(57,0,1440) #@gui : Y-Angle = float(401,0,1440) #@gui : Z-Angle = float(21,0,1440) #@gui : FOV = float(45,1,90) #@gui : X-Light = float(0,-100,100) #@gui : Y-Light = float(0,-100,100) #@gui : Z-Light = float(-100,-100,0) #@gui : Specular Lightness = float(0.5,0,1) #@gui : Specular Shininess = float(0.7,0,3) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_animate_imageobject3d : skip "${4=}" if $3 filename="$4/gmic_imageobject3d.png" else filename="" fi _fx_imageobject3d "_",$5 animate fx_render3d,"${6-7},${9-18},$8",\ "${6-7},${19-28},$8",$1,$2,{``$filename} fx_animate_imageobject3d_preview : skip "${4=}" w,h={w},{h} _fx_imageobject3d "_preview_",$5 fx_animate_preview fx_render3d,$w","$h",${9-18},$8",\ $w","$h",${19-28},$8",0,$w,$h #@gui 3D Text Pointcloud : fx_text_pointcloud3d, fx_text_pointcloud3d_preview #@gui : Frames = _int(64,1,256) #@gui : 1st Text = text("G'MIC") #@gui : 2nd Text = text("Rocks!") #@gui : Smoothness = float(1,0,5) #@gui : Color = color(200,220,255) #@gui : Background = color(255,255,255,255) #@gui : X-Shadow= float(2,0,10) #@gui : Y-Shadow= float(2,0,10) #@gui : Shadow Smoothness = float(1,0,5) #@gui : Stationary Frames = _int(19,1,32) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/01/09.") fx_text_pointcloud3d : W,H,M:=w,h,round(1.5*max(w,h)) rm text_pointcloud3d "$2","$3",$4 col3d. ${5-7} *3d. {0.7*$M} f3d 4000 db3d 0 m3d repeat $1 { rprogress {60*$>/$1} angle:=$>*360/$1 +r3d[0] 1,0,1,$angle $M,$M,1,3,-1 j3d. ..,50%,50%,0,1 rm.. } rm[0] a z autocrop -1 to_rgba s z replace_color 0,0,-1,-1,-1,255,0,0,0,0 if $11 N=$! foreach { rprogress {60+40*$>/$N} i[0] 100%,100%,1,4 fc[0] ${8-11} +channels. 3,3 +negate. b[-2,-1] $14% to_rgba. j[0] .,$12%,$13%,0,0,1,..,255 rm[-2,-1] blend alpha } fi if $W>$H rs $W else rs ,$H fi if $15>1 i[{int($1/2)}] [{int($1/2)}]x{$15-1} i[0] [0]x{$15-1} fi fx_text_pointcloud3d_preview : fx_text_pointcloud3d 4,"$2","$3",$4,${5-7},${8-11},${12-14},1 drgba frame xy,1,0 append_tiles 2,2 #@gui 3D Tiles : fx_transition3d, fx_transition3d_preview(0) #@gui : Inter-Frames = _int(10,3,100) #@gui : X-Tiles = int(8,1,64) #@gui : Y-Tiles = int(8,1,64) #@gui : X-Rotation = text("1") #@gui : Y-Rotation = text("1") #@gui : Z-Rotation = text("0") #@gui : Focale = float(800,100,2000) #@gui : Enable Antialiasing = bool(1) #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter needs two layers to work properly. Set the Input layers option to handle #@gui : multiple input layers. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2012/13/08.") fx_transition3d : f3d $7 transition3d $1,$2,$3,"$4","$5","$6",$8 fx_transition3d_preview : if $!==1 gui_warning_preview "Missing input layer" return fi f3d $7 k[0,1] transition3d 4,$2,$3,"$4","$5","$6",$8 k[1,2] r[0] 50%,100%,1,100%,0 r[1] 50%,100%,1,100%,0,0,1 a x line 50%,0,50%,100%,1,0,0,0,255 #@gui B&W Pencil [Animated] : fx_animate_pencilbw, fx_animate_pencilbw_preview(0) #@gui : Frames = _int(10,2,100) #@gui : Output Frames = _bool(1) #@gui : Output Files = _bool(0) #@gui : Output Folder = _folder() #@gui : note = note{"\nStarting Parameters :"} #@gui : Pencil Type = float(2.3,0,5) #@gui : Amplitude = float(100,0,200) #@gui : note = note{"\nEnding Parameters :"} #@gui : Pencil Type = float(0.3,0,5) #@gui : Amplitude = float(60,0,200) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_animate_pencilbw : skip "${4=}" if $3 filename="$4/gmic_pencilbw.png" else filename="" fi animate pencilbw,"${5-6}",\ "${7-8}",$1,$2,{``$filename} fx_animate_pencilbw_preview : skip "${4=}" fx_animate_preview pencilbw,"${5-6}",\ "${7-8}" #@gui B&W Stencil [Animated] : fx_animate_stencilbw, fx_animate_stencilbw_preview(1) #@gui : Frames = _int(10,2,100) #@gui : Output Frames = _bool(1) #@gui : Output Files = _bool(0) #@gui : Output Folder = _folder() #@gui : note = note{"\nStarting Parameters :"} #@gui : Edge Threshold = float(10,0,30) #@gui : Smoothness = float(10,0,30) #@gui : note = note{"\nEnding Parameters :"} #@gui : Edge Threshold = float(10,0,30) #@gui : Smoothness = float(20,0,30) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_animate_stencilbw : skip "${4=}" if $3 filename="$4/gmic_stencilbw.png" else filename="" fi animate stencilbw,"${5-6}",\ "${7-8}",$1,$2,{``$filename} fx_animate_stencilbw_preview : skip "${4=}" fx_animate_preview stencilbw,"${5-6}",\ "${7-8}" #@gui Cartoon [Animated] : fx_animate_cartoon, fx_animate_cartoon_preview(0) #@gui : Frames = _int(10,2,100) #@gui : Output Frames = _bool(1) #@gui : Output Files = _bool(0) #@gui : Output Folder = _folder() #@gui : note = note{"\nGlobal Parameters :"} #@gui : Color Quantization = int(4,2,256) #@gui : note = note{"\nStarting parameters :"} #@gui : Smoothness = float(0.5,0,2) #@gui : Sharpening = float(200,0,400) #@gui : Edge Threshold = float(10,1,30) #@gui : Edge Thickness = float(0.1,0,1) #@gui : Color Strength = float(1.5,0,3) #@gui : note = note{"\nEnding parameters :"} #@gui : Smoothness = float(3,0,2) #@gui : Sharpening = float(200,0,400) #@gui : Edge Threshold = float(10,1,30) #@gui : Edge Thickness = float(0.1,0,1) #@gui : Color Strength = float(1.5,0,3) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_animate_cartoon : skip "${4=}" if $3 filename="$4/gmic_cartoon.png" else filename="" fi animate cartoon,"${6-10},$5",\ "${11-15},$5",$1,$2,{``$filename} fx_animate_cartoon_preview : skip "${4=}" fx_animate_preview cartoon,"${6-10},$5",\ "${11-15},$5" #@gui Edges [Animated] : fx_animate_edges, fx_animate_edges_preview(0) #@gui : Frames = _int(10,2,100) #@gui : Output Frames = _bool(1) #@gui : Output Files = _bool(0) #@gui : Output Folder = _folder() #@gui : note = note{"\nGlobal Parameters :"} #@gui : Negative Colors = bool(0) #@gui : note = note{"\nStarting Parameters :"} #@gui : Smoothness = float(0,0,10) #@gui : Edge Threshold = float(10,0,30) #@gui : note = note{"\nEnding Parameters :"} #@gui : Smoothness = float(0,0,10) #@gui : Edge Threshold = float(30,0,30) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_animate_edges : skip "${4=}" if $3 filename="$4/gmic_edges.png" else filename="" fi animate fx_edges,"${6-7},$5",\ "${8-9},$5",$1,$2,{``$filename} fx_animate_edges_preview : skip "${4=}" fx_animate_preview fx_edges,"${6-7},$5",\ "${8-9},$5" #@gui Edges on Fire : fx_fire_edges, fx_fire_edges_preview(0) #@gui : Edges = float(0.7,0,3) #@gui : Attenuation = float(0.25,0,1) #@gui : Smoothness = float(0.5,0,5) #@gui : Threshold = float(25,0,100) #@gui : sep = separator() #@gui : Number of Frames = _int(20,1,999) #@gui : Starting Frame = int(20,0,199) #@gui : Frame Skip = _int(0,0,20) #@gui : sep = separator() #@gui : Preview Type = choice("Full","Forward Horizontal","Forward Vertical","Backward Horizontal", #@gui : "Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom","Duplicate Right", #@gui : "Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse") #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/06/07.") fx_fire_edges : fire_edges ${1-7} rv fx_fire_edges_preview : gui_split_preview "fire_edges $1,$2,$3,$4,1,$6,0",${-3--1} #@gui Lava Lamp : fx_lavalampbw, fx_lavalampbw_preview(0) #@gui : Number of Key-Frames = _int(3,2,50) #@gui : Number of Inter-Frames = _int(30,2,100) #@gui : Smooth Looping = _bool(1) #@gui : sep = separator() #@gui : Resolution = float(20,1,100) #@gui : Size = float(2,0,30) #@gui : Smoothness = _float(0.01,0,1) #@gui : Transparent Background = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/06/07.") fx_lavalampbw : if !$! (255;100^64;16^128;0) r. 512,512,1,3,3 fi foreach { remove_opacity w,h={w},{h} +r $4%,$4%,1,1,0 [-1]x{$1-1} rand[^0] 0,1 stencil[^0] $5,0 if $3 [1] fi morph[^0] $2,$6,0 stencil[^0] $5,0 r[^0] $w,$h,1,1,3 b[^0] 10 >=[^0] 50% *[^0] 255 r[^0] 100%,100%,1,4 j[^0] [0] rm[0] if $3 rm. fi } if !$7 foreach { split_opacity n. 0,1 *[^-1] . rm. } fi fx_lavalampbw_preview : fx_lavalampbw 2,2,1,$4,$5,$6,$7 k[0] #@gui Lissajous [Animated] : fx_animate_lissajous, fx_animate_lissajous_preview(1) #@gui : Frames = _int(10,2,100) #@gui : Output as Frames = _bool(1) #@gui : Output as Files = _bool(0) #@gui : Output Folder = _folder() #@gui : sep = separator() #@gui : note = note{"Starting parameters :"} #@gui : Resolution = int(4096,2,8192) #@gui : X-Size = float(0.9,0,2) #@gui : Y-Size = float(0.9,0,2) #@gui : Z-Size = float(3,1,10) #@gui : X-Multiplier = float(8,0,32) #@gui : Y-Multiplier = float(7,0,32) #@gui : Z-Multiplier = float(0,0,32) #@gui : X-Offset = float(0,0,1) #@gui : Y-Offset = float(0,0,1) #@gui : Z-Offset = float(0,0,1) #@gui : X-Angle = float(0,0,360) #@gui : Y-Angle = float(0,0,360) #@gui : Z-Angle = float(0,0,360) #@gui : Thickness = float(0,0,50) #@gui : Color = color(255,255,255,255) #@gui : sep = separator() #@gui : note = note{"Ending parameters :"} #@gui : Resolution = int(4096,2,8192) #@gui : X-Size = float(0.9,0,2) #@gui : Y-Size = float(0.9,0,2) #@gui : Z-Size = float(3,1,10) #@gui : X-Multiplier = float(8,0,32) #@gui : Y-Multiplier = float(7,0,32) #@gui : Z-Multiplier = float(0,0,32) #@gui : X-Offset = float(0,0,1) #@gui : Y-Offset = float(0,0,1) #@gui : Z-Offset = float(0,0,1) #@gui : X-Angle = float(0,0,360) #@gui : Y-Angle = float(0,0,360) #@gui : Z-Angle = float(0,0,360) #@gui : Thickness = float(0,0,50) #@gui : Color = color(255,255,255,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/18/04.") fx_animate_lissajous : skip "${4=}" if $3 filename="$4/gmic_lissajous.png" else filename="" fi animate fx_lissajous,"${5-22}",\ "${23-40}",$1,$2,{``$filename} fx_animate_lissajous_preview : skip "${4=}" fx_animate_preview fx_lissajous,"${5-22}",\ "${23-40}",0 #@gui Moiré Animation : fx_moire, fx_moire_preview(1)+ : * #@gui : Stripe orientation = choice(1,"Horizontal","Vertical") #@gui : Input Transparency = choice("Replace With White","Reconstruct From Previous Frames") #@gui : Output Format = choice{2,"Same as Input","A4 / 75 PPI","A4 / 100 PPI (Recommended)", #@gui : "A4 / 150 PPI","A4 / 300 PPI"} #@gui : Auto-Reduce Number of Frames = bool(1) #@gui : Landscape = bool(1) #@gui : Margin (%) = float(2,0,30) #@gui : sep = separator() #@gui : Print Frame Numbers = choice(1,"Disable","Top Left","Top Right","Bottom Left","Bottom Right") #@gui : Size of Frame Numbers (%) = float(5,0,30) #@gui : sep = separator() #@gui : note = note{"Instructions:\n\n #@gui : This filter renders Moire Animations, as shown on #@gui : this video.\n #@gui : To make the animation visible:\n\n #@gui : • Before running the filter, ensure that all frames are aligned and have the same size #@gui : (and preferably without alpha)!\n #@gui : • Run the filter. It is recommended to keep the number of frames <=6.\n #@gui : • Print the first layer (merged frames) on a A4 blank paper, at 300 PPI.\n #@gui : • Print the second layer (mask) on a A4 transparent sheet, at 300 PPI.\n #@gui : • Drag the transparent layer over the A4 paper to render the animation effect. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/03/02.") fx_moire : if 0$_is_preview" && "!narg($_preview_area_width) _preview_area_width,_preview_area_height=512 fi # Reconstruct frames from base image. if $2 repeat $! { if $>" && "{$<,s==2||s==4} blend[$<] [{$<+1}],alpha,1,1 fi } fi # Auto-reduce number of frames (4, 5 or 6). if $4 skip:=" l<=6?1:( const l5 = int(l/5); const l4 = int(l/4); const l6 = int(l/6); 5*l5==l?l5: 6*l6==l?l6: 4*l4==l?l4:( const r6 = abs(6*l6 - l); const r5 = abs(5*l5 - l); const r4 = abs(4*l4 - l); r5<=r4 && r5<=r6?l5: r6<=r4 && r6<=r5?l6: l4 ) )" k[0--1:$skip] if $!>6 rm[6--1] fi fi N=$! # Blend with white background. foreach { if s==2||s==4 drgba 255 fi } # Constrain size of frames. if 0$_is_preview W,H=$_preview_area_width,$_preview_area_height if $3 if $W>$H W:=$H/sqrt(2) else H:=$W*sqrt(2) fi fW,fH=$W,$H W,H/=arg($3,4,3,2,1) fi elif $3 W,H,fW,fH:="$3==1?[620,877]:$3==2?[827,1169]:$3==3?[1240,1754]:[2480,3508]",2480,3508 W,H,fW,fH:=round([$W,$H,$fW,$fH]*0.95) # Take printer margins into account else W,H=${-max_wh} fi if $3" && "$5 # Landscape mode W,H=$H,$W fW,fH=$fH,$fW fi if !narg($fW) fW,fH=$W,$H fi foreach { if [w,h]!=[$W,$H] - 255 rs {(100-$6)%*[$W,$H]},2,1 r $W,$H,1,100%,0,0,0.5,0.5 + 255 fi } # Generate merged image and mask. if $1 # Vertical stripes 100%,100%,1,100%,"i(#x%"$N",x,y,z,c)" rm[^-1] 100%,100%,1,1,"x%"$N"?0:255" else # Horizontal stripes 100%,100%,1,100%,"i(#y%"$N",x,y,z,c)" rm[^-1] 100%,100%,1,1,"y%"$N"?0:255" fi # Upscale with nearest-neighbor for printing. if $3 ir:=round(max($fW/$W,$fH/$H))*100 r $ir%,$ir%,1,100%,1 r $fW,$fH,1,100%,0,1,0.5,0.5 fi # Insert frame number label. if $7" && "$8 ax,ay:=a=$7-1;[a%2?0.95:0.05,int(a/2)?0.95:0.05] 0 t. "#"$N,0,0,{0,h*$8%},1,255 autocrop. frame. 5%,15%,0 negate. to_rgb. j[^-1] .,$ax~,$ay~ rm. fi => "name(Merged Frames),pos(0,0),opacity(100),mode(alpha)","name(Mask),pos(0,0),opacity(100),mode(alpha)" u $N fx_moire_preview : _is_preview=1 fx_moire $* #@gui Rodilius [Animated] : fx_animate_rodilius, fx_animate_rodilius_preview(1) #@gui : Frames = _int(10,2,100) #@gui : Output as Frames = _bool(1) #@gui : Output as Files = _bool(0) #@gui : Output Folder = _folder() #@gui : Color Mode = choice(1,"Darker","Lighter") #@gui : note = note{"\nStarting Parameters :"} #@gui : Amplitude = float(10,0,30) #@gui : Thickness = float(10,0,100) #@gui : Sharpness = float(300,0,1000) #@gui : Orientations = int(5,2,20) #@gui : Offset = float(0,0,180) #@gui : note = note{"\nEnding Parameters :"} #@gui : Amplitude = float(10,0,30) #@gui : Thickness = float(10,0,100) #@gui : Sharpness = float(300,0,1000) #@gui : Orientations = int(5,2,20) #@gui : Offset = float(180,0,180) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_animate_rodilius : skip "${4=}" if $3 filename="$4/gmic_rodilius.png" else filename="" fi animate rodilius,"${6-10},$5",\ "${11-15},$5",$1,$2,{``$filename} fx_animate_rodilius_preview : skip "${4=}" fx_animate_preview rodilius,"${6-10},$5",\ "${11-15},$5" #@gui Soft Glow [Animated] : fx_animate_glow, fx_animate_glow_preview(1) #@gui : Frames = _int(10,2,100) #@gui : Output as Frames = _bool(1) #@gui : Output as Files = _bool(0) #@gui : Output Folder = _folder() #@gui : note = note{"\nStarting Parameters :"} #@gui : Amplitude = float(0,0,8) #@gui : note = note{"\nEnding Parameters :"} #@gui : Amplitude = float(3,0,8) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_animate_glow : skip "${4=}" if $3 filename="$4/gmic_glow.png" else filename="" fi animate glow,"$5",\ "$6",$1,$2,{``$filename} fx_animate_glow_preview : skip "${4=}" fx_animate_preview glow,"$5",\ "$6" #@gui Spatial Transition : fx_spatial_transition, fx_spatial_transition_preview(1) : * #@gui : Number of Added Frames = _int(10,1,256) #@gui : Shading (%) = float(0,0,100) #@gui : Transition Shape = choice(7,"Bottom Layer","Top Layer","Custom Formula","Horizontal","Vertical", #@gui : "Angular","Radial","Plasma") #@gui : Custom Formula = text{"cos(x*y/(16+32*A))"}_1 #@gui : A-Value = float(0,0,1) #@gui : sep = separator() #@gui : Preview Type = choice(1,"Transition Map","Timed Image","Sequence x4","Sequence x6","Sequence x8") #@gui : Preview Time = float(0.5,0,1) #@gui : Preview = value(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/10/04.") fx_spatial_transition : to_rgba r ${-max_wh},1,100%,0,0,0.5,0.5 shape=-1 formula= if !$3 # Do nothing. elif $3==1 shape=0 elif $3==2 formula="$4" elif $3==3 formula="sin(x*0.5*pi/w*(1+100*A))" elif $3==4 formula="sin(y*0.5*pi/h*(1+100*A))" elif $3==5 formula="atan2(y-h/2,x-w/2)%((1-A)*2*pi+0.001)" elif $3==6 formula="R=0.5*sqrt(w*w+h*h);sqrt((y-h/2)^2+(x-w/2)^2)%(0.001+R*(1-A))" elif $3==7 100%,100% plasma. 1,1,{8/(1+$5)} equalize. 1024 fi if narg($formula) {w},{h},1,1,"A=$5;"$formula fi if $-1 # Preview mode. if !$6 k[$shape] norm n 0,255 elif $6==1" && "!$7 rm[$shape] rm. elif $6==1" && "$7==1 rm[$shape] rm[0] elif $6==1 transition[^$shape] [$shape],$1,$2,$7*($1-1) rm[$shape] rm[0--1:2] else transition[^$shape] [$shape],{$6*2},$2 rm[$shape] to_rgba fi if $!>1 to_rgba frame xy,2%,0,0,0,0 append_tiles , fi else # Apply mode. transition[^$shape] [$shape],$1,$2 rm. fi =>[^] "name(transition),pos(0,0)" if narg($formula) u "{$1}{$2}{$3}{"$formula"}_"{$3==2?2:1}"{$5}{$6}{$7}{0}" fi fx_spatial_transition_preview : if ($3<=1" && "$!<3)" || "($3>1" && "$!<2) gui_print_preview "Warning:",,"This filter requires more input layers to work properly." return fi fx_spatial_transition ${1-3},"$4",${5-7},1 #@gui ____Silhouettes #--------------------------- #@gui Misc #@gui Cupid : fx_cupid, fx_cupid_preview #@gui : Size (%) = float(75,0,100) #@gui : Smoothness = float(0,0,10) #@gui : Color = color(255,255,255,255) #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/01/08.") fx_cupid : max_wh:=$!>0?[${-max_wh}]:[512,512] w,h:=S=[$max_wh]*$1%;[max(S[0],1),max(S[1],1)] l[] { shape_cupid {($7?2:1)*min($w,$h)} if $7 rs 50% fi frame {2.5*$2}%,{2.5*$2}%,0 b $2% * $6 round c 0,255 autocrop 100%,100%,1,3 fc. ${3-5} rv[-2,-1] a c gui_set_layer_name "Heart" gui_set_layer_pos {0.5*([$max_wh]-[w,h])} } mv. 0 fx_cupid_preview : fx_cupid $* blend[^0] [0],alpha rm[0] #@gui Gear : fx_gear, fx_gear_preview #@gui : Size (%) = float(75,0,100) #@gui : Number of Teeth = int(12,1,96) #@gui : Elevation (%) = float(15,0,100) #@gui : Angle (%) = float(0,0,100) #@gui : Inner Radius (%) = float(40,0,100) #@gui : Smoothness = float(0,0,10) #@gui : Color = color(255,255,255,255) #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/01/08.") fx_gear : max_wh:=$!>0?[${-max_wh}]:[512,512] w,h:=S=[$max_wh]*$1%;[max(S[0],1),max(S[1],1)] l[] { shape_gear {($11?2:1)*min($w,$h)},${2-5} if $11 rs 50% fi frame {2.5*$6}%,{2.5*$6}%,0 b $6% * $10 round c 0,255 autocrop 100%,100%,1,3 fc. ${7-9} rv[-2,-1] a c gui_set_layer_name "Gear" gui_set_layer_pos {0.5*([$max_wh]-[w,h])} } mv. 0 fx_gear_preview : fx_gear $* blend[^0] [0],alpha rm[0] #@gui Heart : fx_heart, fx_heart_preview #@gui : Size (%) = float(75,0,100) #@gui : Smoothness = float(0,0,10) #@gui : Color = color(255,255,255,255) #@gui : Antialiasing = bool(1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2018/01/08.") fx_heart : max_wh:=$!>0?[${-max_wh}]:[512,512] w,h:=S=[$max_wh]*$1%;[max(S[0],1),max(S[1],1)] l[] { shape_heart {($7?2:1)*min($w,$h)} if $7 rs 50% fi frame {2.5*$2}%,{2.5*$2}%,0 b $2% * $6 round c 0,255 autocrop 100%,100%,1,3 fc. ${3-5} rv[-2,-1] a c gui_set_layer_name "Heart" gui_set_layer_pos {0.5*([$max_wh]-[w,h])} } mv. 0 fx_heart_preview : fx_heart $* blend[^0] [0],alpha rm[0] #@gui Sierpinski Triangle : fx_sierpinski, fx_sierpinski(1) #@gui : Recursions = int(6,0,10) #@gui : 1st X-Coord = float(50,0,100) #@gui : 1st Y-Coord = float(0,0,100) #@gui : 2nd X-Coord = float(0,0,100) #@gui : 2nd Y-Coord = float(100,0,100) #@gui : 3rd X-Coord = float(100,0,100) #@gui : 3rd Y-Coord = float(100,0,100) #@gui : Color = color(255,255,255) #@gui : Opacity = float(1,0,1) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_sierpinski : foreach { split_opacity l[0] { 100%,100% sierpinski. ${1-7} +fc.. $8,$9,$10 j[0] .,0,0,0,0,$11,..,255 rm[-2,-1] } a c } #@gui _Nature #@gui Barnsley Fern : fx_barnsley_fern, fx_barnsley_fern_preview(1) #@gui : Type = choice("Asplenium Adiantum-Nigrum","Thelypteridaceae") #@gui : Density (%) = float(100,0,300) #@gui : Angle = float(30,-180,180) #@gui : Opacity (%) = float(40,0,100) #@gui : Color = color(10,178,0,255) #@gui : Add as a New Layer = _bool(1) #@gui : sep = separator() #@gui : note = note("This filter renders the Barnsley fern fractal, described here:") #@gui : url = link("https://en.wikipedia.org/wiki/Barnsley_fern") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/18/10.") fx_barnsley_fern : foreach { shape_fern {min(w,h)},$2%,$3,{$4%},$1 *. 255 100%,100%,1,3,[${5-7}] rv[-2,-1] a[-2,-1] c if !$9 blend alpha,{$8/255} else => "name(Barnsley Fern),opacity("{round($8*100/255)})")" rv[-2,-1] fi } fx_barnsley_fern_preview : fx_barnsley_fern ${1-8},0 #@gui Snowflake : fx_snowflake, fx_snowflake(1) #@gui : Recursions = int(5,0,6) #@gui : Opacity = float(1,0,1) #@gui : Color = color(255,255,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_snowflake : foreach { to_color split_opacity l[0] { shape_snowflake {min(w,h)},$1 100%,100%,1,3,[${3-5}] j[0] .,{([w#0,h#0]-[w#1,h#1])/2},0,0,$2,.. k[0] } a c } #@gui _Others #@gui Dragon Curve : fx_dragoncurve, fx_dragoncurve(1) #@gui : Recursions = int(20,0,30) #@gui : Angle = float(0,-180,180) #@gui : Opacity = float(1,0,1) #@gui : Color = color(255,255,255) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2019/01/29.") fx_dragoncurve : foreach { to_color split_opacity l[0] { shape_dragon {min(w,h)},$1,$2 100%,100%,1,3,[${4-6}] j[0] .,{([w#0,h#0]-[w#1,h#1])/2},0,0,$3,.. k[0] } a c } #@gui ____Various #------------------------ #@gui Custom Code [Full] : fx_custom_code, fx_custom_code_preview(1)* #@gui : Code = text(1,"foreach {\n\n to_rgb\n +deform 20\n blend_edges 3\n\n}\n\n\n") #@gui : sep = separator() #@gui : Channel(s) = choice{"None (Allows Multi-layers)","All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]", #@gui : "RGB [Blue]","RGBA [Alpha]","Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]", #@gui : "YCbCr [Luminance]","YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]"} #@gui : Value Action = choice("None","Cut","Normalize") #@gui : Display Debug Info on Preview = bool(0) #@gui : Debug Font Size = choice(2,"Tiny","Small","Normal","Large") #@gui : sep = separator() #@gui : Preview Type = choice{"Full (Allows Multi-Layers)","Forward Horizontal","Forward Vertical", #@gui : "Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom", #@gui : "Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse"} #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter can execute any set of instructions understood by the G'MIC language interpreter. #@gui : Here, you can then test some commands before creating your own G'MIC custom commands and plug-in #@gui : menu entries.\n\n #@gui : Please look at the documentation reference web page :"} #@gui : url = link("https://gmic.eu/reference/") #@gui : note = note{" #@gui : to learn more about available G'MIC commands. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2022/04/28.") #@gui Custom Code [Global] : fx_custom_code, fx_custom_code_preview(1) #@gui : Code = text(1,"foreach {\n\n to_rgb\n +deform 20\n blend_edges 3\n\n}\n\n\n") #@gui : sep = separator() #@gui : Channel(s) = choice{"None (Allows Multi-layers)","All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]", #@gui : "RGB [Blue]","RGBA [Alpha]","Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]", #@gui : "YCbCr [Luminance]","YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]"} #@gui : Value Action = choice("None","Cut","Normalize") #@gui : Display Debug Info on Preview = bool(0) #@gui : Debug Font Size = choice(2,"Tiny","Small","Normal","Large") #@gui : sep = separator() #@gui : Preview Type = choice{"Full (Allows Multi-Layers)","Forward Horizontal","Forward Vertical", #@gui : "Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom", #@gui : "Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse"} #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter can execute any set of instructions understood by the G'MIC language interpreter. #@gui : Here, you can then test some commands before creating your own G'MIC custom commands and plug-in #@gui : menu entries.\n\n #@gui : Please look at the documentation reference web page :"} #@gui : url = link("https://gmic.eu/reference/") #@gui : note = note{" #@gui : to learn more about available G'MIC commands. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/03/10.") #@gui Custom Code [Local] : fx_custom_code, fx_custom_code_preview(0) #@gui : Code = text(1,"foreach {\n\n to_rgb\n +deform 20\n blend_edges 3\n\n}\n\n\n") #@gui : sep = separator() #@gui : Channel(s) = choice{"None (Allows Multi-layers)","All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]", #@gui : "RGB [Blue]","RGBA [Alpha]","Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]", #@gui : "YCbCr [Luminance]","YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]"} #@gui : Value Action = choice("None","Cut","Normalize") #@gui : Display Debug Info on Preview = bool(0) #@gui : Debug Font Size = choice(2,"Tiny","Small","Normal","Large") #@gui : sep = separator() #@gui : Preview Type = choice{"Full (Allows Multi-Layers)","Forward Horizontal","Forward Vertical", #@gui : "Backward Horizontal","Backward Vertical","Duplicate Top","Duplicate Left","Duplicate Bottom", #@gui : "Duplicate Right","Duplicate Horizontal","Duplicate Vertical","Checkered","Checkered Inverse"} #@gui : Preview Split = point(50,50,0,0,200,200,200,0,10)_0 #@gui : sep = separator() #@gui : note = note{"Note: #@gui : This filter can execute any set of instructions understood by the G'MIC language interpreter. #@gui : Here, you can then test some commands before creating your own G'MIC custom commands and #@gui : plug-in menu entries.\n\n #@gui : Please look at the documentation reference web page :"} #@gui : url = link("https://gmic.eu/reference/") #@gui : note = note{" #@gui : to learn more about available G'MIC commands. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/03/10.") fx_custom_code : skip "${1=-skip ,}" ({'{/"$1"}'}) discard. 92,10 _gcp_arg={t} rm. m "_fx_custom_code_start : "$_gcp_arg if $4 _nb_in=$! _dim_in="" sep="" foreach { _dim_in=$_dim_in$sep"["$>"] = "{w}x{h}x{d}x{s}", in ["{_round([im,iM],0.1)}"]" sep="\n" } fi if $2 ac "_fx_custom_code_start _status_out=${}",{$2-1},$3 else _fx_custom_code_start _status_out=${} if $3==1 c 0,255 elif $3==2 n 0,255 fi fi if $4 _nb_out=$! _dim_out="" sep="" foreach { _dim_out=$_dim_out$sep"["$>"] = "{w}x{h}x{d}x{s}", in ["{_round([im,iM],0.1)}"]" sep="\n" } fi um _fx_custom_code_start fx_custom_code_preview : skip "${1=-skip ,}" w,h={w},{h} l { ({'{/"$1"}'}) discard. 92,10 _gcp_arg={t} rm. if $6 gui_split_preview "fx_custom_code $_gcp_arg,${2--2}",${-3--1} else fx_custom_code $_gcp_arg,${2--2} fi onfail error_msg=${} rs $w,$h,1,1 gui_print_preview "Syntax error:",,{``$error_msg},20,40 } if $4 # Display debug infos on preview if !$3 % 256 fi if $!>1 gui_preview fi rs $_preview_area_width,$_preview_area_height,1 siz0=13 siz1=17 siz2=19 siz3=22 if ['$_status_out']==0 _status_out=(empty) fi info="Input images: "#$_nb_in"\n"\ $_dim_in"\n\n"\ "Output images: "#$_nb_out"\n"\ $_dim_out"\n\n"\ "Output status: "$_status_out 0 t. {``$info},0,0,${siz$5},1,255 expand. xy,5 +dilate. 3 a[-2,-1] c rs[^-1] ${-max_wh},2,1 r. ..,..,1,100%,0 drgba[^-1] /[^-1] 2 blend[^-1] .,alpha rm. fi #@gui Export RGB-565 File : fx_output_565,_none_ #@gui : Filename = _fileout("out565.rgb") #@gui : Reverse endianness = _bool(0) #@gui : sep = separator() #@gui : note = note{"Note: This filter saves your selected layer as a raw RGB-565 file. Keep in mind that #@gui : you have to remember the image dimension if you want to reload the image file afterwards!"} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/05/03.") fx_output_565 : output_565 "$1",$2 #@gui Games & Demos : fx_gmic_demos, fx_gmic_demos_preview #@gui : Selection = choice("2048","Blobs Editor","Bouncing Balls","Connect-Four","Fire Effect","Fireworks", #@gui : "Fish-Eye Effect","Fourier Filtering","Hanoi Tower", #@gui : "Histogram","Hough Transform","Jawbreaker","Virtual Landscape","The Game of Life","Light Effect", #@gui : "Mandelbrot Explorer","3D Metaballs","Minesweeper", #@gui : "Minimal Path","Pacman","Paint","Plasma Effect","RGB Quantization","3D Reflection","3D Rubber Object", #@gui : "Shadebobs","Spline Editor", #@gui : "3D Starfield","Tetris","Tic-Tac-Toe","3D Waves","Fractal Whirl") #@gui : sep = separator() #@gui : note = note("Note: This filter proposes a showcase of some interactive demos, all written #@gui : as G'MIC scripts.") #@gui : note = note{"On most demos, you can use the keyboard shortcut CTRL+D to double the window #@gui : size (and CTRL+C to go back to the original size). #@gui : Also, feel free to use the mouse buttons, as they are often used to perform an action. #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2014/10/09.") fx_gmic_demos : coms=2048,blobs,bouncing,connect4,fire,fireworks,fisheye,fourier,hanoi,histogram,hough,jawbreaker,landscape,life,\ light,mandelbrot,metaballs3d,minesweeper,minimal_path,pacman,paint,plasma,quantize_rgb,reflection3d,\ rubber3d,shadebobs,spline,starfield3d,tetris,tictactoe,waves,whirl com=${arg\ {1+$1},$coms} if $!>0 sel=0 else sel= fi +l[$sel] { m "foo : x_"$com foo rm um foo } fx_gmic_demos_preview : rm input_cached img/gmic_demos.cimgz k[$1,-1] rows. $1 map[0] [1] k[0] #@gui Histogram Analysis : _none_, fx_display_histogram(1) #@gui : Number of Clusters = int(256,2,1024) #@gui : sep = separator() #@gui : Channel(s) = choice("All","RGBA [All]","RGB [All]","RGB [Red]","RGB [Green]","RGB [Blue]","RGBA [Alpha]", #@gui : "Linear RGB [All]","Linear RGB [Red]","Linear RGB [Green]","Linear RGB [Blue]","YCbCr [Luminance]", #@gui : "YCbCr [Blue-Red Chrominances]","YCbCr [Blue Chrominance]","YCbCr [Red Chrominance]", #@gui : "YCbCr [Green Chrominance]","Lab [Lightness]","Lab [ab-Chrominances]","Lab [a-Chrominance]", #@gui : "Lab [b-Chrominance]","Lch [ch-Chrominances]","Lch [c-Chrominance]","Lch [h-Chrominance]","HSV [Hue]", #@gui : "HSV [Saturation]","HSV [Value]","HSI [Intensity]","HSL [Lightness]","CMYK [Cyan]","CMYK [Magenta]", #@gui : "CMYK [Yellow]","CMYK [Key]","YIQ [Luma]","YIQ [Chromas]","RYB [All]","RYB [Red]","RYB [Yellow]","RYB [Blue]") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2016/20/06.") fx_display_histogram : mode=${arg0\ $2,all,rgba,rgb,rgb_r,rgb_g,rgb_b,rgba_a,\ lrgb,lrgb_r,lrgb_g,lrgb_b,\ ycbcr_y,ycbcr_cbcr,ycbcr_cb,ycbcr_cr,ycbcr_cg,\ lab_l,lab_ab,lab_a,lab_b,\ lch_ch,lch_c,lch_h,\ hsv_h,hsv_s,hsv_v,hsi_i,hsl_l,\ cmyk_c,cmyk_m,cmyk_y,cmyk_k,\ yiq_y,yiq_iq} _ac_$mode m "_ac_precond : "$_p m "_ac_forward : "$_f m "_ac_backward : "$_b foreach { _ac_precond _ac_forward[0] channels $_s display_histogram {w},{h},$1,0,255 if s==2" || "s==4 channels 0,2 fi } #@gui Import Data : fx_import_image, gui_no_preview #@gui : Filename = filein() #@gui : Normalize = bool(1) #@gui : note = note{"\nNote: #@gui : This filter can import any image data read by the G'MIC language interpreter. #@gui : It includes exotic formats as : Pandore, CImg, Inrimage, AVI/MPEG (requires FFMPEG installed), ... #@gui : "} #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2010/29/12.") fx_import_image : skip "${1=}" rm i "$1" s z if $2 n 0,255 else c 0,255 fi #@gui Import RGB-565 File : fx_input_565 #@gui : Filename = filein() #@gui : Width = text("800") #@gui : Height = text("600") #@gui : Reverse endianness = bool(0) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2020/05/03.") fx_input_565 : skip "${1=}" l[] { check "isint($2,1) && isint($3,1)" onfail error "Invalid Specified Dimensions" } if ['"$1"']==0 gui_warning_preview "Choose a filename" elif !isfile(['"$1"']) gui_warning_preview "Filename not found!" else input_565 "$1",${2-4} mv. 0 fi #@gui Intarsia : fx_intarsia, fx_intarsia_preview #@gui : note = note{"Note: #@gui : Intarsia is a method of Crochet/Knitting with a number of colours, in which a separate ball of yarn #@gui : is used for each area of colour. #@gui : This filter creates a HTML version of a graph chart which is solely used for this purpose #@gui : "} #@gui : sep = separator() #@gui : Output Directory = _folder("") #@gui : Output HTML File = _text("intarsia.html") #@gui : sep = separator() #@gui : Maximum Image Size = int(512,2,1024) #@gui : Maximum Number of Image Colors = _int(12,2,64) #@gui : Starting Point = choice(1,"Top Left","Top Right","Bottom Left","Bottom Right") #@gui : Loop Method = choice("Row by Row","Column by Column") #@gui : sep = separator() #@gui : Add Comment Area in HTML Page = _bool(1) #@gui : Preview Progress (%) = float(100,0,100) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2015/09/07.") fx_intarsia : to_rgb +foreach { nm=${gui_layer_name.} # Constrain image for size and number of colors, and index it with colormap. if max(w,h)>$3 rs $3,$3 fi +colormap 0 if w>$4 rm. +colormap $4,1 fi round[1] index[0] [1] # Output header and title. 0 => $nm ('{b}') f. "x?i:i>=97 && i<=122?i - 32:i" image_basename={t} rm[-2,-1] ('"\n\ \n\
\n\

"$image_basename" ("{0,w}x{0,h}")

\n\
\n\ \n"') # Render image of colors. 0 => "$2" image_name={b} rm. nb_cols={1,w} repeat $nb_cols { color={1,I($>)} R,G,B=$color ('${dec2hex\ {$R*65536+$G*256+$B}}') -. {'0'} r. 6,1,1,1,0,0,1,0 +. {'0'} f. i>=_'a'" && "i<=_'z'?i+_'A'-_'a':i hcolor={t} rm. 48,32,1,4 fc. $color,255 frame. 1,1,0,0,0,255 o. "$1/"${image_name}_$>.png rm. ('"\n"') } ('"
Colour "$>".png"\" />#"$hcolor"
\n
"') # Render result and overview images. starting=${"arg {1+$5},\"Top left\",\"Top right\",\"Bottom left\",\"Bottom right\""} label=${"arg {1+$6},Row,Column"} if $6 dir0="T → B" dir1="B → T" else dir0="L → R" dir1="R → L" fi dir:=arg(1+2*$5+$6,0,0,1,0,0,1,1,1) +map[0] [1] +fx_intarsia_preview. ${1-7},63 drgba. rs.. 200,200,1,2 to_rgba[-2,-1] frame[-2,-1] xy,1,0,0,0,255 frame[-2,-1] y,20,0,0,0,0 t.. "Result",0,0,16,1,0,0,0,255 t. "Ordering overview",0,0,16,1,0,0,0,255 frame[-2,-1] xy,20,0,0,0,0 o.. "$1/"${image_name}_A.png o. "$1/"${image_name}_B.png rm[-2,-1] ('"
"\ "
\n"') if $7 ('"

Additional comments:

\n"') fi ('"

Starting point: "$starting"\           Orientation: "$label" by "$label"

\n"') rm[1] # Output geometry. ('"\n"') _fx_intarsia[0] $5,$6,0 +l[0] { s y foreach { if $>%2 mirror. x fi im:=im compress_rle 0,0 rows 6,100% ('"\n"') rm[0] a x dir:=!$dir } a x } rm[0] ('"
"$label" "{1+$>}""${dir$dir}"\n"') i=0 n=0 do val={0,i[$i]} i+=1 if $val>=0 occ=1 else occ:=-$val val={0,i[$i]} if $val<0 val=0 else i+=1 fi fi val+=$im ('"colour:"$val" "$occ') if {0,$i\n"') fi while $i
\n
\n"') a x ot "$1/$2" rm } fx_intarsia_preview : to_rgb foreach { if max(w,h)>$3 rs $3,$3 fi to_rgba _fx_intarsia $5,$6,0 100%,100%,1,1,"(y%2?y*w+w-1-x:y*w+x)<$8*wh/100" * if min(w,h)<140 rs 140,140,1,2 fi expand xy,16 100%,100% circle. 16,16,1%,1,1 arrow3d. 0,0,0,{w/4},0,0,2%,15%,10% col3d. 1 j3d.. .,16,16,0,1,2,0,0 rm. +dilate. 3 r.. 100%,100%,1,3,0,0,0,0,0,0.5 a[-2,-1] c *. 255 blend alpha _fx_intarsia $5,$6,1 } _fx_intarsia : if $3" && "$2 transpose fi if !$1 # Start from top left. elif $1==1 # Start from top right. mirror x elif $1==2 # Start from bottom left. mirror y elif $1==3 # Start from bottom right. mirror xy fi if !$3" && "$2 transpose fi #@gui Sample Image : fx_image_sample, fx_image_sample_preview #@gui : Input = choice{"Random","Apples","Balloons","Barbara","Boats","Bottles","Butterfly","Cameraman","Car","Cat", #@gui : "Chick","Cliff","Colorful","David","Dog","Duck","Eagle","Elephant","Earth","Flower","Fruits", #@gui : "Gmicky (Deevad)","Gmicky (Mahvin)","Gmicky & Wilber","Greece","Gummy","House","Inside","Landscape","Leaf", #@gui : "Lena","Leno","Lion","Mandrill","Mona Lisa","Monkey","Parrots","Pencils","Peppers","Portrait0","Portrait1", #@gui : "Portrait2","Portrait3","Portrait4","Portrait5","Portrait6","Portrait7","Portrait8","Portrait9", #@gui : "Roddy","Rooster","Rose","Square","Swan","Teddy","Tiger","Tulips","Wall","Waterfall","Zelda"} #@gui : note = note("Choosing 0 for parameters Width or Height means Automatic. #@gui : ") #@gui : Width = _int(0,0,1024) #@gui : Height = _int(0,0,1024) #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2017/16/01.") fx_image_sample : if $1 sp {$1-1},$2,$3 else sp ?,$2,$3 fi mv. 0 fx_image_sample_preview : w,h={w},{h} rm fx_image_sample $1,{$w>$h?$w:0},{$h>$w?$h:0} #@gui Solve Maze : fx_solve_maze, fx_solve_maze_preview(1) #@gui : Starting Point (%) = point(5,5) #@gui : Ending Point (%) = point(95,95) #@gui : Smoothness = float(0.1,0,1) #@gui : Thickness = int(3,1,10) #@gui : Color = color(255,0,0) #@gui : Maze Type = choice("Dark Walls","White Walls") #@gui : sep = separator() #@gui : note = note("Author: David Tschumperlé.      Latest Update: 2011/01/09.") fx_solve_maze : repeat $! { +norm. >=. 50% if !$10 negate. fi *. 255 +b. $5% *.. 1e10 +[-2,-1] minimal_path. $1%,$2%,0,$3%,$4%,0 transpose. pointcloud. 0 dilate. $6 r. ..,..,1,1,0 to_rgba. replace_color. 0,0,1,1,1,255,${7-9},255 replace_color. 0,0,0,0,0,255,0,0,0,0 ellipse. $1%,$2%,5,5,0,1,${7-9},255 ellipse. $3%,$4%,5,5,0,1,${7-9},255 rv[-2,-1] mv[-2,-1] 0 } fx_solve_maze_preview : drgba line $1%,$2%,$3%,$4%,1,0xCCCCCCCC,${7-9} ellipse $1%,$2%,3,3,0,1,${7-9} ellipse $1%,$2%,3,3,0,1,0x1,0 ellipse $3%,$4%,3,3,0,1,${7-9} ellipse $3%,$4%,3,3,0,1,0x1,0 #@gui _ #--------------------------------- # #@cli :: Additional Gallery Images # #--------------------------------- #@cli _gallery_arrays #@cli : This entry defines some examples of array filters for the G'MIC gallery page. #@cli : $ image.jpg fx_frame_blur 30,30,0,5,0,0,128,128,128,0,5,255,255,255,2,2,1,0,0.5,0.5,0 _label="Frame~[blur]" #@cli : $ sample tiger,leno,monkey,duck,eagle frame xy,3,0 frame xy,3,255 montage A _label="Montage" #@cli : $ image.jpg fx_puzzle 5,5,0.5,0,0,0.3,100,0.2,255,100,0,0,0,0,0,0 _label="Puzzle" #@cli _gallery_artistic #@cli : This entry defines some examples of artistic filters for the G'MIC gallery page. #@cli : $ image.jpg fx_engrave 0.5,4,0,7.68,15.2,0,0,1,10,1,0,0,0,1,0 gui_merge_layers _label="Engrave" #@cli : $ image.jpg fx_bokeh 3,8,0,30,8,4,0.3,0.2,210,210,80,160,0.7,30,20,20,1,2,170,130,20,110,0.15,0 _label="Bokeh" #@cli : $ image.jpg fx_8bits 25,800,16,0 _label="Oldschool~8bits" #@cli : $ image.jpg fire_edges 0.7,0.25,0.5,25,20 _fps=6 _label="Edges~on~fire" #@cli : $ image.jpg fx_diffusiontensors 10,5,3,1,0.15,1,0,3,0 _label="Diffusion~tensors" #@cli : $ image.jpg fx_dreamsmooth 3,1,1,0.8,0,0.8,1,24,0 _label="Dream~smoothing" #@cli : $ image.jpg fx_feltpen 300,50,1,0.1,20,5,0 _label="Felt~pen" #@cli : $ image.jpg gtutor_fpaint 0.5,0.5,0,0,45,0.5,0.5,0.5,0 _label="Finger~paint" #@cli : $ image.jpg fx_graphic_novelfxl 0,2,6,5,20,0,0.62,14,0,1,0.5,0.78,1.92,0,0,0,1,1,1,0.5,0.8,1.28 \ # _label="Novel~FX" #@cli : $ image.jpg fx_illustration_look 100,0,0,0,0 _label="Illustration~look" #@cli : $ image.jpg fx_lylejk_painting 10,2,4,10,0 _label="Lylejk~painting" #@cli : $ image.jpg fx_painting 5,2.5,1.5,50,1,0 _label="Painting" #@cli : $ image.jpg fx_posterize 150,30,1,6,0,0,1,0 _label="Posterize" #@cli : $ image.jpg fx_quadtree 2,1024,1.05,0,2.33,0.68,0.39,1,0 _label="Quadtree~variations" #@cli : $ image.jpg fx_vector_painting 9.37,0 _label="Vector~painting" #@cli : $ image.jpg cl_comic 0,2,0,1,1,15,15,1,10,20,6,2,0,0,0,0,0,0,50,50 _label="Comic~Book" #@cli : $ image.jpg fx_cpencil 1.3,50,20,2,2,1,0 _label="Colored~Pencils" #@cli : $ image.jpg fx_paint_with_brush 6,"6",1,16,30,100,100,0,95,0.5,8,-1,-1,2,2,30,0,0,0,100,20,1,0,0,0,30,0,0,0,0,\ # 0,0,1,45,0,0,50,50 _label="Whirls" #@cli : $ image.jpg fx_paint_with_brush 5,"5",0,16,10,100,100,10,80,0.5,3,135,50,6,1,20,0,0,0,90,3,10,0,0,0,30,0,15,15,\ # 15,15,15,1,45,0,0,50,50 _label="Fuzzy~Painting" #@cli : $ sample portrait4 blur 1 fx_polygonize_delaunay 40,5,75,0.5,3,50,0,0,0,255,1,0,50,50 _label="Polygonize" #@cli : $ image.jpg fx_posterize 150,30,1,12,0,0,0,0,50,50 _label="Posterize" #@cli : $ sample portrait2 samj_Test_Skeletik 10,1,0,0,0,3,1,0,0,0,255 _label="Skeletik" #@cli : $ sample portrait6 fx_sketchbw 3,45,180,30,1.75,0.02,0.5,0.75,0.1,0.7,3,6,0,1,4,0,0,50,50 _label="Sketch" #@cli : $ image.jpg warhol 3,3,2,40 _label="Warhol" #@cli _gallery_blackandwhite #@cli : This entry defines some examples of black-and-white filters for the G'MIC gallery page. #@cli : $ image.jpg fx_freaky_bw 90,20,0,0,0,0 _label="Freaky~B&W" #@cli : $ image.jpg fx_engrave 0.5,50,0,8,40,0,0,0,10,1,0,0,0,1,0 _label="Engrave" #@cli : $ image.jpg fx_gcd_layeretch 11,4,12,0.12,100,8.5,5,0,0,3,1,1,0 _label="Multi-layer~etch" #@cli : $ image.jpg fx_pencil_portraitbw 30,120,1,0.5,144,79,21,0 _label="Pencil~portrait" #@cli : $ image.jpg fx_gcd_etch 125,153,171,185,0.1,50,80,50,10,15,12,20,0,1,0.3,1,0,0 _label="Threshold~etch" #@cli : $ image.jpg fx_ghost 200,2,2,1,3,16,0,0,50,50 _label="Ghost" #@cli : $ image.jpg cl_lineart 0,0,2,1,15,15,1,0,6,2,2,0,0,0,50,50 _label="Lineart" #@cli : $ image.jpg gcd_emboss 128,0 _label="Emboss" #@cli _gallery_colors #@cli : This entry defines some examples of color filters for the G'MIC gallery page. #@cli : $ image.jpg fx_color_abstraction 1,10,0.2,0 _label="Color~abstraction" #@cli : $ image.jpg fx_boost_chroma 90,0,0 _label="Boost~chromaticity" #@cli : $ image.jpg fx_retrofade 20,6,40,0 _label="Retro~fade" #@cli : $ image.jpg fx_tk_vintage 2,0.85,0.7,80,200,5,147,26,161,0.3,235,220,176,0.4,190,181,108,0.2,\ # 0,0,100,0,0.3,25,0,0 _label="Vintage~style" #@cli : $ image.jpg fx_retinex 75,16,1,1,1,5,15,80,250,0,50,50 _label="Retinex" #@cli _gallery_deformations #@cli : This entry defines some examples of deformation filters for the G'MIC gallery page. #@cli : $ image.jpg animate "flower","30,10,0,0","30,10,0,360",10 rm. _fps=6 _label="flower" #@cli : $ image.jpg fx_conformal_maps 8,1,0,"((1.1 + i*z/6)/(1.04 - i*z/6))^6.2",0,0,0,0,0,3,0,0,"1024","1024" \ # _label="Conformal~maps" #@cli : $ image.jpg souphead_droste10 40,100,1,1,1,0,0,0,0,0,1,10,1,0,90,0,0,0,0,1,0,1,1,0,0,0,0,0,1,0,0 \ # _label="Continuous~droste" #@cli : $ image.jpg fx_crease 30,10,3 _label="Crease" #@cli : $ image.jpg fx_distort_lens 0.29,0,0.23,50,50,0,0 _label="Distort~lens" #@cli : $ image.jpg fx_drop_water 0,20,2,80,0,3,35,10,1,0.5,0.25,0.5,0.75,0.05,0.15,1 gui_merge_layers \ # _label="Drop~water" #@cli : $ image.jpg fx_reflect 50,1,110,160,190,64,0,1.5,0,-3.3,7,1.5 _label="Reflection" #@cli : $ image.jpg fx_square_circle 0,1,0,0,0,0,0,0 _label="Square~to~circle" #@cli : $ image.jpg fx_textured_glass 40,40,1,1,0,2,0,0 _label="Textured~glass" #@cli : $ sample lena,leno,320 morph 40 _fps=5 _label="morph" #@cli : $ image.jpg fx_breaks 0,30,30,0.5,3 _label="Breaks" #@cli : $ image.jpg fx_circle_transform 50,50,75,50,-2,-2,0,1,3,1 _label="Circle~Transform" #@cli : $ image.jpg fx_symmetrizoscope 4,45,3,0 _label="Kaleidoscope" #@cli : $ image.jpg fx_spherize 50,1,0,50,50,0,0,2,0 _label="Spherize" #@cli _gallery_filtering #@cli : This entry defines some examples of filters for the G'MIC gallery page. #@cli : $ image.jpg fx_gcd_crt 1.8,1.8,0,0 equalize 256 _label="CRT~sub-pixels" #@cli : $ image.jpg fx_dirty 30,1,0,0,0 _label="Dirty" #@cli : $ image.jpg fx_freaky_details 2,10,1,11,0,32,0 _label="Freaky~details" #@cli : $ image.jpg jeje_normalize_local_variance 50,5,5,1,0,0 _label="Local~variance~normalization" #@cli : $ image.jpg fx_mighty_details 25,1,25,1,11,0 _label="Mighty~details" #@cli : $ image.jpg samj_Barbouillage_Paint_Daub 2,2,100,0.2,1,4,1,0,8 _label="Barbouillage" #@cli : $ image.jpg fx_blur_bloom 1,2,5,0,1,0,0,0,7,0,50,50 _label="Blur~Bloom" #@cli : $ image.jpg fx_glow 6,7,0,0,50,50 _label="Glow" #@cli : $ image.jpg fx_jpeg_artefacts 25,0,50,50 _label="JPEG~Artefacts" #@cli : $ image.jpg fx_lomo 20,0,50,50 _label="Lomo" #@cli : $ image.jpg fx_noise_perlin 0,100,8,0,0,4,0,0,2,0,0,1,0,2,0,50,50 _label="Perlin~Noise" #@cli _gallery_patterns #@cli : This entry defines some examples of pattern filters for the G'MIC gallery page. #@cli : $ image.jpg fx_rain 65,10,50,0.1,1,1,0 gui_merge_layers _label="Rain~&~snow" #@cli : $ 400,400,1,3 fx_camouflage 9,12,100,30,46,33,75,90,65,179,189,117,255,246,158 _label="Camouflage" #@cli : $ image.jpg jeje_clouds 50,0.5 _label="Clouds" #@cli : $ image.jpg fx_crystal 50,0.2,20,0 _label="Crystal" #@cli : $ 400,400,1,3 fx_crystal_background 10,25,0,100,1 _label="Crystal~background" #@cli : $ image.jpg fx_marble 0.5,1,0,0,0.4,0.6,0.6,1.1,0,100 _label="Marble" #@cli : $ image.jpg fx_mineral_mosaic 1,2,1,100,0 _label="Mineral~mosaic" #@cli : $ image.jpg fx_shapes 1,16,10,2,5,106.8,2,0,0,1,0 _label="Op~art" #@cli : $ 400,400,1,3 fx_satin 20,1,0,0,0,0,255,255,255,255,255,0,0,0,-50,0,0 _label="Satin" #@cli : $ 400,400,1,3 fx_seamless_turbulence 15,20,0,1,3,1 _label="Seamless~turbulence" #@cli : $ image.jpg fx_shockwaves 10,10,20,0,0 _label="Shock~waves" #@cli : $ 400,400,1,3 fx_equation_parametric "sin(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)",\ # "cos(t)*(exp(cos(t))-2*cos(4*t)-sin(t/12)^5)",0,100,4096,1,0,64,0,0,128,0,0,1,1,1 _label="Equation~plot~[parametric]" #@cli : $ 400,400,1,3 KittyRings 30,8,0,1,113,0,113,0,255,0 _label="Kitaoka~spin~illusion" #@cli : $ 400,400,1,3 fx_neon_lightning 50,50,0,50,50,100,50,0.7,3,130,80,50,0.25,0 _label="Neon~lighting" #@cli : $ image.jpg fx_lava 8,5,3,0,0 _label="Lava" #@cli : $ sample monkey,lion,monkey 100%,100% plasma. equalize. 256 transition[0,1,2] [3],10 rm. _fps=10 \ # _label="transition" #@cli : $ image.jpg fx_shapeism 2,7,0.38,0,1,5,32,8,3,1,5,0.5,1,0,0,0,255 _label="Shapeism" #@cli : $ image.jpg fx_generic_halftone 0,1,100,12,1,0,4,0,90,1,0,0 _label="Halftone" #@cli : $ image.jpg fx_rebuild_from_similar_blocks 5,10,0.75,1 _label="Rebuild~From~Similar~Blocks" #@cli : $ image.jpg fx_warp_by_intensity 1.62,0.6,128,128,0,1,3,0,0,50,50 _label="Warp~by~Intensity" #@cli : $ image.jpg fx_memoakten_algorithm_a 0,20,30,30,2,50,10,50,40,3,60,1,0,0,0,255,255,255,255,0,0,255,128,0,255,\ # 255,0,0,0,0 _label="Algorithm A" #@cli _gallery_3dmeshes #@cli : This entry defines some examples of 3D rendering filters for the G'MIC gallery page. #@cli : $ sample leno,lion,leno resize 400,400 transition3d 20,5,5 rm. _fps=10 _label="transition3d" #@cli : $ 256,192 fx_text_pointcloud3d 64,"G'MIC","Rocks!",1,200,220,255,255,255,255,255,2,2,1,19 _fps=10 \ # _label="3D~text~pointcloud" #@cli : $ sphere3d 10 repeat 5 { +add3d[-1] 10,{u(-10,10)},0 color3d[-1] ${-rgb} } add3d _fps=10 _label="3D~Spheres" #@cli : $ repeat 20 { torus3d 15,2 color3d[-1] ${-rgb} mul3d[-1] 0.5,1 if $>%2 rotate3d[-1] 0,1,0,90 fi add3d[-1] 70 \ # add3d rotate3d[-1] 0,0,1,18 } double3d 0 _fps=10 _label="3D~Ring" #@cli : $ image.jpg distribution3d circles3d 5 o3d 0.5 colorcube3d primitives3d[-1] 1 add3d _fps=10 \ # _label="RGB~Color~Distribution" #@cli : $ image.jpg blur 5 elevation3d. 0.75 _fps=10 _label="3D~Elevation" #@cli : 128,128,1,3,u(255) plasma 10,3 blur 4 sharpen 10000 n 0,255 \ # elevation3d[-1] 'X=(x-64)/6;Y=(y-64)/6;-100*exp(-(X^2+Y^2)/30)*abs(cos(X)*sin(Y))' _fps=10 _label="3D~Elevation" #@cli : $ gmic3d _fps=10 _label="3D~G'MIC~Logo" #@cli : $ image.jpg rescale2d ,32 imageblocks3d -20 mode3d 3 _fps=10 _label="3D~Image~Blocks" #@cli : $ image.jpg imagerubik3d , _fps=10 _label="Rubik~Cube" #@cli : $ shape_cupid 480 skeleton3d , _fps=10 _label="3D~Skeleton" #@cli : $ spherical3d "abs(1+0.5*cos(3*phi)*sin(4*theta))" _fps=10 _label="3D~Spherical~Function" #@cli : $ 6,6,6,9,"U = [x,y,z] - [w,h,d]/2; U/=norm(U); mul(U,U,3) + 0.3*eye(3)" tensors3d 0.8 _fps=10 \ # _label="3D~Tensors" #@cli : $ text3d "G'MIC Rocks!" _fps=10 _label="3D~Text" #@cli : $ torus3d 10,3 color3d[-1] ${-rgb} _fps=10 _label="3D~Torus" #@cli : $ weird3d 48 color3d[-1] ${-rgb} _fps=10 _label="3D~Weird" #@cli _gallery_stylization #@cli : This entry defines some examples of image stylization for the G'MIC gallery page. #@cli : $ sample car _fx_stylize starrynight _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,5,2,1.85,0 _label="from~Van~Gogh:~Starry~Night" #@cli : $ sample car _fx_stylize graytree _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,2,2,1.85,0 _label="from~Mondrian:~Gray~Tree" #@cli : $ sample car _fx_stylize yellowredblue _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,10,2,1.85,0 \ # _label="from~Kandinsky:~Yellow-Red-Blue" #@cli : $ sample car _fx_stylize littlebayatlaciotat _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,2,2,1.85,0 \ # _label="from~Braque:~Little~Bay~at~La~Ciotat" #@cli : $ sample car _fx_stylize leviaducalestaque _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 \ # _label="from~Braque:~Le~Viaduc~a~l'Estaque" #@cli : $ sample car _fx_stylize greatwave _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Hokusai:~The~Great~Wave" #@cli : $ sample elephant input ../img/hatching.png _output_mode=1 \ # +fx_stylize 1,4,0,0,1,2,3,0.5,0.1,3,3,0,0.7,0,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Hatch~Drawing" #@cli : $ sample cat input ../img/hatching.png _output_mode=1 \ # +fx_stylize 1,4,0,0,1,2,3,0.5,0.1,3,3,0,0.7,0,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Hatch~Drawing" #@cli : $ sample bottles _fx_stylize starrynight _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Van~Gogh:~Starry~Night" #@cli : $ sample cat _fx_stylize summertime9a _output_mode=1 \ # +fx_stylize 1,6,0,0,2,0,1,0.5,0.1,3,3,0,0.7,0,2,1,0,5,5,7,1,130,10,2,1.85,0 _label="from~Pollock:~Summertime~No~9A" #@cli : $ sample cat _fx_stylize greatwave _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Hokusai:~The~Great~Wave" #@cli : $ sample dog _fx_stylize convergence _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,5,2,1.85,0 _label="from~Pollock:~Convergence" #@cli : $ sample dog _fx_stylize irises _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,1,5,0,0.7,1,0,1,0,5,5,7,1,30,20,2,1.85,0 _label="from~Van~Gogh:~Irises" #@cli : $ sample mandrill _fx_stylize themandola _output_mode=1 \ # +fx_stylize 1,5,0,0,0,3,1,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Braque:~The~Mandola" #@cli : $ sample square _fx_stylize orientalpleasuregardenanagoria _output_mode=1 \ # +fx_stylize 1,6,0,0,0.52,0.5,3,0.14,0.1,3,3,0,0.7,3.39,0,1,0,5,5,7,5,30,4,2,1.85,0 \ # _label="from~Klee:~Oriental~Pleasure~Garden~Anagoria" #@cli : $ sample monalisa _fx_stylize squareswithconcentriccircles _output_mode=1 \ # +fx_stylize 1,4,0,0,0.15,3,2,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 \ # _label="from~Kandisnky:~Squares~with~Concentric~Circles" #@cli : $ sample monalisa _fx_stylize inthestyleofkairouan _output_mode=1 \ # +fx_stylize 1,4,2,0,0.15,3,2,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,5,2,1.85,0 \ # _label="from~Klee:~In~the~Style~of~Kairouan" #@cli : $ sample square _fx_stylize polyphony2 _output_mode=1 \ # +fx_stylize 1,6,0,0,0.15,3,2,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Klee:~Polyphony~2" #@cli : $ sample square _fx_stylize wheatstacksendofsummer _output_mode=1 \ # +fx_stylize 1,6,0,0,0.15,3,2,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 \ # _label="from~Monet:~Wheatstacks~-~End~of~Summer" #@cli : $ sample square _fx_stylize portraitdemetzinger _output_mode=1 \ # +fx_stylize 1,5,0,0,0.1,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,10,2,1.85,0 \ # _label="from~Delaunay:~Portrait~de~Metzinger" #@cli : $ sample monalisa input ../img/mandelbrot.jpg _output_mode=1 \ # +fx_stylize 1,3,3,0,0.15,4,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,5,2,1.85,0 _label="from~Mandelbrot~Fractal~Set" #@cli : $ sample bottles _fx_stylize redtree _output_mode=1 \ # +fx_stylize 1,5,0,0,2.12,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 \ # _label="from~Mondrian:~Evening;~Red~Tree" #@cli : $ sample bottles _fx_stylize redwaistcoat _output_mode=1 \ # +fx_stylize 1,4,0,0,0.67,3.17,3,0.5,0.06,3,3,0,0.7,5,0,2,0,5,5,7,1,30,5,1.05,1.85,0 \ # _label="from~Klee:~Red~Waistcoat" #@cli : $ sample bottles _fx_stylize reservoirhortadeebro _output_mode=1 \ # +fx_stylize 1,6,0,0,0.5,2,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 _label="from~Picasso:~The~Reservoir" #@cli : $ sample bottles _fx_stylize almondblossom _output_mode=1 \ # +fx_stylize 1,6,0,0,0,3,3,0.5,0.1,3,3,0,0.7,5,0,2,0,5,5,7,1,30,1,2,1.85,0 _label="from~Van~Gogh:~Almond~Blossom" #@cli : $ sample bottles _fx_stylize landscapenearantwerp _output_mode=1 \ # +fx_stylize 1,6,0,0,2.17,3.65,3,0.5,0.1,3,3,0,0.7,1,0,1,0,5,5,7,1,30,1,2,1.85,0 \ # _label="from~Braque:~Landscape~near~Antwerp" #@cli : $ sample bottles _fx_stylize wheatfieldwithcrows _output_mode=1 \ # +fx_stylize 1,6,0,0,3.86,2,3,0.5,0.1,3,3,0,0.7,3.35,1,1,0,5,5,7,1,30,1,5,1.85,0 \ # _label="from~Van~Gogh:~Wheat~Field~with~Crows" #@cli _gallery_codesamples #@cli : This entry defines some examples of coding fun filters for the G'MIC gallery page. #@cli : $ https://gmic.eu/samples/lissajous.gmic go _fps=10 _label="Lissajous" #@cli : $ https://gmic.eu/samples/torus3d.gmic go _fps=10 _label="3D~torus" #@cli : $ https://gmic.eu/samples/pacman.gmic go _fps=25 _label="Pacman" #@cli : $ https://gmic.eu/samples/scrolling.gmic go _fps=25 _label="Scrolling" #@cli : $ https://gmic.eu/samples/landscape.gmic go _fps=12 _label="Landscape" #@cli : $ https://gmic.eu/samples/mandelbrot.gmic go _fps=8 _label="Mandelbrot" #@cli : $ https://gmic.eu/samples/heart.gmic go _fps=15 _label="Heart" #@cli : $ https://gmic.eu/samples/distortion.gmic go _fps=20 _label="Distortion" #@cli : $ https://gmic.eu/samples/rotozoom.gmic go _fps=15 _label="Rotozoom" #@cli : $ https://gmic.eu/samples/french_flag.gmic go _fps=20 _label="French~Flag" #@cli : $ https://gmic.eu/samples/snow.gmic go _fps=20 _label="Snow" # Local Variables: # mode: sh # End: # # (End of G'MIC custom commands)