提取旋转矩形区域图像

OpenCV中有一个很有用的数据结构是RotatedRect,也就是旋转的矩形。最近手上有一个需求是提取图像中旋转矩形的区域作为一张单独的图像保存起来,但是库里面没有现成的API,网上查了一下,大致是这么一种方法:先根据矩形的角度把图像整体旋转到水平方向,再计算出旋转矩形的四个点在旋转之后的位置,也就是一个水平的矩形,可以形成一个bbox从而提取出想要的图像。

思路没有问题,但是网上的一些源码在处理一些特殊情况时依旧会报错,也就是,当矩形的长边大于原图像的短边,并且矩形角度大于45度时,矩形就会进行一个大反转,这时四个点经过旋转变换后,会出现负坐标,解决方法就是增加一个判定,在任何情况下都不让它出现大反转就行了。

说多了没用,上代码,代码是opencvsharp版本的:

public void CutLabel(Mat input, RotatedRect rect, out Bitmap label)
    {
        float angle = rect.Angle;
        int height = Convert.ToInt16(rect.Size.Height);
        int width = Convert.ToInt16(rect.Size.Width);

        
        if (Math.Abs(angle) > 45)
        {
            angle = angle + 90;
        }

        Point2f[] rect_points = rect.Points();

        //把原图旋转到水平方向
        Mat rotate_mat = Cv2.GetRotationMatrix2D(new OpenCvSharp.Point(input.Width / 2, input.Height / 2), angle, 1);
        int heightNew = Convert.ToInt16(input.Width * Math.Abs(Math.Sin(Math.PI * (angle / 180))) +
                                        input.Height * Math.Abs(Math.Cos(Math.PI * (angle / 180))));
        int widthNew = Convert.ToInt16(input.Height * Math.Abs(Math.Sin(Math.PI * (angle / 180))) +
                                        input.Width * Math.Abs(Math.Cos(Math.PI * (angle / 180))));

        rotate_mat.Set<int>(0, 2, (widthNew - input.Width) / 2 + rotate_mat.At<int>(0, 2));
        rotate_mat.Set<int>(1, 2, (heightNew - input.Height) / 2 + rotate_mat.At<int>(1, 2));

        Mat img_rotate = new Mat();
        Cv2.WarpAffine(input, img_rotate, rotate_mat, new OpenCvSharp.Size(widthNew, heightNew));



        List<Point2f> rotatedRectPoints = new List<Point2f>();

        //旋转矩形的四个顶点
        Cv2.Transform(InputArray.Create(rect_points), OutputArray.Create(rotatedRectPoints), rotate_mat);

        //提取矩形区域
        Rect bbox = Cv2.BoundingRect(rotatedRectPoints);
        Mat ret = new Mat();
        img_rotate[bbox].CopyTo(ret);
        if (ret.Width < ret.Height)
        {
            Mat ret_trans = new Mat();
            Mat ret_flip = new Mat();
            Cv2.Transpose(ret, ret_trans);
            Cv2.Flip(ret_trans, ret_flip, FlipMode.X);
            label = BitmapConverter.ToBitmap(ret_flip);
        }
        else
            label = BitmapConverter.ToBitmap(ret);

    }

这里再解释一下RotatedRect中angle的含义,查资料的过程中看了一篇博文讲得很好,它是将x轴按逆时针旋转,碰到的第一根矩形的边的角度,所以angle的范围是[-90,0],并不是想象中的长边与水平方向的夹角。这里我其实是有点懵逼的,因为在后面的计算旋转矩阵时,角度为正表示的是逆时针旋转,所以正负号不要乱加。。。。

判断指定点是否在给定的矩形区域内

opencv有一个函数叫PointPolygonTest()可以用来判断一个点是否在contour内。但是这里我们需要的是判断是否在RotatedRect内,那么就需要把这个矩形转换为contour。怎么转换呢?c++里面可以参考OpenCV中判断点在矩形中的方法。那opencvsharp里面怎么搞??我想的是把矩形区域绘制出来,然后重新计算一遍contour....这里的数据转换可把我整懵了.....具体实现方法如下:

RotatedRect rect = Cv2.MinAreaRect(contours[i]);

Mat rect_img = new Mat(img.Size(), MatType.CV_8UC1, 0);
OpenCvSharp.Point[] pts = new OpenCvSharp.Point[]
{
    new OpenCvSharp.Point(rect.Points()[0].X,rect.Points()[0].Y),
    new OpenCvSharp.Point(rect.Points()[1].X,rect.Points()[1].Y),
    new OpenCvSharp.Point(rect.Points()[2].X,rect.Points()[2].Y),
    new OpenCvSharp.Point(rect.Points()[3].X,rect.Points()[3].Y),
};
OpenCvSharp.Point[][] pts1 = new OpenCvSharp.Point[][] { pts};
Cv2.FillPoly(rect_img, pts1, 255);

OpenCvSharp.Point[][] contours_rect;
HierarchyIndex[] hierarchy;
Cv2.FindContours(rect_img, out contours_rect, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxNone);
double score = Cv2.PointPolygonTest(contours_rect[0], index, true);

如果有大佬知道更简单的转换方法,求告知呀~~~~