Python 如何在图像周围添加圆形边框?

Python 如何在图像周围添加圆形边框?,python,image-processing,python-imaging-library,Python,Image Processing,Python Imaging Library,我有一个矩形图像,我想把它的角弄圆,然后给它添加一个黑色边框(所以边框也是圆的) 有没有一个简单的方法来实现它 这就是期望的输出: 当然,Mark将使用ImageMagick提供一个奇特的解决方案。但是,由于您的问题带有Pillow标签,其他人可能也在寻找解决方案,因此我的手动实现如下,因为我怀疑是否有现成的内置方法: 从matplotlib将pyplot导入为plt#仅用于可视化 从PIL导入图像,ImageDraw 带圆角的def rect_(图像、r、t、c): """ :参数图像:PI

我有一个矩形图像,我想把它的角弄圆,然后给它添加一个黑色边框(所以边框也是圆的)

有没有一个简单的方法来实现它

这就是期望的输出:


当然,Mark将使用ImageMagick提供一个奇特的解决方案。但是,由于您的问题带有Pillow标签,其他人可能也在寻找解决方案,因此我的手动实现如下,因为我怀疑是否有现成的内置方法:

从matplotlib将pyplot导入为plt#仅用于可视化
从PIL导入图像,ImageDraw
带圆角的def rect_(图像、r、t、c):
"""
:参数图像:PIL图像,假设:单色填充矩形
:参数r:圆角半径
:参数t:边框的厚度
:参数c:边框颜色
:return:圆角矩形的新PIL图像
"""
#这里需要一些提取矩形主颜色的方法。。。
mc=img.getpixel((image.width/2,image.height/2))
#创建新图像
new_image=image.new(image.mode,(image.width+2*t,image.height+2*t),(255,255))
draw=ImageDraw.draw(新图像)
#画四个圆角
画弧([(0,0),(2*r-1,2*r-1)],180,270,c,t)
绘制弧([(图像宽度-2*r+2*t,0),(图像宽度+2*t,2*r-1)],270,0,c,t)
绘制弧([(图像宽度-2*r+2*t,图像高度-2*r+2*t),(图像宽度+2*t,图像高度+2*t)],0,90,c,t)
绘制弧([(0,图像高度-2*r+2*t),(2*r-1,图像高度+2*t)],90,180,c,t)
#画四边
画线([(r-1,t/2-1),(图像宽度-r+2*t,t/2-1)],c,t)
画线([(t/2-1,r-1),(t/2-1,图像高度-r+2*t)],c,t)
画线([(图像宽度+1.5*t,r-1),(图像宽度+1.5*t,图像高度-r+2*t)],c,t)
画线([(r-1,图像高度+1.5*t),(图像宽度-r+2*t,图像高度+1.5*t)],c,t)
#用主色填充矩形
ImageDraw.floodfill(新图像,(图像.宽度/2+t,图像.高度/2+t),mc)
返回新的图像
img=Image.new('RGB',(640480),(255128255))
plt.图(1)
plt.imshow(img)
new_img=带圆角的矩形(img,100,20,(0,0,0))
plt.图(2)
plt.imshow(新img)
plt.show()
基本上,它是计算并手动绘制四条圆弧、四条具有所需边界厚度和颜色的边,然后用初始矩形的颜色填充矩形。将其放在某种方法中,并根据需要重新使用,这样主代码中就不会出现混乱

对于指定的图像和参数集,我们得到该输出(Matplotlib图):

对于另一个图像和参数集

img=Image.new('RGB',(400300),(0,64255))
plt.图(1)
plt.imshow(img)
new_img=带圆角的矩形(img,25,10,(255,0,0))
plt.图(2)
plt.imshow(新img)
例如,我们得到:

希望有帮助

----------------------------------------
系统信息
----------------------------------------
平台:Windows-10-10.0.16299-SP0
Python:3.8.1
Matplotlib:3.2.0rc3
枕头:7.0.0
----------------------------------------

在我的第一个答案的评论中与Mark进行了一些讨论后,我决定使用OpenCV和NumPy制作另一个解决方案,它能够轻松地将一些真实图像(例如照片)输入到方法中,并获得包括圆角边框和边框外透明度的图像

