python制作字符画视频来啦~

简介: python制作字符画视频来啦~

字符画是什么?


       字符画是一系列字符的组合,我们可以把字符看作是比较大块的像素,一个字符能表现一种颜色,字符的种类越多,可以表现的颜色也越多,图片也会更有层次感。 如果我们想要手工绘制出字符画,首先要有扎实的美术基础,其次还要花费大量的时间和精力。但是我们可以使用Python,只需要几行代码,就能够将一张图片轻而易举地转化为一个字符画。


       那么我们能将一张图片转化为一个字符画,我们能否将一个视频转化为一个字符画视频呢?


答案当然可以的啦!视频不就是由一张张图片组成的


文章结尾附完整代码

项目效果展示


首先我们要准备自己要转化的视频

导入本次项目需要的包

import os
from PIL import Image
import cv2
import random
import numpy as np

接着将视频转化为图片

# 将视频转换为图片 并进行计数
def video_to_pic(vp):
    number = 0
    if vp.isOpened():
        r, frame = vp.read()
        if not os.path.exists('cache_pic'):
            os.mkdir('cache_pic')
        os.chdir('cache_pic')
    else:
        r = False
    while r:
        number += 1
        cv2.imwrite(str(number) + '.jpg', frame)
        r, frame = vp.read()
    os.chdir("..")
    return number

效果:


接着将我们转化的图片在转化为字符画

def img2strimg(frame, K=3):
    # 读取矩阵的长度 有时返回两个值,有时三个值
    height, width, *_ = frame.shape
    # 颜色空间转化 图片对象, 灰度处理
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # 转换数据类型,将数据降维
    frame_array = np.float32(frame_gray.reshape(-1))
    # 得到labels(类别)、centroids(矩心) compactness(密度值)。
    # 如第一行6个像素labels=[0,2,2,1,2,0],则意味着6个像素分别对应着 第1个矩心、第3个矩心、第3、2、3、1个矩心。
    compactness, labels, centroids = cv2.kmeans(frame_array, K, None, (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0), 10, cv2.KMEANS_RANDOM_CENTERS)
    centroids = np.uint8(centroids)  # 转换成整形
    # labels的数个矩心以随机顺序排列,所以需要简单处理矩心.
    # 返回一个折叠成一维的数组
    centroids = centroids.flatten()
    # 排序
    centroids_sorted = sorted(centroids)
    # 获得不同centroids的明暗程度,0最暗
    centroids_index = np.array([centroids_sorted.index(value) for value in centroids])
    # 亮度设置
    bright = [abs((3 * i - 2 * K) / (3 * K)) for i in range(1, 1 + K)]
    bright_bound = bright.index(np.min(bright))
    # 背景阴影设置
    shadow = [abs((3 * i - K) / (3 * K)) for i in range(1, 1 + K)]
    shadow_bound = shadow.index(np.min(shadow))
    # 返回一个折叠成一维的数组
    labels = labels.flatten()
    # 将labels转变为实际的明暗程度列表,0最暗。
    labels = centroids_index[labels]
    # 列表解析,每2*2个像素挑选出一个,组成(height*width*灰)数组。
    labels_picked = [labels[rows * width:(rows + 1) * width:2] for rows in range(0, height, 2)]
    canvas = np.zeros((3 * height, 3 * width, 3), np.uint8)
    canvas.fill(255)  # 创建长宽为原图三倍的白色画布。
    # 因为 字体大小为0.45时,每个数字占6*6个像素,而白底画布为原图三倍
    # 所以 需要原图中每2*2个像素中挑取一个,在白底画布中由6*6像素大小的数字表示这个像素信息。
    y = 0
    for rows in labels_picked:
        x = 0
        for cols in rows:
            if cols <= shadow_bound:
                # 添加文字  图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细
                cv2.putText(canvas, str(random.randint(2, 9)), (x, y), cv2.FONT_HERSHEY_PLAIN, 0.45, 0.1)
            elif cols <= bright_bound:
                cv2.putText(canvas, "-", (x, y),cv2.FONT_HERSHEY_PLAIN, 0.4, 0, 1)
            x += 6
        y += 6
    return canvas

