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.
im looking for a way to resize a Bitmap, without losing its actually shape, while having in mind that the new size is musn't be proportional to the original image dimensions.
To be exact I want to resize a image of a upright eight (40x110) to a 29x29 Bitmap and im looking for a function to convert the original proportions relativ to its new image, while filling the new created space (in width) with white and having the same side clearance from the actually eight to its white baselines.
Unfortunately this hasn't satisfy my requirements:
public static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
Update 1:
New function for proportional resizing:
public static Bitmap ResizeImageProportional(Bitmap bitmap, int width, int height)
{
Bitmap destImage;
Rectangle destRect;
int destH, destW, destX, dextY;
if (bitmap.Height > bitmap.Width)
{
destH = height;
destW = bitmap.Width / bitmap.Height * height;
destX = (width - destW) / 2;
dextY = 0;
}
else if (bitmap.Height < bitmap.Width)
{
destH = bitmap.Height / bitmap.Width * width;
destW = width;
destX = 0;
dextY = (height - destH) / 2;
}
else
// if (bitmap.Width == bitmap.Height)
{
destH = height;
destW = width;
destX = 0;
dextY = 0;
}
destRect = new Rectangle(destX, dextY, destW, destH);
destImage = new Bitmap(width, height);
destImage.SetResolution(bitmap.HorizontalResolution, bitmap.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(bitmap, destRect, 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
Unfortunately I always get a empty bitmap with this solution. I first crop the original bitmap to a rect in which the element of my bitmap does precisely fit in. Otherwise put the left baseline of the bitmap itself is also the baseline of the graphics element in it, while the whole background is white.
Then I put this bitmap (approx. 40x80) in to the "ResizeImageProportional" (original post edit.) function with a width and height value of 29.
If you want to keep the proportion you need to calculate the destination rectnagle.
Here are the measures, off the top of my head:
destH = height;
destW = image.Width / image.Height * height;
destX = (width - destW) / 2;
dextY = 0;
You should clear the background to the color you want before: Graphics.Clear(someColor);
If you don't you use the bounds of the source and the destination images, thereby distorting the image. Which is what you seem to have.., no?
In that case there is no new space, of course.
When calculating, use float if necessary..
Update:
Maybe you want to analyze the original image and preserve or remove the spaces from the foreground to its edges. That means either looking into the pixels or maybe going one step back to how the original was created..
For the example I have added the bounds of the figure.
Here is a simple cropping function; it assumes the top left corner to hold the background color and returns a cropping rectangle:
public static Rectangle CropBounds(Image image)
{
Bitmap bmp = (Bitmap)image;
int x0 = 0;
int x1 = bmp.Width - 1;
int y0 = 0;
int y1 = bmp.Height - 1;
Color c = bmp.GetPixel(0, 0);
while (x0==0)
{ for (int x = 0; x < x1; x++)
if ((x0==0)) for (int y = 0; y < y1; y++)
if ( bmp.GetPixel(x,y)!= c) {x0 = x-1; break;} }
while (x1 == bmp.Width - 1)
{
for (int x = bmp.Width - 1; x > 0; x--)
if ((x1 == bmp.Width - 1)) for (int y = 0; y < y1; y++)
if (bmp.GetPixel(x, y) != c) { x1 = x + 1; break; } }
while (y0 == 0)
{
for (int y = 0; y < y1; y++)
if ((y0 == 0)) for (int x = 0; x < bmp.Width; x++)
if (bmp.GetPixel(x, y) != c) { y0 = y - 1; break; }
}
while (y1 == bmp.Height - 1)
{
for (int y = bmp.Height - 1; y > 0; y--)
if ((y1 == bmp.Height - 1)) for (int x = 0; x < bmp.Width; x++)
if (bmp.GetPixel(x, y) != c) { y1 = y + 1; break; }
}
return new Rectangle(x0, y0, x1 - x0, y1 - y0);
}
Now you can change your code to use it like this:
graphics.DrawImage(image, destRect, CropBounds(image) , GraphicsUnit.Pixel);
If you really need to flip, simply store the rectangle and change to the appropriate format..!
Update 2:
There was a reason why I wrote When calculating, use float if necessary..:
In your ResizeImageProportional function you need to avoid integer precision loss! Change the divisions to:
destW = (int) (1f * bitmap.Width / bitmap.Height * height);
and
destH = (int) (1f *bitmap.Height / bitmap.Width * width);
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;
Hey all. I resorted to using LockBits for 2d bitmap image rotation after getting fed up with the slow performance and wacky behavior of both Get/Set Pixel, and RotateTransfom. So here is the code I've come up with, and by my reckoning, it should work perfectly. It doesn't.
private static void InternalRotateImage(Bitmap originalBitmap, Bitmap rotatedBitmap, PointF centerPoint, float theta)
{
BitmapData originalData = originalBitmap.LockBits(
new Rectangle(0, 0, originalBitmap.Width, originalBitmap.Height),
ImageLockMode.ReadOnly,
originalBitmap.PixelFormat);
BitmapData rotatedData = rotatedBitmap.LockBits(
new Rectangle(0, 0, rotatedBitmap.Width, rotatedBitmap.Height),
ImageLockMode.WriteOnly,
rotatedBitmap.PixelFormat);
unsafe
{
byte[,] A = new byte[originalData.Height * 2, originalBitmap.Width * 2];
byte[,] R = new byte[originalData.Height * 2, originalBitmap.Width * 2];
byte[,] G = new byte[originalData.Height * 2, originalBitmap.Width * 2];
byte[,] B = new byte[originalData.Height * 2, originalBitmap.Width * 2];
for (int y = 0; y < originalData.Height; y++)
{
byte* row = (byte*)originalData.Scan0 + (y * originalData.Stride);
for (int x = 0; x < originalData.Width; x++)
{
B[y, x] = row[x * 4];
G[y, x] = row[x * 4 + 1];
R[y, x] = row[x * 4 + 2];
A[y, x] = row[x * 4 + 3];
}
}
for (int y = 0; y < rotatedData.Height; y++)
{
byte* row = (byte*)rotatedData.Scan0 + (y * rotatedData.Stride);
for (int x = 0; x < rotatedData.Width; x++)
{
int newy = (int)Math.Abs((Math.Cos(theta) * (x - centerPoint.X) - Math.Sin(theta) * (y - centerPoint.Y) + centerPoint.X));
int newx = (int)Math.Abs((Math.Sin(theta) * (x - centerPoint.X) + Math.Cos(theta) * (y - centerPoint.Y) + centerPoint.Y));
row[x * 4] = B[newy, newx];
row[x * 4 + 1] = G[newy, newx];
row[x * 4 + 2] = R[newy, newx];
row[x * 4 + 3] = A[newy, newx];
}
}
}
originalBitmap.UnlockBits(originalData);
rotatedBitmap.UnlockBits(rotatedData);
}
Anybody got any ideas? I'm fresh out. Thanks in advance!
EDIT:
This is what I ended up using (many thanks to Hans Passant):
private Image RotateImage(Image img, float rotationAngle)
{
Image image = new Bitmap(img.Width * 2, img.Height * 2);
Graphics gfx = Graphics.FromImage(image);
int center = (int)Math.Sqrt(img.Width * img.Width + img.Height * img.Height) / 2;
gfx.TranslateTransform(center, center);
gfx.RotateTransform(rotationAngle);
gfx.DrawImage(img, -img.Width / 2, -img.Height / 2);
return image;
}
It's the same thing as his, just on a per image basis, as opposed to a form.
You are digging yourself a deeper hole. This goes wrong early, the size of the rotated bitmap is not Width x Height. It is also very inefficient. You need to get RotateTransform going, it is important to also use TranslateTransform and pick the correct image drawing location.
Here's a sample Windows Forms app that rotates a bitmap around its center point, offset just enough to touch the inner edge of the form when it rotates. Drop a Timer on the form and add an image resource with Project + Properties, Resource tab. Name it SampleImage, it doesn't have to be square. Make the code look like this:
public partial class Form1 : Form {
private float mDegrees;
private Image mBmp;
public Form1() {
InitializeComponent();
mBmp = Properties.Resources.SampleImage;
timer1.Enabled = true;
timer1.Interval = 50;
timer1.Tick += new System.EventHandler(this.timer1_Tick);
this.DoubleBuffered = true;
}
private void timer1_Tick(object sender, EventArgs e) {
mDegrees += 3.0F;
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e) {
int center = (int)Math.Sqrt(mBmp.Width * mBmp.Width + mBmp.Height * mBmp.Height) / 2;
e.Graphics.TranslateTransform(center, center);
e.Graphics.RotateTransform(mDegrees);
e.Graphics.DrawImage(mBmp, -mBmp.Width/2, -mBmp.Height/2);
}
}
You can make draw a lot faster by creating a bitmap in the 32bppPArgb format, I skipped that step.
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.