天天看点

python opencv 实现图片,视频 转 字符/字符画/字符视频

基于python 3.8

  • github :https://github.com/heli-link/pyCharStyle
  • csdn : https://download.csdn.net/download/qq_42733641/13084065

所有更新及打包 exe 都在github,因为更新方便,csdn链接只有代码(忘了加exe,后面发现不能修改,遂罢)周知

界面及使用

python opencv 实现图片,视频 转 字符/字符画/字符视频
python opencv 实现图片,视频 转 字符/字符画/字符视频

复选框选择对应的转换格式,在下方会出现字符选择器和颜色选择器,点击即可选择

图片转换时,界面会卡顿,表现为按钮按下去不会回弹,正常现象,是因为没有加线程,反正只要几秒钟,不影响

转换完成的图片视频在软件根目录

视频转换请勿使用高分辨率,速度太慢,当然输入分辨率越高,转换后的分辨率也高

参考 : i5-7200u 实测 500x300 mp4, 每秒只能处理 1.5 帧

效果(这里只能上图,视频就不做展示,效果参考图片)

- 原图
python opencv 实现图片,视频 转 字符/字符画/字符视频

-图片转 txt

图片转 txt文本

python opencv 实现图片,视频 转 字符/字符画/字符视频

- 图片转指定字符-jpg彩色

同一个字符,通过颜色的变换展现出图像

python opencv 实现图片,视频 转 字符/字符画/字符视频

- 图片转字符-jpg彩色

图片转txt 顺便在添加个颜色

python opencv 实现图片,视频 转 字符/字符画/字符视频

- 图片转字符-jpg指定颜色

不同字符组成的图片,通过指定颜色可以实现,黑白,彩色字符图

绿色貌似不太好,慎用

python opencv 实现图片,视频 转 字符/字符画/字符视频

代码

图片处理

import time
import cv2
import util as u
from PIL import Image, ImageDraw, ImageFont

# 图片转字符文本
def getTxt(src):
    txt = ""
    path = u.getTime() + '-txt.txt'
    file = open(path, 'w')
    img = cv2.imread(src)
    # 调整图片大小
    img = cv2.resize(img, (int(img.shape[1] / 3), int(img.shape[0] / 8.5)))
    # 遍历像素
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            r, g, b = img[i, j]
            txt += u.getchar(r,g,b)
        txt += "\n"
    file.write(txt)
    file.close()
    return txt,img.shape[1],img.shape[0]

#     图片转字符图片 两种模式
#  mode 0/固定字符/str
#       1/彩色
#       2/指定颜色/color
def img_txtimg(src, msg='o', mode=0, color = '#000000', outpath=None, isvedio=False):
    start = time.time()
    img = cv2.imread(src)
    if outpath == None:
        outpath = u.getTime() + '-color.jpg'
    # 字符间隔
    if isvedio:
        coe = 10
        font = ImageFont.truetype("‪C:\\Windows\\Fonts\\AdobeHeitiStd-Regular.otf", coe + 5)  # 设置字体
    else:
        coe = 25
        font = ImageFont.truetype("‪C:\\Windows\\Fonts\\AdobeHeitiStd-Regular.otf", coe+10)  # 设置字体
    img = cv2.resize(img, (int(img.shape[1] * 0.2), int(img.shape[0] * 0.2)))
    H = img.shape[0]*coe
    W = img.shape[1]*coe
    # 创建画布
    im_txt = Image.new("RGB", (W, H), (220, 220, 220))
    dr = ImageDraw.Draw(im_txt)
    x=y = 0
    # 设置字体

    # font = ImageFont.truetype("C:\\WINDOWS\\Fonts\\Gill Sans\\GILSANUB.TTF", coe)  # 设置字体

    # 遍历像素 转成字符
    index = 0
    buf = 0
    first = time.time()
    for i in range(0, img.shape[0]):
            two = time.time()
            for j in range(0, img.shape[1]):
                r, g, b = img[i, j]
                # 指定字符
                if mode == 0:
                    # 指定字符绘制
                    dr.text([x, y], msg, (r, g, b), font)
                if mode == 1:
                    msg = u.getchar(r, g, b)
                    dr.text([x, y], msg, (r, g, b,), font)  # 彩色
                if mode == 2:
                    msg = u.getchar(r, g, b)
                    dr.text([x, y], msg, color, font)  # 指定颜色
                x += coe
                index+=1
            buf = index
            index = 0
            three = time.time()
            y += coe
            x = 0
    fore = time.time()
    #     保存字符图片
    im_txt.save(outpath,encoding='utf-8')
    three = time.time()
    print("1=" + str(first - start) + '--> '+str(buf)+'次=' + str(three - two) + '--> 3=' + str(three - two))
