先来看看最终效果。
因为实际项目中要用到opencvsharp,所以这里面的旋转矩形就直接用opencvsharp里面封装好了的RotatedRect。
private Size2f cropSize = new Size2f(400, 300);
private RotatedRect cropRect = new RotatedRect();
private PictureBox cropRectBox;
private SolidBrush brush;
public Form1()
{
InitializeComponent();
brush = new SolidBrush(Color.FromArgb(50, 255, 0, 0));
cropRectBox = new PictureBox()
{
Visible = false,
BackColor = Color.Transparent,
SizeMode = PictureBoxSizeMode.Normal,
BorderStyle = BorderStyle.None,
Parent = rawImgPb
};
cropRectBox.MouseDown += CropRectBox_MouseDown;
rawImgPb.Image = Image.FromFile("C:\\Users\\Administrator\\Desktop\\测试\\1.jpg");
}
private void CropRectBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Right)
return;
cropRect.Angle += 10;
ShowCropRectBox(cropRect);
}
private void rawImgPb_MouseDown(object sender, MouseEventArgs e)
{
cropRect.Center = new Point2f(e.X + cropSize.Width / 2, e.Y + cropSize.Height / 2);
cropRect.Size = cropSize;
cropRect.Angle = 0;
ShowCropRectBox(cropRect);
}
private void ShowCropRectBox(RotatedRect cropRect)
{
cropRectBox.Show();
var boundRect = cropRect.BoundingRect();
cropRectBox.Location = new Point(boundRect.X, boundRect.Y);
cropRectBox.Size = new Size(boundRect.Width, boundRect.Height);
cropRectBox.Image = new Bitmap(boundRect.Width, boundRect.Height);
using (var g = Graphics.FromImage(cropRectBox.Image))
{
g.Clear(Color.Transparent);
var pts = cropRect.Points();
Point[] newPts = new Point[pts.Length];
for (int i = 0; i < pts.Length; i++)
{
//把裁剪矩形的点转换成裁剪框里面的坐标,这样才能在裁剪框的图片里画出裁剪矩形
newPts[i] = cropRectBox.PointToClient(cropRectBox.Parent.PointToScreen(new Point((int)pts[i].X, (int)pts[i].Y)));
}
g.FillPolygon(brush, newPts);
}
if (cropRect.Angle == 0)
cropImgPb.Image = GetRoiImage(rawImgPb.Image, new Rectangle(boundRect.X, boundRect.Y, boundRect.Width, boundRect.Height));
else
cropImgPb.Image = GetRoiImage(rawImgPb.Image, cropRect);
}
public static Bitmap GetRoiImage(Image sourceImg, Rectangle roi)
{
if (roi.Width == 0 || roi.Height == 0)
return null;
Bitmap resultBitmap = new Bitmap(roi.Width, roi.Height);
using (Graphics g = Graphics.FromImage(resultBitmap))
{
Rectangle resultRectangle = new Rectangle(0, 0, roi.Width, roi.Height);
g.DrawImage(sourceImg, resultRectangle, roi, GraphicsUnit.Pixel);
}
return resultBitmap;
}
public static Bitmap GetRoiImage(Image sourceImg, RotatedRect rotatedRoi)
{
var source = sourceImg as Bitmap;
//Pen pen = new Pen(Color.FromArgb(50, 100, 100, 100));
//Pen pen1 = new Pen(Color.FromArgb(50, 30, 30, 30));
using (var g = Graphics.FromImage(sourceImg))
{
var result = new Bitmap((int)rotatedRoi.Size.Width, (int)rotatedRoi.Size.Height);
var pt1 = rotatedRoi.Points()[1];
var radian = rotatedRoi.Angle * Math.PI / 180.0;
var sin = Math.Sin(radian);
var cos = Math.Cos(radian);
//在原图上绕着旋转矩形特定的点,找出旋转之后的对应点,然后把对应点都要偏移到一张新的图像上
for (int x1 = (int)pt1.X; x1 < pt1.X + result.Width; x1++)
{
for (int y1 = (int)pt1.Y; y1 < pt1.Y + result.Height; y1++)
{
//g.DrawRectangle(pen1, new Rectangle(x1, y1, 1, 1));
var x2 = (x1 - pt1.X) * cos - (y1 - pt1.Y) * sin + pt1.X;
var y2 = (x1 - pt1.X) * sin + (y1 - pt1.Y) * cos + pt1.Y;
if (x2 < 0 || y2 < 0 || x2 >= source.Width || y2 >= source.Height)
continue;
//把对应点都要偏移到一张新的图像上
var wdith = x1 - (int)pt1.X;
var height = y1 - (int)pt1.Y;
if (wdith < result.Width && height < result.Height)
result.SetPixel(wdith, height, source.GetPixel((int)x2, (int)y2));
//g.DrawRectangle(pen, new Rectangle((int)x2, (int)y2, 1, 1));
}
}
return result;
}
}
public static Mat GetRoiMat(Mat src, RotatedRect rotatedRoi)
{
var dst = new Mat((int)rotatedRoi.Size.Height, (int)rotatedRoi.Size.Width,MatType.CV_8UC3);
var pt1 = rotatedRoi.Points()[1];
var radian = rotatedRoi.Angle * Math.PI / 180.0;
var sin = Math.Sin(radian);
var cos = Math.Cos(radian);
//在原图上绕着旋转矩形特定的点,找出旋转之后的对应点,然后把对应点都要偏移到一张新的图像上
for (int x1 = (int)pt1.X; x1 < pt1.X + dst.Width; x1++)
{
for (int y1 = (int)pt1.Y; y1 < pt1.Y + dst.Height; y1++)
{
var x2 = (x1 - pt1.X) * cos - (y1 - pt1.Y) * sin + pt1.X;
var y2 = (x1 - pt1.X) * sin + (y1 - pt1.Y) * cos + pt1.Y;
if (x2 < 0 || y2 < 0 || x2 >= src.Width || y2 >= src.Height)
continue;
//把对应点都要偏移到一张新的图像上
var wdith = x1 - (int)pt1.X;
var height = y1 - (int)pt1.Y;
if (wdith < dst.Width && height < dst.Height)
dst.Set(height, wdith, src.At<Vec3b>((int)y2, (int)x2));
}
}
return dst;
}