Crop off Parts of the Image that does not include the Face - c#

I have detected the Face in an Image(Only 1 Person) and have the coordinates of the Face Rectangle.
Since the image can be of any size,I need only the part of the image that is important(head.shoulders).What intent to do is extend the bounds of the detected rectangle by some factor so that the important parts are included.
Is this the right approach?
Update:
I have tried this .. but its not giving the correct result.Note that i have changed 1.7 to 2 since it only takes integer arguments.And Top and Left are readonly properties.
foreach (Rectangle f in objects)
{
int x, y;
x = f.Top - (f.Height / 8);
y = f.Left - (f.Width / 2);
Rectangle myrect = new Rectangle(x, y, f.Width * 2, f.Height * 2);
g.DrawRectangle(Pens.Gray, myrect);
}
Detected Face Rectangle
Top----->62
Right----->470
Left----->217
Bottom----->315
Extended Rectangle as per answer
Top----->91
Right----->537
Left----->31
Bottom----->597
Extended rectangle

As my previous answer as off-topic, I will write my correct answer here:
As I am not completely familiar with Emgu CV, I would have the following approaches:
As Emgu CV is open-source, you could spend restless nights and change the code inside the libraries and recompile them etc.
or (my preferable approach):
You think about it biologically, meaning:
You know the position and size of your face rectangle. If you also know body proporions, you can calculate the estimated width of the shoulders and there vertical offset (relative to the face's center).
More details for the biological approach:
Imagine fig. №1 begin true, and imagine that you have the following image and face rectangle:
Bitmap
| .Width == 100
| .Height == 160
Face // type: System.Drawing.Rectangle
| .Top == 20
| .Left == 50
| .Width == 60
| .Height == 60
then, according to the provided image, the new Rectangle should be:
f := Face // face rectangle
Face_and_Shoulder
| .Top = f.Top - (f.Height / 8)
| .Left = f.Left - (f.Width / 2)
| .Width = f.Width * 2
| .Height = f.Height * 1.7
which would result in the following values:
Face_and_Shoulder
| .Top == 12.5
| .Left == 20
| .Width == 120
| .Height == 102
The resulted rectangle (Face_and_Shoulder) should include the shoulder and hair etc. when drawing it over your image.
This method has however a minor drawback: It will not work, if the face is rotated by a certain number of degrees (I believe more than 5..10°).
To calculated the respective rectangle, I would advise you to use this code (you seem to have confused X and Y in your code sample):
foreach (Rectangle f in objects)
{
float x = f.Left - (f.Width / 2f);
float y = f.Top - (f.Height / 8f);
Rectangle myrect = new Rectangle((int)x, (int)y, f.Width * 2, (int)(f.Height * 1.3));
g.DrawRectangle(Pens.Gray, myrect);
}
fig. №1 (source: http://www.idrawdigital.com/wp-content/uploads/2009/01/prop_var.gif)

I would create a second bitmap and draw the first one into the second one as follows:
Bitmap source = Image.FromFile("/my/path/to/myimage.png") as Bitmap;
Rectangle facerectangle = /* your face detection logic */;
Bitmap target = new Bitmap(facerectangle.Width, facerectangle.Height);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(source, new Rectangle(0, 0, target.Width, target.Height),
facerectangle, GraphicsUnit.Pixel);
}
The code should be rather easy to understand :)
You first load your bitmap source, then create your rectangle using your face recognition logic and create the bitmap target, in which you draw the first segment using GDI+'s DrawImage.

Related

How to visually connect 2 circles?

We know 2 circle's x and y center position, and the radius is the same. I want to visually connect the circles without looping the draw ellipse for each point on the line what connects the 2 circle's center.
From this:
To this:
Code:
int radius = 75;
int x1 = 100;
int y1 = 200;
int x2 = 300;
int y2 = 100;
g.FillEllipse(Brushes.Blue, new Rectangle(x1 - radius / 2, y1 - radius / 2, radius, radius));
g.FillEllipse(Brushes.Blue, new Rectangle(x2 - radius / 2, y2 - radius / 2, radius, radius));
A solution for when the Circles don't have the same Diameter.
The first information needed is the distance between the Centers of two Circles.
To calculate it, we use the Euclidean distance applied to a Cartesian plane:
Where (x1, y1) and (x2, y2) are the coordinates of the Centers of two Circles.
We also need to know the Direction (expressed as a positive or negative value): the calculated [Distance] will always be positive.
in C# it, it can be coded as:
float Direction = (Circle1Center.X > Circle2Center.X) ? -1 : 1;
float Distance = (float)Math.Sqrt(Math.Pow(Circle1Center.X - Circle2Center.X, 2) +
Math.Pow(Circle1Center.Y - Circle2Center.Y, 2));
Distance *= Direction;
Now, we have the Distance between the Centers of two Circles, which also expresses a direction.
We also need to know how this virtual line - connecting the two Centers - is rotated in relation to our drawing plane. In the figure below, the Distance can be viewed as the hypotenuse of a right triangle h = (A, B). The C angle is determined by the intersection of the straight lines, parallel to the axis, that cross the Centers of the Circles.
We need to calculate the angle Theta (θ).
Using the Pythagorean theorem, we can derive that the Sine of the angle Theta is Sinθ = b/h (as in the figure)
Using the Circles' Centers coordinates, this can be coded in C# as:
(Distance is the triangle's hypotenuse)
float SinTheta = (Math.Max(Circle1Center.Y, Circle2Center.Y) -
Math.Min(Circle1Center.Y, Circle2Center.Y)) / Distance;
SinTheta expresses an angle in Radians. We need the angle expressed in Degrees: the Graphics object uses this measure for its world transformation functions.
float RotationAngle = (float)(Math.Asin(SinTheta) * (180 / Math.PI));
Now, we need to build a Connector, a shape that links the 2 Circles. We need a Polygon; a Rectangle can't have different pairs of sides (we are considering Circles with different Diameters).
This Polygon will have the longer sides = to the Distance between the Circles Centers, the shorter sides = to the Circles Diameters.
To build a Polygon, we can use both Graphics.DrawPolygon and GraphicsPath.AddPolygon. I'm choosing the GraphicsPath method, because a GraphicsPath can hold more that one shape and these shapes can interact, in a way.
To connect the 2 considered Circles with a Polygon, we need to rotate the Polygon using the RotationAngle previously calculated.
A simple way to perform the rotation, is to move the world coordinates to the Center of one of the Circles, using the Graphics.TranslateTransform method, then rotate the new coordinates, using Graphics.RotateTransform.
We need to draw our Polygon positioning one of the short sides - corresponding to the Diameter of the Circle which is the center of the coordinates transformation - in the center of the Cirle. Hence, when the rotation will be applied, it's short side it will be in the middle of this transformation, anchored to the Center.
Here, figure 3 shows the positioning of the Polygon (yellow shape) (ok, it looks like a rectangle, never mind);in figure 4 the same Polygon after the rotation.
Notes:
As TaW pointed out, this drawing needs to be performed using a SolidBrush with a non-transparent Color, which is kind of disappointing.
Well, a semi-transparent Brush is not forbidden, but the overlapping shapes will have a different color, the sum of the transparent colors of the intersections.
It is however possible to draw the shapes using a semi-transparent Brush without a Color change, using the GraphicsPath ability to fill its shapes using a color that is applied to all the overlapping parts. We just need to change the default FillMode (see the example in the Docs), setting it to FillMode.Winding.
Sample code:
In this example, two couples of Circles are drawn on a Graphics context. They are then connected with a Polygon shape, created using GraphicsPath.AddPolygon().
(Of course, we need to use the Paint event of a drawable Control, a Form here)
The overloaded helper function accepts both the Circles' centers position, expressed as a PointF and a RectangleF structure, representing the Circles bounds.
This is the visual result, with full Colors and using a semi-transparent brush:
using System.Drawing;
using System.Drawing.Drawing2D;
private float Radius1 = 30f;
private float Radius2 = 50f;
private PointF Circle1Center = new PointF(220, 47);
private PointF Circle2Center = new PointF(72, 254);
private PointF Circle3Center = new PointF(52, 58);
private PointF Circle4Center = new PointF(217, 232);
private void form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.CompositingQuality = CompositingQuality.GammaCorrected;
e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
DrawLinkedCircles(Circle1Center, Circle2Center, Radius1, Radius2, Color.FromArgb(200, Color.YellowGreen), e.Graphics);
DrawLinkedCircles(Circle3Center, Circle4Center, Radius1, Radius2, Color.FromArgb(200, Color.SteelBlue), e.Graphics);
//OR, passing a RectangleF structure
//RectangleF Circle1 = new RectangleF(Circle1Center.X - Radius1, Circle1Center.Y - Radius1, Radius1 * 2, Radius1 * 2);
//RectangleF Circle2 = new RectangleF(Circle2Center.X - Radius2, Circle2Center.Y - Radius2, Radius2 * 2, Radius2 * 2);
//DrawLinkedCircles(Circle1, Circle2, Color.FromArgb(200, Color.YellowGreen), e.Graphics);
}
Helper function:
public void DrawLinkedCircles(RectangleF Circle1, RectangleF Circle2, Color FillColor, Graphics g)
{
PointF Circle1Center = new PointF(Circle1.X + (Circle1.Width / 2), Circle1.Y + (Circle1.Height / 2));
PointF Circle2Center = new PointF(Circle2.X + (Circle2.Width / 2), Circle2.Y + (Circle2.Height / 2));
DrawLinkedCircles(Circle1Center, Circle2Center, Circle1.Width / 2, Circle2.Width / 2, FillColor, g);
}
public void DrawLinkedCircles(PointF Circle1Center, PointF Circle2Center, float Circle1Radius, float Circle2Radius, Color FillColor, Graphics g)
{
float Direction = (Circle1Center.X > Circle2Center.X) ? -1 : 1;
float Distance = (float)Math.Sqrt(Math.Pow(Circle1Center.X - Circle2Center.X, 2) +
Math.Pow(Circle1Center.Y - Circle2Center.Y, 2));
Distance *= Direction;
float SinTheta = (Math.Max(Circle1Center.Y, Circle2Center.Y) -
Math.Min(Circle1Center.Y, Circle2Center.Y)) / Distance;
float RotationDirection = (Circle1Center.Y > Circle2Center.Y) ? -1 : 1;
float RotationAngle = (float)(Math.Asin(SinTheta) * (180 / Math.PI)) * RotationDirection;
using (GraphicsPath path = new GraphicsPath(FillMode.Winding))
{
path.AddEllipse(new RectangleF(-Circle1Radius, -Circle1Radius, 2 * Circle1Radius, 2 * Circle1Radius));
path.AddEllipse(new RectangleF(-Circle2Radius + (Math.Abs(Distance) * Direction),
-Circle2Radius, 2 * Circle2Radius, 2 * Circle2Radius));
path.AddPolygon(new[] {
new PointF(0, -Circle1Radius),
new PointF(0, Circle1Radius),
new PointF(Distance, Circle2Radius),
new PointF(Distance, -Circle2Radius),
});
path.AddEllipse(new RectangleF(-Circle1Radius, -Circle1Radius, 2 * Circle1Radius, 2 * Circle1Radius));
path.AddEllipse(new RectangleF(-Circle2Radius + (Math.Abs(Distance) * Direction),
-Circle2Radius, 2 * Circle2Radius, 2 * Circle2Radius));
path.CloseAllFigures();
g.TranslateTransform(Circle1Center.X, Circle1Center.Y);
g.RotateTransform(RotationAngle);
using (SolidBrush FillBrush = new SolidBrush(FillColor)) {
g.FillPath(FillBrush, path);
}
g.ResetTransform();
}
}
As the other answers so far slightly miss the correct solution, here is one that connects two circles of equal size:
using (Pen pen = new Pen(Color.Blue, radius)
{ EndCap = LineCap.Round, StartCap = LineCap.Round } )
g.DrawLine(pen, x1, y1, x2, y2);
Notes:
Usually is is good idea to set the smoothing mode of the graphics object to anti-alias..
To connect two circles of different sizes will take some math to calculate the four outer tangent points. From these one can get a polygon to fill or, if necessary one could create a GraphicsPath to fill, in case the color has an alpha < 1.
Jimi's comments point to a different solution that make use of GDI+ transformation capabilities.
Some of the answers or comments refer to the desired shape as an oval. While this ok in common speech, here, especially when geometry books are mentioned, this is wrong, as an oval will not have any straight lines.
As Jimi noted, what you call radius is really the diameter of the circles. I left the wrong term in the code but you should not!
Pseudo style:
circle1x;
circle1y;
circle2x;
circle2y;
midx=circle1x-circle2x;
midy=circle2x-circle2x;
draw circle at midx midy;
repeat for midx midy, in both directions. add another circle. honestly man, this isnt worth it,in order to make it smooth, you will need several circles. you need to draw an oval using the center of both circles as the two centers of your oval

