我正在尝试制作一个图像裁剪工具。
我正在关注这个 https://www.codeproject.com/Articles/703519/Cropping-Particular-Region-In-Image-Using-Csharp
但是由于它有点旧并且它使用的插件/DLL已经更改,我一直在尝试将他的代码从OpenCvSharp 2.0调整为OpenCvSharp 2.4
。当我将位图转换为 IplImages 并使用 Cv.Mul(( 时,它给了我这个错误:
已尝试读取或写入受保护的内存。这通常表示其他内存已损坏
我从未使用过OpenCvSharp其他方法来创建IplImage,甚至从书面图像中读取IplImage。
法典:
public static IplImage BitmapToIplImage(Bitmap bitmap)
{
IplImage tmp, tmp2;
System.Drawing.Rectangle bRect = new System.Drawing.Rectangle(new System.Drawing.Point(0, 0), new System.Drawing.Size((int)bitmap.Width, (int)bitmap.Height));
BitmapData bmData = bitmap.LockBits(bRect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
tmp = Cv.CreateImage(Cv.Size(bitmap.Width, bitmap.Height), BitDepth.U8, 3);
tmp2 = Cv.CreateImage(Cv.Size(bitmap.Width, bitmap.Height), BitDepth.U8, 1);
byte[] data = new byte[Math.Abs(bmData.Stride * bmData.Height)];
tmp.SetData(bmData.Scan0, data.Length);
bitmap.UnlockBits(bmData);
// Cv.CvtColor(tmp, tmp2, ColorConversion.RgbToGray);
return tmp;
}
private void CropImage()
{
IplImage ipl = Cv.CreateImage(new CvSize(curBmp.Width, curBmp.Height), BitDepth.U8, 3);
Graphics ga = Graphics.FromImage(curBmp);
ga.FillRectangle(new SolidBrush(System.Drawing.Color.Black), new System.Drawing.Rectangle(0, 0, curBmp.Width, curBmp.Height));
SolidBrush brush = new SolidBrush(System.Drawing.Color.FromArgb(1, 1, 1));
curGraphics.FillClosedCurve(brush, imagePoints.ToArray());
Cv.Mul(BitmapToIplImage(curOgBmp), BitmapToIplImage(curBmp), ipl, 1);
ComputeCrop();
Stream s = null;
ipl.ToStream(s, ".png", null);
curBmp = new Bitmap(s);
RefreshImageViewer();
}
-----------------------------编辑-----------------------------------------
我试图遵循 Markus 发布的内容,并让它在代码中没有任何错误的情况下工作。
虽然裁剪的图像有点奇怪,但这是我使用的方法,以及 RefreshImageViewer,这是我在图像控件中放置位图的方式。
几个小时以来,我一直在试图看看我是否错过了什么,但我认为没有。
输出示例:图像链接
法典:
public void RefreshImageViewer()
{
bmpSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
curBmp.GetHbitmap(),
IntPtr.Zero,
System.Windows.Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight(curBmp.Width, curBmp.Height));
imageViewer.Source = bmpSource;
curGraphics = Graphics.FromImage(curBmp);
}
private void CropImage()
{
Graphics Ga = Graphics.FromImage(curBmp);
//the black image
Ga.FillRectangle(new SolidBrush(System.Drawing.Color.Black), new System.Drawing.Rectangle(0, 0, curBmp.Width, curBmp.Height));
//draw from the last point to first point
Ga.DrawLine(new System.Drawing.Pen(System.Drawing.Color.Red, 3), imagePoints[imagePoints.Count - 1], imagePoints[0]);
//all of the rgb values are being set 1 inside the polygon
SolidBrush Brush = new SolidBrush(System.Drawing.Color.FromArgb(1, 1, 1));
//we have to prepare one mask of Multiplying operation for cropping region
curGraphics.FillPolygon(Brush, imagePoints.ToArray());
Mat accc = (BitmapToMat(curOgBmp).Mul(BitmapToMat(curBmp))).ToMat();
System.Drawing.Rectangle r = ComputeCrop();
curBmp = accc.ToBitmap().Clone(r, curOgBmp.PixelFormat);
RefreshImageViewer();
}
private System.Drawing.Rectangle ComputeCrop()
{
int smallestX = curBmp.Width, biggestX = 0, biggestY = 0, smallestY = curBmp.Height;
for (int i = 0; i < imagePoints.Count; i++)
{
biggestX = Math.Max(biggestX, imagePoints[i].X);
smallestX = Math.Min(smallestX, imagePoints[i].X);
biggestY = Math.Max(biggestY, imagePoints[i].Y);
smallestY = Math.Min(smallestY, imagePoints[i].Y);
}
System.Drawing.Rectangle rectCrop = new System.Drawing.Rectangle(smallestX, smallestY, biggestX - smallestX, biggestY - smallestY);
return rectCrop;
}
public static Mat BitmapToMat(Bitmap bitmap)
{
Mat tmp, tmp2;
System.Drawing.Rectangle bRect = new System.Drawing.Rectangle(new System.Drawing.Point(0, 0), new System.Drawing.Size((int)bitmap.Width, (int)bitmap.Height));
BitmapData bmData = bitmap.LockBits(bRect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
tmp2 = new Mat(new OpenCvSharp.Size(bitmap.Width, bitmap.Height), MatType.CV_8U);
tmp = new Mat(bitmap.Height, bitmap.Width, MatType.CV_8UC3, bmData.Scan0);
bitmap.UnlockBits(bmData);
return tmp;
}
"IplImage" 是 OpenCv 1 中的旧映像容器。正如Andreas已经提到的,今天你应该使用"Mat"。也看看这里 : cvMat,Mat和IpImage之间的区别
不幸的是,您的代码示例不完整,因此我更正了原始项目(https://www.codeproject.com/Articles/703519/Cropping-Particular-Region-In-Image-Using-Csharp(中的两种方法。
以下方法在原始项目中进行了测试,并结合最新的OpenCVSharp版本(v4.x(按预期工作。现在,将更改转换为代码应该非常简单。
public static Mat BitmapToIplImage(Bitmap bitmap)
{
Mat tmp, tmp2;
Rectangle bRect = new Rectangle(new System.Drawing.Point(0, 0), new System.Drawing.Size((int)bitmap.Width, (int)bitmap.Height));
BitmapData bmData = bitmap.LockBits(bRect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
tmp2 = new Mat(new Size(bitmap.Width, bitmap.Height), MatType.CV_8U);
tmp = new Mat(bitmap.Height, bitmap.Width, MatType.CV_8UC3, bmData.Scan0);
bitmap.UnlockBits(bmData);
return tmp;
}
private void crop()
{
timer1.Stop();
Graphics Ga = Graphics.FromImage(bmp);
//the black image
Ga.FillRectangle(new SolidBrush(Color.Black), new Rectangle(0, 0, bmp.Width, bmp.Height));
//draw from the last point to first point
Ga.DrawLine(new Pen(Color.Red, 3), polygonPoints[polygonPoints.Count - 1], polygonPoints[0]);
//all of the rgb values are being set 1 inside the polygon
SolidBrush Brush = new SolidBrush(Color.FromArgb(1, 1, 1));
//we have to prepare one mask of Multiplying operation for cropping region
G.FillClosedCurve(Brush, polygonPoints.ToArray());
var accc= (BitmapToIplImage(Source).Mul(BitmapToIplImage(bmp))).ToMat();
computecrop();
croplast = accc.ToBitmap().Clone(rectcrop, Source.PixelFormat);//just show cropped region part of image
pictureBox2.Image = croplast; // crop region of image
}