如何在不包含(或少量)背景像素的情况下调整对象内的矩形或调整其大小?

Posted

技术标签:

【中文标题】如何在不包含(或少量)背景像素的情况下调整对象内的矩形或调整其大小?【英文标题】:How to adapt or resize a rectangle inside an object without including (or with a few numbers) of background pixels? 【发布时间】:2015-09-19 23:39:03 【问题描述】:

在应用阈值处理并找到对象的轮廓后,我使用以下代码获取对象周围的直矩形(或输入其指令的旋转矩形):

img = cv2.imread('image.png')
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,cv2.THRESH_BINARY)
# find contours 
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
# straight rectangle
x,y,w,h = cv2.boundingRect(cnt)
img= cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)

看图

然后我使用以下代码计算了直矩形内的对象和背景像素的数量:

# rectangle area (total number of object and background pixels inside the rectangle)
area_rect = w*h
# white or object pixels (inside the rectangle)
obj = cv2.countNonZero(imgray)
# background pixels (inside the rectangle)
bac = area_rect - obj

现在我想根据背景像素和对象像素之间的关系来调整对象的矩形,例如,让一个矩形占据对象的较大部分,而没有或只有更少的背景像素:

如何创建?

【问题讨论】:

minAreaRect 会找到最小面积的旋转矩形,即背景较少的那个。 @Miki 是的,但在我的情况下,我希望矩形只包含白色像素(我想在构成矩形的对象的大部分上调整它,没有或至少只有很少的背景像素): i.stack.imgur.com/oJAw4.png 【参考方案1】:

这个问题可以表述为找到一个非凸多边形内接的最大矩形

可以在link找到一个近似的解决方案。

这个问题也可以表述为:对于每个角度,找到矩阵中仅包含零的最大矩形,在这个 SO question 中进行了探索。

我的解决方案是基于this 的答案。这将只找到轴对齐的矩形,因此您可以轻松地将图像旋转给定角度并将此解决方案应用于每个角度。 我的解决方案是 C++,但您可以轻松地将其移植到 Python,因为我主要使用 OpenCV 函数,或者在上述答案中调整解决方案以考虑轮换。

我们在这里:

#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;


// https://***.com/a/30418912/5008845
Rect findMinRect(const Mat1b& src)

    Mat1f W(src.rows, src.cols, float(0));
    Mat1f H(src.rows, src.cols, float(0));

    Rect maxRect(0,0,0,0);
    float maxArea = 0.f;

    for (int r = 0; r < src.rows; ++r)
    
        for (int c = 0; c < src.cols; ++c)
        
            if (src(r, c) == 0)
            
                H(r, c) = 1.f + ((r>0) ? H(r-1, c) : 0);
                W(r, c) = 1.f + ((c>0) ? W(r, c-1) : 0);
            

            float minw = W(r,c);
            for (int h = 0; h < H(r, c); ++h)
            
                minw = min(minw, W(r-h, c));
                float area = (h+1) * minw;
                if (area > maxArea)
                
                    maxArea = area;
                    maxRect = Rect(Point(c - minw + 1, r - h), Point(c+1, r+1));
                
            
        
    

    return maxRect;



RotatedRect largestRectInNonConvexPoly(const Mat1b& src)

    // Create a matrix big enough to not lose points during rotation
    vector<Point> ptz;
    findNonZero(src, ptz);
    Rect bbox = boundingRect(ptz); 
    int maxdim = max(bbox.width, bbox.height);
    Mat1b work(2*maxdim, 2*maxdim, uchar(0));
    src(bbox).copyTo(work(Rect(maxdim - bbox.width/2, maxdim - bbox.height / 2, bbox.width, bbox.height)));

    // Store best data
    Rect bestRect;
    int bestAngle = 0;

    // For each angle
    for (int angle = 0; angle < 90; angle += 1)
    
        cout << angle << endl;

        // Rotate the image
        Mat R = getRotationMatrix2D(Point(maxdim,maxdim), angle, 1);
        Mat1b rotated;
        warpAffine(work, rotated, R, work.size());

        // Keep the crop with the polygon
        vector<Point> pts;
        findNonZero(rotated, pts);
        Rect box = boundingRect(pts);
        Mat1b crop = rotated(box).clone();

        // Invert colors
        crop = ~crop; 

        // Solve the problem: "Find largest rectangle containing only zeros in an binary matrix"
        // https://***.com/questions/2478447/find-largest-rectangle-containing-only-zeros-in-an-n%C3%97n-binary-matrix
        Rect r = findMinRect(crop);

        // If best, save result
        if (r.area() > bestRect.area())
        
            bestRect = r + box.tl();    // Correct the crop displacement
            bestAngle = angle;
        
    

    // Apply the inverse rotation
    Mat Rinv = getRotationMatrix2D(Point(maxdim, maxdim), -bestAngle, 1);
    vector<Point> rectPointsbestRect.tl(), Point(bestRect.x + bestRect.width, bestRect.y), bestRect.br(), Point(bestRect.x, bestRect.y + bestRect.height);
    vector<Point> rotatedRectPoints;
    transform(rectPoints, rotatedRectPoints, Rinv);

    // Apply the reverse translations
    for (int i = 0; i < rotatedRectPoints.size(); ++i)
    
        rotatedRectPoints[i] += bbox.tl() - Point(maxdim - bbox.width / 2, maxdim - bbox.height / 2);
    

    // Get the rotated rect
    RotatedRect rrect = minAreaRect(rotatedRectPoints);

    return rrect;