效果:


最后我们将所有的字符画合成为MP4视频

def charts2video(img_path, video_path):
    """将给定目录下的图片转成视频
    Args:
        img_path: 图片路径
        video_path: 输出视频的路径和名称
    Returns: 图片转成的视频
    """
    images = os.listdir(img_path)
    images.sort(key=lambda x: int(x[:-4]))  # 以名称字符串的数字从小到大排序  
    fps = 30  # 帧数
    fourcc = cv2.VideoWriter_fourcc('M', 'P', '4', 'V')
    im = Image.open(img_path + images[0])
    video_writer = cv2.VideoWriter(video_path, fourcc, fps, im.size)
    for img_i in images:
        frame = cv2.imread(img_path + img_i)
        print('开始将 ' + img_i + ' 加入视频\n')
        video_writer.write(frame)  # 注意:图片尺寸必须和视频尺寸一样,不然不会被加入视频中!!!
    video_writer.release()

当然还差一个我们的主函数

if __name__ == '__main__':
    video_path = 'xiaoye.mp4'
    save_pic_path = 'cache_pic'
    save_charpic_path = 'cache_char/'
    vp = cv2.VideoCapture(video_path)
    number = video_to_pic(vp)
    for i in range(1, number):
        fp = r"cache_pic/{}.jpg".format(i)
        img = cv2.imread(fp)  # 返回图片数据 (高度, 宽度,通道数)
        print(img)
        # 若字符画结果不好,可以尝试更改K为3。若依然无法很好地表现原图,请换图尝试。 -_-||
        str_img = img2strimg(img)
        cv2.imwrite("cache_char/{}.jpg".format(i), str_img)
        FPS = vp.get(cv2.CAP_PROP_FPS)
        star_to_char(number, save_pic_path)
    charts2video(save_charpic_path,'char_video.mp4')

其中 video_path就是我们需要转化的视频的名字,最后一行的'char_video.mp4'就是我们最后要保存字符画视频的名字,这个自己随便起,其他的不用修改即可使用

最后的文件目录是这样的


最最最后就是本次项目的完整代码:

import os
from PIL import Image
import cv2
import random
import numpy as np
# 将视频转换为图片 并进行计数
def video_to_pic(vp):
    number = 0
    if vp.isOpened():
        r, frame = vp.read()
        if not os.path.exists('cache_pic'):
            os.mkdir('cache_pic')
        os.chdir('cache_pic')
    else:
        r = False
    while r:
        number += 1
        cv2.imwrite(str(number) + '.jpg', frame)
        r, frame = vp.read()
    os.chdir("..")
    return number
def star_to_char(number, save_pic_path):
    if not os.path.exists('cache_char'):
        os.mkdir('cache_char')
