Windows下控制台播放Badapple,opencv的使用,以及代码优化方案

这篇具有很好参考价值的文章主要介绍了Windows下控制台播放Badapple,opencv的使用,以及代码优化方案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Windows下控制台播放Badapple

环境准备

  • VS2022编译环境
  • Opencv(对图像进行灰度处理)的配置 可以看我写的这篇文章vs下opencv的配置
  • 素材(Badpple的视频文件) 可以私信我
  • FFmpeg(对视频文件进行处理) 让视频文件的声音分离出来生成mp3文件

Windows下控制台播放Badapple,opencv的使用,以及代码优化方案,windows,opencv,c++

ffmpeg -i test.mp4 -map 0:v:0 -c copy test_video.mp4 -map 0:a:0 -c copy test_audio.mp3

关于ffmpeg的音视频分离就到这里,不做赘述。

编写流程

该程序首先使用VideoCapture打开一个视频文件,并获取视频的特征信息,如帧数、帧率和帧大小。然后,它根据设定的抽样大小将每一帧转换为字符画,并保存到内存中。

接下来,程序使用Windows API设置控制台窗口的大小和光标位置,并隐藏光标显示。然后,它从内存中读取字符画,并在控制台中连续输出,实现视频的字符化播放效果。

最后,该程序使用mciSendString函数播放背景音乐,并通过无限循环不断播放字符化视频,直到用户手动停止程序

main()

int main(void)
{  
	Init();
	readData();
	init2();
	play();
	return 0;
}

Init()

Init() 函数主要用于设置控制台窗口的大小、隐藏光标,并调整控制台窗口的缓冲区,为后续字符化视频的播放做准备。

int Init()
{

	SetConsoleTitle(L"坏苹果-M");//设置控制台窗口的标题为 "坏苹果-M"。
	sprintf_s(cmd, sizeof(cmd), "mode con cols=%d lines=%d", width, height);//控制台窗口的大小设置为 width 列和 height 行,并将命令保存在 cmd 字符数组中
	system(cmd);//行上一步生成的命令,从而改变控制台窗口的大小
	//隐藏光标
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);//使用Windows API函数 GetStdHandle() 获取标准输出流的句柄,并将其保存在变量 hOut 中
	CONSOLE_CURSOR_INFO info{ 1,0 };//将其成员 dwSize 设置为 1,使光标变得不可见。
	SetConsoleCursorInfo(hOut, &info);//将 hOut 和 info 作为参数,从而隐藏控制台窗口中的光标。

	COORD co = { width,height};//将其成员 X 设置为 width,将其成员 Y 设置为 height。
	SetConsoleCursorInfo(hOut, &info);
	SetConsoleScreenBufferSize(hOut,co);		//置控制台屏幕缓冲区的大小为 co
	SMALL_RECT rc = { 0, 0, width - 1, height - 1 };	//重要点:设置缓冲,消除闪烁
	SetConsoleWindowInfo(hOut, TRUE, &rc);							//将 hOut、TRUE 和 rc 作为参数,以设置控制台窗口的大小和位置。

	if (ret == false)
	{
		printf("视频文件打开失败!\n");
		exit(-1);
	}

	if (data == NULL)
	{
		printf("内存不足!\n");
		return 1;
	}

}

这里是对代码中的 Init() 函数进行分析:

  1. 首先,该函数使用 SetConsoleTitle() 设置控制台窗口的标题为 “坏苹果-M”。
  2. 然后,通过 sprintf_s() 函数将控制台窗口的大小设置为 width 列和 height 行,并将命令保存在 cmd 字符数组中。
  3. 使用 system() 函数执行上一步生成的命令,从而改变控制台窗口的大小。
  4. 接下来,使用Windows API函数 GetStdHandle() 获取标准输出流的句柄,并将其保存在变量 hOut 中。
  5. 创建一个 CONSOLE_CURSOR_INFO 结构体变量 info,并将其成员 dwSize 设置为 1,使光标变得不可见。
  6. 调用 SetConsoleCursorInfo() 函数,将 hOutinfo 作为参数,从而隐藏控制台窗口中的光标。
  7. 创建一个 COORD 结构体变量 co,将其成员 X 设置为 width,将其成员 Y 设置为 height
  8. 再次调用 SetConsoleCursorInfo() 函数,将 hOutinfo 作为参数,从而设置控制台屏幕缓冲区的大小为 co
  9. 创建一个 SMALL_RECT 结构体变量 rc,将其成员 Left 设置为 0,将其成员 Top 设置为 0,将其成员 Right 设置为 width - 1,将其成员 Bottom 设置为 height - 1
  10. 最后,调用 SetConsoleWindowInfo() 函数,将 hOutTRUErc 作为参数,以设置控制台窗口的大小和位置。

总结起来,Init() 函数主要用于设置控制台窗口的大小、隐藏光标,并调整控制台窗口的缓冲区,为后续字符化视频的播放做准备

代码优化

int Init()
{
    SetConsoleTitle(L"坏苹果-M");
    sprintf_s(cmd, sizeof(cmd), "mode con cols=%d lines=%d", width, height);
    system(cmd);

    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO info{ 1,0 };
    SetConsoleCursorInfo(hOut, &info);

    COORD co = { width,height };
    SetConsoleScreenBufferSize(hOut, co);
    SMALL_RECT rc = { 0, 0, width - 1, height - 1 };
    SetConsoleWindowInfo(hOut, TRUE, &rc);

    // 错误处理改为异常抛出
    if (ret == false)
    {
        throw std::runtime_error("视频文件打开失败!");
    }

    if (data == nullptr)
    {
        throw std::bad_alloc();
    }

    

   
}

视频信息的处理

VideoCapture video;	
Mat frameImg, grayImg;
bool ret = video.open("./apple.mp4");

// 读取视频特征信息
int framecount = video.get(CV_CAP_PROP_FRAME_COUNT); //视频文件的总帧数
int fps = video.get(CV_CAP_PROP_FPS); //帧率
int cols = video.get(CV_CAP_PROP_FRAME_WIDTH); //视频流中的帧宽
int rows = video.get(CV_CAP_PROP_FRAME_HEIGHT);//视频流中的帧高

// 定义抽样大小
int hSize = 10; // 每 10 行,每 5 列,转换为 1 个字符
int wSize = 5;

// 定义字符集合 
char charImgs[] = " .,-'`:!1&@#$";

int height = rows / hSize;
int width = cols / wSize;
char cmd[128];

int countpersent = 1;

// 待优化为指针数组 
int frameSize = height * (width + 1) + 1; //每行的末尾有一个回车符
char* data = (char*)malloc(sizeof(char) * framecount * frameSize);

readdata()

void readData()
{
	sprintf_s(cmd, sizeof(cmd), "mode con cols=%d lines=%d", 22, 4);//
	system(cmd);//设置控制台大小
	 
		for (int n = 0; n < framecount; n++)
		{
				char* p = data + n * frameSize; video.read(frameImg); // 转换图片的色彩,这里转换为灰度效果
				cvtColor(frameImg, grayImg, COLOR_BGR2GRAY);//opencv 函数灰度图片
				string s = "";
				int k = 0;
				for (int row = 0; row < rows - hSize; row = row + hSize)
				{
						for (int col = 0; col < cols - wSize; col = col + wSize)
							 
						{
							int value = grayImg.at<uchar>(row, col);
							p[k++] = charImgs[int(value / 20)];	
						}
							p[k++] = '\n';
						
				}
							p[k++] = 0;
							 system("cls");
            printf("正在读取:%d / %d", n + 1, framecount);  					 						 						 													 				 						
		}	 	 
	mciSendString(L"play apple.mp3 repeat", 0, 0, 0);
			 
}

  1. 使用 sprintf_s() 函数将命令字符串 cmd 格式化为设置控制台窗口大小和行数的命令。
  2. 使用 system() 函数执行上述生成的命令,从而改变控制台窗口的大小和行数。
  3. 在每一帧循环中,首先声明一个指针 p 并将其初始化为指向当前帧的字符画数据的位置。
  4. 使用 video.read(frameImg) 从视频读取一帧图像,并将其转换为灰度图像。
  5. 在双重循环中,将灰度图像中的像素值转换为字符,并将字符保存到指针 p 指向的位置。每个字符的值通过除以20来映射到 charImgs 数组中。
  6. 在内层循环结束后,添加换行符 \n 到字符画中,以分隔每一行的字符。
  7. 在外层循环结束后,添加空字符 \0 到字符画末尾,以标识字符画的结尾。
  8. 使用 system("cls") 清空控制台屏幕。
  9. 使用 printf() 打印正在读取的帧数和总帧数。
  10. 最后,使用 mciSendString() 函数播放背景音乐 “apple.mp3” 并设置循环播放。

代码优化

