python绘制字符画/字符视频

一、绘制字符画

       主要内容是将图片转化成灰度图,然后简化成像素值,然后将像素值映射成字符。

步骤:(结尾附完整代码)

1. 加载图像首先,你需要加载要转换的图像。这通常通过使用图像处理库(如Pillow)来完成。、

首需要安装必要的第三方库:如

pip install pillow

然后加载图片:

image = Image.open(123.jpg") #加载图片

2. 调整图像大小为了使字符画在终端或文本编辑器中显示得更合适,通常需要调整图像的大小。由于字符的高度通常大于宽度,因此调整后的图像高度应该比宽度小一些。

def resize_image(image, new_width=100):
    width, height = image.size
    ratio = height / width
    new_height = int(new_width * ratio * 0.5)  # 0.5 是一个经验系数,用于调整字符的高度
    resized_image = image.resize((new_width, new_height))
    return resized_image

3. 转换为灰度图将彩色图像转换为灰度图,这样每个像素只有一个亮度值,便于后续的字符映射。

def grayscale_image(image):
    return image.convert("L")

4. 将像素值转换为字符将灰度图中的每个像素值映射到一个字符。通常使用一个字符集(如 @%#*+=-:. ),其中 @ 表示最亮,. 表示最暗。

ASCII_CHARS = "@%#*+=-:. "

def pixels_to_ascii(image):
    pixels = image.getdata()
    ascii_str = "".join([
        ASCII_CHARS[pixel // 32]  # 32 是 256(灰度范围)除以字符集长度
        for pixel in pixels
    ])
    return ascii_str

5. 格式化字符画将字符字符串格式化为多行文本,以便在终端或文本编辑器中正确显示

def format_ascii_str(ascii_str, width):
    return "\n".join([ascii_str[i:i+width] for i in range(0, len(ascii_str), width)])

6. 显示或保存字符画最后,你可以将字符画显示在终端中,或者将其保存到文件中。

def main():
    image = Image.open("654.jpg") #你的图片地址
    resized_image = resize_image(image, new_width=100)
    gray_image = grayscale_image(resized_image)
    ascii_str = pixels_to_ascii(gray_image)
    formatted_ascii_str = format_ascii_str(ascii_str, resized_image.width)
    
    print(formatted_ascii_str)
    with open("ascii_image.txt", "w") as f:
        f.write(formatted_ascii_str)

if __name__ == "__main__":
    main()

完整代码:

from PIL import Image, ImageFont, ImageDraw

# 定义字符集
ASCII_CHARS = "@%#*+=-:. "

def resize_image(image, new_width=100):
    """调整图像大小"""
    width, height = image.size
    ratio = height / width
    new_height = int(new_width * ratio)
    resized_image = image.resize((new_width, new_height))
    return resized_image

def grayscale_image(image):
    """将图像转换为灰度图"""
    return image.convert("L")

def pixels_to_ascii(image):
    """使用线性插值将像素值转换为ASCII字符"""
    pixels = image.getdata()
    max_gray = 255
    min_gray = 0
    ascii_str = "".join([
        ASCII_CHARS[int(((pixel - min_gray) / (max_gray - min_gray)) * (len(ASCII_CHARS) - 1))]
        for pixel in pixels
    ])
    return ascii_str

def frame_to_ascii_image(image, new_width=100, font_size=10):
    """将一帧图像转换为ASCII字符画,并返回一个新的PIL图像"""
    resized_image = resize_image(image, new_width)
    gray_image = grayscale_image(resized_image)
    ascii_str = pixels_to_ascii(gray_image)
    img_width = gray_image.width
    ascii_str_len = len(ascii_str)
    ascii_img = "\n".join([ascii_str[index:(index + img_width)] for index in range(0, ascii_str_len, img_width)])

    # 创建一个新的空白图像,背景为白色
    font = ImageFont.truetype("arial.ttf", font_size)
    text_image = Image.new("RGB", (img_width * font_size // 2, gray_image.height * font_size // 2), color=(255, 255, 255))
    draw = ImageDraw.Draw(text_image)
    
    # 模拟抗锯齿效果
    for y, line in enumerate(ascii_img.split('\n')):
        for x, char in enumerate(line):
            position = (x * font_size // 2, y * font_size // 2)
            draw.text(position, char, fill=(0, 0, 0), font=font)

    return text_image

def main():
    # 打开图像文件
    image_path = "123.jpg"  # 替换为你的图像路径
    image = Image.open(image_path)

    # 将图像转换为ASCII字符画
    new_width = 100  # 你可以根据需要调整宽度
    font_size = 10   # 你可以根据需要调整字体大小
    ascii_image = frame_to_ascii_image(image, new_width, font_size)

    # 保存或显示ASCII字符画
    ascii_image.save("456.jpg")  # 替换为你要保存的路径
    ascii_image.show()

if __name__ == "__main__":
    main()

 效果:

二、字符视频

        流程是读取视频,逐帧处理,将每一帧转换为ASCII字符画,再将这些字符画帧组合成一个新的视频文件,最终输出的一个由ASCII字符构成的新视频。

        需要导入第三方库:

                cv2、Pillow、numpy

                同时为了方便查看视频处理进度,加一个tqdm库

这里不过多解释,直接给出代码:

import cv2
from PIL import Image, ImageFont, ImageDraw
import numpy as np
from tqdm import tqdm #加进度条

# 定义字符集
ASCII_CHARS = "@%#*+=-:.{[;'.,&^),.]} "

def resize_image(image, new_width=200): 
    """调整图像大小"""
    width, height = image.size
    ratio = height / width
    new_height = int(new_width * ratio)
    resized_image = image.resize((new_width, new_height))
    return resized_image

def grayscale_image(image):
    """将图像转换为灰度图"""
    return image.convert("L")

def pixels_to_ascii(image):
    """使用线性插值将像素值转换为ASCII字符"""
    pixels = image.getdata()
    max_gray = 255
    min_gray = 0
    ascii_str = "".join([
        ASCII_CHARS[int(((pixel - min_gray) / (max_gray - min_gray)) * (len(ASCII_CHARS) - 1))]
        for pixel in pixels
    ])
    return ascii_str

def frame_to_ascii_image(image, new_width=200, font_size=12):  
    """将一帧图像转换为ASCII字符画,并返回一个新的PIL图像"""
    resized_image = resize_image(image, new_width)
    gray_image = grayscale_image(resized_image)
    ascii_str = pixels_to_ascii(gray_image)
    img_width = gray_image.width
    ascii_str_len = len(ascii_str)
    ascii_img = "\n".join([ascii_str[index:(index + img_width)] for index in range(0, ascii_str_len, img_width)])

    # 创建一个新的空白图像,背景为白色
    font = ImageFont.truetype("arial.ttf", font_size)
    text_image = Image.new("RGB", (img_width * font_size // 2, gray_image.height * font_size // 2), color=(255, 255, 255))
    draw = ImageDraw.Draw(text_image)
    
    # 模拟抗锯齿效果
    for y, line in enumerate(ascii_img.split('\n')):
        for x, char in enumerate(line):
            position = (x * font_size // 2, y * font_size // 2)
            draw.text(position, char, fill=(0, 0, 0), font=font)

    return text_image

def video_to_ascii_video(input_path, output_path="345.mp4", new_width=200, font_size=12):  
 # 修改了默认宽度
    """将视频转换为字符画视频并保存"""
    cap = cv2.VideoCapture(input_path)
    if not cap.isOpened():
        print("Error: Could not open video.")
        return

    frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = cap.get(cv2.CAP_PROP_FPS)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    # 计算新高度
    new_height = int(new_width * frame_height / frame_width)

    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter(output_path, fourcc, fps, (new_width * font_size // 2, new_height * font_size // 2))

    with tqdm(total=total_frames, desc="Processing frames", unit="frame") as pbar:
        while True:
            ret, frame = cap.read()
            if not ret:
                break

            # 将OpenCV的BGR图像转换为PIL的RGB图像
            pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
            ascii_image = frame_to_ascii_image(pil_image, new_width, font_size)

            # 将PIL图像转换为OpenCV图像
            ascii_frame = cv2.cvtColor(np.array(ascii_image), cv2.COLOR_RGB2BGR)
            out.write(ascii_frame)

            pbar.update(1)

    cap.release()
    out.release()
    print(f"视频保存在:{output_path}")

if __name__ == "__main__":
    input_path = r"123.mp4"  # 替换为你的视频路径
    video_to_ascii_video(input_path, new_width=200, font_size=12) 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值