I want to detect a display on an image (more precisely its corners).
I segment the image in display color and not display color:
Image<Gray, byte> segmentedImage = greyImage.InRange(new Gray(180), new Gray(255));
Then I use corner Harris to find the corners:
Emgu.CV.Image<Emgu.CV.Structure.Gray, Byte> harrisImage = new Image<Emgu.CV.Structure.Gray, Byte>(greyImage.Size);
CvInvoke.CornerHarris(segmentedImage, harrisImage, 2);
CvInvoke.Normalize(harrisImage, harrisImage, 0, 255, NormType.MinMax, DepthType.Cv32F);
There are now white pixels in the corners, but I cannot access them:
for (int j = 0; j < harrisImage.Rows; j++)
{
for (int i = 0; i < harrisImage.Cols; i++)
{
Console.WriteLine(harrisImage[j, i].Intensity);
}
}
It writes only 0s. How can I access them? And if I can access them, how can I find the 4 corners of the screen in the harris image? Is there a function to find a perspectively transformed rectangle from points?
EDIT:
On the OpenCV IRC they said FindContours is not that precise. And when I try to run it on the segmentedImage, I get this:
(ran FindContours on the segmentedImage, then ApproxPolyDP and drew the found contour on the original greyscale image)
I cannot get it to find the contours more precise...
EDIT2:
I cannot get this to work for me. Even with your code, I get the exact same result...
Here is my full Emgu code:
Emgu.CV.Image<Emgu.CV.Structure.Gray, Byte> imageFrameGrey = new Image<Emgu.CV.Structure.Gray, Byte>(bitmap);
Image<Gray, byte> segmentedImage = imageFrameGrey.InRange(new Gray(180), new Gray(255));
// get rid of small objects
int morph_size = 2;
Mat element = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle, new System.Drawing.Size(2 * morph_size + 1, 2 * morph_size + 1), new System.Drawing.Point(morph_size, morph_size));
CvInvoke.MorphologyEx(segmentedImage, segmentedImage, Emgu.CV.CvEnum.MorphOp.Open, element, new System.Drawing.Point(-1, -1), 1, Emgu.CV.CvEnum.BorderType.Default, new MCvScalar());
// Find edges that form rectangles
List<RotatedRect> boxList = new List<RotatedRect>();
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(segmentedImage, contours, null, Emgu.CV.CvEnum.RetrType.External, ChainApproxMethod.ChainApproxSimple);
int count = contours.Size;
for (int i = 0; i < count; i++)
{
using (VectorOfPoint contour = contours[i])
using (VectorOfPoint approxContour = new VectorOfPoint())
{
CvInvoke.ApproxPolyDP(contour, approxContour, CvInvoke.ArcLength(contour, true) * 0.01, true);
if (CvInvoke.ContourArea(approxContour, false) > 10000)
{
if (approxContour.Size == 4)
{
bool isRectangle = true;
System.Drawing.Point[] pts = approxContour.ToArray();
LineSegment2D[] edges = Emgu.CV.PointCollection.PolyLine(pts, true);
for (int j = 0; j < edges.Length; j++)
{
double angle = Math.Abs(edges[(j + 1) % edges.Length].GetExteriorAngleDegree(edges[j]));
if (angle < 80 || angle > 100)
{
isRectangle = false;
break;
}
}
if (isRectangle)
boxList.Add(CvInvoke.MinAreaRect(approxContour));
}
}
}
}
}
So as promised i tried it myself. In C++ but you should adopt it easy to Emgu.
First i get rid of small object in your segmented image with an opening:
int morph_elem = CV_SHAPE_RECT;
int morph_size = 2;
Mat element = getStructuringElement(morph_elem, Size(2 * morph_size + 1, 2 * morph_size + 1), Point(morph_size, morph_size));
// Apply the opening
morphologyEx(segmentedImage, segmentedImage_open, CV_MOP_OPEN, element);
Then detect all the contours and take the large ones and check for rectangular shape:
vector< vector<Point>> contours;
findContours(segmentedImage_open, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
for each (vector<Point> var in contours)
{
double area = contourArea(var);
if (area> 30000)
{
vector<Point> approx;
approxPolyDP(var, approx, 0.01*arcLength(var, true), true);
if (4 == approx.size()) //rectangular shape
{
// do something
}
}
}
Here is the result with the contour in red and the approximated curve in green:
Edit:
You can improve your code by increasing the approximation factor until you get a contour with 4 points or you pass a threshold. Just wrap a for loop around approxPolyDP. You can define a range for your approximation value and prevent your code to fail if your object differs too much from a rectangle.
Related
I use:
Emgucv 4.0.1
Opencv 4.1.0
I have a series of circles detected with HoughCircles function that I need to analize one by one.
I need to calculate how much color is in the rounded green circle, so I have to extract only the circle image but I know how to extract only the box that contains much more pixel than the circle. How to extract only the image inside the surrounded green area?
See images in link below.
1) Source Image
2) Boxed image I retrieved with more pixel than i want
3) Image that I would like to extract
// m_ListCircles = list from HoughCircles() circles coordinates.
// cell = cell number to extract.
// pictureBox1 = main picturebox with all circles detected.
// pictureBoxROI = picturebox destination single circles cutted.
int id = (int)m_ListCircles[0 + cell ];
int x = (int)m_ListCircles[1 + cell ];
int y = (int)m_ListCircles[2 + cell ];
int r = (int)m_ListCircles[3 + cell ]; // radius
// box area around the circle
int X0 = x;
int Y0 = y;
int X1 = x + r * 2;
int Y1 = y + r * 2;
// area to copy
int wid = Math.Abs(X0 - X1);
int hgt = Math.Abs(Y0 - Y1);
if ((wid < 1) || (hgt < 1)) return;
// create a rectangle are to copy
Rectangle source_rectangle = new Rectangle(Math.Min(X0, X1),Math.Min(Y0,Y1), wid, hgt);
// assign the area copied to image var
var image = new Image<Bgr, byte>(new Bitmap(pictureBox1.Image));
image.ROI = source_rectangle;
// show image
pictureBoxROI.Image = image.Bitmap;
pictureBoxROI.Refresh();
/*
// tried this but result is always a black image.
Point xyCell = new Point();
xyCell.X = X0;
xyCell.Y = Y0;
Image<Gray, byte> mask = new Image<Gray, byte>(image.Width, image.Height);
CvInvoke.Circle(mask, xyCella, r, new MCvScalar(255, 255, 255), -1,
LineType.AntiAlias, 0);
Image<Bgr, byte> dest = new Image<Bgr, byte>(image.Width, image.Height);
dest = image.And(image, mask);
pictureBoxROI.Image = dest.Bitmap;
pictureBoxROI.Refresh();
*/
You can always create masks of ROI form the found circles and analyze tha images like that
Custom ROI - this shows how to use the mask
You can only have rectangular images. However you can after cutting the rectangle set all the pixels outside of the circle to transparent.
You can determine which pixels are outside of the circle by calculating their distance from the center point of your image using pythagoras.
This is very slow of course, as you must loop over all pixels, but for low pixel counts it's reasonably fast.
try
{
Image rectCroppedImage = originalImage.Clone(CropRect, originalImage.PixelFormat);
double r = rectCroppedImage.Height; // because you are centered on your circle
Bitmap img = new Bitmap(rectCroppedImage);
for (int x = 0; x < img.Width; x++)
{
for (int y = 0; y < img.Height; y++)
{
// offset to center
int virtX = x - img.Width / 2;
int virtY = y - img.Height / 2;
if (Math.Sqrt(virtX * virtX + virtY * virtY) > r)
{
img.SetPixel(x, y, Color.Transparent);
}
}
}
return img; // your circle cropped image
}
catch (Exception ex)
{
}
This could also be achieved by using a mask and "multiplying" your image with a white circle. Such a thing can be achieved for example with image magick. You can find an ImageMagick NuGet packet here: https://github.com/dlemstra/Magick.NET
I am doing project in Emgu cv in C#.
I have stuck in this first step. I calculated opticalflow.HS and LK and I don't know how to add velx and vely to draw them in frame as points and show them in ImageBox.
OpticalFlow.HS(prev, frame1, true, velx, vely, 0.1d, new MCvTermCriteria(100));
Does anyone can describe me what to do or even better some code example will be a lot of help? I don't want to show color of direction, only motion as points in frame.
I found the solution, so if anyone needs here is my example.
Image<Gray, Byte> coloredMotion2 = new Image<Gray, Byte>(frame1.Size);
for (int i = 0; i < coloredMotion2.Width; i+=2)
{
for (int j = 0; j < coloredMotion2.Height; j+=2)
{
dx = (int)CvInvoke.cvGetReal2D(velx, j, i);
dy = (int)CvInvoke.cvGetReal2D(vely, j, i);
int pomi = i + dx;
int pomj = j + dy;
if (i != pomi && j != pomj)
// uncoment line below if you want lines but it needs rgb image not gray {
//CvInvoke.cvLine(coloredMotion, new Point(i,j),new Point(i+dx,j+dy), new MCvScalar(255,0,0), 1, Emgu.CV.CvEnum.LINE_TYPE.CV_AA, 0);
CvInvoke.cvCircle(coloredMotion2, new Point(pomi, pomj), 1, new MCvScalar(255,255,255), 1, Emgu.CV.CvEnum.LINE_TYPE.CV_AA, 0);
}
motionImageBox.Image = coloredMotion2;
I have many shapes in image which I want to save their contours in arrays .
I mean that I want the coordinates for contours for shape 1 in array 1 , for shape 2 in array 2 ext...
And if there are two shapes how can I draw the shortest line between them using their coordinates?
for example I had this result after many operations on an image
after finding contours :
So I need the coordinates for each shape contour to calculate the shortest distance between them
You can refer this link & this wiki for detecting Contours from an Image.
To find the min Distance from two Shapes follow the following steps:
Find the two Contours for which you want to find the min distance between them.
Cycle through each point in the Two contours & find the distance between them.
Take the minimum Distance by comparing all other distances & Mark that Points.
Here is the EMGUCV Implementation for this algorithm.
private void button2_Click(object sender, EventArgs e)
{
Image<Gray, byte> Img_Scene_Gray = Img_Source_Bgr.Convert<Gray, byte>();
Image<Bgr, byte> Img_Result_Bgr = Img_Source_Bgr.Copy();
LineSegment2D MinIntersectionLineSegment = new LineSegment2D();
Img_Scene_Gray = Img_Scene_Gray.ThresholdBinary(new Gray(10), new Gray(255));
#region Finding Contours
using (MemStorage Scene_ContourStorage = new MemStorage())
{
for (Contour<Point> Contours_Scene = Img_Scene_Gray.FindContours(CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
RETR_TYPE.CV_RETR_EXTERNAL, Scene_ContourStorage); Contours_Scene != null; Contours_Scene = Contours_Scene.HNext)
{
if (Contours_Scene.Area > 25)
{
if (Contours_Scene.HNext != null)
{
MinIntersectionLine(Contours_Scene, Contours_Scene.HNext, ref MinIntersectionLineSegment);
Img_Result_Bgr.Draw(MinIntersectionLineSegment, new Bgr(Color.Green), 2);
}
Img_Result_Bgr.Draw(Contours_Scene, new Bgr(Color.Red), 1);
}
}
}
#endregion
imageBox1.Image = Img_Result_Bgr;
}
void MinIntersectionLine(Contour<Point> a, Contour<Point> b,ref LineSegment2D Line)
{
double MinDist = 10000000;
for (int i = 0; i < a.Total; i++)
{
for (int j = 0; j < b.Total; j++)
{
double Dist = Distance_BtwnPoints(a[i], b[j]);
if (Dist < MinDist)
{
Line.P1 = a[i];
Line.P2 = b[j];
MinDist = Dist;
}
}
}
}
double Distance_BtwnPoints(Point p, Point q)
{
int X_Diff = p.X - q.X;
int Y_Diff = p.Y - q.Y;
return Math.Sqrt((X_Diff * X_Diff) + (Y_Diff * Y_Diff));
}
i'm implementing Harries Corner detector using Emgucv
and i've converted the Code to C#
using this link
http://docs.opencv.org/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.html
So i want to access the coordinates of those corners or any information about this interest points
how to do this ?
Thanks
You can make a threshold image of the Harris Corner Image and then iterate over it. This way were the intensity of the point is 255 you have a corner point whit X and Y values on the image. Example:
// create corner strength image and do Harris
m_CornerImage = new Image<Gray, float>(m_SourceImage.Size);
CvInvoke.cvCornerHarris(m_SourceImage, m_CornerImage, 3, 3, 0.01);
// create and show inverted threshold image
m_ThresholdImage = new Image<Gray, Byte>(m_SourceImage.Size);
CvInvoke.cvThreshold(m_CornerImage, m_ThresholdImage, 0.0001, 255.0, Emgu.CV.CvEnum.THRESH.CV_THRESH_BINARY_INV);
imageBox2.Image = m_ThresholdImage;
imageBox1.Image = m_CornerImage;
const double MAX_INTENSITY = 255;
int contCorners = 0;
for (int x = 0; x < m_ThresholdImage.Width; x++)
{
for (int y = 0; y < m_ThresholdImage.Height; y++)
{
Gray imagenP = m_ThresholdImage[y,x];
if (imagenP.Intensity == MAX_INTENSITY)
{
//X and Y are harris point cordenates
}
}
}
In OpenCV I use std::vector<std::vector<cv::Point>>::const_iterator like the code here:
std::vector<std::vector<cv::Point>> contours;
cv::findContours(contour,contours,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE);
std::vector<std::vector<cv::Point>>::const_iterator itContours = contours.begin();
while(itContours != contours.end())
{
if(Condition1)
itContours = contours.erase(itContours);
else if(Condition2)
itContours = contours.erase(itContours);
else if(Condition3)
itContours = contours.erase(itContours);
else
++itContours;
}
But now I start using EmguCV but I can't find how to do like the code above. How can I do it?
Have a look at the shape detection example in the EMGU.Examples folder. It shows you how to deal with contours. I have copied the relevant code below for your reference but it's much better to have a look at the example.
#region Find triangles and rectangles
List<Triangle2DF> triangleList = new List<Triangle2DF>();
List<MCvBox2D> boxList = new List<MCvBox2D>(); //a box is a rotated rectangle
using (MemStorage storage = new MemStorage()) //allocate storage for contour approximation
for (Contour<Point> contours = cannyEdges.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_LIST, storage); contours != null; contours = contours.HNext)
{
Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.05, storage);
if (currentContour.Area > 250) //only consider contours with area greater than 250
{
if (currentContour.Total == 3) //The contour has 3 vertices, it is a triangle
{
Point[] pts = currentContour.ToArray();
triangleList.Add(new Triangle2DF(
pts[0],
pts[1],
pts[2]
));
}
else if (currentContour.Total == 4) //The contour has 4 vertices.
{
#region determine if all the angles in the contour are within [80, 100] degree
bool isRectangle = true;
Point[] pts = currentContour.ToArray();
LineSegment2D[] edges = PointCollection.PolyLine(pts, true);
for (int i = 0; i < edges.Length; i++)
{
double angle = Math.Abs(
edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i]));
if (angle < 80 || angle > 100)
{
isRectangle = false;
break;
}
}
#endregion
if (isRectangle) boxList.Add(currentContour.GetMinAreaRect());
}
}
}
#endregion
Let me know if you need any additional help and if any errors pop up,
Cheers,
Chris