用DrawLine与cvLine在位图中绘制直线
Graphics.DrawLine与CvInvoke.cvLine可以在位图中的指定位置绘制指定颜色的直线(C#) ,近期刚学会使用,记录一下学习过程中遇到的“挫折”,便于今后查阅。
1. Graphics.DrawLine
Graphics.DrawLine在位图中绘制直线的代码示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public void DrawLinePoint(string imageSrcPath) { Bitmap bmp = new Bitmap(imageSrcPath); Graphics gNew = Graphics.FromImage(binaryBmp); // Create pen. Pen blackPen = new Pen(Color.Black, 3); // Create points that define line. Point point1 = new Point(100, 100); Point point2 = new Point(500, 100); // Draw line to screen. gNew.DrawLine(blackPen, point1, point2); } |
Graphics.DrawLine方法无法从带有索引像素格式的图像创建graphics对象。如果我将原始位图先执行二值化,将二值化数组转换为位图,若直接在二值化图像上创建graphics对象时,就会报错“无法从带有索引像素格式的图像创建graphics对象”,如下图所示。无论使用自行编写的二值化函数或是使用EMGU的CvInvoke.cvThreshold执行二值化,均会出现此错误。
针对此错误,我在http://www.cnblogs.com/qixuejia/archive/2010/09/03/1817248.html找到了对策(感谢原作者),我们可以先判断原图片是否是索引像素格式的,如果是,则可以采用将此图片先clone到一张BMP上的方法来解决,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
/// <summary> /// 会产生graphics异常的PixelFormat /// </summary> private static PixelFormat[] indexedPixelFormats = { PixelFormat.Undefined, PixelFormat.DontCare, PixelFormat.Format16bppArgb1555, PixelFormat.Format1bppIndexed, PixelFormat.Format4bppIndexed, PixelFormat.Format8bppIndexed }; /// <summary> /// 判断图片的PixelFormat 是否在 引发异常的 PixelFormat 之中 /// </summary> /// <param name="imgPixelFormat">原图片的PixelFormat</param> /// <returns></returns> private static bool IsPixelFormatIndexed(PixelFormat imgPixelFormat) { foreach (PixelFormat pf in indexedPixelFormats) { if (pf.Equals(imgPixelFormat)) return true; } return false; } //.........使用 using (Image img = Image.FromFile("原图片路径")) { //如果原图片是索引像素格式之列的,则需要转换 if (IsPixelFormatIndexed(img.PixelFormat)) { Bitmap bmp = new Bitmap(img.Width, img.Height, PixelFormat.Format32bppArgb); using (Graphics g = Graphics.FromImage(bmp)) { g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; g.DrawImage(img, 0, 0); } //下面就直接对 bmp 进行处理 //...... } else //否则直接操作 { //直接对img进行处理 } } |
2.CvInvoke.cvLine
每次使用EMGU提供的相关函数,参数的给定方式都颇费周折,因此成功使用CvInvoke下的函数后均要将其参数写法记录下来,避免今后再被相同的问题困扰。
1 2 3 |
Image<Gray, Byte> imgBgr = new Image<Gray, byte>(imageSrcPath); CvInvoke.cvLine(imgBgr, new Point(10, 30), new Point(30, 30), new MCvScalar(0, 0, 255), 1, Emgu.CV.CvEnum.LINE_TYPE.EIGHT_CONNECTED, 0); |
CvInvoke.cvLine可以直接对索引像素格式的图像进行处理,也就是说CvInvoke.cvLine可以在二值化后的图像上画线。
3. Graphics.DrawLine与CvInvoke.cvLine中坐标
两个函数均需要指明直线的起点与终点,而这两个点的坐标与二值化图像数组的坐标风格不一致。假设Byte[,] Array为某个图像的二值化数组,定义该图像从左到右为一行,从上到下为一列,那么该图像的高度即为该数组的行数,而宽度为该数组的列数。那么Array[i,j]指的是图像上的第i行第j列的像素,但new Point(i,j)则指的是第i列第j行。 也就是所如果给出直线的两点为Point(10,30),Point(30,30),则结果为一条直线;若为Point(30,10),Point(30,30),则结果为一条竖线。
Awesome you should think of sonhetimg like that