当前位置:Gxlcms > Python > Python照片合成的方法详解

Python照片合成的方法详解

时间:2021-07-01 10:21:17 帮助过:120人阅读

相关学习推荐:python教程

文章目录

      • 前言
      • Github
      • 效果
      • 实现过程
      • 整体代码

前言

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

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

Github

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

效果

在这里插入图片描述
在这里插入图片描述

实现过程

1.获取图片文件夹的图片个数N,将底图拆分成XY块区域,且使X * Y<N
(为了保证整体的协调,会舍弃几张图片,比如5张时可能只取2
2的4张图片)

  1. # 打开图片
  2. base = Image.open(baseImgPath)
  3. base = base.convert('RGBA')
  4. # 获取图片文件夹图片并打乱顺序
  5. files = glob.glob(imagesPath + '/*.*')
  6. random.shuffle(files)
  7. # 图片数量
  8. num = len(files)
  9. # 底图大小
  10. x = base.size[0]
  11. y = base.size[1]
  12. # 每张图片数量 这个公式是为了xNum * yNum 的总图片数量<num又成比例的最大整数
  13. yNum = int((num / (y / x)) ** 0.5)
  14. if yNum == 0:
  15. yNum = 1
  16. xNum = int(num / yNum)
  17. # 图片大小 因为像素没有小数点 为防止黑边所以+1
  18. xSize = int(x / xNum) + 1
  19. ySize = int(y / yNum) + 1

在这里插入图片描述

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

  1. for file in files:
  2. fromImage = Image.open(file)
  3. i = int(num % xNum)
  4. j = int(num / xNum)
  5. out = fromImage.resize((xSize, ySize), Image.ANTIALIAS).convert('RGBA')
  6. toImage.paste(out, (i * xSize, j * ySize))
  7. toImage = toImage.convert('RGBA')
  8. img = Image.blend(base, toImage, 0.3)
  9. # 显示图片
  10. photo = ImageTk.PhotoImage(img)
  11. showLabel.config(image=photo)
  12. showLabel.image = photo
  13. if num < xNum * yNum:
  14. num = num + 1

3.生成结束后保存图片
toImage.save(‘generator.png’)
img.save(“final.png”)
在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190805150649966.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoaWppYW5kaQ==,size_16,color_FFFFFF,t_70
4.建立可视化界面
在这里插入图片描述
5.Pyinstaller生成exe可执行文件
安装pyinstaller模块,执行命令生成exe文件

  1. pyinstaller -F -w test.py (-w就是取消窗口)

整体代码

Python的语法和设计规范还没学过,所以代码规范代码复用之类的可能会有点不到位,本博文主要是一个思路与整体流程的记录。
后续又优化了一下一些特效,比如合成图片采用随机位置,增加黑白,流年等显示特效,透明度自选等。

  1. import PIL.Image as Image
  2. import glob
  3. import random
  4. import tkinter.filedialog
  5. from tkinter.filedialog import askdirectory, Label, Button, Radiobutton, Entry
  6. import threading
  7. import numpy as np
  8. from PIL import ImageTk
  9. alpha = 0.3
  10. imagesPath = ''
  11. # 滑动条回调 修改透明度
  12. def resize(ev=None):
  13. global alpha
  14. alpha = scale.get() / 100
  15. # 黑白
  16. def blackWithe(image):
  17. # r,g,b = r*0.299+g*0.587+b*0.114
  18. im = np.asarray(image.convert('RGB'))
  19. trans = np.array([[0.299, 0.587, 0.114], [0.299, 0.587, 0.114], [0.299, 0.587, 0.114]]).transpose()
  20. im = np.dot(im, trans)
  21. return Image.fromarray(np.array(im).astype('uint8'))
  22. # 流年
  23. def fleeting(image, params=12):
  24. im = np.asarray(image.convert('RGB'))
  25. im1 = np.sqrt(im * [1.0, 0.0, 0.0]) * params
  26. im2 = im * [0.0, 1.0, 1.0]
  27. im = im1 + im2
  28. return Image.fromarray(np.array(im).astype('uint8'))
  29. # 旧电影
  30. def oldFilm(image):
  31. im = np.asarray(image.convert('RGB'))
  32. # 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
  33. trans = np.array([[0.393, 0.769, 0.189], [0.349, 0.686, 0.168], [0.272, 0.534, 0.131]]).transpose()
  34. # clip 超过255的颜色置为255
  35. im = np.dot(im, trans).clip(max=255)
  36. return Image.fromarray(np.array(im).astype('uint8'))
  37. # 反色
  38. def reverse(image):
  39. im = 255 - np.asarray(image.convert('RGB'))
  40. return Image.fromarray(np.array(im).astype('uint8'))
  41. def chooseBaseImagePath():
  42. name = tkinter.filedialog.askopenfilename()
  43. if name != '':
  44. global baseImgPath
  45. baseImgPath = name
  46. baseImageLabel.config(text=name)
  47. baseImg = Image.open(baseImgPath)
  48. widthEntry.delete(0, tkinter.END)
  49. heightEntry.delete(0, tkinter.END)
  50. widthEntry.insert(0, baseImg.size[0])
  51. heightEntry.insert(0, baseImg.size[1])
  52. else:
  53. baseImageLabel.config(text="您没有选择任何文件")
  54. def chooseImagesPath():
  55. name = askdirectory()
  56. if name != '':
  57. global imagesPath
  58. imagesPath = name
  59. ImagesLabel.config(text=name)
  60. else:
  61. ImagesLabel.config(text="您没有选择任何文件")
  62. def thread_it(func, *args):
  63. # 创建
  64. t = threading.Thread(target=func, args=args)
  65. # 守护 !!!
  66. t.setDaemon(True)
  67. # 启动
  68. t.start()
  69. def test():
  70. MyThread(1, "Thread-1", 1).start()
  71. baseImgPath = ''
  72. def generator():
  73. baseImg = Image.open(baseImgPath)
  74. baseImg = baseImg.convert('RGBA')
  75. files = glob.glob(imagesPath + '/*.*') # 获取图片
  76. random.shuffle(files)
  77. num = len(files)
  78. # 模板图片大小
  79. x = baseImg.size[0]
  80. y = baseImg.size[1]
  81. # 每张图片数量 这个公式是为了xNum * yNum 的总图片数量<num又成比例的最大整数
  82. yNum = int((num / (y / x)) ** 0.5)
  83. if yNum == 0:
  84. yNum = 1
  85. xNum = int(num / yNum)
  86. # 图片大小 因为像素没有小数点 为防止黑边所以+1
  87. xSize = int(x / xNum) + 1
  88. ySize = int(y / yNum) + 1
  89. # 生成数量的随机列表 用于随机位置合成图片
  90. l = [n for n in range(0, xNum * yNum)]
  91. random.shuffle(l)
  92. toImage = Image.new('RGB', (x, y))
  93. num = 1
  94. for file in files:
  95. if num <= xNum * yNum:
  96. num = num + 1
  97. else:
  98. break
  99. fromImage = Image.open(file)
  100. temp = l.pop()
  101. i = int(temp % xNum)
  102. j = int(temp / xNum)
  103. out = fromImage.resize((xSize, ySize), Image.ANTIALIAS).convert('RGBA')
  104. toImage.paste(out, (i * xSize, j * ySize))
  105. toImage = toImage.convert('RGBA')
  106. img = Image.blend(baseImg, toImage, alpha)
  107. # 特效 但是会读取像素会降低效率
  108. choose = v.get()
  109. if choose == 1:
  110. img = blackWithe(img)
  111. elif choose == 2:
  112. img = fleeting(img)
  113. elif choose == 3:
  114. img = oldFilm(img)
  115. elif choose == 4:
  116. img = reverse(img)
  117. resize = img.resize((300, 300), Image.ANTIALIAS).convert('RGBA')
  118. # 显示图片
  119. photo = ImageTk.PhotoImage(resize)
  120. showLabel.config(image=photo)
  121. showLabel.image = photo
  122. toImage.save('generator.png')
  123. img = img.resize((int(widthEntry.get()),int(heightEntry.get())), Image.ANTIALIAS).convert('RGBA')
  124. img.save("final.png")
  125. resize.save("resize.png")
  126. class MyThread(threading.Thread): # 继承父类threading.Thread
  127. def __init__(self, threadID, name, counter):
  128. threading.Thread.__init__(self)
  129. self.threadID = threadID
  130. self.name = name
  131. self.counter = counter
  132. def run(self): # 把要执行的代码写到run函数里面 线程在创建后会直接运行run函数
  133. generator()
  134. root = tkinter.Tk()
  135. root.title('generator')
  136. root.geometry('500x550')
  137. baseImageLabel = Label(root, text='')
  138. baseImageLabel.place(x=10, y=10)
  139. baseImageBtn = Button(root, text="选择底图", command=chooseBaseImagePath).place(x=10, y=30)
  140. ImagesLabel = Label(root, text='')
  141. ImagesLabel.place(x=10, y=60)
  142. ImagesBtn = Button(root, text="选择合成图文件夹", command=chooseImagesPath).place(x=10, y=80)
  143. v = tkinter.IntVar()
  144. v.set(0)
  145. Radiobutton(root, variable=v, text='默认', value=0, ).place(x=10, y=120)
  146. Radiobutton(root, variable=v, text='黑白', value=1, ).place(x=110, y=120)
  147. Radiobutton(root, variable=v, text='流年', value=2, ).place(x=210, y=120)
  148. Radiobutton(root, variable=v, text='旧电影', value=3, ).place(x=310, y=120)
  149. Radiobutton(root, variable=v, text='反色', value=4, ).place(x=410, y=120)
  150. scaleLabel = Label(root, text='透明度').place(x=10, y=170)
  151. scale = tkinter.Scale(root, from_=0, to=100, orient=tkinter.HORIZONTAL, command=resize)
  152. scale.set(30) # 设置初始值
  153. scale.pack(fill=tkinter.X, expand=1)
  154. scale.place(x=70, y=150)
  155. Label(root, text='宽(像素)').place(x=180, y=170)
  156. widthEntry = Entry(root, bd=1)
  157. widthEntry.place(x=230, y=173, width=100)
  158. Label(root, text='高(像素)').place(x=320, y=170)
  159. heightEntry = Entry(root, bd=1)
  160. heightEntry.place(x=370, y=173, width=100)
  161. generatorBtn = Button(root, text="生成", command=test).place(x=10, y=220)
  162. showLabel = Label(root)
  163. showLabel.place(x=100, y=220)
  164. root.mainloop()

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

以上就是Python照片合成的方法详解的详细内容,更多请关注gxlcms其它相关文章!

人气教程排行