说在前面
- opencv版本:4.0.1
- 操作系统:win10
- vs版本:2017
- 其他说明:瞎折腾系列第一期,目前实现的效果并不是很好
- 基础:【opencv/core module】(二)How to scan images, lookup tables and time measurement with OpenCV
【opencv/videoio module】(一)Video Input with OpenCV and similarity measurement
【opencv/videoio module】(二)Creating a video with OpenCV
Theory
- 原理其实很简单
- 捕捉视频一帧,转为灰度图
- 降低颜色空间的大小
- 对每一个位置的像素用一个字符代替,字符所占的比例逐渐增大(例如 . ; + # @这样,相当于颜色逐渐加深)
- 最后,将转化后的由字符形成这一帧保存起来
- 存在的问题
-
用哪些字符替代?
-
用哪种方式保存字符帧?
这两问题我觉得有些联系,我在实验中用的是putText()函数,将字符直接输出到Mat上,然后使用VideoWriter写入到视频文件中,这样便于视频的生成;但是putText这个函数用的时候有些偏差,有些字符所占的宽度不是固定的,会导致最终结果emmm(っ °Д °;)っ
另一种保存方式,直接输出到文本文件中,这样就没有putText的问题,但是如何将其转换成视频?难道一帧一帧的截图(不嫌麻烦可以一试,代码也有)?(而且ascii码的话长宽是不等的)emmmm┗|`O′|┛
SourceCode
主要是putText函数
void cv::putText
(
InputOutputArray img, //输入到img上
const String & text, //要显示的字符
Point org, //要显示的位置(像素)
int fontFace, //字体类型
double fontScale, //字体缩放
Scalar color, //字体颜色
int thickness = 1, //字体粗细
int lineType = LINE_8, //线条类型
bool bottomLeftOrigin = false //是否将text的左下角作为起始位置,否的话左上角
)
字体类型(实验中用的是1,也就是FONT_HERSHEY_PLAIN,貌似最小的)
name | 含义 |
---|---|
FONT_HERSHEY_SIMPLEX | normal size sans-serif font |
FONT_HERSHEY_PLAIN | small size sans-serif font |
FONT_HERSHEY_DUPLEX | normal size sans-serif font (more complex than FONT_HERSHEY_SIMPLEX) |
FONT_HERSHEY_COMPLEX | normal size serif font |
FONT_HERSHEY_TRIPLEX | normal size serif font (more complex than FONT_HERSHEY_COMPLEX) |
FONT_HERSHEY_COMPLEX_SMALL | smaller version of FONT_HERSHEY_COMPLEX |
FONT_HERSHEY_SCRIPT_SIMPLEX | hand-writing style font |
FONT_HERSHEY_SCRIPT_COMPLEX | more complex variant of FONT_HERSHEY_SCRIPT_SIMPLEX |
FONT_ITALIC | flag for italic font |
线条类型(用的0,填充)
name | 含义 |
---|---|
FILLED | |
LINE_4 | 4-connected line |
LINE_8 | 8-connected line |
LINE_AA | antialiased line |
最后一个参数,在确定Point org后,相当于下面哪一个点(左边俩黑色)作为org
#include <iostream> // for standard I/O
#include <string> // for strings
#include <opencv2/core.hpp> // Basic OpenCV structures (cv::Mat)
#include <opencv2/videoio.hpp> // Video write
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace std;
using namespace cv;
int CNT = 0;
void ProcessText(Mat& I)
{
CV_Assert(I.depth() == CV_8U && I.channels() == 1);
string txt_name = "test_" + to_string(CNT) + ".txt";
ofstream out(txt_name);
for (int i = 15; i < I.rows - 10; ++i)
{
char text[240] = { 0 };
for (int j = 20; j < I.cols - 25; ++j)
{
if (I.at<uchar>(i, j) < 25)
text[j - 20] = '-';
else if (I.at<uchar>(i, j) < 50)
text[j - 20] = '-';
else if (I.at<uchar>(i, j) < 75)
text[j - 20] = '<';
else if (I.at<uchar>(i, j) < 100)
text[j - 20] = '>';
else if (I.at<uchar>(i, j) < 125)
text[j - 20] = '+';
else if (I.at<uchar>(i, j) < 150)
text[j - 20] = '+';
else if (I.at<uchar>(i, j) < 175)
text[j - 20] = '&';
else if (I.at<uchar>(i, j) < 200)
text[j - 20] = '&';
else if (I.at<uchar>(i, j) < 225)
text[j - 20] = '%';
else if (I.at<uchar>(i, j) < 250)
text[j - 20] = '#';
else
text[j - 20] = '@';
}
out << text << endl;
}
CNT++;
out.close();
}
int ggg = 0;
void Process(Mat& I, Mat& Out)
{
// accept only char type matrices
CV_Assert(I.depth() == CV_8U && I.channels() == 1);
for (int i = 15; i < I.rows - 10; ++i)
{
char text[240] = { 0 };
for (int j = 20; j < I.cols - 25; ++j)
{
if (I.at<uchar>(i, j) < 25)
text[j - 20] = '-';
else if (I.at<uchar>(i, j) < 50)
text[j - 20] = '-';
else if (I.at<uchar>(i, j) < 75)
text[j - 20] = '<';
else if (I.at<uchar>(i, j) < 100)
text[j - 20] = '>';
else if (I.at<uchar>(i, j) < 125)
text[j - 20] = '+';
else if (I.at<uchar>(i, j) < 150)
text[j - 20] = '+';
else if (I.at<uchar>(i, j) < 175)
text[j - 20] = '&';
else if (I.at<uchar>(i, j) < 200)
text[j - 20] = '&';
else if (I.at<uchar>(i, j) < 225)
text[j - 20] = '%';
else if (I.at<uchar>(i, j) < 250)
text[j - 20] = '#';
else
text[j - 20] = '@';
}
putText(Out, text, Point(0, (i - 14) * 10), 1, 1, Scalar(255), 1, 1);
if (ggg == 0)
cout << text << endl;
}
ggg = 1;
}
int main()
{
const string source = "1.mp4"; // the source file name
const string NAME = "test.avi"; // Form the new name with container
int divideWith = 25;
uchar table[256];
for (int i = 0; i < 256; ++i)
table[i] = (uchar)(divideWith * (i / divideWith));
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
for (int i = 0; i < 256; ++i)
p[i] = table[i];
VideoCapture inputVideo(source); // Open input
if (!inputVideo.isOpened())
{
cout << "Could not open the input video: " << source << endl;
return -1;
}
VideoWriter outputVideo(NAME, VideoWriter::fourcc('M', 'J', 'P', 'G'),
inputVideo.get(CAP_PROP_FPS), Size(960*2, 540*2), true);
if (!outputVideo.isOpened())
{
cout << "Could not open the output video for write: " << NAME << endl;
return -1;
}
Mat src;
while (true)
{
inputVideo >> src; // read
if (src.empty()) break; // check if at end
pyrDown(src, src, Size(src.cols / 2, src.rows / 2));//缩放
pyrDown(src, src, Size(src.cols / 2, src.rows / 2));//缩放
pyrDown(src, src, Size(src.cols / 2, src.rows / 2));//缩放
cvtColor(src, src, COLOR_BGR2GRAY);//转为灰度图
LUT(src, lookUpTable, src);//降低颜色空间
imshow("src", src);
Mat res(Size(1920, 1080),CV_8UC1);//用于保存结果
Process(src, res);//处理
//pyrDown(res, res, Size(960, 540));//缩放
cvtColor(res, res, COLOR_GRAY2BGR);//转化成BGR,VideoWriter的问题
imshow("test", res);
outputVideo << res;//写入
//ProcessText(src);
waitKey(15);
}
return 0;
}
Result
- 上面原图,下面结果,都是gif
END