Regarding this Opencv Tutorial, the following C++ code snippet:
vector<Vec4i> lines;
// Find hough lines
HoughLinesP(edges, lines, 1, CV_PI / 180, 100, 100, 10);
// Prepare blank mat with same sizes as image
Mat Blank(image.rows, image.cols, CV_8UC3, Scalar(0, 0, 0));
// Draw lines into image and Blank images
for (size_t i = 0; i < lines.size(); i++)
{
Vec4i l = lines[i];
line(image, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 0), 2, CV_AA);
line(Blank, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(255, 255, 255), 2, CV_AA);
}
has been translated into C# like this:
private void openFileDialogButton_Click(object sender, EventArgs e)
{
try
{
const string filename = #"E:\___MSc in Computer Systems & Network\EMSC1,2,3\lena.png";
Mat image = Cv2.ImRead(filename, LoadMode.GrayScale);
Mat edges = new Mat();
Cv2.Canny(image, edges, 95, 100);
Cv2.ImWrite("edges.jpg", edges);
Mat dx = new Mat();
Mat dy = new Mat();
Cv2.Sobel(edges, dx, MatType.CV_32F, 1, 0);
Cv2.Sobel(edges, dy, MatType.CV_32F, 0, 1);
Cv2.ImWrite("dx.jpg", dx);
Cv2.ImWrite("dy.jpg", dy);
Mat linesssssss = new Mat();
Cv.HoughLines2((CvArr)linesssssss.ToIplImage(),
(CvMat)edges,
HoughLinesMethod.Standard,
1,
Math.PI / 180,
100, 100, 10);
//Cv2.HoughLinesP(edges, lines, 1, Math.PI / 180, 100, 100, 10);
List<Vec4i> lines = IntPtrToList(linesssssss.Data);
Mat Blank = new Mat(image.Rows, image.Cols, MatType.CV_8UC3, new Scalar(0, 0, 0));
for (int i = 0; i < lines.Count; i++)
{
Vec4i l = lines[i];
Cv2.Line(image, new OpenCvSharp.CPlusPlus.Point(l[0], l[1]), new OpenCvSharp.CPlusPlus.Point(l[2], l[3]), new Scalar(0, 0, 0), 2, Cv.AA);
Cv2.Line(Blank, new OpenCvSharp.CPlusPlus.Point(l[0], l[1]), new OpenCvSharp.CPlusPlus.Point(l[2], l[3]), new Scalar(255, 255, 255), 2, Cv.AA);
}
//Cv2.ImWrite("houg.jpg", image);
//Cv2.ImShow("Edges", image);
//Cv2.ImWrite("houg2.jpg", Blank);
//Cv2.ImShow("Edges Structure", Blank);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Unfortunately, it seems to be not working. It is giving the following exception:
Nonpositive cols or rows
This is the original image from the article:
Based on Github's sample code for HoughLinesP:
static void Main(string[] args)
{
string filename = "Alliance.jpg";
Mat imageIn = Cv2.ImRead(filename, ImreadModes.GrayScale).Resize(new Size(800, 600));
Mat edges = new Mat();
Cv2.Canny(imageIn, edges, 95, 100);
//HoughLinesP
LineSegmentPoint[] segHoughP = Cv2.HoughLinesP(edges, 1, Math.PI / 180, 100, 100, 10);
Mat imageOutP = imageIn.EmptyClone();
foreach (LineSegmentPoint s in segHoughP)
imageOutP.Line(s.P1, s.P2, Scalar.White, 1, LineTypes.AntiAlias, 0);
using (new Window("Edges", WindowMode.AutoSize, edges))
using (new Window("HoughLinesP", WindowMode.AutoSize, imageOutP))
{
Window.WaitKey(0);
}
}
Related
I have an ellipse which is growing with time.
To detect the ellipse I have used CvInvoke.AbsDiff method .
and I gets an image like this
I want to put this ellipse to fit-ellipse method and gain the radius es of it.
This is the approach I took.
CvInvoke.AbsDiff(First, img, grayscale);
CvInvoke.CvtColor(grayscale, grayscale, ColorConversion.Bgr2Gray);
CvInvoke.GaussianBlur(grayscale, grayscale, new System.Drawing.Size(11, 11), 15, 15);
CvInvoke.Threshold(grayscale, grayscale, Convert.ToInt16(Threshold), Convert.ToInt16(Threshold * 2), ThresholdType.Binary );
Mat element = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle, new System.Drawing.Size(3, 3), new System.Drawing.Point(-1, -1));
CvInvoke.Dilate(grayscale, grayscale, element, new System.Drawing.Point(-1, 1), 5, BorderType.Constant, new MCvScalar(255, 255, 255));
CvInvoke.Canny(grayscale, grayscale, Threshold, MaxThreshold * 2, 3);
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
CvInvoke.FindContours(grayscale, contours, null, RetrType.Ccomp, ChainApproxMethod.ChainApproxTc89Kcos);
double area = 0;
double ContourArea = 0;
int contour = 0;
int CenterX;
int CenterY;
for (int i = 0; i < contours.Size; i++)
{
System.Drawing.Rectangle rec = CvInvoke.BoundingRectangle(contours[i]);
output.Draw(rec, new Bgr(255, 0, 255), 2);
CenterX = ((rec.Width) / 2) + rec.X;
CenterY = ((rec.Height) / 2) + rec.Y;
ContourArea = rec.Width * rec.Height; ;
if ((HWidth - CenterFactor) < CenterX && CenterX < (HWidth + CenterFactor) && (HHeight - CenterFactor) < CenterY && CenterY< (HHeight + CenterFactor) )
{
if (ContourArea < 1000000)
if (area < ContourArea)
{
area = ContourArea;
contour = i;
}
}
}
//if (contour == 0)
//{
// return arr;
//}
System.Drawing.Rectangle rect = CvInvoke.BoundingRectangle(contours[contour]);
output.Draw(rect, new Bgr(0, 255, 0), 3);
But i am not getting the best ellipse everytime. This is the contour which I'm getting
Is there any other way to do this?
Although this method is not completely perfect, this could be a possible direction that you could take.
Mat input = CvInvoke.Imread(#"C:\Users\ajones\Desktop\Images\inputImg.png", ImreadModes.AnyColor);
Mat input2 = input.Clone();
Mat thresh = new Mat();
CvInvoke.GaussianBlur(input, thresh, new System.Drawing.Size(7, 7), 10, 10);
CvInvoke.Threshold(thresh, thresh, 3, 10, ThresholdType.Binary);
CvInvoke.Imshow("The Thresh", thresh);
CvInvoke.WaitKey(0);
Mat output = new Mat();
CvInvoke.CvtColor(thresh, output, ColorConversion.Bgr2Gray);
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
CvInvoke.FindContours(output, contours, null, RetrType.External, ChainApproxMethod.ChainApproxSimple);
CvInvoke.DrawContours(input, contours, -1, new MCvScalar(0, 255, 0), 3, LineType.FourConnected);
CvInvoke.Imshow("The Image", input);
CvInvoke.WaitKey(0);
int biggest = 0;
int index = 0;
for (int i = 0; i<contours.Size; i++)
{
if (contours[i].Size > biggest)
{
biggest = contours[i].Size;
index = i;
}
}
CvInvoke.DrawContours(input2, contours, index, new MCvScalar(0, 255, 0), 3, LineType.FourConnected);
CvInvoke.Imshow("The Image2", input2);
CvInvoke.WaitKey(0);
First blur the image using a Gaussian filter.
Then, using a binary threshold.
Afterwards, find all contours on the image
Finally, all you would need to do is just sort through your contours until you found the biggest one.
Like I said, its not completely perfect, but I should help push you in the right direction.
Here's what I do to Rotate and Scale. But so far, if the rotation is working, the scaling isn't and vice versa. So how do I combine rotation and scaling in one method? I feels like they can't coexist using my code.
....................................................................................................................................................................
Here's what I have for now:
Image Drawing:
public LayerClass ImageDrawing(LayerClass.Type img, Bitmap bm, Rectangle imgRect, String filepath, int angle, PaintEventArgs e)
{
bm = ImageClass.GrayscaleImage(bm);
bm = MakeTransparentImage(bm);
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
bm = RotateImage(bm, angle, imgRect);
imgRect = new Rectangle((int)(Shape.center.X - (bm.Width / 2)), (int)(Shape.center.Y - (bm.Height / 2)), (int)bm.Width, (int)bm.Height);
e.Graphics.DrawImage(bm, imgRect);
this.imageBitmap = bm;
this.filePath = filePath;
this.rotationAngle = angle;
this.location = location;
this.imageRect = imgRect;
return new LayerClass(LayerClass.Type.Image, this, filePath, imgRect);
}
Rotation:
public static Bitmap RotateImage(Bitmap bitmap, float angle, Rectangle rect)
{
Matrix matrix = new Matrix();
matrix.Translate(bitmap.Width / -2, bitmap.Height / -2, MatrixOrder.Append);
matrix.RotateAt(angle, new System.Drawing.Point(0, 0), MatrixOrder.Append);
using (GraphicsPath graphicsPath = new GraphicsPath())
{
graphicsPath.AddPolygon(new System.Drawing.Point[] { new System.Drawing.Point(0, 0), new System.Drawing.Point(bitmap.Width, 0), new System.Drawing.Point(0, bitmap.Height) });
graphicsPath.Transform(matrix);
System.Drawing.PointF[] points = graphicsPath.PathPoints;
rect = boundingBox(bitmap, matrix);
Bitmap resultBitmap = new Bitmap(rect.Width, rect.Height);
using (Graphics g = Graphics.FromImage(resultBitmap))
{
Matrix matrix2 = new Matrix();
matrix2.Translate(resultBitmap.Width / 2, resultBitmap.Height / 2, MatrixOrder.Append);
g.Transform = matrix2;
g.DrawImage(bitmap, points);
return resultBitmap;
}
}
}
Scaling:
private void trackBar_ScaleImg_Scroll(object sender, EventArgs e)
{
if(rb_BothImage.Checked)
{
if (imgRect.Width > imgRect.Height)
{
imgRect.Width = trackBar_ScaleImg.Value;
imgRect.Height = (int)(trackBar_ScaleImg.Value / aspect);
ImageBitmap = new Bitmap(ImageBitmap, new Size(imgRect.Width, imgRect.Height));
}
else if (imgRect.Height > imgRect.Width)
{
imgRect.Height = trackBar_ScaleImg.Value; //64mm
imgRect.Width = (int)(trackBar_ScaleImg.Value / aspect);
ImageBitmap = new Bitmap(ImageBitmap, new Size(imgRect.Width, imgRect.Height));
}
else if (imgRect.Width == imgRect.Height)
{
imgRect.Width = trackBar_ScaleImg.Value;
imgRect.Height = trackBar_ScaleImg.Value;
}
imgRect.X = (int)(Shape.center.X - (imgRect.Width / 2));
imgRect.Y = (int)(Shape.center.Y - (imgRect.Height / 2));
ImageBitmap = new Bitmap(ImageBitmap, new Size(imgRect.Width, imgRect.Height));
}
pictureBox_Canvass.Invalidate();
}
You can add another matrix transformation for scaling: matrix.Scale(2, 2, MatrixOrder.Append);
Matrix matrix = new Matrix();
matrix.Translate(bitmap.Width / -2, bitmap.Height / -2, MatrixOrder.Append);
matrix.RotateAt(angle, new System.Drawing.Point(0, 0), MatrixOrder.Append);
matrix.Scale(2, 2, MatrixOrder.Append);
using (GraphicsPath graphicsPath = new GraphicsPath())
I trying to identify text in an image.
Actually I'm trying to identify the text positions in the image then convert it to text.
I found a some code written on c++ and I am trying to convert it to c#.
Can you help me please?
Extracting text OpenCV
std::vector<cv::Rect> detectLetters(cv::Mat img)
{ std::vector<cv::Rect> boundRect;[enter image description here][1]
cv::Mat img_gray, img_sobel, img_threshold, element;
cvtColor(img, img_gray, CV_BGR2GRAY);
cv::Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT);
cv::threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);
element = getStructuringElement(cv::MORPH_RECT, cv::Size(10, 15) );
cv::morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element); //Does the trick
std::vector< std::vector< cv::Point> > contours;
cv::findContours(img_threshold, contours, 0, 1);
std::vector<std::vector<cv::Point> > contours_poly( contours.size() );
for( int i = 0; i < contours.size(); i++ )
if (contours[i].size()>80)
{
cv::approxPolyDP( cv::Mat(contours[i]), contours_poly[i], 17, true );
cv::Rect appRect( boundingRect( cv::Mat(contours_poly[i]) ));
if (appRect.width>appRect.height)
boundRect.push_back(appRect);
}
return boundRect;
}
and i try to convert it to c#, but it didn't work
private List<Rectangle> detectLetters(IntPtr img)
{
//cvtColor(img, img_gray, CV_BGR2GRAY);
List<Rectangle> boundRect = new List<Rectangle>();
//cv::Mat img_gray, img_sobel, img_threshold, element;
IntPtr
img_gray = IntPtr.Zero,
img_sobel= IntPtr.Zero,
img_threshold= IntPtr.Zero,
img_tmp = IntPtr.Zero,
element= IntPtr.Zero;
//cvtColor(img, img_gray, CV_BGR2GRAY);
CvInvoke.cvCvtColor(img, img_gray,COLOR_CONVERSION.CV_BGR2GRAY); //CV_BGR2GRAY);
//cv::Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT);
CvInvoke.cvSobel(img_gray, img_sobel, 0, 1, 1);//, 3, 1, 0, cv.BORDER_DEFAULT);
//cv.threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
CvInvoke.cvThreshold(img_sobel, img_threshold, 0, 255, THRESH.CV_THRESH_BINARY|THRESH.CV_THRESH_OTSU);
//element = getStructuringElement(cv.MORPH_RECT, cv.Size(10, 15));
element = CvInvoke.cvCreateStructuringElementEx(1,1,10,15,CV_ELEMENT_SHAPE.CV_SHAPE_RECT,element);// GetStructuringElement(
//cv.morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element); //Does the trick
CvInvoke.cvMorphologyEx(img_threshold,img_threshold,img_tmp,element,CV_MORPH_OP.CV_MOP_CLOSE,1);
//List<List<cv.Point>> contours = new List<List<cv.Point>>();
var contours = new List<IntPtr>();
//cv.findContours(img_threshold, contours, 0, 1);
CvInvoke.cvFindContours(img_threshold, element,ref ((IntPtr)contours[0]), 1, RETR_TYPE.CV_RETR_EXTERNAL, CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_NONE,new Point(0,0));
//std::vector<std::vector<cv::Point> > contours_poly( contours.size() );
var contours_poly = new List<List<Point>>(contours.Count);
//for( int i = 0; i < contours.size(); i++ )
for (int i = 0; i < contours.Count; i++)
{
//if (contours[i].size()>80)
if (contours[i].ToInt32() > 80)
{
//cv.approxPolyDP(Emgu.CV.Matrix<>(contours[i]), contours_poly[i], 17, true);
CvInvoke.cvApproxPoly(contours[i], 17,contours_poly[i],APPROX_POLY_TYPE.CV_POLY_APPROX_DP, 1,1);
//cv::Rect appRect( boundingRect( cv::Mat(contours_poly[i]) ));
Rectangle appRect = new Rectangle(CvInvoke.cvBoundingRect(contours_poly[i],false));
//if (appRect.width>appRect.height)
if (appRect.width > appRect.height)
{
//boundRect.push_back(appRect);
boundRect.Add(appRect);
}
}
}
//return boundRect;
return boundRect;
}
Depending on what the size of the text you're looking for, you may have to play around with the variables for element size and ApproxPolyDP but this code is pretty close to the original but in OpenCvSharp lingo.
static List<Rect> RunTextRecog(string inFile)
{
List<Rect> boundRect = new List<Rect>();
using (Mat img = new Mat(inFile))
using (Mat img_gray = new Mat())
using (Mat img_sobel = new Mat())
using (Mat img_threshold = new Mat())
{
Cv2.CvtColor(img, img_gray, ColorConversionCodes.BGR2GRAY);
Cv2.Sobel(img_gray, img_sobel, MatType.CV_8U, 1, 0, 3, 1, 0, BorderTypes.Default);
Cv2.Threshold(img_sobel, img_threshold, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);
using (Mat element = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(10, 15)))
{
Cv2.MorphologyEx(img_threshold, img_threshold, MorphTypes.Close, element);
Point[][] edgesArray = img_threshold.Clone().FindContoursAsArray(RetrievalModes.External, ContourApproximationModes.ApproxNone);
foreach (Point[] edges in edgesArray)
{
Point[] normalizedEdges = Cv2.ApproxPolyDP(edges, 17, true);
Rect appRect = Cv2.BoundingRect(normalizedEdges);
boundRect.Add(appRect);
}
}
}
return boundRect;
}
I need to create a thumbnail image with transparent rounded corners. Before this requirement I used the simple:
using (var b = new Bitmap(dataSize.Width, dataSize.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(b))
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
g.DrawImage(original, 0, 0, b.Width, b.Height);
}
which produced great results (for reductions to approx 50x50px) even without any interpolation. Now with the rounded corners I used the following algorithm (the 4 'if's are there so I can have variable roundness on each of the 4 corners):
using (var b = new Bitmap(dataSize.Width, dataSize.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(b))
{
// set interpolation
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
// transformation to scale and shift the brush
var transform = new Matrix();
transform.Scale(ratio, ratio);
transform.Translate(start.X / ratio, start.Y / ratio);
var brush = new TextureBrush(original) { Transform = transform };
// create path for stamping the iamge
var gp = new GraphicsPath(FillMode.Winding);
if (descriptor.CornerRadiusLeftTop > 0)
gp.AddArc(descriptor.GetLeftTopCorner(b.Size), 180, 90);
else
gp.AddLine(-1, -1, -1, -1);
if (descriptor.CornerRadiusRightTop > 0)
gp.AddArc(descriptor.GetRightTopCorner(b.Size), 270, 90);
else
gp.AddLine(b.Width + 1, -1, b.Width + 1, -1);
if (descriptor.CornerRadiusRightBottom > 0)
gp.AddArc(descriptor.GetRightBottomCorner(b.Size), 0, 90);
else
gp.AddLine(b.Width + 1, b.Height + 1, b.Width + 1, b.Height + 1);
if (descriptor.CornerRadiusLeftBottom > 0)
gp.AddArc(descriptor.GetLeftBottomCorner(b.Size), 90, 90);
else
gp.AddLine(-1, b.Height + 1, -1, b.Height + 1);
// stamp the image with original
g.FillPath(brush, gp);
}
but this approach produced ugly un-interpolated imaged with really jagged gradients. Is there a better approach to create transparent thumbnails or are there some settings I could use to improve the output?
I've written a blog post which explains exactly how to do this.
http://danbystrom.se/2008/08/24/soft-edged-images-in-gdi/
If you look at the first sample images, you're seeing 5) and I show how to arrive at 6). Good luck.
I would first copy the image to second one with rounded corners, then use GetThumbnailImage to scale it down.
I've used a modified TransferChannel method to add mask, which is not unsafe as in blog post by danbystrom.
public static void TransferChannel(Bitmap src, Bitmap dst, ChannelARGB sourceChannel, ChannelARGB destChannel)
{
if (src.Size != dst.Size)
throw new ArgumentException();
var r = new Rectangle(Point.Empty, src.Size);
var bdSrc = src.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var bdDst = dst.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
var s = bdSrc.Stride * src.Height;
var baSrc = new byte[s];
var baDst = new byte[s];
Marshal.Copy(bdSrc.Scan0, baSrc, 0, s);
Marshal.Copy(bdDst.Scan0, baDst, 0, s);
for (var counter = 0; counter < baSrc.Length; counter += 4)
baDst[counter + (int)destChannel] = baSrc[counter + (int)sourceChannel];
Marshal.Copy(baDst, 0, bdDst.Scan0, s);
src.UnlockBits(bdSrc);
dst.UnlockBits(bdDst);
}
And my method to resize and make rounded corners is:
var b = new Bitmap(dataSize.Width, dataSize.Height, PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(b))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(original, start.X, start.Y, original.Width * ratio, original.Height * ratio);
if (hasRoundedCorners)
using (var mask = CreateMask(dataSize, radius))
TransferChannel(mask, b, ChannelARGB.Blue, ChannelARGB.Alpha);
}
return b;
I have been trying to draw an annulus (ring with thickness) with a transparent hole and a gradient rim in C# with very little success. Does anyone have any suggestions on how to do this?
here's a nice Blend Utility
Here's the Final result - thanks to BlueMonkMN
Rectangle GetSquareRec(double radius, int x, int y)
{
double r = radius;
double side = Math.Sqrt(Math.Pow(r, 2) / 2);
Rectangle rec = new Rectangle(x - ((int)side), y - ((int)side), (int)(side * 2) + x, (int)(side * 2) + y);
return rec;
}
void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics gTarget = e.Graphics;
gTarget.SmoothingMode = SmoothingMode.AntiAlias;
GraphicsPath pTemp = new GraphicsPath();
Rectangle r = GetSquareRec(200, 225, 225);
pTemp.AddEllipse(r);
pTemp.AddEllipse(GetSquareRec(50, 225, 225));
Color[] colors = new Color[5];
colors[0] = Color.FromArgb(192, 192, 192);
colors[1] = Color.FromArgb(105, 0, 0);
colors[2] = Color.FromArgb(169, 169, 169);
colors[3] = Color.FromArgb(0, 0, 0);
colors[4] = Color.FromArgb(0, 0, 0);
float[] positions = new float[5];
positions[0] = 0f;
positions[1] = 0.1f;
positions[2] = 0.35f;
positions[3] = 0.5f;
positions[4] = 1f;
ColorBlend Cb = new ColorBlend();
Cb.Colors = colors;
Cb.Positions = positions;
PathGradientBrush pgb = new PathGradientBrush(pTemp);
pgb.InterpolationColors = Cb;
pgb.CenterPoint = new PointF(r.X + (r.Width / 2), r.Y + (r.Height / 2));
gTarget.FillPath(pgb, pTemp);
}
http://www.freeimagehosting.net/uploads/th.515733e62e.jpg
This is how I did it in the Scrolling Game Development Kit:
pTemp = new GraphicsPath();
pTemp.AddEllipse(Start.X, Start.Y, End.X - Start.X, End.Y - Start.Y);
pTemp.AddEllipse((Start.X * 3 + End.X) / 4f,
(Start.Y * 3 + End.Y) / 4f,
(End.X - Start.X) / 2f,
(End.Y - Start.Y) / 2f);
PathGradientBrush pgb = new PathGradientBrush(pTemp);
Blend b = new Blend();
b.Factors = new float[] { 0, 1, 1 };
b.Positions = new float[] { 0, .5F, 1 };
pgb.Blend = b;
pgb.CenterColor = ((SolidBrush)CurrentBrush).Color;
pgb.SurroundColors = new Color[] {CurrentPen.Color};
gTarget.FillPath(pgb, pTemp);
pgb.Dispose();
pTemp.Dispose();
(source: enigmadream.com)
I edited the original SGDK code for this sample because originally I wasn't smart enough to scale the gradient to exclude the hole, but now I guess I am :).
If you would rather see the gradient like this:
(source: enigmadream.com)
Then change the blend code to look like this:
Blend blend = new Blend();
blend.Factors = new float[] { 0, 1, 0, 0 };
blend.Positions = new float[] { 0, 0.25F, .5F, 1 };
pgb.Blend = blend;
You may use two calls to Graphics.DrawArc combined, drawing the top and bottom or left and right portions of the annulus, one portion at a time.