int main()

    Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);

    // Compute largest rect inside polygon
    RotatedRect r = largestRectInNonConvexPoly(img);

    // Show
    Mat3b res;
    cvtColor(img, res, COLOR_GRAY2BGR);

    Point2f points[4];
    r.points(points);

    for (int i = 0; i < 4; ++i)
    
        line(res, points[i], points[(i + 1) % 4], Scalar(0, 0, 255), 2);
    

    imshow("Result", res);
    waitKey();

    return 0;

结果图片是:

注意

我想指出,这段代码没有经过优化,所以它可能会表现得更好。有关近似解决方案,请参阅here,以及那里报道的论文。

这个answer 到一个相关问题让我朝着正确的方向前进。

【讨论】:

非常感谢@Miki,和我想要的差不多,没关系;我找到了一个与您类似的答案,让我很满意:***.com/questions/21410449/… 但是我对 opencv-python 比较陌生,我无法将我找到的链接中的代码从 C++ 更改为 Python。 :( 我也看到了,但如果我没记错的话,它只适用于轴对齐的矩形。在我提到的链接中,您会找到轴对齐情况的 python 代码,您只需要添加旋转部分 您使用点(或 opencv 3 中的 boxPoints)获得顶点坐标,中心、宽度和高度是旋转矩形的属性(现在不记得是函数还是公共成员)。 那行不通。适用于“矩形”,而不适用于“旋转矩形”。他们是不同的班级。您可以尝试:1)设置旋转 Rect 的宽度和高度,并检查顶点是否已更新(可能这会起作用)或 2)在形态上扩大您的 blob 并在这个更大的 blob 上找到一个矩形 @MohamedChamsaddin 其实很简单:RotatedRect r = largestRectInNonConvexPoly(img); r.size = Size(r.size.width + 10, r.size.height + 10);【参考方案2】:

现在有一个 python 库可以计算多边形内的最大可绘制矩形。

:maxrect


通过 pip 安装

pip install git+https://$GITHUB_TOKEN@github.com/planetlabs/maxrect.git

用法

from maxrect import get_intersection, get_maximal_rectangle, rect2poly

# For a given convex polygon
coordinates1 = [ [x0, y0], [x1, y1], ... [xn, yn] ]
coordinates2 = [ [x0, y0], [x1, y1], ... [xn, yn] ]

# find the intersection of the polygons
_, coordinates = get_intersection([coordinates1, coordinates2])

# get the maximally inscribed rectangle
ll, ur = get_maximal_rectangle(coordinates)

# casting the rectangle to a GeoJSON-friendly closed polygon
rect2poly(ll, ur)

来源:https://pypi.org/project/maxrect/

【讨论】:

【参考方案3】:

here 是我编写的包含旋转的python 代码。我试图加快速度,但我想它可以改进。

【讨论】:

您能否提供您所拥有的图像,但结果不正确。 我发现了一个问题。不确定它是否会解决你的问题。输入数组的维度必须是奇数。它有助于旋转在图像的中间居中。代码已更新here。 是的@paugam,这是图像:“i.stack.imgur.com/QlbyX.png”,这是 C ++ 中的结果(在@Miki 的第一个答案中建议):“i.stack.imgur.com/vHm6P.png” 谢谢。我修改了算法以处理不同的图像尺寸。在您的特定情况下,它现在给出了 -66 度的角度,而您的 C++ 示例给出了 -64 度。查看 git 存储库中的新代码。 感谢@paugam,它比以前效果更好,但是当我使用另一个图像时,它给了我同样的问题;它应该工作!这是我使用的新图像:“i.stack.imgur.com/o943j.png”,这些是 Python 和 C++ 中的结果(@Miki 建议):“i.stack.imgur.com/fBeve.png”【参考方案4】:

对于未来的谷歌员工,

由于您提供的示例解决方案允许背景像素位于矩形内,我想您希望找到可能覆盖 80% 白色像素的最小矩形。

这可以通过给定一组数据使用类似的方法找到误差椭圆来完成(在这种情况下,数据是所有白色像素,需要将误差椭圆修改为矩形)

因此以下链接会有所帮助

How to get the best fit bounding box from covariance matrix and mean position?

http://www.visiondummy.com/2014/04/draw-error-ellipse-representing-covariance-matrix/

【讨论】:

以上是关于如何在不包含(或少量)背景像素的情况下调整对象内的矩形或调整其大小?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不改变文本透明度的情况下为 UILabel 设置背景图像并调整其 alpha?

在不将单个像素值转换为浮点数的情况下调整目标分割图的大小

在不使用自动布局的情况下调整 iPhone 5 屏幕尺寸

如何在不破坏图像纵横比的情况下调整 QImage 或 QML 图像的大小以适应父容器

如何在不裁剪的情况下调整新图像对象的大小?

在没有 JS 的情况下调整溢出 div 内的表格大小