Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Transform an image into different aspect ratios
#1
Hello,

I just started today with this quest:

1.) Read the filename and save it.
2.) Resize the image to a specific aspect ratio.
3.) Add a prefix to the filename and save it as an jpeg in a specific folder.
4.) Undo all steps and repeat steps 2. und 3. different values.

I came this far:

Code:
from gimpfu import *

# The real code.
def setAspectRatios():
   # create the object of the image
   image=gimp.image_list()[0]
   # resizes the file to AR 2-3
   pdb.gimp_image_resize(image, 5333, 8000, -345, 0)
   # create the new name AR 2-3
   img_name_ar_2_3 = "AR_2-3_" + image.name
   # define drawable
   drw = image.layers[0]
   # save the image as jpeg
   pdb.file_jpeg_save(image, drw, img_name_ar_2_3, img_name_ar_2_3, 0.90, 0, 1, 1, "Aspect Ratio 2-3", 3, 1, 0, 2)

   
register(
   'set-aspect-ratios',                # Unique ID,
   'create different aspect ratios',   # Description/title
   'Add a white layer',                # Help
   "Author",                           # KJ
   "Author",                           # Copyright KJ
   "2024",                             # Copyright 2024
   'Set Aspect Ratios',                # The menu label
   "RGB*",                             # The type of images it can work on. Use "*" for all types
   # [                                 # List of input parameters
   #    (PF_IMAGE, "image", "Input image", None)  
   #                                                
   #],
   [],                                 # List of output parameters
   setAspectRatios,                      # The Python code that implementsthe plugin
   menu="<Image>/Layer",               # Where the menu label above appears
)

main()

But I have some questions to fill the blanks:

Q1: I know now that image.name does not work properly. Is there another way besides using pdb.gimp_image_get_filename(image) and trim the complete path down to the filename to just get the name of the file I imported?
Q2: What type is "drawable" - I could not find a good explanation. Do I use this correctly?
Q3: Undo is still a miracle for me. I saw that you can create groups and that you can enable, freeze, and disable it but I dont know how to trigger a simple undo to reverse steps 2. and 3. to the back to the original image.

Thanks in advance! Smile
Reply
#2

  1. Try image.filename. Then (with an import os), os.path.basename(image.filename) and may be even os.path.splitext(os.path.basename(image.filename))[0] if you want to drop the extension
  2. A "drawable" is anything you can paint on: layer, layer mask or channel, usually a layer. If you save the image as JPEG, no point in adding a white layer, Gimp will do it for you (if the background color is set to white of course). And if you add a layer, you have to merge the layers or create a new-from-visible to pass to the save call.
  3. You can't undo in a a script. In a script you know what you are doing and you don't need to undo (undoing is meant for the puny humans). The APIs with *undo* are there to 1) allow you to be nice to the user (undo_group_start/end) or 2) improve performance (undo_freeze/thaw and undo_disable/enable). 
