GIMP Chat
http://gimpchat.com/

use of a color curves preset in a python filter
http://gimpchat.com/viewtopic.php?f=8&t=20729
Page 1 of 3

Author:  dinasset [ Thu Jan 25, 2024 9:12 am ]
Post subject:  use of a color curves preset in a python filter

GIMP Version: 2.10.34
Operating System: Windows
GIMP Experience: Intermediate Level



- given that exist predefined color curves presets and that those can be used interactively and each preset usually contains 256 values per color
- given that exists a procedure called gimp-drawable-curves-explicit available for those who creates filters

my question is:

- does exist somewhere an example on how to:
- 1.load the 256 values (text) per color stored in one preset
- 2.convert those into the requested series of 256 float values required by the plug-in statement
- 3.create a python routine to be inserted into a python plug-in?

Thanks.

Author:  ofnuts [ Thu Jan 25, 2024 5:16 pm ]
Post subject:  Re: use of a color curves preset in a python filter

None I know of but not that difficult. Things to take care of:

* Spotting the right setting
* Settings lists all 5 channels (value/red/green/blue/alpha) and you possibly want to recognize those that are identity to avoid playing them.

Author:  dinasset [ Thu Jan 25, 2024 10:26 pm ]
Post subject:  Re: use of a color curves preset in a python filter

Thanks Ofnuts, for your reply.
Yes, you are right, it's not difficult, but simply ...annoying, it would have been much simpler if there was a defined (python) function which having in input the name of the preset and its location, could give in output the gimp procedure. And in practice, there is: just embedded in the online selection of a stored preset.

Author:  ofnuts [ Fri Jan 26, 2024 5:19 am ]
Post subject:  Re: use of a color curves preset in a python filter

import os,sys,traceback,re,codecs

from itertools import islice

class Channel:
    def __init__(self,name,samples):
        self.name=str(name)  # Never unicode
        self.samples=samples
   
class Setting:
    def __init__(self,name,time,channels):
        self.name=name
        self.time=time
        self.channels=channels

def extract(pattern,line):
    matched=re.match(pattern,line.strip())
    return matched.group(1) if matched else None

def readChannel(settingsFile,channelName):
    nameLine,_,typeLine,pointsLine,pointsTypeLine,samplesCountLine,samplesLine=islice(settingsFile,7)
    actualName=extract(r'\(channel (\w+)\)',nameLine)
    if actualName!=channelName:
        raise Exception("Channel name mismatch: expected: %s, actual: %s" % (channelName,actualName))
    if pointsLine.strip()=='(points 4 0 0 1 1)': # identity for channel
        return Channel(channelName,None)
    samplesCount=int(extract(r'\(n-samples (\d+)\)',samplesCountLine))
    samplesString=extract(r'\(samples \d+ ([0-9. ]+)\)',samplesLine) # skip count value
    samples=[float(x) for x in samplesString.split()]
    if len(samples)!=samplesCount:
        raise Exception("Samples count mismatch: expected: %d, actual: %d" % (samplesCount,len(samples)))
    return Channel(channelName,samples)
   
def readSetting(settingsFile):
    try:
        nameLine,timeLine,linearLine=islice(settingsFile,3)
    except ValueError:
        return None
    settingName=extract(r'\(GimpCurvesConfig "(.+)"',nameLine)
    settingTime=int(extract(r'\(time (\d+)\)',timeLine))
    channels=[]
    for channelName in ["value","red","green","blue","alpha"]:
        channel=readChannel(settingsFile,channelName)
        if channel.samples:
            channels.append(channel) # Don't add identity channels
    return Setting(settingName,settingTime,channels)

def settingsFrom(settingsFile):
    # Skip top two lines
    _,_=islice(settingsFile,2)
    while True:
        setting=readSetting(settingsFile)
        if not setting:
            break
        yield setting

def load(settingsFilePath):
    settings=[]
    with codecs.open(settingsFilePath, 'r', encoding='utf-8') as settingsFile:
        for setting in settingsFrom(settingsFile):
            if setting.time==0: # Only named settings
                settings.append(setting)
    return settings

if __name__=="__main__":
    settings=load("./GimpCurvesConfig.settings")
   
    for s in settings:
        print "### %s" % s.name
        for c in s.channels:
            print "   %s: %d" % (c.name,len(c.samples))
            for sample in c.samples[:5]:
                print "        %7.5f" % sample


