Hi have already function solution but one issue:
// The screenshot will be stored in this bitmap.
Bitmap capture = new Bitmap(rec.Width, rec.Height, PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(capture))
{
g.CopyFromScreen(rec.Location, new System.Drawing.Point(0, 0), rec.Size);
}
MCvSURFParams surfParam = new MCvSURFParams(500, false);
SURFDetector surfDetector = new SURFDetector(surfParam);
// Template image
Image<Gray, Byte> modelImage = new Image<Gray, byte>("template.jpg");
// Extract features from the object image
ImageFeature[] modelFeatures = surfDetector.DetectFeatures(modelImage, null);
// Prepare current frame
Image<Gray, Byte> observedImage = new Image<Gray, byte>(capture);
ImageFeature[] imageFeatures = surfDetector.DetectFeatures(observedImage, null);
// Create a SURF Tracker using k-d Tree
Features2DTracker tracker = new Features2DTracker(modelFeatures);
Features2DTracker.MatchedImageFeature[] matchedFeatures = tracker.MatchFeature(imageFeatures, 2);
matchedFeatures = Features2DTracker.VoteForUniqueness(matchedFeatures, 0.8);
matchedFeatures = Features2DTracker.VoteForSizeAndOrientation(matchedFeatures, 1.5, 20);
HomographyMatrix homography = Features2DTracker.GetHomographyMatrixFromMatchedFeatures(matchedFeatures);
// Merge the object image and the observed image into one image for display
Image<Gray, Byte> res = modelImage.ConcateVertical(observedImage);
#region draw lines between the matched features
foreach (Features2DTracker.MatchedImageFeature matchedFeature in matchedFeatures)
{
PointF p = matchedFeature.ObservedFeature.KeyPoint.Point;
p.Y += modelImage.Height;
res.Draw(new LineSegment2DF(matchedFeature.SimilarFeatures[0].Feature.KeyPoint.Point, p), new Gray(0), 1);
}
#endregion
#region draw the project region on the image
if (homography != null)
{
// draw a rectangle along the projected model
Rectangle rect = modelImage.ROI;
PointF[] pts = new PointF[] {
new PointF(rect.Left, rect.Bottom),
new PointF(rect.Right, rect.Bottom),
new PointF(rect.Right, rect.Top),
new PointF(rect.Left, rect.Top)
};
homography.ProjectPoints(pts);
for (int i = 0; i < pts.Length; i++)
pts[i].Y += modelImage.Height;
res.DrawPolyline(Array.ConvertAll<PointF, Point>(pts, Point.Round), true, new Gray(255.0), 2);
}
#endregion
pictureBoxScreen.Image = res.ToBitmap();
the result is:
And my problem is that, function homography.ProjectPoints(pts);
Get only first occurrence of pattern (white rectangle in pic above)
How i can Project all occurrence of template, respectively how I can get occurrence of template rectangle in image
I face a problem similar to yours in my master thesis. Basically you have two options:
Use a clustering such as Hierarchical k-means or a point density one such as DBSCAN (it depends on two parameters but you can make it threshold free in bidimensional R^2 space)
Use a multiple robust model fitting estimation techniques such as JLinkage. In this more advanced technique you clusters points that share an homography instead of cluster points that close to each other in euclidean space.
Once you partition your matches in "clusters" you can estimate homographies between matches belonging to correspondant clusters.
Related
I am having three image of pan card for testing skew of image using emgucv and c#.
1st image which is on top Detected 180 degree working properly.
2nd image which is in middle Detected 90 dgree should detected as 180 degree.
3rd image Detected 180 degree should detected as 90 degree.
One observation I am having that i wanted to share here is when i crop unwanted part of image from up and down side of pan card using paint brush, it gives me expected result using below mention code.
Now i wanted to understand how i can remove the unwanted part using programming.
I have played with contour and roi but I am not able to figure out how to fit the same. I am not able to understand whether emgucv itself selects contour or I have to do something.
Please suggest any suitable code example.
Please check code below for angle detection and please help me. Thanks in advance.
imgInput = new Image<Bgr, byte>(impath);
Image<Gray, Byte> img2 = imgInput.Convert<Gray, Byte>();
Bitmap imgs;
Image<Gray, byte> imgout = imgInput.Convert<Gray, byte>().Not().ThresholdBinary(new Gray(50), new Gray(125));
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
Emgu.CV.Mat hier = new Emgu.CV.Mat();
var blurredImage = imgInput.SmoothGaussian(5, 5, 0 , 0);
CvInvoke.AdaptiveThreshold(imgout, imgout, 255, Emgu.CV.CvEnum.AdaptiveThresholdType.GaussianC, Emgu.CV.CvEnum.ThresholdType.Binary, 5, 45);
CvInvoke.FindContours(imgout, contours, hier, Emgu.CV.CvEnum.RetrType.External, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxSimple);
if (contours.Size >= 1)
{
for (int i = 0; i <= contours.Size; i++)
{
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
RotatedRect box = CvInvoke.MinAreaRect(contours[i]);
PointF[] Vertices = box.GetVertices();
PointF point = box.Center;
PointF edge1 = new PointF(Vertices[1].X - Vertices[0].X, Vertices[1].Y - Vertices[0].Y);
PointF edge2 = new PointF(Vertices[2].X - Vertices[1].X, Vertices[2].Y - Vertices[1].Y);
double r = edge1.X + edge1.Y;
double edge1Magnitude = Math.Sqrt(Math.Pow(edge1.X, 2) + Math.Pow(edge1.Y, 2));
double edge2Magnitude = Math.Sqrt(Math.Pow(edge2.X, 2) + Math.Pow(edge2.Y, 2));
PointF primaryEdge = edge1Magnitude > edge2Magnitude ? edge1 : edge2;
double primaryMagnitude = edge1Magnitude > edge2Magnitude ? edge1Magnitude : edge2Magnitude;
PointF reference = new PointF(1, 0);
double refMagnitude = 1;
double thetaRads = Math.Acos(((primaryEdge.X * reference.X) + (primaryEdge.Y * reference.Y)) / (primaryMagnitude * refMagnitude));
double thetaDeg = thetaRads * 180 / Math.PI;
imgInput = imgInput.Rotate(thetaDeg, new Bgr());
imgout = imgout.Rotate(box.Angle, new Gray());
Bitmap bmp = imgout.Bitmap;
break;
}
}
The Problem
Let us start with the problem before the solution:
Your Code
When you submit code, asking for help, at least make some effort to "clean" it. Help people help you! There's so many lines of code here that do nothing. You declare variables that are never used. Add some comments that let people know what it is that you think your code should do.
Bitmap imgs;
var blurredImage = imgInput.SmoothGaussian(5, 5, 0, 0);
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
PointF point = box.Center;
double r = edge1.X + edge1.Y;
// Etc
Adaptive Thresholding
The following line of code produces the following images:
CvInvoke.AdaptiveThreshold(imgout, imgout, 255, Emgu.CV.CvEnum.AdaptiveThresholdType.GaussianC, Emgu.CV.CvEnum.ThresholdType.Binary, 5, 45);
Image 1
Image 2
Image 3
Clearly this is not what you're aiming for since the primary contour, the card edge, is completely lost. As a tip, you can always use the following code to display images at runtime to help you with debugging.
CvInvoke.NamedWindow("Output");
CvInvoke.Imshow("Output", imgout);
CvInvoke.WaitKey();
The Soltuion
Since your in example images the card is primarily a similar Value (in the HSV sense) to the background. I do not think simple gray scale thresholding is the correct approach in this case. I purpose the following:
Algorithm
Use Canny Edge Detection to extract the edges in the image.
Dilate the edges so as the card content combines.
Use Contour Detection to filter for the combined edges with the largest bounding.
Fit this primary contour with a rotated rectangle in order to extract the corner points.
Use the corner points to define a transformation matrix to be applied using WarpAffine.
Warp and crop the image.
The Code
You may wish to experiment with the parameters of the Canny Detection and Dilation.
// Working Images
Image<Bgr, byte> imgInput = new Image<Bgr, byte>("Test1.jpg");
Image<Gray, byte> imgEdges = new Image<Gray, byte>(imgInput.Size);
Image<Gray, byte> imgDilatedEdges = new Image<Gray, byte>(imgInput.Size);
Image<Bgr, byte> imgOutput;
// 1. Edge Detection
CvInvoke.Canny(imgInput, imgEdges, 25, 80);
// 2. Dilation
CvInvoke.Dilate(
imgEdges,
imgDilatedEdges,
CvInvoke.GetStructuringElement(
ElementShape.Rectangle,
new Size(3, 3),
new Point(-1, -1)),
new Point(-1, -1),
5,
BorderType.Default,
new MCvScalar(0));
// 3. Contours Detection
VectorOfVectorOfPoint inputContours = new VectorOfVectorOfPoint();
Mat hierarchy = new Mat();
CvInvoke.FindContours(
imgDilatedEdges,
inputContours,
hierarchy,
RetrType.External,
ChainApproxMethod.ChainApproxSimple);
VectorOfPoint primaryContour = (from contour in inputContours.ToList()
orderby contour.GetArea() descending
select contour).FirstOrDefault();
// 4. Corner Point Extraction
RotatedRect bounding = CvInvoke.MinAreaRect(primaryContour);
PointF topLeft = (from point in bounding.GetVertices()
orderby Math.Sqrt(Math.Pow(point.X, 2) + Math.Pow(point.Y, 2))
select point).FirstOrDefault();
PointF topRight = (from point in bounding.GetVertices()
orderby Math.Sqrt(Math.Pow(imgInput.Width - point.X, 2) + Math.Pow(point.Y, 2))
select point).FirstOrDefault();
PointF botLeft = (from point in bounding.GetVertices()
orderby Math.Sqrt(Math.Pow(point.X, 2) + Math.Pow(imgInput.Height - point.Y, 2))
select point).FirstOrDefault();
PointF botRight = (from point in bounding.GetVertices()
orderby Math.Sqrt(Math.Pow(imgInput.Width - point.X, 2) + Math.Pow(imgInput.Height - point.Y, 2))
select point).FirstOrDefault();
double boundingWidth = Math.Sqrt(Math.Pow(topRight.X - topLeft.X, 2) + Math.Pow(topRight.Y - topLeft.Y, 2));
double boundingHeight = Math.Sqrt(Math.Pow(botLeft.X - topLeft.X, 2) + Math.Pow(botLeft.Y - topLeft.Y, 2));
bool isLandscape = boundingWidth > boundingHeight;
// 5. Define warp crieria as triangles
PointF[] srcTriangle = new PointF[3];
PointF[] dstTriangle = new PointF[3];
Rectangle ROI;
if (isLandscape)
{
srcTriangle[0] = botLeft;
srcTriangle[1] = topLeft;
srcTriangle[2] = topRight;
dstTriangle[0] = new PointF(0, (float)boundingHeight);
dstTriangle[1] = new PointF(0, 0);
dstTriangle[2] = new PointF((float)boundingWidth, 0);
ROI = new Rectangle(0, 0, (int)boundingWidth, (int)boundingHeight);
}
else
{
srcTriangle[0] = topLeft;
srcTriangle[1] = topRight;
srcTriangle[2] = botRight;
dstTriangle[0] = new PointF(0, (float)boundingWidth);
dstTriangle[1] = new PointF(0, 0);
dstTriangle[2] = new PointF((float)boundingHeight, 0);
ROI = new Rectangle(0, 0, (int)boundingHeight, (int)boundingWidth);
}
Mat warpMat = new Mat(2, 3, DepthType.Cv32F, 1);
warpMat = CvInvoke.GetAffineTransform(srcTriangle, dstTriangle);
// 6. Apply the warp and crop
CvInvoke.WarpAffine(imgInput, imgInput, warpMat, imgInput.Size);
imgOutput = imgInput.Copy(ROI);
imgOutput.Save("Output1.bmp");
Two extension methods are used:
static List<VectorOfPoint> ToList(this VectorOfVectorOfPoint vectorOfVectorOfPoint)
{
List<VectorOfPoint> result = new List<VectorOfPoint>();
for (int contour = 0; contour < vectorOfVectorOfPoint.Size; contour++)
{
result.Add(vectorOfVectorOfPoint[contour]);
}
return result;
}
static double GetArea(this VectorOfPoint contour)
{
RotatedRect bounding = CvInvoke.MinAreaRect(contour);
return bounding.Size.Width * bounding.Size.Height;
}
Outputs
Meta Example
I have an image which I grab using a camera. Sometimes, the lighting is uneven in them image. There are some dark shades. This causes incorrect optimal thresholding in EMGU as well as Aforge to process the image for OCR.
This is the image:
This is what I get after thresholding:
How do I correct the lighting? I tried adaptive threshold, gives about the same result. Tried gamma correction too using the code below:
ImageAttributes attributes = new ImageAttributes();
attributes.SetGamma(10);
// Draw the image onto the new bitmap
// while applying the new gamma value.
System.Drawing.Point[] points =
{
new System.Drawing.Point(0, 0),
new System.Drawing.Point(image.Width, 0),
new System.Drawing.Point(0, image.Height),
};
Rectangle rect =
new Rectangle(0, 0, image.Width, image.Height);
// Make the result bitmap.
Bitmap bm = new Bitmap(image.Width, image.Height);
using (Graphics gr = Graphics.FromImage(bm))
{
gr.DrawImage(HSICONV.Bitmap, points, rect,
GraphicsUnit.Pixel, attributes);
}
same result. Please help.
UPDATE:
as per Nathancy's suggestion I converted his code to c# for uneven lighting correction and it works:
Image<Gray, byte> smoothedGrayFrame = grayImage.PyrDown();
smoothedGrayFrame = smoothedGrayFrame.PyrUp();
//canny
Image<Gray, byte> cannyFrame = null;
cannyFrame = smoothedGrayFrame.Canny(50, 50);
//smoothing
grayImage = smoothedGrayFrame;
//binarize
Image<Gray, byte> grayout = grayImage.Clone();
CvInvoke.AdaptiveThreshold(grayImage, grayout, 255, AdaptiveThresholdType.GaussianC, ThresholdType.BinaryInv, Convert.ToInt32(numericmainthreshold.Value) + Convert.ToInt32(numericmainthreshold.Value) % 2 + 1, 1.2d);
grayout._Not();
Mat kernelCl = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new System.Drawing.Point(-1, -1));
CvInvoke.MorphologyEx(grayout, grayout, MorphOp.Close, kernelCl, new System.Drawing.Point(-1, -1), 1, BorderType.Default, new MCvScalar());
Here's an approach:
Convert image to grayscale and Gaussian blur to smooth image
Adaptive threshold to obtain binary image
Perform morphological transformations to smooth image
Dilate to enhance text
Invert image
After converting to grayscale and blurring, we adaptive threshold
There are small holes and imperfections so we perform a morph close to smooth the image
From we here can optionally dilate to enhance the text
Now we invert the image to get our result
I implemented this method in OpenCV and Python but you can adapt the same strategy into C#
import cv2
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, \
cv2.THRESH_BINARY_INV,9,11)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
dilate = cv2.dilate(close, kernel, iterations=1)
result = 255 - dilate
cv2.imshow('thresh', thresh)
cv2.imshow('close', close)
cv2.imshow('dilate', dilate)
cv2.imshow('result', result)
cv2.waitKey()
I am working on a project where I need to identify dots from IR lasers on a surface. I use for that a camera with IR filter
Some input images:
There can be several dots, too. So I tried to sharpen this image from webcam and then use FindContours method of Emgu CV.
There is my code:
public static Image<Gray, byte> Sharpen(Image<Gray, byte> image, int w, int h, double sigma1, double sigma2, int k)
{
w = (w % 2 == 0) ? w - 1 : w;
h = (h % 2 == 0) ? h - 1 : h;
//apply gaussian smoothing using w, h and sigma
var gaussianSmooth = image.SmoothGaussian(w, h, sigma1, sigma2);
//obtain the mask by subtracting the gaussian smoothed image from the original one
var mask = image - gaussianSmooth;
//add a weighted value k to the obtained mask
mask *= k;
//sum with the original image
image += mask;
return image;
}
private void ProcessFrame(object sender, EventArgs arg)
{
Mat frame = new Mat();
if (_capture.Retrieve(frame, CameraDevice))
{
Image<Bgr, byte> original = frame.ToImage<Bgr, byte>();
Image<Gray, byte> img = Sharpen(frame.ToImage<Gray, byte>(), 100, 100, 100, 100, 30);
Image<Gray, byte> thresh = new Image<Gray, byte>(img.Size);
CvInvoke.PyrDown(img, thresh);
CvInvoke.PyrUp(thresh, thresh);
Image<Gray, byte> mask = new Image<Gray, byte>(thresh.Size);
Image<Gray, byte> cannyImg = thresh.Canny(10, 50);
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
Mat hierarchy = new Mat();
CvInvoke.FindContours(
cannyImg,
contours,
hierarchy,
RetrType.External,
ChainApproxMethod.ChainApproxSimple
);
Image<Bgr, byte> resultImage = img.Copy().Convert<Bgr, byte>();
int contCount = contours.Size;
for (int i = 0; i < contCount; i++)
{
using (VectorOfPoint contour = contours[i])
{
resultImage.Draw(CvInvoke.BoundingRectangle(contour), new Bgr(255, 0, 0), 5);
}
}
captureBox.Image = original.Bitmap;
cvBox.Image = resultImage.Bitmap;
}
}
Example of result image:
So it almost all the time works as I expect it to, but framerate is very low. I'm getting like 10-15 fps with resolution of 640x480. I need to be able to do the same thing for 1920x1080 with at least 30 fps. It's my first time with OpenCV and Emgu.CV. What can I do to make it perform better?
I solved this just setting the threshold, so that image turns black and white only. By adjusting the threshold I was able to achieve the same results if not better in terms of clarity, but also performance drastically improved since there is not heavy processing going on
Here is a snippet with ARCore library instead on EmguCV
var bitmap = eventArgs.Frame;
var filter = new Grayscale(0.2125, 0.7154, 0.0721);
var grayImage = filter.Apply(bitmap);
var thresholdFilter = new Threshold(CurrentThreshold);
thresholdFilter.ApplyInPlace(grayImage);
var blobCounter = new BlobCounter();
blobCounter.ProcessImage(grayImage);
var rectangles = blobCounter.GetObjectsRectangles();
I have implemented this code and have detcted logo in couple of images,
I was able to get some results like this but I need to count that how many images contain this logo,
may be something like finding all keypoints of logo inside big image or some thing else.
I can see I have foud the logo inside big image but I want to confirm it programetically, using emguCV.
Please help.
-- edited
this is the piece of code with homography, can you guide me a bit here, because I am totaly new to emguCV and openV please help me counting these inlier
public static Mat Draw(Mat modelImage, Mat observedImage, out long matchTime)
{
Mat homography;
VectorOfKeyPoint modelKeyPoints;
VectorOfKeyPoint observedKeyPoints;
using (VectorOfVectorOfDMatch matches = new VectorOfVectorOfDMatch())
{
Mat mask;
FindMatch(modelImage, observedImage, out matchTime, out modelKeyPoints, out observedKeyPoints, matches,
out mask, out homography);
//Draw the matched keypoints
Mat result = new Mat();// new Size(400,400), modelImage.Depth, modelImage.NumberOfChannels);
Features2DToolbox.DrawMatches(modelImage, modelKeyPoints, observedImage, observedKeyPoints,
matches, result, new MCvScalar(255, 255, 255), new MCvScalar(255, 255, 255), mask);
#region draw the projected region on the image
if (homography != null)
{
//draw a rectangle along the projected model
Rectangle rect = new Rectangle(Point.Empty, modelImage.Size);
PointF[] pts = new PointF[]
{
new PointF(rect.Left, rect.Bottom),
new PointF(rect.Right, rect.Bottom),
new PointF(rect.Right, rect.Top),
new PointF(rect.Left, rect.Top)
};
pts = CvInvoke.PerspectiveTransform(pts, homography);
Point[] points = Array.ConvertAll<PointF, Point>(pts, Point.Round);
using (VectorOfPoint vp = new VectorOfPoint(points))
{
CvInvoke.Polylines(result, vp, true, new MCvScalar(255, 0, 0, 255), 5);
}
}
#endregion
return result;
}
}
I think my answer is a bit to late, but may I can help someone other. With following code snipppet you can count the matching feature points that belongs to you question (counting lines). The importents variables is the mask variable. It contains the informations.
private int CountHowManyParisExist(Mat mask) {
Matrix<Byte> matrix = new Matrix<Byte>(mask.Rows, mask.Cols);
mask.CopyTo(matrix);
var matched = matrix.ManagedArray;
var list = matched.OfType<byte>().ToList();
var count = list.Count(a => a.Equals(1));
return count;
}
I am working with emguCV for finding contours essential points then saving this point in a file and user redraw this shape in future. so, my goal is this image:
example
my solution is this:
1. import image to picturebox
2. edge detection with canny algorithm
3. finding contours and save points
I found a lot of points with below codes but i can't drawing first shape with this point!
using Emgu.CV;
using Emgu.Util;
private void button1_Click(object sender, EventArgs e)
{
Bitmap bmp = new Bitmap(pictureBox1.Image);
Image<Bgr, Byte> img = new Image<Bgr, byte>(bmp);
Image<Gray, Byte> gray = img.Convert<Gray, Byte>().PyrDown().PyrUp();
Gray cannyThreshold = new Gray(80);
Gray cannyThresholdLinking = new Gray(120);
Gray circleAccumulatorThreshold = new Gray(120);
Image<Gray, Byte> cannyEdges = gray.Canny(cannyThreshold, cannyThresholdLinking).Not();
Bitmap color;
Bitmap bgray;
IdentifyContours(cannyEdges.Bitmap, 50, true, out bgray, out color);
pictureBox1.Image = color;
}
public void IdentifyContours(Bitmap colorImage, int thresholdValue, bool invert, out Bitmap processedGray, out Bitmap processedColor)
{
Image<Gray, byte> grayImage = new Image<Gray, byte>(colorImage);
Image<Bgr, byte> color = new Image<Bgr, byte>(colorImage);
grayImage = grayImage.ThresholdBinary(new Gray(thresholdValue), new Gray(255));
if (invert)
{
grayImage._Not();
}
using (MemStorage storage = new MemStorage())
{
for (Contour<Point> contours = grayImage.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.015, storage);
if (currentContour.BoundingRectangle.Width > 20)
{
CvInvoke.cvDrawContours(color, contours, new MCvScalar(255), new MCvScalar(255), -1, 1, Emgu.CV.CvEnum.LINE_TYPE.EIGHT_CONNECTED, new Point(0, 0));
color.Draw(currentContour.BoundingRectangle, new Bgr(0, 255, 0), 1);
}
Point[] pts = currentContour.ToArray();
foreach (Point p in pts)
{
//add points to listbox
listBox1.Items.Add(p);
}
}
}
processedColor = color.ToBitmap();
processedGray = grayImage.ToBitmap();
}
In your code you have added contour approximation operation
Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.015, storage);
This contour approximation will approximate your Contour to a nearest polygon & so your actual points got shifted. If you want to reproduce the same image you need not to do any approximation.
Refer this thread.