Click here to Skip to main content
Click here to Skip to main content

Cubic Bezier Spline Curves and Image Curve Adjustment

, 12 May 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
Cubic Bezier Spline Curves constructed and used in Image Curve adjustment

Introduction

This program is my second image editing tool --- Image Curve Adjustment. The first one is Free Image Transformation. This image tool includes two user controls, ImageCurve and Canvas. The control ImageCurve had been written before (Link). But they are totally different. The old one is based on the quadratic Bezier curve, but this new one is based on Cubic Bezier Spline and works more like Photoshop.

Cubic Bezier Spline

We know C# provides the method DrawCurve to draw curves, but we can't get coordinates of points on the curve drawn by DrawCurve. We have to construct a curve by ourselves for adjusting the image curve.

An easy way of making a controlled-design curve with many control points is to use Bezier spline curves. To specify a cubic Bezier curve, we need four control points P0, P1, P2, P3, and the curve is given by:

P(t)=(1-t)<sup>3</sup>P<sub>0</sub>+3(1-t)<sup>2</sup>tP<sub>1</sub>+3(1-t)t<sup>2</sup>P<sub>2</sub>+t<sup>3</sup>P<sub>3</sub>, for 0<=t<1 

Points P0 and P3 are on the curve, but P1, P2 usually are not on the curve. So we have to compute the control points from the data points for the individual pieces. For a mathematical background, please read this paper.

To get control points, first we made the augmented matrix [M|C] then row reduced completely to [I|P]:

private void getControlPoints()
{
    if (dataPoint != null && dataPoint.Length == 3)
    {
        controlPoint = new Vector[3];
        controlPoint[0] = dataPoint[0];
        controlPoint[1] =(6 * dataPoint[1] - dataPoint[0] - dataPoint[2])/4;
        controlPoint[2] = dataPoint[2];
    }
    if (dataPoint!=null && dataPoint.Length> 3)
    {
        int n = dataPoint.Length;
        controlPoint = new Vector[n];
        double[] diag = new double[n]; 	// tridiagonal matrix a(i , i)
        double[] sub = new double[n]; 	// tridiagonal matrix a(i , i-1)
        double[] sup = new double[n]; 	// tridiagonal matrix a(i , i+1)
         for (int i = 0; i < n; i++)
        {
            controlPoint[i] = dataPoint[i];
            diag[i] = 4;
            sub[i] = 1;
            sup[i] = 1;
        }
        controlPoint[1] = 6 * controlPoint[1] - controlPoint[0];
        controlPoint[n - 2] = 6 * controlPoint[n - 2] - controlPoint[n - 1];
        for (int i = 2; i < n - 2; i++)
        {
            controlPoint[i] = 6 * controlPoint[i];
        }
         // Gaussian elimination from row 1 to n-2
        for (int i = 2; i < n - 1; i++)
        {
            sub[i] = sub[i] / diag[i - 1];
            diag[i] = diag[i] - sub[i] * sup[i - 1];
            controlPoint[i] = controlPoint[i] - sub[i] * controlPoint[i - 1];
        }
         controlPoint[n - 2] = controlPoint[n - 2] / diag[n - 2];
         for (int i = n - 3; i >0; i--)
        {
            controlPoint[i] = (controlPoint[i] - sup[i] * controlPoint[i + 1]) / diag[i];
        }
    }

We can use the function P(t)=(1-t)3P0+3(1-t) 2tP1+3(1-t)t2P2+t3P3 to get the spline once we know the control points, the t is based on the precision of axis:

for (int i = 0; i < controlPoint.Length - 1; i++)
{
    Vector b1 = controlPoint[i] * 2.0 / 3.0 + controlPoint[i + 1] / 3.0;
    Vector b2 = controlPoint[i] / 3.0 + controlPoint[i + 1] * 2.0 / 3.0;
     int n = 1;
    if(isXcalibrated)
        n=(int)((dataPoint[i + 1].X - dataPoint[i].X) / precision);
    else n = (int)((dataPoint[i + 1].Y - dataPoint[i].Y) / precision);
    if (n == 0) n = 1;
    if (n < 0) n = -n;
    for (int j = 0; j < n; j++ )
    {
        double t = (double)j / (double)n;
        Vector v = (1 - t) * (1 - t) * (1 - t) * dataPoint[i] + 
		3 * (1 - t) * (1 - t) * t * b1 +
            3 * (1 - t) * t * t * b2 + t * t * t * dataPoint[i + 1];
        splinePoint.Add(v);
    }
}

In this program, the t is based on x-axis precision. For drawing spline curves on screen, I set t = 5...

YLScsDrawing.Geometry.Spline spline = new YLScsDrawing.Geometry.Spline();
        spline.ListDataPoint = keyPt;
        spline.Precision = 5;
        Point[] splinePt=spline.SplinePoint;
        g.DrawLines(new Pen(Color.Black), splinePt);
        g.DrawLine(new Pen(Color.Black), keyPt[keyPt.Count - 1], 
					splinePt[splinePt.Length - 1]);

and I set t = 1 for getting image level:

YLScsDrawing.Geometry.Spline sp = new YLScsDrawing.Geometry.Spline();
        sp.DataPoint = pts;
        sp.Precision = 1.0;
        Point[] spt=sp.SplinePoint;
        for (int i = 0; i < spt.Length; i++)
        {
            int n = spt[i].Y;
            if (n < 0) n = 0;
            if (n > 255) n = 255;
            level[pts[0].X + i] = (byte)n;
        }

Control ImageCurve

After construction of the cubic Bezier spline, we can specify a curve to edit image colors, its X axis as the image input level and Y axis as the image output level. That is the new user control ImageCurve.

This control can let the user add the curve's control point:

for (int i = 1; i < keyPt.Count; i++)
        {
            if (e.X > keyPt[i-1].X+20 && e.Y > 0 && 
			e.X < keyPt[i].X-20 && e.Y < this.Height)
            {
                keyPt.Insert(i, e.Location); // add a point
                drag = true;
                moveflag = i;
                this.Cursor = Cursors.Hand;
                Invalidate();
            }
        }	

It can also let the user remove the curve's control point:

if (drag && moveflag > 0 && moveflag < keyPt.Count - 1)
        {
            if (e.X > keyPt[moveflag - 1].X + 20 && e.X < keyPt[moveflag + 1].X - 20)
            {
                keyPt[moveflag] = e.Location;
            }
            else
            {
                keyPt.RemoveAt(moveflag); // remove a point
                drag = false;
            }
        }	

It works like PhotoShop.

Control Canvas

The last one I'll introduce here is the control Canvas. It looks like PictureBox. But it is zoomable and scrollable, and always keeps the picture in the center. It is another Zoomable and Scrollable PictureBox that I wrote a year ago. Hope you like both.

Thanks

Thanks for trying this program. Any suggestion will be much appreciated.

History

  • May 12, 2009: First posted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

YLS CS

Unknown
No Biography provided

Comments and Discussions

 
Questionask question: how does the getControlPoints function works Pinmemberluluio200029-Nov-12 22:19 
QuestionGaussian elimination PinmemberKiTann26-Apr-12 20:46 
GeneralMy vote of 5 Pinmembermanoj kumar choubey20-Feb-12 20:55 
GeneralThanks! PinmemberMember 24770538-Dec-09 23:36 
Generalvery interesting PinmemberJeffCirceo13-May-09 5:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411023.1 | Last Updated 12 May 2009
Article Copyright 2009 by YLS CS
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid