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];
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)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);
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