Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Cubic Bezier Spline Curves and Image Curve Adjustment

0.00/5 (No votes)
12 May 2009 2  
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)3P0+3(1-t)2tP1+3(1-t)t2P2+t3P3, 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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here