Search code examples
c#imagemagickclipping

How to clip a image using 4 corner(x,y) in imagemagick C#


I am using afroge previously to clip image using 4 corners of the image like below and it works fine and only problem with Aforge is not able to handle large images like 40000 X 34000 using bitmap, so I want to check imageMagick to crop using 4 corner, but it seems it accepts x,y and width and height of the image to crop, but this will not work if the image is tilted or not straight.

I have tried ImageMagick using below code, but it takes only one x,y location and height and width, which will provide wrong clipped image

Point location = new Point();
location.X = topleft.X;
location.Y = topleft.Y;        
Size ze = new Size();
ze.Height = width;
ze.Height = height;
Rectangle rt = new Rectangle(location, ze);
MagickGeometry mgeomentory = new MagickGeometry(rt);
originalImage.Crop(mgeomentory);

aforge code - existing code - working perfectly

List<AForge.IntPoint> corners = new List<AForge.IntPoint>();
corners.Add(new AForge.IntPoint(topleft.X, topleft.Y));
corners.Add(new AForge.IntPoint(topright.X, topright.Y));
corners.Add(new AForge.IntPoint(bottomright.X, bottomright.Y));
corners.Add(new AForge.IntPoint(bottomleft.X, bottomleft.Y));

SimpleQuadrilateralTransformation filter = new 
SimpleQuadrilateralTransformation(corners, width, height);
Bitmap newImage = filter.Apply(originalImage);

Solution

  • I've rewritten jcupitt's example code in C#. This example uses net-vips, the C# binding for libvips.

    static void Main(string[] args)
    {
        if (args.Length != 8)
        {
            Console.WriteLine(
                "Usage: [input] [output] " +
                "[topLeftX] [topLeftY] " +
                "[topRightX] [topRightY] " +
                "[bottomRightX] [bottomRightY]");
            return;
        }
    
        Image image = Image.NewFromFile(args[0]);
        string outFilename = args[1];
        int topLeftX = int.Parse(args[2]);
        int topLeftY = int.Parse(args[3]);
        int topRightX = int.Parse(args[4]);
        int topRightY = int.Parse(args[5]);
        int bottomRightX = int.Parse(args[6]);
        int bottomRightY = int.Parse(args[7]);
    
        // the angle the top edge is rotated by
        int dx = topRightX - topLeftX;
        int dy = topRightY - topLeftY;
        double angle = (180 / Math.PI) * Math.Atan2(dx, dy);
        if (angle < -45 || angle >= 45)
        {
            angle = 90 - angle;
        }
    
        // therefore the angle to rotate by to get it straight
        angle = -angle;
    
        image = image.Rotate(angle);
    
        // the new position of the rectangle in the rotated image
        double radians = (Math.PI * angle) / 180.0;
        double c = Math.Cos(radians);
        double s = Math.Sin(radians);
    
        int left = Convert.ToInt32(topLeftX * c - topLeftY * s);
        int top = Convert.ToInt32(topLeftX * s + topLeftY * c);
        int width = Convert.ToInt32(Math.Sqrt(Math.Pow(topRightX - topLeftX, 2) +
                                              Math.Pow(topRightY - topLeftY, 2)));
        int height = Convert.ToInt32(Math.Sqrt(Math.Pow(topRightX - bottomRightX, 2) +
                                               Math.Pow(topRightY - bottomRightY, 2)));
    
        // after a rotate, the new position of the origin is given by .Xoffset, .Yoffset
        Image tile = image.Crop(left + image.Xoffset, top + image.Yoffset, width, height);
    
        tile.WriteToFile(outFilename);
    }
    

    Note that if you need OpenSlide support on Windows, you'll have to use the libvips -all distribution. For security reasons, NetVips only bundles the -web libvips x86/x64 distribution. See here for a complete tutorial.