优雅的用Lua调用OpenCV

说起脚本语言调用opencv,我们通常会想到python。如果说要在更小的系统中使用脚本语言,我们会想到lua。如果说用lua进行图像处理,我们肯定会想到大名鼎鼎的torch。torch中的image模块重写了opencv,封装了lua接口,但是如果我们不想修改opencv如此庞大的源码,又想像python调用opencv那样简单直接,那应该怎么做呢?

在正式开始介绍之前,想展示一段简答的代码,来告诉大家即将要做的事情是什么。(也是假人以渔了^_^)

cv = require("cv")
local img = cv.Mat.zeros(500, 500, cv.CV_8UC3)
cv.putText(img, 'Hello OpenCV', {50, 50}, cv.FONT_HERSHEY_SIMPLEX, 2, cv.Scalar(0, 0, 200), 2, cv.LINE_AA)
cv.imshow('test', img);
cv.waitKey(0);

上面不是python,可是一段lua代码哦(⊙o⊙)

一、编译lua

参见我上一篇博客,编译出的lua5.3.5.lib需要在这篇博客的程序中进行链接

二、编译LuaOpenCV

博主参考过LuaCV、torch-opencv等各种lua调用opencv的方式,它们都大张旗鼓的修改了OpenCV源码,也就是说我们必须有无限的精力来跟上OpenCV的迭代,博主认为对于一个不断迭代的系统颇为不妥,于是博主在github上找到了一位专门折腾lua的大神satoren(应该是个日本人)  主页 https://github.com/satoren

我们使用他给的一套工具kaguya来编译OpenCV

0. 编译OpenCV

如果我们已经有OpenCV了我们就可以跳过这一步。

这里我们要注意OpenCV版本的问题,3.1 -3.4.13都可以,OpenCV4在编译LuaOpenCV的时候会有许多问题,用OpenCV3.1 几乎不需要怎么修改,因为原作者就是指定了OpenCV3.1,用高版本的OpenCV就需要在后面多解几个bug。确认版本后,我们就可以编译OpenCV了。

1. 下载LuaOpenCV源码

这个的来源也是satoren大神,这里博主用的opencv3.4.3,需要做些修改,这里就fork了下,引用了博主自己的仓库(你完全可以链接到原始仓库)

2. 下载3rdparty库

这里有四个3rdparty库:

opencv >>>  如果你已经有opencv的话,就不用了

googletest>>> 如果你编译测试程序,需要在github上clone googletest放到源码3rdparty目录下(使用git submodule命令同步也可以,参见readme)

kaguya>>> 这个库非常重要,也是放到3rdparty目录下(也可使用submodule命令同步)

lua>>> 这种直接下载官网的源码包,解压到3rdparty目录下就可以了

3. 修改CMakeLists

首先,修改OpenCV版本要求大于3就可以了

然后,修改Lua库查找(博主发现findLua.cmake有问题,可能找不到lua库)。需要指定Lua包含目录、库目录、链接库名称,这三项就可以了。

博主fork之后,已经修改了cmakelist,你可能需要按照你自己的lua路径对照修改

4. 解决编译错误

博主在用VS2013工具集编译过程中,遇到了一个 “!”运算符重载出错的问题,在!后面加一个bool强制类型转换就可以了  if(!(bool)tabel[“name”]){} 类似这种

5. 解决绑定错误

由于OpenCV版本的更迭,raw_bind_generated.inc 这个包含函数绑定信息的文件也需要更新。由于博主还没有弄清楚 kaguya 是如何生成该文件的,所以博主没有选择重新生成该文件,而是直接在原文件基础上进行修改(主要是注释掉一些类构造函数的绑定)。

下图中.constructors就是构造函数,在编译过程中,一个模板生成的构造函数检查会疯狂报raw_bind_generated.inc的问题,每报一次错,就注释掉对应的类的构造函数(有时需要注释掉整个类),发现有问题就注释,然后重新编译。(这一步博主重复了大概二三十次,所以要有耐心)

如果你生成了cv.dll那么恭喜,我们来测试以下吧

三、测试Demo

我们使用 sample/hello_opencv.lua 的opencv来测试。我们保证opencv_world.dll\lua5.3.5.dll\lua.exe都在环境变量中。我们把刚编译好的cv.dll拷贝到sample目录下。

然后命令行进入sample目录,说出咒语  > lua hello_opencv.lua

如果弹出hello_opencv 窗口,那么恭喜,欢迎来到opencv的lua世界。

四、使用kaguya封装自己的函数