#     视频转字符 mode
# 0: '视频转指定字符mp4-原彩'
# 1: '视频转字符jpg-原彩'
# 2: '图片转字符jpg-指定颜色'
def vidio_txtimg(src,mode,msg='o',color=(255,255,255),var=None):
    # 清空文件夹
    u.clearFile('image')
    vidio = cv2.VideoCapture(src)
    # 获取原视频帧率
    fps = vidio.get(5)
    c = 0
    while True:
        start = time.time()
        check, frame = vidio.read()
        if check:

            src = 'image/'+str(c)+'.jpg'
            # 视频抽帧 转换
            cv2.imwrite(src,frame)
            # '视频转指定字符mp4-原彩'
            if mode == 0:
                img_txtimg(src, mode=0, msg=msg, outpath=src, isvedio=True)

            #  '视频转字符mp4-原彩'
            if mode == 1:
                img_txtimg(src,mode=1,outpath=src,isvedio=True)

            #  '图片转字符mp4-指定颜色'
            if mode == 2:
                img_txtimg(src,mode=2,color=color,outpath=src,isvedio=True)

            # 实时播放
            # img = cv2.imread(src)
            # cv2.imshow("frame",cv2.imread(src))
            c += 1
            var.set('正在处理第'+str(c)+'帧')
        else:
            break
        cv2.waitKey(1)
        end = time.time()
        print('帧率:'+str(1/(end-start)))
    print('帧数:' + str(c))
    vidio.release()
    u.createVidio(fps,var)

if __name__ == '__main__':
    img_txtimg('icon/color.png', mode=2)
           

GUI 界面

import threading
import tkinter
from tkinter import filedialog
from PIL import Image, ImageTk
import windnd
import tkinter.ttk
import util as u
import cvimg
import text as tx
import tkinter.colorchooser as cc  # 给导入的包指定一个别名
from text import color as c
import tkinter.messagebox #这个是消息框,对话框的关键

win = tkinter.Tk()
var = tkinter.StringVar()
# 把图片绑定在类属性中,防止运行过程中被gc清理
class iconimg():
    ssabericon = None
    coloricon = None
    def __init__(self):
        # tk只支持 gif,所以需要先使用pil转一下 改代码需放在tk初始化之后,否则会报错
        # 设置条形框,插入图片
        image = Image.open("icon/saber.jpg")
        image = image.resize((32,32))
        self.sabericon =  ImageTk.PhotoImage(image)

        img = Image.open('icon/color.png')  # 打开图片
        img = img.resize((48,48))
        self.coloricon =  ImageTk.PhotoImage(img)  # 用PIL模块的PhotoImage打开
iconimg = iconimg()
coloricon = iconimg.coloricon
sabericon = iconimg.sabericon

