## 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 **P**_{0}, **P**_{1}, **P**_{2}, **P**_{3}, and the curve is given by:

**P**(t)=(1-t)<sup>3</sup>**P<sub>0</sub>**+3(1-t)<sup>2</sup>t**P<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 **P**_{0} and **P**_{3} are on the curve, but **P**_{1}, **P**_{2} 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];
double[] sub = new double[n];
double[] sup = new double[n];
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];
}
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)^{3}**P**_{0}+3(1-t) ^{2}t**P**_{1}+3(1-t)t^{2}**P**_{2}+t^{3}**P**_{3} 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);
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);
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