如果自己的函数也需要封装kaguya应该怎么办呢?这就需要我们简单了解下kaguya了。

kaguya是一个纯模板文件,没有任何实现,所有实例都是在编译期完成的

我们使用kaguya封装自己的函数完全可以参考luaOpenCV中的manual_bind.cc 和 manual_bind.inc

1. 书写自己的注册函数

将自己的封装按照kaguya_manual_bind(); 函数一样进行书写

这里注意,function可能会报错,我们一定要使用kaguya::function来注册我们的函数,因为std里面也有function,可能会串扰。

用constant来注册我们常量,用class_<>来注册我们自己的类,参考kaguya_manual_bind();这个的写法就可以

2. 将自己的注册函数加入绑定

自己的注册函数需要在manual_bind.cc中进行声明

然后再manual_bind.inc中再声明一次(这次是为了加入raw_bind_generated.inc)

3. 将工程修改成自己的dll(so)名

再raw_bin_generated.inc中KAGUYA_BINDINGS(cv) 这个宏,就定义了lua调用dll的入口,因此我们编译出的只能是cv.dll才能被调用。如果我们需要用其它的dll名,修改这个宏的参数就可以了

这里注意4786行这里,就将我们自己的注册项进行注册了,因为KAGUYA_BINDINGS所包围的块都会被注册。大致过程清晰明了吗。

4. 使用kaguya提供的python自动封装工具

这个博主还没有弄清楚,只是告诉大家,kaguya和opencvlua中各自都有一些自动生成注册 .inc 文件的工具,opencv如此庞大的代码量,不可能一个个类去手工分析注册,可以研究下,看下官方的github\文档\issus等。

五、OpenCV4.0的lua移植

大体过程是一样的,只是中间的.inc绑定文件需要用kaguya工具和luaopencv里面的.py重新生成,博主一时还没机会折腾,如果有少侠折腾了并且愿意分享出来,请一定告知博主(下方留言即可),比万分感谢。如果再折腾过程中有问题,也欢迎与博主交流。

《优雅的用Lua调用OpenCV》有10条评论

  1. 不知道为啥,这样调用会报错,看起来像是参数错误
    local srcImage=cv.imread(“test.PNG”);
    local fast=cv.FastFeatureDetector.create();
    local detectKeyPoint=cv.KeyPoint.new()
    fast:detect(srcGrayImage,detectKeyPoint);
    错误信息:
    maybe…Argument mismatch:userdata,nil,userdata candidate is:
    cv::Feature2D,cv::_InputArray,std::__1::vector<cv::KeyPoint, std::__1::allocator >,[OPT]cv::_InputArray,

    回复
    • Argument mismatch 就是入参类型不正确了,可能需要打印出类型来看看,看是不是要转化写写法,输入需要是:cv::Feature2D,cv::_InputArray,std::__1::vector, 但是你输入的参数类型是 userdata,nil,userdata

      回复
      • 我看了下源码需要转换。就是我没学过c++。对kaguya 那个自定义类型转换看的一知半解的。您可以帮我看看么?。付费。谢谢了

        回复
        • opencv的dll,和自己编译出来的dll,出问题的lua脚本,打包发到我的邮箱deeplearning@vip.qq.com 吧,我看一下

          回复
          • …..我编译的iOS 上的。没有编译电脑上的。用的opencv 3.2。 获取能微信或者 QQ联系到你么?
            lua code:
            local img=cv.imread(“test.PNG”);
            local keypoints=cv.KeyPoint.new()
            local detector=cv.ORB.create(50);
            detector:detect(img,keypoints)

          • 根据网上c++的教程 转的。 opencv 的detect 定义是这样的 detect(InputArrayOfArrays images,std::vector< std::vector > & keypoints,InputArrayOfArrays masks = noArray() ) 他第二个参数在经过kaguya 的绑定后变成了 2个 报错如下:
            maybe…Argument mismatch:userdata,userdata,userdata candidate is:
            cv::Feature2D,cv::_InputArray,std::__1::vector<cv::KeyPoint, std::__1::allocator >,[OPT]cv::_InputArray,
            cv::Feature2D,cv::_InputArray,std::__1::vector<std::__1::vector<cv::KeyPoint, std::__1::allocator >, std::__1::allocator<std::__1::vector<cv::KeyPoint, std::__1::allocator > > >,[OPT]cv::_InputArray,

          • 您有 ios 的越狱设备吗?或者我吧 编译好的iOS 库发您?另外 我可以可以联系到您么?不能让你白白忙活啊。配置这个挺麻烦的。 我也可以远程给您测试

回复 jia 取消回复