def Conversion(path):
    # 获取文件和命令下标
    suf = u.suffix(path)
    index = combobox.current()
    print('suf'+str(suf))
    print('index'+str(index))
     # 获取字符和颜色
    msg = tx.letter[cb.current()]
    color = img.color
     # 图片处理
    if suf == 0:
        # 图片转txt
        if index == 0:
            var.set('图片转txt...')
            cvimg.getTxt(path)
            var.set('图片转txt,转换结束,请在根目录查看,宁也可以选择继续转换')
        # 图片转指定字符画 - 彩色
        elif index == 1:
            msg = tx.letter[cb.current()]
            var.set('图片转指定字符-jpg彩色...')
            cvimg.img_txtimg(path, mode=0, msg=msg)
            var.set('图片转指定字符-jpg彩色,完成,请在根目录查看,宁也可以选择继续转换')
        # 图片转字符画-原彩
        elif index == 2:
            var.set('图片转字符-jpg彩色...')
            cvimg.img_txtimg(path,mode=1)
            var.set('图片转字符-jpg彩色,完成,请在根目录查看,宁也可以选择继续转换')
        # 图片转字符画-指定颜色
        elif index == 3:
            var.set('图片转字符-jpg指定颜色...')
            cvimg.img_txtimg(path, mode=2,color=color)
            var.set('图片转字符-jpg指定颜色,完成,请在根目录查看,宁也可以选择继续转换')
        else:
            var.set('错误选项')
    # 视频处理
    if suf == 1:
        # '视频转指定字符-mp4彩色',
        if index == 4:
            msg = tx.letter[cb.current()]
            var.set('视频转指定字符-mp4彩色...'+msg)
            threading.Thread(target=cvimg.vidio_txtimg,args=(path,0,msg,(255,255,255),var)).start()
        # '视频转字符-mp4彩色',
        elif index == 5:
            var.set('视频转字符-mp4彩色...')
            threading.Thread(target=cvimg.vidio_txtimg, args=(path, 1, msg, (255, 255, 255), var)).start()
            # cvimg.vidio_txtimg(path, mode=1)
        # '视频转字符-mp4指定颜色'
        elif index == 6:
            var.set('视频转字符-mp4指定颜色...')
            threading.Thread(target=cvimg.vidio_txtimg, args=(path, 2, msg, color, var)).start()
            # cvimg.vidio_txtimg(path,mode=2,color=color)
        else:
            var.set('错误选项')
for i in tx.mat:
    tx.labermsg += i
# 获取图片路径,并进行格式判别
class img():
    def __init__(self):
        self.imgfile = ''
        self.color = '#000000'
    def _func(self,ls):
        for i in ls:
            #得到的是一个字节
            i = i.decode()
            if u.suffix(i) == 2:
                var.set('不支持的格式')
            else:
                self.imgfile = i
                var.set('已选择:'+self.imgfile)
    def showfile(self,event):
        cur = filedialog.askopenfilenames(filetypes=[('图片视频',tx.mat)])
        for i in cur:
            if u.suffix(i) == 2:
                var.set('不支持的格式')
            else:
                self.imgfile = i
                var.set('已选择:' + self.imgfile)
img = img()

# 全局点击事件
class winOnclick():
    def logoclick(self,event):
        tkinter.messagebox.showinfo(title='Hi', message=tx.info)
        #     图片选择器点击事件
    def colorclick(self,event):
        (rgb, hx) = cc.askcolor()
        img.color = hx
    #         复选框点击事件
    def comboboxclick(self,event):
        index = combobox.current()
        if index == 1 or index == 4:
    #         开启字符选择
            var.set('请选择目标字符,默认为 o ')
            cb.place(x=270, y=90)
        else:
            var.set('')

            cb.place_forget()
        if index == 3 or index == 6:
            var.set('请选择要转换的颜色,默认为黑白 ')
            colorlaber.place(x=370, y=80)
        else:
            colorlaber.place_forget()
    # 开始转换点击事件
    def start(self):
        path = img.imgfile
        print('imgfile:' + path)
        if path == '':
            var.set('没有选择任何文件')
            return
        else:
            Conversion(path)
wc = winOnclick()
def initWindows(w,h):
    win.title("图片视频转字符")
    # 获取屏幕宽高
    ws = win.winfo_screenwidth()
    hs = win.winfo_screenheight()
    # 计算中心点
    x = (ws - w) / 2
    y = (hs - h) / 3
    win.geometry('%dx%d+%d+%d' % (w, h, x, y))
    # win.configure(bg='#95938a')
    # 可调整大小
    win.resizable(0, 0)
initWindows(450,200)

# 文件拖拽上传区域绘制
canvas = tkinter.Canvas(win, width=500, height=300,bg=c.bg)
canvas.create_rectangle(15, 45, 255, 185, outline = c.frame,fill=c.frame)
x=130
y= 70
line = canvas.create_line(x,y-20,x,y+20,fill = c.line,dash=1)
line2 = canvas.create_line(x-20,y,x+20,y,fill = c.line,dash=1)
canvas.place(x=-3, y=-3)
tkinter.Label(win, text=tx.labermsg,bg=c.frame).place(x=50, y=100)

