首页 > 解决方案 > Windows - 有效地将大量数据打印到终端

问题描述

TL;DR 正在寻找一种方法来加快打印到终端的速度。

我目前正在开发一个项目,该项目基本上可以让您使用 ANSI 转义码在终端中打印出图像,(需要缩小以获得最佳效果,所以我建议使用新的 Windows 终端)

这是代码:

try:
    from PIL import Image
except:
    from Lib.PIL import Image
from time import time
from os import system
from sys import argv, stdout
from pathlib import Path


def imgToTxt(path: Path, x_ratio: int, y_ratio: int, pixel: str, do_bg: bool, stat: bool, do_prnt: bool, do_warp: bool):
    # COLOR FUNCTIONS
    fg = lambda c: f"\x1b[38;2;{str(c[0])};{str(c[1])};{str(c[2])}m"
    bg = lambda c: f"\x1b[48;2;{str(c[0])};{str(c[1])};{str(c[2])}m"
    # /COLOR FUNCTIONS

    st = time()
    prnt_pixels = list()
    _c = False

    # IMAGE DATA
    im = Image.open(path)
    pixels = im.load()
    width, height = im.size
    # / IMAGE DATA

    if stat:
        print(height, y_ratio)
        print(width, x_ratio)

    system("")
    # GENERATION LOOP
    for y in range(0, height, y_ratio):
        if do_warp:
            if _c:
                _c = False; continue
            else:
                _c = True
        for x in range(0, width, x_ratio):

            pu = pixels[x, y]
            pl = pixels[x, y + 1 if y + 1 < height else y]

            p = fg(pu) + ((bg(pl) if do_warp else bg(pu)) if do_bg else "") + pixel + "\x1b[0m"
            prnt_pixels.append(p)

        else:
            prnt_pixels.append("\n")
    # /GENERATION LOOP

    # PRINTING
    if do_prnt:
        for p in prnt_pixels:
            stdout.write(p)
    # /PRINTING

    nd = time()
    print(("time to generate", nd - st) if stat else "")
    return prnt_pixels


if __name__ == '__main__':
    argv.append("-run")
    argv.append("True")
    if "help" in argv[1]:
        print(
            """ Convert an image into text
                  \r    line commands:
                  \r        -path [PATH]    -- [PATH] full path to the image
                  \r        -xr [INT]       -- [INT] number between 1 and size of image, determines the x ratio (1 being raw)
                  \r        -yr [INT]       -- [INT] number between 1 and size of image, determines the y ratio (1 being raw)
                  \r        -pix [STR]      -- [STR] string of length 2, determines the pixel to use, default 'OO'
                  \r        -do_bg [BOOL]   -- [BOOL] boolean, determines if the background has to be filled, default 'True'
                  \r        -do_prnt [BOOL] -- [BOOL] boolean, determines if the image will be displayed, default 'True'
                  \r        -do_warp [BOOL] -- [BOOL] boolean, determines if to warp the image in return for higher quality, default 'False', WARNING this option will force the change of default pixel
                  \r        -help OR /help  -- Display this message""")

    else:
        # ALL
        if len(argv) == 1:
            argv = input(r'args \>').split(" ")
        # /ALL

        # SET TO NEXT VALUE
        if "-path" in argv:
            _ = argv.index("-path") + 1
            path = Path(argv[_])
        else:
            path = r"C:\\Windows\\Cursors\\aero_unavail_xl.cur"

        if "-xr" in argv:
            _ = argv.index("-xr") + 1
            x_ratio = int(argv[_])
        else:
            x_ratio = 1

        if "-yr" in argv:
            _ = argv.index("-yr") + 1
            y_ratio = int(argv[_])
        else:
            y_ratio = 1

        if "-pix" in argv:
            _ = argv.index("-pix") + 1
            pix = argv[_]
        else:
            pix = "00"

        # /SET TO NEXT VALUE

        # TRUE | FALSE
        if "-do_bg" in argv:
            _ = argv.index("-xr") + 1
            match argv[_].lower():
                case 'true':
                    do_bg = True
                case 'false':
                    do_bg = False
                case _:
                    raise Exception("-do_bg takes only true/false statements")
        else:
            do_bg = True

        if "-do_warp" in argv:
            _ = argv.index("-do_warp") + 1
            match argv[_].lower():
                case 'true':
                    do_warp = True
                    pix = '▀'
                case 'false':
                    do_warp = False
                case _:
                    raise Exception("-do_warp takes only true/false statements")
        else:
            do_warp = False

        if "-do_prnt" in argv:
            _ = argv.index("-do_prnt") + 1
            match argv[_].lower():
                case 'true':
                    do_prnt = True
                case 'false':
                    do_prnt = False
                case _:
                    raise Exception("-do_prnt takes only true/false statements")
        else:
            do_prnt = True

        if "-stat" in argv:
            _ = argv.index("-stat") + 1
            match argv[_].lower():
                case 'true':
                    stat = True
                case 'false':
                    stat = False
                case _:
                    raise Exception("-stat takes only true/false statements")
        else:
            stat = True

        if "-run" in argv:
            _ = argv.index("-run") + 1
            match argv[_].lower():
                case 'true':
                    run = True
                case 'false':
                    run = False
                case _:
                    raise Exception("-run takes only true/false statements")
        else:
            run = False
        # /TRUE | FALSE

        if run:
            imgToTxt(path=path, x_ratio=x_ratio, y_ratio=y_ratio, pixel=pix, do_bg=do_bg, do_prnt=do_prnt, do_warp=do_warp, stat=stat)

它的基本工作方式是获取图像的每个像素颜色数据并为颜色准备正确的 ANSI 代码; ESC[38;2;{R};{G};{B}m
然后在后期打印它

我一直在寻找一种方法来加快打印过程 IE,使用stdout,使用单独的循环来生成数据并打印它,我什至尝试使用多个线程(我知道这听起来有多愚蠢......),使用替代缓冲区,但最后我得到了速度提升的一小部分。

有什么办法,即使是疯狂的,可以显着加快这一进程?

标签: pythonwindowsstdout

解决方案


问题是你应该缓冲你的输出,不要为每个字符自己写并等待它(描述here)。一些解释也在这里

我没有拿你的完整代码,但你应该尽可能多地同时打印。改变这个:

if do_prnt:
    for p in prnt_pixels:
        stdout.write(p)

像这样:

if do_prnt:
    stdout.write("".join(prnt_pixels))

您必须在打印之前将数据与空字符串连接起来,因为您有一个列表,但您只想打印列表的数据而不打印其他列表内容。

此外,您应该查看argparse以进行参数解析


推荐阅读