Perfect Rectangle(完美矩形)

探讨了判断多个矩形是否能精确覆盖一个大矩形的问题,提供了三种解法:暴力检查重叠与面积总和,四叉树算法,及利用矩形顶点计数的巧妙方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们有 N 个与坐标轴对齐的矩形, 其中 N > 0, 判断它们是否能精确地覆盖一个矩形区域。

每个矩形用左下角的点和右上角的点的坐标来表示。例如, 一个单位正方形可以表示为 [1,1,2,2]。 ( 左下角的点的坐标为 (1, 1) 以及右上角的点的坐标为 (2, 2) )。

示例 1:

rectangles = [
  [1,1,3,3],
  [3,1,4,2],
  [3,2,4,4],
  [1,3,2,4],
  [2,3,3,4]
]

返回 true。5个矩形一起可以精确地覆盖一个矩形区域。

 

 

示例 2:

rectangles = [
  [1,1,2,3],
  [1,3,2,4],
  [3,1,4,2],
  [3,2,4,4]
]

返回 false。两个矩形之间有间隔,无法覆盖成一个矩形。

 

 

示例 3:

rectangles = [
  [1,1,3,3],
  [3,1,4,2],
  [1,3,2,4],
  [3,2,4,4]
]

返回 false。图形顶端留有间隔,无法覆盖成一个矩形。

 

 

示例 4:

rectangles = [
  [1,1,3,3],
  [3,1,4,2],
  [1,3,2,4],
  [2,2,4,4]
]

返回 false。因为中间有相交区域,虽然形成了矩形,但不是精确覆盖。

这道题我在写前两个解法的时候还没ac通过,但是已经忍不住要写了。思路还是很清晰的,遍历矩形,算有没有重合,面积累加,最后看看总面积是不是最大范围的覆盖的矩形的面积。
第一遍暴力解法:
class Solution {
public:
    bool Cover(vector<int>& a, vector<int>&b)
    {
        if (min(a[0], a[2]) >= max(b[0], b[2]) || max(a[0], a[2]) <= min(b[0], b[2])
            || min(a[1], a[3]) >= max(b[1], b[3]) || max(a[1], a[3]) <= min(b[1], b[3]))
        {
            return false;
        }
        return true;
    }
    bool isRectangleCover(vector<vector<int>>& rectangles) {
        int minx, miny, maxx, maxy;
        minx = miny = INT_MAX;
        maxx = maxy = INT_MIN;
        long long int sum = 0;
        for (int i = 0; i < rectangles.size();i++)
        {
            for (int r = 0; r < i;r++)
            {
                if (Cover(rectangles[i], rectangles[r]))
                {
                    return false;
                }
            }
            minx = min(minx, min(rectangles[i][0], rectangles[i][2]));
            miny = min(miny, min(rectangles[i][1], rectangles[i][3]));
            maxx = max(maxx, max(rectangles[i][0], rectangles[i][2]));
            maxy = max(maxy, max(rectangles[i][1], rectangles[i][3]));
            sum += abs((rectangles[i][0] - rectangles[i][2])*(rectangles[i][1] - rectangles[i][3]));
        }
        return sum == (maxx - minx)*(maxy - miny);
    }
};

果不其然挂了,测试最后两组都是3w+的数据量

第二遍:四叉树解法

