Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Photobooth "look" - python script
#1
Python 
Hello
I asked Gemini GPT about how to replicate a colour chemical photobooth look in GIMP. Gemini explained the photobooth history, different lenses and chemical approaches and eventually wrote me a python script that creates the look.
Of course, the python script looks the part to an neophyte such as myself, but does not work either in python3 or Gimp python or as a filter. It does not show in GEGL either.
(I have yet to get a GPT sourced python script that works off the bat, so dev still have a career in front of them).
The funny thing is, the every time I ask the GPT to correct the script, it finds an "error" to correct, but none of these corrections ever make the code work.
The primary issue is that when I run the script in fu-script, I get loads of indentation error! Python indentation drives me crazy.
Therefore ... Does anyone know if there is a working "photobooth" GIMP script?
Can anyone help me debug this script for GIMP 3 linux  please ?
Code:
#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import gi
import datetime

try:
   gi.require_version('Gimp', '3.0')
   gi.require_version('Gegl', '0.4')
   from gi.repository import Gimp, Gegl, GObject, GLib
except ValueError, e:
   sys.exit(1)


def apply_schneider_look(image, drawable):
   """Applies optical and chemical effects."""

   pdb = Gimp.get_pdb()

   # 1. Mirror

   drawable.transform_flip_simple(Gimp.OrientationType.HORIZONTAL,
                                  True, 0)

   # 2. Schneider Optical Effects
   # We use Gimp.ValueArray.new(count) for GIMP 3 compatibility

   dist_args = Gimp.ValueArray.new(3)
   dist_args.insert(0, GObject.Value(Gimp.Drawable, drawable))
   dist_args.insert(1, GObject.Value(GObject.TYPE_STRING,
                    'gegl:lens-distortion'))
   pdb.run_procedure('gimp-drawable-edit-gegl-config', dist_args)

   bloom_args = Gimp.ValueArray.new(3)
   bloom_args.insert(0, GObject.Value(Gimp.Drawable, drawable))
   bloom_args.insert(1, GObject.Value(GObject.TYPE_STRING, 'gegl:bloom'
                     ))
   pdb.run_procedure('gimp-drawable-edit-gegl-config', bloom_args)

   # 3. Chemical Color Shift

   cb_args = Gimp.ValueArray.new(11)
   cb_args.insert(0, GObject.Value(Gimp.Drawable, drawable))
   cb_args.insert(1, GObject.Value(GObject.TYPE_DOUBLE, -0.15))
   cb_args.insert(2, GObject.Value(GObject.TYPE_DOUBLE, 0.0))
   cb_args.insert(3, GObject.Value(GObject.TYPE_DOUBLE, 0.10))
   cb_args.insert(4, GObject.Value(GObject.TYPE_DOUBLE, 0.0))
   cb_args.insert(5, GObject.Value(GObject.TYPE_DOUBLE, 0.0))
   cb_args.insert(6, GObject.Value(GObject.TYPE_DOUBLE, 0.0))
   cb_args.insert(7, GObject.Value(GObject.TYPE_DOUBLE, 0.10))
   cb_args.insert(8, GObject.Value(GObject.TYPE_DOUBLE, 0.0))
   cb_args.insert(9, GObject.Value(GObject.TYPE_DOUBLE, -0.25))
   cb_args.insert(10, GObject.Value(GObject.TYPE_BOOLEAN, True))
   pdb.run_procedure('gimp-drawable-color-balance', cb_args)

   # 4. Silver Halide Grain

   grain_args = Gimp.ValueArray.new(3)
   grain_args.insert(0, GObject.Value(Gimp.Drawable, drawable))
   grain_args.insert(1, GObject.Value(GObject.TYPE_STRING,
                     'gegl:noise-rgb'))
   pdb.run_procedure('gimp-drawable-edit-gegl-config', grain_args)


def photobooth_main_proc(
   procedure,
   run_mode,
   image,
   drawables,
   args,
   data,
   ):
   Gimp.context_push()
   image.undo_group_start()

   # Constants

   (STRIP_W, STRIP_H) = (600, 1800)
   (MARGIN, FRAME_H) = (40, 400)

   new_image = Gimp.Image.new(STRIP_W, STRIP_H, Gimp.ImageBaseType.RGB)

   bg_color = Gimp.RGB()
   bg_color.set_parse('#F9F7F2')
   bg_layer = Gimp.Layer.new(
       new_image,
       'Paper Base',
       STRIP_W,
       STRIP_H,
       Gimp.Precision.U8_GAMMA,
       100,
       Gimp.LayerMode.NORMAL,
       )
   new_image.insert_layer(bg_layer, None, 0)
   Gimp.context_set_background(bg_color)
   bg_layer.fill(Gimp.FillType.BACKGROUND)

   y_offset = MARGIN
   for i in range(min(len(drawables), 4)):
       source_layer = drawables[i]
       new_frame = Gimp.Layer.new_from_drawable(source_layer,
               new_image)
       new_image.insert_layer(new_frame, None, -1)

       apply_schneider_look(new_image, new_frame)

       target_w = STRIP_W - MARGIN * 2
       scale_ratio = target_w / new_frame.get_width()
       new_frame.scale(target_w, int(new_frame.get_height()
                       * scale_ratio), True)
       new_frame.set_offsets(MARGIN, y_offset)
       y_offset += FRAME_H + 15

   # Mechanical Date Stamp

   stamp_color = Gimp.RGB()
   stamp_color.set_parse('#B22222')
   Gimp.context_set_foreground(stamp_color)
   date_str = datetime.datetime.now().strftime('PHOTO-ME - %d %b %Y'
           ).upper()

   # Text Layer logic for GIMP 3

   stamp_layer = Gimp.text_fontname(
       new_image,
       None,
       0,
       0,
       date_str,
       0,
       True,
       18,
       'Sans-Serif',
       )
   if stamp_layer:
       stamp_layer.transform_rotate_simple(Gimp.OrientationType.VERTICAL,
               True, 0, 0)
       stamp_layer.set_offsets(STRIP_W - 35, STRIP_H - 450)
       stamp_layer.set_opacity(70)

   Gimp.Display.new(new_image)
   image.undo_group_end()
   Gimp.context_pop()
   Gimp.displays_flush()

   return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS,
           GLib.Error())


