之前就一直很羡慕大佬能制作出用字符跑bad apple的项目,一直很想试试。最近突然想到了,于是动手自己实操了一番。
项目里的很多代码都是借鉴了大佬们的,也感谢很多大佬的博客讲的很详细。
主要借鉴了以下几个大佬的博客:
https://blog.csdn.net/clauyiye/article/details/78995219?spm=1001.2014.3001.5501
https://www.cnblogs.com/CodeMIRACLE/p/5508236.html
我的大部分操作是借鉴大佬的,有一些操作是找到了一些更快捷的方式替代了。
下面将介绍具体的操作:
视频可以自备,如果不知道怎么下,可以点
https://www.bilibili.com/video/BV1xx411c79H?from=search&seid=6687738456694002600&spm_id_from=333.337.0.0
然后在www.后面加个i就可以跳转下载了,它会要求下个下载器,觉得麻烦的可以通过其他方式获得视频。
我看几个大佬推荐的是用OpenCV来将视频处理成一帧帧的图片的,我觉得太麻烦了,安装个OpenCV就花了我许多时间,还不会用。另一个大佬用的是kmplayer或者potplayer,potplayer我电脑上有,但我打开的时候要更新就想让它更新了,我担心截到的帧的格式不对,又去下了个格式工厂,发现里面直接就可以将图片截成帧。
我立刻就将视频导了进去,帧的间隔建议设为0.1s,如果太小了,截下来的会非常多,特别占空间,即使如此,我也截了两千多张,好像有1.2g,空间不足就别逞强了。
然后截好就存在一个新项目文档里,建议在项目文档里建个文件夹命名为p(为啥叫这个,因为代码里引用的是这个,为了有些同学看不懂代码又想自己实现一遍,你只要按步操作就行),如图所示
上代码!!!
下面代码,你需要改一些地方,具体看注释
#include
#include
#include
#include
using namespace std;
int32_t width,height;
RGBQUAD *pixels;
bool OpenBitmap(char const *filename)
{
FILE *file = fopen(filename, "rb");
if (file)
{
width=0;
height=0;
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
fread(&bf, sizeof(bf), 1, file);
fread(&bi, sizeof(bi), 1, file);
if(bi.biBitCount!=24)
return false;
if(bi.biCompression!=BI_RGB)
return false;
width=bi.biWidth;
height=bi.biHeight;
pixels=new RGBQUAD[width*height];
uint32_t rowSize = (bi.biBitCount * width + 31) / 32 * 4;
uint8_t *line = new uint8_t[rowSize];
for (int y = 0; y < height; y++)
{
fread(line, rowSize, 1, file);
for (int x = 0; x < width; x++)
{
uint8_t *color = line + x * 3;
RGBQUAD *pixel = &pixels[(height-y-1) * width+x];
pixel->rgbBlue = color[0];
pixel->rgbGreen = color[1];
pixel->rgbRed = color[2];
}
}
delete[] line;
fclose(file);
return true;
}
return false;
}
RGBQUAD GetColor(int x, int y, int w, int h)
{
int r = 0, g = 0, b = 0;
for (int i = 0; i < w; i++)
{
if (i + x >= width) continue;
for (int j = 0; j < h; j++)
{
if (j + y >= height) continue;
RGBQUAD const& color = pixels[(y + j) * width + (x + i)];
r += color.rgbRed;
g += color.rgbGreen;
b += color.rgbBlue;
}
}
return RGBQUAD{r / (w * h), g / (w * h),b / (w * h)};
}
char ColorToCharacter(RGBQUAD const& color)
{
int brightness = (color.rgbRed + color.rgbGreen + color.rgbBlue) / 3;
static char const *characters = "Qdogc*;:-. ";
int len = strlen(characters);
int span = 0xFF / len;
int cidx = brightness / span;
if (cidx == len)
cidx--;
return characters[cidx];
}
void OutputAscii(const char* filename, int w, int h)
{
FILE *file=fopen(filename,"a+");
int x = width / w;
int y = height / h;
for (int i = 0; i < height; i += y)
{
for (int j = 0; j < width; j += x)
{
RGBQUAD color = GetColor(j, i, x, y);
fprintf(file, "%c", ColorToCharacter(color));
}
fprintf(file, "\n");
}
delete [] pixels;
fclose(file);
}
int main()
{
char filename[1024];
printf("转换中.....");
for(int i=1;i<=2173;i++)//将这里的i改为你p文件夹下图片的数量
{
sprintf(filename,"p/image%d.bmp",i);//这里是读入图片,如果图片的命名不是image+数字,那就自己根据具体命名改下
if(OpenBitmap(filename));
OutputAscii("badapple.txt",width/6,height/12);//输出字符文档
}
printf("转换完成!");
}
该程序的存放位置要和p文件夹在同一个文件夹下。
接下来有了txt文档,我们就可以播放了
#include
#include
struct fps_limit {
int previous_time;
int tpf_limit;
int tpf;
fps_limit(int fps = 60) : previous_time(GetTickCount()), tpf(0) {
limit_fps(fps);
}
void reset() {
previous_time = GetTickCount(),
tpf = 0;
tpf_limit = 60;
}
void limit_fps(int fps) {
tpf_limit = (int)(1000.0f / (float)fps);
}
void delay() {
tpf = GetTickCount() - previous_time;
if (tpf < tpf_limit)
Sleep(tpf_limit - tpf - 1);
previous_time = GetTickCount();
}
};
int main()
{
FILE* fp = fopen("badapple.txt", "r");
char buf[2048];
fps_limit fps(20);//画面刷新帧率,不同设备最适不同,需要慢慢试
while (!feof(fp))
{
HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;//定义光标起始点
pos.X = 0;
pos.Y = 10;
SetConsoleCursorPosition(hConsoleOutput, pos);
for (int i = 0; i<32; i++) //这里调整的是整个画面的刷新频率,具体啥值播放最流畅需要自己慢慢试
{
fgets(buf, 2048, fp);
printf("%s", buf);
}
fps.delay();
}
return 0;
}
该程序也要和txt文档放在同一个文件夹下,播放时如果不流畅,需要自己慢慢挑最适的fps还有刷新频率。
整个程序,如果有人想要的话,可以私信我,我发给你。
另外,再次感谢大佬们的博客,让我能自己对着博客实现了这样一个有趣的项目。