On 04/16/2011 04:36 AM, houghi wrote:
On Wed, Apr 06, 2011 at 06:15:52PM -0700, Patrick Horgan wrote:
        The command and output is:
        houghi@penne : gimp --verbose --batch-interpreter plug-in-script-fu-eval -i -b "(script-fu-fuzzy-border \"file.jpg\" '(0 0 0) 50 0 16 FALSE 1 FALSE)" -b '(gimp-quit 0)'

    This command:

    gimp -i -c -d -b '(batch_fuzzy_border "./alyssa01.jpg" "pink" 40 TRUE 10
    TRUE 50) ' -b '(gimp-quit 0)'

    just worked for me with the attached versions of batchfuzzy.scm and
    fuzzyborder.scm using GNU Image Manipulation Program version 2.7.2.  I
    made some changes here and there to fix errors in the scripts and to get
    rid of calling deprecated methods.  This may make it not work on earlier
    versions.  YMMV.  I'm sorry that no one bothered to look at the problem
    and led you on wild goose chases.  This group is usually more helpful
    than that.

I should add that if it doesn't work on your version, you can ask and I'll get
it working on whatever version you have.  You might have fun browsing the
procedures yourself in the script-fu console.  If you need help figuring that
out, let me know.  Where I put ./alyssa01.jpg you could also put "./*.jpg" if
you would rather batch process a lot of files at once with the same arguments.
I have the fuzzyborder working, but unfortunatly only by copy and pasting.
I have looked at the code and have absolutely no idea on how to get it
working for slide.scm or drop-shadow.scm

  I assume from your previous emails that you understand fuzzyborder and its arguments.   Unfortunately, it's written to work with an image in gimp, not a file on the harddisk.  So batchfuzzy.scm was written to bridge the gap.  What it does, is to accept all the arguments that you want to pass on to fuzzyborder and additionally, the list of files that you want to apply them to.  Then, one by one, it loads the files into gimp, applies fuzzy-border to them, and then saves the result back out under the original filename.  Below is the script that does this, and below it I'll go line by line and explain how it does what it does.  If you get lost in the line by line description below, feel free to refer back up to here so you can see the whole thing.