class PhotoboothPro(Gimp.PlugIn):

   def do_query_procedures(self):
       return ['python-fu-photobooth-pro']

   def do_create_procedure(self, name):
       procedure = Gimp.ImageProcedure.new(self, name,
               Gimp.PDBProcType.PLUGIN, photobooth_main_proc, None)
       procedure.set_image_types('RGB*')
       procedure.set_documentation('Schneider Photobooth Pro',
                                   'Full analog strip recreation',
                                   name)
       procedure.set_menu_label('Schneider Photobooth Pro...')
       procedure.add_menu_path('<Image>/Filters/Artistic')
       procedure.set_attribution('Gemini', 'Gemini', '2026')
       return procedure


Gimp.main(PhotoboothPro.__gtype__, sys.argv)
Reply
#2
I can see right at the start - the shebang should be #!/usr/bin/env python3   and if the AI gets that wrong what else.  I have not seen any sympathy from coders to get involved in a fixing AI code, but who knows,  maybe one someone will help.
Also quote...The primary issue is that when I run the script in fu-script  Not script-fu, it is python.

Post an example of the type of image effect, I did a search for Schneider Photobooth and nothing apart from a load of social media rubbish.

Maybe as an alternative there is the gimp_gmic_qt plugin http://www.gmic.eu  Comes with many (600) look-up-tables (LUT) See if anything there matches your requirments.

   
Reply
#3
Hi,
The code is very rough. It's output isn't very useful as is, but it runs now. I had some help with Google AI and and ChatGPT.

Code:
#!/usr/bin/env python3
import sys
import gi
import datetime

"""
# I use this when debugging. Any 'print()' statements get sent to 'file'.

# Trace errors by recording them to a file.
# If the file doesn't exist, create it.
import time
file = sys.stderr = sys.stdout = open("D:\\error.txt", 'a')
_start = f"\nPhotoBoothPro, {time.ctime()}"
_end = "_" * (80 - len(_start))
print(f"{_start}{_end}")
"""

gi.require_version('Gimp', '3.0')
gi.require_version('Gegl', '0.4')
from gi.repository import Gegl, Gimp, GLib   # noqa

STRIP_W, STRIP_H = 600, 1800
MARGIN, FRAME_H = 40, 400


def run_filter(layer, filter_, q, mode, opacity):
   """
   layer: Gimp.Layer
       Receive filter output.
   filter_: Gimp.DrawableFilter
   q: iterable
       [(property str, property value)]
   mode: Gimp.LayerMode
       Identify the layer blend mode with an enum (int).
   opacity: float
       .0 to 100.
   """
   config = filter_.get_config()

   for q1 in q:
       config.set_property(*q1)

   filter_.set_blend_mode(mode)
   filter_.set_opacity(opacity)
   filter_.update()
   layer.append_filter(filter_)
   return filter_


def make_filter(layer, s):
   """
   Create a "Gimp.DrawableFilter" by string.

   layer: Gimp.Layer
       The filter name, e.g. 'oil-paint'.
   """
   print(f"Making filter: {s}")
   return Gimp.DrawableFilter.new(layer, f'gegl:{s}', "")


def do_color_balance(
   drawable,
   transfer_mode,
   preserve_lum,
   cyan_red,
   magenta_green,
   yellow_blue
):
   """Adjust color balance of a drawable."""
   procedure = Gimp.get_pdb().lookup_procedure('gimp-drawable-color-balance')
   config = procedure.create_config()
   config.set_property('drawable', drawable)
   config.set_property('transfer-mode', transfer_mode)
   config.set_property('preserve-lum', preserve_lum)
   config.set_property('cyan-red', cyan_red)
   config.set_property('magenta-green', magenta_green)
   config.set_property('yellow-blue', yellow_blue)
   result = procedure.run(config)
   success = result.index(0)
   return success


