Cut the shapes out of the image with EMGU CV - c#

All good times of day!
Faced with the following task: you need to find the shapes in the image, cut them out and save them in jpg or png.
Find the shapes worked out (attached the image), but how do I get their extreme coordinates?
I find shapes with EMGU CV.
I'm taking Image it out of PictureBox.
// - was like the idea to find the distance from the center to the edge of the figure through Moments, but did not understand how they work
Image<Gray, byte> grayImage = inputImage.SmoothMedian(1).Convert<Gray, byte>().ThresholdBinaryInv(new Gray(230), new Gray(255));
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
Mat hierarchy = new Mat();
for (int i = 0; i < contours.Size; i++)
{
double perimetr = CvInvoke.ArcLength(contours[i], true);
VectorOfPoint approximation = new VectorOfPoint();
CvInvoke.ApproxPolyDP(contours[i], approximation, 0.04 * perimetr, true);
if (approximation.Size >= 3 && perimetr > 100 && approximation.Size != 5)
{
CvInvoke.DrawContours(inputImage, contours, i, new MCvScalar(0, 255, 0), 2);
}
Moments moments = CvInvoke.Moments(contours[i]);
int x = (int)(moments.M10 / moments.M00);
int y = (int)(moments.M01 / moments.M00);
if (perimetr > 100)
{
if (approximation.Size == 3)
{
CvInvoke.DrawContours(inputImage, contours, i, new MCvScalar(0, 255, 0), 2);
CvInvoke.PutText(inputImage, "Triangle", new Point(x, y), Emgu.CV.CvEnum.FontFace.HersheyPlain, 1,
new MCvScalar(0, 0, 255), 1);
}
if (approximation.Size == 4)
{
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
double aspectRatio = (double)rect.Width / (double)rect.Height;
if (aspectRatio >= 0.95 && aspectRatio <= 1.05)
{
CvInvoke.DrawContours(inputImage, contours, i, new MCvScalar(0, 255, 0), 2);
CvInvoke.PutText(inputImage, "Square", new Point(x, y), Emgu.CV.CvEnum.FontFace.HersheyPlain, 1,
new MCvScalar(0, 0, 255), 1);
}
else
{
CvInvoke.DrawContours(inputImage, contours, i, new MCvScalar(0, 255, 0), 2);
CvInvoke.PutText(inputImage, "Rectangle", new Point(x, y), Emgu.CV.CvEnum.FontFace.HersheyPlain, 1,
new MCvScalar(0, 0, 255), 1);
}
}
if (approximation.Size == 5)
{
CvInvoke.DrawContours(inputImage, contours, i, new MCvScalar(0, 255, 0), 2);
CvInvoke.PutText(inputImage, "Pentagon", new Point(x, y), Emgu.CV.CvEnum.FontFace.HersheyPlain, 1,
new MCvScalar(0, 0, 255), 1);
}
if (approximation.Size == 6)
{
CvInvoke.DrawContours(inputImage, contours, i, new MCvScalar(0, 255, 0), 2);
CvInvoke.PutText(inputImage, "Hexagon", new Point(x, y), Emgu.CV.CvEnum.FontFace.HersheyPlain, 1,
new MCvScalar(0, 0, 255), 1);
}
if (approximation.Size > 6)
{
var R = (double)perimetr / (3.14 * 2);
if (R > (Math.Min(inputImage.Width, inputImage.Height) / 3) * 0.1 && R < (Math.Min(inputImage.Width, inputImage.Height) / 2))
{
CvInvoke.DrawContours(inputImage, contours, i, new MCvScalar(0, 255, 0), 2);
CvInvoke.PutText(inputImage, "Circle", new Point(x, y), Emgu.CV.CvEnum.FontFace.HersheyPlain, 1,
new MCvScalar(0, 0, 255), 1);
}
}
}
}
Thank you for your attention.
Input image
Output image

I found a solution to my question. There is a method that returns a description of the contour with an array of points.
Point[] contour = contours[i].ToArray();

Related

color blending using PathGradientBrush

I have been stuck at this problem for few hours but can n't seem to make progress. I am trying to draw a rectangle which shows like the following
with a lightgrey border line. I am successfully able to draw the grey border line rectangle, but i am having trouble filling the rectangle.
There are 9 colors and i would like them to be space equally across the width of my rectangle.
My Code Looks like the following
using (var gp = new GraphicsPath())
{
gp.AddRectangle(rect);
using (var pgb = new PathGradientBrush(gp))
{
var pos = new[] { 0f, .125f, .25f, .375f, .5f, .625f, .75f, .875f, 1f };
var b = new Blend
{
Positions = pos,
Factors = new[] { .125f, .125f, .125f, .125f, .125f, .125f, .125f, .125f, .125f }
};
var cb = new ColorBlend
{
Positions = pos,
Colors = new[]
{
Color.FromArgb(0, 0, 0),
Color.FromArgb(0, 0, 79),
Color.FromArgb(81, 0, 123),
Color.FromArgb(152, 0, 118),
Color.FromArgb(211, 0, 62),
Color.FromArgb(245, 31, 0),
Color.FromArgb(255, 175, 0),
Color.FromArgb(255, 255, 100),
Color.FromArgb(255, 255, 255),
}
};
pgb.Blend = b;
pgb.InterpolationColors = cb;
pgb.CenterPoint = new PointF(rect.Width / 2, rect.Height / 2);
pgb.FocusScales = new PointF(.5f, .5f);
bufferedGraphics.Graphics.FillPath(pgb, gp);
}
}
but this ends up producing
Please ignore the inconsistent sizes of the two graphics.
Is this possible to do and if so how can i change my method to accomplish this ?
Instead of using the PathGradientBrush, use a LinearGradientBrush, because you want to draw the colors in a straight block. And (at least for me) it's easier to understand what happens.
I've tried the following code in a framework 2.0 project, s sorry for removing C#4.0 syntax.
using (LinearGradientBrush lgb = new LinearGradientBrush(rect, Color.Black, Color.White, 0f)) {
float[] pos = new float[] { 0f, .125f, .25f, .375f, .5f, .625f, .75f, .875f, 1f };
Blend b = new Blend();
b.Positions = pos;
b.Factors = new float[] { .125f, .125f, .125f, .125f, .125f, .125f, .125f, .125f, .125f };
ColorBlend cb = new ColorBlend();
cb.Positions = pos;
cb.Colors = new Color[] {
Color.FromArgb(0, 0, 0),
Color.FromArgb(0, 0, 79),
Color.FromArgb(81, 0, 123),
Color.FromArgb(152, 0, 118),
Color.FromArgb(211, 0, 62),
Color.FromArgb(245, 31, 0),
Color.FromArgb(255, 175, 0),
Color.FromArgb(255, 255, 100),
Color.FromArgb(255, 255, 255),
};
lgb.Blend = b;
lgb.InterpolationColors = cb;
e.Graphics.FillRectangle(lgb, rect);
}
Result:

Use HSV instead of RGB in WPF

