How to align ROI bounding rectangle to main image coordinate - c#

I'am decoding a QR code from an image, since the image is large and have other unwanted graphics I'm drawing an roi around the code and decoding it. Everything works, but when i try to draw a bounding box around the QR code, it is drawn somewhere else. How can I align the rectangle in the same area of the main image?
This is what im getting - The red rectangle is the roi, the green one is the bounding box.
Here is the code :
Image<Gray, byte> Gray_Image = My_Image.Convert<Gray, byte>();
Gray_Image.ROI = Coderect;
Gray_Image._Not();
CvInvoke.cvThreshold(Gray_Image,Gray_Image,50,255.0,Emgu.CV.CvEnum.THRESH.CV_THRESH_BINARY);
Gray_Image.Dilate(5);
StructuringElementEx element = new StructuringElementEx(3, 3, 1, 1, Emgu.CV.CvEnum.CV_ELEMENT_SHAPE.CV_SHAPE_RECT);
CvInvoke.cvMorphologyEx(Gray_Image, Gray_Image, IntPtr.Zero, element, Emgu.CV.CvEnum.CV_MORPH_OP.CV_MOP_CLOSE, 18);
var contour = Gray_Image.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL);
CvInvoke.cvRectangle(My_Image, new Point(contour.BoundingRectangle.X, contour.BoundingRectangle.Y), new Point(contour.BoundingRectangle.X + contour.BoundingRectangle.Width, contour.BoundingRectangle.Y + contour.BoundingRectangle.Height), new MCvScalar(0, 255, 0), 4, Emgu.CV.CvEnum.LINE_TYPE.EIGHT_CONNECTED, 0);
pictureBox1.Image = My_Image.Bitmap;

I found the solution.
Since I know the (roiX,roiY) of the roi and i also know the (bbX,bbY,bbW,bbH) of the bounding box.
By adding the X of roi and X of bounding box i got the X of the location, similarly for Y.
I simply had to do the following :
cvInvoke.cvRectangle(My_Image, new Point(roiX+bbX, roiY+bbY), new Point((roiX+bbX)+bbW,(roiY+bbY)+bbH), new McvScalar(0,255,0), Line_Type.Eight_Connected,0);

Related

Shift Emgu.CV.Image to the right using WarpAffine()