class QuadNode
{
public:
    enum{
        quad_1,//四个象限
        quad_2,
        quad_3,
        quad_4,
        quad_count,
    };
    vector<vector<int>>* data;
    QuadNode* Children[quad_count];//孩子指针,数组大小为8
    QuadNode* Parent;//父节点指针
    typedef std::list<int> RecList;
    typedef std::list<int>::iterator RecListIter;
    RecList rectlist;//携带的参数  实体列表
    int quad;//在父节点中的象限
    int deep;//自己所在的层索引
    int minx,miny;
    int maxx,maxy;
    QuadNode(vector<vector<int>>* data, int x1, int x2, int y1, int y2, int dp, int qd)
    {
        minx = x1;
        maxx = x2;
        miny = y1;
        maxy = y2;
        deep = dp;
        quad = qd;
        Parent = NULL;
        this->data = data;
        memset(Children, 0, sizeof(Children));
    }
    ~QuadNode()
    {
        for (int i = 0; i < quad_count; i++)
        {
            if (Children[i])
            {
                delete Children[i];
                Children[i] = NULL;
            }
        }
        rectlist.clear();
    }
    QuadNode* GetDeepest(int index)
    {
        if (deep > 0)
        {
            //4个孩子都要创建
            for (int r = 0; r < QuadNode::quad_count; r++)
            {
                if (!Children[r])
                {
                    int ix = r == QuadNode::quad_1 || r == QuadNode::quad_3 ? minx : (minx + maxx) / 2;
                    int ax = r == QuadNode::quad_1 || r == QuadNode::quad_3 ? (minx + maxx) / 2 : maxx;

                    int iy = r == QuadNode::quad_1 || r == QuadNode::quad_2 ? miny : (miny + maxy) / 2;
                    int ay = r == QuadNode::quad_1 || r == QuadNode::quad_2 ? (miny + maxy) / 2 : maxy;
                    QuadNode *node = new QuadNode(data, ix, ax, iy, ay, deep - 1, r);
                    node->Parent = this;
                    Children[r] = node;
                }
                if (Children[r]->CheckInRange(index))
                {
                    return Children[r]->GetDeepest(index);
                }
            }
        }
        return this;
    }
    bool CheckInRange(int index)
    {
        if ((*data)[index][0] >= minx && (*data)[index][2] <= maxx && (*data)[index][1] >= miny && (*data)[index][3] <= maxy)
        {
            return true;
        }
        return false;
    }
    bool CheckCover(int index)
    {
        QuadNode* n = GetDeepest(index);
        QuadNode* parent = n->Parent;
        while (parent)
        {
            if (parent->CheckWithTrianglelist(index))
            {
                return true;
            }
            parent = parent->Parent;
        }

        if (n->CollisionCheck(index))
            return true;
        n->rectlist.push_back(index);
        return false;
    }
    bool CollisionCheck(int index)
    {
        return CheckWithTrianglelist(index) || CheckWithSubSpace(index);
    }
    bool CheckWithTrianglelist(int index)
    {
        RecListIter itr = rectlist.begin();
        while (itr != rectlist.end())
        {
            int id = *itr;
            if (Cover((*data)[id], (*data)[index]))
            {
                return true;
            }
            itr++;
        }
        return false;
    }

    bool CheckWithSubSpace(int index)
    {
        bool collision = false;
        for (int i = 0; i < quad_count && Children[i]; i++)
        {
            int vec[] = { minx, miny, maxx, maxy };
            vector<int> para(vec, vec + 4);
            if (Cover((*data)[index], para))
            {
                collision |= Children[i]->CollisionCheck(index);
            }
            if (collision)
            {
                return true;
            }
        }
        return false;
    }

    bool Cover(vector<int>& a, vector<int>&b)
    {
        if (min(a[0], a[2]) >= max(b[0], b[2]) || max(a[0], a[2]) <= min(b[0], b[2])
            || min(a[1], a[3]) >= max(b[1], b[3]) || max(a[1], a[3]) <= min(b[1], b[3]))
        {
            return false;
        }
        return true;
    }
};
class Solution {
public:
    int GetMax2Power(int xmax, int ymax, int& lg)
    {
        int max = xmax;
        if (ymax > max)
            max = ymax;
        if ((max & (max - 1)) == 0)
        {
            double L = log(max*1.0) / log(2.0);
            lg = (int)L + 1;
            return max;
        }
        else
        {
            double L = log(max*1.0) / log(2.0);
            lg = (int)L + 2;
            return (int)pow(2 * 1.0, lg - 1.0);
        }
    }
    bool isRectangleCover(vector<vector<int>>& rectangles) {
        int minx, miny, maxx, maxy;
        minx = miny = INT_MAX;
        maxx = maxy = INT_MIN;
        long long int sum = 0;
        for (int i = 0; i < rectangles.size(); i++)
        {
            minx = min(minx, min(rectangles[i][0], rectangles[i][2]));
            miny = min(miny, min(rectangles[i][1], rectangles[i][3]));
            maxx = max(maxx, max(rectangles[i][0], rectangles[i][2]));
            maxy = max(maxy, max(rectangles[i][1], rectangles[i][3]));
        }
        int mx = max(abs(maxx), abs(minx));
        int my = max(abs(maxy), abs(miny));
        int range, lg;
        range = GetMax2Power(mx, my, lg);
        //四叉树
        QuadNode* root = new QuadNode(&rectangles, -range, range, -range, range, lg, 0);

        for (int i = 0; i < rectangles.size();i++)
        {
            if (root->CheckCover(i))
            {
                return false;
            }
            sum += abs((rectangles[i][0] - rectangles[i][2])*(rectangles[i][1] - rectangles[i][3]));
        }
        delete root;
        return sum == (maxx - minx)*(maxy - miny);
    }
};