导入cv2
将numpy作为np导入
带圆角的def rect_(图像、r、t、c):
"""
:param image:image作为NumPy数组
:参数r:圆角半径
:参数t:边框的厚度
:参数c:边框颜色
:return:新图像为圆角NumPy数组
"""
c+=(255,)
h、 w=图像.形状[:2]
#创建新图像(此处硬编码的三通道…)
新图像=np.ones((h+2*t,w+2*t,4),np.uint8)*255
新_图像[:,:,3]=0
#画四个圆角
新图像=cv2.椭圆(新图像,(int(r+t/2),int(r+t/2)),(r,r),180,0,90,c,t)
新图像=cv2.椭圆(新图像,(int(w-r+3*t/2-1),int(r+t/2)),(r,r),270,0,90,c,t)
新图像=cv2.椭圆(新图像,(int(r+t/2),int(h-r+3*t/2-1)),(r,r),90,0,90,c,t)
新图像=cv2.椭圆(新图像,(int(w-r+3*t/2-1),int(h-r+3*t/2-1)),(r,r),0,0,90,c,t)
#画四边
新图像=cv2。直线(新图像,(int(r+t/2),int(t/2)),(int(w-r+3*t/2-1),int(t/2)),c,t)
新图像=cv2。直线(新图像,(int(t/2),int(r+t/2)),(int(t/2),int(h-r+3*t/2)),c,t)
新图像=cv2.直线(新图像,(int(r+t/2),int(h+3*t/2)),(int(w-r+3*t/2-1),int(h+3*t/2)),c,t)
新图像=cv2.直线(新图像,(int(w+3*t/2),int(r+t/2)),(int(w+3*t/2),int(h-r+3*t/2)),c,t)
#生成适当混合的遮罩
掩码=新建图像[:,:,3]。复制()
掩模=cv2。泛光填充(掩模,无,(int(w/2+t),int(h/2+t)),128)[1]
掩码[掩码!=128]=0
掩码[掩码==128]=1
掩码=np.堆栈((掩码,掩码,掩码),轴=2)
#混合图像
temp=np.zero_like(新图像[:,:,:3])
temp[(t-1):(h+t-1),(t-1):(w+t-1)]=image.copy()
新图片[:,:,:3]=新图片[:,:,:3]*(1-掩码)+临时*掩码
#在新图像中设置适当的alpha通道
temp=new_图像[:,:,3]。复制()
新图像[:,:,3]=cv2.泛洪填充(温度,无,(int(w/2+t),int(h/2+t)),255)[1]
返回新的图像
img=cv2.imread('path/to/your/image.png'))
cv2.imshow(“img”,img)
new_img=带圆角的矩形(img,50,20,(0,0,0))
cv2.imshow(“新img”,新img)
cv2.等待键(0)
cv2.destroyAllWindows()
这与我在另一个答案中使用的概念相同,在正确的透明度方面有更多的代码

一些示例性输入:

相应的输出:

另一个输入和参数集:

new\u img=rect\u带圆角(img,20,10,(0,0,128))
输出:

希望
#!/usr/bin/env python3

from PIL import ImageOps, Image
from cairosvg import svg2png
from io import BytesIO

def frame(im, thickness=5):
    # Get input image width and height, and calculate output width and height
    iw, ih = im.size
    ow, oh = iw+2*thickness, ih+2*thickness

    # Draw outer black rounded rect into memory as PNG
    outer = f'<svg width="{ow}" height="{oh}" style="background-color:none"><rect rx="20" ry="20" width="{ow}" height="{oh}" fill="black"/></svg>'
    png   = svg2png(bytestring=outer)
    outer = Image.open(BytesIO(png))

    # Draw inner white rounded rect, offset by thickness into memory as PNG
    inner = f'<svg width="{ow}" height="{oh}"><rect x="{thickness}" y="{thickness}" rx="20" ry="20" width="{iw}" height="{ih}" fill="white"/></svg>'
    png   = svg2png(bytestring=inner)
    inner = Image.open(BytesIO(png)).convert('L')

    # Expand original canvas with black to match output size
    expanded = ImageOps.expand(im, border=thickness, fill=(0,0,0)).convert('RGB')

    # Paste expanded image onto outer black border using inner white rectangle as mask
    outer.paste(expanded, None, inner)
    return outer

# Open image, frame it and save
im = Image.open('monsters.jpg')
result = frame(im, thickness=10)
result.save('result.png')
#!/bin/bash

# Get width and height of input image
read iw ih < <(identify -format "%w %h" monsters.jpg)

# Calculate size of output image, assumes thickness=10
((ow=iw+20))
((oh=ih+20))

magick -size ${ow}x${oh} xc:none  -fill black -draw "roundrectangle 0,0 $ow,$oh 20,20" \
    \( -size ${iw}x${ih} xc:black -fill white -draw "roundrectangle 0,0,$iw,$ih 20,20" monsters.jpg -compose darken -composite \) \
       -gravity center -compose over -composite result.png
import cv2
import numpy as np

# set thickness, rounding and color of border
t = 21
r = 21
c = (0,0,255)

# read image
img = cv2.imread("bear.png")
hh, ww = img.shape[0:2]

# create white image of size of input
white = np.full_like(img, (255,255,255))

# add black border of thickness
border = cv2.copyMakeBorder(white, t, t, t, t, borderType=cv2.BORDER_CONSTANT, value=(0,0,0))

# blur image by rounding amount as sigma
blur = cv2.GaussianBlur(border, (0,0), r, r)

# threshold blurred image
thresh1 = cv2.threshold(blur, 128, 255, cv2.THRESH_BINARY)[1]

# create thesh2 by eroding thresh1 by 2*t
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2*t,2*t))
thresh2 = cv2.morphologyEx(thresh1, cv2.MORPH_ERODE, kernel, iterations=1)

# subtract the two thresholded images to make a border mask
mask = thresh1 - thresh2

# shave border mask by t
mask = mask[t:hh+t,t:ww+t]

# create colored image the same size as input
color = np.full_like(img, c)

# combine input and color with mask
result = cv2.bitwise_and(color, mask) + cv2.bitwise_and(img, 255-mask)

# add thresh1 as alpha channel
thresh1 = thresh1[t:hh+t,t:ww+t][:,:,0]
result = np.dstack([result,thresh1])

# write 
cv2.imwrite("bear_thresh1.png", thresh1)
cv2.imwrite("bear_thresh2.png", thresh2)
cv2.imwrite("bear_mask.png", mask)
cv2.imwrite("bear_red_border.png", result)

# display it
cv2.imshow("IMAGE", img)
cv2.imshow("BORDER", border)
cv2.imshow("BLUR", blur)
cv2.imshow("THRESHOLD1", thresh1)
cv2.imshow("THRESHOLD2", thresh2)
cv2.imshow("MASK", mask)
cv2.imshow("RESULT", result)
cv2.waitKey(0)