简体   繁体   中英

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);

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.

You could try net-vips , the C# binding for libvips . It's a lazy, streaming, demand-driven image processing library, so it can do operations like this without needing to load the whole image.

There isn't an operation that can directly do an angled crop, but it's easy to make one yourself with a little trig.

For example, using the libvips binding for Python (sorry, I don't have C# installed here, but the API is the same):

#!/usr/bin/python3

import sys
import math
import pyvips

image = pyvips.Image.new_from_file(sys.argv[1])
out_filename = sys.argv[2]
top_left_x = int(sys.argv[3])
top_left_y = int(sys.argv[4])
top_right_x = int(sys.argv[5])
top_right_y = int(sys.argv[6])
bottom_right_x = int(sys.argv[7])
bottom_right_y = int(sys.argv[8])

# the angle the top edge is rotated by
dx = top_right_x - top_left_x
dy = top_right_y - top_left_y
angle = math.degrees(math.atan2(dx, dy))
if angle < -45 or 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
c = math.cos(math.radians(angle))
s = math.sin(math.radians(angle))
left = top_left_x * c - top_left_y * s
top = top_left_x * s + top_left_y * c
width = math.hypot(top_right_x - top_left_x, top_right_y - top_left_y)
height = math.hypot(top_right_x - bottom_right_x, top_right_y - bottom_right_y)

# after a rotate, the new position of the origin is given by .xoffset, .yoffset
tile = image.crop(left + image.xoffset, top + image.yoffset, width, height)

tile.write_to_file(out_filename)

I can run it like this on a huge image:

$ vipsheader ~/pics/openslide/bild.ndpi 
/home/john/pics/openslide/bild.ndpi: 126976x98304 uchar, 4 bands, rgb, openslideload
$ time ./angled_crop.py ~/pics/openslide/bild.ndpi x.jpg 680 760 936 836 484 1552
real    0m0.304s
user    0m0.388s
sys 0m0.040s

So it takes 0.3s to crop a small angled area from a 127000 x 98000 pixel image.

Openslide images are (approximately) tiled tiff, so it only needs to decode the parts of the input image necessary to make the output.

It'll work on untiled images too, though it will need to decode the whole image first. It'll decode to memory for images under 100mb, and to a temporary file otherwise.

Convert your four corners (assuming a non-rotated rectangle) to WxH+X+Y and then use Imagemagick.

If we assume the 4 points are arranged clockwise from the top-left as (top-left, top-right, bottom-right, bottom-left), then mathematically we would compute:

W=(top-right-x - top-left-x)
H=(bottom-left-y -top-left-y)
X=top-left-x
Y=top-left-y


Then in Imagemagick 6 command line:

convert image -crop WxH+X+Y +repage croppedimage

If I misunderstand your issue, please clarify.

ADDITION:

If you have a rotated rectangle defined by the 4 points, then you can compute the bounding box and convert that to WxH+X+Y to crop with the command above.

To do that get the min-x, min-y, max-x, max-y from your 4 points. Then

W=(max-x - min-x)
H=(max-y - min-y)
X=min-x
Y=min-y

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM