/*****************************************************************************
* Marker.cpp
* Example_MarkerBasedAR
******************************************************************************
* by Khvedchenia Ievgen, 5th Dec 2012
* http://computer-vision-talks.com
******************************************************************************
* Ch2 of the book "Mastering OpenCV with Practical Computer Vision Projects"
* Copyright Packt Publishing 2012.
* http://www.packtpub.com/cool-projects-with-opencv/book
*****************************************************************************/
#include "Marker.hpp"
#include "DebugHelpers.hpp"
Marker::Marker()
: id(-1)// id初始化為-1
{
}
// 重載了‘<’運算符
bool operator<(const Marker &M1,const Marker&M2)
{
return M1.id<M2.id;
}
// 順時針旋轉标記矩陣
cv::Mat Marker::rotate(cv::Mat in)
{
cv::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);
}
}
return out;
}
// 計算标記矩陣的海明距離
int Marker::hammDistMarker(cv::Mat bits)
{
// bits中的每一行,都要與ids中的所有行進行比較
int ids[4][5]=
{
// id的順序無所謂
{1,0,0,0,0},
{1,0,1,1,1},
{0,1,0,0,1},
{0,1,1,1,0}
};
int dist=0;
// rows
for (int y=0;y<5;y++)
{
int minSum=1e5; // hamming distance to each possible word
for (int p=0;p<4;p++)
{
int sum=0;
// 統計海明距離
// cols
for (int x=0;x<5;x++)
{
sum += (bits.at<uchar>(y,x) == ids[p][x]) ? 0 : 1;
}
// 求bits中第y行與ids中第p行的最小距離
if (minSum>sum)
minSum=sum;
}
// 計算bits中所有行的總距離
dist += minSum;
}
return dist;
}
// mat轉id
int Marker::mat2id(const cv::Mat &bits)
{
int val=0;
for (int y=0;y<5;y++)
{
val<<=1;// val=val<<1
// 隻有1和3位是有用的,其他3位(0 2 4)是校驗位
if ( bits.at<uchar>(y,1)) val|=1;
val<<=1;
if ( bits.at<uchar>(y,3)) val|=1;
}
return val;
}
int Marker::getMarkerId(cv::Mat &markerImage,int &nRotations)
{
assert(markerImage.rows == markerImage.cols);
assert(markerImage.type() == CV_8UC1);
cv::Mat grey = markerImage;
// Threshold image
cv::threshold(grey, grey, 125, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
#ifdef SHOW_DEBUG_IMAGES
cv::showAndSave("Binary marker", grey);
#endif
//Markers are divided in 7x7 regions, of which the inner 5x5 belongs to marker info
//the external border should be entirely black
// 檢測是否是标記的邊界
int cellSize = markerImage.rows / 7;
for (int y=0;y<7;y++)
{
int inc=6;
if (y==0 || y==6) inc=1; //for first and last row, check the whole border
for (int x=0;x<7;x+=inc)
{
int cellX = x * cellSize;
int cellY = y * cellSize;
// 在grey矩陣中取ROI矩陣
cv::Mat cell = grey(cv::Rect(cellX,cellY,cellSize,cellSize));
int nZ = cv::countNonZero(cell);
if (nZ > (cellSize*cellSize) / 2)
{
return -1;//can not be a marker because the border element is not black!
}
}
}
cv::Mat bitMatrix = cv::Mat::zeros(5,5,CV_8UC1);
//get information(for each inner square, determine if it is black or white)
// 根據5*5的黑白格子元素來設定bitMatrix矩陣:白色=1 黑色=0
for (int y=0;y<5;y++)
{
for (int x=0;x<5;x++)
{ // 從5*5的第一個格子開始
int cellX = (x+1)*cellSize;
int cellY = (y+1)*cellSize;
// 在grey矩陣中取ROI矩陣
cv::Mat cell = grey(cv::Rect(cellX,cellY,cellSize,cellSize));
int nZ = cv::countNonZero(cell);
if (nZ> (cellSize*cellSize) /2)
bitMatrix.at<uchar>(y,x) = 1;
}
}
//check all possible rotations
cv::Mat rotations[4];
int distances[4];
rotations[0] = bitMatrix;
distances[0] = hammDistMarker(rotations[0]);
// 将第一個标記的海明距離作為最小距離
// 鍵值對裡存的時 1.最小距離 2.對應标記的索引
std::pair<int,int> minDist(distances[0],0);
for (int i=1; i<4; i++)
{
//get the hamming distance to the nearest possible word
// 将其餘的三種旋轉可能标記的海明距離與最小距離進行比較ß
rotations[i] = rotate(rotations[i-1]);
distances[i] = hammDistMarker(rotations[i]);
if (distances[i] < minDist.first)
{
minDist.first = distances[i];
minDist.second = i;
}
}
// 儲存識别出的标記的索引
nRotations = minDist.second;
if (minDist.first == 0)
{
return mat2id(rotations[minDist.second]);
}
return -1;
}
void Marker::drawContour(cv::Mat& image, cv::Scalar color) const
{
float thickness = 2;
cv::line(image, points[0], points[1], color, thickness, CV_AA);
cv::line(image, points[1], points[2], color, thickness, CV_AA);
cv::line(image, points[2], points[3], color, thickness, CV_AA);
cv::line(image, points[3], points[0], color, thickness, CV_AA);
}
另附上TinyLA.cpp中的相關注釋:
bool isInto(cv::Mat &contour, std::vector<cv::Point2f> &b)
{
for (size_t i=0;i<b.size();i++)
{
// 檢查指定的點和指定的多邊形的相對位置關系(在多邊形内、外、邊上或頂點上)
// 傳回值:該點和多邊形上最近的一條邊有符号的距離(>0表示在内部,<0表示在外部,=0表示在邊上)
// 當最後一個參數值為0的時候,傳回的距離隻會是100或-100.
if (cv::pointPolygonTest( contour,b[i],false)>0) return true;
}
return false;
}
