Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Crop/Remove Unwanted Space at the edges of image

I have search a lot to remove the unwanted space but could not found. I only found links which can used to remove black and white background space. But my background images can be anything. So, If I have these images,

enter image description here

Image1

enter image description here

How I can extract the part of image which I required. For example,

enter image description hereenter image description hereenter image description here

like image 944
Imran Qadir Baksh - Baloch Avatar asked Nov 25 '12 06:11

Imran Qadir Baksh - Baloch


People also ask

How do I remove white space from a PNG?

1 Correct answer Open the png in Photoshop use the magic wand to select the white, delete it and save the document as a png without the white.

How do I crop a image in Photoshop?

Use any selection tool, such as the Rectangular Marquee tool , to select the part of the image you want to keep. Choose Image > Crop.


2 Answers

Here's my solution for your question :

I have declared a method which gets the Original Image then It looks for the background Color by checking the corners of the provided Image , if at least 3 corners have similar Color (10% offset at most) then we've found the background color then It tries to find the bounds of those shapes in the Image which of course have different color than Background Color

after Finding the Bounds The Function Crops the Image and Return the new Cropped Area as a new Bitmap !

This is the Demo File : Download

Complete Solution : Download

Here's the results for :

Image 1 :

enter image description here

Image 2 :

enter image description here

Image 3 :

enter image description here

here's the Function inside ImageProcessingTools class Simplified,

public class ImageHelper
{
    #region CropUnwantedBackground
    public static Bitmap CropUnwantedBackground(Bitmap bmp)
    {
        var backColor = GetMatchedBackColor(bmp);
        if (backColor.HasValue)
        {
            var bounds = GetImageBounds(bmp, backColor);
            var diffX = bounds[1].X - bounds[0].X + 1;
            var diffY = bounds[1].Y - bounds[0].Y + 1;
            var croppedBmp = new Bitmap(diffX, diffY);
            var g = Graphics.FromImage(croppedBmp);
            var destRect = new Rectangle(0, 0, croppedBmp.Width, croppedBmp.Height);
            var srcRect = new Rectangle(bounds[0].X, bounds[0].Y, diffX, diffY);
            g.DrawImage(bmp, destRect, srcRect, GraphicsUnit.Pixel);
            bmp.Dispose();
            return croppedBmp;
        }
        else
        {
            bmp.Dispose();
            return null;
        }
    }
    #endregion

    #region Private Methods

    #region GetImageBounds
    private static Point[] GetImageBounds(Bitmap bmp, Color? backColor)
    {
        //--------------------------------------------------------------------
        // Finding the Bounds of Crop Area bu using Unsafe Code and Image Proccesing
        Color c;
        int width = bmp.Width, height = bmp.Height;
        bool upperLeftPointFounded = false;
        var bounds = new Point[2];
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                c = bmp.GetPixel(x, y);
                bool sameAsBackColor = ((c.R <= backColor.Value.R * 1.1 && c.R >= backColor.Value.R * 0.9) &&
                                        (c.G <= backColor.Value.G * 1.1 && c.G >= backColor.Value.G * 0.9) &&
                                        (c.B <= backColor.Value.B * 1.1 && c.B >= backColor.Value.B * 0.9));
                if (!sameAsBackColor)
                {
                    if (!upperLeftPointFounded)
                    {
                        bounds[0] = new Point(x, y);
                        bounds[1] = new Point(x, y);
                        upperLeftPointFounded = true;
                    }
                    else
                    {
                        if (x > bounds[1].X)
                            bounds[1].X = x;
                        else if (x < bounds[0].X)
                            bounds[0].X = x;
                        if (y >= bounds[1].Y)
                            bounds[1].Y = y;
                    }
                }
            }
        }
        return bounds;
    } 
    #endregion

    #region GetMatchedBackColor
    private static Color? GetMatchedBackColor(Bitmap bmp)
    {
        // Getting The Background Color by checking Corners of Original Image
        var corners = new Point[]{
            new Point(0, 0),
            new Point(0, bmp.Height - 1),
            new Point(bmp.Width - 1, 0),
            new Point(bmp.Width - 1, bmp.Height - 1)
        }; // four corners (Top, Left), (Top, Right), (Bottom, Left), (Bottom, Right)
        for (int i = 0; i < 4; i++)
        {
            var cornerMatched = 0;
            var backColor = bmp.GetPixel(corners[i].X, corners[i].Y);
            for (int j = 0; j < 4; j++)
            {
                var cornerColor = bmp.GetPixel(corners[j].X, corners[j].Y);// Check RGB with some offset
                if ((cornerColor.R <= backColor.R * 1.1 && cornerColor.R >= backColor.R * 0.9) &&
                    (cornerColor.G <= backColor.G * 1.1 && cornerColor.G >= backColor.G * 0.9) &&
                    (cornerColor.B <= backColor.B * 1.1 && cornerColor.B >= backColor.B * 0.9))
                {
                    cornerMatched++;
                }
            }
            if (cornerMatched > 2)
            {
                return backColor;
            }
        }
        return null;
    }  
    #endregion
    
    #endregion
}

and here is a simple one usage in ASP.NET,