以为没什么问题了,跑一下又超时了,真是恶心的一p啊。拿测试数据跑一跑发现,几乎95%的数据都在边界上,四叉树无法往下细化。写个四叉树容易吗???

只能硬着头皮继续想了,果然没有想到。参考了下别人的歪门邪道,感觉前面写的东西都白瞎了。有时候解决问题,还得靠技巧。

解法三:所有的矩形顶点,有且只有四个边角是只出现一次,剩下的顶点要么两次,要么四次

 

   long long int getHash(int x, int y)
    {
        long long int t = 2 << 16;
        return x*t + y;
    }
    bool isRectangleCover(vector<vector<int>>& rectangles) {
        int minx, miny, maxx, maxy;
        minx = miny = INT_MAX;
        maxx = maxy = INT_MIN;
        long long int sum = 0;
        unordered_set<long long int> st;
        for (int i = 0; i < rectangles.size(); i++)
        {
            minx = min(minx, min(rectangles[i][0], rectangles[i][2]));
            miny = min(miny, min(rectangles[i][1], rectangles[i][3]));
            maxx = max(maxx, max(rectangles[i][0], rectangles[i][2]));
            maxy = max(maxy, max(rectangles[i][1], rectangles[i][3]));
            sum += abs((rectangles[i][0] - rectangles[i][2])*(rectangles[i][1] - rectangles[i][3]));
            long long int lu = getHash(rectangles[i][0], rectangles[i][3]);
            long long int ld = getHash(rectangles[i][0], rectangles[i][1]);
            long long int ru = getHash(rectangles[i][2], rectangles[i][3]);
            long long int rd = getHash(rectangles[i][2], rectangles[i][1]);
            if (st.count(lu) == 0) st.insert(lu);
            else st.erase(lu);
            if (st.count(ld) == 0) st.insert(ld);
            else st.erase(ld);
            if (st.count(ru) == 0) st.insert(ru);
            else st.erase(ru);
            if (st.count(rd) == 0) st.insert(rd);
            else st.erase(rd);
        }
        
        return sum == (maxx - minx)*(maxy - miny) && st.size() == 4 
            && st.count(getHash(minx, miny)) == 1
            && st.count(getHash(minx, maxy)) == 1
            && st.count(getHash(maxx, miny)) == 1
            && st.count(getHash(maxx, maxy)) == 1;
    }

吐血!

 

转载于:https://www.cnblogs.com/shit/p/9726245.html

