控制台实现书记舞!
杂谈
长的帅的人可能会问了:
这玩意这么糊,你看原视频她不香吗?
对此,我郑重表示:
我就是闲着没事干你管得着吗?
咳咳,好了,进入正题
实现思路
- 首先,我们得找一个视频(这不废话吗)
- 将这个视频转化为一个一个的图片(用ffmepg实现)
- 图片转化为字符画
- 读入字符并输出
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为黑色
转化公式:
- 仅取绿色:Gray=G;
- 平均值法:Gray=(R+G+B)/3;
- 整数法:Gray=(R*30+G*59+B*11)/100
- 浮点法: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

浙公网安备 33010602011771号