Why Python?

I got this mail from Marc (posted with permission):

Rolf writes in his blog:

> I need help with learning enough Python to write scripts for GIMP.
> Do you know a good site or other ressource? Mail me!

Why Python? I played with that language many years ago. I don’t remember anything special that would make me want to use it over the scheme based scripting language.

Example, here’s a script to increase contrast in an image, written shortly after I saw you do it in one of podcasts :-) It copies the current layer twice naming them “pop” and “pop-ovly” then sets the mode of pop-ovly to overlay (5) and the opacity to 50 percent. That’s it. I can then adjust the opacity for the amount of “pop” I want and then do a merge-down leaving me with a layer “pop” that has the adjustment.

following are two scripts, an analysis and the same script in Python. Boring for some….


(define (script-fu-increase-contrast img drw)
  (let ((pop-base (car (gimp-layer-copy drw TRUE)))
        (pop-ovly (car (gimp-layer-copy drw TRUE))))
  

    (gimp-image-undo-group-start img)
    (gimp-image-add-layer img pop-base -1)
    (gimp-drawable-set-name pop-base "pop")
    (gimp-image-add-layer img pop-ovly -1)
    (gimp-drawable-set-name pop-ovly "pop-ovly")
    (gimp-layer-set-mode pop-ovly 5)
    (gimp-layer-set-opacity pop-ovly 50)
    (gimp-image-undo-group-end img)
    (gimp-displays-flush)))

(script-fu-register
    "script-fu-increase-contrast"
    "contrast+"
    "Increase contrast to give an image some extra pop"
    "Marco S Hyman <marc@snafu.org>"
    "PUBLIC DOMAIN: No Rights Reserved"
    "2007-11-24"
    "RGB RGBA GRAY GRAYA"
    SF-IMAGE "Image" 0
    SF-DRAWABLE "Drawable" 0)

(script-fu-menu-register "script-fu-increase-contrast"
    "<Image>/Script-Fu")

It goes well with the script to reduce contrast that I wrote some time last year after seeing the technique used in some gimp tutorial (forget which one)


(define (script-fu-reduce-contrast img drw blur)
  (let ((contrast-base (car (gimp-layer-copy drw TRUE)))
        (contrast-ovly (car (gimp-layer-copy drw TRUE))))
   

    (gimp-image-undo-group-start img)
    (gimp-image-add-layer img contrast-base -1)
    (gimp-drawable-set-name contrast-base "contrast")
    (gimp-image-add-layer img contrast-ovly -1)
    (gimp-drawable-set-name contrast-ovly "contrast-ovly")
    (gimp-desaturate contrast-ovly)
    (gimp-invert contrast-ovly)
    (plug-in-gauss TRUE img contrast-ovly blur blur 0)
    (gimp-layer-set-mode contrast-ovly 5)
    (gimp-layer-set-opacity contrast-ovly 40)
    (gimp-image-undo-group-end img)
    (gimp-displays-flush)))

(script-fu-register
    "script-fu-reduce-contrast"
    "contrast-"
    "Reduce contrast)"
    "Marco S Hyman <marc@snafu.org>"
    "PUBLIC DOMAIN: No Rights Reserved"
    "2006-07-26"
    "RGB RGBA GRAY GRAYA"
    SF-IMAGE "Image" 0
    SF-DRAWABLE "Drawable" 0
    SF-ADJUSTMENT "Blur radius" '(20 0 100 1 10 1 0))

(script-fu-menu-register "script-fu-reduce-contrast"
    "<Image>/Script-Fu")

Scheme is easily learned. It is the language used in an entry level course for under classmen at MIT. The full text to the book used in that course is available on line: http://mitpress.mit.edu/sicp/

Learning gimp should be so easy :-)

// marc

Why Python and not Scheme? This is a good question. I have never looked deeper into Scheme and so I can’t really tell if I would like it.

The best way for me to learn a programming language is first to “read” some programs. Let me try the top one:


(define (script-fu-increase-contrast img drw)

Function definition. The ( at the beginning will hopefully be closed in the last line. Assumption: Blocks are put into parantheses. img and drw are the parameters to this function. img must be the image to work on, drw is still a bit mysterious.


   (let ((pop-base (car (gimp-layer-copy drw TRUE)))
        (pop-ovly (car (gimp-layer-copy drw TRUE))))

Create two new layers by copying from drw. I started GIMP and looked into the Procedure Browser in the Xtns menue of the main window. gimp-layer-copy takes two arguments, the layer to copy and if to add an alpha channel. So drw is the active layer? car must be something in Scheme to give a variable a value or so. Too lazy to count the (())()()) balance. ;-)

    
    (gimp-image-undo-group-start img)

Get all the stuff we’ll do into one step for undo

.


    (gimp-image-add-layer img pop-base -1)
    (gimp-drawable-set-name pop-base "pop")

Add the layer to the image and put on top of the stack (-1). Again looked up in the Procedure Browser.


    (gimp-image-add-layer img pop-ovly -1)
    (gimp-drawable-set-name pop-ovly "pop-ovly")
    (gimp-layer-set-mode pop-ovly 5)
    (gimp-layer-set-opacity pop-ovly 50)

First two lines as above, then set the layer mode to “Overlay” (5) and set the opacity to 50%.


    (gimp-image-undo-group-end img)
    (gimp-displays-flush)))

Close the undo-batch and update the display. Here is the closing ) from the start. But it is one more than expected, somewhere above one has been still open. An editor with parentheses highlighting is mandatory…..


(script-fu-register
    "script-fu-increase-contrast"
    "contrast+"
    "Increase contrast to give an image some extra pop"
    "Marco S Hyman <marc@snafu.org>"
    "PUBLIC DOMAIN: No Rights Reserved"
    "2007-11-24"
    "RGB RGBA GRAY GRAYA"
    SF-IMAGE "Image" 0
    SF-DRAWABLE "Drawable" 0)

Register the new plugin with GIMP….


(script-fu-menu-register "script-fu-increase-contrast"
    "<Image>/Script-Fu")

… and give it a home in the menu.

Well, not that complicated.

Now I’ll give Python a chance. I read the doc page at gimp.org and tried to modify the program there into a copy of this one. First impression – Python is not as strange for me as Scheme, my old Pascal, Delphi and Tcl/Tk skills help.

With some trial and error I ended with this here:


#!/usr/bin/env 
pythonimport math
from gimpfu import *

def python_increase_contrast(img, drw):
    img.disable_undo()

    layer_base = drw.copy(True)
    layer_base.mode = NORMAL_MODE
    layer_base.name = "Pop Base"
    img.add_layer(layer_base, -1)

    layer_over = drw.copy(True)
    layer_over.mode = OVERLAY_MODE
    layer_over.name = "Pop Overlay"
    layer_over.opacity = 50.0
    img.add_layer(layer_over, -1)

    img.enable_undo()
    gimp.displays_flush

register(
        "python_fu_increase_contrast",
        "Give more pop to the image",
        "Just adjust the opacity",
        "Rolf Steinort - Marco S Hyman <marc@snafu.org>",
        "public domain",
        "2007",
        "<Image>/Filters/Enhance/_Increase Contrast",
        "RGB*, GRAY*",
        [],
        [],
        python_increase_contrast)

main()

I had started GIMP in from a terminal window and could see the error messages. They were quite enlightening.


 /home/rs/.gimp-2.4/plug-ins/pop-contrast.py:15: DeprecationWarning: gimpenums.TRUE is deprecated, use True instead
  layer_over = layer_base.copy(TRUE)

and


   File "/home/rs/.gimp-2.4/plug-ins/pop-contrast.py", line 18, in python_increase_contrast

    layer_over.opacity = 50

TypeError: type mismatch

gave me quite good pointers to my errors (True and 50.0 (float) instead of 50 (integer)).

The use of indentation as a means to structure the blocks is quite intuitve for me, it’s like properly written Pascal without BEGIN and END. ;-)

Now for reading this program:


#!/usr/bin/env 
pythonimport math
from gimpfu import *

Some preliminaries, getting the math and the gimpfu library.


def python_increase_contrast(img, drw):

Defining the function with its two parameters, like above.


    img.disable_undo()

Disableling the undo is a method of the image object.


    layer_base = drw.copy(True)
    layer_base.mode = NORMAL_MODE
    layer_base.name = "Pop Base"
    img.add_layer(layer_base, -1)

Create the new object layer_base and set some properties. Here I like NORMAL_MODE instead of the number above. But these constants should be also available in Scheme. Just sloppy work by Marc ;-)

The image object swallows the new layer.


    layer_over = drw.copy(True)
    layer_over.mode = OVERLAY_MODE
    layer_over.name = "Pop Overlay"
    layer_over.opacity = 50.0
    img.add_layer(layer_over, -1)