For this kind of process you do something like this:
  • create a new image with the target dimensions (this "behind the scenes" image has no "view", you won't see it on the screen).
  • disable undo in that image (makes the rest run faster)
  • create a new-from-visible in the source image using the new image as the destination
  • insert that layer in the new image
  • resize the layer and apply offsets
  • save the layer (note that for this you have to work from the filename of the source image of course)
  • delete the new image (gimp.delete(new_image))
  • repeat for another set of dimensions
      
A very overlooked dialog is the Images dialog because it shows all images, even those without an attached view, so allows you to still see the images create by your script, and lets you
create a view on them if you want to check out things:

   

And of course when your script ends you shouldn't see any lingering image there.
Reply
#3
Thanks for the fast and detailed reply!

I implemented the points you mentioned. 

Code:
#!/usr/bin/env python2
# Preferred form of import, avoids "gimpfu" prefixes all over.
from gimpfu import *

# The real code.
def setAspectRatios():
  import os
  # create the object of the image
  image=gimp.image_list()[0]
  filename = os.path.basename(image.filename)
  # create a new image with the same dimensions
  new_image = pdb.gimp_image_new(6000, 8000, 0)
  # disable undo to make it faster
  pdb.gimp_image_undo_disable(new_image)
  # define layer to copy
  current_layer = image.layers[0]
  # create the new name AR 2-3
  new_layer = pdb.gimp_layer_new_from_visible(image, new_image, current_layer)
  # add new layer to the new image
  pdb.gimp_image_add_layer(new_image, new_layer, 0)
  # resize the layer
  pdb.gimp_layer_resize(new_layer, 5333, 8000, -345, 0)
  img_name_ar_2_3 = "AR_2-3_" + filename
  # define drawable
  drw = new_image.layers[0]
  # save the image as jpeg
  pdb.file_jpeg_save(new_image, drw, img_name_ar_2_3, img_name_ar_2_3, 0.90, 0, 1, 1, "Aspect Ratio 2-3", 3, 1, 0, 2)
  # delete the new image
  gimp.delete(new_image)

  #Aspect Ratio 4-5
  new_image = pdb.gimp_image_new(6000, 8000, 0)
  pdb.gimp_image_undo_disable(new_image)
  current_layer = image.layers[0]
  new_layer = pdb.gimp_layer_new_from_visible(image, new_image, current_layer)
  pdb.gimp_image_add_layer(new_image, new_layer, 0)
  pdb.gimp_layer_resize(new_layer, 5333, 8000, -345, 0)
  img_name_ar_4_5 = "AR_4-5_" + filename
  drw = new_image.layers[0]
  pdb.file_jpeg_save(new_image, drw, img_name_ar_4_5, img_name_ar_4_5, 0.90, 0, 1, 1, "Aspect Ratio 4-5", 3, 1, 0, 2)
  gimp.delete(new_image)

 
register(
  'set-aspect-ratios',                # Unique ID, I prefix mine with "ofn-" to avoid clashes
  'create different aspect ratios',   # Description/title (short)
  'Set Aspect Ratios',                # Help (can be longer...)
  "Author",                           # KJ
  "Author",                           # Copyright KJ
  "2024",                             # Copyright 2024
  "Set Aspect Ratios",                # The menu label
  "RGB*",                             # The type of images it can work on. Use "*" for all types
  # [                                 # List of input parameters
  #    (PF_IMAGE, "image", "Input image", None)  
  #                                                
  #],
  [],                                 # List of output parameters
  setAspectRatios,                    # The Python code that implements the plugin
  menu="<Image>/Filters",             # Where the menu label above appears
)

main()

Sadly it wont register and show up in gimp - do you can see what I miss? I use gimp 2.10.32

Thanks in advance! Smile
Reply
#4
(01-22-2024, 07:32 PM)SpaceMonkey Wrote: Thanks for the fast and detailed reply!

I implemented the points you mentioned. 

Code:
#!/usr/bin/env python2
# Preferred form of import, avoids "gimpfu" prefixes all over.
from gimpfu import *

# The real code.
def setAspectRatios():
  import os
  # create the object of the image
  image=gimp.image_list()[0]
  filename = os.path.basename(image.filename)
  # create a new image with the same dimensions
  new_image = pdb.gimp_image_new(6000, 8000, 0)
  # disable undo to make it faster
  pdb.gimp_image_undo_disable(new_image)
  # define layer to copy
  current_layer = image.layers[0]
  # create the new name AR 2-3
  new_layer = pdb.gimp_layer_new_from_visible(image, new_image, current_layer)
  # add new layer to the new image
  pdb.gimp_image_add_layer(new_image, new_layer, 0)
  # resize the layer
  pdb.gimp_layer_resize(new_layer, 5333, 8000, -345, 0)
  img_name_ar_2_3 = "AR_2-3_" + filename
  # define drawable
  drw = new_image.layers[0]
  # save the image as jpeg
  pdb.file_jpeg_save(new_image, drw, img_name_ar_2_3, img_name_ar_2_3, 0.90, 0, 1, 1, "Aspect Ratio 2-3", 3, 1, 0, 2)
  # delete the new image
  gimp.delete(new_image)

  #Aspect Ratio 4-5
  new_image = pdb.gimp_image_new(6000, 8000, 0)
  pdb.gimp_image_undo_disable(new_image)
  current_layer = image.layers[0]
  new_layer = pdb.gimp_layer_new_from_visible(image, new_image, current_layer)
  pdb.gimp_image_add_layer(new_image, new_layer, 0)
  pdb.gimp_layer_resize(new_layer, 5333, 8000, -345, 0)
  img_name_ar_4_5 = "AR_4-5_" + filename
  drw = new_image.layers[0]
  pdb.file_jpeg_save(new_image, drw, img_name_ar_4_5, img_name_ar_4_5, 0.90, 0, 1, 1, "Aspect Ratio 4-5", 3, 1, 0, 2)
  gimp.delete(new_image)

 
register(
  'set-aspect-ratios',                # Unique ID, I prefix mine with "ofn-" to avoid clashes
  'create different aspect ratios',   # Description/title (short)
  'Set Aspect Ratios',                # Help (can be longer...)
  "Author",                           # KJ
  "Author",                           # Copyright KJ
  "2024",                             # Copyright 2024
  "Set Aspect Ratios",                # The menu label
  "RGB*",                             # The type of images it can work on. Use "*" for all types
  # [                                 # List of input parameters
  #    (PF_IMAGE, "image", "Input image", None)  
  #                                                
  #],
  [],                                 # List of output parameters
  setAspectRatios,                    # The Python code that implements the plugin
  menu="<Image>/Filters",             # Where the menu label above appears
)

main()

Sadly it wont register and show up in gimp - do you can see what I miss? I use gimp 2.10.32

Thanks in advance! Smile

because you removed the thing that describes the input arguments by commenting in out, instead of making it an empty list. But it's better to keep it in an have an image argument to your plugin, so you can work even if several images are open in Gimp:

Code:
#!/usr/bin/env python2
# Preferred form of import, avoids "gimpfu" prefixes all over.
from gimpfu import *

# The real code.
def setAspectRatios(image):
  import os
  # create the object of the image
  #image=gimp.image_list()[0]     #### commented out, use the image in the argument instead
  filename = os.path.basename(image.filename)
  # create a new image with the same dimensions
  new_image = pdb.gimp_image_new(6000, 8000, 0)
  # disable undo to make it faster
  pdb.gimp_image_undo_disable(new_image)
  # define layer to copy
  current_layer = image.layers[0]
  # create the new name AR 2-3
  new_layer = pdb.gimp_layer_new_from_visible(image, new_image, current_layer)
  # add new layer to the new image
  pdb.gimp_image_add_layer(new_image, new_layer, 0)
  # resize the layer
  pdb.gimp_layer_resize(new_layer, 5333, 8000, -345, 0)
  img_name_ar_2_3 = "AR_2-3_" + filename
  # define drawable
  drw = new_image.layers[0]
  # save the image as jpeg
  pdb.file_jpeg_save(new_image, drw, img_name_ar_2_3, img_name_ar_2_3, 0.90, 0, 1, 1, "Aspect Ratio 2-3", 3, 1, 0, 2)
  # delete the new image
  gimp.delete(new_image)

  #Aspect Ratio 4-5
  new_image = pdb.gimp_image_new(6000, 8000, 0)
  pdb.gimp_image_undo_disable(new_image)
  current_layer = image.layers[0]
  new_layer = pdb.gimp_layer_new_from_visible(image, new_image, current_layer)
  pdb.gimp_image_add_layer(new_image, new_layer, 0)
  pdb.gimp_layer_resize(new_layer, 5333, 8000, -345, 0)
  img_name_ar_4_5 = "AR_4-5_" + filename
  drw = new_image.layers[0]
  pdb.file_jpeg_save(new_image, drw, img_name_ar_4_5, img_name_ar_4_5, 0.90, 0, 1, 1, "Aspect Ratio 4-5", 3, 1, 0, 2)
  gimp.delete(new_image)

 
register(
  'set-aspect-ratios',                # Unique ID, I prefix mine with "ofn-" to avoid clashes
  'create different aspect ratios',   # Description/title (short)
  'Set Aspect Ratios',                # Help (can be longer...)
  "Author",                           # KJ
  "Author",                           # Copyright KJ
  "2024",                             # Copyright 2024
  "Set Aspect Ratios",                # The menu label
  "RGB*",                             # The type of images it can work on. Use "*" for all types
  [                                 # List of input parameters
     (PF_IMAGE, "image", "Input image", None)   ####### reinstated the image input argument
                                                 
  ],
  [],                                 # List of output parameters
  setAspectRatios,                    # The Python code that implements the plugin
  menu="<Image>/Filters",             # Where the menu label above appears
)

main()

So, given this the code sorta works(*) but:
  • you save the image with its initial extension (say, PNG) even if you enforce the jpeg format.
  • image is saved in the "current directory" which is certainly not what you want in the general case. Once you have generated the new file name, you probably want to create a full path by putting it at the end of the directory of the source image (that you obtain with os.path.dirname()) using os.path.join().
This may provides a working script(*), but then, DRY: Don't Repeat Yourself: there is very little difference between the 4:3 and 3:2 image ratios: just the name of the image and the image size, so normally both cases can be treated with the same function to which you pass the output file path and the image dimensions. And while you are making functions, you can make one that takes the input path, the aspect ratio, and return a complete output path with adequate extension. So you can test it independently.

(*) at least the script registers, doesn't crash, and produces output, but where and what are still open to debate.
Reply
#5
Quote:because you removed the thing that describes the input arguments by commenting in out, instead of making it an empty list. But it's better to keep it in an have an image argument to your plugin, so you can work even if several images are open in Gimp:

Ah ok! I thought I could comment it out without any harm. Do I need at least one parameter, or is an empty bracket okay too?


Quote:So, given this the code sorta works(*) but:
  • you save the image with its initial extension (say, PNG) even if you enforce the jpeg format.

I only open and save jpegs so that wont be an issue.
  • Quote:image is saved in the "current directory" which is certainly not what you want in the general case. Once you have generated the new file name, you probably want to create a full path by putting it at the end of the directory of the source image (that you obtain with os.path.dirname()) using os.path.join().


Yeah, that was my plan Smile


Quote:This may provides a working script(*), but then, DRY: Don't Repeat Yourself: there is very little difference between the 4:3 and 3:2 image ratios: just the name of the image and the image size, so normally both cases can be treated with the same function to which you pass the output file path and the image dimensions. And while you are making functions, you can make one that takes the input path, the aspect ratio, and return a complete output path with adequate extension. So you can test it independently.


For the current transformations it wont be necessary to repeat, thats right. Functions will be my next step to avoid it in the future. Thanks for the help! Smile
Reply
#6
(01-29-2024, 10:59 PM)SpaceMonkey Wrote:
Quote:because you removed the thing that describes the input arguments by commenting in out, instead of making it an empty list. But it's better to keep it in an have an image argument to your plugin, so you can work even if several images are open in Gimp:

Ah ok! I thought I could comment it out without any harm. Do I need at least one parameter, or is an empty bracket okay too?

You need a least the PF_IMAGE parameter (and the corresponding parameter in the function definition).

(01-29-2024, 10:59 PM)SpaceMonkey Wrote:
Quote:So, given this the code sorta works(*) but:
  • you save the image with its initial extension (say, PNG) even if you enforce the jpeg format.

I only open and save jpegs so that wont be an issue.

Until it becomes one  Big Grin
Reply
#7
I want to add a white frame around the image and found this solution to add to the existing code:

Code:
import sys, os
from gimpfu import *


def SetAspectRatios (image):
   #Output path
   outputPath = "C:\\Users\\"  
    # create the object of the image
   
   #Aspec Ratio 3-4
   image=gimp.image_list()[0]
   filename = os.path.basename(image.filename)
   # create a new image with the same dimensions
   new_image = pdb.gimp_image_new(6000, 8000, 0)
   # disable undo to make it faster
   pdb.gimp_image_undo_disable(new_image)
   # define layer to copy
   current_layer = image.layers[0]
   # create the new name AR 2-3
   new_layer = pdb.gimp_layer_new_from_visible(image, new_image, current_layer)
   # add new layer to the new image
   pdb.gimp_image_add_layer(new_image, new_layer, 0)
   
   # rescale the layer
   pdb.gimp_layer_scale(new_layer, 4538, 6537, 1)
   # resize the layer
   pdb.gimp_layer_resize(new_layer, 6000, 8000, 731, 731)
   #add white background
   background=gimp.Layer(new_image,'Background',new_image.width,new_image.height,RGB_IMAGE,100.,LAYER_MODE_NORMAL)
   background.fill(FILL_WHITE)
   new_image.add_layer(background,len(new_image.layers))
   final_layer = pdb.gimp_image_merge_visible_layers(new_image, 1)

   img_name_ar_3_4 = outputPath + "AR_3-4_w_Border" + filename
   # define drawable
   # drw = new_image.layers[0]
   drw = final_layer
   # save the image as jpeg
   pdb.file_jpeg_save(new_image, drw, img_name_ar_3_4, img_name_ar_3_4, 0.90, 0, 1, 1, "Aspect Ratio 3-4", 3, 1, 0, 2)
   # delete the new image
   gimp.delete(new_image)

           
### Registration
whoiam="\n"+os.path.abspath(sys.argv[0])
author="K"
menu="<Image>/Kevin/SetAspectR/"
copyrightYear="2024"
desc="Set Aspect Ratios v.1.0"

register(
    "SetAspectRatios",
    desc+whoiam,desc,
    author,author,
    copyrightYear,
    "Set Aspect Ratios v.1.0.",
    "*",
    [
        (PF_IMAGE, "image", "Input image", None)          
    ],
    [],
    SetAspectRatios,
    menu=menu
)

main()

Is this the best way to do it or is there a more elegant version? It works but I think it can be done better.
Reply
#8
drw = final_layer isn't necessary you can use final_layer directly in the file_jpeg_save call.

But for me the non-elegant things are the hardcoded output path (that requires admin privs if I am not mistaken...) and all the hard-coded sizes and offsets that will make you suffer if you want to do more than one aspect ratio.
Reply


Forum Jump: