3

I am trying to implement some function like below

enter image description here

For this I am trying to use Cubic interpolation and Catmull interpolation ( check both separately to compare the best result) , what i am not understanding is what impact these interpolation show on image and how we can get these points values where we clicked to set that curve ? and do we need to define the function these black points on the image separately ?

I am getting help from these resources

Source 1

Source 2

Approx the same focus

Edit

int main (int argc, const char** argv)
{
Mat input = imread ("E:\\img2.jpg");
for(int i=0 ; i<input.rows ; i++)
{
    for (int p=0;p<input.cols;p++)
    {
        //for(int t=0; t<input.channels(); t++)
    //{

        input.at<cv::Vec3b>(i,p)[0] = 255*correction(input.at<cv::Vec3b>(i,p)[0]/255.0,ctrl,N);  //B
        input.at<cv::Vec3b>(i,p)[1] = 255*correction(input.at<cv::Vec3b>(i,p)[1]/255.0,ctrl,N);  //G
        input.at<cv::Vec3b>(i,p)[2] = 255*correction(input.at<cv::Vec3b>(i,p)[2]/255.0,ctrl,N);  //R
    //}
    }
}

imshow("image" , input);
waitKey();
}
Community
  • 1
  • 1
AHF
  • 1,070
  • 2
  • 15
  • 47
  • I. cubic has 4 control points not 5 !!!, II. I use this interpolation cubic http://stackoverflow.com/a/22582447/2521214 are your control points always on the x grid lines? in that case you can use just 1D interpolation but have to create the coefficients accordingly – Spektre May 13 '14 at 08:29
  • Yes my points are always on the same x coordinates – AHF May 13 '14 at 09:23
  • mine function is on 0-1 range !!! try new_col = 255.0*correction(colors[0]/255.0,ctrl,N); also control points are set to some values ? (if they are zero then the result is zero). do knot know what split and merge does in your case ... you have to pick pixel ... recompute RGB and then set pixel back ... – Spektre May 13 '14 at 14:28
  • isn't it the control point values `0.00, 0.25, 0.50, 0.75, 1.00,` – AHF May 13 '14 at 14:47
  • yeas but these make no difference between input and output if you change them slightly then you will see... I add some image to answer in a minute – Spektre May 13 '14 at 15:03
  • above code which i wrote show me the green plane image – AHF May 13 '14 at 15:05
  • 1
    your problem is that t is actual channel intensity not channel index !!! remove last for just left that 3 correction calls. and change t/255 with input.at(i,p)[0,1 or 2]/255.0 – Spektre May 13 '14 at 15:14
  • updated your code ... btw you have the same ctrl for each channel you should have separate control points per each channel (ctrl_r,ctrl_g,ctrl_b) so you can change each channel separately. – Spektre May 13 '14 at 15:36

1 Answers1

4

So if your control points are always on the same x coordinate
and linearly dispersed along whole range then you can do it like this:

//---------------------------------------------------------------------------
const int N=5;      // number of control points (must be >= 4)
float ctrl[N]=      // control points y values initiated with linear function y=x
    {           // x value is index*1.0/(N-1)
    0.00,
    0.25,
    0.50,
    0.75,
    1.00,
    };
//---------------------------------------------------------------------------
float correction(float col,float *ctrl,int n)
    {
    float di=1.0/float(n-1);
    int i0,i1,i2,i3;
    float t,tt,ttt;
    float a0,a1,a2,a3,d1,d2;
    // find start control point
    col*=float(n-1);
    i1=col; col-=i1;
    i0=i1-1; if (i0< 0) i0=0;
    i2=i1+1; if (i2>=n) i2=n-1;
    i3=i1+2; if (i3>=n) i3=n-1;
    // compute interpolation coefficients
    d1=0.5*(ctrl[i2]-ctrl[i0]);
    d2=0.5*(ctrl[i3]-ctrl[i1]);
    a0=ctrl[i1];
    a1=d1;
    a2=(3.0*(ctrl[i2]-ctrl[i1]))-(2.0*d1)-d2;
    a3=d1+d2+(2.0*(-ctrl[i2]+ctrl[i1]));
    // now interpolate new colro intensity
    t=col; tt=t*t; ttt=tt*t;
    t=a0+(a1*t)+(a2*tt)+(a3*ttt);
    return t;
    }
//---------------------------------------------------------------------------

It uses 4-point 1D interpolation cubic (from that link in my comment above) to get new color just do this:

new_col = correction(old_col,ctrl,N);

this is how it looks:

interpolation example

the green arrows shows derivation error (always only on start and end point of whole curve). It can be corrected by adding 2 more control points one before and one after all others ...

[Notes]

color range is < 0.0 , 1.0 > so if you need other then just multiply the result and divide the input ...

[edit1] the start/end derivations fixed a little

float correction(float col,float *ctrl,int n)
    {
    float di=1.0/float(n-1);
    int i0,i1,i2,i3;
    float t,tt,ttt;
    float a0,a1,a2,a3,d1,d2;
    // find start control point
    col*=float(n-1);
    i1=col; col-=i1;
    i0=i1-1;
    i2=i1+1; if (i2>=n) i2=n-1;
    i3=i1+2;
    // compute interpolation coefficients
    if (i0>=0) d1=0.5*(ctrl[i2]-ctrl[i0]); else d1=ctrl[i2]-ctrl[i1];
    if (i3< n) d2=0.5*(ctrl[i3]-ctrl[i1]); else d2=ctrl[i2]-ctrl[i1];
    a0=ctrl[i1];
    a1=d1;
    a2=(3.0*(ctrl[i2]-ctrl[i1]))-(2.0*d1)-d2;
    a3=d1+d2+(2.0*(-ctrl[i2]+ctrl[i1]));
    // now interpolate new colro intensity
    t=col; tt=t*t; ttt=tt*t;
    t=a0+(a1*t)+(a2*tt)+(a3*ttt);
    return t;
    }

[edit2] just some clarification on the coefficients

they are all derived from this conditions:

y(t) = a0 + a1*t + a2*t*t + a3*t*t*t // direct value
y'(t) = a1 + 2*a2*t + 3*a3*t*t        // first derivation

now you have points y0,y1,y2,y3 so I chose that y(0)=y1 and y(1)=y2 which gives c0 continuity (value is the same in the joint points between curves)
now I need c1 continuity so i add y'(0) must be the same as y'(1) from previous curve.
for y'(0) I choose avg direction between points y0,y1,y2
for y'(1) I choose avg direction between points y1,y2,y3
These are the same for the next/previous segments so it is enough. Now put it all together:

y(0)  = y0           = a0 + a1*0 + a2*0*0 + a3*0*0*0
y(1)  = y1           = a0 + a1*1 + a2*1*1 + a3*1*1*1
y'(0) = 0.5*(y2-y0) = a1 + 2*a2*0 + 3*a3*0*0
y'(1) = 0.5*(y3-y1) = a1 + 2*a2*1 + 3*a3*1*1

And solve this system of equtions (a0,a1,a2,a3 = ?). You will get what I have in source code above. If you need different properties of the curve then just make different equations ...

[edit3] usage

pic1=pic0; // copy source image to destination pic is mine image class ...
for (y=0;y<pic1.ys;y++) // go through all pixels
 for (x=0;x<pic1.xs;x++)
    {
    float i;
     //  read, convert, write pixel 
    i=pic1.p[y][x].db[0]; i=255.0*correction(i/255.0,red control points,5); pic1.p[y][x].db[0]=i;
    i=pic1.p[y][x].db[1]; i=255.0*correction(i/255.0,green control points,5); pic1.p[y][x].db[1]=i;
    i=pic1.p[y][x].db[2]; i=255.0*correction(i/255.0,blue control points,5); pic1.p[y][x].db[2]=i;
    }

example

On top there are control points per R,G,B. On bottom left is original image and on bottom right is corrected image.

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • so is this changes the colour values when I move the given points on curve ? I don't want to give the colour manually but when I find the image colour better for my use on adjusting curve then I want to know that value, because I want to apply the same scenario for different colours and in the end thank you for your nice answer , I got some points which I want to learn. – AHF May 13 '14 at 09:31
  • correction converts single value to modified single value. To convert whole image you have to do 2 nested for loops (x,y) per all pixels get R,G,B value of pixel(x,y) call R=correction(R,...); G=correction(G,...),... and write the new R,G,B values to that pixel. now encapsulate this to some function like update_image(...) and call it each time you change any control point,... do not forget to redraw window after ... also each color has its own control points .... – Spektre May 13 '14 at 09:35
  • can you please tell me what this line is actually performing programetically `if (i0>=0) d1=0.5*(ctrl[i2]-ctrl[i0]); else d1=ctrl[i2]-ctrl[i1];` – AHF May 13 '14 at 10:16
  • d1,d2 are derivations (directions) of start and end of the cubic curve. they must be the same for neigbouring cubics so they merge together smoothly. so I set them as average of neighouring points derivations. the problem is that for first cubic there is no cubic before and therefore the average derivation d1 is not OK instead it takes full direction of only next point (no previous point used). The same goes for d2 on the end of th last cubic. – Spektre May 13 '14 at 10:24
  • this interpolation takes 4 control points and interpolate curve for t = < 0,1 > between 2nd and 3th point only !!! that means that for 5 points you have 4 cubic curves that are joined together !!! – Spektre May 13 '14 at 10:26
  • you can adjust interpolation to any shape ... you just change the way the coefficients are computed from the input control points – Spektre May 13 '14 at 10:28
  • Which language your using for code ? because i am not getting curves on my screen – AHF May 13 '14 at 15:20
  • mostly C++ but I use Borlad/Embarcadero IDE's so the gfx access is different – Spektre May 13 '14 at 15:23
  • Can you please check my final update , because i am still confusing with my code – AHF May 13 '14 at 15:31
  • @AHF lol you have to draw the curves first to see them. (or use some visual component). The same goes for image if you do not draw it onto screen or save it to some file you can not see what is happening – Spektre May 13 '14 at 15:39
  • i give up on finding function of drawing such curves in c++ – AHF May 13 '14 at 17:33
  • @AHF do not know how to do it in your IDE, I use VCL + GDI for low level stuff like this. Simple for (t=0;t<=1;t+=0.01) loop with Canvas->LineTo(t,correction(t,ctrl,5)); inside + some scaling from 0-1 to actual size and position on screen will do. + event on mouse to edit the actual points but as I see it that is too hard to explain for me when I use different programming environment. Look here http://stackoverflow.com/a/21699076/2521214 for more info – Spektre May 13 '14 at 18:47
  • when we edit the point then can we get their values when we like image on that point , so that next time we can draw the image by directly using that values rather then using the curve or drawing the curve ? – AHF May 13 '14 at 19:13
  • 1
    @AHF yes just print control points somewhere or save them to file ... and when needed reload – Spektre May 13 '14 at 20:59
  • The thing i am saying is look in your resultant image , there are two shape , one is original and one which is following the curves and colored , so if i have the right one image and i want to convert it into left one with curves once but when i get it through curve , i want to save that values of curve and then next time i only multiply that value with the image to get the resultant image without drawing curves , like we did for sepia filter we multiply `.131,.534,.272, .168,.686,.349, .189,.769,.393;` these values with original image we will get sepia image – AHF May 15 '14 at 10:57
  • as I wrote before for that just use saved coefficient values into ctrl tables for each color component. I am a bit lost from your text so in the case you want to get the original image (from converted on the right to original on the left) then you have to do inverse correction which can be done until some clamping occurs (also done by the same process but with different coefficients that can be computed algebraically) ... or visually just swap axises on the curve – Spektre May 15 '14 at 13:24