Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Accessing last applied Auto Levels from a script
#1
Hi all

I'm new to writing GIMP scripts, and relatively new to python, but slowly I'm beginning to make progress with python-fu and simple script development.

I have a process I'm currently performing manually on a regular basis, and I'd like to create a script to automate it. The manual process is as follows:

1. Select an area of the image
2. Perform Auto Levels (Colors -> Levels -> Auto Input Levels)
3. Undo Levels
4. Select all of the image
5. Re-apply the previous levels (Filters -> Repeat Levels)

In scripting the above, I've got as far as step 4 successfully - but for step 5 I'm stuck, as I can't find any procedure in the pdb that lets me re-apply the previous levels. After a bit of searching online, I found that GIMP writes these values to a GimpLevelsConfig.settings file in the AppData\Roaming\GIMP\2.10\Filters directory (on my installation, at least) - and if I look in this file, I can indeed see the levels last applied... but I can't find any procedure that will let me retrieve the values.

So... my questions to the esteemed and knowledgeable python scripters here are:

1. Is there a way to call the Filters -> Repeat Levels function via an existing internal GIMP procedure?
2. If "no" to the above, is there a way to access the levels values last applied without resorting to the "GimpLevelsConfig.settings" file?
3. If "no" to the above, is there a way of reading the file via one or more existing internal GIMP procedures?
4. If "no" to the above, (a) how do I obtain the directory for the file (in case it's configured differently across versions and platforms), and (b) can anyone help me with guidance on reading and parsing the file to a set of variables?

Any assistance for this rank novice would be greatly appreciated. Thanks in advance!

Mike

PS. Sorry, I forgot to mention, I'm running GIMP 2.10 on Windows, Ubuntu and Fedora environments...
Reply
#2

  1. For Python, there is an optional run_mode named parameter that supports values'RUN_INTERACTIVE, RUN_NONINTERACTIVE, RUN_WITH_LAST_VALS , but AFAIK this works mostly with the file export dialogs.
  2. None I know of
  3. None I know of
  4. a) You can use the gimp.directory to obtain the Gimp use profiles, and for there its is os.path.join(gimp.directory,'filters','GimpLevelsConfig.settings') to access the file. b) The latest setting is at the top (so you don't need to search for it) and the rest. Will provide some sample code later today.
Reply
#3
Hi @Ofnuts and thanks so much for the helpful reply...

I checked the pdb.gimp_drawable_levels procedure and it doesn't seem to support the run_mode parameter, unless I'm missing something:

   pdb.gimp_drawable_levels(drawable, channel, low_input, high_input, clamp_input, gamma, low_output, high_output, clamp_output)

Thanks for the information on how to obtain the correct directory path, and forthcoming sample code. Very much appreciated!
Reply
#4
(04-09-2024, 09:40 AM)BigMackCam Wrote: Hi @Ofnuts and thanks so much for the helpful reply...

I checked the pdb.gimp_drawable_levels procedure and it doesn't seem to support the run_mode parameter, unless I'm missing something:

   pdb.gimp_drawable_levels(drawable, channel, low_input, high_input, clamp_input, gamma, low_output, high_output, clamp_output)

Thanks for the information on how to obtain the correct directory path, and forthcoming sample code. Very much appreciated!

Sample code

Code:
#! /bin/env python

import sys,re
from collections import namedtuple

stringValuePattern=r'\(([a-z-]+) ([^)]+)\)'
intValuePattern=r'\(([a-z-]+) (\d+)\)'
floatValuePattern=r'\(([a-z-]+) (\d+(\.\d+)?)\)'
booleanValuePattern=r'\(([a-z-]+) (yes|no)\)'

ChannelSettings=namedtuple('ChannelSettings',['name','loInput','hiInput','gamma','loOutput','hiOutput'])
LevelsSettings=namedtuple('LevelsSettings',['time','linear','clampInput','clampOutput','value','red','green','blue','alpha'])

def expected(pattern,name,line):
    matched=re.search(pattern,line)
    if not matched:
        raise Exception('Pattern not matched for line %s',line)
    if matched.group(1)!=name:
        raise Exception('Unexpected name %s (expected: %s)' % (matched.group(1),name))
    return matched.group(2)

def expectedChannel(channelName,line):
    value=expected(stringValuePattern,'channel',line)
    if value!=channelName:
        raise Exception('Unexpected channel %s (expected: %s)' % (value,channelName))
                     
def expectedInt(name,line):
    value=expected(intValuePattern,name,line)
    return int(value)

def expectedFloat(name,line):
    value=expected(floatValuePattern,name,line)
    return float(value)

def expectedBoolean(name,line):
    value=expected(booleanValuePattern,name,line)
    return value=='yes'

def readChannel(channelName,levelsConf):
    expectedChannel(channelName,next(levelsConf))
    return ChannelSettings(
        channelName,
        expectedFloat('low-input',next(levelsConf)),
        expectedFloat('high-input',next(levelsConf)),
        expectedFloat('gamma',next(levelsConf)),
        expectedFloat('low-output',next(levelsConf)),
        expectedFloat('high-output',next(levelsConf)),
        )

def readLevels(file):
    with open(file) as levelsConf:
        next(levelsConf) # skip top comment
        next(levelsConf) # skip blank line
        next(levelsConf) # settings header

        return LevelsSettings(
            expectedInt('time',next(levelsConf)),
            expectedBoolean('linear',next(levelsConf)),
            expectedBoolean('clamp-input',next(levelsConf)),
            expectedBoolean('clamp-output',next(levelsConf)),
            readChannel('value',levelsConf),
            readChannel('red',levelsConf),
            readChannel('green',levelsConf),
            readChannel('blue',levelsConf),
            readChannel('alpha',levelsConf),
        )   
    
settings=readLevels(sys.argv[1])
print settings       

From a file that beings with:

Code:
# settings

(GimpLevelsConfig "2024-04-09 10:32:38"
    (time 1712651558)
    (linear no)
    (clamp-input no)
    (clamp-output no)
    (channel value)
    (low-input 0)
    (high-input 1)
    (gamma 1)
    (low-output 0.05849056603773585)
    (high-output 1)
    (channel red)
    (low-input 0)
    (high-input 1)
    (gamma 1)
    (low-output 0)
    (high-output 1)
    (channel green)
    (low-input 0)
    (high-input 1)
    (gamma 1)
    (low-output 0)
    (high-output 1)
    (channel blue)
    (low-input 0)
    (high-input 1)
    (gamma 1)
    (low-output 0)
    (high-output 1)
    (channel alpha)
    (low-input 0)
    (high-input 1)
    (gamma 1)
    (low-output 0)
    (high-output 1))
(GimpLevelsConfig "2024-04-09 10:30:18"
    (time 1712651418)
    (linear no)
    (clamp-input no)
    (clamp-output no)
    (channel value)
    (low-input 0)
    (high-input 1)
    (gamma 1)
    (low-output 0)
######## etc....
Yields:
Code:
LevelsSettings(time=1712651558, linear=False, clampInput=False, clampOutput=False,
value=ChannelSettings(name='value', loInput=0.0, hiInput=1.0, gamma=1.0, loOutput=0.05849056603773585, hiOutput=1.0),
red=ChannelSettings(name='red', loInput=0.0, hiInput=1.0, gamma=1.0, loOutput=0.0, hiOutput=1.0),
green=ChannelSettings(name='green', loInput=0.0, hiInput=1.0, gamma=1.0, loOutput=0.0, hiOutput=1.0),
blue=ChannelSettings(name='blue', loInput=0.0, hiInput=1.0, gamma=1.0, loOutput=0.0, hiOutput=1.0),
alpha=ChannelSettings(name='alpha', loInput=0.0, hiInput=1.0, gamma=1.0, loOutput=0.0, hiOutput=1.0))


Moderately tested. Enjoy.
Reply
#5
(04-09-2024, 12:55 PM)Ofnuts Wrote: Moderately tested. Enjoy.

Wow... This is great! At my current novice level with Python, I can understand what the code does, but I wouldn't have come up with such an elegant solution at this stage. I can't thank you enough for your help and the time you've spent on this. Hopefully I can pay it forward when my completed and tested scripts are ready for release, as I suspect a few folks may find them as useful as I will...

Many thanks & all the best.

Mike
Reply


Forum Jump: