Python 拼图成心2.0【重新梳理】【附完整代码】

最新版本,点这里


在这里插入图片描述

之前一时兴起,写了个生成心形贴图的程序【链接在这里】但是只是把功能实现了,有很多鸡肋还有逻辑之类混乱的,于是又重新整理了一下。

需要用到的库

import os
import os.path as op
import numpy as np
from PIL import Image
from tkinter import Tk
from tkinter.filedialog import asksaveasfilename, askopenfilename, askdirectory

框架参考之前的-----【链接在这里】

  • 首先是获取所需心形图数据
说明
获取贴图形状(不一定是心形
输入图片路径(图片为严格黑白,获取黑色部分横向像素条数据
获取完整数据(每一行单独黑色像素的纵坐标、起始
获取的数据单个元素[y,x0,x1] ,y并非从一定0开始
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, out=None, 
		show=True, per_size=None, 
		bg=(0, 0, 0), ):
说明
每隔leap行取一行像素贴图
source:心形图地址
out: 字典类型。key有:path(输出路径)、quality(输出图片质量)
per_size:每张贴图的尺寸(后续会将源图压缩为该尺寸的方形图)
bg:顾名思义,背景(底图)颜色
贴图时图间横、纵间距皆可调整(类方法里)
贴完图是选择是否展示
(xx, yy) 为底图尺寸
lst 为像素条数据列表
top_yblank 为顶部留白
dx, dy 为贴图时横、纵间距
canv = Image.new('RGB', (int(xx), int(yy)), bg)
# 先把参量全部整数化
lst = np.array(lst, dtype='int')
top_yblank = int(top_yblank)
for line in lst:
    #根据leap值筛去不贴图的像素条,达到控制贴图像素条数
    if not (line[0] - top_yblank) % leap:
        # 当前-行数-(从零开始) [每行高度(per_size+dy)]
        column = int((line[0] - top_yblank) // leap)
        print(f'\rget__{column+1}/{lst[-1][0]//leap}__!', end='')
        for x in range(line[1], line[2]+1, per_size+self.dx):
            s_img = Image.open(next(imgs)).resize((per_size, per_size))
            canv.paste(s_img, (x, top_yblank+column*int(per_size+self.dy)))
else:
    print('\rAll Done!', end='')
if out:
    canv.save(out['path'], quality=out['quality'])
    print('\rSaved it!')
if show:
    canv.show()
问题
底图大小需要计算(比例应与心形图保持一致)
由于照像素条贴图后像素条“厚度”增加,心形比例失协,所以需要提前将获取到的像素条横向放大,保证比例
由于底图、心心相素条位置并不匹配(心形位置不居中),所以需要将像素条平移使心形居中。
需要从所给路径获取图集(地址),滤除非图片路径后制作迭代器

  • 基于问题的调整

Ⅰ . 图集迭代器
  • 从 imgs_path 获取到其中的所有文件路径
imgs_list = [
			op.join(imgs_path, name) for name in os.listdir(imgs_path)
			]
  • 滤除其中的非图片路径并制作迭代器 ims
def is_pic(self, path):
    if path.endswith(('.jpg', '.png', '.gif', '.jpeg')):
        return 1
    else:
        return 0
# 图片路径生成器
def img_generator(self, imgs_list):
    while 1:
        for path in imgs_list:
            yield path   
            
imgs_list = list(filter(self.is_pic, imgs_list))
imgs = self.img_generator(imgs_list)
Ⅱ . 底图大小计算(xx, yy)
# y_more 是裕度,dy 为纵向间距
yy = (self.y_more + self.lst[-1][0]//leap)*(per_size+self.dy)
#计算图片应有的宽度 xx = yy*(心图宽/心图高)
xx = int(yy*(self.s_size[0]/self.s_size[1]))
Ⅲ. 像素条横向放大系数计算
#先对像素条的横向结束坐标排序,以最大值即l1[-1][2]近似于最大像素条长度
end_x = sorted(self.lst, key=lambda x: x[2])
# 用底图宽度xx除以最大宽度end_x[-1][2]计算出横向放大/缩小的比例
x_up_size = xx//end_x[-1][2]
# 横向像素条按比例放大了,但是整体右移了
# 例: [1,2]放大两倍后为[2,4], 长度放大,但是线段也整体右移

# 放大后的最长像素条基本跟底图一样宽
lst = lst*[1, x_up_size, x_up_size]
# 本来是最大贴图宽度跟xx一样,现在给它2个图的裕度
xx += 2*per_size
Ⅳ. 平移使居中
像素条全部存于 lst(ndarray对象) 中
dy、dx 分别为纵、横向贴图间距
  • 横向偏移量计算(dxx)
# 对放大后的像素条的横向起末坐标分别排序
x_start = sorted(lst, key=lambda x: x[1])
x_end = sorted(lst, key=lambda x: x[2])
# 计算像素条最长时,横向贴图数
max_len = x_end[-1][2]-x_start[0][1]
# 加.6个“宽度” 调整一下
max_xpic = max_len/(per_size+self.dx) + .6
# 再计算贴图条的长度(
max_piclen = max_xpic*(per_size+self.dx)
# 计算出总共留白宽度
all_xblank = xx - max_piclen
left_xblank = all_xblank/2
# 最左点横坐标
most_left = x_start[0][1]
# 计算像素条左移长度
ddx = most_left - left_xblank
  • 纵向偏移量(top_yblank)
# 计算y纵向留白高度 裕度*单位高度+最后一张图的附加间隔. 除以2以后可以得到单边留白高度
all_yblank = self.y_more*(per_size+self.dy) + self.dy
top_yblank = all_yblank/2
  • 最后,平移像素,使图形居中
top_yblank = all_yblank/2
# 平移像素条使居中,顺便把像素条的行号操作一下让它从0开始 然后加上留白使其居中
lst = lst + [-lst[0][0]+top_yblank, -ddx, -ddx]
至此已经解决的差不多了


来使用一下

框架
class Heart():
    def __init__(self):
        # 存取原始行像素条
        self.lst = []
        # 这是心形图尺寸
        self.s_size = None
        # 绘制线条的颜色
        self.pencolor = 'black'
        # 拼图保存路径
        self.out_path = None
        # 横向间距
        self.dx = 20
        # 纵向间距
        self.dy = 20
        # 纵向裕度 不一定整数。一单位 = per_size+dy
        self.y_more = 3

    # 图片路径生成器
    def img_generator(self, imgs_list):

    def heart_get(self, source_path):

    def get_outpath(self):
        self.out_path = asksaveasfilename(
                title='选择图片保存路径',
                initialdir=r'c:\users\pxo\desktop',
                filetypes=[('pic', '.jpg .png .gif .jpeg'), ],
                defaultextension='.png'
            )

    def is_pic(self, path):

    # 拼图
    # leap 为跳行数(每次跳过的像素行数)
    def mkit(self, leap, imgs_path, source_path, out=None, show=True, per_size=None, bg=(0, 0, 0)):
        # 图片大小 per_size x per_size
        if not per_size:
            per_size = 100
        if not out['path']:
            while not self.out_path:
                self.get_outpath()
            out['path'] = self.out_path
        imgs_list = [op.join(imgs_path, name) for name in os.listdir(imgs_path)]
        imgs_list = list(filter(self.is_pic, imgs_list))
        imgs = self.img_generator(imgs_list)
        # 获取线条 存于self.lst
        self.heart_get(source_path)
        # 将像素条列表转换为array对象,然后只直接缩放像素条的起末坐标达到缩放像素条的效果
        lst = np.array(self.lst)
		# 接着就是之前分析 放大,平移,贴图,保存

示例

if __name__ == '__main__':
	root = Tk()
	root.withdraw()
	imgs_path = askdirectory()
	bg = (0, 0, 0)
	out = {
			'path': None,
			'quality': 50,
			}
	one_test = Heart()
	# 更改贴图间距
	oen_test.dx = 20
	one_test.dy = 22
	# 更改纵向裕度(跟纵向留白有关)
	one_test.y_more = 3

完整代码-需要直接Copy

不恰当的地方欢迎指教啊,一起学习学习!!

'''
@project:PYTHON
@IDE:PyCharm
@file: heart_s.py
@time: 2020-02-23  10:26 
'''

# 贴图成心,我的天,太乱了,得花时间整理一下

import os
import os.path as op
import turtle as t
import numpy as np
from PIL import Image
from random import *
from tkinter.filedialog import asksaveasfilename, askopenfilename, askdirectory
from tkinter import Tk

class Heart():
    def __init__(self):
        # 存取原始行像素条
        self.lst = []
        # 这是心形图尺寸
        self.s_size = None
        # 拼图保存路径
        self.out_path = None
        # 横向间距
        self.dx = 20
        # 纵向间距
        self.dy = 20
        # 纵向裕度 不一定整数。一单位 = per_size+dy
        self.y_more = 3

    # 图片路径生成器
    def img_generator(self, imgs_list):
        while 1:
            for path in imgs_list:
                yield path

    # 获取贴图形状(不一定是心形
    # 输入图片路径(图片为严格黑白,获取黑色部分
    # 返回完整数据(每一行单独黑色像素的起始
    # 返回的数据元素[y,x0,x1] y并非从0开始
    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

        if get_it:
            ts = t.getscreen()
            file_out = ''
            while not len(file_out):
                file_out = asksaveasfilename(
                    title='选择图片保存路径',
                    filetypes=[('Pic', '.eps')],
                    defaultextension='.eps',
                    initialdir=r'c:\users\pxo\desktop'
                )
            ts.getcanvas().postscript(file=file_out)
        t.mainloop()

    def get_outpath(self):
        self.out_path = asksaveasfilename(
                title='选择图片保存路径',
                initialdir=r'c:\users\pxo\desktop',
                filetypes=[('pic', '.jpg .png .gif .jpeg'), ],
                defaultextension='.png'
            )

    def is_pic(self, path):
        if path.endswith(('.jpg', '.png', '.gif', '.jpeg')):
            return 1
        else:
            return 0

    # 拼图
    # leap 为跳行数(每次跳过的像素行数)
    def mkit(self, leap, imgs_path, source_path, out=None, show=True, per_size=None, bg=(0, 0, 0)):
        # 图片大小 per_size x per_size
        if not per_size:
            per_size = 100
        if not out['path']:
            while not self.out_path:
                self.get_outpath()
            out['path'] = self.out_path
        imgs_list = [op.join(imgs_path, name) for name in os.listdir(imgs_path)]
        imgs_list = list(filter(self.is_pic, imgs_list))
        imgs = self.img_generator(imgs_list)
        # 获取线条 存于self.lst
        self.heart_get(source_path)
        # 将像素条列表转换为array对象,然后只直接缩放像素条的起末坐标达到缩放像素条的效果
        lst = np.array(self.lst)
        if per_size >= min(self.s_size):
            per_size = 100
        # 图片宽度yy
        # y_more 是裕度,
        yy = (self.y_more + self.lst[-1][0]//leap)*(per_size+self.dy)
        #计算图片应有的宽度 xx = yy*(图宽/图高)
        # 如果横纵间隔差距较大
        xx = int(yy*(self.s_size[0]/self.s_size[1]))

        #先对像素条的横向结束坐标排序,以最大值即l1[-1][2]近似于最大像素条长度
        end_x = sorted(self.lst, key=lambda x: x[2])
        # 用底图宽度xx除以最大宽度end_x[-1][2]计算出横向放大/缩小的比例
        x_up_size = xx//end_x[-1][2]

        # 横向像素条放大了,但是整体右移了
        # 放大后的最长像素条跟底图一样宽
        lst = lst*[1, x_up_size, x_up_size]
        # 本来是最大贴图宽度跟xx一样,现在给它2个图的裕度
        xx += 2*per_size

        # 对放大后的像素条的横向起末坐标分别排序
        x_start = sorted(lst, key=lambda x: x[1])
        x_end = sorted(lst, key=lambda x: x[2])
        # 计算像素条最长时,横向贴图数
        max_len = x_end[-1][2]-x_start[0][1]
        # 加.6 调整一下
        max_xpic = max_len/(per_size+self.dx) + .6
        # 再计算贴图条的长度(
        max_piclen = max_xpic*(per_size+self.dx)
        # 计算出总共留白宽度
        all_xblank = xx - max_piclen
        left_xblank = all_xblank/2
        # 最左点横坐标
        most_left = x_start[0][1]
        # 计算像素条左移长度
        ddx = most_left - left_xblank
        # 计算y纵向留白高度 裕度*单位高度+最后一张图的附加间隔. 除以2以后可以得到单边留白高度
        all_yblank = self.y_more*(per_size+self.dy) + self.dy
        top_yblank = all_yblank/2
        # 平移像素条使居中,顺便把像素条的行号操作一下让它从0开始 然后加上留白使其居中
        lst = lst + [-lst[0][0]+top_yblank, -ddx, -ddx]

        canv = Image.new('RGB', (int(xx), int(yy)), bg)
        # 先把参量全部整数化
        lst = np.array(lst, dtype='int')
        top_yblank = int(top_yblank)

        ''' 调试段
        x_start = sorted(lst, key=lambda x: x[1])
        y_start = sorted(lst, key=lambda x: x[0])
        print(f'最左起始坐标{x_start[0][1]}, 最顶起始坐标{y_start[0][0]}', f'{top_yblank=}')
        '''
        for line in lst:
            #根据leap值筛去不贴图的像素条,达到控制贴图像素条数
            if not (line[0] - top_yblank) % leap:
                # 当前-行数-(从零开始) [每行高度(per_size+dy)]
                column = int((line[0] - top_yblank) // leap)
                print(f'\rget__{column+1}/{lst[-1][0]//leap}__!', end='')
                for x in range(line[1], line[2]+1, per_size+self.dx):
                    s_img = Image.open(next(imgs)).resize((per_size, per_size))
                    canv.paste(s_img, (x, top_yblank+column*int(per_size+self.dy)))
        else:
            print('\rAll Done!', end='')
        if out:
            canv.save(out['path'], quality=out['quality'])
            print('\rSaved it!')
        if show:
            canv.show()

if __name__ == '__main__':
    root = Tk()
    root.withdraw()
    imgs_path = askdirectory(title='请选择图片文件夹路径')
	bg = (0, 0, 0)
	out = {
			'path': None,
			'quality': 50,
			}
	one_test = Heart()
	# 更改贴图间距
	oen_test.dx = 20
	one_test.dy = 22
	# 更改纵向裕度(跟纵向留白有关)
	one_test.y_more = 3
    one.mkit(leap=20, imgs_path=imgs_path, source_path='heart.png', bg=bg, show=True, out=out, per_size=100)

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的壳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值