OpenCV AR之二维码叠加视频

15 篇文章 0 订阅

OpenCV AR之二维码叠加视频

前言

最近心血来潮,想着用OpenCV做一个AR的小应用,也是看知乎的回答,想到了识别二维码,然后在二维码上放视频,就花了大概两三天做出了这个小demo,完成度大概有70%,这篇文章简单说明一下。
做出来的效果的视频已经放在了b站上:
链接

总体思路

实现这个AR demo,首先就是识别到二维码,然后根据二维码的位置信息,通过透视变换得到一个区域,然后用过掩码的方式,将一段视频叠加到实时场景中。
根据上面所说的,用到的技术分为三点

  1. 二维码检测
    关于二维码的检测,有一篇文章已经讲的很清楚了,链接
    在这个demo中,只需要检测二维码的位置就可以了,所以用的是这个API,用法也是很简单的,
    第一个参数为待检测的图像,第二个参数为二维码的四个顶点坐标,返回值表示是否含有二维码

    bool cv::QRCodeDetector::detect(InputArray  img, OutputArray  points)
    
  2. 透视变换
    得到二维码的四个定点之后,随着视角的移动,二维码的四个定点肯定不是正方形的形状,这就需要我们叠加的视频区“适配”二维码的视角,这就需要仿射变换和透视变换,仿射变换和透视变换介绍。本项目使用的是透视变换,在OpenCV中,可以通过warpPerspective函数实现,具体的实现可以参考完整的代码。

  3. 掩码mask操作
    得到了放射变换之后的图,我们还需要把图片贴上去,这就用到了很常见的mask掩码操作,就是生成一个mask图像,在mask图像中(一般是灰度图),只像copy素值不为0的像素点,简单的实例如下:

dst_warp.copyTo(frame_bg, mask);

完整代码

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

using namespace cv;
using namespace std;
 
int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cout << "eg. " << argv[0] << " video" << std::endl;
        return 0;
    }

    // bg is camera
    VideoCapture cap_bg(2); // 2 is camera index
    // cap_bg.set(CAP_PROP_FRAME_WIDTH, 1280);
    // cap_bg.set(CAP_PROP_FRAME_HEIGHT, 720); 
    VideoCapture cap_show(argv[1]);
    if (!cap_show.isOpened()) {
        std::cout << "open video failed!" << std::endl;
        return 0;
    }

	Point2f srcPoints[4];//原图中的四点 ,一个包含三维点(x,y)的数组,其中x、y是浮点型数
	Point2f dstPoints[4];//目标图中的三点

    Mat frame_bg;
    Mat frame_show;
    Mat dst_warp;
    QRCodeDetector qrcodedetector;
    vector<Point> points;
    int i = 0;
    bool start_flag[2] = {false, false};
    bool start_end[2] = {false, false};
    while(1) {
        cap_bg >> frame_bg;
        if(frame_bg.empty()) //如果某帧为空则退出循环
            break;

        std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now();
        bool has_code = qrcodedetector.detect(frame_bg, points);
        if (has_code) {
            //add video
            cap_show >> frame_show;
            if (frame_show.empty())
                continue;

            start_flag[1] = true;
            bool tmp = start_flag[0];
            start_flag[0] = start_flag[1];
            start_flag[1] = tmp;

            if (start_flag[0] == true && start_flag[1] == true) {
                srcPoints[0] = Point2f(0, 0);
                srcPoints[1] = Point2f(frame_show.cols, 0);
                srcPoints[2] = Point2f(0, frame_show.rows);
                srcPoints[3] = Point2f(frame_show.cols, frame_show.rows);
                dstPoints[0] = Point2f(points[0].x, points[0].y);
                dstPoints[1] = Point2f(points[1].x, points[1].y);
                dstPoints[2] = Point2f(points[3].x, points[3].y);
                dstPoints[3] = Point2f(points[2].x, points[2].y);

                Mat M1 = getPerspectiveTransform(srcPoints, dstPoints);//由四个点对计算变换矩阵
                warpPerspective(frame_show, dst_warp, M1, frame_bg.size());//仿射变换

                vector<cv::Point> point_area;
                point_area.push_back(points[0]);
                point_area.push_back(points[1]);
                point_area.push_back(points[2]);
                point_area.push_back(points[3]);
                vector<vector<Point>> point_areas;
                point_areas.push_back(point_area);

                Mat mask(dst_warp.size(), CV_8UC1, Scalar(0));
                polylines(mask, point_area, true, Scalar(255), 1);
                fillPoly(mask, point_areas, cv::Scalar(255));
                dst_warp.copyTo(frame_bg, mask);
            }
        }
        else {
            start_flag[0] == false;
            start_flag[1] == false;
        }
        imshow("frame_bg_origin", frame_bg);

        std::chrono::steady_clock::time_point end_time = std::chrono::steady_clock::now();
        std::chrono::milliseconds cost_time = std::chrono::duration_cast<std::chrono::milliseconds>(
            end_time - start_time);
        int time_tmp = cost_time.count();
        int delay = (time_tmp < 33) ? (33 - time_tmp) : 1;
        std::cout << "total time: " << time_tmp << "ms, "
                  << "delay time: " << delay << "ms" << std::endl;
        if (waitKey(delay) == 'q') {
            break;
        }
    }
	return 0;
}

反思改进

  1. 可以看出来,演示的视频还是有很多误检测的,会出现一闪一闪的情况,这种情况就需要进行滤波,改善闪的情况。
  2. 其实AR的最重要一部分就是动画的渲染,这个demo中只是通过类似于添加logo的方式渲染的,更加专业的话,其实是可以用专门的工具进行的,比如OpenGL等。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chasentech

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值