if (IsPostBack && Request.Files.Count > 0)
{
    var file = Request.Files[0];
    var bmp = new Bitmap(file.InputStream);
    var croppedBmp = ImageHelper.CropUnwantedBackground(bmp);
    Response.ContentType = file.ContentType;
    croppedBmp.Save(Response.OutputStream, ImageFormat.Jpeg);
    Response.End();
}

And Finally I should mention that , these Fantastic Tutorials have Helped me a lot in Image Processing :

Image Processing for Dummies with C# and GDI+

Image Processing using C#

Hope it Helps :)

like image 95
26 revs, 3 users 65% Avatar answered Oct 12 '22 18:10

26 revs, 3 users 65%


Here's a more reliable approach that uses a Sobel energy filter and a fast bounding box detection routine (extracted from the WhitespaceTrimmer plugin for ImageResizer).

namespace ImageResizer.Plugins.WhitespaceTrimmer {
public class BoundingBoxFinder {

    /// <summary>
    /// Returns a rectangle inside 'lookInside' that bounds any energy greater than 'threshold'. 
    /// </summary>
    /// <param name="image"></param>
    /// <param name="lookInside">A rectangle of 'image' to look inside. </param>
    /// <param name="threshold">1-255, the energy threshold to detect activity. 80-150 is a good range.</param>
    /// <returns></returns>
    public Rectangle FindBoxSobel(Bitmap originalImage, Rectangle lookInside, byte threshold) {

        Bitmap image = originalImage;
        try {
            //Convert if needed (makes an extra copy)
            if (image.PixelFormat != PixelFormat.Format24bppRgb &&
                image.PixelFormat != PixelFormat.Format32bppArgb &&
                image.PixelFormat != PixelFormat.Format32bppRgb) {
                image = AForge.Imaging.Image.Clone(image, PixelFormat.Format24bppRgb);
            }

            //Crop if needed (makes an extra copy unless we converted too, then only 1 extra copy)
            if (!lookInside.Equals(new Rectangle(0, 0, image.Width, image.Height))) {
                Bitmap oldImage = image;
                try {
                    image = new Crop(lookInside).Apply(image);
                } finally {
                    if (oldImage != originalImage) oldImage.Dispose(); //Dispose the cloned 
                }
            }


            //Makes 1 more copy at 1/3rd the size, in grayscale
            Rectangle result = FindBoxSobel(image, threshold);
            return new Rectangle(lookInside.X + result.X, lookInside.Y + result.Y, result.Width, result.Height);


        } finally {
            if (image != originalImage) image.Dispose();
        }

    }
    /// <summary>
    /// Requires 24 bit or 32 bit (A) RGB image. 
    /// </summary>
    /// <param name="rgb"></param>
    /// <param name="threshold"></param>
    /// <returns></returns>
    public Rectangle FindBoxSobel(Bitmap rgb, byte threshold) {
        using (Bitmap gray = Grayscale.CommonAlgorithms.Y.Apply(rgb)) {

            //Apply sobel operator to grayscale image
            new SobelEdgeDetector().ApplyInPlace(gray);
            //Threshold into black and white.
            new Threshold(threshold).ApplyInPlace(gray);
            //Trim only exact black pixels
            // lock source bitmap data
            BitmapData data = gray.LockBits(new Rectangle(0, 0, gray.Width, gray.Height), ImageLockMode.ReadOnly, gray.PixelFormat);
            try {
                return FindBoxExactGrayscale(data, 0);
            } finally {
                gray.UnlockBits(data);
            }
        }
    }
    /// <summary>
    /// Returns a bounding box that only excludes the specified color. 
    /// Only works on 8-bit images.
    /// </summary>
    /// <param name="sourceData"></param>
    /// <param name="colorToRemove">The palette index to remove.</param>
    /// <returns></returns>
    public Rectangle FindBoxExactGrayscale(BitmapData sourceData, byte indexToRemove) {
        if (sourceData.PixelFormat != PixelFormat.Format8bppIndexed) throw new ArgumentOutOfRangeException("FindBoxExact only operates on 8-bit grayscale images");
        // get source image size
        int width = sourceData.Width;
        int height = sourceData.Height;
        int offset = sourceData.Stride - width;

        int minX = width;
        int minY = height;
        int maxX = 0;
        int maxY = 0;

        // find rectangle which contains something except color to remove
        unsafe {
            byte* src = (byte*)sourceData.Scan0;

            for (int y = 0; y < height; y++) {
                if (y > 0) src += offset; //Don't adjust for offset until after first row
                for (int x = 0; x < width; x++) {
                    if (x > 0 || y > 0) src++; //Don't increment until after the first pixel.
                    if (*src != indexToRemove) {
                        if (x < minX)
                            minX = x;
                        if (x > maxX)
                            maxX = x;
                        if (y < minY)
                            minY = y;
                        if (y > maxY)
                            maxY = y;
                    }
                }
            }
        }

        // check
        if ((minX == width) && (minY == height) && (maxX == 0) && (maxY == 0)) {
            minX = minY = 0;
        }

        return new Rectangle(minX,minY,maxX - minX + 1, maxY - minY + 1);
    }
}
}
like image 4
Lilith River Avatar answered Oct 12 '22 16:10

Lilith River