------------------------------------------------------------------------------- IMv7 Command Line Interface re-design... I have added a CLI interface of ImageMagick as part of version 7 development. Yes I am on the very small development team for this. This will let the software become more script-like, and you will be able to use a script rather than VERY VERY LONG COMMAND LINES. That is the current norm. See ImageMagick Examples... http://www.imagemagick.org/Usage/ Specifically Basic Usage, Working Example http://www.imagemagick.org/Usage/basics/#example And that is a 'small' command line. You will still be able to use commands as you always have, but you will also be able to script those commands, using files or pipelines. As part of this I am also going to ensure that you can run ImageMagick as a co-processor. That is run Imagemagick in background with image processing options being sent to it from a wrapper program (shell or any other language). The background ImageMagick command, will read/process/write the images, holding the images in memory for as long as needed, report information about them, all according to the instructions it recieves. The wrapping program (shell, PHP, or other) can ask for images to be read in, request information about them, sent processing options based on that information (feedback loop). The wrapper thus provides the if-then, looping, and function handling aspects, while letting ImageMagick do what it does best, handle images. The real point is to remove having to run multiple image processing commands, where it spends all its time simply reading and writing intermediate images, that are being passed between multiple commands (via pipelines or disk files). Instead the images are just kept in memory for as long as it is needed. Making shell script image processing very much faster. UPDATE: Cristy added the ability to use a remote 'image caching service'. It does not need to be shell, but could be PHP (using its proc_open() function), or any other language, but it will be designed with shell scripting in mind! --- "magick" -- The replacement "convert" command. Its usage will be either like IMv6 "convert" magick [options|images]... [output] Or as a "scripting" command magick [options] -script scriptname [script_arguments] or for basic information (and abort) options... magick -version magick -list listtype Note using -version or -list within a "script" will work as expected, but will not abort the image processing as the above does, which allows you to use these options in a co-processing (parent process controlled) technique, as part of the making decisions as to how to process images. Also "magick" will have a much more strict option processing order. As such if you don't have an image in memory when you try to process the image you will get an error. The big change is the "-script" option that will switch to reading options from given file, file descriptor, or pipeline (stdin). It will also provide a simple 'CLI' interface, where you can type options in directly. --- Scripting Details... Script Launching (command line only) -script {script_file} # argument for script files This means you can create "magick scripts" of the form... #!/path/to/magick -script # # This is a Magick Script # -read granite: ( rose: -rotate 180 ) -gravity center -composite -write show: If the "magick" command is named "magick-script" as a single word, then the "-script" option will be assumed. This will allow you to run a "magick script" using rather well known "env" program trick... #!/usr/bin/env magick-script # -pointsize 48 label:'Magick with "env"' -write show: In this form of she-bang ("#!"), only one argument can follow the "env" command, which is why more compact form of the command ("magick-script") is needed. Actually this will work if "magick" is named (copied, symlinked, or hard linked) any command that ends with the string "script". The script file is parsed in a way that is very close to that of a bash script. In fact you should be able to copy the CLI arguments into a script file, and prepend "-write" to the last argument, so as get exactly the same result. EG: This IMv6 command... convert rose: -scale 200% big_rose.png can be converted directly to this IMv7 "magick" command... magick rose: -scale 200% big_rose.png Or to a magick script... (no shell pasing of arguments IM does that parsing) #!/path/to/magick -script rose: -scale 200% -write big_rose.png You can even 'pipe' the otpions into the magick command... echo "rose: -scale 200% -write big_rose.png" | magick -script - Each argument (token) in the script consists of a white-space separated words. And you can escape characters using backslashes '\', single quotes ''' or double quotes '"' in exactly the same way as bash does. Any '#' that is the start of a word (token) starts a comment to the end of the line and is ignored. This means if something 'works' for a bash "echo", then it should work the same way in a magick script. For example prompt> echo abc#123 abc#123 Which shows the shell did not see '#' as the start of a comment, so neither will the "magick" command when it interprets a "magick script". For example each of the following produces a single character 'word' exactly as you would if you had used them in a shell "echo" command. \# '#' "#" - \' "'" - \" '"' "\"" - \\ '\' "\\" These are all the special meta-characters handled by "magick". The other bash meta-characters (namely: $ ` ( ) { } [ ] ; ! > < * ? ) are not handled in any special way by "magick". They are just ordinary characters and as such they do not need escaping. However escaping them does not hurt! --- Parsing Details (extract from "MagickWand/script-token.c" documentation) The input stream is dived into individual 'tokens' (representing 'words' or 'options'), in a way that is as close to a UNIX shell, as is feasable. Only shell variable, and command substitutions will not be performed. Tokens can be any length. The main function call is GetScriptToken() (see below) which returns one and only one token at a time. The other functions provide support to this function, opening scripts, and seting up the required structures. More specifically... Tokens are white space separated, and may be quoted, or even partially quoted by either single or double quotes, or the use of backslashes, or any mix of the three. For example: This\ is' a 'single" token" A token is returned immediatally the end of token is found. That is as soon as a unquoted white-space or EOF condition has been found. That is to say the file stream is parsed purely character-by-character, regardless any buffering constraints set by the system. It is not parsed line-by-line. The function will return 'MagickTrue' if a valid token was found, while the token status will be set accordingally to 'OK' or 'EOF', according to the cause of the end of token. The token may be an empty string if the input was a quoted empty string. Other error conditions return a value of MagickFalse, indicating any token found but was incomplete due to some error condition. Single quotes will preserve all characters including backslashes. Double quotes will also preserve backslashes unless escaping a double quote, or another backslashes. Other shell meta-characters are not treated as special by this tokenizer. For example Quoting the quote chars: \' "'" \" '"' "\"" \\ '\' "\\" Outside quotes, backslash characters will make spaces, tabs and quotes part of a token returned. However a backslash at the end of a line (and outside quotes) will cause the newline to be completely ignored (as per the shell line continuation). Comments start with a '#' character at the start of a new token, will be completely ignored upto the end of line, regardless of any backslash at the end of the line. You can escape a comment '#', using quotes or backlsashes just as you can in a shell. The parser will accept both newlines, returns, or return-newlines to mark the EOL. Though this is technically breaking (or perhaps adding to) the 'BASH' syntax that is being followed. UNIX script Launcher... The use of '#' comments allow normal UNIX 'scripting' to be used to call on the "magick" command to parse the tokens from a file #!/path/to/command/magick -script UNIX 'env' command launcher... If "magick" is renamed "magick-script" you can use a 'env' UNIX launcher #!/usr/bin/env magick-script Shell script launcher... As a special case a ':' at the start of a line is also treated as a comment This allows a magick script to ignore a line that can be parsed by the shell and not by the magick script (tokenizer). This allows for an alternative script 'launcher' to be used for magick scripts. #!/bin/sh :; exec magick -script "$0" "$@"; exit 10 # # The rest of the file is magick script -read label:"This is a Magick Script!" -write show: -exit r with some shell pre/post processing... #!/bin/sh :; echo "This part is run in the shell, but ignored by Magick" :; magick -script "$0" "$@" :; echo "This is run after the "magick" script is finished!" -------------- Script option Handling We have alreay seen the Script Launching option... -script {script_file} # argument for script files But you can use -script as a sort of 'complex sub-routine' call For example... magick input_image.png \ -script make_fancy.mgk \ -script shadow.mgk \ output_image.png Other IMv7 specific scripting options... These are generally only useful within a "magick script" -exit # Exit the file or pipeline being processed (quit). # It is equivelent to reaching the end of the script. # After this option nothing more is read from the script # file or the command line. -read "image_file" # equivelent to just "image_file" on it own. WARNING: ---- the rest have yet to be implemented ---- -return # Return execution back to the original option process. # If the return is to CLI, it will be at a point after # the "-script" option and last -args script processed. # Equivelent to -exit if the last CLI argument was used. # The last argument will again be regarded as the final # output filename. -reset # Delete all images and reset settings. # Basically prepare for completely new image process, # except for perhaps user-defined global defines, # and a reset of the image cache. -debug ??? # trace each command as it is processed by the "magick" # script returning (some) signal when it is done. # This may be a 'trace' or other logging setting instead. -define-setting {key} # expand percent escapes found in this global # define (artifact) and save as a per-image # setting (property) to each image in the current # image sequence. -fatal-level {level} # level is 'fatal' 'error' 'warning' (def: 'error') # replaces -regard-warning IMv6 option # so as to provide more control over when "magick" # aborts its processing of a command, and allow # it to vary during processing. Argument Handling (script or pipeline only) Note that -args can NOT be used on the command line, as any other place really does not make much sense! -args ReadNextImage # read the next argument as a image filename -args ReadAllImages # read all arguments as images (except last) -args ReadLastImage # read the last argument as a input image -args WriteLastImage # write current images using last argument -args DefineGlobal {key} # Read next argument string as a global define # for {key}. that is {key}={value_from_arg} # Basically freeform 'argument input' -args Process # process all later args, much like "montage" does. # Up to either a '--' or end or arguments. # Only last argument is ignored (for later write) Notes: * -args will only work if -script is called from CLI. They can not be used within a script that was called by another script (if made posible) * "-args Process" will permit normal "convert"-like argument processing of the remaining arguments, up to a special '--' symbol. After those arguments are processed the script itself will continue where it left off. It will not cause an error (just ignored) if used from -script called from a script, in which case it is assumed images are already pre-processed. NOTE: No 'flow-control' operators are provided at this time. If you want more control (such as if-then or looping) create a wrapper script around a "ImageMagick" co-processor, and feed it the operations you want it to perform (see below). ---- Example a 'montage'-like ImageMagick script... Setup, process user image processing options, finish up and write results filename: "my_montage_script" =======8<-------- #!/usr/env magick -script # # set defaults before processing user supplied images. -density 300 # process all user supplied command line options (like-convert) now # This also read in any images the user may have supplied. -args Process # finish processing, adding frames and appending the resulting images -frame 6x6+2+2 -border 2x2 -append # output the final post-processed results. -args WriteLast -exit Nothing else in the script file will be read by the "imagemagick" command as such this could be a documentation area. =======8<-------- Example usage for a "my_montage_script"... my_montage_script image1 image2 -append image3 output This would produce an actual 'scripting' command of... /usr/bin/magick -script /path/to/montage_script \ input1 input2 -append image3 output And process the operations in this order (indent indicate processing depth)... first indent is read from the script, the second processing of arguments. -script /path/to/montage_script -density 300 -args Process -read "image_1" -read "image_2" -append -read "image_3" -frame 6x6+2+2 -border 2x2 -append -args WriteLast -write "output" -exit Notes on the above... I designed the argument handling so you can write scripts that can do things in a "montage"-like way. That is process arguments like "convert" and THEN finalise the processing of the images now in memory to produce the "montage" output that is saved to the final arguement. The script however may only only accept image filenames as arguments making any 'operations' an error. Or perhaps only '-set' or '-define' like options could be allowed (like "awk" command line variables). Even the last argument filename may be treated as a read image, final save image, or even as a single image read and write (like a simple "mogrify"). Basically I am trying to make scripting as versatile as possible but without making it a full scripting language. ----------- Use of '--' in scripts... This special option will allow user supplied options to be grouped into 'stages' for processing at specific points in the script. for example.. montage '*.png' -- -frame 10x10+3+3 result.png Anything before the '--' is processed to read-in the images, then at the '--' the "montage" script will take those images and tile, lable, frame, shadow, and finally position and merged the images together into a paged grid layout. After that the next set of user supplied options (if any) will be performed, so the montaged image is 'framed' before being saved into 'result.png' QUESTION: use of -script in the user supplied arguments? FUTURE DIRECTION... Handling 'per image' options ... that is one image in memory being processed as in "mogrify" ??? mogrify {setup_options} -- {per-image processing} -- {images to process} Something to read one image from final group? per-image processing can be a user supplied script? ----------- Demonic pipeline example (heavy shell scripting)... The IM command is run in background and the options feed to the script as needed and calculated, based on feed back. This is probably a little simplistic, but should be posible. =======8<-------- #!/bin/bash # start co-process for image processing coproc magick -script - IM_pid=$COPROC_PID # the Process ID of the imagemagick co-process IM_out=${COPROC[0]} # file descriptor IM writes to (and this script reads) IM_in=${COPROC[1]} # file descriptor IM reads (and this script write to) # create an image and 'identify' it. echo >$IM_in "xc: -identify" # read the single line of identify results from above read -r <$IM_out identify_output # kill co-process (no write so junk images in memory) echo >$IM_in "-exit" # wait for co-process to exit (not really needed). wait $IM_pid =======8<-------- Basically you have a "magick" command running in background. This 'co-process' reads in the images, and performs any tasks requested of it by the parent shell script (or any other programming language). The parent shell can ask it for information about images being held, have it read in more images, or save out images to whereever you want, at any time. The tasks are not fixed and can be decided by the parent shell based in that shells input options, or information returned from the "imagemagick" co-process, or even from other sources. This is highly efficent as you only need to run "imagemagick" once and images only need to be read in once. No need for pipelines or saving of intermediate images to disk. The parent shell process can do all conditional IF-THEN-ELSE constructs and even do looped processing of operations, simply and easilly. The calling structure can be simplified by using functional wrappers around the image processing requests. In PHP you would use the proc_open() function. =======8<-------- array("pipe", "r"), // stdin (feed image processing options) 1 => array("pipe", "w"), // stdout (for results) 2 => array("file", "/tmp/im-errors.txt", "a") // stderr ); $IM = proc_open($IM_cmd, $descriptorspec, $pipes, NULL, $_ENV); if (!(is_resource($IM)) { echo "ERROR: Failed to launch \"imagemagick\" command"; die(); } stream_set_block($pipes[2], 0); // set non-blocking // Any error reports during launch (say from the shell?) if ( $err = stream_get_contents($pipes[2])) { echo $err; die(); } // Make image processing requests fwrite($pipes[0], "-read image.gif\n"); // read in this image fwrite($pipes[0], "-identify\n"); // identify it fflush($pipes[0]); // ensure buffer is sent $identify = fgets($pipes[1], 1024); // get the -identify result print "$identify"; // print it to stdout // Clean up and exit fwrite($pipes[0], "-exit\n"); // ask co-processor to exit fclose($pipes[0]); // Get any error reports during launch (say from the shell?) if ( $err = stream_get_contents($pipes[2])) { echo $err; die(); } ?> =======8<-------- In this form of control the wrapping script (or compiled language) becomes a true 'wrapper' around that single image processor, simplifying things to a point where you simply feed it image processing commands, at any time in the shell script. The shell or PHP wrapper in this case provides all the control and looping structures needed, allowing IM to concentrate on holding images in memory and processing them! The Imagemagick co-process itself does not even need to be on the same machine, allowing you to generating image processing farms! This is actually taking a old forum discussion topic to a new level See http://www.imagemagick.org/Usage/forum_link.cgi?f=2&t=13100 ------------------------------------------------------------------------------- Scripting will need a major expansion in percent escape handling... See Proposal... http://www.imagemagick.org/Usage/bugs/IMv7_FX_and_Percent.html THIS HAS BEEN DONE (except in %[fx..] and other planned special escapes) ------------------------------------------------------------------------------- Scripting methods for multiple images... Adding text to video frames? forum_link.cgi?f=1&t=18983&p=73784 Of course looping through images, using one command per image is probably the easiest to implement. One method of looping over a large number of images, as a 'image stream', without saving intermeditae images, was demonstrated in another similar discussion. However the images are streams using ASCII PPM file format. forum_link.cgi?f=2&t=18320&p=71150 and resulting in the script http://www.imagemagick.org/Usage/scripts/process_ppm_pipeline But these methods require you to constantly start (fork) a separate process or command for each image. That is you would be starting a new process and initializing (setup) the core library once for each image, which can be in-efficent. That is you can not relate the image processing with what has been already been done before. At this time you can only truely avoid forking a "convert" command multiple times by using some other non-shell API, so that the library only is configured once. Then have it read, process, write and destory (remove from memory) each frame in turn. In shell scripts you can minimise startup time by processing images using mogrify which reads and processes one of the given image file at a time. Mogrify works by cycling though the non-image command line options once for each image. http://www.imagemagick.org/Usage/basics/#mogrify That however is limited, it is hard to modify the arguments being used for each image (say for the credit string and its position). You also can not easily compose or merge two or more images onto each frame. You can also read a small batch of images, say 10 to 50 frames (depending on memory) using convert, and a technique for using the input filename to set the output filename of each image. See "Using Convert Instead of Morgify" http://www.imagemagick.org/Usage/basics/#mogrify_convert Which may be useful for say a static credit on N frames, then a different credit for the next N frames and so on. You can also generate a very long convert command that reads, processes, and writes (deleting from memory) one image at a time. This lets you use one command, but apply different operations for each image (say drawing scrolling credits, or adding a side animation that is common on modern movies). For example convert \ ... \ "f_0053.png" -annotate +10+30 "Scroll" -write "new_0053.png" +delete \ "f_0054.png" -annotate +10+27 "Scroll" -write "new_0054.png" +delete \ "f_0055.png" -annotate +10+24 "Scroll" -write "new_0055.png" +delete \ ... \ null:[/code] It is a most versatile and efficient technique, as you don't have to fork, setup, or re-read secondary images over and over. At any one time, only one image is being processed in memory, but only one one command processing all the images. The technqiue also allows you to be free to completely switch processing to completely different tasks. For example you can have a extra image in memory with each frame (in turn). Such as one very long static credit image that you can then overlay on your smaller video frame, at different offsets so as to generate a long sequence of scrolling text over the top of your video. The limitation with this is at the length of the command line! Not all shells will having a command line longer than one to two thousand characters (bash seems to handle extremely long command lines though). The above scripting proposals will take this to a new level. That is... Image co-processing, or image pipeline processing, with feedback. -------------------------------------------------------------------------------