Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to auto crop an image?

I am trying to make a scanning application. That application will scan the document and will display image in a picture box. The problem I am facing is image (Documents image which kept in the scanner or say ‘real ’image) is displaying inside another image with some background (That background color is also changing) its look like this image.
enter image description here
I have tried lot of things but nothing giving me a perfect result I tried it with a forge.net. Here is the code I have tried.

public static System.Drawing.Image AforgeAutoCrop(Bitmap selectedImage)
    {
        Bitmap autoCropImage = null;
    try
    {

        autoCropImage = selectedImage;
        // create grayscale filter (BT709)
        Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721);
        Bitmap grayImage = filter.Apply(autoCropImage);
        // create instance of skew checker
        DocumentSkewChecker skewChecker = new DocumentSkewChecker();
        // get documents skew angle
        double angle = skewChecker.GetSkewAngle(grayImage);
        // create rotation filter
        RotateBilinear rotationFilter = new RotateBilinear(-angle);
        rotationFilter.FillColor = Color.White;
        // rotate image applying the filter
        Bitmap rotatedImage = rotationFilter.Apply(grayImage);
        new ContrastStretch().ApplyInPlace(grayImage);
        new Threshold(100).ApplyInPlace(grayImage);
        BlobCounter bc = new BlobCounter();
        bc.FilterBlobs = true;
        // bc.MinWidth = 500;
        //bc.MinHeight = 500;
        bc.ProcessImage(grayImage);
        Rectangle[] rects = bc.GetObjectsRectangles();
        MemoryStream writeName = new MemoryStream();
        if (rects.Length == 0)
        {
            System.Windows.Forms.MessageBox.Show("No rectangle found in image ");
        }
        else if (rects.Length == 1)
        {
            Bitmap cropped = new Crop(rects[0]).Apply(autoCropImage);
            autoCropImage = cropped;
           // pictureBox1.Image = cropped;
        }
        else if (rects.Length > 1)
        {
            // get largets rect
            Console.WriteLine("Using largest rectangle found in image ");
            var r2 = rects.OrderByDescending(r => r.Height * r.Width).ToList();
            //var r2 = rects.OrderByDescending(r => r.Height < 1500 && r.Width < 1000).ToList();
            Bitmap cropped = new Crop(r2[0]).Apply(autoCropImage);

            Graphics gr = Graphics.FromImage(cropped);
            gr.DrawRectangles(new Pen(Color.Red), rects);
            autoCropImage = cropped;
           // pictureBox1.Image = cropped;

        }
        else
        {
            Console.WriteLine("Huh? on image ");
        }
    }
    catch(Exception ex)
    {
        MessageBox.Show(ex.Message);
    }

    return autoCropImage;
    }
like image 296
Rakesh Avatar asked Feb 19 '23 16:02

Rakesh


2 Answers

i changed your code to this and works well. thanks

 public static System.Drawing.Image AforgeAutoCrop(Bitmap selectedImage)
        {
            Bitmap autoCropImage = null;
            try
            {

                autoCropImage = selectedImage;
                // create grayscale filter (BT709)
                Grayscale filter = new Grayscale(0.2125, 0.7154, 0.0721);
                Bitmap grayImage = filter.Apply(autoCropImage);
                // create instance of skew checker
                DocumentSkewChecker skewChecker = new DocumentSkewChecker();
                // get documents skew angle
                double angle = skewChecker.GetSkewAngle(grayImage);
                // create rotation filter
                RotateBilinear rotationFilter = new RotateBilinear(-angle);
                rotationFilter.FillColor = Color.White;
                // rotate image applying the filter
                Bitmap rotatedImage = rotationFilter.Apply(grayImage);
                new ContrastStretch().ApplyInPlace(rotatedImage);
                new Threshold(100).ApplyInPlace(rotatedImage);
                BlobCounter bc = new BlobCounter();
                bc.FilterBlobs = true;
                // bc.MinWidth = 500;
                //bc.MinHeight = 500;
                bc.ProcessImage(rotatedImage);
                Rectangle[] rects = bc.GetObjectsRectangles();

                if (rects.Length == 0)
                {
                    System.Windows.Forms.MessageBox.Show("No rectangle found in image ");
                }
                else if (rects.Length == 1)
                {
                    autoCropImage = rotatedImage.Clone(rects[0], rotatedImage.PixelFormat); ;
                }
                else if (rects.Length > 1)
                {
                    // get largets rect
                    Console.WriteLine("Using largest rectangle found in image ");
                    var r2 = rects.OrderByDescending(r => r.Height * r.Width).ToList();
                    autoCropImage = rotatedImage.Clone(r2[1], rotatedImage.PixelFormat);
                }
                else
                {
                    Console.WriteLine("Huh? on image ");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            return autoCropImage;
        }
like image 196
mostafa hk Avatar answered Feb 28 '23 11:02

mostafa hk


I assume you always have an image with a distinct foreground and background, and you want to do something like a zealous crop of the background.

In this case I would do something similar to region growning. Start at a point where you can be guaranteed a background pixel.

Get another image (or matrix or whatever), initialized to zeros, and set the corresponding pixel value to 1. If any neighboring pixels are within a threshold value in the original image, move to them recursively and set their corresponding pixel values to 0 as well.

That is:

map = 0's, size of image
function f(x,y,image,map)
    if map(x,y) is not  0
        return
    if pixel value at image(x,y)<T
        map(x,y) = 1;
        for all neighbors of x,y
           function([neighbor coordinates],image,map)
    else
        map(x,y) = 2;
 end

Now map should have all background pixels as 1 and forground as 2. You can change this to allow multiple objects and thresholds and so forth. You might want threshold to be a value change rather than an absolute value.

Then simply find the min and max x and y and store the pixels in that range to a new image.

I hope this was along the lines of what you need.

like image 33
Bill Avatar answered Feb 28 '23 11:02

Bill