# 信息提示
# tkinter.Label(win,text='提示:',fg='red').place(x=40,y=10)
info = tkinter.Label(win,textvariable=var,fg=c.info,bg=c.bg)
info.place(x=55, y=10)

# 图片选择器 laber没有点击事件 通过通用事件绑定 Button-1表示左键单击事件
colorlaber = tkinter.Label(win, image = coloricon)
colorlaber.bind('<Button-1>', wc.colorclick)

# 个人logo
sabericon = tkinter.Label(win, image = sabericon)
sabericon.bind('<Button-1>', wc.logoclick)
sabericon.place(x=10, y=3)
# tkinter.Label(win,text='UID:4669').place(x=340, y=120)

# 复选框
combobox = tkinter.ttk.Combobox(win)
combobox['value'] = tx.mode
combobox.current(0)
combobox['state'] = 'readonly'
combobox.bind("<<ComboboxSelected>>",wc.comboboxclick)
combobox.place(x = 270,y = 45)

# 字符选择器
cb = tkinter.ttk.Combobox(win, width=4)
cb['value'] = tx.letter
cb.current(78)
cb['state'] = 'readonly'

# 按钮
bt1 = tkinter.Button(win, text='开始',bg=c.bt,width=22, command=wc.start)
bt1.place(x=270, y=150)
# 文件上传
windnd.hook_dropfiles(win,func=img._func)
bt2 = tkinter.Label(win, text='点击上传',bg=c.bt, width=8)
bt2.bind('<Button-1>', img.showfile)
bt2.place(x=181, y=48)


win.mainloop()
           

工具类

import os
from datetime import datetime
import cv2
import shutil

def clearFile(file):
    shutil.rmtree(file)
    os.mkdir(file)
# 返回当前时间
def getTime():
    return datetime.now().strftime("%Y%m%d_%H%M%S")
# rgb 转 字符
def getchar(r,g,b):
    # 我们定义的不重复的字符列表,灰度值小(暗)的用列表开头的符号,灰度值大(亮)的用列表末尾的符号
    ascii_char = list("MNHQ$OC67)oa+>!:+. ")
    length = len(ascii_char)
    unit = (256.0 + 1) / length
    gray = int(0.2126 * r + 0.7152 * g + 0.0722 * b)
    return ascii_char[int(gray / unit)]
# 合成视频
def createVidio(fps,var):
    img = cv2.imread('image/1.jpg')  # 读取第一张图片
    outpath = getTime()+'-color.mp4'
    imgInfo = img.shape
    size = (imgInfo[1], imgInfo[0])  # 获取图片宽高度信息
    print(size)
    #     设置视频格式 MP4
    fourcc = cv2.VideoWriter_fourcc(*"mp4v")
    videoWrite = cv2.VideoWriter(outpath, fourcc, fps, size)  # 根据图片的大小,创建写入对象 (文件名,支持的编码器,5帧,视频大小(图片大小))
    # videoWrite = cv2.VideoWriter('0.mp4',fourcc,fps,(1920,1080))
    files = os.listdir('image/')
    # 获取图片数量
    out_num = len(files)
    for i in range(0, out_num):
        fileName = 'image/' + str(i) + '.jpg'  # 循环读取所有的图片,假设以数字顺序命名
        img = cv2.imread(fileName)
        videoWrite.write(img)  # 将图片写入所创建的视频对象
        # 进度条
        per = int((i / out_num)*100)
        print('进度:'+str(per)+'%')
        var.set('正在生成视频,进度:'+str(per)+'%')
    videoWrite.release()
    var.set('完成,请在根目录中查看,帧率:'+str(fps))
    # 清除痕迹
    clearFile('image')
def suffix(path):
  suf =  path.split('.')[-1]
  if suf == 'jpg' or suf == 'png' or suf == 'jpeg':
      return 0
  if suf == 'mp4':
      return 1
  else:
      return 2
if __name__ == '__main__':
    print(str(suffix('D:\python\python.exe D:/code/Python/风格转换/main.mp4')))
           

还有一些零零碎碎的

python opencv 实现图片,视频 转 字符/字符画/字符视频
  • delete 删除的部分代码,总感觉以后有用
  • text 一些文本信息