Creating an ASCII‑Art Video from a Celebrity Clip Using Python and OpenCV
This tutorial demonstrates how to convert a video of a celebrity into an ASCII art video using Python 3.7, OpenCV, PIL, and NumPy, covering tool setup, frame extraction, grayscale conversion, K‑means clustering for brightness mapping, character rendering, and final video assembly.
This article explains how to turn a short video (e.g., a clip of singer Cyndi Wang) into an ASCII‑art video by leveraging Python libraries such as OpenCV, Pillow (PIL), and NumPy. It starts with a brief nostalgic introduction and presents the overall workflow.
Tools required
Development IDE: PyCharm
Runtime: Python 3.7 on Windows 10
Libraries: Pillow (PIL), OpenCV (cv2), NumPy
Project effect
Project idea
The process consists of three main stages: (1) split the source video into individual frames, (2) convert each frame into an ASCII‑style image, and (3) re‑assemble the processed frames into a new video.
Step 1 – Video to frames
<code># 将视频转换为图片 并进行计数,返回总共生成了多少张图片!
def video_to_pic(vp):
# vp = cv2.VideoCapture(video_path)
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()
print('\n由视频一共生成了{}张图片!'.format(number))
os.chdir('..')
return number</code>Step 2 – Convert frames to ASCII images
<code>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))
# K‑means 聚类
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).flatten()
centroids_sorted = sorted(centroids)
centroids_index = np.array([centroids_sorted.index(v) for v in centroids])
# 亮度设置
bright = [abs((3 * i - 2 * K) / (3 * K)) for i in range(1, K+1)]
bright_bound = bright.index(np.min(bright))
# 背景阴影设置
shadow = [abs((3 * i - K) / (3 * K)) for i in range(1, K+1)]
shadow_bound = shadow.index(np.min(shadow))
labels = labels.flatten()
labels = centroids_index[labels]
# 每2*2像素挑选一个
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)
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</code>Step 3 – Assemble ASCII frames into a video
<code>def jpg_to_video(char_image_path, FPS):
video_fourcc = cv2.VideoWriter_fourcc(*"MP42") # 使用 MP42 编码器生成更小的视频文件
char_img_path_list = [char_image_path + r'/{}.jpg'.format(i) for i in range(1, number+1)]
char_img_test = Image.open(char_img_path_list[1]).size # 获取分辨率
if not os.path.exists('video'):
os.mkdir('video')
video_writter = cv2.VideoWriter('video/new_char_video.avi', video_fourcc, FPS, char_img_test)
sum = len(char_img_path_list)
count = 0
for image_path in char_img_path_list:
img = cv2.imread(image_path)
video_writter.write(img)
count += 1
process_bar(count / sum, start_str='', end_str='100%', total_length=15)
video_writter.release()
print('\n=======================')
print('The video is finished!')
print('=======================')</code>Simple source sharing
<code># from platypus import
import os
from PIL import Image, ImageFont, ImageDraw
import cv2
import random
import numpy as np
import threading
# Main workflow
if __name__ == '__main__':
video_path = '王心凌.mp4'
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)
str_img = img2strimg(img)
cv2.imwrite("cache_char/{}.jpg".format(i), str_img)
FPS = vp.get(cv2.CAP_PROP_FPS)
jpg_to_video('cache_char', FPS)</code>The article concludes with a reminder that the generated video can become large if a high frame rate is used, and provides links to related Python tutorials.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.