As written, only keeps settings explicitly saved with a name, and only saves channels that have been changed so you can iterate these and apply gimp-drawable-curves-explicit() on each.

The bit with "if __name__=="__main__":" is of course to test that the thing works.

Author:  dinasset [ Fri Jan 26, 2024 6:50 am ]
Post subject:  Re: use of a color curves preset in a python filter

Wow, thanks a lot!
I will try to understand it as much as possible for my limited capabilities.

Author:  MareroQ [ Fri Jan 26, 2024 7:26 am ]
Post subject:  Re: use of a color curves preset in a python filter

apply_curve by code Tin Tran:
viewtopic.php?f=9&t=14058&start=0

apply_color_curves_preset.py by code Tin Tran:
viewtopic.php?f=9&t=14064&start

My way of using it:
https://www.gimpscripts.net/2021/10/mrq ... s-rpl.html

Attachments:
Aply curves.png
Aply curves.png [ 27.96 KiB | Viewed 680 times ]

Author:  dinasset [ Fri Jan 26, 2024 7:47 am ]
Post subject:  Re: use of a color curves preset in a python filter

Thanks Marek, I'll download also yours.

Author:  trandoductin [ Fri Jan 26, 2024 7:59 am ]
Post subject:  Re: use of a color curves preset in a python filter

Here's my finding I got stuck trying to debug because I can't seem to export presets. Not sure why. (ofnuts code seems way cleaner so I try to use it but...)
My export1 file always has a single time 0. No matter what I do I can't get my saved named color curved presets to export to file with multiple named. it's always single and without my named saved color curve settings. Wonder if my gimp is broken with that (I am on GIMP 2.10.34)
#!/usr/bin/env python
from gimpfu import *
import os,sys,traceback,re,codecs

from itertools import islice

class Channel:
    def __init__(self,name,samples):
        self.name=str(name)  # Never unicode
        self.samples=samples
   
class Setting:
    def __init__(self,name,time,channels):
        self.name=name
        self.time=time
        self.channels=channels

def extract(pattern,line):
    matched=re.match(pattern,line.strip())
    return matched.group(1) if matched else None

def readChannel(settingsFile,channelName):
    nameLine,_,typeLine,pointsLine,pointsTypeLine,samplesCountLine,samplesLine=islice(settingsFile,7)
    actualName=extract(r'\(channel (\w+)\)',nameLine)
    if actualName!=channelName:
        raise Exception("Channel name mismatch: expected: %s, actual: %s" % (channelName,actualName))
    if pointsLine.strip()=='(points 4 0 0 1 1)': # identity for channel
        return Channel(channelName,None)
    samplesCount=int(extract(r'\(n-samples (\d+)\)',samplesCountLine))
    samplesString=extract(r'\(samples \d+ ([0-9. ]+)\)',samplesLine) # skip count value
    samples=[float(x) for x in samplesString.split()]
    if len(samples)!=samplesCount:
        raise Exception("Samples count mismatch: expected: %d, actual: %d" % (samplesCount,len(samples)))
    return Channel(channelName,samples)
   
def readSetting(settingsFile):
    try:
        pdb.gimp_message("readSetting try")
        nameLine,timeLine,linearLine=islice(settingsFile,3)
    except ValueError:
        pdb.gimp_message("readSetting Valueerror")
        return None
    pdb.gimp_message("readSetting settingNameTime")   
    pdb.gimp_message(nameLine)   
    pdb.gimp_message(timeLine)   
    settingName=extract(r'\(GimpCurvesConfig "(.+)"',nameLine)
    settingTime=int(extract(r'\(time (\d+)\)',timeLine))
    pdb.gimp_message("readSetting after settingNameTime")
    channels=[]
    for channelName in ["value","red","green","blue","alpha"]:
        pdb.gimp_message("channelName in list")
        channel=readChannel(settingsFile,channelName)
        if channel.samples:
            channels.append(channel) # Don't add identity channels
    return Setting(settingName,settingTime,channels)

def settingsFrom(settingsFile):
    # Skip top two lines
    pdb.gimp_message("inside settingsFrom")
    _,_=islice(settingsFile,2)
    while True:
        pdb.gimp_message("settingsFrom while Loop")
        setting=readSetting(settingsFile)
        pdb.gimp_message("settings after readSetting")
        if not setting:
            break
        yield setting