I'm using the Color.FromArgb to for determining the brush color. Using this for examplePen p= new Pen(new SolidColorBrush(Color.FromArgb(95, 255, 0, 0)), 6); the problem with this is that I want to use 4 colors of the brushes (red, orange, yellow and green). And I have a condition where depending on the value in this condition the color is chosen with a certain transparency. The problem with this is the transition between the two different colors, for example from green to yellow or from orange to red, the color changes suddenly and I don't want this. I want to transition to be smooth between different colors so for example from orange to red I want it to take the different degrees of oranges till it becomes reddish till it becomes red.
The below is a snapshot of my code trying to clarify what I did, so if anyone could please advise, and please let me know if any more clarification needed. So as shown below depending of the value of X the color is determined with a certain intensity, but the problem is in if the last value of X was 10 which means Green and the current is 11 which means the color is Yellow, this makes the view not smooth because the color changed from Green to Yellow without any degrees of smoothness. And I thought that may be using HSV can solve this problem. So if anyone could please advise.
for(X=0; X<=40; X++)
//this is green phase
if (X == 0)
P = new Pen(new SolidColorBrush(Color.FromArgb(75, 0, 255, 0)), 6);
else if (X == 1)
P = new Pen(new SolidColorBrush(Color.FromArgb(77, 0, 255, 0)), 6);
else if (X == 2)
P = new Pen(new SolidColorBrush(Color.FromArgb(79, 0, 255, 0)), 6);
else if (X == 3)
P = new Pen(new SolidColorBrush(Color.FromArgb(81, 0, 255, 0)), 6);
else if (X == 4)
P = new Pen(new SolidColorBrush(Color.FromArgb(83, 0, 255, 0)), 6);
else if (X == 5)
P = new Pen(new SolidColorBrush(Color.FromArgb(85, 0, 255, 0)), 6);
else if (X == 6)
P = new Pen(new SolidColorBrush(Color.FromArgb(87, 0, 255, 0)), 6);
else if (X == 7)
P = new Pen(new SolidColorBrush(Color.FromArgb(89, 0, 255, 0)), 6);
else if (X == 8)
P = new Pen(new SolidColorBrush(Color.FromArgb(91, 0, 255, 0)), 6);
else if (X == 9)
P = new Pen(new SolidColorBrush(Color.FromArgb(93, 0, 255, 0)), 6);
else if (X == 10)
P = new Pen(new SolidColorBrush(Color.FromArgb(95, 0, 255, 0)), 6);
// this is yellow phase
else if (X == 11)
P = new Pen(new SolidColorBrush(Color.FromArgb(75, 255, 255, 0)), 6);
else if (X == 12)
P = new Pen(new SolidColorBrush(Color.FromArgb(77, 255, 255, 0)), 6);
else if (X == 13)
P = new Pen(new SolidColorBrush(Color.FromArgb(79, 255, 255, 0)), 6);
else if (X == 14)
P = new Pen(new SolidColorBrush(Color.FromArgb(81, 255, 255, 0)), 6);
else if (X == 15)
P = new Pen(new SolidColorBrush(Color.FromArgb(83, 255, 255, 0)), 6);
else if (X == 16)
P = new Pen(new SolidColorBrush(Color.FromArgb(85, 255, 255, 0)), 6);
else if (X == 17)
P = new Pen(new SolidColorBrush(Color.FromArgb(87, 255, 255, 0)), 6);
else if (X == 18)
P = new Pen(new SolidColorBrush(Color.FromArgb(89, 255, 255, 0)), 6);
else if (X == 19)
P = new Pen(new SolidColorBrush(Color.FromArgb(91, 255, 255, 0)), 6);
else if (X == 20)
P = new Pen(new SolidColorBrush(Color.FromArgb(93, 255, 255, 0)), 6);
// this is orange phase
else if (X == 21)
P = new Pen(new SolidColorBrush(Color.FromArgb(75, 255, 127, 0)), 6);
else if (X == 22)
P = new Pen(new SolidColorBrush(Color.FromArgb(77, 255, 127, 0)), 6);
else if (X == 23)
P = new Pen(new SolidColorBrush(Color.FromArgb(79, 255, 127, 0)), 6);
else if (X == 24)
P = new Pen(new SolidColorBrush(Color.FromArgb(81, 255, 127, 0)), 6);
else if (X == 25)
P = new Pen(new SolidColorBrush(Color.FromArgb(83, 255, 127, 0)), 6);
else if (X == 26)
P = new Pen(new SolidColorBrush(Color.FromArgb(85, 255, 127, 0)), 6);
else if (X == 27)
P = new Pen(new SolidColorBrush(Color.FromArgb(87, 255, 127, 0)), 6);
else if (X == 28)
P = new Pen(new SolidColorBrush(Color.FromArgb(89, 255, 127, 0)), 6);
else if (X == 29)
P = new Pen(new SolidColorBrush(Color.FromArgb(91, 255, 127, 0)), 6);
else if (X == 30)
P = new Pen(new SolidColorBrush(Color.FromArgb(93, 255, 127, 0)), 6);
//this is red phase
else if (X == 31)
P = new Pen(new SolidColorBrush(Color.FromArgb(75, 255, 0, 0)), 6);
else if (X == 32)
P = new Pen(new SolidColorBrush(Color.FromArgb(77, 255, 0, 0)), 6);
else if (X == 33)
P = new Pen(new SolidColorBrush(Color.FromArgb(79, 255, 0, 0)), 6);
else if (X == 34)
P = new Pen(new SolidColorBrush(Color.FromArgb(81, 255, 0, 0)), 6);
else if (X == 35)
P = new Pen(new SolidColorBrush(Color.FromArgb(83, 255, 0, 0)), 6);
else if (X == 36)
P = new Pen(new SolidColorBrush(Color.FromArgb(85, 255, 0, 0)), 6);
else if (X == 37)
P = new Pen(new SolidColorBrush(Color.FromArgb(87, 255, 0, 0)), 6);
else if (X == 38)
P = new Pen(new SolidColorBrush(Color.FromArgb(89, 255, 0, 0)), 6);
else if (X == 39)
P = new Pen(new SolidColorBrush(Color.FromArgb(91, 255, 0, 0)), 6);
else if (X == 40)
P = new Pen(new SolidColorBrush(Color.FromArgb(93, 255, 0, 0)), 6);
Maybe you should take a look into this answer. It helps you to use the methods GetHue(), GetSaturation() and GetBrightness() from the color class and create a new color from these parameters.
By using this you can simply get out the hue of your start and end color and depending on your step size get as much intermediate colors as needed.
Wouldn't it be great to have a method:
int numberOfIntermediateColors = 8;
IEnumerable<Colors> colorPalette = Color.Red.Range(Color.Green, numberOfIntermediateColors);
And here it is:
public static IEnumerable<Color> Range(this Color firstColor, Color lastColor, int count)
{
float stepHueClockwise = GetStepping(firstColor.GetHue(), lastColor.GetHue(), count, Direction.Clockwise);
float stepHueCounterClockwise = GetStepping(firstColor.GetHue(), lastColor.GetHue(), count, Direction.CounterClockwise);
if (Math.Abs(stepHueClockwise) >= Math.Abs(stepHueCounterClockwise))
return Range(firstColor, lastColor, count, Direction.Clockwise);
else
return Range(firstColor, lastColor, count, Direction.CounterClockwise);
}
public static IEnumerable<Color> Range(this Color firstColor, Color lastColor, int count, Direction hueDirection)
{
var color = firstColor;
if (count <= 0)
yield break;
if (count == 1)
yield return firstColor;
float startingHue = color.GetHue();
float stepHue = GetStepping(firstColor.GetHue(), lastColor.GetHue(), count - 1, hueDirection);
var stepSaturation = (lastColor.GetSaturation() - firstColor.GetSaturation()) / (count - 1);
var stepBrightness = (lastColor.GetBrightness() - firstColor.GetBrightness()) / (count - 1);
var stepAlpha = (lastColor.A - firstColor.A) / (count - 1.0);
for (int i = 1; i < count; i++)
{
yield return color;
var hueValue = startingHue + stepHue * i;
if (hueValue > 360)
hueValue -= 360;
if (hueValue < 0)
hueValue = 360 + hueValue;
color = FromAhsb(
Clamp((int)(color.A + stepAlpha), 0, 255),
hueValue,
Clamp(color.GetSaturation() + stepSaturation, 0, 1),
Clamp(color.GetBrightness() + stepBrightness, 0, 1));
}
yield return lastColor;
}
public enum Direction
{
Clockwise = 0,
CounterClockwise = 1
}
private static float GetStepping(float start, float end, int count, Direction direction)
{
var hueDiff = end - start;
switch (direction)
{
case Direction.CounterClockwise:
if (hueDiff >= 0)
hueDiff = (360 - hueDiff) * -1;
break;
default:
if (hueDiff <= 0)
hueDiff = 360 + hueDiff;
break;
}
return hueDiff / count;
}
private static int Clamp(int value, int min, int max)
{
if (value < min)
return min;
if (value > max)
return max;
return value;
}
private static float Clamp(float value, float min, float max)
{
if (value < min)
return min;
if (value > max)
return max;
return value;
}
To get your list of pens without any transparency you can take this simple approach:
var startColor = Color.Green;
var endColor = Color.Red;
var penWidth = 6;
var rainbow = startColor.Range(endColor, 40, bla.Direction.CounterClockwise);
var pens = rainbow.Select(color => new Pen(color, penWidth))
.ToList();
// Somewhere else...
int x = RetrieveDesiredCondition();
var neededPen = pens[x];
As far as i can see in your example you like to let the transparency iterate from 75 - 95 for each block of ten colors and a total of four blocks. In that case maybe this algorithm may help:
var startColor = Color.Green;
var endColor = Color.Red;
var penWidth = 6;
var lowerTransparency = 75;
var higherTransparency = 95;
var stepsPerSection = 10;
var sections = 4;
var stepsNeeded = stepsPerSection * sections;
var transparencyRange = higherTransparency - lowerTransparency;
var stepSize = transparencyRange / stepsPerSection;
var rainbow = startColor.Range(endColor, stepsNeeded, Direction.CounterClockwise);
var rainbowWithTransparency = rainbow.Select((color, index) =>
{
var step = (index % stepsPerSection) * stepSize;
var neededTransparency = lowerTransparency + step;
return Color.FromArgb(neededTransparency, color);
});
var pens = rainbowWithTransparency.Select(color => new Pen(color, penWidth))
.ToList();
// Somewhere else...
int x = RetrieveDesiredCondition();
var neededPen = pens[x];

