OpenCV+C++实现视频字符画(附源码)

字符画实现基本思路

萌新小白为了实现这个功能真的是百度了很久很久,看了很多大佬的文章,终于我成功了Ψ( ̄∀ ̄)Ψ。现在来分享一下我的大致思路。

思路分析

若相对整个视频进行字符画处理,则需要对视频的每一帧进行处理,读取视频的每一帧的代码如下:

VideoCapture cap;
cap.open("F:/opencv/images/test01.MP4");
if (!cap.isOpened()) {
	cout << "can't open file" << endl;
	return -1;
}
Mat frame, frame_resize, frame_gray;
while (true)
{
	cap >> frame;
	if (frame.empty())	break;
}

因为要输出原始图像的大小,我们需要对图像的尺寸进行重新调整,然后将图片转换为灰度图(后面需要根据灰度等级选择字符):

resize(frame, frame_resize, Size(300, 90));
cvtColor(frame_resize, frame_gray, COLOR_BGR2GRAY);

接下来就是要对图像的每个像素进行遍历,根据灰度等级划分字符,如果想要更加明显的效果可以选用更多的字符。

因为最后要将字符串进行输出,所以我们首先对字符串进行初始化,然后依次加上每个像素的字符。

String txt = "";
int height = frame_gray.rows;
int width = frame_gray.cols;

for (int j = 0; j < height; j++) {
	for (int i = 0; i < width; i++)
	{
		int pdata = frame_gray.ptr<uchar>(j)[i];
		if (pdata == 255)
			txt += char_list[87];
		else
			txt += char_list[pdata / len];
	}
	txt += "\n";
}

这样的话一帧就处理好啦,紧接着直接将字符串直接进行输出就可以了:

cout << txt;//输出当前字符

如果你看到这里就去尝试的话,很快就会发现输出的字符串很吓人,让你眼花缭乱Σ(っ °Д °;)っ。这是因为在你的编译器中,字符默认大小比较大,而一张图像又有很多个像素,图像肯定会变大了,解决这个问题的最好办法就是直接改变编译器输出的字体大小

我用的是VS2019,可以右键单击控制台窗口后点击默认值,会出来这个窗口:
在这里插入图片描述
我这里将字体大小改为了5,你们可以根据自己视频的情况进行更改,只要能让窗口大小大于一帧字符串的大小即可。

然后我们就实现了基本的字符画功能了ψ(`∇´)ψ,但是可能细心的小朋友又会发现字符画视频显示的很不稳定,甚至闪的眼睛疼。这个时候我们可以在程序里面加点内容就好啦:

//将光标移动到(0,0)处
HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = 0;
pos.Y = 0;
SetConsoleCursorPosition(hConsoleOutput, pos);

每次改变帧数的时候都将光标移动到(0, 0)处,大家再也不用担心闪眼睛了w(゚Д゚)w。代码参考了好多大佬的文章,附上一张我自己测试的图片啊:
在这里插入图片描述
不知道能不能看出来这是一坨什么w(゚Д゚)w,好吧其实它是奥特曼里面的怪兽。

源代码

#include <opencv2/opencv.hpp>
#include <iostream>
#include <Windows.h>

using namespace cv;
using namespace std;

const char* char_list = " .,-'`:!1+*abcdefghijklmnopqrstuvwxyz<>()\/{}[]?234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ%&@#$";
int len = 255 / strlen(char_list);
int main()
{
	VideoCapture cap;
	cap.open("F:/opencv/images/test01.MP4");
	if (!cap.isOpened()) {
		cout << "can't open file" << endl;
		return -1;
	}
	Mat frame, frame_resize, frame_gray;
	while (true)
	{
		cap >> frame;
		if (frame.empty())	break;
		resize(frame, frame_resize, Size(300, 90));
		cvtColor(frame_resize, frame_gray, COLOR_BGR2GRAY);
		String txt = "";

		int height = frame_gray.rows;
		int width = frame_gray.cols;

		for (int j = 0; j < height; j++) {
			for (int i = 0; i < width; i++)
			{
				int pdata = frame_gray.ptr<uchar>(j)[i];
				if (pdata == 255)
					txt += char_list[87];
				else
					txt += char_list[pdata / len];
			}
			txt += "\n";
		}

		//将光标移动到(0,0)处
		HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
		COORD pos;
		pos.X = 0;
		pos.Y = 0;
		SetConsoleCursorPosition(hConsoleOutput, pos);
		
		//输出当前字符
		cout << txt;
	}
	return 0;
}
  • 13
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值