3

I am working on image editing using OPENGL in Android and I have applied filter to an image using photoshop curve now I want to reproduce the same in Android using glsl. Is there any formula to calculate single fragment color using photoshops curve output value?

EDIT

The math behind the photoshop curve has already been answered in this question How to recreate the math behind photoshop curves but I am not very clear about how to reproduce the same in glsl fragment shader.

Screen Shot of my photoshop curve enter image description here

Community
  • 1
  • 1
Praveena
  • 6,340
  • 2
  • 40
  • 53
  • possible duplicate of [How to recreate the math behind photoshop curves](http://stackoverflow.com/questions/4356788/how-to-recreate-the-math-behind-photoshop-curves) – jozxyqk Jun 11 '15 at 05:32
  • @jozxyqk I have gone through it already but that is not very clear so I posted this question. – Praveena Jun 11 '15 at 05:36
  • In that case, could you narrow down exactly what you question is in relation to [the linked question/answers](http://stackoverflow.com/questions/4356788/how-to-recreate-the-math-behind-photoshop-curves)? – jozxyqk Jun 11 '15 at 05:38

1 Answers1

2

You're after a function fragColour = curves(inColour, constants...). If you have just the one curve for red, green and blue you apply the same curve to each individually. This answer has a link (below) to code which plots points along the function. The key line is:

double y = ...

Which you'd return from curves. The variable x in the loop is your inColour. All you need now is the constants which come from the points and the second derivative sd arrays. These you'll have to pass in as uniforms. The function first has to figure out which point each colour x is between (finding cur, next, sd[i] and sd[i+1]), then evaluate and return y.

EDIT:
If you just want to apply some curve you've created in photoshop then the problem is much simpler. The easiest way is to create a simple function that gives a similar shape. I use these as a starting point. A gamma correction curve is also quite common.

This is overkill, but if you do need a more exact result, you could create an image with a linear ramp (e.g. 255 pixels from black to white), apply your filter to it in photoshop and the result becomes a lookup table. Passing in all 255 values to a shader is expensive so if it's a smooth curve you could try some curve fitting tools (for example).

Once you have a function, simply apply it to your colour in GLSL. Applying a gamma curve for example is done like this:

fragColour = vec4(pow(inColour.rgb, 1.0 / gamma.rgb), inColour.a);

EDIT2:

The curve you have looks very similar to this:

fragColour = vec4(pow(inColour.rgb, 1.0 / vec3(0.6)), inColour.a);

Or even simpler:

fragColour = vec4(inColour.rgb * inColour.rgb, inColour.a);

Just in case the link dies, I'll copy the code here (not that I've tested it):

Point[] points = /* liste de points, triés par "x" croissants */

double[] sd = secondDerivative(points);

for(int i=0;i<points.length-1;i++) {
    Point cur   = points[i];
        Point next  = points[i+1];

        for(int x=cur.x;x<next.x;x++) {
        double t = (double)(x-cur.x)/(next.x-cur.x);

        double a = 1-t;
        double b = t;
        double h = next.x-cur.x;

        double y= a*cur.y + b*next.y + (h*h/6)*( (a*a*a-a)*sd[i]+ (b*b*b-b)*sd[i+1] );

        draw(x,y); /* ou tout autre utilisation */
    }
}

And the second derivative:

public static double[] secondDerivative(Point... P) {
    int n = P.length;
    double yp1=0.0; 
    double ypn=0.0;

    // build the tridiagonal system 
    // (assume 0 boundary conditions: y2[0]=y2[-1]=0) 
    double[][] matrix = new double[n][3];
    double[] result = new double[n];
    matrix[0][1]=1;
    for(int i=1;i<n-1;i++) {
        matrix[i][0]=(double)(P[i].x-P[i-1].x)/6;
        matrix[i][1]=(double)(P[i+1].x-P[i-1].x)/3;
        matrix[i][2]=(double)(P[i+1].x-P[i].x)/6;
        result[i]=(double)(P[i+1].y-P[i].y)/(P[i+1].x-P[i].x) - (double)(P[i].y-P[i-1].y)/(P[i].x-P[i-1].x);
    }
    matrix[n-1][1]=1;

    // solving pass1 (up->down)
    for(int i=1;i<n;i++) {
        double k = matrix[i][0]/matrix[i-1][1];
        matrix[i][1] -= k*matrix[i-1][2];
        matrix[i][0] = 0;
        result[i] -= k*result[i-1];
    }
    // solving pass2 (down->up)
    for(int i=n-2;i>=0;i--) {
        double k = matrix[i][2]/matrix[i+1][1];
        matrix[i][1] -= k*matrix[i+1][0];
        matrix[i][2] = 0;
        result[i] -= k*result[i+1];
    }

    // return second derivative value for each point P
    double[] y2 = new double[n];
    for(int i=0;i<n;i++) y2[i]=result[i]/matrix[i][1];
    return y2;
}
Community
  • 1
  • 1
jozxyqk
  • 16,424
  • 12
  • 91
  • 180
  • where is the `curves()` function? – Praveena Jun 11 '15 at 05:57
  • @Praveen The function you'll be creating. It's essentially `y=f(x)` given `points` and `sd` in the linked code. – jozxyqk Jun 11 '15 at 07:07
  • Still confused. Can you please tell me what is `draw(x,y)` and `constants...` – Praveena Jun 11 '15 at 08:43
  • @Praveen The linked code generates `x` coordinates and plots points on the curve using draw `draw()`. You have `x` and want `y`. Easy. use the line `y = ...` and provide your own `x`. However, there's other stuff on that line you need. Those are the `constants` I referred to. The constants define the shape of the curve and ultimately come from the plot points you've specified just like in photoshop when you click on the graph. – jozxyqk Jun 11 '15 at 09:04
  • In the answer you said `x` is `inColor` but here you are saying `x` is coordinate :-( – Praveena Jun 11 '15 at 09:14
  • The linked code uses `x` and `y` as the coordinates to `draw()` because it's plotting the points. Instead, you want to find the `y` coordinate on the curve given your `x` coordinate. You're applying the curve function to colour. `x` and `y` in your case are `inColour` and `fragColour` respectively. – jozxyqk Jun 11 '15 at 09:23
  • Oh God feeling like shooting myself.. I am still not able to understand. I am not getting how to write my `curves` function. What are the `points` and what are the values I should get it from photoshop? – Praveena Jun 11 '15 at 11:08
  • `points` are the control points you'd use to create the curve. I assumed you wanted to implement the curve editing GUI yourself. I'm not sure if you can get the exact values from photoshop but if you're after a static function things become much easier. I'll edit the answer... – jozxyqk Jun 11 '15 at 11:46
  • I am not all implementing GUI... My intension is just take the values for the filter I created from the photoshop and directly apply it to the image using `glsl` thats it.. – Praveena Jun 11 '15 at 11:51
  • @Praveen if you have a screenshot of the curve (add it to your question), I may be able to suggest a function. – jozxyqk Jun 11 '15 at 12:09
  • Thank you so much. I added screenshot of my photoshop curve to the question. – Praveena Jun 11 '15 at 15:11
  • Two more doubts.. `inColor` is just the color of original image right? and how could you write the function so fast just looking at the curve? any tricks? – Praveena Jun 11 '15 at 15:36
  • Yes, could just be `fragColour.rgb *= fragColour.rgb`. I'm pretty familiar with maths and what cartesian functions look like. But I also wrote [this](http://goanna.cs.rmit.edu.au/~pknowles/graphs.html) for just these situations. – jozxyqk Jun 11 '15 at 15:40