Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
A simple function to use non-linear curves in Python
#1
Hello,

I hit a wall when trying to convert one of my old v2python scripts making use of curves. The problem is that the curves_spline(...) API function works in linear space, with no option to use curves in non-linear space. Designing curves in linear space is a nightmare, so don't even start me on this ;-)

Simply converting the curves coordinates to linear don't work, as x values are compressed to the left, and the result is very, very ugly. The right way to do it would be to sample many points along the curve, by applying the Catmull-Rom splines algorithm, but these splines require 4 points to work, and I have no idea how GIMP calculates the first and last segments.

It seems I'm not the only one on the internet with this problem since 3.0, and all questions of this kind are generally left unanswered, so I wanted to share a mildly clever little hack I found here. 

The idea is to transform the coordinates values to linear, apply the non-linear spline curve, and transform back the values to the original non-linear space.If you find this back-and-forth confusing, so do I, but after much brain storming and many tests I can say that it works really well... except for 8 bits integer images which really don't like 3 consecutive operations in linear space!!! (please keep that in mind)!

Here is my code. Reuse it as you wish!



Code:
def srgb_curves_spline(drawable, channel, spline) :
    # GIMP 3.0 API curves functions only work in linear space. This function is a hack
    #   to reproduce the effect of a non-linear srgb spline curve.
    # IMPORTANT: this function does NOT work well with 8 bits integer images. 
    # The "spline" parameter is a list of x y coordinates of the spline points. 
    # See the GIMP API doc for the channel parameter...
    #   example: 'Gimp.HistogramChannel.VALUE' (without quotes)
    # Non-smooth points are not supported. 
    # License is GPL3: http://www.gnu.org/licenses/gpl.html
    
    samplecount = 1024 # count of equidistant samples in linear space
    
    linofx_curve = []
    srgbofx_curve = []
    i = 0
    
    while i < samplecount :
        
        # current step in linear space
        xlin = float(i) / (float(samplecount) - 1.0)
        
        # srgb to linear
        if xlin < 0.0031308 :
            linofx_curve.append( xlin * 12.92 )
        else :
            linofx_curve.append( 1.055 * xlin**(1.0/2.4) - 0.055 )
            
        # linear to srgb
        if xlin < 0.04045 :
            srgbofx_curve.append( xlin / 12.92 )
        else :
            srgbofx_curve.append( ( (xlin+0.055)/1.055 )**2.4 )
        
        i += 1
        
    # end while
    
    # apply the curve surrounded by a back-and-forth to linear space values
    # note that we don't change the color space of the image, it's just en intermediate 
    # representation of values 
    drawable.curves_explicit(channel, linofx_curve)
    drawable.curves_spline(channel, spline)
    drawable.curves_explicit(channel, srgbofx_curve)

    return


Notes: 
The default "srgb" color space is used for the non-linear space. If you really need curves in other spaces, change the transfer functions accordingly.
You can tweak the "samplecount" parameter for performance (down to 256). The value chosen (1024) ensures good quality conversion of dark values, but 256 is perfectly acceptable.
Reply


Forum Jump: