python和php合成,Python照片合成的方法详解

7564a382302caadbf67182436c1b296d.png

【相关学习推荐:python教程】

文章目录前言

Github

效果

实现过程

整体代码

前言

看电影的时候发现一个照片墙的功能,觉得这样生成照片挺好玩的,于是就动手用Python做了一下,觉得用来作照片纪念的效果可能会不错。

P:后面了解到我想做的功能叫蒙太奇拼图,所以这篇博客记录先留着,闲下来会去看一下蒙太奇拼图的算法

Github

https://github.com/jiandi1027/photo.git

效果

babda74441bacb321f9bef0374c71267.gif

88569e6a328ce94a79be008deb60db8f.gif

实现过程

1.获取图片文件夹的图片个数N,将底图拆分成XY块区域,且使X * Y

(为了保证整体的协调,会舍弃几张图片,比如5张时可能只取22的4张图片)# 打开图片

base = Image.open(baseImgPath)

base = base.convert('RGBA')

# 获取图片文件夹图片并打乱顺序

files = glob.glob(imagesPath + '/*.*')

random.shuffle(files)

# 图片数量

num = len(files)

# 底图大小

x = base.size[0]

y = base.size[1]

# 每张图片数量 这个公式是为了xNum * yNum 的总图片数量

yNum = int((num / (y / x)) ** 0.5)

if yNum == 0:

yNum = 1

xNum = int(num / yNum)

# 图片大小 因为像素没有小数点 为防止黑边所以+1

xSize = int(x / xNum) + 1

ySize = int(y / yNum) + 1

faad0df95c84dca1cdaa4b64a2b7717a.png

2.遍历文件夹的图片,依次填充生成最终合成图for file in files:

fromImage = Image.open(file)

i = int(num % xNum)

j = int(num / xNum)

out = fromImage.resize((xSize, ySize), Image.ANTIALIAS).convert('RGBA')

toImage.paste(out, (i * xSize, j * ySize))

toImage = toImage.convert('RGBA')

img = Image.blend(base, toImage, 0.3)

# 显示图片

photo = ImageTk.PhotoImage(img)

showLabel.config(image=photo)

showLabel.image = photo

if num < xNum * yNum:

num = num + 1

3.生成结束后保存图片

toImage.save(‘generator.png’)

img.save(“final.png”)

a27c24bf29db313542d1458e15d5fed2.png

a4bf7cac95b7dc4396160429c9f6e24c.png

4.建立可视化界面

163da4876c3701436ef28155077fbb52.png

5.Pyinstaller生成exe可执行文件

安装pyinstaller模块,执行命令生成exe文件pyinstaller -F -w test.py (-w就是取消窗口)

整体代码

Python的语法和设计规范还没学过,所以代码规范代码复用之类的可能会有点不到位,本博文主要是一个思路与整体流程的记录。

后续又优化了一下一些特效,比如合成图片采用随机位置,增加黑白,流年等显示特效,透明度自选等。import PIL.Image as Image

import glob

import random

import tkinter.filedialog

from tkinter.filedialog import askdirectory, Label, Button, Radiobutton, Entry

import threading

import numpy as np

from PIL import ImageTk

alpha = 0.3

imagesPath = ''

# 滑动条回调 修改透明度

def resize(ev=None):

global alpha

alpha = scale.get() / 100

# 黑白

def blackWithe(image):

# r,g,b = r*0.299+g*0.587+b*0.114

im = np.asarray(image.convert('RGB'))

trans = np.array([[0.299, 0.587, 0.114], [0.299, 0.587, 0.114], [0.299, 0.587, 0.114]]).transpose()

im = np.dot(im, trans)

return Image.fromarray(np.array(im).astype('uint8'))

# 流年

def fleeting(image, params=12):

im = np.asarray(image.convert('RGB'))

im1 = np.sqrt(im * [1.0, 0.0, 0.0]) * params

im2 = im * [0.0, 1.0, 1.0]

im = im1 + im2

return Image.fromarray(np.array(im).astype('uint8'))

# 旧电影

def oldFilm(image):

im = np.asarray(image.convert('RGB'))

# r=r*0.393+g*0.769+b*0.189 g=r*0.349+g*0.686+b*0.168 b=r*0.272+g*0.534b*0.131

trans = np.array([[0.393, 0.769, 0.189], [0.349, 0.686, 0.168], [0.272, 0.534, 0.131]]).transpose()

# clip 超过255的颜色置为255

im = np.dot(im, trans).clip(max=255)

return Image.fromarray(np.array(im).astype('uint8'))

# 反色

def reverse(image):

im = 255 - np.asarray(image.convert('RGB'))

return Image.fromarray(np.array(im).astype('uint8'))

def chooseBaseImagePath():

name = tkinter.filedialog.askopenfilename()

if name != '':

global baseImgPath

baseImgPath = name

baseImageLabel.config(text=name)

baseImg = Image.open(baseImgPath)

widthEntry.delete(0, tkinter.END)

heightEntry.delete(0, tkinter.END)

widthEntry.insert(0, baseImg.size[0])

heightEntry.insert(0, baseImg.size[1])

else:

baseImageLabel.config(text="您没有选择任何文件")

def chooseImagesPath():

name = askdirectory()

if name != '':

global imagesPath

imagesPath = name

ImagesLabel.config(text=name)

else:

ImagesLabel.config(text="您没有选择任何文件")

def thread_it(func, *args):

# 创建

t = threading.Thread(target=func, args=args)

# 守护 !!!

t.setDaemon(True)

# 启动

t.start()

def test():

MyThread(1, "Thread-1", 1).start()

baseImgPath = ''

def generator():

baseImg = Image.open(baseImgPath)

baseImg = baseImg.convert('RGBA')

files = glob.glob(imagesPath + '/*.*') # 获取图片

random.shuffle(files)

num = len(files)

# 模板图片大小

x = baseImg.size[0]

y = baseImg.size[1]

# 每张图片数量 这个公式是为了xNum * yNum 的总图片数量

yNum = int((num / (y / x)) ** 0.5)

if yNum == 0:

yNum = 1

xNum = int(num / yNum)

# 图片大小 因为像素没有小数点 为防止黑边所以+1

xSize = int(x / xNum) + 1

ySize = int(y / yNum) + 1

# 生成数量的随机列表 用于随机位置合成图片

l = [n for n in range(0, xNum * yNum)]

random.shuffle(l)

toImage = Image.new('RGB', (x, y))

num = 1

for file in files:

if num <= xNum * yNum:

num = num + 1

else:

break

fromImage = Image.open(file)

temp = l.pop()

i = int(temp % xNum)

j = int(temp / xNum)

out = fromImage.resize((xSize, ySize), Image.ANTIALIAS).convert('RGBA')

toImage.paste(out, (i * xSize, j * ySize))

toImage = toImage.convert('RGBA')

img = Image.blend(baseImg, toImage, alpha)

# 特效 但是会读取像素会降低效率

choose = v.get()

if choose == 1:

img = blackWithe(img)

elif choose == 2:

img = fleeting(img)

elif choose == 3:

img = oldFilm(img)

elif choose == 4:

img = reverse(img)

resize = img.resize((300, 300), Image.ANTIALIAS).convert('RGBA')

# 显示图片

photo = ImageTk.PhotoImage(resize)

showLabel.config(image=photo)

showLabel.image = photo

toImage.save('generator.png')

img = img.resize((int(widthEntry.get()),int(heightEntry.get())), Image.ANTIALIAS).convert('RGBA')

img.save("final.png")

resize.save("resize.png")

class MyThread(threading.Thread): # 继承父类threading.Thread

def __init__(self, threadID, name, counter):

threading.Thread.__init__(self)

self.threadID = threadID

self.name = name

self.counter = counter

def run(self): # 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数

generator()

root = tkinter.Tk()

root.title('generator')

root.geometry('500x550')

baseImageLabel = Label(root, text='')

baseImageLabel.place(x=10, y=10)

baseImageBtn = Button(root, text="选择底图", command=chooseBaseImagePath).place(x=10, y=30)

ImagesLabel = Label(root, text='')

ImagesLabel.place(x=10, y=60)

ImagesBtn = Button(root, text="选择合成图文件夹", command=chooseImagesPath).place(x=10, y=80)

v = tkinter.IntVar()

v.set(0)

Radiobutton(root, variable=v, text='默认', value=0, ).place(x=10, y=120)

Radiobutton(root, variable=v, text='黑白', value=1, ).place(x=110, y=120)

Radiobutton(root, variable=v, text='流年', value=2, ).place(x=210, y=120)

Radiobutton(root, variable=v, text='旧电影', value=3, ).place(x=310, y=120)

Radiobutton(root, variable=v, text='反色', value=4, ).place(x=410, y=120)

scaleLabel = Label(root, text='透明度').place(x=10, y=170)

scale = tkinter.Scale(root, from_=0, to=100, orient=tkinter.HORIZONTAL, command=resize)

scale.set(30) # 设置初始值

scale.pack(fill=tkinter.X, expand=1)

scale.place(x=70, y=150)

Label(root, text='宽(像素)').place(x=180, y=170)

widthEntry = Entry(root, bd=1)

widthEntry.place(x=230, y=173, width=100)

Label(root, text='高(像素)').place(x=320, y=170)

heightEntry = Entry(root, bd=1)

heightEntry.place(x=370, y=173, width=100)

generatorBtn = Button(root, text="生成", command=test).place(x=10, y=220)

showLabel = Label(root)

showLabel.place(x=100, y=220)

root.mainloop()想了解更多编程学习,敬请关注php培训栏目!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/560408.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯入门练习1-4(python)

1.Fibonacci数列 Fibonacci数列的递推公式为&#xff1a;FnFn-1Fn-2&#xff0c;其中F1F21。 当n比较大时&#xff0c;Fn也非常大&#xff0c;现在我们想知道&#xff0c;Fn除以10007的余数是多少。 输入格式 输入包含一个整数n。 输出格式 输出一行&#xff0c;包含一个整数…

12514oracle数据库重启,12514(linux登录oracle数据库)

ora-12514的错误的原因有很多。但无外乎这几种&#xff1a;1)、 ORA-12541: TNS&#xff1a; 没有监听器 显而易见&#xff0c;服务器端的监听器没有启动&#xff0c;另外检查客户端IP地址或端口填写是否正.早上同事用PL/SQL连接虚拟机中的Oracle数据库&#xff0c;发现又报了“…

python常用输入输出の方法

获取用户输入&#xff1a; Python split()方法&#xff1a; #Python split()方法&#xff1a; """ 描述 Python split() 通过指定分隔符对字符串进行切片&#xff0c;如果参数 num 有指定值&#xff0c;则分隔 num1 个子字符串 语法 split() 方法语法&#xf…

存储器和寄存器数据传输(ARMv8)

存储器和寄存器交互操作(ARMv8) 1.为什么需要存储器和寄存器交互操作&#xff1f; 编程语言中可能有数组和结构体这样复杂的数据结构。处理器只能保存少量数据到寄存器&#xff0c;但是可以存储器中放数十亿数据&#xff0c;因此数据结构存在存储器中。LEGv8指令只对寄存器操…

linux下载命令 scp,linux命令详解之scp命令

作用scp命令常用于linux之间复制文件和目录。scp是secure copy的缩写, scp是linux系统下基于ssh登陆进行安全的远程文件拷贝命令。格式从本地复制到远程复制文件scp local_file remote_usernameremote_ip:remote_folder或者scp local_file remote_usernameremote_ip:remote_fil…

python常用类型转换の方法

python实现字符和ascll转换 # 用户输入字符 c input("请输入一个字符: ") # 用户输入ASCII码&#xff0c;并将输入的数字转为整型 a int(input("请输入一个ASCII码: ")) print( c " 的ASCII 码为", ord(c)) print( a , " 对应的字符为&…

linux系统运行pbs出现ntf,Linux系统启动故障修复

Linux在启动过程中会出现一些故障&#xff0c;导致系统无法正常启动&#xff0c;本文列举了几个应用单用户模式、GRUB命令操作、Linux救援模式的典型故障修复案例。一、单用户模式Linux提供了单用户模式(类似Windows安全模式)&#xff0c;可以在最小环境中进行系统维护。在单用…

python常用进制转换の方法

python常用进制转换の方法 &#xff08;正整数&#xff09;任意进制转十进制 1.int()函数 描述 int() 函数用于将一个字符串或数字转换为整型。 语法 class int(x, base10) 参数 x -- 字符串或数字。 base -- 进制数&#xff0c;默认十进制。 返回值 返回整型数据。>>&…

在安卓手机上下载linux系统,如何在安卓手机上运行Ubuntu系统

Ubuntu是一款linux系统&#xff0c;一般我们都是将其运行在电脑中&#xff0c;可不可以在手机端也能运行Ubuntu呢&#xff1f;也是可以的&#xff0c;想知道如何实现的&#xff0c;就跟我来吧。第一步:首先, 你的手机需要获取root权限. 如果不知道如何获取, 可以到搜索一下安卓…

linux系统如何挂载新硬盘,Linux系统挂载新硬盘操作流程

1、登录后输入fdisk -l命令看当前磁盘信息2、可以看到除了当前的第一块硬盘外还有一块sdb的第二块硬盘&#xff0c;然后用fdisk /dev/sdb 进行分区3、进入fdisk命令&#xff0c;输入h可以看到该命令的帮助&#xff0c;按n进行分区4、这里输入e即分为逻辑分区&#xff0c;按p即分…

蓝桥杯基础练习1-15(python)

BASIC-1 闰年判断 问题描述 给定一个年份&#xff0c;判断这一年是不是闰年。 当以下情况之一满足时&#xff0c;这一年是闰年&#xff1a; 年份是4的倍数而不是100的倍数&#xff1b;年份是400的倍数。 其他的年份都不是闰年。 输入格式 输入包含一个整数y&#xff0c;表…

linux长期版本维护内容,[图]Linux Kernel 4.20首个维护版本更新发布 已稳定可广泛部署...

Linux Kernel 4.20内核系列由Linus Torvalds于2018年12月23日发布&#xff0c;是目前Linux内核最新的分支。目前在Kernel.org网站上4.20.1版本标记已经从“Mainline”调整为“stable”&#xff0c;意味着可以被大部分Linux发行版本使用。而Arch Linux是首个装备4.20.1的发行版本…

python列表常用の操作

python列表常用の方法 列表操作&#xff1a; 1.创建列表&#xff1a; 向列表添加元素&#xff1a; [i for i in range(1,10)]列表解析式&#xff1a; #将 1-10 每个数乘以 2 放入一个列表&#xff1a; >>> li [] >>> for i in range(1, 11):li.append(…

linux讲日志暂停,linux – Rsyslog在日志轮换后停止向远程服务器发送数据

在我的配置中,我有rsyslog负责使用imfile跟踪/home/user/my_app/shared/log/unicorn.stderr.log的更改.使用TCP将内容发送到另一个远程日志记录服务器.当日志文件旋转时,rsyslog会停止向远程服务器发送数据.我尝试重新加载rsyslog,发送HUP信号并完全重新启动它,但没有任何效果.…

三角函数和反三角函数图像、导数、积分、等式关系

之前对三角函数的理解仅局限于sin&#xff0c;cos&#xff0c;tan。但是目前遇到的都是些csc&#xff0c;sec&#xff0c;cot&#xff0c;arctan&#xff0c;arccos&#xff0c;arcsin。积分和求导还有一堆公式 最近看到了一个六边形记忆法&#xff0c;更加简便。 1.倒三角&am…

linux分区后盘符找不到,为什么我的磁盘不见了,怎么找回来啊?

2006-03-24 02:14:35找不回来了&#xff0c;因为你已经删除了这个分区&#xff0c;也就是说这个分区已经不存在了&#xff0c;文件分配表也被删掉了&#xff0c;所以没有办法找回来。动态、不可读取的意思就是这个空间还没有建立分区。全部2006-03-24 02:14:352006-03-23 12:36…

ds18b20温度传感器驱动编写

协议 DS18B20的一线工作协议流程是&#xff1a;初始化→ROM操作指令→存储器操作指令→数据传输&#xff0c;其工作时序包括&#xff1a;初始化时序、写时序、读时序。 黑色部分表示单片机操作&#xff0c;蓝色部分表示18b20操作&#xff0c;每次主机操作完成之后等待18b20状…

安装linux前分区,安装Linux系统前的准备之磁盘分区

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼欢迎加入IT技术交流YY频道8625.安装Red Hat Enterpeise Linux系统时&#xff0c;至少需要3个磁盘分区&#xff0c;分别用于创建“/”文件系统&#xff0c;/boot文件系统和交换分区。对于初学者、个人使用的Red Hat Enterpeise Linu…

SHT1x/SHT7x数字温湿度传感器驱动编写

结构图 启动传输时序图 SHT10串行通信IO初始化 其中SDA和SCL分别是数据线和时钟线。分别对应单片机的1.1口和1.0口 #define SCL P1_0 //SHT10时钟 #define SDA P1_1 //SHT10数据线由时序图可知&#xff0c;一开始SDA和SCL分别高电平和低电平 /****…

puppy linux中文设置,Puppy Linux 中文支持包制作方法

chali20122012-02-02 15:23博主您好&#xff01;我有个问题&#xff0c;u-boot-1.3.4移植到S3C2416开发板上&#xff0c;使用TFTP下载时得到这样的提示&#xff1a;SAM2416EDK # tftp 0x30000000 u-boot.bindm9000 i/o: 0x20000300, id: 0x90000a46MAC: 1f:2e:3d:4c:5b:6aTFTP …