背景:
这个问题也是在CV群里,有位同志问的,我给她解决了,就写个博客记录下!这个图形如下所示,要定位到图中的6个黑点和判断他们的方向,如图的方向为指向左边!
分析:
1、定位到目标点(图中那6个)
怎么定位到图中的6个小黑点捏?我的方法是二值化后寻找图形的外部连通域,很显然利用此方法后可以很容易的索引到图中的索引白色圆,然后怎么确定小黑点位置捏?我是利用这些连通域求出所有轮廓的重心,这里是圆形的也就是他们的圆心,然后判断圆心坐标位置的像素值是否为黑色像素(0),如果是的话那么该重心就是目标点。(我还想到另一种方法:利用霍夫变换检测图中的所有圆形,我们就可以很容易的得到半径,判断方法和上面一样,判断圆心的像素即可),但是我为嘛采用连通域然后求重心勒,因为,用霍夫变换求圆心的话有局限性,这个圆要比较大一点才好检测,而且如果变成正方形咋办了,而利用连通域和重心的话,只要是对称的图像都可以检测到他们的重心!
2、利用这6个点判断他们的方向
经过步骤一,我们可以获取到图中的6个目标点(他们的坐标),接下来我就利用这6个点的坐标求出他们的方向的,仔细观察规律,不难发现,对图中的6个点的X轴坐标求和,在求均值得到xAve。然后用每一个点的X坐标与这个均值做差的绝对值|Xi - xAve|得到的结果的最大值就是水平方向的箭头的位置,如上图,做6次差值后,其中差值绝对值最大的会是最左边的哪一个点,也就是箭头的位置点,所以可以判断出方向了!(我还想到了另一种方法,就是利用莫办匹配法,事先先去提取上下左右方向的模板,然后我们获得目标模板后与事先提取的模板进行匹配,就可以判断方向了,系不系啊!)。
实现:
代码如下:
void findDirect(){
// to gray
cv::cvtColor(cellImage,result1d,CV_BGR2GRAY);
cv::cvtColor(cellImage,result3d,CV_BGR2GRAY);
// threshold
cv::threshold(result1d,result1d,128,255,cv::THRESH_BINARY);
cv::threshold(result3d,result3d,128,255,cv::THRESH_BINARY);
// find contours and got contours sum
cv::findContours(result1d,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
int ctsSum = contours.size();
int ptn_cnt = 0;
int px[6];
int py[6];
for(int i=0;i<ctsSum;i++){
//cv::drawContours(cellImage,contours,i,cv::Scalar(255,0,0),1);
// 计算重心
cv::Moments mom = cv::moments(cv::Mat(contours[i]));
uchar *data = result3d.ptr<uchar>(static_cast<int >(mom.m01/mom.m00));
int x = static_cast<int >(mom.m10/mom.m00);
if(data[x] == 0){
cv::circle(cellImage,cv::Point(mom.m10/mom.m00,mom.m01/mom.m00),3,cv::Scalar(0,0,255),2);
px[ptn_cnt] = mom.m10/mom.m00;
py[ptn_cnt] = mom.m01/mom.m00;
ptn_cnt++;
}
}
qDebug() << "ptn cnt = " << ptn_cnt;
int xsum = 0;
int ysum = 0;
for(int i=0;i<ptn_cnt;i++){
xsum += px[i];
ysum += py[i];
}
int xave = xsum/ptn_cnt;
int yave = ysum/ptn_cnt;
int dismax = fabs(xave - px[0]);
int obj_index = 0;
for(int i=1;i<ptn_cnt;i++){
if(fabs(xave - px[i]) > dismax){
dismax = fabs(xave - px[i]);
obj_index = i;
}
}
cv::circle(cellImage,cv::Point(px[obj_index],py[obj_index]),3,cv::Scalar(255,0,0),2);
// show marked in cellImage
cv2QtImageAndShow(cellImage);
}
效果图如下:
如上图,图中绿色位置是索引到的全部连通域,红色位置是所以得6个黑点(那个蓝色的圆心也是红色的,被后面的代码再次用蓝色覆盖而已),而蓝色的位置显然就是方向咯!
OK!3KS 2 SEE!!!
oh!对了,也许你会问图像怎么变大了,那是因为我放大了!!!