最新版本,点这里
在学习PIL库Image用法的时候,瞅见了个函数,Image.paste(),突然有个点子,来拼一个小心心怎么样?说干就干!
Image | 一个图像对象,img=Image.open(img_path) |
---|
box | 这里就是粘贴时图像img的左上角‘坐标’ |
那么就需要一系列的坐标,然后逐个给粘贴上去,最终才能出来。
思来想去,凭现在的能力,就只能嫁接一下以前的一个小东西,那就是画家驹的那一篇说的。
说来就是先画一个小心心,或者找一张心形图,然后挨行遍历,存储小心心每一行像素的纵坐标 y,以及起始、末尾横坐标x0, x1。(为了方便比较,就把除了小心心之外的背景搞成白色的)。就像下面这样的一张图:
def heart_get(self, source_path):
img = Image.open(source_path)
pix = img.load()
self.s_size = size = img.size
for y in range(0, size[1]):
index = 0
for x in range(0, size[0] - 1):
if (pix[x, y]!=pix[x + 1, y] and (255, 255, 255) in [pix[x, y], pix[x + 1, y]]) or (
pix[x, y]!=pix[x + 1, y] and (255, 255, 255, 255) in [pix[x, y], pix[x + 1, y]]):
if index:
self.lst[-1].append(x)
index = 0
else:
self.lst.append([y, x + 1])
index = 1
这样就得到了所有线条的坐标,每个元素的形式为 [y, x0, x1]
接下来就是要如何运用这些线条的坐标来贴出心形了
如果每一行像素条都贴的话,不是不行,只是最终的图有点大,那么可以这样:隔n行取用。怎么贴呢?先设置好每张图的大小mm的方图,然后像素条每m个像素贴一张图(即:贴图左上角横坐标Xn=x0+nm)为了美观一点,还可以隔开一点(Xn=x0+n*(m+a))。想必还是图示直观:(蓝色块表示贴的图)
问题来了,这样直接来就不能保证贴出来心形的比例。但也不要方,根据选取的像素条数,以及每张图片尺寸,可以计算出心形的高度,由于心形素材图片的留白部分较少,心形的比例可以近似于图片的长宽比。那么就可以推算出心形的宽度,那么就可以将每条像素条按照比例做相应的放大缩小。
梳理思路:
-
根据选取的像素条数计算出心形的高度。H=条数x(图片宽度+每行间隙)
-
计算出心形应有的宽度。W=H·(图宽/图高)
-
算出每条像素的放大系数。
[首先找出像素条中最宽的一条,宽度记为w],那么W/w就可以近似看做每条像素的放大(缩小)倍数
-
接着借用numpy库的函数直接完成像素条的缩放。
这样,就基本搞定了比例的问题。
per_list = np.arange(1.1, 1.9, .2)
index = self.lst[-1][0]//55
if index >= len(per_list):
index = -1
per = per_list[index]
if not per_size:
per_size = 100
yy = (2 + self.lst[-1][0]//leap)*(per_size*per)
xx = int(yy*(self.s_size[0]/self.s_size[1]))
l1 = sorted(self.lst, key=lambda x:x[2])
up_size = xx//l1[-1][2]
lst = np.array(self.lst)
lst = lst*[1, up_size, up_size]
比例虽然弄好的,但是心形的位置还需要稍微调整一下
代码附带解析如下
l0 = sorted(lst, key=lambda x: x[1])
l1 = sorted(lst, key=lambda x: x[2])
max_x = (per_size+20)*(((l1[-1][2]-l0[0][1])//(per_size+20))+1)
if xx>=max_x:
x_zh = (xx-max_x)//2
dx = x_zh - l0[0][1]
else:
xx = max_x*1.05
x_zh = int(max_x*0.05)
dx = x_zh - l0[0][1]
lst = lst + [-lst[0][0], dx, dx]
dy = ((yy - (per_size*per)*(lst[-1][0]//leap))//(per_size*per))//2
最后,就可以开始遍历贴图了
代码如下
canv = Image.new('RGB', (int(xx*1.01), int(yy)), bg)
for line in lst:
if not line[0] % leap:
y = int(dy) + line[0]//leap
print(f'\rget__{y}/{lst[-1][0]//leap}__', end='')
for x in range(line[1], line[2]+1, per_size+22):
s_img = Image.open(next(imgs)).resize((per_size, per_size))
canv.paste(s_img, (x, y*int(per_size*per)))
来个完整代码
import os
import os.path as op
import turtle as t
import numpy as np
from PIL import Image
from random import *
class Heart():
def __init__(self):
self.lst = []
self.s_size = None
def img_generator(self, imgs_list):
while 1:
for path in imgs_list:
yield path
def heart_get(self, source_path):
img = Image.open(source_path)
pix = img.load()
self.s_size = size = img.size
for y in range(0, size[1]):
index = 0
for x in range(0, size[0]-1):
if (pix[x, y] != pix[x+1, y] and (255, 255, 255) in [pix[x, y], pix[x+1, y]]) or (pix[x, y] != pix[x+1, y] and (255, 255, 255, 255) in [pix[x, y], pix[x+1, y]]):
if index:
self.lst[-1].append(x)
index = 0
else:
self.lst.append([y, x+1])
index = 1
def mkit(self, leap, imgs_path, source_path=r'c:\users\pxo\desktop\heart.png', out=None, show=True, per_size=None, bg=(0, 0, 0)):
self.heart_get(source_path)
if not out['path']:
out['path'] = r'c:\users\pxo\desktop\{}.jpg'.format(randint(0, 99999))
imgs_list = [op.join(imgs_path, name) for name in os.listdir(imgs_path)]
imgs = self.img_generator(imgs_list)
per_list = np.arange(1.1, 1.9, .2)
index = self.lst[-1][0]//55
if index >= len(per_list):
index = -1
per = per_list[index]
if not per_size:
per_size = 100
yy = (2 + self.lst[-1][0]//leap)*(per_size*per)
xx = int(yy*(self.s_size[0]/self.s_size[1]))
l1 = sorted(self.lst, key=lambda x:x[2])
up_size = xx//l1[-1][2]
lst = np.array(self.lst)
lst = lst*[1, up_size, up_size]
l0 = sorted(lst, key=lambda x: x[1])
l1 = sorted(lst, key=lambda x: x[2])
max_x = (per_size+20)*(((l1[-1][2]-l0[0][1])//(per_size+20))+1)
if xx>=max_x:
x_zh = (xx-max_x)//2
dx = x_zh - l0[0][1]
else:
xx = max_x*1.05
x_zh = int(max_x*0.05)
dx = x_zh - l0[0][1]
lst = lst + [-lst[0][0], dx, dx]
dy = ((yy - (per_size*per)*(lst[-1][0]//leap))//(per_size*per))//2
canv = Image.new('RGB', (int(xx*1.01), int(yy)), bg)
for line in lst:
if not line[0] % leap:
y = int(dy) + line[0]//leap
print(f'\rget__{y}/{lst[-1][0]//leap}__', end='')
for x in range(line[1], line[2]+1, per_size+22):
s_img = Image.open(next(imgs)).resize((per_size, per_size))
canv.paste(s_img, (x, y*int(per_size*per)))
if out:
canv.save(out['path'], quality=out['quality'])
if show:
canv.show()
if __name__ == '__main__':
imgs_path = r'c:\users\pxo\desktop\杂\toutiao_pic\斗图'
imgs_path1 = r'c:\users\pxo\desktop\tem'
bg = (0, 0, 0)
out = {
'path': None,
'quality': 70
}
first_t = Heart()
first_t.mkit(leap=20, imgs_path=imgs_path, show=True, out=out, per_size=60)
== 这是心形图素材 ==
这里创建了一个类方法Heart(), 要贴出小心心,就只用调用一下mkit()方法就可以贴出一个小心心了。
Heart.mkit(leap, imgs_path, source_path, out, show, per_size, bg) |
---|
leap | 整数。可以理解为跳跃选取贴图像素条的步长,数值越大,选择的像素条数越少,相应的贴图数就少。 |
---|
imgs_path | 文件路径。用于贴图的图片文件的存储路径。例如:c:\users\lalala\pictures |
source_path | 心形图的存储地址 |
out | 字典型。out[‘path’]为最终文件输出的路径,out[‘quality’]为存储图片的质量(好像是5-95),越大图片质量越高。默认为None值,即不输出。 |
show | 用于控制贴完图是否显示,默认为真 |
per_size | 整数。用来控制每张小贴图的大小。 |
bg | 三元元组。贴图的背景颜色,默认(0,0,0)黑色 |
不止可以表白啊,用来做表情包不 更好?接下来放一张我舍友的表情包,估计他是刷不到这片博客了,哈哈哈~~