Zedboard甲诊opencv图像处理(一)

首先基于前面的工作,通过调整已经很好的把指甲边缘显示出来了,不曾想我却从那时开始走上了弯路,使用matlab去处理静态图片,以获得更好的指甲和特征提取效果,结果就是,效果不理想(光照影响)并且用到摄像头上来一点都不实用。辛辛苦苦的研究了n多的图像处理算法,几乎把边缘提取的算法全部过了一遍,阅读了不下100篇论文,还折腾出B样条曲线拟合进行边缘连接,现在想想太可笑了,其实OpenCV现有的图像处理方法已经可以满足我提取指甲的要求了,只不过我需要进行合理的组合搭配,从而实现我想要的效果。

总有那么一句话叫:摸着石头过河。我今天算是体会到了,不过中间老板还让写创业计划,总之,该回归正道了!

接着之前的canny提取的边缘,我想进一步提取出指甲,把其他背景都省略,这样就只留下指甲了,从而进一步提取特征。首先我想到的是findContours函数及其轮廓操作

//! retrieves contours and the hierarchical information from black-n-white image.
CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset=Point());

//! retrieves contours from black-n-white image.
CV_EXPORTS void findContours( InputOutputArray image, OutputArrayOfArrays contours,
int mode, int method, Point offset=Point());

其中image为输入的 binary image,输出是一个向量数组,每个向量是Points类型的指针向量数组。这个数组会用做后面的轮廓处理。

mode代表提取轮廓的类型, 可以是以下几种类型 //CV_RETR_LIST, // retrieve all contours
//CV_RETR_EXTERNAL, // retrieve the external contours
//CV_RETR_TREE, // retrieve all contours in tree format
//CV_RETR_CCOMP is similar but limits the hierarchy at two levels.建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。

method代表轮廓点的类型,可以有 //CV_CHAIN_APPROX_NONE // all pixels of each contours存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
//CV_CHAIN_APPROX_SIMPLE //only the end points would be included for horizontal,vertical, or diagonal contours. 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息

最后一个不用管先,然后就是一个OutputArray hierarchy代表分层结构,hierarchy, // hierarchical representation

接下来用 cv::Mat result(image.size(),CV_8U,cv::Scalar(255));
cv::drawContours(result,contours,
-1, // draw all contours
cv::Scalar(0), // in black
2); // with a thickness of 2

出来的效果还不错,利用黑背景,能将指甲分离出来,但是接下来的分离过程除了错误。就是在我将多余的或者不符合要求的轮廓去除过程中报错了

// Eliminate too short or too long contours
int cmin= 100; // minimum contour length
int cmax= 1000; // maximum contour length
std::vector >::const_iterator itc = contours.begin();
while (itc!=contours.end())
{
if (itc->size() < cmin || itc->size() > cmax)
itc=contours.erase(itc);
else
++itc;
}

错误如下:错误:no matching function for call to 'std::vector > >::erase(std::vector > >::const_iterator&)',我理解的意思是这里erase函数用的不对,但是我是照着OpenCVcookbook做的,网上也搜了很多,都是这样写的,感觉不同于他们的是我的实在QT中编写,用OpenCV2.3.1,不知到为什么会报错,

然后就找他的定义,感觉用得也没错:

iterator
erase(iterator __position)
{
if (__position + 1 != end())
std::copy(__position + 1, end(), __position);
--this->_M_impl._M_finish;
return __position;
}

iterator
erase(iterator __first, iterator __last)
{
_M_erase_at_end(std::copy(__last, end(), __first));
return __first;
}

后来仔细看了,iterator和const_iterator不一样, typedef __gnu_cxx::__normal_iterator iterator; typedef __gnu_cxx::__normal_iterator const_iterator;

而我抄袭的程序里面都是const_iterator,该过来之后就可以了。

现在的问题是,我该怎么取适当的值,使得只有指甲的轮廓保留下来,其余的省去。还是一步一步来吧,先把轮廓查找的过程搞清楚。

findContours经常与drawContours配合使用,用来将轮廓绘制出来。其中第一个参数image表示目标图像,第二个参数contours表示输入的轮廓组,每一组轮廓由点vector构成,第三个参数contourIdx指明画第几个轮廓,如果该参数为负值,则画全部轮廓,第四个参数color为轮廓的颜色,第五个参数thickness为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部,第六个参数lineType为线型,第七个参数为轮廓结构信息,第八个参数为maxLevel。

但是用在视频处理中,还是不行,所以我改为拍照处理,拍5张照片后重合为一张,效果如下:

虽然还不是很好,但我也只能勉强的继续往下走着先。这过程中涉及到一些Mat图像基本容器的处理,因为我是存储了5张图片,所以不能直接赋值,需要用cvcopyto函数。

capture.read(frame);
++flag;
switch(flag)
{
case1:
frame.copyTo(image1);
break;
case2:
frame.copyTo(image2);
break;
case3:
frame.copyTo(image3);
break;
case4:
frame.copyTo(image4);
break;
default:
flag=0;
break;
}

后然处理重合吧:

Mat edge1=edged(image1);
Mat edge2=edged(image2);
Mat edge3=edged(image3);
Mat edge4=edged(image4);
Mat edge;
Mat result;
cv::addWeighted(edge1,1,edge2,1,0.,edge);
cv::addWeighted(edge,1,edge3,1,0.,edge);
cv::addWeighted(edge,1,edge4,1,0.,edge);

接下来我准备进行多边形逼近或者其他手段把,把背景去除,然后特征提取。比较慢哈,不过最近听说一位美国留学的博士师兄在做视网膜诊病,和我这个甲诊有点类似之处哈,好歹给了我一点鼓励吧。


来源:preorder69的博客园