控制台实现书记舞!

杂谈

长的帅的人可能会问了:

这玩意这么糊,你看原视频她不香吗?

对此,我郑重表示:

 

我就是闲着没事干你管得着吗?

 

 

咳咳,好了,进入正题

实现思路

  1. 首先,我们得找一个视频(这不废话吗)
  2. 将这个视频转化为一个一个的图片(用ffmepg实现)
  3. 图片转化为字符画
  4. 读入字符并输出

ffmepg

这个工具真的nb

下载地址

https://wws.lanzoui.com/i0ocMh35g2d

使用方法

在ffmepg目录下输入cmd

 

然后在命令框中输入

ffmepg -i 1.mp4 -r 60 D:\lcg\shujiwu\%d.bmp

-i 指输入路径,我这里ffmepg和.mp4文件在同一目录下,如果不在,一定要打全

-r 是帧数,也就是每秒截图张数,我这里一般是60

最后是输出文件的路径

注意:文件会非常大,要留心你的内存是否够用

(大约5个g)

图片转字符

大体思路

 首先要知道,一张图片每一个像素点的颜色是以R(red)、G(green)、B(blue)为标准的

RGB各有256级亮度(0-255)故RGB色彩最多能组合出2563 ≈1.6×107种不同的色彩

而控制台只有16种颜色,显然显示不了一张图片(比灰色还丑)

所以我们首先得把RGB图像转化为灰度图像

灰度图像即黑白图像,每个像素只有一个采样颜色,称为灰度值(Gray),灰度值取值范围也是0-255,255为白色,0为黑色

转化公式:

  1. 仅取绿色:Gray=G;
  2. 平均值法:Gray=(R+G+B)/3;
  3. 整数法:Gray=(R*30+G*59+B*11)/100
  4. 浮点法:Gray=R*0.3+G*0.59+B*0.11

这里我推荐最后一种方法,精度比较高

之后我们得把灰度值再转化为ASCII字符

虽然说ASCII字符也有256个,但是他们实在是不适合填充图像,我们只能大致得根据这些字符的颜色深浅(应该说是密集程度)去和灰度值对应,然后把灰度值映射到每一个字符上

关于这个其实我找了很久,没有找到哪位dalao写了很好的转化方法

但是最后我发现了一个自动转化的软件(Ascgen),还是挺好用的。

真的不是因为懒

Ascgen

下载链接:https://wws.lanzoui.com/iSGAoh35d2f

使用方法:

太简单了我不想讲

打开软件后点左上方批量转化就行了,这玩意挺好用的。

读入文件

我tm直接贴代码

char filename[1024];
void readData()
{
    FILE *file;
    for(int i=1;i<=NUM;i++)//遍历所有文件
    {
        sprintf(filename,"lcg\\ASCII-%d.txt",i);
     //写入文件路径到filename;

        file =fopen(filename, "r");
        if(file==NULL)
        {
            printf("%s打开失败\n",filename);
            exit(1);
        }
        else for(int j=0;j<HEIGHT;++j)
        {
            fgets(picture[i][j],sizeof(picture[i][j]),file);
            getc(file);//读入末尾换行符 
        }
        fclose(file);//非常重要!不然内存爆炸,之后的文件会打不开
    }
}

打印文本

重头戏来了!!!

 

现在我们已经把数据全部读进了数组里(picture[i][j][k]表示第i张图片第j行第j列)

如果我们用printf一行一行输出的话,显示出来画面就是一行一行加载的。

 

那么,这个时候,长得帅的人可能就会问了:这到底怎么办呢?

这就得使用一个nb的东西了:双缓冲!!!

(其实我也不是非常懂双缓冲,以下仅仅是我的理解)

首先,我们要了解什么是缓冲区

通常情况下,我们printf输出数据时,会把数据先写入缓冲区,再输出到屏幕

而我们在标准输出(stdout)的缓冲区的类型是不带缓冲。

也就是说,输出到缓冲区的同时也会把数据输出到屏幕

要知道,从缓冲区输出到屏幕的速度几乎是瞬间的,所以我们可以利用好缓冲区这玩意。

 

双缓冲

我们可以创建两个缓冲区(A、B),一个是活跃的缓冲区(假设为A),一个不活跃(B)。

我们先向B写入数据,写完之后,将B设置为活跃缓冲区,这时,B中的所有数据会自动输出到屏幕

然后再往A写入数据..........…

 

 上代码

需要头文件

#include<windows.h>

创建缓冲区

HANDLE Buf,put;
DWORD bytes; Buf
= CreateConsoleScreenBuffer( GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL ); put = CreateConsoleScreenBuffer( GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL ); //创建buf,put两个缓冲区
for(int j=0;j<HEIGHT;++j)
    WriteConsoleOutputCharacterA(hOutBuf, picture[i][j], LINE, (COORD){0,j}, &bytes);
        //将数据写入Buf缓冲区 
SetConsoleActiveScreenBuffer(hOutBuf);
//将Buf设置为活跃缓冲区

for (int j=0;j<HEIGHT;j++) WriteConsoleOutputCharacterA(hOutput,picture[i][j], LINE ,(COORD){0,j}, &bytes); //将数据写入put缓冲区 SetConsoleActiveScreenBuffer(hOutput); //将put设置为活跃缓冲区

这里解释一下WriteConsoleOutputCharacterA这个函数的参数

首先是一个缓冲区

然后是要写入的字符串

接着是字符串长度

接下来是光标的坐标

最后是一个指向DOWRD变量的指针,该变量接收实际写入的字符数

一些细节

隐藏光标

CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &CursorInfo);//设置控制台光标状态

窗口置顶

HWND hWnd;

hWnd=GetForegroundWindow();//获取当前置顶的窗口

while(1)//将窗口置顶
{
    SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}

播放音乐

播放音乐我真的找了半天,playsound()用了半天没用。

所以我这里直接用了vbs来写

Set Ws = CreateObject("Wscript.Shell")

Ws.Run("1.mp3"),0

新建一个文本文档,写入以上内容,然后重命名:1.vbs

打开vbs:

system("1.vbs");

 

下载地址(白嫖党直接点这就行)

https://wws.lanzoui.com/i1tu7h3t39g

 

posted @ 2020-10-02 15:45  Endergarten  阅读(257)  评论(0)    收藏  举报