Gil*_*tin 4 python image-manipulation border python-imaging-library
我正在尝试使用 Pillow (python-imaging-library) Python 库在我的 .png 图像周围创建轮廓/描边/边框(选择任何颜色和宽度)。您可以在这里看到原始图像和我想要的结果(由手机应用程序创建):

您可以在这里下载原始图像的png文件:https://pixabay.com/illustrations/brain-character-organ-smart-eyes-1773885/
我已经用中等尺寸(1280x1138)完成了它,但也许用最小尺寸(640x569)来完成它更好。
我尝试用两种方法解决这个问题。
方法一
第一种方法是创建 Brain.png 图像的全黑图像,将其放大,然后将原始彩色大脑图像粘贴在其上。这是我的代码:
brain_black = Image.open("brain.png") #load brain image
width = brain_black.width #in order not to type a lot
height = brain_black.height #in order not to type a lot
rectangle = Image.new("RGBA", (width, height), "black") #creating a black rectangle in the size of the brain image
brain_black.paste(rectangle, mask=brain_black) #pasting on the brain image the black rectangle, and masking it with the brain picture
#now brain_black is the brain.png image, but all its pixels are black. Let's continue:
brain_black = brain_black.resize((width+180, height+180)) #resizing the brain_black by some factor
brain_regular = Image.open("brain.png") #load the brain image in order to paste later on
brain_black.paste(brain_regular,(90,90), mask=brain_regular) #paste the regular (colored) brain on top of the enlarged black brain (in x=90, y=90, the middle of the black brain)
brain_black.save("brain_method_resize.png") #saving the image
Run Code Online (Sandbox Code Playgroud)
正如您在上面的图片链接中看到的那样,此方法不起作用。它可能适用于简单的几何形状,但不适用于像这样的复杂形状。
方法二
第二种方法是将大脑图像像素数据加载到二维数组中,并循环遍历所有像素。检查每个像素的颜色,并在每个不透明的像素(即rgbA形式中的A(或Alpha)不为0)中绘制一个黑色像素,在该像素的上、下、右、左、主对角线下,主对角线向上,次对角线 (/) 向下,次对角线 (/) 向上。然后在上面的第二个像素、下面的第二个像素等中绘制一个像素。这是通过“for 循环”完成的,其中重复次数是所需的笔划宽度(在本例中为 30)。这是我的代码:
brain=Image.open("brain.png") #load brain image
background=Image.new("RGBA", (brain.size[0]+400, brain.size[1]+400), (0, 0, 0, 0)) #crate a background transparent image to create the stroke in it
background.paste(brain, (200,200), brain) #paste the brain image in the middle of the background
pixelsBrain = brain.load() #load the pixels array of brain
pixelsBack=background.load() #load the pixels array of background
for i in range(brain.size[0]):
for j in range(brain.size[1]):
r, c = i+200, j+200 #height and width offset
if(pixelsBrain[i,j][3]!=0): #checking if the opacity is not 0, if the alpha is not 0.
for k in range(30): #the loop
pixelsBack[r, c + k] = (0, 0, 0, 255)
pixelsBack[r, c - k] = (0, 0, 0, 255)
pixelsBack[r + k, c] = (0, 0, 0, 255)
pixelsBack[r - k, c] = (0, 0, 0, 255)
pixelsBack[r + k, c + k] = (0, 0, 0, 255)
pixelsBack[r - k, c - k] = (0, 0, 0, 255)
pixelsBack[r + k, c - k] =(0, 0, 0, 255)
pixelsBack[r - k, c + k] = (0, 0, 0, 255)
background.paste(brain, (200,200), brain) #pasting the colored brain onto the background, because the loop "destroyed" the picture.
background.save("brain_method_loop.png")
Run Code Online (Sandbox Code Playgroud)
这个方法确实有效,但是非常耗时(一张图片和30个像素的笔划大约需要30秒)。我想对很多图片进行此操作,所以这种方法不适合我。
有没有一种更简单、更好的方法来使用 Python Pillow 库达到我想要的结果。我该怎么做?另外,我怎样才能紧固我的循环代码(我了解一些关于 Numpy 和 OpenCV 的知识,哪个更适合这个目的?)
我知道如果手机应用程序可以在几毫秒内完成,Python 也可以,但我没有找到任何方法来做到这一点。
谢谢。
我使用 OpenCV 尝试了一些与 Photoshop 描边效果类似的解决方案(它并不完美,我仍在寻找更好的解决方案)
该算法基于欧氏距离变换。我也尝试过椭圆核结构的膨胀算法,它和photoshop有点不同,有一些信息说距离变换是photoshop使用的方式。
def stroke(origin_image, threshold, stroke_size, colors):
img = np.array(origin_image)
h, w, _ = img.shape
padding = stroke_size + 50
alpha = img[:,:,3]
rgb_img = img[:,:,0:3]
bigger_img = cv2.copyMakeBorder(rgb_img, padding, padding, padding, padding,
cv2.BORDER_CONSTANT, value=(0, 0, 0, 0))
alpha = cv2.copyMakeBorder(alpha, padding, padding, padding, padding, cv2.BORDER_CONSTANT, value=0)
bigger_img = cv2.merge((bigger_img, alpha))
h, w, _ = bigger_img.shape
_, alpha_without_shadow = cv2.threshold(alpha, threshold, 255, cv2.THRESH_BINARY) # threshold=0 in photoshop
alpha_without_shadow = 255 - alpha_without_shadow
dist = cv2.distanceTransform(alpha_without_shadow, cv2.DIST_L2, cv2.DIST_MASK_3) # dist l1 : L1 , dist l2 : l2
stroked = change_matrix(dist, stroke_size)
stroke_alpha = (stroked * 255).astype(np.uint8)
stroke_b = np.full((h, w), colors[0][2], np.uint8)
stroke_g = np.full((h, w), colors[0][1], np.uint8)
stroke_r = np.full((h, w), colors[0][0], np.uint8)
stroke = cv2.merge((stroke_b, stroke_g, stroke_r, stroke_alpha))
stroke = cv2pil(stroke)
bigger_img = cv2pil(bigger_img)
result = Image.alpha_composite(stroke, bigger_img)
return result
def change_matrix(input_mat, stroke_size):
stroke_size = stroke_size - 1
mat = np.ones(input_mat.shape)
check_size = stroke_size + 1.0
mat[input_mat > check_size] = 0
border = (input_mat > stroke_size) & (input_mat <= check_size)
mat[border] = 1.0 - (input_mat[border] - stroke_size)
return mat
def cv2pil(cv_img):
cv_img = cv2.cvtColor(cv_img, cv2.COLOR_BGRA2RGBA)
pil_img = Image.fromarray(cv_img.astype("uint8"))
return pil_img
output = stroke(test_image, threshold=0, stroke_size=10, colors=((0,0,0),))
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5550 次 |
| 最近记录: |