以下为实现aruco检测并读取id的代码,直接复制粘贴即可。相信看到这篇博客的伙伴应该知道aruco,我就不解释了。
opencv3.0以上有实现aruco的库。
一、cmake编译信息
Cmakelist.txt
cmake_minimum_required(VERSION 2.6)
project(artag2)
find_package(OpenCV REQUIRED )
include_directories(${OpenCV_INCLUDE_DIR})
link_directories(${OpenCV_LIBRARY_DIR})
add_executable(artag2 main.cpp Marker.cpp)
target_link_libraries( artag2 ${OpenCV_LIBRARIES} )
install(TARGETS artag2 RUNTIME DESTINATION bin)
二、源码实现
Marker.hpp
#include "Marker.hpp"
using namespace cv;
using namespace std;
Marker::Marker()
:id(-1)
{
}
bool operator<(const Marker& M1,const Marker& M2)
{
return M1.id<M2.id;
}
Mat Marker::rotate(Mat in)//就是把矩阵旋转90度
{
Mat out;
in.copyTo(out);
for(int i=0;i<in.rows;i++)
{
for(int j=0;j<in.cols;j++)
{
out.at<uchar>(i,j)=in.at<uchar>(in.cols-j-1,i);//at<uchar>用来指定某个位置的像素,同时指定数据类型。就是交换元素,怎么交换的?
}
}
return out;
}
//在信息论中,两个等长字符串之间的汉明距离是两个字符串对应位置的字符不同的个数。换句话说,它就是将一个字符串变换成另外一个字符串所需要替换的字符个数。
int Marker::hammDistMarker(Mat bits)//对每个可能的标记方向找到海明距离,和参考标识一致的为0,其他旋转形式的标记不为0,因为经过透射变换后,只能得到四个方向的标记,则旋转四次,找到和参考标识一致的方向。
{
int ids[4][5]=
{
{1,0,0,0,0},
{1,0,1,1,1},
{0,1,0,0,1},
{0,1,1,1,0}
};
int dist = 0;
for(int y=0;y<5;y++)
{
int minSum = 1e5;//每个元素的海明距离
for(int p=0;p<4;p++)
{
int sum=0;
//now,count
for(int x=0;x<5;x++)
{
sum += bits.at<uchar>(y,x) == ids[p][x]?0:1;
}
if(minSum>sum)
minSum=sum;
}
dist += minSum;
}
return dist;
}
int Marker::mat2id(const Mat& bits)//移位,求或,再移位,得到最终的ID
{
int val=0;
for(int y=0;y<5;y++)
{
val<<=1;//移位操作
if(bits.at<uchar>(y,1)) val |= 1;
val<<=1;
if(bits.at<uchar>(y,3)) val |= 1;
}
return val;
}
int Marker::getMarkerId(Mat& markerImage,int &nRotations)
{
assert(markerImage.rows == markerImage.cols);//如果它的条件返回错误,则终止程序执行
assert(markerImage.type() == CV_8UC1);
Mat grey = markerImage;
//Threshold image使用Otsu算法移除灰色的像素,只留下黑色和白色像素。
//这是固定阀值方法
//输入图像image必须为一个2值单通道图像
//检测的轮廓数组,每一个轮廓用一个point类型的vector表示
//阀值
//max_value 使用 CV_THRESH_BINARY 和 CV_THRESH_BINARY_INV 的最大值
//type
threshold(grey,grey,125,255,THRESH_BINARY | THRESH_OTSU);//对候选标记区域的灰度图使用大律OSTU算法,求取二值化图,大范围图片用这个算法会影响性能。
#ifdef SHOW_DEBUG_IMAGES
imshow("Binary marker",grey);
imwrite("Binary marker" + ".png",grey);
#endif
//所使用的标记都有一个内部的5x5编码,采用的是简单修改的汉明码。简单的说,就是5bits中只有2bits被使用,其他三位都是错误的识别码,也就是说我们至多有1024种不同的标识。我们的汉明码最大的不同是,汉明码的第一位(奇偶校验位的3和5)是反向的。所有ID 0(在汉明码是00000),在这里是10000,目的是减少环境造成的影响.
//标识被划分为7x7的网格,内部的5x5表示标识内容,额外的是黑色边界,接下来是逐个检查四条边的像素是否都是黑色的,若有不是黑色,那么就不是标识。
int cellSize = markerImage.rows/7;
for(int