OpenCV学习笔记(五十六)——InputArray和OutputArray的那些事core
看过OpenCV源代码的朋友,肯定都知道很多函数的接口都是InputArray或者OutputArray型的,这个接口类还是很强大的,今个就来说说它们的那些事。
InputArray这个接口类可以是Mat、Mat_<T>、Mat_<T, m, n>、vector<T>、vector<vector<T>>、vector<Mat>。也就意味着当你看refman或者源代码时,如果看见函数的参数类型是InputArray型时,把上诉几种类型作为参数都是可以的。
有时候InputArray输入的矩阵是个空参数,你只需要用cv::noArray()作为参数即可,或者很多代码里都用cv::Mat()作为空参。
这个类只能作为函数的形参参数使用,不要试图声明一个InputArray类型的变量
如果在你自己编写的函数中形参也想用InputArray,可以传递多类型的参数,在函数的内部可以使用_InputArray::getMat()函数将传入的参数转换为Mat的结构,方便你函数内的操作;必要的时候,可能还需要_InputArray::kind()用来区分Mat结构或者vector<>结构,但通常是不需要的。例如:
- void myAffineTransform(InputArray _src, OutputArray _dst, InputArray _m)
- {
- Mat src = _src.getMat(), m = _m.getMat();
- CV_Assert( src.type() == CV_32FC2 && m.type() == CV_32F && m.size() == Size(3, 2) );
- _dst.create(src.size(), src.type());
- Mat dst = _dst.getMat();
- for( int i = 0; i < src.rows; i++ )
- for( int j = 0; j < src.cols; j++ )
- {
- Point2f pt = src.at<Point2f>(i, j);
- dst.at<Point2f>(i, j) = Point2f(m.at<float>(0, 0) * pt.x + m.at<float>(0, 1) * pt.y + m.at<float>(0, 2);
- }
- }
至于有的源代码里使用 InputArrayOfArrays作为形参,不用慌张,其实它和InputArray是一样一样一样的。
OutputArray是InputArray的派生类。使用时需要注意的问题和InputArray一样。和InputArray不同的是,需要注意在使用_OutputArray::getMat()之前一定要调用_OutputArray::create()为矩阵分配空间。可以用_OutputArray::needed()来检测输出的矩阵是否需要被计算。有时候传进去的参不是空就不需要计算
还有就是OutputArrayOfArrays、InputOutputArray、InputOutputArrayOfArrays都是OutputArray的别名而已
OpenCV学习笔记(五十七)——在同一窗口显示多幅图片
好久没更新blog里,看到OpenCV官网做的越来越好,心里也是很高兴的,真有些冲动将来加入到这个组织里做些事。估计2.4.3要在国庆左右跟大家见面,让我们多期待一下吧。
闲话少说,今天不介绍复杂的算法了,来个简单的,大家写文章做图经常用Matlab,在Matlab里经常在一个窗口里打开多幅图片。遗憾的是OpenCV没有集成这样的功能,但这难不倒大家,让我试试用ROI来解决这个问题。
没啥好讲的,上代码好了:
- void imshowMany(const std::string& _winName, const vector<Mat>& _imgs)
- {
- int nImg = (int)_imgs.size();
- Mat dispImg;
- int size;
- int x, y;
- // w - Maximum number of images in a row
- // h - Maximum number of images in a column
- int w, h;
- // scale - How much we have to resize the image
- float scale;
- int max;
- if (nImg <= 0)
- {
- printf("Number of arguments too small....\n");
- return;
- }
- else if (nImg > 12)
- {
- printf("Number of arguments too large....\n");
- return;
- }
- else if (nImg == 1)
- {
- w = h = 1;
- size = 300;
- }
- else if (nImg == 2)
- {
- w = 2; h = 1;
- size = 300;
- }
- else if (nImg == 3 || nImg == 4)
- {
- w = 2; h = 2;
- size = 300;
- }
- else if (nImg == 5 || nImg == 6)
- {
- w = 3; h = 2;
- size = 200;
- }
- else if (nImg == 7 || nImg == 8)
- {
- w = 4; h = 2;
- size = 200;
- }
- else
- {
- w = 4; h = 3;
- size = 150;
- }
- dispImg.create(Size(100 + size*w, 60 + size*h), CV_8UC3);
- for (int i= 0, m=20, n=20; i<nImg; i++, m+=(20+size))
- {
- x = _imgs[i].cols;
- y = _imgs[i].rows;
- max = (x > y)? x: y;
- scale = (float) ( (float) max / size );
- if (i%w==0 && m!=20)
- {
- m = 20;
- n += 20+size;
- }
- Mat imgROI = dispImg(Rect(m, n, (int)(x/scale), (int)(y/scale)));
- resize(_imgs[i], imgROI, Size((int)(x/scale), (int)(y/scale)));
- }
- namedWindow(_winName);
- imshow(_winName, dispImg);
- }
附上效果图一张:
工程的下载链接为http://download.csdn.net/detail/yang_xian521/4531610
OpenCV学习笔记(五十八)——读《Master OpenCV》初感
好久没更新这个系列了。去年12月初的时候就知道出了一本OpenCV的新书《Master OpenCV with Practical Computer Vision Projects》,一直没来得及看,春节前也不想做什么任务,就把这书读一读吧。大概看了一下,和OpenCV的其他书对比了一下,感觉如下:
《Learning OpenCV》是一本经典的老书了,是一个入门教材,读完可以知道OpenCV能做些什么,但里面的具体代码个人觉得还是有点out,但好处就是中文资料很全,网上可以找到很多参考资料。
《OpenCV Cookbook》是我以前推荐过的一本书,是基于2.2版本写的。我的感觉是一本上手教材,书的目的是让大家知道应该如何去调用OpenCV的函数,如何用OpenCV的类去实现简单的视觉任务。需要一定的C++基础。
《Master OpenCV》感觉更像是一个上层建筑,是基于2.4版本写的。是教会大家如何用OpenCV去实现复杂的任务,去完成OpenCV自带函数没有提供的功能。(因为大家经常会因为“OpenCV里面有xxx的函数么?”的否定答案而苦恼,这本书就是告诉大家OpenCV只是个工具,如何去驾驭这个工具进行二次开发是要动脑的)
这本《Master OpenCV》书第一章没有很复杂的东西。
第一章就是介绍了一个边缘检测、肤色检测、一个填充算法,并把这个功能移植到android平台,边缘检测和填充算法都是OpenCV自带的函数功能。这里随便说说读完第一章的几点收获:
1、具体任务要具体分析。这里因为要做到嵌入式平台中,算法的复杂度被放在了首位,所以双边滤波做了简化、填充算法也只是在原图的缩小1/2的图上进行的计算。肤色检测也没有先用经典的人脸检测算法,而是用了一种土鳖的方案,都是为了在嵌入式平台上能运行的高效。这种处理问题的方法值得借鉴学习
2、面向对象编程的思想。这章里也提到了显示FPS,只是人家是在一个类中实现,再对比自己之前写的OpenCV学习笔记(三十八)——显示当前FPS,高下自分。实在是自惭形愧啊~~。
3、貌似把自己的c++工程移植到android平台并不需要改写自己的代码,只要做个JNI function作为接口就可以调用c++的程序了,感觉有点像Matlab中的mex。android下的OpenCV开发基本不懂,这里就不乱讲了。
这几天抓紧把这书读完,把后几章的阅读笔记也写出来分享一下。希望大家多多指正交流
OpenCV学习笔记(五十九)——marker检测识别"Master
第二章原本是讲如何将基于标定的增强现实在ios平台实现,包括以下4个方面:
1、在ios平台建立opencv工程
2、Marker检测识别
3、摄像机标定及Marker姿态估计
4、在Marker基础上渲染一个3维虚拟物体
这里面第一部分是IOS平台的开发,我不是太关注,略去;第四部分是基于OpenGL的3维虚拟物体建立,也是基于IOS平台,因为第三章里还要用到OpenGL,这里留着第三章再解剖。所以这里主要分析第二部分和第三部分。这一篇介绍第二部分。感觉这个东西有点像二维码识别。不知道2维码是怎么做的哦
MarkerDetection类任务processFrame:图1
转为灰度图、2值化(固定阈值法threshold:受光照等影响明显;自适应阈值法adaptiveThreshold:更好)(我这里用OpenCV243里的adaptiveThreshold函数未能实现自适应滤波的效果,效果像边缘检测的算法,很困惑。。最后用threshold函数代替,这个问题未能解决,希望高手指点,ps:网上高手多啊,这个问题已经解决了),检测后的结果如图threshold(图1左上)
findMarkerContours函数进行轮廓检测findContours(用多边形的顶点最好,去掉小于阈值的点(对小的轮廓不感兴趣),把每个轮廓的点按照逆时针排序,并去掉距离太近的轮廓),结果如图contours(图1中上)
接下来findMarkerCandidates函数对轮廓进行筛选,先用approxPolyDP得到轮廓近似的多边形。进行筛选,为凸多边形且顶点为4的才有可能是marker,并检测这个4边形的边长,最小边长如果小于10pixel,也不认为是一个marker。然后把得到的可能的marker的轮廓点按照逆时针排序。并且检测是否检测到重复的marker,如果检测到重复的marker,去掉周长更短的那个。这步之后效果如下markerCandidate(图1左下)
detectMarkers函数有3个任务,去除投影变换的影响(getPerspectiveTransform得到投影矩阵,warpPerspective得到正面的视角的图像),得到marker正面的视图。
然后对这个marker的正面图进行解码,threshold对marker使用THRESH_OTSU进行2值化。效果图:
接下来对这个marker进行识别marker.decode。检测编码marker(对marker解码,marker编码为7*7的栅格,中心5*5为ID,周围一圈为黑色边界,检测的时候先检测周围一圈是否为黑色边界,然后再对中心5*5解码(注意,只有5*5具有旋转不变形才能得到唯一的码),是5bit*5word,每个word中的5bit,2位为id(2位4位),3位为校验码(用来保证旋转),所以5word一共有2^10=1024个不同id,而且第一位要置反,目的是要防止一个word全黑,不易检测。举例,我这里使用的marker的5个word的id分别为10、01、11、11、11。那么如何从刚刚得到的marker图提取出7*7的2值栅格呢,这里用个Mat(Rect)取marker中的小方块,用countNonZero来判断这个方块为0or1。因为marker有4个方向,哪个方向才是我对应的marker的id的,这里用id和我验证用的id的hamming距离来做依据,汉明距最小的即marker的方向。
确认为一个marker后再得到轮廓的细致的corner,使用cornerSubPix,这时才进行细化,是因为这个函数相对耗时,如果之前就对各corner细化,由于候选目标很多,会加重计算负担。)效果图marker(图1右下)。
最后我试了其他的marker编码,都能正确解码出id信息,效果图如下:第一幅图的id为0011010101,第二幅图为第一图的旋转,id相同,第三图id为0011000110,第四图不是一个marker,故没有检测出来。
代码的下载地址:http://download.csdn.net/detail/yang_xian521/5040634
在下一篇里,将介绍如何用这个marker的轮廓位置,和轮廓(红色)的方向(黄点)来在marker上建立一个3维的虚拟物体。
OpenCV"chp.2 OpenCV学习笔记(六十一)——建立支持OpenGL的OpenCV工程“Master OpenCV”chp.3
从OpenCV2.4beta版本,OpenGL就可以有接口到highgui的模块中了。结合Master OpenCV第三章的阅读,这里说说如何在OpenCV的显示中嵌入OpenGL的虚拟物体。
要注意的一点:如果想使OpenCV支持OpenGL,不能使用预编译好的library,要用cmake rebuild工程,注意ENABLE_OPENGL = YES,(在2.4.2版本中,默认ENABLE_OPENGL = NO),标签的改变在CMake的高级版本都是图形界面的,只需把WITH_OPENGL的对号勾选即可。
这里实战过程中我还遇到了一个问题,用这个CMake得到的vs工程(添加了WITH_OPENGL)无法编译通过,郁闷了好久。因为opengl在vs中是支持的,不需要安装,最后找到了这个bug,需要把\modules\core\src\opengl_interop.cpp文件中使用<gl\gl.h>前面添加#include <windows.h>,(其实#include <gl.h>前都需要添加#include <windows.h>)这样才能编译通过,这里我只重新编译opencv_core243d.lib 和opencv_highgui243d.lib
已经得到了支持OpenGL的OpenCV lib,接下来就是如何用OpenCV建立OpenGL窗口,基本的调用方式很像OpenCV中鼠标的使用,都是通过回调函数实现,核心代码如下:
- // callback function
- void onDraw(void* param)
- {
- // Draw something using OpenGL here
- }
- int main(void)
- {
- string openGLWindowName = "OpenGL Test";
- namedWindow(openGLWindowName, WINDOW_OPENGL);
- resizeWindow(openGLWindowName, 640, 480);
- setOpenGlContext(openGLWindowName);
- setOpenGlDrawCallback(openGLWindowName, onDraw, NULL);
- updateWindow(openGLWindowName); // when needed
- return 0;
- }
以前我们调用 namedWindow最后一个参数通常会用默认或者使用WINDOW_AUTOSIZE,这回用 WINDOW_OPENGL,然后调用 setOpenGLContext建立窗口关联,为了在这个窗口上画虚拟物体,需要使用回调函数,建立方法就是 setOpenGLDrawCallback,注意这个函数第一个参数是窗口名称,第二个参数是回调函数名,第三个参数是回调函数的参数,因为我这里回调函数onDraw是无参函数,所以这里为NULL。跟MFC重绘需要调用Invalidate或者uadate类似,在需要重绘的时候还要调用 updateWindow。
把我做的一个最基础的OpenGL演示上传:http://download.csdn.net/detail/yang_xian521/5023063(附上我rebulid的支持OpenGL的lib),效果图如下:
OpenCV学习笔记(六十二)——《OpenCV Computer Version with Python》阅读摘要
现在python火啊,每次OpenCV自带的ml模块都让我直呼坑爹,索性准备用python来做OpenCV后期的机器学习算法的处理。于是赶紧拿起这本书读读。
适合OpenCV和python都有一定基础的。。。。由于都比较熟悉这两个东西,我阅读之前比较关心的只有几个问题,具体的应用实例没有仔细看。
1.如何在python中安装opencv
2.OpenCV的Mat数据结构能否方便的转换成numpy的array结构
3.OpenCV的GUI模块在python里好用么
4.二者还能擦出什么我想不到的火花么。。。。
书中提到在windows系统中,python-32bit表现的比64bit要好,推荐安装32位的python
第一个问题在windows下很简单,OpenCV安装好之后,找到目录<build_folder>\lib\Release\cv2.pyd(from a Visual Studio build) 这个文件,然后copy到C:\Python2.7\Lib\site-packages。搞定了,就这么简单。毕竟脚本语言,简直无情,\sources\samples下有很多python的例子,跑几个试试就知道是否安装好了。import cv2这句就可以导入cv2模块了
第二个问题也不用担心了,因为python不用声明变量的类型,实验了一下,发现得到的矩阵的数据类型就是array,稳了,直接拿来用。
第三个问题也超简单,图像显示读写的模块、摄像头模块、鼠标键盘的响应模块都可以,跟c++的版本使用起来也差不多。
第四个问题我简单粗看了一遍书,没发现什么亮点,只是书中提到一个pygame可以用来做hgui效果还行,支持画画和编辑文本,不过好像对CV也没啥帮助,所以就没研究了。
补充几个我学习的时候遇见的问题:
opencv里的Rect数据结构在python里是没有对应类型的,这个要注意调用的时候需要注意。比如rectangle函数输入的就是矩形两个点的坐标,不是Rect。
还有就是opencv里的很多宏在python里需要加上cv2.cv前缀就可以生效了。
from: http://blog.csdn.net/yang_xian521/article/category/910716