如何在openCV C++中填充圆角矩形

How to fill a rounded rectangle in openCV c++

我想出了一种使用 OpenCV C++ 绘制圆角矩形的方法。我的函数是:

void RoundedRectangle(cv::Mat& src, 
                      cv::Point topLeft, 
                      cv::Size rectSz, 
                      const cv::Scalar lineColor, 
                      const int thickness, 
                      const int lineType, 
                      const float  cornerCurvatureRatio)
{
    // corners:
    // p1 - p2
    // |     |
    // p4 - p3
    //
    cv::Point p1 = topLeft;
    cv::Point p2 = cv::Point (p1.x + rectSz.width, p1.y);
    cv::Point p3 = cv::Point (p1.x + rectSz.width, p1.y + rectSz.height);
    cv::Point p4 = cv::Point (p1.x, p1.y + rectSz.height);
    float cornerRadius = rectSz.height*cornerCurvatureRatio;

    // draw straight lines
    cv::line(src, cv::Point (p1.x + cornerRadius, p1.y), cv::Point (p2.x - cornerRadius, p2.y), lineColor, thickness, lineType);
    cv::line(src, cv::Point (p2.x, p2.y + cornerRadius), cv::Point (p3.x, p3.y - cornerRadius), lineColor, thickness, lineType);
    cv::line(src, cv::Point (p4.x + cornerRadius, p4.y), cv::Point (p3.x - cornerRadius, p3.y), lineColor, thickness, lineType);
    cv::line(src, cv::Point (p1.x, p1.y + cornerRadius), cv::Point (p4.x, p4.y - cornerRadius), lineColor, thickness, lineType);

     // draw arcs
    cv::Size rad = cv::Size(cornerRadius, cornerRadius);
    cv::ellipse(src, p1 + cv::Point(cornerRadius, cornerRadius),   rad, 180.0, 0, 90, lineColor, thickness, lineType);
    cv::ellipse(src, p2 + cv::Point(-cornerRadius, cornerRadius),  rad, 270.0, 0, 90, lineColor, thickness, lineType);
    cv::ellipse(src, p3 + cv::Point(-cornerRadius, -cornerRadius), rad, 0.0, 0, 90, lineColor, thickness, lineType);
    cv::ellipse(src, p4 + cv::Point(cornerRadius, -cornerRadius),  rad, 90.0, 0, 90, lineColor, thickness, lineType);
}

现在我要填充矩形。我发现了一些填充函数,例如 cv::fillPoly() and cv::fillConvexPoly 但是,我需要一个带点的向量。如何从我的构造中获取点列表?

要从您使用 cv::linecv::ellipse 构建的形状中获取点,您可以在黑色背景上绘制形状,然后找到该图像的轮廓。

另一种不使用 lineellipse 命令的方法是直接使用 trig 计算形状的轮廓。

import cv2, numpy as np, math

# Define the rectangle parameters
directions, ro, next_corner, radius, increment, angle, leg, corners = [(-1,0),(0,-1),(1,0),(0,1)],[(-1,-1),(1,-1),(1,1),(-1,1)],[3,0,1,2],56, 100, 0, 321, [(500,500)]

# Create list of corners
for side in range(4): corners.append((corners[side][0]+leg*directions[side][0], corners[side][1]+leg*directions[side][1]))

# Distance function
def distance(a,b): return math.sqrt((a[0]-b[0])**2+(a[1]-b[1])**2)

# Compute the contour points for each side and corner
contour_points = []
for i in range(4):
    # Do the corner
    center = corners[i][0] + radius*ro[i][0], corners[i][1] + radius*ro[i][1]
    for angle_increment in range(increment):
        contour_points.append((int(center[0] + math.cos(angle) * radius), int(center[1] + math.sin(angle) * radius)))
        angle += .5*math.pi/increment
    # Do the line
    start = corners[i][0]+radius*directions[i][0], corners[i][1] + radius*directions[i][1]
    while distance(start, (corners[i][0]+radius*directions[i][0], corners[i][1] + radius*directions[i][1])) < leg-2*radius:
        contour_points.append(start)
        start = start[0]+directions[i][0], start[1]+directions[i][1]