(define (batch_fuzzy_border  pattern color size blurt gran shadowt shadowp)
  (let* ((filelist (cadr (file-glob pattern 1))))
    (while (not (null? filelist))
      (let* ((filename (car filelist))
             (image (car (gimp-file-load RUN-NONINTERACTIVE filename filename)))
             (drawable (car (gimp-image-get-active-layer image)))
        (script-fu-fuzzy-border image drawable color size blurt gran shadowt shadowp FALSE TRUE)
        (set! drawable (car (gimp-image-get-active-layer image)))
        (gimp-file-save RUN-NONINTERACTIVE image drawable filename filename)
        (gimp-image-delete image)
      (set! filelist (cdr filelist))

We are making a procedure.  The syntax for making a procedure this way is:
(define (name formal_parameters) body)
You see below, that the name of our procedure is batch_fuzzy_border, and the formal_parameters are pattern, color, size, blurt, gran, shadowt, and shadowp then a right parenthesis closes the formal parameters, and everything up to the last ) is the body of the procedure.

(define (batch_fuzzy_border  pattern color size blurt gran shadowt shadowp)
Remember that we called gimp like this:
gimp -i -c -d -b '(batch_fuzzy_border "./alyssa01.jpg" "pink" 40 TRUE 10 TRUE 50) ' -b '(gimp-quit 0)'
Look at the part that calls batch_fuzzy_border:
(batch_fuzzy_border "./alyssa01.jpg" "pink" 40 TRUE 10 TRUE 50)
You can see that each of the arguments matches up with something from the formal parameter list.
pattern will be set to "./alyssa01.jpg"
color will be set to "pink"
size is set to 40
blurt is set to TRUE
gran is set to 10
shadowt is set to TRUE
shadowp is set to 50
You always have to make sure that the arguments you pass to a procedure match what the procedure is expecting.

Ok, now we're in the body of the procedure batch_fuzzy_border.  The first thing we're going to do is to make local variables that are only visible within a certain area.  The syntax for doing this is
(let* ((var1 exp1) 
       (var2 exp2) 
       (varn expn))
Each of the variables is set to the value of its _expression_, var1=expr1, etc, and then these are visible inside the body portion of the let.  In this case, there's only one variable, filelist, defined, and it's visible for the rest of the procedure--well, actually up to the next to the last right parenthesis.

The body that goes with this let* starts at below at the (while and continues to the end.
  (let* ((filelist (cadr (file-glob pattern 1))))

Before we go on, lets talk about the value assigned to filelist.  You might guess that it will be set to the value of a list, something like (file1.jpg file2.jpg file3.png).  cadr gets the second value of a list.  If you don't know about this, a great scheme tutorial is:


So we need to know about (file-glob pattern 1), because apparently it returns a list and the second element of that list is also a list, a list of the filenames.

The name file-glob comes from a unix concept of file globbing, or using wildcards to match 0 or more names.  In this case, file-glob was written (by Sven, yay!), to understand ? which matches 1 character and * which matches 0 or more characters.  file?.png would match file1.png, file2.png or fileQ.png.  *.png would match anything that ended with .png including .png.

So how do I know all this about file-glob?  Well, I ran gimp, selected, (through the menus), Filters/Script-Fu/Console to run the script-fu console, and then clicked on the Browse button down on the bottom right of the script-fu console.  That brings up the Script-Fu Procedure Browser.  (Boy I wish it had a maximize button on it!) Then I typed file-glob in the search box, and after dragging the window larger so I could read everything at once, I was able to read about file-glob. 

In particular, I found out that it returns the number of files and the list of files. 

That's why we need the cadr, because for this routine we don't use the number of files, we only need the list of filenames.  So, to make a long story short, filelist is a local variable that is a list of all the files that matched the pattern.

    (while (not (null? filelist))

When you see a while like that, it means that we're going to loop through something.  In this case, it's the filelist, and we're going to keep looping until it's empty.  That tells us that we expect that in the body of the while, something is going to pull the file names off of filelist one at a time.

      (let* ((filename (car filelist))
             (image (car (gimp-file-load RUN-NONINTERACTIVE filename filename)))
             (drawable (car (gimp-image-get-active-layer image)))
We learned about let* earlier, but the last time we only set one local variable.  This time we're setting three variables, filename, image, and drawable.

filename is set to the first thing in the filelist, (but doesn't change filelist, we get a copy of the first member, but filelist still has it too, you'll see where filelist is shortened later).

You can use the procedure browser yourself to learn about gimp-file-load, but in essence, it will open the file and return the image that fuzzy-border needs!!!  Yay!

Next we can see that we'll use car to get the first thing returned by gimp-image-get-active-layer from the image, and assign it to drawable.  drawable is just a made up name so we can store it and use it in the body of the let*.  It could just as well be be called theLayer.

So now there are 4 more lines inside the body of this let*, first we call script-fu-fuzzy-border.

        (script-fu-fuzzy-border image drawable color size blurt gran shadowt shadowp FALSE TRUE)

The argument image came from gimp-file-load, drawable is the active layer in the image, color, size, blurt, gran, shadowt, and shadowp were passed into our routine and are just passed through to script-fu-fuzzy-border, and the last two arguments to script-fu-fuzzy-border are set always to FALSE and TRUE.

I know you read about the arguments and use of script-fu-fuzzy-border so I'm not going to talk about them.

        (set! drawable (car (gimp-image-get-active-layer image)))

script-fu-fuzzy-border has changed the identifier for the active layer so we reset drawable because we're going to need it in the next line.

        (gimp-file-save RUN-NONINTERACTIVE image drawable filename filename)

gimp-file-save saves the changed file to the original name.

        (gimp-image-delete image)

Now we delete that image because we're done with it, we already saved it.


That's the end of the second let*.

We're still inside the while loop though.  There's one more line left in the while.

Remember I said that we expected something to make the filelist shorter?  The next line does that.

      (set! filelist (cdr filelist))

The set! resets filelist to be everything BUT the first thing, so if it was (file1.jpg file2.jpg file3.jpg) it is now set to (file2.jpg file3.jpg).

The next time through the while loop it would get set to (file3.jpg) and the final time it will be set to (), the empty list and that time when we loop back up (null? filelist) will be true.

That means (not (null? filelist)) will be false, and the while loop will exit.
We end up here after the while exits.
That was the end of the first let* that set filelist originally, there's nothing to stop us so...
That was the end of the define that created our procedure!  All done.  Any questions, please ask.

I understand that I need to write some sort of batch or something else to
use them, but all I do is stare at a lot of code and have no idea on how
to commence, regardless of the many websites I read.
http://www.gimp.org/docs/scheme_plugin/ is just one of them.

Or do I understand wrongly and there is a (secret) way to directly adress
the scm files from the command line?
It's not a secret.  The problem is that the thing you wanted to run needed images, and you only had filenames, so we needed this wrapper script that would turn each of the filenames into an open image that could be passed to the routine.

In english what happened is this:

while there are filenames
  This takes each filename and opens the file.
  It takes the resultant image and does fuzzyborder on it.
  It saves the changed image
  It deletes the image.
  take the first name off of the filename list
  loop back up to the while.

If you have other scripts you want to run that expect and image or layer, you will have to write a wrapper script similar to this one.  Any differences will come from the arguments that the script expects.  You'll have to accept all the arguments that the user will specify, along with a filename or fileglob, open the file, and then pass all the right arguments from the wrapper to the script you want to call.  If you get stuck, just tell me another script you want to wrap like this and I'll write a little tutorial.

If you have more questions, please ask.  I don't know what you don't understand if you don't ask:)


Gimp-user mailing list

Reply via email to