def load(settingsFilePath):
    settings=[]
    pdb.gimp_message("inside load")
    with codecs.open(settingsFilePath, 'r', encoding='utf-8') as settingsFile:
        pdb.gimp_message("before settings From")
        for setting in settingsFrom(settingsFile):
            pdb.gimp_message("for loop inside Settings from")
            if setting.time==0: # Only named settings
                pdb.gimp_message("setting.time==0")
                settings.append(setting)
    return settings

# if __name__=="__main__":
#     settings=load("./GimpCurvesConfig.settings")
#     for s in settings:
#         print "### %s" % s.name
#         for c in s.channels:
#             print "   %s: %d" % (c.name,len(c.samples))
#             for sample in c.samples[:5]:
#                 print "        %7.5f" % sample

def python_cc_test(image,layer):
    pdb.gimp_image_undo_group_start(image)
    # pdb.gimp_context_push()
    #YOUR CODE BEGINS=======================
    pdb.gimp_message("what")
    settings=load("C:\\Users\\tintran\\AppData\\Roaming\\GIMP\\2.10\\curves\\export1")

    # for s in settings:
    #     pdb.gimp_message("### " + str(s.name))
    #     for c in s.channels:
    #         pdb.gimp_message("   "+c.name+":"+" "+str(len(c.samples)))
    #         for sample in c.samples[:5]:
    #             pdb.gimp_message("        "+str(sample))

    #pdb.gimp_message(get4Points(ovectors))
    #YOUR CODE ENDS ========================
    # pdb.gimp_context_pop()
    pdb.gimp_image_undo_group_end(image)
    pdb.gimp_displays_flush()

register(
    "python_fu_cc_test",
    "Color Curves Test",
    "Color Curves Test",
    "TT",
    "TT",
    "2024.1.25",
    "<Image>/Python-Fu/A CC Test...",
    "*",      # Create a new image, don't work on an existing one
    [
    #INPUT BEGINS
    #INPUT ENDS
    ],
    [],
    python_cc_test)

main()

Author:  dinasset [ Fri Jan 26, 2024 8:06 am ]
Post subject:  Re: use of a color curves preset in a python filter

Well, Tin, I don't need to "export" a preset but to "load" an existing one (already stored in the appropriate folder) and apply it to the layer active in the python filter.

Author:  dinasset [ Fri Jan 26, 2024 8:11 am ]
Post subject:  Re: use of a color curves preset in a python filter

Any how my first impression from all your lines of code: I'm feeling really a "caveman" as far as a gimp python filter structure is concerned. My filters are all so "troglodyte"...
Hope at end to be able to add -with all your help- the possibility for the user of my filter to:
- select a preset (from those already stored and accessible) and apply it to modify the colors look of the layer.
Thanks

Author:  trandoductin [ Fri Jan 26, 2024 8:22 am ]
Post subject:  Re: use of a color curves preset in a python filter

Yeah but it needs to load from a file. And the file with multiple named color curves is my first step in testing and I can't even do that.
I feel like a caveman too. I just cut and paste many things.

Author:  trandoductin [ Fri Jan 26, 2024 8:28 am ]
Post subject:  Re: use of a color curves preset in a python filter

Actually do you have file with named settings I can use...my GIMP isn't exporting them.

Author:  dinasset [ Fri Jan 26, 2024 8:42 am ]
Post subject:  Re: use of a color curves preset in a python filter

This is what I put (copied from some post on GC) on samj's folder:"...Preferences/Filters"

Attachment:
GimpCurvesConfig.7z [158.5 KiB]
Downloaded 27 times

Author:  trandoductin [ Fri Jan 26, 2024 12:09 pm ]
Post subject:  Re: use of a color curves preset in a python filter

Hi Diego,
With your sample files
I was able to use ofnuts's code with minor changes here
#!/usr/bin/env python
from gimpfu import *
import os,sys,traceback,re,codecs

from itertools import islice

class Channel:
    def __init__(self,name,samples):
        self.name=str(name)  # Never unicode
        self.samples=samples
   
class Setting:
    def __init__(self,name,time,channels):
        self.name=name
        self.time=time
        self.channels=channels

def extract(pattern,line):
    matched=re.match(pattern,line.strip())
    return matched.group(1) if matched else None