itextsharp rotate image at point [duplicate]

double degPi = degrees * Math.PI / 180;
double a = Math.cos(degPi)*tImgCover.getScaledHeight();
double b = Math.sin(degPi)*tImgCover.getScaledWidth();
double c = -Math.sin(degPi) * tImgCover.getScaledHeight();
double d = Math.cos(degPi)* tImgCover.getScaledWidth();
double e = absX;
double f = absY;
contentByte.addImage(imgae, a, b, c, d, e, f);/*add image*/
How to rotate around the image center by itext?
If we have an Image image and coordinates x, y, we can draw the image without rotation with its lower left corner at the given coordinates like this
contentByte.addImage(image, image.getWidth(), 0, 0, image.getHeight(), x, y);
A bitmap image from the resources has a size of 1x1 with the coordinate origin at its lower left. Thus, this operation stretches the image to its correct size and moves it so its lower left is at the given coordinates.
If we want to draw the same image as if the one drawn above was rotated around its center by an angle rotate, therefore, we can do this by moving the 1x1 image so that the origin is in its center, stretch it to its correct size, rotate it, and then move the origin (which still is at the center of the rotated image) to the center of the unrotated image. These operations are easier to express using AffineTransform instances (from package com.itextpdf.awt.geom) instead number tupels. Thus:
// Draw image as if the previous image was rotated around its center
// Image starts out being 1x1 with origin in lower left
// Move origin to center of image
AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
// Stretch it to its dimensions
AffineTransform B = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
// Rotate it
AffineTransform C = AffineTransform.getRotateInstance(rotate);
// Move it to have the same center as above
AffineTransform D = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
// Concatenate
AffineTransform M = (AffineTransform) A.clone();
M.preConcatenate(B);
M.preConcatenate(C);
M.preConcatenate(D);
//Draw
contentByte.addImage(image, M);
(AddRotatedImage.java test method testAddRotatedImage)
For example drawing both images using
int x = 200;
int y = 300;
float rotate = (float) Math.PI / 3;
results in something like this:
With a Flip
The OP asked in a comment
how to add rotate and flip image?
For this you simply insert a mirroring affine transformation into the sequence of transformations above.
Unfortunately the OP did not mention which he meant a horizontal or a vertical flip. But as changing the rotation angle accordingly transforms one in the other, that isn't really necessary, either.
// Draw image as if the previous image was flipped and rotated around its center
// Image starts out being 1x1 with origin in lower left
// Move origin to center of image
AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
// Flip it horizontally
AffineTransform B = new AffineTransform(-1, 0, 0, 1, 0, 0);
// Stretch it to its dimensions
AffineTransform C = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
// Rotate it
AffineTransform D = AffineTransform.getRotateInstance(rotate);
// Move it to have the same center as above
AffineTransform E = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
// Concatenate
AffineTransform M = (AffineTransform) A.clone();
M.preConcatenate(B);
M.preConcatenate(C);
M.preConcatenate(D);
M.preConcatenate(E);
//Draw
contentByte.addImage(image, M);
(AddRotatedImage.java test method testAddRotatedFlippedImage)
The result with the same image as above:
With Interpolation
The OP asked in a yet another comment
How anti aliasing ?
The iText Image class knows an Interpolation property. By setting it to true (before adding the image to the document, obviously),
image.setInterpolation(true);
low resolution images are subject to interpolation when drawn.
E.g. using a 2x2 image with differently colored pixels instead of the image of Willi, you get the following results, first without interpolation, then with interpolation:
Confer the AddRotatedImage.java test testAddRotatedInterpolatedImage which adds this image:
Beware: iText Image property Interpolation effectively sets the Interpolate entry in the PDF image dictionary. The PDF specification notes in this context:
NOTE A conforming Reader may choose to not implement this feature of PDF, or may use any specific implementation of interpolation that it wishes.
Thus, on some viewers interpolation may occur differently than in your viewer, maybe even not at all. If you need a specific kind of interpolation on every viewer, upscale the image with the desired amount of interpolation / anti-aliasing before loading it into an iText Image.
public static BufferedImage rotateClockwise90( BufferedImage inputImage ){
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage returnImage = new BufferedImage( height, width , inputImage.getType() );
for( int x = 0; x < width; x++ ) {
for( int y = 0; y < height; y++ ) {
returnImage.setRGB( height-y-1, x, inputImage.getRGB( x, y ) );
}
}
return returnImage;
}