def apply_schneider_look(image, drawable):
   """Applies optical and chemical effects."""
   # 1. Mirror
   drawable.transform_flip_simple(Gimp.OrientationType.HORIZONTAL, True, 0)

   # Prepare arguments for Lens Distortion.
   filter_ = make_filter(drawable, 'lens-distortion')

   # 2. Schneider Optical Effects
   # Use the default filter settings, '[]', for Lens Distortion.
   run_filter(
       drawable,
       filter_,
       [],
       Gimp.LayerMode.NORMAL,
       100.
   )

   filter_ = make_filter(drawable, 'bloom')

   # Use the default filter settings, '[]', for Bloom.
   run_filter(
       drawable,
       filter_,
       [],
       Gimp.LayerMode.NORMAL,
       100.
   )

   # 3. Chemical Color Balance
   preserve_lum = True
   cyan_red = -0.15
   magenta_green = 0.10
   yellow_blue = -0.25

   do_color_balance(
       drawable,
       Gimp.TransferMode.SHADOWS,
       preserve_lum,
       cyan_red, magenta_green, yellow_blue
   )
   do_color_balance(
       drawable,
       Gimp.TransferMode.HIGHLIGHTS,
       preserve_lum,
       cyan_red, magenta_green, yellow_blue
   )
   do_color_balance(
       drawable,
       Gimp.TransferMode.MIDTONES,
       preserve_lum,
       cyan_red, magenta_green, yellow_blue
   )

   # 4. Silver Halide Grain
   filter_ = make_filter(drawable, 'noise-rgb')
   run_filter(
       drawable,
       filter_,
       [],
       Gimp.LayerMode.NORMAL,
       100.
   )


def photobooth_main_proc(
   procedure,
   run_mode,
   image,
   drawables,
   args,
   data,
):
   Gimp.context_push()
   image.undo_group_start()

   new_image = Gimp.Image.new(STRIP_W, STRIP_H, Gimp.ImageBaseType.RGB)

   Gimp.Display.new(new_image)     #

   bg_color = Gegl.Color.new('#F9F7F2')
   bg_layer = Gimp.Layer.new(
       new_image,
       'Paper Base',
       STRIP_W,
       STRIP_H,
       Gimp.ImageType.RGBA_IMAGE,
       100.,
       Gimp.LayerMode.NORMAL,
   )
   new_image.insert_layer(bg_layer, None, 0)
   Gimp.context_set_background(bg_color)
   bg_layer.fill(Gimp.FillType.BACKGROUND)

   y_offset = MARGIN

   for i in range(min(len(drawables), 4)):
       source_layer = drawables[i]
       new_frame = Gimp.Layer.new_from_drawable(source_layer, new_image)

       new_image.insert_layer(new_frame, None, -1)
       apply_schneider_look(new_image, new_frame)

       target_w = STRIP_W - MARGIN * 2
       scale_ratio = target_w / new_frame.get_width()

       new_frame.scale(
           target_w, int(new_frame.get_height() * scale_ratio), True
       )
       new_frame.set_offsets(MARGIN, y_offset)
       y_offset += FRAME_H + 15

   # Mechanical Date Stamp
   stamp_color = Gegl.Color.new('#B22222')
   date_str = datetime.datetime.now().strftime('PHOTO-ME - %d %b %Y').upper()
   Gimp.context_set_foreground(stamp_color)

   # This should work but doesn't.
   font = Gimp.Font.get_by_name('Sans-serif')

   # Text Layer logic for GIMP 3
   stamp_layer = Gimp.TextLayer.new(
       new_image,
       date_str,
       font,
       18.0,
       Gimp.Unit.pixel()
   )
   new_image.insert_layer(stamp_layer, None, 0)

   if stamp_layer:
       stamp_layer.transform_rotate_simple(
           Gimp.OrientationType.VERTICAL, True, 0, 0
       )
       # stamp_layer.set_offsets(STRIP_W - 35, STRIP_H - 450)  Needs work.
       stamp_layer.set_opacity(70.)

   Gimp.Display.new(new_image)
   image.undo_group_end()
   Gimp.context_pop()
   Gimp.displays_flush()
   return procedure.new_return_values(
       Gimp.PDBStatusType.SUCCESS, GLib.Error()
   )


class PhotoboothPro(Gimp.PlugIn):

   def do_query_procedures(self):
       return ['python-fu-photobooth-pro']

   def do_create_procedure(self, name):
       procedure = Gimp.ImageProcedure.new(
           self, name, Gimp.PDBProcType.PLUGIN, photobooth_main_proc, None
       )
       procedure.set_image_types('RGB*')
       procedure.set_documentation(
           'Schneider Photobooth Pro',
           'Full analog strip recreation',
           name
       )
       procedure.set_menu_label('Schneider Photobooth Pro...')
       procedure.add_menu_path('<Image>/Filters/Artistic')
       procedure.set_attribution('Gemini', 'Gemini', '2026')
       return procedure


Gimp.main(PhotoboothPro.__gtype__, sys.argv)


Attached Files
.zip   PhotoboothPro.zip (Size: 3.01 KB / Downloads: 2)
Reply


Forum Jump: