OpencvSharp基础学习5

您所在的位置:网站首页 opencv裁剪圆形区域 OpencvSharp基础学习5

OpencvSharp基础学习5

2024-01-12 00:44| 来源: 网络整理| 查看: 265

一、本章学习以下几个算子 1.MinAreaRect:最小外接矩形 2.CopyTo: 复制图片(掩膜复制法) 3.GetRotationMatrix2D:计算旋转矩阵 4.WarpAffine:图像变换 5.GetRectSubPix:裁剪图像 二、算子介绍 1.MinAreaRect:最小外接矩形 函数解析:

该函数计算并返回指定点集的最小区域边界斜矩形。

函数原型: RotatedRect minAreaRect(InputArray points) 函数参数: points:输入信息,可以为包含点的容器(vector)或是Mat。 函数返回值:

RotatedRect类型,返回包覆输入信息的最小斜矩形,参数有最小外接矩形的中心center,(宽度,高度),旋转角度等

使用注意事项:

(1). 旋转角度θ是水平轴(x轴)逆时针旋转,与碰到的矩形的第一条边的夹角。并且这个边的边长是width,另一条边边长是height。也就是说,在这里,width与height不是按照长短来定义的。 (2). 在opencv中,坐标系原点在左上角,相对于x轴,逆时针旋转角度为负,顺时针旋转角度为正。所以,θ∈(-90度,0]。 (3). 获取的4个顶点中顺序为顺时针,第一个点位为y值最小的点。

2.CopyTo: 复制图片 函数解析:

把一张图片的整张或掩膜区域像素拷贝到另一张图片上。

函数原型: void copyTo( OutputArray m ); //拷贝整张图(被拷贝的图) void copyTo( OutputArray m, InputArray mask ); //拷贝掩膜图(被拷贝的图和掩膜图作与运算) 函数参数: m:输出图像,Mat。 mask:输入掩膜图 函数返回值:

使用注意事项:

(1). 格式为:被拷贝图像.copyTo(输出图像,掩膜图)。 (2). 被拷贝图像和掩膜图大小必须一致,如果已知掩膜图比被拷贝图像大,可以裁剪掉不需要的部分,反之则新建一个新掩膜图用0像素填充,裁剪ROI区域(大小和老掩膜图一致),然后把老掩膜图拷贝到新掩膜图的ROI中。 (3).ROI拷贝:在原图上截取指定区域的ROI,大小和被拷贝图一致,注意此处截取使用关联截取,C++有两种方式cv:Rect和 cv::Range ,C#则用Mat roi = new Mat(cv2.Rect()),然后再使用copyTo进行整图

3.GetRotationMatrix2D:计算旋转矩阵 函数解析:

仿射变换是一种二维坐标(x, y)到二维坐标(u, v)的线性变换,其数学表达式形式如下: 在这里插入图片描述 对应的齐次坐标矩阵表示形式为: 在这里插入图片描述

仿射变换保持了二维图形的“平直性”(直线经仿射变换后依然为直线)和“平行性”(直线之间的相对位置关系保持不变,平行线经仿射变换后依然为平行线,且直线上点的位置顺序不会发生变化)。 非共线的三对对应点可以确定一个唯一的仿射变换。

函数原型: Mat getRotationMatrix2D(Point2f center, double angle, double scale) 函数参数: Point2f center:表示旋转的中心点 double angle:表示旋转的角度 double scale:图像缩放因子 函数返回值:

Mat类型,存放一个2*3的矩阵(变换参数)

使用注意事项:

4.WarpAffine:图像变换 函数解析:

v2.warpAffine()函数主要是利用变换矩阵M对图像进行如旋转、仿射、平移等变换,只需要我们提供一个2*3的变换矩阵M,就可以对图像进行变换。它一般是和cv2.getRotationMatrix2D(旋转和平移)和cv.GetAffineTransform(扭曲仿射变换)两个函数在一起使用,这两个函数是用来获取变换矩阵M,这样就不需要我们自己设置M。

函数原型: void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar& boederValue = Scalar()); 函数参数: src,输入图像,即原图像,填 Mat 类对象那个即可。 dst,输出图像,需要和源图像有一样的类型。 M,2×3 的变换矩阵。因为变换矩阵第三行形式固定,所以忽略。 dsize,输出图像的尺寸。 flags,插值的标识符。默认为 INTER_LINEAR (线性插值)。插值就是根据已知数据点(条件),来预测未知数据点值得方法。在尺寸调整过程中,图像的大小可能发生改变。此时像素与像素之间的关系就不是一一对应关系,因此在尺寸调整过程中,可能会涉及到像素值的插值计算。可选插值方式如下: INTER_NEAREST(最近邻差值) INTER_LINEAR(线性插值,默认) INTER_AREA(区域插值,利用像素区域关系的重采样插值) INTER_CUBIC(三次样条插值,超过 4×4 像素邻域内的双三次插值) INTER_LANCZOS4(Lanczos 插值,超过 8×8 像素邻域的 Lanczos 插值) borderMode,边界扩展类型。默认值为 BORDER_CONSTANT: borderValue ,只有当 borderMode取值为 BORDER_CONSTANT 时,这个参数才会被使用,边界会被填充成 borderValue 指定的颜色。 默认是BORDER_CONSTANT(即指定常数值填充) ,实质上,边界处理类型,该枚举型还有: BORDER_CONSTANT iiiiii|abcdefgh|iiiiiii with some specified i(指定常数填充) BORDER_REPLICATE aaaaaa|abcdefgh|hhhhhhh(复制边缘像素填充) BORDER_REFLECT fedcba|abcdefgh|hgfedcb(反射复制边界像素) BORDER_WRAP cdefgh|abcdefgh|abcdefg BORDER_REFLECT_101 gfedcb|abcdefgh|gfedcba(对称填充,也就是以最边缘像素为轴) BORDER_TRANSPARENT uvwxyz|absdefgh|ijklmno BORDER_REFLECT101 same as BORDER_REFLECT_101 BORDER_DEFAULT same as BORDER_REFLECT_101 BORDER_ISOLATED do not look outside of ROI 函数返回值:

使用注意事项:

(1). 使用dsize参数控制输出图像尺寸,如果要无损旋转,disze的大小要比原图大,即: int heightNew = int(src.cols * fabs(sin(angle * CV_PI / 180.0)) + src.rows * fabs(cos(angle * CV_PI / 180.0))); int widthNew = int(src.cols * fabs(cos(angle * CV_PI / 180.0)) + src.rows * fabs(sin(angle * CV_PI / 180.0))); (2). 参数M是通过cv2.getRotationMatrix2D(旋转和平移)或者 cv.GetAffineTransform(扭曲仿射变换)获得的变换矩阵。

5.GetRectSubPix:裁剪图像 函数解析:

从原图像中提取一个感兴趣的矩形区域图像。

函数原型: void getRectSubPix(InputArray image, Size patchSize, Point2f center, OutputArray dst, int patchType=-1 ) 函数参数: InputArray image:输入图像 Size patchSize:获取感兴趣区域矩形的大小 Point2f center:感兴趣区域矩形在原图像中的位置(即感兴趣区域矩形的中心点坐标) OutputArray patch:输出的图像 int patchType=-1 :表示输出图像的深度。默认-1 ,深度不变 函数返回值:

使用注意事项:

三、案例展示 1.结果展示

在这里插入图片描述

2.创造原图 Mat binMat; //二值图 Mat srcMat;//原图 private void button1_Click(object sender, EventArgs e) { srcMat = new Mat(new OpenCvSharp.Size(400, 400), MatType.CV_8UC1, new Scalar(50)); 生成三角形 //RotatedRect rotateRect = new RotatedRect(new Point2f(200, // 200), new Size2f(200, 200), // 10); OpenCvSharp.Point[][] points = new OpenCvSharp.Point[1][]; points[0] = new OpenCvSharp.Point[3] { new OpenCvSharp.Point(220,280), new OpenCvSharp.Point(150,150),new OpenCvSharp.Point(280,220)}; //绘制三角形 Cv2.DrawContours(srcMat, points, 0, new Scalar(255, 255, 255), -1); pictureBox1.Image = BitmapConverter.ToBitmap(srcMat); } 2.处理原图:二值化原图(可忽略或者其他图像处理方式,把目标展示出来) private void button6_Click(object sender, EventArgs e) { binMat = new Mat(); Cv2.Threshold(srcMat, binMat, 200, 255, ThresholdTypes.Binary); pictureBox6.Image = BitmapConverter.ToBitmap(binMat); } 3.求目标ROI区域(这里用最小外接矩形来求),正常需要加面积过滤来过滤干扰 OpenCvSharp.Point[][] points; HierarchyIndex[] hierarchyIndices; RotatedRect rotatedRect; OpenCvSharp.Point[][] minPoints; private void button2_Click(object sender, EventArgs e) { //查找轮廓 Cv2.FindContours(binMat, out points, out hierarchyIndices, RetrievalModes.External, ContourApproximationModes.ApproxNone); //求最小外接矩形 Point2f[] minRect; rotatedRect = Cv2.MinAreaRect(points[0]); Mat minMat = binMat.Clone(); Point2f[] point2F = rotatedRect.Points(); //顶点浮点数的中心点转成整数 OpenCvSharp.Point[] rectVertices = new OpenCvSharp.Point[4]; for (int i = 0; i < 4; i++) { rectVertices[i] = new OpenCvSharp.Point(point2F[i].X, point2F[i].Y); } minPoints = new OpenCvSharp.Point[1][]; minPoints[0] = rectVertices; Cv2.DrawContours(minMat, minPoints, 0, new Scalar(150, 150), 4); pictureBox2.Image = BitmapConverter.ToBitmap(minMat); } 4.图像变换前裁剪,和变换后作对比参考(实际应用不需要这步) private void button9_Click(object sender, EventArgs e) { Mat mat = new Mat(); Cv2.GetRectSubPix(binMat, new OpenCvSharp.Size(rotatedRect.Size.Width + 10, rotatedRect.Size.Height + 10), rotatedRect.Center, mat); MessageBox.Show("变换前ROI宽高和中心是,宽:" + rotatedRect.Size.Width + ",高:" + rotatedRect.Size.Height + ",X:" + rotatedRect.Center.X + ",Y:" + rotatedRect.Center.Y); pictureBox8.Image = BitmapConverter.ToBitmap(mat); } 5.把ROI区域用255掩膜,掩膜图用来拷贝 private void button7_Click(object sender, EventArgs e) { //在二值图像中圈出轮廓区域(这里是最小外接矩形)并染白 Cv2.DrawContours(binMat, minPoints, -1, Scalar.White, -1); pictureBox3.Image = BitmapConverter.ToBitmap(binMat); } 6.在原图上,扣取掩膜区域的图 //把掩膜区域的原图抠到此图上 Mat outRoi; private void button4_Click(object sender, EventArgs e) { outRoi = new Mat(400,400, MatType.CV_8UC1); outRoi.SetTo(100); //提取Roi,要把目标画到此处 Mat roi = new Mat(outRoi, new Rect(0, 0, 400, 400)); //将原图通过mask抠图到Roi,这里意思是,将原图srcMat的对应binMat区域中大于1的值抠到新图Roi中,小于1的值ROI图保留原数据 srcMat.CopyTo(roi, binMat); pictureBox4.Image = BitmapConverter.ToBitmap(outRoi); }

原则上,5、6都可以省略,这里添加进去是为了测试,把图片ROI区域通过掩膜的方式提取到另一张图上

7.图像变换 //存放ROI发生旋转变换后的图片(画布) Mat afterRotato; private void button3_Click(object sender, EventArgs e) { //存放ROI发生旋转变换后的图片(画布) afterRotato = new Mat(srcMat.Size(), MatType.CV_8UC1); afterRotato.SetTo(0); //设置像素值,不起作用,因为使用了WarpAffine填充边界 //获取最小矩形中心(旋转中心) Point2f center = rotatedRect.Center; //获取最小矩形角度(旋转角度) double angel = rotatedRect.Angle; //根据中心和角度计算变换矩阵,缩放比率为1 Mat M = Cv2.GetRotationMatrix2D(center, angel, 1); //得到变换后的图像,控制输入图像大小来改变画布大小,填充边界使用默认0值,这里使用BorderTypes.Replicate(复制边缘像素填充) Cv2.WarpAffine(outRoi, afterRotato, M, outRoi.Size(), InterpolationFlags.Linear, BorderTypes.Replicate); pictureBox5.Image = BitmapConverter.ToBitmap(afterRotato); } 8.变换后裁剪ROI

private void button8_Click(object sender, EventArgs e) { Mat mat = new Mat(); Cv2.GetRectSubPix(afterRotato, new OpenCvSharp.Size(rotatedRect.Size.Width+1, rotatedRect.Size.Height+1), rotatedRect.Center, mat); MessageBox.Show(“变换后ROI宽高和中心是,宽:” + rotatedRect.Size.Width + “,高:” + rotatedRect.Size.Height + “,X:” + rotatedRect.Center.X + “,Y:” + rotatedRect.Center.Y); pictureBox7.Image = BitmapConverter.ToBitmap(mat); }

四、总结 1.案例里,创造的裁剪矩形是变换前的最小外接矩形的长宽和中心,把角度归0。因为我们使用ROI本身去变换,最小矩形通过变换后,就是我们裁剪的矩形,当然需要判断一下长短边,这里省略了,关于最小矩形长短边判断后面再总结。 2.在工业视觉中,模板匹配去做初定位,然后绘制ROI去找直线或者圆等应用,其图像变换是在模板中心和模板角度发生,所以绘制的ROI也要通过同样的变换,才能裁剪正常。 3.案例里,是把目标纠正,然后去裁剪。工业视觉中,通常是不肯正的,所以需要用到案例里的4、5步,以上第二点提到,ROI通过同样的变换后,圈中了目标区域,但是当前ROI很可能带有角度,部分裁剪。所以要掩膜把当前ROI复制出来,然后再一次变换为不带角度裁剪,或者直接扩大裁剪区域。

注:文章部分函数解析参考网上资料!如有侵权,连续删除! 转载本文需要标明出处! 谷子彭:[email protected]



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3