如何使用OpenCV绘制带有圆角的矩形?

14
如何在OpenCV中绘制带有圆角的矩形?我知道可以简单地将ellipse()和line()函数组合在一起以绘制它。我想知道是否有人之前已经做过并将其放入适当的函数中,以便我可以使用它?理想情况下,角半径应根据参数进行校准。
我为此搜索了很多,但似乎之前没有人遇到过这个问题。如果没有人有这样的函数,我可能会在几天后在这里发布自己的解决方案。
4个回答

16

我意识到,这比我想象中的要容易得多。这是我的函数,希望对某些人有所帮助。

/**
 * Draws a rectangle with rounded corners, the parameters are the same as in the OpenCV function @see rectangle();
 * @param cornerRadius A positive int value defining the radius of the round corners.
 * @author K
 */
void rounded_rectangle( Mat& src, Point topLeft, Point bottomRight, const Scalar lineColor, const int thickness, const int lineType , const int cornerRadius)
{
    /* corners:
     * p1 - p2
     * |     |
     * p4 - p3
     */
    Point p1 = topLeft;
    Point p2 = Point (bottomRight.x, topLeft.y);
    Point p3 = bottomRight;
    Point p4 = Point (topLeft.x, bottomRight.y);
    
    // draw straight lines
    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);
    
    // draw arcs
    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 );
}

最好能够自动计算 cornerRadius,因为当您绘制一个小尺寸的矩形时,它看起来就像一个圆形,所以可以按如下方式计算 cornerRadius = (rect.width+rect.height)*0.1 - Bahramdun Adil

11

这里是Python版本,具有填充/未填充功能,并根据图像高度自动计算圆角半径。

import cv2
import numpy as np

def rounded_rectangle(src, top_left, bottom_right, radius=1, color=255, thickness=1, line_type=cv2.LINE_AA):

    #  corners:
    #  p1 - p2
    #  |     |
    #  p4 - p3

    p1 = top_left
    p2 = (bottom_right[1], top_left[1])
    p3 = (bottom_right[1], bottom_right[0])
    p4 = (top_left[0], bottom_right[0])

    height = abs(bottom_right[0] - top_left[1])

    if radius > 1:
        radius = 1

    corner_radius = int(radius * (height/2))

    if thickness < 0:

        #big rect
        top_left_main_rect = (int(p1[0] + corner_radius), int(p1[1]))
        bottom_right_main_rect = (int(p3[0] - corner_radius), int(p3[1]))

        top_left_rect_left = (p1[0], p1[1] + corner_radius)
        bottom_right_rect_left = (p4[0] + corner_radius, p4[1] - corner_radius)

        top_left_rect_right = (p2[0] - corner_radius, p2[1] + corner_radius)
        bottom_right_rect_right = (p3[0], p3[1] - corner_radius)

        all_rects = [
        [top_left_main_rect, bottom_right_main_rect], 
        [top_left_rect_left, bottom_right_rect_left], 
        [top_left_rect_right, bottom_right_rect_right]]

        [cv2.rectangle(src, rect[0], rect[1], color, thickness) for rect in all_rects]

    # draw straight lines
    cv2.line(src, (p1[0] + corner_radius, p1[1]), (p2[0] - corner_radius, p2[1]), color, abs(thickness), line_type)
    cv2.line(src, (p2[0], p2[1] + corner_radius), (p3[0], p3[1] - corner_radius), color, abs(thickness), line_type)
    cv2.line(src, (p3[0] - corner_radius, p4[1]), (p4[0] + corner_radius, p3[1]), color, abs(thickness), line_type)
    cv2.line(src, (p4[0], p4[1] - corner_radius), (p1[0], p1[1] + corner_radius), color, abs(thickness), line_type)

    # draw arcs
    cv2.ellipse(src, (p1[0] + corner_radius, p1[1] + corner_radius), (corner_radius, corner_radius), 180.0, 0, 90, color ,thickness, line_type)
    cv2.ellipse(src, (p2[0] - corner_radius, p2[1] + corner_radius), (corner_radius, corner_radius), 270.0, 0, 90, color , thickness, line_type)
    cv2.ellipse(src, (p3[0] - corner_radius, p3[1] - corner_radius), (corner_radius, corner_radius), 0.0, 0, 90,   color , thickness, line_type)
    cv2.ellipse(src, (p4[0] + corner_radius, p4[1] - corner_radius), (corner_radius, corner_radius), 90.0, 0, 90,  color , thickness, line_type)

    return src

