Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added detection of inverted Markers, tests included #2055

Merged
merged 2 commits into from
May 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions modules/aruco/include/opencv2/aruco.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ enum CornerRefineMethod{
* done at full resolution.
* - aprilTagQuadSigma: What Gaussian blur should be applied to the segmented image (used for quad detection?)
* Parameter is the standard deviation in pixels. Very noisy images benefit from non-zero values (e.g. 0.8).
* - detectInvertedMarker: to check if there is a white marker. In order to generate a "white" marker just
* invert a normal marker by using a tilde, ~markerImage. (default false)
*/
struct CV_EXPORTS_W DetectorParameters {

Expand Down Expand Up @@ -183,6 +185,9 @@ struct CV_EXPORTS_W DetectorParameters {
CV_PROP_RW float aprilTagMaxLineFitMse;
CV_PROP_RW int aprilTagMinWhiteBlackDiff;
CV_PROP_RW int aprilTagDeglitch;

// to detect white (inverted) markers
CV_PROP_RW bool detectInvertedMarker;
};


Expand Down
17 changes: 15 additions & 2 deletions modules/aruco/src/aruco.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ DetectorParameters::DetectorParameters()
aprilTagCriticalRad( (float)(10* CV_PI /180) ),
aprilTagMaxLineFitMse(10.0),
aprilTagMinWhiteBlackDiff(5),
aprilTagDeglitch(0){}
aprilTagDeglitch(0),
detectInvertedMarker(false){}


/**
Expand Down Expand Up @@ -512,7 +513,19 @@ static bool _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray
int(dictionary->markerSize * dictionary->markerSize * params->maxErroneousBitsInBorderRate);
int borderErrors =
_getBorderErrors(candidateBits, dictionary->markerSize, params->markerBorderBits);
if(borderErrors > maximumErrorsInBorder) return false; // border is wrong

// check if it is a white marker
if(params->detectInvertedMarker){
// to get from 255 to 1
Mat invertedImg = ~candidateBits-254;
int invBError = _getBorderErrors(invertedImg, dictionary->markerSize, params->markerBorderBits);
// white marker
if(invBError<borderErrors){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix indentation (it is "jumping").

borderErrors = invBError;
invertedImg.copyTo(candidateBits);
}
}
if(borderErrors > maximumErrorsInBorder) return false;

// take only inner bits
Mat onlyBits =
Expand Down
104 changes: 79 additions & 25 deletions modules/aruco/test/test_arucodetection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,29 @@ static void getSyntheticRT(double yaw, double pitch, double distance, Mat &rvec,
*/
static Mat projectMarker(Ptr<aruco::Dictionary> &dictionary, int id, Mat cameraMatrix, double yaw,
double pitch, double distance, Size imageSize, int markerBorder,
vector< Point2f > &corners) {
vector< Point2f > &corners, int encloseMarker=0) {

// canonical image
Mat markerImg;
Mat marker, markerImg;
const int markerSizePixels = 100;
aruco::drawMarker(dictionary, id, markerSizePixels, markerImg, markerBorder);

aruco::drawMarker(dictionary, id, markerSizePixels, marker, markerBorder);
marker.copyTo(markerImg);

if(encloseMarker){ //to enclose the marker
int enclose = int(marker.rows/4);
markerImg = Mat::zeros(marker.rows+(2*enclose), marker.cols+(enclose*2), CV_8UC1);

Mat field= markerImg.rowRange(int(enclose), int(markerImg.rows-enclose))
.colRange(int(0), int(markerImg.cols));
field.setTo(255);
field= markerImg.rowRange(int(0), int(markerImg.rows))
.colRange(int(enclose), int(markerImg.cols-enclose));
field.setTo(255);

field = markerImg(Rect(enclose,enclose,marker.rows,marker.cols));
marker.copyTo(field);
}

// get rvec and tvec for the perspective
Mat rvec, tvec;
Expand All @@ -205,10 +222,10 @@ static Mat projectMarker(Ptr<aruco::Dictionary> &dictionary, int id, Mat cameraM
projectPoints(markerObjPoints, rvec, tvec, cameraMatrix, distCoeffs, corners);

vector< Point2f > originalCorners;
originalCorners.push_back(Point2f(0, 0));
originalCorners.push_back(Point2f((float)markerSizePixels, 0));
originalCorners.push_back(Point2f((float)markerSizePixels, (float)markerSizePixels));
originalCorners.push_back(Point2f(0, (float)markerSizePixels));
originalCorners.push_back(Point2f(0+float(encloseMarker*markerSizePixels/4), 0+float(encloseMarker*markerSizePixels/4)));
originalCorners.push_back(originalCorners[0]+Point2f((float)markerSizePixels, 0));
originalCorners.push_back(originalCorners[0]+Point2f((float)markerSizePixels, (float)markerSizePixels));
originalCorners.push_back(originalCorners[0]+Point2f(0, (float)markerSizePixels));

Mat transformation = getPerspectiveTransform(originalCorners, corners);

Expand Down Expand Up @@ -238,6 +255,11 @@ class CV_ArucoDetectionPerspective : public cvtest::BaseTest {
public:
CV_ArucoDetectionPerspective();

enum checkWithParameter{
USE_APRILTAG=1, /// Detect marker candidates :: using AprilTag
DETECT_INVERTED_MARKER, /// Check if there is a white marker
};

protected:
void run(int);
};
Expand All @@ -246,9 +268,10 @@ class CV_ArucoDetectionPerspective : public cvtest::BaseTest {
CV_ArucoDetectionPerspective::CV_ArucoDetectionPerspective() {}


void CV_ArucoDetectionPerspective::run(int aprilDecimate) {
void CV_ArucoDetectionPerspective::run(int tryWith) {

int iter = 0;
int szEnclosed = 0;
Mat cameraMatrix = Mat::eye(3, 3, CV_64FC1);
Size imgSize(500, 500);
cameraMatrix.at< double >(0, 0) = cameraMatrix.at< double >(1, 1) = 650;
Expand All @@ -257,29 +280,35 @@ void CV_ArucoDetectionPerspective::run(int aprilDecimate) {
Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);

// detect from different positions
for(double distance = 0.1; distance <= 0.5; distance += 0.2) {
for(int pitch = 0; pitch < 360; pitch += 60) {
for(int yaw = 30; yaw <= 90; yaw += 50) {
for(double distance = 0.1; distance < 0.7; distance += 0.2) {
for(int pitch = 0; pitch < 360; pitch += (distance == 0.1? 60:180)) {
for(int yaw = 70; yaw <= 120; yaw += 40){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int yaw = 70; yaw <= 120; yaw += 40

yaw: 70, 110 ?

Old yaw values are: 30, 80

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even though the detection of a default marker happens using that yaw value, it was not the case for the inverted marker, however, it also occurs in the opposite way, that is, where the inverted marker is detected more easily than the default marker.

int currentId = iter % 250;
int markerBorder = iter % 2 + 1;
iter++;
vector< Point2f > groundTruthCorners;
// create synthetic image
Mat img =
projectMarker(dictionary, currentId, cameraMatrix, deg2rad(yaw), deg2rad(pitch),
distance, imgSize, markerBorder, groundTruthCorners);

// detect markers
vector< vector< Point2f > > corners;
vector< int > ids;
Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
params->minDistanceToBorder = 1;
params->markerBorderBits = markerBorder;

if(aprilDecimate == 1){
params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_APRILTAG;
/// create synthetic image
Mat img=
projectMarker(dictionary, currentId, cameraMatrix, deg2rad(yaw), deg2rad(pitch),
distance, imgSize, markerBorder, groundTruthCorners, szEnclosed);
szk1509 marked this conversation as resolved.
Show resolved Hide resolved
// marker :: Inverted
if(CV_ArucoDetectionPerspective::DETECT_INVERTED_MARKER == tryWith){
img = ~img;
params->detectInvertedMarker = true;
}

if(CV_ArucoDetectionPerspective::USE_APRILTAG == tryWith){
params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_APRILTAG;
}

// detect markers
vector< vector< Point2f > > corners;
vector< int > ids;
aruco::detectMarkers(img, dictionary, corners, ids, params);

// check results
Expand All @@ -293,14 +322,33 @@ void CV_ArucoDetectionPerspective::run(int aprilDecimate) {
}
for(int c = 0; c < 4; c++) {
double dist = cv::norm(groundTruthCorners[c] - corners[0][c]); // TODO cvtest
if(dist > 5) {
ts->printf(cvtest::TS::LOG, "Incorrect marker corners position");
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
return;
if(CV_ArucoDetectionPerspective::DETECT_INVERTED_MARKER == tryWith){
if(szEnclosed && dist > 3){
ts->printf(cvtest::TS::LOG, "Incorrect marker corners position");
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
return;
}
if(!szEnclosed && dist >15){
ts->printf(cvtest::TS::LOG, "Incorrect marker corners position");
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
return;
}
}
else{
if(dist > 5) {
ts->printf(cvtest::TS::LOG, "Incorrect marker corners position");
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
return;
}
}
}
}
}
// change the state :: to detect an enclosed inverted marker
if( CV_ArucoDetectionPerspective::DETECT_INVERTED_MARKER == tryWith && distance == 0.1 ){
distance -= 0.1;
szEnclosed++;
}
}
}

Expand Down Expand Up @@ -488,10 +536,16 @@ void CV_ArucoBitCorrection::run(int) {
}

typedef CV_ArucoDetectionPerspective CV_AprilTagDetectionPerspective;
typedef CV_ArucoDetectionPerspective CV_InvertedArucoDetectionPerspective;

TEST(CV_InvertedArucoDetectionPerspective, algorithmic) {
CV_InvertedArucoDetectionPerspective test;
test.safe_run(CV_ArucoDetectionPerspective::DETECT_INVERTED_MARKER);
}

TEST(CV_AprilTagDetectionPerspective, algorithmic) {
CV_AprilTagDetectionPerspective test;
test.safe_run(1);
test.safe_run(CV_ArucoDetectionPerspective::USE_APRILTAG);
}

TEST(CV_ArucoDetectionSimple, algorithmic) {
Expand Down