Allan's Blog

浅谈OpenCV植入广告

随着公司的业务的发展有幸接触到OpenCV 这个图形库,这篇文章主要是总结一下在Mac 上使用OpenCV做一个动态的广告植入。

OpenCV 是什么:

OpenCV的全称是:Open Source Computer Vision Library。OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。 OpenCV用C++语言编写,它的主要接口也是C++语言,但是依然保留了大量的C语言接口。该库也有大量的Python, Java and MATLAB/OCTAVE (版本2.5)的接口。这些语言的API接口函数可以通过在线文档获得。如今也提供对于C#,Ch, Ruby的支持。 本文不作过多的基本介绍,想了解请点击 详细介绍

知道它是什么之后,你可以使用它做很多有趣的图形,图像识别,图像重绘等工作。我所做的项目需求是要在一个视频上无缝的植入一个广告。这听起来好像好高大上,但其实分析之后可以由OpenCV里面的一些函数实现。下面先看一个老外的例子:JavaScript-Motion-tracking

分析例子:

上述例子主要涉及到的技术点主要有两个:
追踪ROI(追踪感兴趣区域)
图片Transform

  1. 追踪ROI 是比较有难度的所幸的是我们有很多的解决办法,例子中的作者用来追踪点的方式来简化这个过程(Tracking Point)
  2. 有了Tracking Data 之后剩下的就是如何让图片Transform了,方法有两个。
    第一:直接上框架,严格来说这只是一个可以根据Tracking Data 来改变图片形状的JS。
    第二:CSS 直接使用CSS3的变形属性就可以了。
    例子采取了第一种方式,但我反而推荐的是CSS3的变形属性。

到此,这个例子主要的逻辑和技术点已经分析清楚了。接下来就要对每个点进行一些讲解和我自己项目中使用到哪些点以及最终的Demo。

追踪感兴趣区域(ROI)

感兴趣区域追踪是这个项目中最为难的一点,因为你要追踪的点色值不能太相近,不能太小等等限制条件。例子的ROI 是用了AE的出来的。如何用AE追踪物体 但用AE做出来就违背了自动化的目的 ( PS:老板不给用!!!!!)这时候OpenCV派上用场了,里面有一个算法叫光流,它可以追踪一个点在视频中的位移。 关于光流是什么,它能最什么东西这里不一一细讲,Google 一下你就会明白。直接上代码:

__author__ = 'Allan'
import sys
import numpy as np
import cv2
import time
import json

pointA = []
pointB = []
pointC = []
pointD = []

debug = True
cap = cap = cv2.VideoCapture('Your Movie')

# params for ShiTomasi corner detection
feature_params = dict( maxCorners = 500,
                       qualityLevel = 0.6,
                       minDistance = 7,
                       blockSize = 7 )

# Parameters for lucas kanade optical flow
lk_params = dict(winSize = (15,15),
                 maxLevel = 2,
                 criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))

mask = []

p0 = []

def tracking():
    # 感兴趣的区域 (ROI)
    p2 = np.array([np.array([[47.0,128.0]], np.float32),
                   np.array([[127.0,128.0]],np.float32),
                   np.array([[47.0,170]],np.float32),
                   np.array([[127.0,170]],np.float32)],
                   np.float32)
    p0 = p2

    get_the_first_frame()

    while(1):
        ret,frame = cap.read()
        if ret:
            frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            # calculate optical flow
            p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
            # Select good points
            good_new = p1[st==1]
            good_old = p0[st==1]

            save_four_point_position(p0)
            if (debug):
                draw_point(good_new,good_old,frame)
                img = cv2.add(frame,mask)
                cv2.imshow('frame',img)
                k = cv2.waitKey(30) & 0xff
                if k == 27:
                    break
            # Now update the previous frame and previous points
            old_gray = frame_gray.copy()
            p0 = good_new.reshape(-1,1,2)
        else:
            export_data()
            break

    cv2.destroyAllWindows()
    cap.release()

def get_the_first_frame():
    # Take first frame and find corners in it
    ret, old_frame = cap.read()
    old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)

    # Create a mask image for drawing purposes
    mask = np.zeros_like(old_frame)
    p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
    

def export_data():
    point_a_data ="{" +string_replace(json.dumps(np.asarray(pointA).tolist()))+"}"
    point_b_data ="{" +string_replace(json.dumps(np.asarray(pointB).tolist()))+"}"
    point_c_data ="{" +string_replace(json.dumps(np.asarray(pointC).tolist()))+"}"
    point_d_data ="{" +string_replace(json.dumps(np.asarray(pointD).tolist()))+"}"
    export_data = [("A",point_a_data),("B",point_b_data),
                   ("C",point_c_data),("D",point_d_data)]
    export_data = json.dumps(export_data)
    print(export_data)

if __name__ == '__main__':
    start = time.clock()
    tracking()
    print "%s  %s"%("set_tracker_postion", time.clock() - start), "second"

上述的代码你完全可以用C++ 来重写一遍,只是Python于我而言用到更加顺手。

到此你已经可以用OpenCV来追踪你想要的点了,剩下的就是如何用js来就行形变,融合到视频里面。

JavaScript 形变

拿到Tracking Data之后,把Data 直接放在js上让其进行计算。

	var tracking_data = [
	// put your tracking data here
	]

具体的JS 实现请看这里

总结

到此,你已经能够成功的把一个广告植入到你的视频中了。由于篇幅有限,一些技术细节没有一一罗列出来,想详细了解的请关注我的Github

还没解决的问题

在此项目完成后,其实还有一点问题没有完美的解决。

  1. ROI 出镜后,没有办法在此追踪。
  2. ROI 选取太单一。

问题1 导致的结果是广告位会漂移
问题2 导致的结果是没法追踪正确的区域

Allan Chan