private List<Mask> DrawSegmentationOnYoloCanvas(SKBitmap bitmap, List<Segmentation> results) { List<Mask> maskList = new List<Mask>(); // 生成新的SKImage var newImage = SKImage.FromBitmap(bitmap); // 创建一个新的 Bitmap 来绘制所有轮廓 using (var surface = SKSurface.Create(new SKImageInfo(bitmap.Width, bitmap.Height))) { var canvas = surface.Canvas; // 绘制 newImage 到画布 canvas.DrawImage(newImage, new SKRect(0, 0, bitmap.Width, bitmap.Height)); // 设置画笔 using (var paint = new SKPaint()) { paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = 1; // 遍历所有结果并绘制轮廓 foreach (var result in results) { Pixel[] pixels = result.SegmentedPixels; // 创建一个与原图相同大小的透明图层 Mat overlay = new Mat(new OpenCvSharp.Size(bitmap.Width, bitmap.Height), MatType.CV_8UC4, Scalar.All(0)); // 在透明图层上绘制掩码 foreach (var pixel in pixels) { paint.Color = new SKColor(1, 1, 1, 66); // 绘制每个像素的轮廓到 SkiaSharp 的 canvas canvas.DrawPoint(pixel.X, pixel.Y, paint); overlay.Circle(new OpenCvSharp.Point(pixel.X, pixel.Y), 1, new Scalar(0, 0, 255, 127), -1); } // 用梯度算法计算梯度 Mat laplacian = overlay.CvtColor(ColorConversionCodes.BGRA2GRAY).Laplacian(overlay.Type(), 1, 5).ConvertScaleAbs(); // 从梯度图查找轮廓 var contours = laplacian.FindContoursAsArray(RetrievalModes.External, ContourApproximationModes.ApproxSimple); laplacian.Dispose(); // 最小外接矩形的宽度和高度和最大内接圆的半径 float width = 0; float height = 0; float rectWidth = 0; float rectHeight = 0; float radius = 0; RotatedRect minRect = new RotatedRect(); SKPoint centerMask = new SKPoint(0, 0); // 画最大内接圆和最小外接矩形 if (contours.Length > 0) { // 计算最小外接矩形 minRect = Cv2.MinAreaRect(contours[0]); // 绘制外接矩形到 SkiaSharp 的 canvas Point2f[] rectPoints = minRect.Points(); foreach (var point in rectPoints) { paint.Color = SKColors.Red; // 绘制矩形的边界 canvas.DrawLine(point.X, point.Y, rectPoints[(Array.IndexOf(rectPoints, point) + 1) % 4].X, rectPoints[(Array.IndexOf(rectPoints, point) + 1) % 4].Y, paint); } // 获取矩形的宽度和高度 width = minRect.Size.Width; height = minRect.Size.Height; // 如果宽度大于高度,交换值以确保 width 是较小的边,height 是较大的边 if (width > height) { float temp = width; width = height; height = temp; } if (null != skglControlColor.Tag) { // 从画布 Tag 属性中取出深度相机数据帧 var skgData = skglControlColor.Tag as SkgData; if (skgData != null) { // 获取深度数据值 var point0 = pxToImgMatrix.MapPoint(new SKPoint(rectPoints[0].X, rectPoints[0].Y)); int depthValue0 = GetDepthValueAtXY(skgData, (int)point0.X, (int)point0.Y); var point1 = pxToImgMatrix.MapPoint(new SKPoint(rectPoints[1].X, rectPoints[1].Y)); int depthValue1 = GetDepthValueAtXY(skgData, (int)point1.X, (int)point1.Y); var point2 = pxToImgMatrix.MapPoint(new SKPoint(rectPoints[2].X, rectPoints[2].Y)); int depthValue2 = GetDepthValueAtXY(skgData, (int)point2.X, (int)point2.Y); double widthD = CalculateDistance(point0, depthValue0, point1, depthValue1); double heightD = CalculateDistance(point1, depthValue1, point2, depthValue2); // 判断小的值为w,大的值为h if (widthD < heightD) { rectWidth = (float)widthD; rectHeight = (float)heightD; } else { rectWidth = (float)heightD; rectHeight = (float)widthD; } } else { LogError("深度数据为空,无法计算掩码的长宽。"); } } // 计算轮廓的质心(几何中心) Moments moments = Cv2.Moments(contours[0]); OpenCvSharp.Point2f center = new OpenCvSharp.Point2f( (float)(moments.M10 / moments.M00), (float)(moments.M01 / moments.M00) ); // 计算最大内接圆的半径 float minDistance = float.MaxValue; foreach (var point in contours[0]) { // 确保 point 是 Point2f 类型 OpenCvSharp.Point2f contourPoint = new OpenCvSharp.Point2f(point.X, point.Y); // 手动计算点到质心的距离 float distance = (float)Math.Sqrt(Math.Pow(contourPoint.X - center.X, 2) + Math.Pow(contourPoint.Y - center.Y, 2)); if (distance < minDistance) { minDistance = distance; } } // 限制最大内接圆的半径,确保圆完全位于最小外接矩形内 float maxRadius = width / 2; minDistance = Math.Min(minDistance, maxRadius); // 绘制最大内接圆到 SkiaSharp 的 canvas paint.Color = SKColors.LightBlue; canvas.DrawCircle(center.X, center.Y, minDistance, paint); if (null != skglControlColor.Tag) { // 从画布 Tag 属性中取出深度相机数据帧 var skgData = skglControlColor.Tag as SkgData; if (skgData != null) { // 和外接矩形同理,计算圆的半径 var pointCenter = pxToImgMatrix.MapPoint(new SKPoint(center.X, center.Y)); int depthValueCenter = GetDepthValueAtXY(skgData, (int)pointCenter.X, (int)pointCenter.Y); // 算一个圆心正上方的一个圆上坐标 var pointC = pxToImgMatrix.MapPoint(new SKPoint(center.X, center.Y + minDistance)); int depthValueC = GetDepthValueAtXY(skgData, (int)pointC.X, (int)pointC.Y); // 计算圆的半径 radius = (float)CalculateDistance(pointCenter, depthValueCenter, pointC, depthValueC); } else { LogError("深度数据为空,无法计算掩码的圆半径。"); } } // 在圆心处画上 分数result.Confidence paint.Color = SKColors.Blue; canvas.DrawText($"{result.Confidence * 100 :F0}", center.X + 5, center.Y - 5, paint); centerMask = new SKPoint(center.X, center.Y); } // 画掩码轮廓 if (contours.Length > 0) { // 绘制轮廓边缘 foreach (var contour in contours) { // 创建一个完整的封闭轮廓 OpenCvSharp.Point[] contourAll = new OpenCvSharp.Point[contour.Length + 1]; // 遍历轮廓点并添加到 contourAll for (int i = 0; i < contour.Length; i++) { contourAll[i] = new OpenCvSharp.Point(contour[i].X, contour[i].Y); } // 闭合轮廓,添加最后一个点到第一个点的连接 contourAll[contour.Length] = new OpenCvSharp.Point(contour[0].X, contour[0].Y); // 绘制封闭轮廓 paint.Color = SKColors.LightGray; for (int i = 0; i < contourAll.Length - 1; i++) { canvas.DrawLine(new SKPoint(contourAll[i].X, contourAll[i].Y), new SKPoint(contourAll[i + 1].X, contourAll[i + 1].Y), paint); } RotatedRect maxInscribedRect = GetMaxInscribedRectangle(contourAll); //绘制最大内接矩形 Point2f[] maxRectPoints = maxInscribedRect.Points(); for (int j = 0; j < 4; j++) { paint.Color = SKColors.Yellow; canvas.DrawLine( maxRectPoints[j].X, maxRectPoints[j].Y, maxRectPoints[(j + 1) % 4].X, maxRectPoints[(j + 1) % 4].Y, paint ); } } } // 获取圆心,判断圆半径是否满足14mm以上的要求 吸嘴的圆半径是14mm if (radius > fixedTempRadius) { // 创建一个新的Mask对象 var mask = new Mask { Name = result.Label.Name, Iou = result.Confidence, Cx = centerMask.X, Cy = centerMask.Y, X = 0, Y = 0, Area = 0, Deep = 0, // 深度信息可以根据需要设置 MaskImage = null, // 可以根据需要生成对应的Mask图像 BoxArea = 0, RectCenterX = 0, RectCenterY = 0, RectWidth = rectWidth, RectHeight = rectHeight, RectAngle = 0, RectPoints = null }; var (widthRatio, heightRatio) = GetMaskAspectRatios(mask, fixedTemplateWidth, fixedTemplateHeight); // 判断尺寸范围是否在预设范围内 if (widthRatio >= ratioMin && widthRatio <= ratioMax && heightRatio >= ratioMin && heightRatio <= ratioMax) { maskList.Add(mask); } else { // 在圆心画一个“x” paint.Color = SKColors.Red; canvas.DrawLine(centerMask.X - 5, centerMask.Y - 5, centerMask.X + 5, centerMask.Y + 5, paint); canvas.DrawLine(centerMask.X - 5, centerMask.Y + 5, centerMask.X + 5, centerMask.Y - 5, paint); } } else { // 在圆心画一个“x” paint.Color = SKColors.Red; canvas.DrawLine(centerMask.X - 5, centerMask.Y - 5, centerMask.X + 5, centerMask.Y + 5, paint); canvas.DrawLine(centerMask.X - 5, centerMask.Y + 5, centerMask.X + 5, centerMask.Y - 5, paint); } } } // 获取绘制后的图像作为 SKImage var outlinedImage = surface.Snapshot(); newImage = outlinedImage; } // 绘制检测框和置信度 var resultImage = newImage.Draw(results); // 生成一个完整的画面,置入到skglControlYolo的Tag中,调用Invalidate()方法来刷新画面 skglControlYolo.Tag = newImage; skglControlYolo.Invalidate(); return maskList; } // 使用中心扩散法获取最大内接矩形 public RotatedRect GetMaxInscribedRectangle(OpenCvSharp.Point[] contour) { // 获取轮廓的最小外接矩形 RotatedRect minRect = Cv2.MinAreaRect(contour); // 初始化最大内接矩形 RotatedRect maxInscribedRect = minRect; // 获取最小外接矩形的中心点、角度和尺寸 Point2f center = minRect.Center; float angle = minRect.Angle; Size2f size = minRect.Size; // 确保最长边是 height,最短边是 width float width = Math.Min(size.Width, size.Height); float height = Math.Max(size.Width, size.Height); // 检查宽边(最短边)的两个角点是否都在轮廓内 Point2f leftEdge = RotatePoint(new Point2f(center.X - width / 2, center.Y), center, angle); Point2f rightEdge = RotatePoint(new Point2f(center.X + width / 2, center.Y), center, angle); bool isWidthFixed = Cv2.PointPolygonTest(contour, leftEdge, false) >= 0 && Cv2.PointPolygonTest(contour, rightEdge, false) >= 0; // 检查长边(最长边)的两个角点是否都在轮廓内 Point2f topEdge = RotatePoint(new Point2f(center.X, center.Y - height / 2), center, angle); Point2f bottomEdge = RotatePoint(new Point2f(center.X, center.Y + height / 2), center, angle); bool isHeightFixed = Cv2.PointPolygonTest(contour, topEdge, false) >= 0 && Cv2.PointPolygonTest(contour, bottomEdge, false) >= 0; // 如果宽边不固定,调整宽度 if (!isWidthFixed) { while (true) { leftEdge = RotatePoint(new Point2f(center.X - width / 2, center.Y), center, angle); rightEdge = RotatePoint(new Point2f(center.X + width / 2, center.Y), center, angle); if (Cv2.PointPolygonTest(contour, leftEdge, false) >= 0 && Cv2.PointPolygonTest(contour, rightEdge, false) >= 0) { break; // 宽边已经在轮廓内 } // 如果不在轮廓内,缩小宽度 width -= 0.5f; if (width <= 0) break; // 防止宽度变为负数 } } // 如果长边不固定,调整高度 if (!isHeightFixed) { while (true) { topEdge = RotatePoint(new Point2f(center.X, center.Y - height / 2), center, angle); bottomEdge = RotatePoint(new Point2f(center.X, center.Y + height / 2), center, angle); if (Cv2.PointPolygonTest(contour, topEdge, false) >= 0 && Cv2.PointPolygonTest(contour, bottomEdge, false) >= 0) { break; // 长边已经在轮廓内 } // 如果不在轮廓内,缩小高度 height -= 0.5f; if (height <= 0) break; // 防止高度变为负数 } } // 返回最大内接矩形 return new RotatedRect(center, new Size2f(width, height), angle); } 在我的代码中,我绘制了一个最小外接矩形,然后基于给出的点绘制出轮廓, paint.Color = SKColors.LightGray; for (int i = 0; i < contourAll.Length - 1; i++) { canvas.DrawLine(new SKPoint(contourAll[i].X, contourAll[i].Y), new SKPoint(contourAll[i + 1].X, contourAll[i + 1].Y), paint); } 这个是我轮廓的代码。我想要获得这个轮廓的最大内接矩形。 RotatedRect maxInscribedRect = GetMaxInscribedRectangle(contourAll); //绘制最大内接矩形 Point2f[] maxRectPoints = maxInscribedRect.Points(); for (int j = 0; j < 4; j++) { paint.Color = SKColors.Yellow; canvas.DrawLine( maxRectPoints[j].X, maxRectPoints[j].Y, maxRectPoints[(j + 1) % 4].X, maxRectPoints[(j + 1) % 4].Y, paint ); }。但是我的代码中我绘制了这个最大内接矩形,效果不是很好。请你帮我查阅资料帮我修改,我看到网上很多使用python已经实现了,你帮我转成C#写入我的代码,像什么凸包什么的,或者你遍历每一个角度都可以。要保证是轮廓的最大内接矩形,然后就是分割出来的图形不论是什么角度都可以获得正确的最大内接矩形
最新发布
07-19
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值