def readChannel(settingsFile,channelName):
    nameLine,_,typeLine,pointsLine,pointsTypeLine,samplesCountLine,samplesLine=islice(settingsFile,7)
    #pdb.gimp_message("nameline" +  nameLine)
    #pdb.gimp_message("typeLine" + typeLine)
    #pdb.gimp_message("pointsLine" + pointsLine)
    #pdb.gimp_message(pointsTypeLine)
    #pdb.gimp_message(samplesCountLine)
    #pdb.gimp_message(samplesLine)
    actualName=extract(r'\(channel (\w+)\)',nameLine)
    #pdb.gimp_message(actualName)
    if actualName!=channelName:
        #pdb.gimp_message("1")
        pdb.gimp_message("Channel name mismatch: expected: "+channelName+", actual: "+actualName)
       
    if pointsLine.strip()=='(points 4 0 0 1 1)': # identity for channel
        #pdb.gimp_message("2")
        return Channel(channelName,None)
    #pdb.gimp_message("3")   
    samplesCount=int(extract(r'\(n-samples (\d+)\)',samplesCountLine))
    samplesString=extract(r'\(samples \d+ ([0-9. ]+)\)',samplesLine) # skip count value
    samples=[float(x) for x in samplesString.split()]
    if len(samples)!=samplesCount:
        raise Exception("Samples count mismatch: expected: %d, actual: %d" % (samplesCount,len(samples)))
    return Channel(channelName,samples)
   
def readSetting(settingsFile):
    try:
        #pdb.gimp_message("readSetting try")
        # nameLine,timeLine,linearLine=islice(settingsFile,3)
        nameLine,timeLine=islice(settingsFile,2)
        #pdb.gimp_message("nameLine" + nameLine)
        #pdb.gimp_message("timeLine" + timeLine)
        ##pdb.gimp_message("linearLine" + linearLine)
    except:
        #pdb.gimp_message("readSetting Valueerror")
        return None
    #pdb.gimp_message("readSetting settingNameTime")   
    #pdb.gimp_message(nameLine)   
    #pdb.gimp_message(timeLine)   
    settingName=extract(r'\(GimpCurvesConfig "(.+)"',nameLine)
    settingTime=int(extract(r'\(time (\d+)\)',timeLine))
    #pdb.gimp_message("readSetting after settingNameTime")
    channels=[]
    for channelName in ["value","red","green","blue","alpha"]:
        #pdb.gimp_message("channelName in list")
        channel=readChannel(settingsFile,channelName)
        if channel.samples:
            channels.append(channel) # Don't add identity channels
        #pdb.gimp_message("done")   
    return Setting(settingName,settingTime,channels)

def settingsFrom(settingsFile):
    # Skip top two lines
    #pdb.gimp_message("inside settingsFrom")
    _,_=islice(settingsFile,2)
    while True:
        try:       
            setting=readSetting(settingsFile)
        except:
            setting = None
        #pdb.gimp_message("settings after readSetting")
        if not setting:
            break
        yield setting

def load(settingsFilePath):
    settings=[]
    #pdb.gimp_message("inside load")
    with codecs.open(settingsFilePath, 'r', encoding='utf-8') as settingsFile:
        #pdb.gimp_message("before settings From")
        for setting in settingsFrom(settingsFile):
            #pdb.gimp_message("for loop inside Settings from")
            if setting.time==0: # Only named settings
                #pdb.gimp_message("setting.time==0")
                settings.append(setting)
    return settings

# if __name__=="__main__":
#     settings=load("./GimpCurvesConfig.settings")
#     for s in settings:
#         print "### %s" % s.name
#         for c in s.channels:
#             print "   %s: %d" % (c.name,len(c.samples))
#             for sample in c.samples[:5]:
#                 print "        %7.5f" % sample

def python_cc_test(image,layer):
    pdb.gimp_image_undo_group_start(image)
    # pdb.gimp_context_push()
    #YOUR CODE BEGINS=======================
   
    #CHANGE BELOW to POINT TO YOUR OWN Color Curves Settings
    settings=load("C:\\Users\\tintran\\AppData\\Roaming\\GIMP\\2.10\\curves\\GimpCurvesConfig.settings")
    pdb.gimp_message(settings[0].name)
    pdb.gimp_message(settings[0].time)
    pdb.gimp_message(settings[0].channels[0].name) #value channel
    pdb.gimp_message(settings[0].channels[1].name) #red channel
    pdb.gimp_message(settings[0].channels[1].samples) #red channel
    redchannel_samples = settings[0].channels[1].samples
    pdb.gimp_drawable_curves_explicit(layer,HISTOGRAM_RED,len(redchannel_samples),redchannel_samples)

    # for s in settings:
    #     #pdb.gimp_message("### " + str(s.name))
    #     for c in s.channels:
    #         #pdb.gimp_message("   "+c.name+":"+" "+str(len(c.samples)))
    #         for sample in c.samples[:5]:
    #             #pdb.gimp_message("        "+str(sample))

    ##pdb.gimp_message(get4Points(ovectors))
    #YOUR CODE ENDS ========================
    # pdb.gimp_context_pop()
    pdb.gimp_image_undo_group_end(image)
    pdb.gimp_displays_flush()