As above. And Python has typed variables, 50 is not 50.0!


    img.enable_undo()
    gimp.displays_flush

Just as with Scheme, bundeling up the undo stack and update the display.


register(
        "python_fu_increase_contrast",
        "Give more pop to the image",
        "Just adjust the opacity",
        "Rolf Steinort - Marco S Hyman <marc@snafu.org>",
        "public domain",
        "2007",
        "<Image>/Filters/Enhance/_Increase Contrast",
        "RGB*, GRAY*",
        [],
        [],
        python_increase_contrast)

Registering the new filter and giving it a home in the menu.


main()

Why is this here? Well, I’ll find out with time.

After this 3 hours spent with both languages I am sure that I’ll go the Python way.

If you want to install this plugin under Linux, just copy the source code into a text file and save it under ~/.gimp-2.4/plug-ins/mtg-pop.py . Change the permissions to “executable”

11 thoughts on “Why Python?

  1. OK, so it is shorter in python. Most important, IMHO, is to use what is comfortable. For you that is python, for me scheme. A few comments on your scheme analysis.. your comment

    > Here is the closing ) from the start. But it is one more than expected, somewhere
    > above one has been still open. An editor with parentheses highlighting is mandatory…..

    is right in that parentheses hilighting in an editor is helpful. But your count got off in that the end of the program not only encloses the ( from the start, but also the ( from the let statement.

    (let ((var1 exp1) (var2 exp2)) body) sets up en environment where the expressions are bound to the local variables. This binding of name to expression holds throughout the body of the let.

    Lastly, values are returned in script-fu in the form of a scheme list. (car list) returns the head of the list so

    (let ((pop-base (car (gimp-layer-copy drw TRUE))) …

    says start an environment in which the variable pop-base is assigned the value of the head of the list returned from calling function gimp-layer-copy. Phew!

    // marc

  2. Length depends on what you count, it’s 35 lines, 70 words and 872 characters for Python, 27 lines, 75 words and 944 characters for the Scheme version. (Thanks for wc! :-) ) But I think length should be no argument here.

    You are on the spot with being “comfortable”. I think I’ll be that more with Python than with Scheme because I have some background in OO.

    Thanks for the further input on Scheme – I see that it is a very logical and strict language- And easy to program in if you have internalized the concept but difficult to understand if you haven’t. A bit like FORTH – I once learned that too.

    I am not sure how far I can or want to go into writing plugins. And how to make a video out of it. ;-) If I do it I’ll keep Scheme on the screen – and threaten now to ask you for further advice.

  3. Another interesting argument in Python’s favor (of course the article may be biased ;) ) from this site (http://www.gimp.org/docs/python/index.html):
    GIMP-Python is a scripting extension for GIMP, similar to Script-Fu. The main difference is in what is called first. In Script-Fu, the script-fu plug-in executes the script, while in GIMP-Python the script is in control.

    In fact, you will find that the GIMP-Python scripts start with the line !/usr/bin/python . The GIMP extension is loaded with the familiar import command.

    Another point of difference between GIMP-Python and Script-Fu is that GIMP-Python stores images, layers, channels and other types as objects rather than just storing their ID. This allows better type checking that is missing from Script-Fu, and allows those types to act as objects, complete with attributes and methods.

    Also, GIMP-Python is not limited to just calling procedures from the PDB. It also implements the rest of libgimp , including tiles and pixel regions, and access to other lower level functions.

  4. I’ve developped a simple Orton Effect python plugin,… i have to say that i learned gimp-python in 1 night :D It’s so easy!

    In spanish, there’s a good ressource for learning python-fu in javie’s linux blogsite (python-fu for non-programmers), aswell as some nice scripts to download.

    Btw, this comic stripe shows why Python is so confortable and easy to learn XDD

    Sorry for putting so many links in this comment, i think it is interesting to share this things, and i’m not a spam bot :D

  5. Carlos, thanks for all the links. I am sure that you are not a spam bot because there are no tips about enlarging body parts among your links. ;-)

    This Orton Effect looks good – I found some very interesting links,

    I’ll steal your Orton Plugin and try to rewrite it. Some things are different to the program on the Gimp site – I have to find out why.

    I love XKCD – here is the comic Carlos mentioned.

Anything to add from your side of the computer?