void readData()
{
	sprintf_s(cmd, sizeof(cmd), "mode con cols=%d lines=%d", 22, 4);
	system(cmd);

	// 预分配内存
	char** data = new char*[framecount];
	for (int n = 0; n < framecount; n++)
	{
		data[n] = new char[frameSize];
	}

#pragma omp parallel for
	for (int n = 0; n < framecount; n++)
	{
		char* p = data[n];

		Mat frameImg;
		video.read(frameImg);
		cvtColor(frameImg, grayImg, COLOR_BGR2GRAY);

		string s = "";
		int k = 0;
		for (int row = 0; row < rows - hSize; row = row + hSize)
		{
			for (int col = 0; col < cols - wSize; col = col + wSize)
			{
				int value = grayImg.at<uchar>(row, col);
				p[k++] = charImgs[int(value / 20)];
			}
			p[k++] = '\n';
		}
		p[k++] = 0;

#pragma omp critical
	{
		system("cls");
		printf("正在读取:%d / %d", n + 1, framecount);
	}

	}

	mciSendString(L"play apple.mp3 repeat", 0, 0, 0);

	// 释放内存
	for (int n = 0; n < framecount; n++) {
		delete[] data[n];
	}
	delete[] data;
}

这里使用了OpenMP来并行处理每一帧的转换过程,以提高性能。使用#pragma omp parallel for将循环并行化,从而使每个线程独立处理一帧的转换操作。

另外,内存分配和释放的操作被提到循环外部,通过预分配内存避免了在每一帧循环中的重复动态内存分配和释放操作。最后,在关键的控制台输出部分使用了临界区(#pragma omp critical)来保证多个线程之间的输出不会互相干扰。

这些优化措施可以显著提高程序的运行速度。然而,请注意在实际应用中进行测试和评估,以确保其在特定环境下的有效性。

Init2()

void init2()
{
	sprintf_s(cmd, sizeof(cmd), "mode con cols=%d lines=%d", width, height);
	system(cmd);
}

paly()

函数优化

void play()
{
    for (int i = 0; i < framecount; i++)
    {
        char* p = data + i * frameSize;
        SetConsoleCursorPosition(h, pos); // 跳转控制台光标位置
        
        // 使用 WriteConsoleOutputCharacterA() 直接写入控制台屏幕缓冲区
        DWORD written;
        WriteConsoleOutputCharacterA(h, p, frameSize , pos, &written);
        
        Sleep(1000 / fps);
    }
}

在优化后的代码中:

使用 WriteConsoleOutputCharacterA() 函数直接将帧数据 p 写入控制台屏幕缓冲区,而不是使用 printf() 函数。这样可以提高输出的效率。

删除了外部的 while 循环,因为循环内的逻辑已经包含了需要重复播放的功能。每次迭代都会播放一帧,并通过 Sleep() 函数等待 1000 / fps 毫秒,以模拟视频的帧率。

请注意,在使用该函数之前,请确保已经定义和初始化了相关变量(如 framecountdataframeSizehpos),并且已经设置了合适的 fps 值。

指针数组优化

	// 待优化为指针数组 
	int frameSize = height * (width + 1) + 1; //每行的末尾有一个回车符
	char* data = (char*)malloc(sizeof(char) * framecount * frameSize);

要将上述代码优化为使用指针数组,您需要进行以下修改:

  1. 修改 data 的声明:将 char* data 改为 char** data,用于存储指向字符画的指针。
  2. 动态分配内存给 data:替换 char* data = (char*)malloc(sizeof(char) * framecount * frameSize);char** data = new char*[framecount],这将创建一个指针数组,每个指针指向一帧的字符画。
  3. 在循环中为每帧分配内存并保存字符画:在 for (int n = 0; n < framecount; n++) 循环开始之前添加 data[n] = new char[frameSize]; 来为每一帧分配内存。
  4. 将字符画保存到对应的指针位置:在 char* p = data + n * frameSize; 处将 p 更改为 char* p = data[n];
  5. 在程序结束前释放内存:在 main() 函数的最后使用循环来释放为每帧字符画分配的内存,例如:
for (int n = 0; n < framecount; n++) {
    delete[] data[n];
}
delete[] data;

通过上述修改,您将能够将字符画以指针数组的形式保存,并正确释放内存。请记得适时进行错误处理和异常处理。

需要代码以及素材或者我的opencv可以私信我文章来源地址https://www.toymoban.com/news/detail-544847.html

到了这里,关于Windows下控制台播放Badapple,opencv的使用,以及代码优化方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何循环执行windows和linux上的控制台指令

    linux以centos为例做介绍 将以下脚本复制到并命名为repeat.sh 执行指令 ps -ef,执行1000次 先考虑脚本的形式 将以下代码复制仅repeat.bat中 以下指令是通过curl向某个服务器的8866端口发送10000次图片的请求 使用方法,具体结果就是不同的服务器不同了 我们改一个可以在本地之心并看现

    2024年02月13日
    浏览(33)
  • 打 jar 包运行 在windows 平台控制台和日志 乱码解决

    --拒絕鷄巴囉嗦,直接解決問題  我们在Windows下运行jar包时,常常会出现乱码,主要分为dos窗口输出的日志中出现乱码和程序返回数据出现乱码。 dos窗口输出的日志中出现乱码 执行如下命令,将控制台输出编码改为UTF8: 程序返回数据出现乱码 执行如下命令,运行jar包时指

    2024年01月25日
    浏览(39)
  • 【课程设计】Windows控制台上的交互界面(通过c/c++,在windows控制台实现选择菜单(可通过方向键选择))

    以前做课设的时候,费了很大功夫,在windows控制台实现方向键选择菜单。希望可以在这里总结一下,也欢迎大家一起交流学习。 SetConsoleTextAttribute函数 以下摘自百度百科 传送门 SetConsoleTextAttribute是Windows系统中一个可以设置控制台窗口字体颜色和背景色的计算机函数,该函数

    2024年02月07日
    浏览(37)
  • windows下tomcat控制台按天输出catalina.out日志

    windows服务器情况下,无法和linux服务器一样,启动web服务之后,直接tail查看日志,而windwos控制台的输出空间有限,如果遇到大量错误的情况下,console日志刷新太快,无法看到首次错误是在哪里出现的,会产生很多不必要的麻烦,所以从网上查找了windows控制台信息输出到文件

    2024年02月05日
    浏览(32)
  • Windows10环境下的Eclipse控制台输出中文乱码的问题的解决

    操作系统:Windows10中文版 IDE:Eclipse 2022-12 开发语言:Java JDK19 项目编码:UTF-8 写了一个很简单地main方法,打印出中文,结果出现了乱码的情形,如下图: 上网搜索了很多解决方式,大部分都是说要更改项目的字符编码集。如下图: 按照网上的说法将项目的编码字符集由UT

    2024年02月02日
    浏览(50)
  • 给数字人生成加上界面,基于ER-NeRF/RAD-NeRF/AD-NeRF,Gradio框架构建WEBUI,使用HLS流媒体,实现边推理边播放——之一:在WEBUI中实时输出服务器控制台日志

    目前数字人实现技术众多,我这里采用基于ER-NeRF,在这里可以看到其介绍:ICCV 2023 | ER-NeRF: 用于合成高保真Talking Portrait的高效区域感知神经辐射场-https://zhuanlan.zhihu.com/p/644520609 ER-NeRF的项目地址:https://github.com/Fictionarry/ER-NeRF ER-NeRF,RAD-NeRF,他们都继承自AD-NeRF,都有一个基于

    2024年02月05日
    浏览(33)
  • 如何使用Python的交互控制台

    简介 Python的交互控制台(也叫做Python解释器,或是Python Shell)为程序员提供了\\\"运行指令\\\"和\\\"不创建文件测试测试代码\\\"的快速途径。 交互控制台可以调用所有的Python内置函数和任何已安装的模块、命令行历史、和自动补全。它为\\\"探索Python语言\\\"和\\\"写好代码后粘贴入文件\\\"提供了

    2024年02月02日
    浏览(58)
  • 【RabbitMQ】RabbitMQ控制台的使用

    如果在本机上装了RabbitMQ则在浏览器访问127.0.0.1:15672,如果在服务器装了RabbitMQ则通过在浏览器输入urlip:15762来访问 登录后进入主页   进入主页后选择Admin,对应找到添加用户选择输入信息后即可完成添加 还是在Admin界面,与添加用户类似   我们给刚创建的用户授权使用刚刚创

    2024年02月12日
    浏览(37)
  • 使用RabbitMQ控制台查看和发送消息

    控制台发消息 点击queues -- Publish message 发送消息 Headers 设置contentType application/json Payload里填上发送内容,点击Publish message 就可以 控制台查看消息 点击queues -- Get Message(s) -- Payload里查看内容 get message只是查看消息,不会消费该条消息

    2024年02月10日
    浏览(42)
  • FISCO BCOS控制台配置及使用

    通过控制台链接FISCO BCOS节点,实现查询区块链状态、部署调用合约等功能,能够快速获取到所需要的信息。 一、环境准备 1.搭建FISCO BCOS联盟链节点 2.安装java 3.下载控制台 操作如下图所示 4.拷贝控制台配置文件 5.拷贝节点证书 二、启动控制台 1.启动 操作如下图所示 2.使用

    2024年02月09日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包