代码执行:

我的源文件夹名和生成后文件夹名,badapple中是一张张图片,badapple_char中是生成的一张张字符

python 照片动漫化 python图片转动漫_python

python 照片动漫化 python图片转动漫_ci_02

python 照片动漫化 python图片转动漫_python 照片动漫化_03


效果展示: 因为没办法上传视频,所以截取了一小段,做成了动画gif,左边是字符画,右边是badapple源视频。

python 照片动漫化 python图片转动漫_Image_04



一)原理

将视频的每一帧提取成图片

用python代码处理每一张图片

每处理一张图片便将结果输出到终端。

二 )详细步骤:

一 .提取视频所有帧的图片

在学会将图片转字符画后我就一直在想怎么才能像那些大佬们一样实现动画效果。后来苦思冥想,动画也是一帧一帧的图片组成的,那既然这样我就把badapple原视频的所有帧提取出来!

没错我的确把所有帧全部取了出来,改成png格式(也可以是jpg格式)。我用的 pr(Adobe Premiere) 改的。(这里穿插一个小插曲:在改的时候我遇到了一个大麻烦,导出的时候我没有改路径直接放到了桌面也没有给他创一个文件夹放进去,结果图片全部导出到了桌面。。。然后。。。电脑桌面崩了。。。直接死机。。。3288张图片,电脑屏幕根本装不下。。。我疯了。
        重启大法好,重启吧。。。一重启。。。果然,还是没有好!桌面根本显示不出来东西!只有下面一个任务栏,我就在缓慢的电脑反应下 打开此电脑 找到桌面文件夹拖着缓慢的鼠标划啊划选中所有图片终于删掉了。。。。世界清净了。。。这里就告诉大家千万要改一下路径把图片导到一个文件夹里,不然直接导出到桌面,别来找我。。。找谁都不好使!)

ps:提取视频图片的步骤不做演示,网上很多教程,非常简单。

二 .修改代码,一次性处理所有图片

那么重点来了,我之前的代码每次只能处理一张图片,难道我要执行~3288次吗?别说能不能执的了了,就算我执行了,我手速也跟不上啊,更别说出来动画效果了。那么问题来了,怎么办?改代码!让代码自己去一次性处理完所有图片。

(1)RGB—>灰度值—>字符映射
def get_char(r,g,b,alpha = 256):
   
    """将256灰度映射到70个字符上"""

    if alpha == 0:

      return ' '
    length = len(ascii_char)
    """gray = int(0.2126 * r + 0.7152*g + 0.0722 * b)

    unit = (256 + 1.0)/length
    return ascii_char[int(gray/unit)]
    """
    gray = int(0.2126 * r + 0.7152*g + 0.0722 * b)
     
    return ascii_char[int(gray/256*length)]

这部分没有任何改变。

(2)命令行参数解析
def parser_image():

    #命令行输入参数处理
    parser = argparse.ArgumentParser()

    #parser.add_argument('file') #输入文件
    #parser.add_argument('output')#输出文件
    parser.add_argument('--width',type = int, default = 350) #输出字符画宽
    parser.add_argument('--height',type = int, default = 150)#输出字符画高

    #获取参数
    args = parser.parse_args()
    return args

这里我们已经不需要再手动输入每一张要处理的图片名了所以定位参数 file 和 output 我们直接注释掉。可选参数我们给它默认值分别是宽350 高150 ,可以自行调节。数值越大我们出来的效果相对更好一些,但是过大,cpu压力会变大,处理速度会慢,动画效果不流畅。

(3)图片处理
def handle_image(file_name, output_name, old_folder_name, new_folder_name):
    args = parser_image()

    IMG = old_folder_name + "/" + file_name # 将一个路径路径  源文件夹/文件名 赋值给IMG
                                            	
    WIDTH = args.width 

    HEIGHT = args.height

    OUTPUT = output_name
    im = Image.open(IMG) #打开IMG路径下的文件
    im = im.resize((WIDTH,HEIGHT),Image.NEAREST) #给图片重新设置大小
    
    txt = ""  #给txt一个空文本
    for i in range(HEIGHT):   #逐行逐列处理图片
      for j in range(WIDTH):
          txt += get_char(*im.getpixel((j,i)))#获取(j,x)坐标的像素值并传给get_char()转换为对应的字符
      txt += '\n' #每处理一行加上换行符
    print(txt) #输出到终端
    #字符画输出到文本
    #if OUTPUT:
    with open(new_folder_name + "/" + OUTPUT,'w') as f:  #通过 with上下文管理器 以只写方式打开 指定路径下的文件
          f.write(txt)   #写入txt文本 完成后with上下文管理器会自动帮我们关闭文件
    """else:
      with open("output.txt",'w') as f:
          f.write(txt)"""

这里大部分内容已经卸载注释里面。

(4)主函数部分
def main():
  
    old_folder_name = input("请输入文件名") #输入我们要处理的文件夹名称

    try:
      new_folder_name = old_folder_name + "_char" #给新文件夹一个名字
      os.mkdir(new_folder_name)#创建new_folder_name文件夹用来写入我们的字符画 
    except:
      pass
    file_names = os.listdir(old_folder_name)  #通过os.listdir()方法获取源文件夹下的所有文件并以列表形式存储
    file_names.sort()  #os.listdir()方法获取的文件名是无序的所以我们通过sort()进行一个简单的排序。
    po = multiprocessing.Pool(5)  #创建一个进程池,这个进程池最多能同时容纳5个进程
    #print(file_names)
    for file_name in file_names: #按顺序遍历文件名
        po.apply_async(handle_image, args=(file_name, file_name.replace("jpg","txt"), old_folder_name, new_folder_name))#将handle_image函数扔到进程池里面等待进程池处理
    po.close()
    po.join() #等待进程池里面的所有内容执行完毕
    end = time.time() #这里我是为了记录代码执行时间
    print("Running time: %s seconds"%(end - start))

注意:这里用到了 python 多进程的知识,引入它完全是为了提高处理文件效率,同时处理多个文件总比一次处理一个文件效率要高很多。还有一点,我的虚拟机一开始设置的是处理器核数是 1 ,这样处理速度会很慢,不能发挥多进程的功能 于是我在设置中将处理器核数改为了 2 处理速度相对提高,且输出可以达到动画的效果。

(5)导入相关模块
import os #文件读写模块
import multiprocessing  #多进程
from PIL import Image  
import argparse 
import time   #time模块
(6)其他注意事项

因为我设置的图片宽和高分别是350 和 150 所以在ubuntu16.04的终端下执行的时候,我将字体调到了最小,(ctrl - 缩小终端字体 ctrl shift + 放大终端字体)拖动终端窗口为合适大小即可,不同系统下可能会有不同,大家自行调节就好。
好的,打完收工!

完整代码:

import os
import multiprocessing
from PIL import Image
import argparse
import time   
start = time.time()
ascii_char = list("$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. ")
   
 
def get_char(r,g,b,alpha = 256):
   
    """将256灰度映射到70个字符上"""

    if alpha == 0:

      return ' '
    length = len(ascii_char)
    """gray = int(0.2126 * r + 0.7152*g + 0.0722 * b)

    unit = (256 + 1.0)/length
    return ascii_char[int(gray/unit)]
    """
    gray = int(0.2126 * r + 0.7152*g + 0.0722 * b)
     
    return ascii_char[int(gray/256*length)]
def parser_image():

    #命令行输入参数处理
    parser = argparse.ArgumentParser()

    #parser.add_argument('file') #输入文件
    #parser.add_argument('output')#输出文件
    parser.add_argument('--width',type = int, default = 350) #输出字符画宽
    parser.add_argument('--height',type = int, default = 150)#输出字符画高

    #获取参数
    args = parser.parse_args()
    return args

def handle_image(file_name, output_name, old_folder_name, new_folder_name):
    args = parser_image()

    IMG = old_folder_name + "/" + file_name

    WIDTH = args.width

    HEIGHT = args.height

    OUTPUT = output_name
    im = Image.open(IMG)
    im = im.resize((WIDTH,HEIGHT),Image.NEAREST)
    
    txt = ""
    for i in range(HEIGHT):
      for j in range(WIDTH):
          txt += get_char(*im.getpixel((j,i)))
      txt += '\n'
    print(txt)
    #字符画输出到文本
    #if OUTPUT:
    with open(new_folder_name + "/" + OUTPUT,'w') as f:
          f.write(txt)
    """else:
      with open("output.txt",'w') as f:
          f.write(txt)"""

def main():
  
    old_folder_name = input("请输入文件名")

    try:
      new_folder_name = old_folder_name + "_char"
      os.mkdir(new_folder_name)
    except:
      pass
    file_names = os.listdir(old_folder_name)
    file_names.sort()
    po = multiprocessing.Pool(5)
    #print(file_names)
    for file_name in file_names:
        po.apply_async(handle_image, args=(file_name, file_name.replace("jpg","txt"), old_folder_name, new_folder_name))
    po.close()
    po.join()
    end = time.time()
    print("Running time: %s seconds"%(end - start))
if __name__ == "__main__":
          main()