Cut rectangle area from given image

I have the following problem:
I have an image saved as: Image X; and a list of Point.
point is defined as following:
public struct Point
{
public int X;
public int Y;
}
on the list (which isn't sorted) there are 2 Points. the Points represent cords on the image. these cords define a rectangle shape. for example if cords are: (0,0) and (1,1) then the rectangle edges are: (0,0) - (0,1) - (1,1) - (1,0).
I am suppose to write a method that returns a cropped image where the rectangle lays. in the above example the cropped image will be everything within the boundary of (0,0) - (0,1) - (1,1) - (1,0).
any ideas for simple way to implement it?
note that i can't know where the rectangle lays in the image X. rectangles can have same area or even the exact same shape but in different places on the image.
assume it can be anywhere yet rectangle can not be outside of the image in any way (whole nor partly)
First of all, you need to get the min corner and the max corner, an easy way is:
//Having p1 and p2
Point min = new Point(Math.Min(p1.X, p2.X), Math.Min(p1.Y, p2.Y));
Point max = new Point(Math.Max(p1.X, p2.X), Math.Max(p1.Y, p2.Y));
Once you have max and min you can construct a rectangle for source:
Rectangle srcRect = new Rectangle(min.X, min.Y, max.X - min.X, max.Y - min.Y);
Then you create a Bitmap with the rect size:
Bitmap cropped= new Bitmap(srcRect.Width, srcRect.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
Create a Graphics object from the image:
Graphics g = Graphics.FromImage(bmp);
And draw the cropped area:
//Having source image SrcImg
g.DrawImage(SrcImage, new Rectangle(Point.Empty, srcRect.Size), srcRect, GraphicsUnit.Pixel);
Now you have your cropped image at "cropped"
Don't forget to dispose graphics!!

WinForms - how much % of the area one rectangle covers another

Given two System.Drawing.Rectangle's - how to determine what % of the first rectangle's area the second rectangle covers?
For example, if the second rectangle is half-way positioned to the first one, the result should be 50%.
You can use the Rectangle.Intersect method to get the intersection rectangle:
Rectangle rect = new Rectangle(firstRect.Location, firstRect.Size);
rect.Intersect(secondRectangle);
var percentage = (rect.Width * rect.Height) * 100f/(firstRect.Width * firstRect.Height);

Drawing an envelope around a curve

In my C# WinForms application I have a picturebox that hosts 2 curves (Resulted from a voltage/current measurement). The X axis is voltage and Y axis is current. The voltage axis is ranged from -5 to 5 but the current axis is a much smaller scale ranged from -10 uA to 10 uA. The task is to see if the second curve is within 10% of the first curve.
For visual inspection I am trying to draw an envelope around the first curve (Blue one). The curve is just a PointF array. At the moment since I have no idea how to draw a correct envelope around the blue curve, I just draw two other curves that are result of X points of the actual curve added and subtracted by 10% of the original curve. Of course this is a bad approach, but atleast for the section of the curve that is noticably vertical, it works. But as soon as the curve is on its non vertical section, this trick does not work anymore, as you can see in the picture below:
Here is the code that I am using to draw the envelope:
public Bitmap DrawEnvelope(double[,] pinData, float vLimit, float iLimit)
{
g = Graphics.FromImage(box);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
PointF[] u = new PointF[pinData.GetLength(0)]; //Up line
PointF[] d = new PointF[pinData.GetLength(0)]; //Down Line
List<PointF> joinedCurves = new List<PointF>();
float posX = xMaxValue * (vLimit / 100);
float minX = posX * -1;
for (int i = 0; i < pinData.GetLength(0); i++)
{
u[i] = new PointF(400 * (1 + (((float)pinData[i, 0]) + minX) / (xMaxValue + vExpand)), 400 * (1 - ((float)pinData[i, 1] * GetInvers((yMaxValue + iExpand)))));
}
for (int i = 0; i < pinData.GetLength(0); i++)
{
d[i] = new PointF(400 * (1 + (((float)pinData[i, 0]) + posX) / (xMaxValue + vExpand)), 400 * (1 - ((float)pinData[i, 1] * GetInvers((yMaxValue + iExpand)))));
}
Pen pengraph = new Pen(Color.FromArgb(50, 0 ,0 ,200), 1F);
pengraph.Alignment = PenAlignment.Center;
joinedCurves.AddRange(u);
joinedCurves.AddRange(d.Reverse());
PointF[] fillPoints = joinedCurves.ToArray();
SolidBrush fillBrush = new SolidBrush(Color.FromArgb(40, 0, 0, 250));
FillMode newFillMode = FillMode.Alternate;
g.FillClosedCurve(fillBrush, fillPoints, newFillMode, 0);
g.Dispose();
return box;
}
The green circles are added by myself, and they indicate the region that the second curve (Red one) is potentially has a difference bigger than 10% from the orginal curve.
Would be nice if someone put me in the right way, what should I look to to achive a nice envelope around original curve?
UPDATE
Because I am so noob I cant find a way to implement the answers given to this question until now, So put a bounty to see if somone can kindly show me atleast a coding approach to this problem.
You could try finding the gradient between each pair of points and calculating two points either side that are on the orthogonal that passes through the midpoint.
You would then have two more lines defined as a set of points that you could use to draw the envelope.
Your best bet is to iterate your point array and to calculate a perpendicular vector to two consecutive points each time (see Calculating a 2D Vector's Cross Product for implementation clues). Project in either direction along these perpendicular vectors to generate the two point arrays of your envelope.
This function generates them roughly using segment midpoints (as long as the point count is high and your offset is not too small it should look ok when plotted):
private void GetEnvelope(PointF[] curve, out PointF[] left, out PointF[] right, float offset)
{
left = new PointF[curve.Length - 1];
right = new PointF[curve.Length - 1];
for (int i = 1; i < curve.Length; i++)
{
PointF normal = new PointF(curve[i].Y - curve[i - 1].Y, curve[i - 1].X - curve[i].X);
float length = (float)Math.Sqrt(normal.X * normal.X + normal.Y * normal.Y);
normal.X /= length;
normal.Y /= length;
PointF midpoint = new PointF((curve[i - 1].X + curve[i].X) / 2F, (curve[i - 1].Y + curve[i].Y) / 2F);
left[i - 1] = new PointF(midpoint.X - (normal.X * offset), midpoint.Y - (normal.Y * offset));
right[i - 1] = new PointF(midpoint.X + (normal.X * offset), midpoint.Y + (normal.Y * offset));
}
}
It all depends on the way you want the envelop to be sized.
You could calculate/guestimate the slope of the curve in each point by calculating the slope to the next point and the slope to the previous point, average these and then calculate a perpendicular vector to the slope.
Add this vector to the point of the curve; this gives you the right-hand edge of the envelop.
Subtract this vector from the point of the curve; this gives you the left-hand edge of the envelop.
This method will fail if the points are too far apart or very sudden changes in the points appear.
This is probably a dumb suggestion. Perhaps instead of drawing the envelope yourself, maybe you could let winforms do it for you. Try drawing the envelope as a line with a pen that has a larger width. Perhaps it might work.
If you look at this msdn example on varying the pen width, you might see what I mean.
http://msdn.microsoft.com/en-us/library/3bssbs7z.aspx
2 (probably incorrect) possibilities.
Do what you did originally to get the pale blue wide area, but also do it in the vertical direction (not just the horizontal)
Do what Dan suggested with a REALLY thick line (in pale blue) then draw it again, then draw the original (thin) line on top of it.

Categories

Resources