# Draw the contour and show the image
img = np.zeros((600,600), np.uint8)
cv2.drawContours(img, [np.array(contour_points, dtype=np.int32)], 0, 255, -1)
cv2.imshow('img',img)
cv2.waitKey(0)

cv2.destroyAllWindows()

将 cv::floodFill 与矩形内的起点一起使用。

这是我的解决方案,以防对任何人有帮助。

void FilledRoundedRectangle(cv::Mat& src,            //Image where rect is drawn
                      cv::Point topLeft,             //top left corner
                      cv::Size rectSz,               //rectangle size
                      const cv::Scalar fillColor,    //fill color
                      const int lineType,            //type of line
                      const int delta,               //angle between points on the ellipse
                      const float cornerCurvatureRatio) //curvature of the corner
{
    // corners:
    // p1 - p2
    // |     |
    // p4 - p3
    //
    cv::Point p1 = topLeft;
    cv::Point p2 = cv::Point (p1.x + rectSz.width, p1.y);
    cv::Point p3 = cv::Point (p1.x + rectSz.width, p1.y + rectSz.height);
    cv::Point p4 = cv::Point (p1.x, p1.y + rectSz.height);
    int cornerRadius = static_cast<int>(rectSz.height*cornerCurvatureRatio);
    std::vector<cv::Point> points;
    std::vector<cv::Point> pts;
    // Add arcs points

    cv::Size rad = cv::Size(cornerRadius, cornerRadius);

    // segments:
    //    s2____s3
    // s1          s4
    // |           |
    // s8          s5
    //   s7_____s6
    //
    //Add arc s1 to s2 
    cv::ellipse2Poly(p1 + cv::Point(cornerRadius, cornerRadius)  , rad, 180.0, 0, 90, delta , pts);
    points.insert(points.end(), pts.begin(), pts.end());
    pts.clear();
    //Add line s2-s3 
    points.push_back(cv::Point (p1.x + cornerRadius, p1.y)); points.push_back(cv::Point (p2.x - cornerRadius, p2.y));

    //Add arc s3 to s4 
    cv::ellipse2Poly(p2 + cv::Point(-cornerRadius, cornerRadius) , rad, 270.0, 0, 90, delta, pts);
    points.insert(points.end(), pts.begin(), pts.end());
    pts.clear();
    //Add line s4 to s5
    points.push_back(cv::Point (p2.x, p2.y + cornerRadius)); points.push_back(cv::Point (p3.x, p3.y - cornerRadius));

    //Add arc s5 to s6 
    cv::ellipse2Poly(p3 + cv::Point(-cornerRadius, -cornerRadius), rad, 0.0,   0, 90, delta, pts);
    points.insert(points.end(), pts.begin(), pts.end());
    pts.clear();
    //Add line s7 to s8
    points.push_back(cv::Point (p4.x + cornerRadius, p4.y)); points.push_back(cv::Point (p3.x - cornerRadius, p3.y));

    //Add arc s7 to s8 
    cv::ellipse2Poly(p4 + cv::Point(cornerRadius, -cornerRadius) , rad, 90.0,  0, 90, delta, pts);
    points.insert(points.end(), pts.begin(), pts.end());
    //Add line s1 to s8
    points.push_back(cv::Point (p1.x, p1.y + cornerRadius)); points.push_back(cv::Point (p4.x, p4.y - cornerRadius));    

    //fill polygon
    cv::fillConvexPoly(src, points, fillColor, lineType);
}


   int main(int argc, char** argv)
   {
      try
      {
          cv::Mat img = cv::Mat(600, 600,CV_8UC1,cv::Scalar(0));
          cv::Point topLeft(179, 179);
          cv::Size rectSz(321, 321);
          cv::Scalar fillColor(255, 255, 255);
          int delta                  = 1; //every 1 degree
          int lineType               = cv::LINE_AA;
          float cornerCurvatureRatio = 0.1;
          FilledRoundedRectangle(img,
                                 topLeft,
                                 rectSz,
                                 fillColor,
                                 lineType,
                                 delta,
                                 cornerCurvatureRatio);
          cv::imshow("", img);
          cv::waitKey(0);
         return 0;
         std::cin.get();
      } //end try
      catch ( std::exception const & ex )
      {
         std::string errMsg = ex.what();
         printf( "%s\n", errMsg.c_str() );
      }
      catch ( ... )
      {
         printf( "Error: unknown exception\n" );
      }



   }