register(
    "python_fu_cc_test",
    "Color Curves Test",
    "Color Curves Test",
    "TT",
    "TT",
    "2024.1.25",
    "<Image>/Python-Fu/A CC Test...",
    "*",      # Create a new image, don't work on an existing one
    [
    #INPUT BEGINS
    #INPUT ENDS
    ],
    [],
    python_cc_test)

main()

Author:  ofnuts [ Fri Jan 26, 2024 12:31 pm ]
Post subject:  Re: use of a color curves preset in a python filter

    #CHANGE BELOW to POINT TO YOUR OWN Color Curves Settings
    settings=load("C:\\Users\\tintran\\AppData\\Roaming\\GIMP\\2.10\\curves\\GimpCurvesConfig.settings")

No need to hardcode file paths. Normally this should be:
settings=load(os.path.join(gimp.directory,'filters','GimpCurvesConfig.settings'))

Author:  trandoductin [ Fri Jan 26, 2024 3:26 pm ]
Post subject:  Re: use of a color curves preset in a python filter

thanks for that ofnuts :D

Author:  dinasset [ Fri Jan 26, 2024 9:14 pm ]
Post subject:  Re: use of a color curves preset in a python filter

Sorry Tin and Ofnuts.
- if I copy what you posted (included ofnuts' suggested change) I get a list of messages, but nothing else
- whatever I add in the code, for instance create a new layer, it is executed but then no more filter messages but an error
Attachment:
error-msg.PNG
error-msg.PNG [ 31.22 KiB | Viewed 603 times ]


As long as my little brain does not see at least one curve applied to an existing image I can't try to use the proposed flow.
You too Tin says: does not work on existing image, which is what I need: apply a curve to the input layer.
Thanks a lot for trying.

Author:  trandoductin [ Fri Jan 26, 2024 9:51 pm ]
Post subject:  Re: use of a color curves preset in a python filter

can you share you code?
it looks like it's not reading the file.
Update:
I think instead of
settings=load("C:\\Users\\tintran\\AppData\\Roaming\\GIMP\\2.10\\curves\\GimpCurvesConfig.settings")

with ofnuts piece it's should be.
settings=load(os.path.join(gimp.directory,'curves','GimpCurvesConfig.settings'))

Author:  teapot [ Fri Jan 26, 2024 11:31 pm ]
Post subject:  Re: use of a color curves preset in a python filter

I'm on an old version of 2.10 and my GimpCurvesConfig.settings has lines:
(linear no)
which Ofnuts script is also expecting.
However Tim has taken it out as the, presumably later version, file on post #13 doesn't have them.

Also I'm not getting this even though I deliberately saved a preset that does nothing:

    if pointsLine.strip()=='(points 4 0 0 1 1)': # identity for channel
            return Channel(channelName,None)

If I print pointsLine I get loads of these:
(n-points 17)

Whereas the above check looks like it's better aimed at pointsTypeLine. This is an example print of one of pointsTypeLine:

(points 34 0.000000 0.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 -1.000000 1.000000 1.000000)

Was there another format change to get rid of the -1s in between the 0 0 and 1 1?

Author:  dinasset [ Sat Jan 27, 2024 12:13 am ]
Post subject:  Re: use of a color curves preset in a python filter

trandoductin wrote:
can you share you code?
it looks like it's not reading the file.
Update:
I think instead of
settings=load("C:\\Users\\tintran\\AppData\\Roaming\\GIMP\\2.10\\curves\\GimpCurvesConfig.settings")

with ofnuts piece it's should be.
settings=load(os.path.join(gimp.directory,'curves','GimpCurvesConfig.settings'))

this is my filter as tested with my additions
Attachment:
python_cc_test.7z [2.04 KiB]
Downloaded 18 times

note: I left ofnuts indication (filters, not curves), it doesn't find "curves"

Page 1 of 3 All times are UTC - 5 hours [ DST ]
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/