def img2strimg(frame, K=3):
    # 读取矩阵的长度 有时返回两个值,有时三个值
    height, width, *_ = frame.shape
    # 颜色空间转化 图片对象, 灰度处理
    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # 转换数据类型,将数据降维
    frame_array = np.float32(frame_gray.reshape(-1))
    # 得到labels(类别)、centroids(矩心) compactness(密度值)。
    # 如第一行6个像素labels=[0,2,2,1,2,0],则意味着6个像素分别对应着 第1个矩心、第3个矩心、第3、2、3、1个矩心。
    compactness, labels, centroids = cv2.kmeans(frame_array, K, None, (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0), 10, cv2.KMEANS_RANDOM_CENTERS)
    centroids = np.uint8(centroids)  # 转换成整形
    # labels的数个矩心以随机顺序排列,所以需要简单处理矩心.
    # 返回一个折叠成一维的数组
    centroids = centroids.flatten()
    # 排序
    centroids_sorted = sorted(centroids)
    # 获得不同centroids的明暗程度,0最暗
    centroids_index = np.array([centroids_sorted.index(value) for value in centroids])
    # 亮度设置
    bright = [abs((3 * i - 2 * K) / (3 * K)) for i in range(1, 1 + K)]
    bright_bound = bright.index(np.min(bright))
    # 背景阴影设置
    shadow = [abs((3 * i - K) / (3 * K)) for i in range(1, 1 + K)]
    shadow_bound = shadow.index(np.min(shadow))
    # 返回一个折叠成一维的数组
    labels = labels.flatten()
    # 将labels转变为实际的明暗程度列表,0最暗。
    labels = centroids_index[labels]
    # 列表解析,每2*2个像素挑选出一个,组成(height*width*灰)数组。
    labels_picked = [labels[rows * width:(rows + 1) * width:2] for rows in range(0, height, 2)]
    canvas = np.zeros((3 * height, 3 * width, 3), np.uint8)
    canvas.fill(255)  # 创建长宽为原图三倍的白色画布。
    # 因为 字体大小为0.45时,每个数字占6*6个像素,而白底画布为原图三倍
    # 所以 需要原图中每2*2个像素中挑取一个,在白底画布中由6*6像素大小的数字表示这个像素信息。
    y = 0
    for rows in labels_picked:
        x = 0
        for cols in rows:
            if cols <= shadow_bound:
                # 添加文字  图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细
                cv2.putText(canvas, str(random.randint(2, 9)), (x, y), cv2.FONT_HERSHEY_PLAIN, 0.45, 0.1)
            elif cols <= bright_bound:
                cv2.putText(canvas, "-", (x, y),cv2.FONT_HERSHEY_PLAIN, 0.4, 0, 1)
            x += 6
        y += 6
    return canvas
def charts2video(img_path, video_path):
    """将给定目录下的图片转成视频
    Args:
        img_path: 图片路径
        video_path: 输出视频的路径和名称
    Returns: 图片转成的视频
    """
    images = os.listdir(img_path)
    images.sort(key=lambda x: int(x[:-4]))  # 以名称字符串的数字从小到大排序  
    fps = 30  # 帧数
    fourcc = cv2.VideoWriter_fourcc('M', 'P', '4', 'V')
    im = Image.open(img_path + images[0])
    video_writer = cv2.VideoWriter(video_path, fourcc, fps, im.size)
    for img_i in images:
        frame = cv2.imread(img_path + img_i)
        print('开始将 ' + img_i + ' 加入视频\n')
        video_writer.write(frame)  # 注意:图片尺寸必须和视频尺寸一样,不然不会被加入视频中!!!
    video_writer.release()
if __name__ == '__main__':
    video_path = 'xiaoye.mp4'
    save_pic_path = 'cache_pic'
    save_charpic_path = 'cache_char/'
    vp = cv2.VideoCapture(video_path)
    number = video_to_pic(vp)
    for i in range(1, number):
        fp = r"cache_pic/{}.jpg".format(i)
        img = cv2.imread(fp)  # 返回图片数据 (高度, 宽度,通道数)
        print(img)
        # 若字符画结果不好,可以尝试更改K为3。若依然无法很好地表现原图,请换图尝试。 -_-||
        str_img = img2strimg(img)
        cv2.imwrite("cache_char/{}.jpg".format(i), str_img)
        FPS = vp.get(cv2.CAP_PROP_FPS)
        star_to_char(number, save_pic_path)
    charts2video(save_charpic_path,'char_video.mp4')

其中cv2使用pip install opencv安装