How to fill enclosed area in the Bitmap object with a color

Give point within the region with a color to fill the region, similar to the "drawing" in the paint bucket function.
The. NET Framework, there is no direct equivalent.
but i hope use C# to do it.
is it possible?
Here's a very naive flood fill algorithm that should get you started
void Form1_Paint(object sender, PaintEventArgs e)
{
using (Bitmap bitmap = new Bitmap(500, 500))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.Clear(Color.White);
List<Point> points = new List<Point>();
for (double i = 0; i < 10; i++)
{
double dist = (i % 2 == 0) ? 100 : 50;
double x = 200 + Math.Cos(i / 10d * Math.PI * 2d) * dist;
double y = 200 + Math.Sin(i / 10d * Math.PI * 2d) * dist;
points.Add(new Point((int)x, (int)y));
}
g.DrawPolygon(Pens.Black, points.ToArray());
}
FloodFill(bitmap, 200, 200, Color.Red);
e.Graphics.DrawImage(bitmap, 0, 0);
}
}
void FloodFill(Bitmap bitmap, int x, int y, Color color)
{
BitmapData data = bitmap.LockBits(
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int[] bits = new int[data.Stride / 4 * data.Height];
Marshal.Copy(data.Scan0, bits, 0, bits.Length);
LinkedList<Point> check = new LinkedList<Point>();
int floodTo = color.ToArgb();
int floodFrom = bits[x + y * data.Stride / 4];
bits[x + y * data.Stride / 4] = floodTo;
if (floodFrom != floodTo)
{
check.AddLast(new Point(x, y));
while (check.Count > 0)
{
Point cur = check.First.Value;
check.RemoveFirst();
foreach (Point off in new Point[] {
new Point(0, -1), new Point(0, 1),
new Point(-1, 0), new Point(1, 0)})
{
Point next = new Point(cur.X + off.X, cur.Y + off.Y);
if (next.X >= 0 && next.Y >= 0 &&
next.X < data.Width &&
next.Y < data.Height)
{
if (bits[next.X + next.Y * data.Stride / 4] == floodFrom)
{
check.AddLast(next);
bits[next.X + next.Y * data.Stride / 4] = floodTo;
}
}
}
}
}
Marshal.Copy(bits, 0, data.Scan0, bits.Length);
bitmap.UnlockBits(data);
}

How to make a Non Rectangular Winform?

I am using the code below to change to shape of the winform.
It's changing the shape, but not like how I wanted.
I need the form to have curved corners.
What points should I use to get it?
public void MakeNonRectangularForm()
{
var p = new GraphicsPath();
int width = ClientSize.Width;
int height = ClientSize.Height;
p.AddClosedCurve(new Point[] { new Point(width / 2, height / 2),
new Point(width, 0), new Point(width, height / 3),
new Point(width - width / 3, height),
new Point(width / 7, height - height / 8)});
Region = new Region(p);
}
The following is some code I have used to create rounded edges before, using AddArc and lines to piece together the border:
(You can play with xRadius and yRadius to achieve your desired amount of "rounded-ness")
int xRadius = {insert value here};
int yRadius = {insert value here};
GraphicsPath edge = new GraphicsPath();
int rightHandLeft = this.Width - xRadius - 1;
int bottomSideTop = this.Height - yRadius - 1;
edge.AddArc(0, 0, xRadius, yRadius, 180, 90);
edge.AddLine(xRadius, 0, rightHandLeft, 0);
edge.AddArc(rightHandLeft, 0, xRadius, yRadius, 270, 90);
edge.AddLine(this.Width, yRadius, this.Width, bottomSideTop);
edge.AddArc(rightHandLeft, bottomSideTop, xRadius, yRadius, 0, 90);
edge.AddLine(rightHandLeft, this.Height, xRadius, this.Height);
edge.AddArc(0, bottomSideTop, xRadius, yRadius, 90, 90);
edge.AddLine(0, bottomSideTop, 0, yRadius);
this.Region = new Region(edge);

c# bitmap filling

I want to create a bitmap of size 160*160 and split it into four squares with each square filled with one color. How can this be done?
Just in case anyone needs a method solving this specific problem in a more general way, I wrote an extension method, taking colors and an integer that states how many tiles it should split off in x and y direction:
public static void FillImage(this Image img, int div, Color[] colors)
{
if (img == null) throw new ArgumentNullException();
if (div < 1) throw new ArgumentOutOfRangeException();
if (colors == null) throw new ArgumentNullException();
if (colors.Length < 1) throw new ArgumentException();
int xstep = img.Width / div;
int ystep = img.Height / div;
List<SolidBrush> brushes = new List<SolidBrush>();
foreach (Color color in colors)
brushes.Add(new SolidBrush(color));
using (Graphics g = Graphics.FromImage(img))
{
for (int x = 0; x < div; x++)
for (int y = 0; y < div; y++)
g.FillRectangle(brushes[(y * div + x) % colors.Length],
new Rectangle(x * xstep, y * ystep, xstep, ystep));
}
}
The four squares, the OP wanted would be produced with:
new Bitmap(160, 160).FillImage(2, new Color[]
{
Color.Red,
Color.Blue,
Color.Green,
Color.Yellow
});
You can try something like
using (Bitmap b = new Bitmap(160, 160))
using (Graphics g = Graphics.FromImage(b))
{
g.FillRectangle(new SolidBrush(Color.Blue), 0, 0, 79, 79);
g.FillRectangle(new SolidBrush(Color.Red), 79, 0, 159, 79);
g.FillRectangle(new SolidBrush(Color.Green), 0, 79, 79, 159);
g.FillRectangle(new SolidBrush(Color.Yellow), 79, 79, 159, 159);
b.Save(#"c:\test.bmp");
}

Categories

Resources