这个答案是对@Stephen Meschke 提交的答案的概括,以防有人感兴趣

import cv2
import numpy as np

# Distance function
def distance(a,b): 
    return np.sqrt((a[0]-b[0])**2+(a[1]-b[1])**2)

def RoundedRectPoints(topLeft,rectSz,cornerCurvature):
    # Define the rectangle parameters
    directions  = [(-1,0),(0,-1),(1,0),(0,1)]
    ro          = [(-1,-1),(1,-1),(1,1),(-1,1)]
    radius      = cornerCurvature*(rectSz[0]+rectSz[1]);
    increment   = 100
    angle       = 0
    corners     = [(topLeft[0]+rectSz[0],topLeft[1]+rectSz[1])]

    # Create list of corners
    for side in range(4): 
        corners.append((corners[side][0]+rectSz[side%2]*directions[side][0], corners[side][1]+rectSz[side%2]*directions[side][1]))

    # Compute the contour points for each side and corner
    contour_points = []
    for i in range(4):
        # Do the corner
        center = corners[i][0] + radius*ro[i][0], corners[i][1] + radius*ro[i][1]
        for angle_increment in range(increment):
            contour_points.append((int(center[0] + np.cos(angle) * radius), int(center[1] + np.sin(angle) * radius)))
            angle += .5*np.pi/increment
        # Do the line
        start = corners[i][0]+radius*directions[i][0], corners[i][1] + radius*directions[i][1]
        while distance(start, (corners[i][0]+radius*directions[i][0], corners[i][1] + radius*directions[i][1])) < np.min(rectSz)-2*radius:
            contour_points.append(start)
            start = start[0]+directions[i][0], start[1]+directions[i][1]
    return contour_points

# Draw the contour and show the image
img = np.zeros((600,600), np.uint8)
topLeft = (179,179)
rectSz = (321,321)
cornerCurvature = 0.09
contour_points = RoundedRectPoints(topLeft,rectSz,cornerCurvature)
cv2.drawContours(img, [np.array(contour_points, dtype=np.int32)], 0, 255, -1)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Nuzhny 是正确的,floodFill() 是填充该圆角矩形的最简单且可能最快的方法。 (使用 CV 4.1.1 测试)

一行代码即可完成,但这里是整个示例。

void rounded_rectangle( Mat& src, Point topLeft, Point bottomRight, const Scalar lineColor, int thickness, const int lineType , const int cornerRadius)
{
    Point p1 = topLeft;
    Point p2 = Point (bottomRight.x, topLeft.y);
    Point p3 = bottomRight;
    Point p4 = Point (topLeft.x, bottomRight.y);


    line(src, Point (p1.x+cornerRadius,p1.y), Point (p2.x-cornerRadius,p2.y), lineColor, thickness, lineType);
    line(src, Point (p2.x,p2.y+cornerRadius), Point (p3.x,p3.y-cornerRadius), lineColor, thickness, lineType);
    line(src, Point (p4.x+cornerRadius,p4.y), Point (p3.x-cornerRadius,p3.y), lineColor, thickness, lineType);
    line(src, Point (p1.x,p1.y+cornerRadius), Point (p4.x,p4.y-cornerRadius), lineColor, thickness, lineType);

    ellipse( src, p1+Point(cornerRadius, cornerRadius), Size( cornerRadius, cornerRadius ), 180.0, 0, 90, lineColor, thickness, lineType );
    ellipse( src, p2+Point(-cornerRadius, cornerRadius), Size( cornerRadius, cornerRadius ), 270.0, 0, 90, lineColor, thickness, lineType );
    ellipse( src, p3+Point(-cornerRadius, -cornerRadius), Size( cornerRadius, cornerRadius ), 0.0, 0, 90, lineColor, thickness, lineType );
    ellipse( src, p4+Point(cornerRadius, -cornerRadius), Size( cornerRadius, cornerRadius ), 90.0, 0, 90, lineColor, thickness, lineType );

    // choose arbitrary starting point for fill => Top left plus 10,10
    Point fillFrom(topLeft.x+10, topLeft.y+10);
    Scalar fillColor(199, 120, 0);
    // You may want to use `lineColor` instead of `fillColor`
    floodFill(src, fillFrom, fillColor);
}