目录
打赏
0
0
0
0
38
分享
相关文章
小白学python-深入解析一位字符判定算法
小白学python-深入解析一位字符判定算法
69 0
【01】python开发之实例开发讲解-如何获取影视网站中经过保护后的视频-用python如何下载无法下载的视频资源含m3u8-python插件之dlp-举例几种-详解优雅草央千澈
【01】python开发之实例开发讲解-如何获取影视网站中经过保护后的视频-用python如何下载无法下载的视频资源含m3u8-python插件之dlp-举例几种-详解优雅草央千澈
112 34
【01】python开发之实例开发讲解-如何获取影视网站中经过保护后的视频-用python如何下载无法下载的视频资源含m3u8-python插件之dlp-举例几种-详解优雅草央千澈
Python实用记录(一):如何将不同类型视频按关键帧提取并保存图片,实现图片裁剪功能
这篇文章介绍了如何使用Python和OpenCV库从不同格式的视频文件中按关键帧提取图片,并展示了图片裁剪的方法。
131 0
深入解析:使用Python爬取Bilibili视频
本文介绍了如何使用Python编写脚本自动化下载Bilibili视频。通过requests等库获取视频和音频URL,使用ffmpeg合并音视频文件,最终实现高效下载。注意遵守网站爬虫政策和法律法规。
588 4
python将照片集变成视频
`shigen`是一位坚持更新文章的博客作者,记录成长历程,分享认知见解,留住生活感动。他利用Python库`Pillow`和`MoviePy`开发了一个工具,能够批量处理照片并生成高质量视频。该工具支持多种分辨率、自定义播放时间和照片方向,并能自动调整照片比例以实现居中对齐。通过简单的代码实现了照片视频化的需求,适合强迫症患者使用。**与shigen一起,每天不一样!**个人IP:shigen。
84 9
python将照片集变成视频
[oeasy]python0041_输出ASCII码表_英文字符编码_键盘字符_ISO_646
本文介绍了ASCII码表的生成与使用,包括英文字符、数字和符号的编码。通过Python代码遍历0到127的ASCII值,解决了找不到竖线符号的问题,并解释了ASCII码的固定映射关系及其重要性。文章还介绍了ASCII码的历史背景,以及它如何成为国际标准ISO 646。最后,通过安装`ascii`程序展示了完整的ASCII码表。
36 1
|
5月前
|
python获取字符串()里面的字符
在Python中,如果你想获取字符串中括号(比如圆括号`()`、方括号`[]`或花括号`{}`)内的字符,你可以使用正则表达式(通过`re`模块)或者手动编写代码来遍历字符串并检查字符。 这里,我将给出使用正则表达式的一个例子,因为它提供了一种灵活且强大的方式来匹配复杂的字符串模式。 ### 使用正则表达式 正则表达式允许你指定一个模式,Python的`re`模块可以搜索字符串以查找匹配该模式的所有实例。 #### 示例:获取圆括号`()`内的内容 ```python import re def get_content_in_parentheses(s): # 使用正则表达
155 36
python知识点100篇系列(18)-解析m3u8文件的下载视频
【10月更文挑战第6天】m3u8是苹果公司推出的一种视频播放标准,采用UTF-8编码,主要用于记录视频的网络地址。HLS(Http Live Streaming)是苹果公司提出的一种基于HTTP的流媒体传输协议,通过m3u8索引文件按序访问ts文件,实现音视频播放。本文介绍了如何通过浏览器找到m3u8文件,解析m3u8文件获取ts文件地址,下载ts文件并解密(如有必要),最后使用ffmpeg合并ts文件为mp4文件。
109 1
[oeasy]python040_缩进几个字符好_输出所有键盘字符_循环遍历_indent
本文探讨了Python代码中的缩进问题。通过研究`range`函数和`for`循环,发现缩进对于代码块的执行至关重要。如果缩进不正确,程序会抛出`IndentationError`。文章还介绍了Python的PEP8规范,推荐使用4个空格进行缩进,并通过示例展示了如何使用Tab键实现标准缩进。最后,通过修改代码,输出了从0到122的字符及其对应的ASCII码值,但未能找到竖线符号(`|`)。文章在总结中提到,下次将继续探讨竖线符号的位置。
46 0

热门文章

最新文章