使用方法:

top_left = (0, 0)
bottom_right = (500, 800)
color = (255, 255, 255)
image_size = (500, 800, 3)
img = np.zeros(image_size)
img = rounded_rectangle(img, top_left, bottom_right, color=color, radius=0.5, thickness=-1)

cv2.imshow('rounded_rect', img)
cv2.waitKey(0)

圆角矩形填充

圆角矩形未填充


我不知道这个问题怎么会被忽视了三年,但是你的x/y坐标并不清晰。你需要给这个函数一个 top_left=(x1, y1), bottom_right=(y2, x2)。因为你使用了 top_left=(0/0),所以这个问题没有被注意到!至少在完整的矩形中。 - M.Winkens

4
这是一个Python实现(如果有人需要的话):它在图像周围绘制一个圆角(随机半径和线条厚度 - 如果需要,请更改)的边框:
def addRoundedRectangleBorder(img):
    height, width, channels = img.shape

    border_radius = int(width * random.randint(1, 10)/100.0)
    line_thickness = int(max(width, height) * random.randint(1, 3)/100.0)
    edge_shift = int(line_thickness/2.0)

    red = random.randint(230,255)
    green = random.randint(230,255)
    blue = random.randint(230,255)
    color = (blue, green, red)

    #draw lines
    #top
    cv2.line(img, (border_radius, edge_shift), 
    (width - border_radius, edge_shift), (blue, green, red), line_thickness)
    #bottom
    cv2.line(img, (border_radius, height-line_thickness), 
    (width - border_radius, height-line_thickness), (blue, green, red), line_thickness)
    #left
    cv2.line(img, (edge_shift, border_radius), 
    (edge_shift, height  - border_radius), (blue, green, red), line_thickness)
    #right
    cv2.line(img, (width - line_thickness, border_radius), 
    (width - line_thickness, height  - border_radius), (blue, green, red), line_thickness)

    #corners
    cv2.ellipse(img, (border_radius+ edge_shift, border_radius+edge_shift), 
    (border_radius, border_radius), 180, 0, 90, color, line_thickness)
    cv2.ellipse(img, (width-(border_radius+line_thickness), border_radius), 
    (border_radius, border_radius), 270, 0, 90, color, line_thickness)
    cv2.ellipse(img, (width-(border_radius+line_thickness), height-(border_radius + line_thickness)), 
    (border_radius, border_radius), 10, 0, 90, color, line_thickness)
    cv2.ellipse(img, (border_radius+edge_shift, height-(border_radius + line_thickness)), 
    (border_radius, border_radius), 90, 0, 90, color, line_thickness)

    return img

0
根据 K. 的代码,使该函数能够绘制填充的圆角矩形,如果传递 -1 作为厚度。
Python 版本。
def DrawRoundedRectangle(img, topLeft, bottomRight, radius=1, color=255, thickness=1, line_type=cv.LINE_AA):

min_half = int(min((bottomRight[0] - topLeft[0]), (bottomRight[1] - topLeft[1])) * 0.5)
radius = min(radius, min_half)

# /* corners:
#  * p1 - p2
#  * |     |
#  * p4 - p3
#  */
p1 = topLeft
p2 = (bottomRight[0], topLeft[1])
p3 = bottomRight
p4 = (topLeft[0], bottomRight[1])

