It is necessary to cut an object by photo using a mask and blur the edges.
Code to search for edges. But what about the blur?
Mat imgGray = new Mat();
Cv2.CvtColor(segmentationMap, imgGray, ColorConversionCodes.BGR2GRAY, 0);
Cv2.Blur(imgGray,imgGray, new Size(3,3));
Mat imgCanny = new Mat();
Cv2.Canny(imgGray,imgCanny, 2, 3);
Point[][] contours;
HierarchyIndex[] hierarchy;
Cv2.FindContours(imgCanny, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);
Mat drawing = new Mat(imgCanny.Size(), MatType.CV_8UC3);
for (int i = 0; i < contours.Length; i++)
{
Scalar color = Scalar.Red;
Cv2.DrawContours(drawing,contours, i, color);
}
Paint the area inside the contour.
Cut by mask
Blur the edges
Mask
Contour (But he is not closed for some reason)
Related
My project need to crop the image automatically to remove the white space around the drawing (Lattice).
Here is my code
grayImage = grayImage.ThresholdBinary(new Gray(threshold), new Gray(255));
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
CvInvoke.FindContours(grayImage, contours, null, RetrType.External, ChainApproxMethod.ChainApproxSimple);
for (int i = 0; i < contours.Size; i++)
{
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
if (rect.Width > minWidth && rect.Height > minHeight)
{
CvInvoke.DrawContours(image, contours, i, new MCvScalar(255, 0, 0), 2);
}
}
imageBox.Image = image;
The main issue is that FindContours finds white contours, and the image background is white.
We use ThresholdBinaryInv instead of ThresholdBinary.
ThresholdBinaryInv applies threshold and invert black and white after applying the threshold (the pattern is going to be white on black instead of black on white).
Code sample:
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using System.Drawing;
namespace Testings
{
public class Program
{
static void Main(string[] args)
{
int threshold = 254;
var image_file_name = #"auxetic_lattice_screen.png";
Mat image = new Mat(image_file_name, Emgu.CV.CvEnum.ImreadModes.Color); //Read input image as BGR
var grayImage = new Image<Gray, System.Byte>(image_file_name); //Read input image as Grayscale
//grayImage = grayImage.ThresholdBinary(new Gray(threshold), new Gray(255));
grayImage = grayImage.ThresholdBinaryInv(new Gray(threshold), new Gray(255)); // Use ThresholdBinaryInv - invert black and white so that the pattern be white
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
CvInvoke.FindContours(grayImage, contours, null, RetrType.External, ChainApproxMethod.ChainApproxSimple);
int minWidth = grayImage.Width / 2;
int minHeight = grayImage.Height / 2;
Mat croppedImage = null;
for (int i = 0; i < contours.Size; i++)
{
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
if (rect.Width > minWidth && rect.Height > minHeight)
{
croppedImage = new Mat(image.Clone(), rect); //Crop the rectangle
CvInvoke.DrawContours(image, contours, i, new MCvScalar(255, 0, 0), 2);
}
}
//Show images for testing
CvInvoke.Imshow("grayImage", grayImage);
CvInvoke.Imshow("image", image);
CvInvoke.WaitKey();
CvInvoke.Imwrite("output_image.png", image); //Save output for testing
CvInvoke.Imwrite("croppedImage.png", croppedImage); //Save output for testing
}
}
}
Result:
Using CvInvoke.Canny and CvInvoke.FindContours I'm trying to find the rectangle containing the item name (Schematic: Maple Desk). This rectangle is shown in the image below:
I'm able to find a lot of rectangles but I'm not able to get this one. Tried a lot of different thresholds for Canny but to no effect. The following image shows all rectangles I currently get:
Any ideas how to tackle this? Do I need to use other thresholds or another approach? I already experimented using grayscale and blurring but that didn't give better result. I added my current source below and the original image I'm using is this:
public Mat ProcessImage(Mat img)
{
UMat filter = new UMat();
UMat cannyEdges = new UMat();
Mat rectangleImage = new Mat(img.Size, DepthType.Cv8U, 3);
//Convert the image to grayscale and filter out the noise
//CvInvoke.CvtColor(img, filter, ColorConversion.Bgr2Gray);
//Remove noise
//CvInvoke.GaussianBlur(filter, filter, new System.Drawing.Size(3, 3), 1);
// Canny and edge detection
double cannyThreshold = 1.0; //180.0
double cannyThresholdLinking = 1.0; //120.0
//CvInvoke.Canny(filter, cannyEdges, cannyThreshold, cannyThresholdLinking);
CvInvoke.Canny(img, cannyEdges, cannyThreshold, cannyThresholdLinking);
// Find rectangles
List<RotatedRect> rectangleList = new List<RotatedRect>();
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(cannyEdges, contours, null, RetrType.List, 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.05, true);
// Only consider contours with area greater than 250
if (CvInvoke.ContourArea(approxContour, false) > 250)
{
// The contour has 4 vertices.
if (approxContour.Size == 4)
{
// Determine if all the angles in the contour are within [80, 100] degree
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) rectangleList.Add(CvInvoke.MinAreaRect(approxContour));
}
}
}
}
}
// Draw rectangles
foreach (RotatedRect rectangle in rectangleList)
{
CvInvoke.Polylines(rectangleImage, Array.ConvertAll(rectangle.GetVertices(), System.Drawing.Point.Round), true,
new Bgr(Color.DarkOrange).MCvScalar, 2);
}
//Drawing a light gray frame around the image
CvInvoke.Rectangle(rectangleImage,
new Rectangle(System.Drawing.Point.Empty,
new System.Drawing.Size(rectangleImage.Width - 1, rectangleImage.Height - 1)),
new MCvScalar(120, 120, 120));
//Draw the labels
CvInvoke.PutText(rectangleImage, "Rectangles", new System.Drawing.Point(20, 20),
FontFace.HersheyDuplex, 0.5, new MCvScalar(120, 120, 120));
Mat result = new Mat();
CvInvoke.VConcat(new Mat[] { img, rectangleImage }, result);
return result;
}
Edit 1:
After some more fine tuning with the following thresholds for Canny
cannyThreshold 100
cannyThresholdLinking 400
And using a minimum size for all ContourAreas of 10000 I can get the following result:
Edit 2:
For those interested, solved using the current detection, no changes were needed. Used the 2 detected rectangles in the screenshot above to get the location of the missing rectangle containing the item name.
Result can be found here:
https://github.com/josdemmers/NewWorldCompanion
For those interested, solved using the current detection, no changes were needed. Used the 2 detected rectangles in the screenshot above to get the location of the missing rectangle containing the item name.
Result can be found here: https://github.com/josdemmers/NewWorldCompanion
I used the Accord Framework to implement the cross correlation between two images. My goal is to find by how much (how many pixels, and in which direction) the second image is shifted compared to the first one.
The basic formula I used is the following :
corr(a, b) = ifft(fft(a_and_zeros) * conj(fft(b_and_zeros)))
I'll put the whole code at the end of my message, everything happens on a Click event. My initial images were stored in 1024*768 bitmaps. So here is the steps I have taken :
I cropped the 2 images into the 4 zones that were interesting for me (ExpInit, ExpFinal, RefInit, RefFinal), that I want to correlate two by two (ExpInit with ExpFinal and RefInit with RefFinal). Those cropped images have dimensions of 1024*131.
I put those cropped images in the center of new bitmaps with 2^n dimensions (2048*512).
Applied a Grayscaling filter to get 8bppIndexed PixelFormat.
Converted each image to ComplexImageformat and applied forward FFT on the 4 images.
Complex-conjugating every elements in the RefFinal and ExpFinal fourier-transformed ComplexImage.
Execute element-wise multiplication between the ComplexImageobjects I want to cross-correlate (ExpInit with ExpFinal, RefInit wit RefFinal).
Apply the backward FFT to the product of the element-wise multiplication. Tadaaa, my cross-correlation is done, and I have two Complex[,] objects with the dimensions of my images (2048*512 pixels)
Now I want to answer my initial question : by how much (how many pixels, and in which direction) is the ExpFinal (respectively RefFinal) image shifted compared to the ExpInit (respectively ExpFinal). Here I am left puzzled.
I have the intutition I should be drawing a 3D graph with my Complex[,] object, where x and y are the index in the array, and z the value at the index, and search for the max value, but how do I do that with complex numbers ? Do I use only the Re part ? Only the Im part ? The amplitude ? Or am I completely mistaken ?
Bonus question : what is a good library for drawing such graphs ?
Here is the whole code for the described cross-correlation :
private void crosscorrButton_Click(object sender, EventArgs e)
{
// Cropping all 4 sections (RefInit, ExpInit, RefFinal, ExpFinal) and placing them in the center of new Bitmaps with 2^n dimensions
Rectangle rExp = new Rectangle(1, 157, 1024, 131);
Bitmap ExpInitCrop = new Bitmap(rExp.Width, rExp.Height);
Graphics g = Graphics.FromImage(ExpInitCrop);
g.DrawImage(BMInit, -rExp.X, -rExp.Y);
Bitmap ExpInitLarge = new Bitmap(2048, 512);
using (Graphics largeGraphics = Graphics.FromImage(ExpInitLarge))
{
largeGraphics.DrawImage(ExpInitCrop, 513, 190);
}
Rectangle rRef = new Rectangle(1, 484, 1024, 131);
Bitmap RefInitCrop = new Bitmap(rRef.Width, rRef.Height);
Graphics h = Graphics.FromImage(RefInitCrop);
h.DrawImage(BMInit, -rRef.X, -rRef.Y);
Bitmap RefInitLarge = new Bitmap(2048, 512);
using (Graphics largeGraphics = Graphics.FromImage(RefInitLarge))
{
largeGraphics.DrawImage(RefInitCrop, 513, 190);
}
Bitmap ExpFinalCrop = new Bitmap(rExp.Width, rExp.Height);
Graphics i = Graphics.FromImage(ExpFinalCrop);
i.DrawImage(BMFinal, -rExp.X, -rExp.Y);
Bitmap ExpFinalLarge = new Bitmap(2048, 512);
using (Graphics largeGraphics = Graphics.FromImage(ExpFinalLarge))
{
largeGraphics.DrawImage(ExpFinalCrop, 513, 190);
}
Bitmap RefFinalCrop = new Bitmap(rRef.Width, rRef.Height);
Graphics j = Graphics.FromImage(RefFinalCrop);
j.DrawImage(BMFinal, -rRef.X, -rRef.Y);
Bitmap RefFinalLarge = new Bitmap(2048, 512);
using (Graphics largeGraphics = Graphics.FromImage(RefFinalLarge))
{
largeGraphics.DrawImage(RefFinalCrop, 513, 190);
}
// Grayscalling the 4 sections to get 8bppIndexed PixelFormat
Accord.Imaging.Filters.Grayscale filterGS = new Accord.Imaging.Filters.Grayscale(0.2125, 0.7154, 0.0721);
Bitmap RefFinalLargeGS = filterGS.Apply(RefFinalLarge);
Bitmap ExpFinalLargeGS = filterGS.Apply(ExpFinalLarge);
Bitmap RefInitLargeGS = filterGS.Apply(RefInitLarge);
Bitmap ExpInitLargeGS = filterGS.Apply(ExpInitLarge);
// FFT on the 4 sections
Accord.Imaging.ComplexImage ExpInitComplex = Accord.Imaging.ComplexImage.FromBitmap(ExpInitLargeGS);
ExpInitComplex.ForwardFourierTransform();
Accord.Imaging.ComplexImage RefInitComplex = Accord.Imaging.ComplexImage.FromBitmap(RefInitLargeGS);
RefInitComplex.ForwardFourierTransform();
Accord.Imaging.ComplexImage ExpFinalComplex = Accord.Imaging.ComplexImage.FromBitmap(ExpFinalLargeGS);
ExpFinalComplex.ForwardFourierTransform();
Accord.Imaging.ComplexImage RefFinalComplex = Accord.Imaging.ComplexImage.FromBitmap(RefFinalLargeGS);
RefFinalComplex.ForwardFourierTransform();
//Conjugating the ExpFinal and RefFinal section
Complex[,] CompConjExpFinal = new Complex[ExpFinalComplex.Height, ExpFinalComplex.Width];
Complex[,] CompConjRefFinal = new Complex[RefFinalComplex.Height, RefFinalComplex.Width];
for (int l = 0; l < ExpFinalComplex.Height; l++)
{
for (int m = 0; m < ExpFinalComplex.Width; m++)
{
CompConjExpFinal[l, m] = System.Numerics.Complex.Conjugate(ExpFinalComplex.Data[l, m]);
ExpFinalComplex.Data[l, m] = CompConjExpFinal[l, m];
}
}
for (int l = 0; l < RefFinalComplex.Height; l++)
{
for (int m = 0; m < RefFinalComplex.Width; m++)
{
CompConjRefFinal[l, m] = System.Numerics.Complex.Conjugate(RefFinalComplex.Data[l, m]);
RefFinalComplex.Data[l, m] = CompConjRefFinal[l, m];
}
}
//Element-wise multiplication of the complex arrays two by two
Complex[,] ExpMultipliedMatrix = new Complex[ExpFinalComplex.Height, ExpFinalComplex.Width];
Complex[,] RefMultipliedMatrix = new Complex[RefFinalComplex.Height, RefFinalComplex.Width];
for (int l = 0; l < ExpFinalComplex.Height; l++)
{
for (int m = 0; m < ExpFinalComplex.Width; m++)
{
ExpMultipliedMatrix[l, m] = System.Numerics.Complex.Multiply(ExpInitComplex.Data[l, m], ExpFinalComplex.Data[l, m]);
RefMultipliedMatrix[l, m] = System.Numerics.Complex.Multiply(RefInitComplex.Data[l, m], RefFinalComplex.Data[l, m]);
}
}
//InverseFFT
Complex[,] CrossCorrExpMatrix = new Complex[ExpFinalComplex.Height, ExpFinalComplex.Width];
Complex[,] CrossCorrRefMatrix = new Complex[RefFinalComplex.Height, RefFinalComplex.Width];
Accord.Math.FourierTransform.FFT2(ExpMultipliedMatrix, FourierTransform.Direction.Backward);
Accord.Math.FourierTransform.FFT2(RefMultipliedMatrix, FourierTransform.Direction.Backward);
CrossCorrExpMatrix = ExpMultipliedMatrix;
CrossCorrRefMatrix = RefMultipliedMatrix;
}
Thanks a lot !
The imaginary part of the result should be 0 (or within numerical error). To find the shift you should be looking at the location of the peak of the correlation's amplitude (but unless you've got one is the negative image of the other, that's likely to correspond to the peak of the correlation's real part). The main thing to be careful about: since you centered both images, an extra shift (of half the image size) will be introduced.
As for viewing the graph, you could fairly easily map the result to a grayscale image and view it with your favorite image viewer.
I want to draw a rotated rectangle. The canny image is not one counter it is a set of counters. So I want to get all points of all counters. e.g.
Points[] pts = points of all counters.
Please see attached image.
The canny show all edges in your image, not an external contour. This one way (but not the only one) to do object detection (foreground / background extraction).
The following snippet extract object roi (Min area rect) by using a canny edge detector. Be carrefull about canny thresh parameteres comming from otsu threshold.
Hope it helps.
Detection result
Image<Gray,byte> imageFullSize = new Image<Gray, byte>(imagePath);
Rectangle roi = new Rectangle(Your Top Left X,Your Top Left Y,Your ROI Width,Your ROI Height);
//Now get a roi (No copy, it is a new header pointing original image
Image<Gray,byte> image = imageFullSize.GetSubRect(roi);
//Step 1 : contour detection using canny
//Gaussian noise removal, eliminate background false détections
image._SmoothGaussian(15);
//Canny thresh based on otsu threshold value:
Mat binary = new Mat();
double otsuThresh = CvInvoke.Threshold(image, binary, 0, 255, ThresholdType.Binary | ThresholdType.Otsu);
double highThresh = otsuThresh;
double lowThresh = otsuThresh * 0.5;
var cannyImage = image.Canny(lowThresh, highThresh);
//Step 2 : contour extraction
Mat hierarchy=new Mat();
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
CvInvoke.FindContours(cannyImage,contours,hierarchy,RetrType.External,ChainApproxMethod.ChainApproxSimple);
//Step 3 : contour fusion
List<Point> points=new List<Point>();
for(int i = 0; i < contours.Size; i++)
{
points.AddRange(contours[i].ToArray());
}
//Step 4 : Rotated rect
RotatedRect minAreaRect = CvInvoke.MinAreaRect(points.Select(pt=>new PointF(pt.X,pt.Y)).ToArray());
Point[] vertices = minAreaRect.GetVertices().Select(pt => new Point((int)pt.X, (int)pt.Y)).ToArray();
//Step 5 : draw result
Image <Bgr,byte > colorImageFullSize =new Image<Bgr, byte>(imagePath);
Image <Bgr,byte > colorImage =colorImageFullSize.GetSubRect(roi);
colorImage.Draw(vertices,new Bgr(Color.Red),2 );
I am trying to detect Circle inside Rectangle in AForge. I have successfully determined Rectangles but unable to find circles inside Rectangle. How to find shape inside another shape in AForge.
string strPath = Server.MapPath("~/Recipt001.png");
Bitmap myBitmap = new Bitmap(strPath);
//Some filters Grayscale, invert, threshold
//Blod Filtering
BlobCounter blobCounter = new BlobCounter();
blobCounter.ProcessImage(temp);
blobCounter.ObjectsOrder = ObjectsOrder.YX;
blobCounter.FilterBlobs = true;
Blob[] blobs = blobCounter.GetObjectsInformation();
Graphics g = Graphics.FromImage(myBitmap);
Pen redPen = new Pen(Color.Red, 2);
SimpleShapeChecker shapeChecker = new SimpleShapeChecker();
// dictionary of color to highlight different shapes
Dictionary<PolygonSubType, Color> colors = new Dictionary<PolygonSubType, Color>();
colors.Add(PolygonSubType.Unknown, Color.White);
colors.Add(PolygonSubType.Trapezoid, Color.Orange);
colors.Add(PolygonSubType.Parallelogram, Color.Red);
colors.Add(PolygonSubType.Rectangle, Color.Green);
colors.Add(PolygonSubType.Square, Color.Blue);
colors.Add(PolygonSubType.Rhombus, Color.Gray);
colors.Add(PolygonSubType.EquilateralTriangle, Color.Pink);
colors.Add(PolygonSubType.IsoscelesTriangle, Color.Purple);
colors.Add(PolygonSubType.RectangledTriangle, Color.SkyBlue);
colors.Add(PolygonSubType.RectangledIsoscelesTriangle, Color.SeaGreen);
for (int i = 0, n = blobs.Length; i < n; i++)
{
List<IntPoint> corners;
List<IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints(blobs[i]);
Point center;
double radius;
if (shapeChecker.IsQuadrilateral(edgePoints, out corners))
{
if (shapeChecker.CheckPolygonSubType(corners) == PolygonSubType.Rectangle)
{
g.DrawPolygon(redPen, ToPointsArray(corners));
}
}
}
redPen.Dispose();
g.Dispose();
None of image processing libraries and even image processing in MATLAB lets you search ROI inside ROI (ROI - region of intrest like rectangles or circles). Concept is CROP REGION -> SEARCH OBJECTS IN REGION
So first locate primary rectangles, thereafter crop image to rectangles and perform circle search inside them. Otherwise search for all circles and all rectangles and then classify circles to be belonging to which rectangle using simple maths.