如何在 imagemagick C# 中使用 4 个角 (x,y) 裁剪图像
How to clip a image using 4 corner(x,y) in imagemagick C#
我之前使用 afroge 使用图像的 4 个角来剪辑图像,如下所示,它工作正常,唯一的问题是 Aforge 无法使用位图处理 40000 X 34000 这样的大图像,所以我想检查 imageMagick使用 4 角进行裁剪,但它似乎接受要裁剪的图像的 x、y 和宽度和高度,但如果图像倾斜或不直,这将不起作用。
我尝试使用以下代码使用 ImageMagick,但它只需要一个 x、y 位置以及高度和宽度,这将提供错误的裁剪图像
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);
伪造代码 - 现有代码 - 完美运行
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);
你可以试试 net-vips, the C# binding for libvips。它是一个惰性的、流式的、需求驱动的图像处理库,因此它可以在不需要加载整个图像的情况下执行这样的操作。
目前还没有可以直接进行角度裁剪的操作,但是自己动手做一个很容易。
例如,为 Python 使用 libvips 绑定(抱歉,我这里没有安装 C#,但 API 是相同的):
#!/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)
我可以运行在大图上这样:
$ 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
因此从 127000 x 98000 像素的图像中裁剪一个小角度区域需要 0.3 秒。
Openslide 图像是(近似)平铺的 tiff,因此它只需要对输入图像中进行输出所需的部分进行解码。
它也适用于未处理的图像,尽管它需要先解码整个图像。对于小于 100mb 的图像,它将解码到内存,否则解码到临时文件。
我用 C# 重写了 jcupitt 的示例代码。此示例使用 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);
}
请注意,如果您需要 Windows 上的 OpenSlide 支持,则必须使用 libvips -all
发行版。出于安全原因,NetVips 仅捆绑了 -web
libvips x86/x64 发行版。有关完整教程,请参阅 here。
将您的四个角(假设是一个未旋转的矩形)转换为 WxH+X+Y,然后使用 Imagemagick。
如果我们假设这4个点从左上角顺时针排列为(左上角,右上角,右下角,左下角),那么数学上我们会计算:
W=(top-right-x - top-left-x)
H=(bottom-left-y -top-left-y)
X=top-left-x
Y=top-left-y
然后在 Imagemagick 6 命令行中:
convert image -crop WxH+X+Y +repage croppedimage
如果我误解了你的问题,请澄清。
添加:
如果您有一个由 4 个点定义的旋转矩形,那么您可以计算边界框并将其转换为 WxH+X+Y 以使用上面的命令进行裁剪。
要做到这一点,请从您的 4 个点中获取 min-x、min-y、max-x、max-y。那么
W=(max-x - min-x)
H=(max-y - min-y)
X=min-x
Y=min-y
我之前使用 afroge 使用图像的 4 个角来剪辑图像,如下所示,它工作正常,唯一的问题是 Aforge 无法使用位图处理 40000 X 34000 这样的大图像,所以我想检查 imageMagick使用 4 角进行裁剪,但它似乎接受要裁剪的图像的 x、y 和宽度和高度,但如果图像倾斜或不直,这将不起作用。
我尝试使用以下代码使用 ImageMagick,但它只需要一个 x、y 位置以及高度和宽度,这将提供错误的裁剪图像
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);
伪造代码 - 现有代码 - 完美运行
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);
你可以试试 net-vips, the C# binding for libvips。它是一个惰性的、流式的、需求驱动的图像处理库,因此它可以在不需要加载整个图像的情况下执行这样的操作。
目前还没有可以直接进行角度裁剪的操作,但是自己动手做一个很容易。
例如,为 Python 使用 libvips 绑定(抱歉,我这里没有安装 C#,但 API 是相同的):
#!/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)
我可以运行在大图上这样:
$ 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
因此从 127000 x 98000 像素的图像中裁剪一个小角度区域需要 0.3 秒。
Openslide 图像是(近似)平铺的 tiff,因此它只需要对输入图像中进行输出所需的部分进行解码。
它也适用于未处理的图像,尽管它需要先解码整个图像。对于小于 100mb 的图像,它将解码到内存,否则解码到临时文件。
我用 C# 重写了 jcupitt 的示例代码。此示例使用 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);
}
请注意,如果您需要 Windows 上的 OpenSlide 支持,则必须使用 libvips -all
发行版。出于安全原因,NetVips 仅捆绑了 -web
libvips x86/x64 发行版。有关完整教程,请参阅 here。
将您的四个角(假设是一个未旋转的矩形)转换为 WxH+X+Y,然后使用 Imagemagick。
如果我们假设这4个点从左上角顺时针排列为(左上角,右上角,右下角,左下角),那么数学上我们会计算:
W=(top-right-x - top-left-x)
H=(bottom-left-y -top-left-y)
X=top-left-x
Y=top-left-y
然后在 Imagemagick 6 命令行中:
convert image -crop WxH+X+Y +repage croppedimage
如果我误解了你的问题,请澄清。
添加:
如果您有一个由 4 个点定义的旋转矩形,那么您可以计算边界框并将其转换为 WxH+X+Y 以使用上面的命令进行裁剪。
要做到这一点,请从您的 4 个点中获取 min-x、min-y、max-x、max-y。那么
W=(max-x - min-x)
H=(max-y - min-y)
X=min-x
Y=min-y