if(thickness < 0):
    # // draw rectangle
    cv.rectangle(img, (p1[0] + radius, p1[1]),  (p3[0] - radius, p3[1]), color, thickness, line_type)
    cv.rectangle(img, (p1[0], p1[1] + radius),  (p3[0], p3[1] - radius), color, thickness, line_type)
else:
    # // draw straight lines
    cv.line(img, (p1[0] + radius, p1[1]),  (p2[0] - radius, p2[1]), color, thickness, line_type);
    cv.line(img, (p2[0], p2[1] + radius),  (p3[0], p3[1] - radius), color, thickness, line_type);
    cv.line(img, (p4[0] + radius, p4[1]),  (p3[0]-radius, p3[1]), color, thickness, line_type);
    cv.line(img, (p1[0], p1[1] + radius),  (p4[0], p4[1] - radius), color, thickness, line_type);

# // draw arcs
if(radius > 0):
    cv.ellipse( img, (p1[0] + radius, p1[1] + radius), ( radius, radius ), 180.0, 0, 90, color, thickness, line_type );
    cv.ellipse( img, (p2[0] - radius, p2[1] + radius), ( radius, radius ), 270.0, 0, 90, color, thickness, line_type );
    cv.ellipse( img, (p3[0] - radius, p3[1] - radius), ( radius, radius ), 0.0, 0, 90, color, thickness, line_type );
    cv.ellipse( img, (p4[0] + radius, p4[1] - radius), ( radius, radius ), 90.0, 0, 90, color, thickness, line_type );

JavaScript版本

function DrawRoundedRectangle(img, topLeft, bottomRight, radius=1, color=255, thickness=1, line_type=cv.LINE_AA){

let min_half = Math.floor(Math.min((bottomRight.x - topLeft.x), (bottomRight.y - topLeft.y)) * 0.5)
radius = Math.min(radius, min_half)

/* corners:
#  * p1 - p2
#  * |     |
#  * p4 - p3
#  */
let p1 = topLeft
let p2 = new cv.Point(bottomRight.x, topLeft.y)
let p3 = bottomRight
let p4 = new cv.Point(topLeft.x, bottomRight.y)

if(thickness < 0){
    // draw rectangle
    cv.rectangle(img, new cv.Point(p1.x + radius, p1.y),  new cv.Point(p3.x - radius, p3.y), color, thickness, line_type)
    cv.rectangle(img, new cv.Point(p1.x, p1.y + radius),  new cv.Point(p3.x, p3.y - radius), color, thickness, line_type)
}
else{
    // draw straight lines
    cv.line(img, new cv.Point(p1.x + radius, p1.y),  new cv.Point(p2.x - radius, p2.y), color, thickness, line_type);
    cv.line(img, new cv.Point(p2.x, p2.y + radius),  new cv.Point(p3.x, p3.y - radius), color, thickness, line_type);
    cv.line(img, new cv.Point(p4.x + radius, p4.y),  new cv.Point(p3.x-radius, p3.y), color, thickness, line_type);
    cv.line(img, new cv.Point(p1.x, p1.y + radius),  new cv.Point(p4.x, p4.y - radius), color, thickness, line_type);
}
// draw arcs
if(radius > 0){
    cv.ellipse( img, new cv.Point(p1.x + radius, p1.y + radius), new cv.Size( radius, radius ), 180.0, 0, 90, color, thickness, line_type );
    cv.ellipse( img, new cv.Point(p2.x - radius, p2.y + radius), new cv.Size( radius, radius ), 270.0, 0, 90, color, thickness, line_type );
    cv.ellipse( img, new cv.Point(p3.x - radius, p3.y - radius), new cv.Size( radius, radius ), 0.0, 0, 90, color, thickness, line_type );
    cv.ellipse( img, new cv.Point(p4.x + radius, p4.y - radius), new cv.Size( radius, radius ), 90.0, 0, 90, color, thickness, line_type );
}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接