Kurento自定义OpenCV模块开发方法总结(一)
Kurento自定义OpenCV模块开发方法总结(一)
标签: kurentowebrtcopencv |
分类: IT那点事儿说起来却也又臭又长 |
Kurento Media Server(KMS)本身被设计为一个可插拔框架,KMS默认使用的模块只有三个:kms-core、kms-elements和kms-filters。此外,Kurento还内置了几个用于能力增强的模块:kms-crowddetector,kms-chroma,kms-platedetector和kms-pointerdector。除了以上内置的模块,KMS允许用户自定义模块并插入到系统框架中。目前支持的模块类型分为两种:OpenCV模块和Gstreamer模块,前者可用于开发计算机视觉相关的过滤器,而后者能力更强但开发难度也更大,需要对Gstreamer多媒体开发框架比较熟悉。为了方便开发者,Kurento提供了自定义模块开发工具,可以协助开发者自动生成模块的核心框架代码,开发者只需关注于自身逻辑的代码实现即可。
对于Kurento自定义模块的开发方法,官方文档在How to Develop Kurento Modules中给出了方法和步骤描述,但写的并不是十分清楚,导致本博主在开发Kurento模块前期走了不少弯路,因此也希望本系列博文能对后来者有所裨益,毕竟国内关于Kurento自定义模块开发的资料实在少得可怜。闲言少叙,下面本博主就以实现一个简单的OpenCV模块来展开介绍Kurento自定义模块的开发,该OpenCV模块的代码在Kurento的github代码库中找到。
首先,安装Kurento自定模块开发辅助工具kurento-module-scaffold。本文开篇便提到了Kurento为开发者提供了一个能够自动生成模块核心框架代码的工具,这个工具就是kurento-module-scaffold,该工具随kurento-media-server-6.0-dev开发工具包一同发布。完成kurento-media-server-6.0-dev开发工具包的安装之后直接执行命令kurento-module-scaffold.sh即可看到其在使用时需要提供的三个参数:ModuleName(模块名称)、output_directory(模块代码生成的输出目录)和opencv_filter(OpenCV模块,可选参数,默认GStreamer模块)。
# sudo apt-get install kurento-media-server-6.0-dev
# kurento-module-scaffold.sh
Usage: kurento-module-scaffold [opencv_filter]
其次,使用kurento-module-scaffold生成模块代码框架。以下命令将在modules目录下创建一个名为OpencvPluginSample的OpenCV Filter,因为kurento-module-scaffold同时还会将生成的代码放到一个git库中来管理,所以如果之前没有配置过git用户名和邮箱的话会出现一些错误信息,不过可以忽略。此时在modules目录下可以看到已经生成好的代码目录,目录名称与模块名称相同,只不过由驼峰命名法变成了用横线分割了。
# kurento-module-scaffold.sh OpencvPluginSample modules/ opencv_filter
Initialized empty Git repository in /home/ubuntu/modules/opencv-plugin-sample/.git/
*** Please tell me who you are.
Run
git config --global user.email "[email protected]"
git config --global user.name "Your Name"
to set your account's default identity.
Omit --global to set the identity only in this repository.
fatal: unable to auto-detect email address (got '[email protected](none)')
Folder tree created successfully
# ls -l modules/opencv-plugin-sample/
total 16
-rw-rw-r-- 1 ubuntu ubuntu 2261 Feb 5 09:52 CMakeLists.txt
-rw-rw-r-- 1 ubuntu ubuntu 414 Feb 5 09:52 config.h.cmake
drwxrwxr-x 3 ubuntu ubuntu 4096 Feb 5 09:52 debian
drwxrwxr-x 3 ubuntu ubuntu 4096 Feb 5 09:52 src
再次,修改模块接口文件并生成代码。经过以上步骤生成的opencv-plugin-sample目录并未包含真正意义上的代码,只是一个框架而已,要想生成代码需要根据模块的功能修改接口文件,之后根据接口文件就可以生成相应的代码了。在opencv-plugin-sample/src/server目录下可以看到两个子目录:implementation和interface,前者是存放实现接口功能代码的目录,后者就是存放定义接口文件的目录。在interface目录下,默认已经生成好了两个基本的接口文件:opencvpluginsample.kmd.json和opencvpluginsample.OpencvPluginSample.kmd.json,前者用来定义模块信息如模块名称、模块版本号和KMS版本号,后者则用来定义模块本身如何构造、包含哪些方法、方法如何调用等信息。这里我们只修改后面这个文件,仿照github上的模块接口文件opencvpluginsample.OpencvPluginSample.kmd.json添加两个方法:setFilterType和setEdgeThreshold,简单起见直接拷贝github上的模块接口文件即可,关于模块接口文件的具体定义语法,kurento代码库中有很多已有文件可供参考,感兴趣的看客们自己就学习就好了。修改完模块接口文件之后就可以生成接口实现代码了,在opencv-plugin-sample目录下执行cmake命令,我们可以看到命令执行过程中会生成很多hpp和cpp文件,至此一个OpenCV模块的核心框架代码就生成完了,之后要做的就是向代码中添加具体功能实现。
# cmake .
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
...
-- opencv Already found
-- Found KMSFILTERS: 6.2.0 (found version "6.2.0")
-- Resolving KMSFILTERS version ^6.0.0 with 6.2.0
-- Generated: OpencvPluginSample.hpp
-- Generated: OpencvPluginSample.cpp
-- Generated: OpencvPluginSampleInternal.cpp
-- Generated: OpencvPluginSampleInternal.hpp
-- Generated: kmsopencvpluginsample.pc.in
-- Generated: OpencvPluginSampleImplInternal.cpp
-- Generated: OpencvPluginSampleImplFactory.hpp
-- Generated: SerializerExpanderOpencvpluginsample.cpp
-- Generated: OpencvPluginSampleOpenCVImpl.hpp
-- Generated: OpencvPluginSampleImpl.cpp
-- Generated: OpencvPluginSampleOpenCVImpl.cpp
-- Generated: OpencvPluginSampleImpl.hpp
-- Generated: Module.cpp
-- Generated: FindKMSOPENCVPLUGINSAMPLE.cmake.in
...
-- Generating done
-- Build files have been written to: /home/ubuntu/modules/opencv-plugin-sample
再次,在已有代码框架下添加具体功能实现。在上文中已经提到了实现具体功能的代码都存储在opencv-plugin-sample/src/server/implementation目录下,而需要我们修改的代码则存储在了这个目录的objects子目录下,这个子目录下有四个自动生成的文件,在两个头文件中分别定义了两个类:OpencvPluginSampleImpl和OpencvPluginSampleOpenCVImpl,前者继承了后者。需要我们重点关注的就是名为OpencvPluginSampleOpenCVImpl的这个类,具体功能实现代码添加到这个类中即可。通过查看这个类的定义可以看到,除了构造和析构函数以外还包括了另外三个函数,其中两个是我们在接口文件中定义好的setFilterType和setEdgeThreshold,而最后一个就是process函数,这个函数在视频流中的每一帧画面到来时都会被调用,而其参数mat就是要处理的那帧图片。
# ls -l
total 16
-rw-rw-r-- 1 ubuntu ubuntu 1553 Feb 5 10:31 OpencvPluginSampleImpl.cpp
-rw-rw-r-- 1 ubuntu ubuntu 1809 Feb 5 10:31 OpencvPluginSampleImpl.hpp
-rw-rw-r-- 1 ubuntu ubuntu 1152 Feb 5 10:31 OpencvPluginSampleOpenCVImpl.cpp
-rw-rw-r-- 1 ubuntu ubuntu 712 Feb 5 10:31 OpencvPluginSampleOpenCVImpl.hpp
class OpencvPluginSampleOpenCVImpl : public virtual OpenCVProcess
{
public:
OpencvPluginSampleOpenCVImpl ();
virtual ~OpencvPluginSampleOpenCVImpl () {};
virtual void process (cv::Mat &mat);
void setFilterType (int filterType);
void setEdgeThreshold (int edgeValue);
}
既然找到了在哪里添加功能代码,就可以动手写我们自己的第一行代码了。当然前提是你已经对图像处理和Opencv库的使用比较熟悉了,像本博主这种图片处理小白就只好直接拷贝示例代码中的OpencvPluginSampleOpenCVImpl.hpp和OpencvPluginSampleOpenCVImpl.cpp了。这里我们不妨先看看例子代码。OpencvPluginSampleOpenCVImpl.hpp这个头文件为OpencvPluginSampleOpenCVImpl这个类添加了两个私有成员变量,用来作为处理配置参数。OpencvPluginSampleOpenCVImpl.cpp则实现了头文件中的所有函数,可以看到process函数首先将原始的BGRA图片转换成了灰度图,然后对灰度图执行Canny算法进行边缘检测,最后再将灰度图转换回BRGA图。从处理逻辑上来看,OpencvPluginSample这个模块的功能就是对视频流中的图像进行边缘检测。
class OpencvPluginSampleOpenCVImpl : public virtual OpenCVProcess
{
public:
OpencvPluginSampleOpenCVImpl ();
virtual ~OpencvPluginSampleOpenCVImpl () {};
virtual void process (cv::Mat &mat);
void setFilterType (int filterType);
void setEdgeThreshold (int edgeValue);
private:
int filterType;
int edgeValue;
};
OpencvPluginSampleOpenCVImpl::OpencvPluginSampleOpenCVImpl ()
{
this->filterType = 0;
this->edgeValue = 125;
}
void OpencvPluginSampleOpenCVImpl::process (cv::Mat& mat)
{
cv::Mat matBN (mat.rows, mat.cols, CV_8UC1);
cv::cvtColor(mat, matBN, COLOR_BGRA2GRAY);
if (filterType == 0) {
Canny (matBN, matBN, edgeValue, 125);
}
cvtColor (matBN, mat, COLOR_GRAY2BGRA);
}
void OpencvPluginSampleOpenCVImpl::setFilterType (int filterType)
{
this->filterType = filterType;
}
void OpencvPluginSampleOpenCVImpl::setEdgeThreshold (int edgeValue)
{
this->edgeValue = edgeValue;
}
再次,编译修改后的代码并将自定义模块添加到KMS中。在opencv-plugin-sample目录下执行make命令,完成编译之后在opencv-plugin-sample/src/server下会生成需要的动态链接库。为了将该模块添加到KMS中,我们有两种方法:生成Debian安装包或者修改Kurento环境变量,这里我们使用后一种方法,在文件/etc/default/kurento-media-server-6.0中追加如下内容,把自定义模块的路径添加到环境变量KURENTO_MODULES_PATH中,KMS在启动时会自动到改路径下搜索可用模块的动态链接库。
export KURENTO_MODULES_PATH=/home/ubuntu/modules/opencv-plugin-sample
最后,重启KMS加载自定义模块。通过KMS在启动过程中生成的日志,我们可以看到模块的加载信息。KMS的默认日志存储路径为/var/log/kurento-media-server,KMS自身打印出来的日志记录在类似于media-server_2016-02-05_11-53-08.00000.pid1775.log这样的文件中,而自定义模块的打印信息则记录在media-server_error.log文件中。如果自定义模块正常加载,那么在KMS日志中我们可以找到类似于下面内容的日志信息,至此一个自定义模块就完成了。
2016-02-05 11:53:08,815797 1775 [0x00007fd5871618c0] info KurentoModuleManager
ModuleManager.cpp:108 loadModule() Loaded opencvpluginsample version 0.0.1~.g
以上就是为Kurento开发一个Opencv模块的全过程,但是仅仅在KMS完成模块的加载还是不够的,应用端现在还无法使用已经加载到KMS中的新模块,在下一篇博文中本博主会继续介绍如何为新模块生成客户端调用接口并通过一个简单的Node应用来查看这个Opencv模块的处理效果。