I use OpenCV for image pre-processing. I need cut only scanned photo, whithout white area.
I use algoritm:
image_canny <- apply canny edge detector to this channel
for threshold in bunch_of_increasing_thresholds:
image_thresholds[threshold] <- apply threshold to this channel
for each contour found in {image_canny} U image_thresholds:
Approximate contour with polygons
if the approximation has four corners and the angles are close to 90 degrees.for find rectangle object on scanned image. But this example not work, if i put my picture in corner of scanner!
Can anybody advise, how i can find this photo on scanned image? any examples, methods?
There are several ways to achieve your goal. I will give code for OpenCvSharp, it will be similar for plain C++.
Try to add some neutral border around your image. For example, you can just add 10-20 pixels of white around your source image. It can create false contours, but still your target part of the image will be not in the corner anymore.
Mat img = Cv2.ImRead("test.jpg");
Mat imgExtended = new Mat(new OpenCvSharp.Size(img.Size().Width + 40, img.Size().Height + 40), MatType.CV_8UC3, Scalar.White);
OpenCvSharp.Rect roi = new OpenCvSharp.Rect(new OpenCvSharp.Point(20, 20), img.Size());
img.CopyTo(imgExtended.SubMat(roi));
img = imgExtended;
Mat coloredImage = img.Clone();
Cv2.CvtColor(img, img, ColorConversionCodes.BGR2GRAY);
OpenCvSharp.Point[][] contours;
HierarchyIndex[] hierarchy;
Cv2.Canny(img, result, 80, 150);
Cv2.FindContours(result, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
You have object and practically white background. You can do any thresholding operation, and then take the biggest blob.
Update.
In both cases dark line at the top of your image and dark area at the left corner can still be the problem. In this case you can choose the contour with the largest area by the function
double Cv2.ContourArea(Point[] Contour);
And then try to create bounding box, which will minimize the error.
Related
I'm using C# WinForms.
The rotated polygon is drawn on a picturebox. The width and height of the rotated polygon is 101, 101. Now, I want to transfer the contents of rotated polygon to new bitmap of size 101,101
I tried to paint pixels of the rectangle using this code
for (int h = 0; h < pictureBox1.Image.Height; h++)
{
for (int w = 0; w < pictureBox1.Image.Width; w++)
{
if (IsPointInPolygon(rotatedpolygon, new PointF(w, h)))
{
g.FillRectangle(Brushes.Black, w, h, 1, 1); //paint pixel inside polygon
}
}
}
The pixels are painted in the following manner:
Now, how do I know which location on the rotated rectangle goes to which location in the new bitmap. That is how do i translate pixel co-ordinates of rotated rectangle to new bitmap.
or simply put, is it possible to map rows and columns from rotated rectangle to new bitmap as shown below?
Sorry, if the question is not clear.
What you asking to do is not literally possible. Look at your diagram:
On the left side, you've drawn pixels that are themselves oriented diagonally. But, that's not how the pixels actually are oriented in the source bitmap. The source bitmap will have square pixels oriented horizontally and vertically.
So, let's just look at a little bit of your original image:
Consider those four pixels. You can see in your drawing that, considered horizontally and vertically, the top and bottom pixels overlap the left and right pixels. More specifically, if we overlay the actual pixel orientations of the source bitmap with your proposed locations of source pixels, we get something like this:
As you can see, when you try to get the value of the pixel that will eventually become the top-right pixel of the target image, you are asking for the top pixel in that group of four. But that top pixel is actually made up of two different pixels in the original source image!
The bottom line: if the visual image that you are trying to copy will be rotated during the course of copying, there is no one-to-one correspondence between source and target pixels.
To be sure, resampling algorithms that handle this sort of geometric projection do apply concepts similar to that which you're proposing. But they do so in a mathematically sound way, in which pixels are necessarily merged or interpolated as necessary to map the square, horizontally- and vertically-oriented pixels from the source, to the square, horizontally- and vertically-oriented pixels in the target.
The only way you could get literally what you're asking for — to map the pixels on a one-to-one basis without any change in the actual pixel values themselves — would be to have a display that itself rotated.
Now, all that said: I claim that you're trying to solve a problem that not only is not solvable, but also is not worth solving.
Display resolution is so high on modern computing devices, even on the phone that you probably have next to you or in your pocket, that the resampling that occurs when you rotate bitmap images is of no consequence to the human perception of the bitmap.
Just do it the normal way. It will work fine.
I am creating an Circle on a bitmap but want to have a hole in it. After serching for half an hour I only found ways to crop an image to a circle. The hard thing is, that the hole in the middle should be transparent as the rest of the Image.
This is the base image and the yellow circle represents the transparent area that should be added.
Thanks for any kind of help.
The start is simple: Create a transparent bitmap by doing a g.Clear(Color.Transparent) and then draw/fill a circle in a color.
The next step is a bit trickier: You next want to paint the hole with transparency.
To do so you need to switch the Graphics object to the right CompositingMode; default is SourceOver but you want SourceCopy. The former overlays the alpha values creating mixed colors. The latter will do what we want: Draw the hole by copying the drawn colors including alpha right over the old ones..
Here is an example:
Bitmap bmp = new Bitmap(500, 500);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Transparent);
//g.SmoothingMode = SmoothingMode.AntiAlias;
g.CompositingMode = CompositingMode.SourceCopy;
g.FillEllipse(Brushes.DarkGreen, 100, 100, 300, 300);
g.FillEllipse(Brushes.Transparent, 200, 200, 100, 100);
}
pictureBox1.Image = bmp;
This is what is looks like in a PictureBox with a BackgroundImage:
A few notes:
You can also use a semi-transparent brush to create a 'tinted' hole; do not use anti-aliasing for this though, as it would introduce colored fringes.
We used simple circles here but with a GraphicsPath you can create and fill shapes of almost any shape and complexity..
And using a GraphicsPath would also have been an alternative to filling with transparency: By first adding the large and then the smaller, inner ellipse the path would have been created with a hole and filling it would have had the very same result! But I found the solution above more instructive..
Final note: As clarkitect noted, to save, do use a format that supports transparency. Png is always recommended..
I am using C# to write a program to detect the paper edges and crop out the square edges of the paper from the images.
Below is the image I wish to crop. The paper will always appear at the bottom of the pages.
I had read through these links but I still have no idea how to do it.
OpenCV C++/Obj-C: Detecting a sheet of paper / Square Detection
Edit: I am using EMGU for this OCR project
You can also:
Convert your image to grayscale
Apply ThresholdBinary by the pixel intensity
Find contours.
To see examples on finding contours you can look on this post.
FundContours method doesn't care about the contours size. The only thing to be done here before finding contours is emphasizing them by binarizing the image (and we do this in step 2).
For more info also look at OpenCV docs: findContours, example.
Find proper contour by the size and position of its bounding box.
(In this step we iterate over all found on contours and try to figure out, which one the contour of the paper sheet, using known paper dimensions, proportions of them and the relative positon - left bottom corner of the image ).
Crop the image using bounding box of the sheet of paper.
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));
using (MemStorage store = new MemStorage())
for (Contour<Point> contours= grayImage.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_NONE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE, store); contours != null; contours = contours.HNext)
{
Rectangle r = CvInvoke.cvBoundingRect(contours, 1);
// filter contours by position and size of the box
}
// crop the image using found bounding box
UPD: I have added more details.
Decide on the Paper color
Decide on a delta to allow the color to vary
Decide on points along the bottom to do vertical tests
Do the vertical tests going up, collecting the minimum y where the color stops appearing
Do at least 10-20 such tests
The resulting y should be 1 more than what you want to keep. You may need to insert a limit to avoid cropping everything if the image is too bright. Either refine the algorithm or mark such an the image as an exception for manual treatment!
To crop you use the DrawImage overload with source and dest rectangles!
Here are a few more hints:
To find the paper color you can go up right from the left bottom edge diagonally until you hit a pixel with a Color.GetBrightness of > 0.8; then go further for 2 pixels to get clear of any antialiased pixels.
A reasonable delta will depend on your images; start with 10%
Use a random walk along the bottom; when you are done maybe add one extra pass in the close vicinity of the minimum found in pass one.
The vertical test can use GetPixel to get at the colors or if that is too slow you may want to look into LockBits. But get the search algorithm right first, only then think about optimizing!
If you run into trouble with your code, expand your question!
I have been trying to figure this out for hours with no answer.
I have an UIimageView with the ContentMode as UIViewContentMode.ScaleAspectFill which I would like to crop using an overlying resizable frame. The cropping frame no longer maintains a 1:1 relationship between the bounds that contains both the UIimageView and cropping tool. The cropping usually would be as simple as:
using (var imageRef = image.CGImage.WithImageInRect(frame)) {
return UIImage.FromImage(imageRef);
}
But, the calculations are required in the case, how would I calculate the offset to match the cropping tool frame to the newly inflated UIImageView (or rather UIImage)? Here's a image to help paint the picture:
This picture shows a few key things.
Upper Right Image: What will be cropped currently (area contained in blue section)
Blue Rectangle: Where the cropping is currently being taken place, notice the position and height is skewed compared to red.
Red Rectangle: The cropping control, essentially where the cropping should be taking place. INSTEAD of where blue is.
Upper Blue Textbox: Ignore this.
Essentially, I want the blue rect to be where the red frame is.
Any help would be greatly appeciated, I am using C# for this, but Objective C answers are more than welcomed.
I am able to add an image to my map just fine via code.
However when I zoom in/out, the image stays the same. I would like it scale relative to the map.
In the WPF version of the Map, you could use an ImageBrush for a MapPolygon and it would be constrained to the bounding box.
I tried the solution from this SO question, but it seems to have no effect on the Image.
imageLayer.Children.Clear();
MapLayer.SetPosition(_vm.RadarImage, new Location(_vm.Overlay.LatN, _vm.Overlay.LonW));
imageLayer.Children.Add(_vm.RadarImage);
shapeLayer.Shapes.Clear();
var rect = new MapPolygon();
rect.Locations.Add(new Location(_vm.Overlay.LatN, _vm.Overlay.LonW));
rect.Locations.Add(new Location(_vm.Overlay.LatS, _vm.Overlay.LonW));
rect.Locations.Add(new Location(_vm.Overlay.LatS, _vm.Overlay.LonE));
rect.Locations.Add(new Location(_vm.Overlay.LatN, _vm.Overlay.LonE));
rect.FillColor = Colors.Green;
shapeLayer.Shapes.Add(rect);
mappy.SetView(new LocationRect(new Location(_vm.Overlay.LatN + 0.0001, _vm.Overlay.LonW + 0.0001), new Location(_vm.Overlay.LatS - 0.0001, _vm.Overlay.LonE - 0.0001)));
This is the correct scaling.
When you zoom once via the Navigation, you can see the image is now larger than the Polygon
There isn't a simple solution for this. I have put together a sample app that shows one approach to do this. You can find it here: http://code.msdn.microsoft.com/Binding-and-Image-to-a-01a56e48 What I did was add a Canvas to the map, and then use the map to calculate the pixel coordinates of the bounding box for the image. I then used these pixel coordinates to scale and position the image on the canvas overtop the map. I've done something similar to create custom polygons that support image brushes in the past but haven't uploaded that code sample yet.