Enjoy It

Opencv+Cpp实现视频动画转字符动画
最近看到网上挺多人把视频动画转换为字符动画的,于是也想用C++试一下。准备工作考虑到视频解码过于复杂,于是使用了现...
扫描右侧二维码阅读全文
15
2018/08

Opencv+Cpp实现视频动画转字符动画

最近看到网上挺多人把视频动画转换为字符动画的,于是也想用C++试一下。

准备工作

考虑到视频解码过于复杂,于是使用了现成的工具opencv.

以下是我的环境:

系统工具语言
Ubuntu18.04opencvC++

Step 1. 编译安装opencv

先去opencv官方网站下载最新版的opencv源代码:点击进入

下载之后,进行编译安装,具体怎么编译安装,请自行Google。

编译opencv的时候,要添加上 -D WITH_FFMEPG=1,否则很有可能会出现无法读取视频的情况。另外也尽量把 opencv_contrib 下载和opencv一起编译。 对了,还要加上 -D CMAKE_INSTALL_PREFIX=/usr/local/

Step 2. 配置opencv环境

vi ~/.bashrc

讲一下语句加入到最后一行

PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/pkgconfig
export PKG_CONFIG_PATH

这两句的意思是讲opencv中的pkg-config加入到pkg中,方面后面编译。

原理解析

  1. 建立一个字符串str,里面包含若含字符,字形结构较为复杂的在字符串前面,字形结构简单的在字符串后面,设字符串长度为L
  2. 使用opencv读取视频,获取一帧图像
  3. 对每一帧图像进行处理:取得图像的每个像素点,根据公式将r,g,b三个通道转换为灰度值gray,0<gray<255,因为我们需要把这个灰度值映射到字符串str中的字符中,所以使用公式 $ index = \frac{gray x L}{255} $
  4. 根据str(index) 获取到该灰度值所对应的映射字符,并输出。
  5. 重复2,3,4

编译代码

g++ main.cpp -o main `pkg-config --cflags --libs opencv`

这个上面两个不是引号,而是Esc下面的那个符号。pkg-config的作用自行Google。

代码

#include <iostream>
#include <opencv2/opencv.hpp>
#include <cmath>
#include <opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;
class Mp4ToChar
{
  public:
    Mp4ToChar(string filename, int width, int height, string pattern);
    void Run(int p);
    void ToChar(cv::Mat frame);

  private:
    string filename;
    string pattern;
    int width;
    int height;
};

Mp4ToChar::Mp4ToChar(string filename, int width, int height, string pattern)
{
    this->filename = filename;
    this->width = width;
    this->height = height;
    this->pattern = pattern;
}

void Mp4ToChar::Run(int p)
{
    //读取视频
    cv::VideoCapture capture(this->filename);
    if (!capture.isOpened())
    {
        cout << "视频文件打开失败,请检查错误" << endl;
        return;
    }
    int cnt = 0;
    cv::Mat frame;
    while (capture.read(frame))
    {
        if (cnt % p == 0)
        {
            this->ToChar(frame);
        }
        // return;
        cnt++;
    }
}

void Mp4ToChar::ToChar(cv::Mat frame)
{
    printf("\033[2J");
    cv::Mat f = frame.clone();
    cv::Mat nf;
    //计算新的宽和高
    double w = f.cols, h = f.rows;
    double mx = max(w, h);
    int nw = (int)(this->width * 1.0 * w / mx);
    int nh = (int)(this->height * 1.0 * h / mx);
    //resize
    cv::resize(f, nf, cv::Size(nw, nh));
    // imshow("img",nf);
    // waitKey(0);

    MatIterator_<Vec3b> colorit, colorend;
    colorend = nf.end<Vec3b>();
    int i, r, g, b, t, idx;
    string  str= "";
    for (colorit = nf.begin<Vec3b>(), i = 0; colorit != colorend; colorit++, i++)
    {
        r = (*colorit)[0];
        g = (*colorit)[1];
        b = (*colorit)[2];
        t = (int)(r * 0.299 + g * 0.589 + b * 0.114);
        idx = (int)(strlen(this->pattern.c_str()) * t / 255);
        str += this->pattern[idx];
        //cout << this->pattern[idx];
        if (i != 0 && i % nw == 0)
        {
            str+="\n";
        }
    }
    cout << str << endl;
}

int main(int argc, char *argv[])
{
    const char data[] = "$@B8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft()1{}[]?-_+~<>i!lI;:,\"^`'.";
    Mp4ToChar mp4Tochar("1.avi", 200, 100, data);
    while (1)
    {
        mp4Tochar.Run(1);
    }

    return 0;
}

不足

由于没有采用缓冲机制,所以造成控制台输出的时候,不断地出现闪屏现象。

最后修改:2018 年 08 月 15 日 10 : 05 AM
如果觉得我的文章对你有用,请随意赞赏

发表评论