my goal is, to shift an Image an x-amount of pixels to the right. I guess this can be achieved by using WarpAffine. This is at least what my reseach tells me. I used quite a variety of different approaches, like:
CvInvoke.WarpAffine(realImage, transformedImage, transformationMatrix, new Size(realImage.Size);
//or while m is the Mat used to create realImage
transImage.WarpAffine(m,Emgu.CV.CvEnum.Inter.Area,Emgu.CV.CvEnum.Warp.Default,Emgu.CV.CvEnum.BorderType.Default,new Gray());
I get the Exeption:
Exception thrown: 'Emgu.CV.Util.CvException' in Emgu.CV.Platform.NetStandard.dll
[ WARN:0] global E:\bb\cv_x86\build\opencv\modules\videoio\src\cap_msmf.cpp (436) `anonymous-namespace'::SourceReaderCB::~SourceReaderCB terminating async callback
I guess I am using it the wrong way, but there is no suiting example online for me to learn from.
Does anyone has a clean way to explain it to me?
Thank you in advance!
If you are shifting the pixels x amount to the right, I assume that there would be black empty pixels on the left side? If so, you could create an ROI and cut off some pixels on the right, since you are shifting all pixels to the right, and copy the image onto another image.
//The image that you want to shift pixels with
Image<Bgr, byte> inputImage = new Image<Bgr, byte>(1000, 1000);
//The output image
Image<Bgr, byte> image = new Image<Bgr, byte>(990, 1000);
//Create the roi, with 10 pixels cut off from the right side because of the shift
inputImage.ROI = new Rectangle(0, 0, inputImage.Width - 10, inputImage.Height);
inputImage.CopyTo(image);
CvInvoke.ImShow("The Output", image);
CvInvoke.WaitKey(0);
EDIT
Now lets say you want to keep that black stripe on the left side of the image as well. Doing this is very similar to the code above, but only with a few modifications.
//The image that you want to shift pixels with
Image<Bgr, byte> inputImage = new Image<Bgr, byte>(1000, 1000);
//The output image
Image<Bgr, byte> image = new Image<Bgr, byte>(1000, 1000);
//Create the roi, with 10 pixels cut off from the right side because of the shift
inputImage.ROI = new Rectangle(0, 0, inputImage.Width - 10, inputImage.Height);
//The image we want to shift, the same one we created a ROI with,
//has the dimensions 990 X 1000 and the output image has
//dimensions of 1000 x 1000. Unfortunately, in order to paste
//an image onto another image, they need to be the same dimensions.
//How do we do this? We must create an ROI with the output image
//that has the same dimensions as the input image.
image.ROI = new Rectangle(10, 0, image.Width, image.Height);
//Now we can past the image onto the output because the dimensions match
inputImage.CopyTo(image);
//Inorder to make our output seem normal, we must empty the ROI of the output image
image.ROI = Rectangle.Empty;
CvInvoke.ImShow("The Output", image);
CvInvoke.WaitKey(0);

FloodFill function producing weird results

I need to use the floodFill function of EMGUCV in C#.
I managed to use it like this:
Image<Gray, Byte> img = new Image<Gray, byte>(this.B);
img.Save("orig.png");
int height = img.Rows;
int width = img.Cols;
Point s = new Point( 227, 295);
int tol = 8;
Mat outputMask = new Mat(height + 2, width + 2, Emgu.CV.CvEnum.DepthType.Cv8U, 1);
Rectangle dummyRect = new Rectangle();
CvInvoke.FloodFill(img,
outputMask,
s,
new MCvScalar(255, 0, 0),
out dummyRect,
new MCvScalar(tol), new MCvScalar(tol),
Emgu.CV.CvEnum.Connectivity.EightConnected);
//Emgu.CV.CvEnum.FloodFillType.MaskOnly);
img.Save("imgModif.png");
Console.Write("End");
However the output I get in img (or in outputMask) when I use it does not make any sense at all. Some white rectangular areas. I am expecting the results of a "photoshop like" magic wand. ANy idea of what is wrong? Moreover, even though I specify (255,0,0) as the fill color, the result is white in img. (B is a preloaded Bitmap image)
I am trying to do the same as this:
http://www.andrew-seaford.co.uk/flood-fill-opencv/
except I work with a grayscale image initially.
THis is what i get if I set the tol=255 (which should fill the whole image in white, if this function was really behaving like a magic wand):
Lena tol=255
This ''rectangle'' that can be seen does not make sense, even worse, you can see some white pixels going out of this rectangle region (on Lena's hat)... so it does not even seem to be a sort of rectangular constraint. Also a few pixels in the "pseudo rectangle" are not white... I would be curious if someone could test this function on their system with a lena image.

Invert Crop from (Cut hole into) Image

Everywhere I look online, I see people posting on how to successfully crop an image. However, I want to 'crop'/ clear a hole out of an image. I want to keep the original image, but crop out a rectangle
As you can see in the image above, I have "cropped" out the kittens face. I maintained the original image, but removed only part of it. I cannot figure out how to do that.
Assuming you want to replace the original pixel colors with transparency you run into a small problem: You can't draw or fill with transparency in GDI+.
But you can use Graphics.Clear(Color.Transparent).
To do that you restrict the region where the Graphics object will draw. Here we can use the simple cropping rectangle but you can clear more complex shapes using a GraphicsPath..
Example using a bitmap bmp:
using (Graphics g = Graphics.FromImage(bmp))
{
Rectangle crop = new Rectangle(222,222,55,55);
g.SetClip(crop);
g.Clear(Color.Transparent);
}
bmp.Save(somefilename, ImageFormat.Png);
Setting your Graphics object's CompositingMode property to CompositingMode.SourceCopy will allow your drawing operations to replace the alpha value instead of proportionally opacifying it:
public static void TestDrawTransparent()
{
//This code will, successfully, draw something transparent overwriting an opaque area.
//More precisely, it creates a 100*100 fully-opaque red square with a 50*50 semi-transparent center.
using(Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb))
{
using(Graphics g = Graphics.FromImage(bmp))
using(Brush opaqueRedBrush = new SolidBrush(Color.FromArgb(255, 255, 0, 0)))
using(Brush semiRedBrush = new SolidBrush(Color.FromArgb(128, 255, 0, 0)))
{
g.Clear(Color.Transparent);
Rectangle bigRect = new Rectangle(0, 0, 100, 100);
Rectangle smallRect = new Rectangle(25, 25, 50, 50);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
g.FillRectangle(opaqueRedBrush, bigRect);
g.FillRectangle(semiRedBrush, smallRect);
}
bmp.Save(#"C:\FilePath\TestDrawTransparent.png", ImageFormat.Png);
}
}
In this code, I first draw a fully-opaque red square, then a semi-transparent red square "over" it. The result is a semi-transparent "hole" in the square:
And on a black background:
A zero-opacity brush works just as well, leaving a clear hole through the image (I checked).
With that in mind, you should be able to crop any shapes you want, simply by filling them with a zero-opacity brush.

Improve a fitted Ellipse using EmguCV (2.4) in C#

I'm using EmguCV 2.4 in C# for edge detection and ellipse fitting of ellipsoid objects in a picture (e.g. laser spot).
EmguCV has implemented functions for fitting an ellipse to a point cloud using the least squares method, but the ellipses do not fit very well to the ellipsoids, depending on their angle.
Here's a basic code, I'm using:
CHAIN_APPROX_METHOD approxMethod = CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_NONE;
RETR_TYPE RetrType = RETR_TYPE.CV_RETR_LIST;
Bitmap Bmp = new Bitmap(#"C:\Users\pernizki\Downloads\binary2.bmp"); // read Bitmap
Image<Bgr, byte> ImgIn = new Image<Bgr, byte>(Bmp); // Convert to Image<>
Image<Gray, byte> ImgBin = ImgIn.Convert<Gray, byte>(); // convert Bgr to Gray
ImgBin = ImgBin.ThresholdBinary(new Gray(140), new Gray(120)).PyrDown().PyrUp(); // convert to binary and reduce noise
ImgBin = ImgBin.Canny(100, 120); // detect the edges from binary image
Contour<Point> Contour = ImgBin.FindContours(approxMethod, RetrType); // Get Contour from binary Image
int cntr = 0;
PointF[] ContourPts = new PointF[Contour.Total];
// Convert Contour to PointF Array
foreach(Point p in Contour)
{
ContourPts[cntr++] = new PointF(p.X, p.Y);
}
Ellipse fittedEllipse = PointCollection.EllipseLeastSquareFitting(ContourPts);
ImgIn.Draw(Contour, new Bgr(Color.Green), 2);
ImgIn.Draw(fittedEllipse, new Bgr(Color.Tomato), 2);
CvInvoke.cvShowImage("Fitted Ellipse", ImgIn.Ptr);
When I have vertical or horizontal ellipse as an input image, the fitted ellipse is always 90° rotated to the input shape. If the angle is something between 0° and 90° the fitted ellipse is still rotated, but at different angle.
I understand, that this problem is fundamental to the least square method. But are there more robust algorithms to fit an ellipse, that is enclosing all of the points?
The picture in the EmguCV tutorial for fitting an ellipse seems to be exactly, what I'm looking for. Unfortunately, that's not how the function works.
Here's an example image, that I've used.
(sorry for having only one, as I don't have enough reputation for more)
rotated Ellipse

Cropping a cross rectangle from image using c#

What I want to do is basically cropping a rectangle from an image. However, it should satisfy some special cases:
I want to crop an angled rectangle on image.
I don't want to rotate the image and crop a rectangle :)
If cropping exceeds the image size, I don't want to crop an empty background color.
I want to crop from back of the starting point, that will end at starting point when rectangle size completed. I know I couldn't explain well so if I show what I want visually:
The blue dot is the starting point there, and the arrow shows cropping direction. When cropping exceeds image borders, it will go back to the back of the starting point as much as, when the rectangle width and height finished the end of the rectangle will be at starting point.
Besides this is the previous question I asked:
How to crop a cross rectangle from an image using c#?
In this question, I couldn't predict that a problem can occur about image dimensions so I didn't ask for it. But now there is case 3. Except case three, this is exactly same question. How can I do this, any suggestions?
What needs to be done is to add offsets to the matrix alignment. In this case I am taking one extra length of the rectangle from each side (total 9 rectangles) and offsetting the matrix each time.
Notice that it is necessary to place offset 0 (the original crop) last, otherwise you will get the wrong result.
Also note that if you specify a rectangle that is bigger than the rotated picture you will still get empty areas.
public static Bitmap CropRotatedRect(Bitmap source, Rectangle rect, float angle, bool HighQuality)
{
int[] offsets = { -1, 1, 0 }; //place 0 last!
Bitmap result = new Bitmap(rect.Width, rect.Height);
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = HighQuality ? InterpolationMode.HighQualityBicubic : InterpolationMode.Default;
foreach (int x in offsets)
{
foreach (int y in offsets)
{
using (Matrix mat = new Matrix())
{
//create the appropriate filler offset according to x,y
//resulting in offsets (-1,-1), (-1, 0), (-1,1) ... (0,0)
mat.Translate(-rect.Location.X - rect.Width * x, -rect.Location.Y - rect.Height * y);
mat.RotateAt(angle, rect.Location);
g.Transform = mat;
g.DrawImage(source, new Point(0, 0));
}
}
}
}
return result;
}
To recreate your example:
Bitmap source = new Bitmap("C:\\mjexample.jpg");
Bitmap dest = CropRotatedRect(source, new Rectangle(86, 182, 87, 228), -45, true);

Categories

Resources