Skip to content
49 changes: 45 additions & 4 deletions modules/aruco/include/opencv2/aruco.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,33 @@ enum CornerRefineMethod{
* Parameter is the standard deviation in pixels. Very noisy images benefit from non-zero values (e.g. 0.8). (default 0.0)
* - 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)
* - useAruco3Detection: to enable the new and faster Aruco detection strategy. The most important observation from the authors of
* Romero-Ramirez et al: Speeded up detection of squared fiducial markers (2018) is, that the binary
* code of a marker can be reliably detected if the canonical image (that is used to extract the binary code)
* has a size of minSideLengthCanonicalImg (in practice tau_c=16-32 pixels).
* In addition, very small markers are barely useful for pose estimation and thus a we can define a minimum marker size that we
* still want to be able to detect (e.g. 50x50 pixel).
* To decouple this from the initial image size they propose to resize the input image
* to (I_w_r, I_h_r) = (tau_c / tau_dot_i) * (I_w, I_h), with tau_dot_i = tau_c + max(I_w,I_h) * tau_i.
* Here tau_i (parameter: minMarkerLengthRatioOriginalImg) is a ratio in the range [0,1].
* If we set this to 0, the smallest marker we can detect
* has a side length of tau_c. If we set it to 1 the marker would fill the entire image.
* For a FullHD video a good value to start with is 0.1.
* - minSideLengthCanonicalImg: minimum side length of a marker in the canonical image.
* Latter is the binarized image in which contours are searched.
* So all contours with a size smaller than minSideLengthCanonicalImg*minSideLengthCanonicalImg will omitted from the search.
* - minMarkerLengthRatioOriginalImg: range [0,1], eq (2) from paper
* - cameraMotionSpeed: is in the range [0,1]. This parameter (tau_s in the paper) implements the feature proposed
* in Section 3.7. and is particularly useful for video sequences.
* The parameter tau_i has a direct influence on the processing speed. Instead of setting a fixed value for it,
* it can be adjusted at the end of each frame using
* tau_i = (1-tau_s)*P(v_s)/4 (eq. 6 in paper).
* Where P(v_s) is the perimeter of the smallest marker that was detected in the last frame.
* - useGlobalThreshold: if we process a video, the assumption is, that the illumination conditions remains
* constant and global instead of adaptive thresholding can be applied, speeding up the binarization step.
* - foundGlobalThreshold: internal variable. It is used to cache the variable to the next detector call.
* - otsuGlobalThreshold: internal variable. It is used to cache the global otsu threshold to the next detector call.
* - foundMarkerInLastFrames: internal variable. It is used to cache if markers were found in the last frame.
Copy link
Member

Choose a reason for hiding this comment

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

internal variable

Parameters structs are used in OpenCV as non-modified inputs only.
This requirement is necessary for Python and other bindings.

For such video processing case with internal state it make sense to create dedicated API with detector class which store this internal state (use PImpl pattern, see QRCodeDetector class from objdetect module)

*/
struct CV_EXPORTS_W DetectorParameters {

Expand Down Expand Up @@ -188,6 +215,19 @@ struct CV_EXPORTS_W DetectorParameters {

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

// New Aruco functionality proposed in the paper:
// Romero-Ramirez et al: Speeded up detection of squared fiducial markers (2018)
CV_PROP_RW bool useAruco3Detection;
CV_PROP_RW int minSideLengthCanonicalImg;
CV_PROP_RW float minMarkerLengthRatioOriginalImg;

// New Aruco functionality especially for video
CV_PROP_RW float cameraMotionSpeed;
CV_PROP_RW bool useGlobalThreshold;
CV_PROP_RW bool foundGlobalThreshold;
CV_PROP_RW float otsuGlobalThreshold;
CV_PROP_RW int foundMarkerInLastFrames;
};


Expand Down Expand Up @@ -215,12 +255,13 @@ struct CV_EXPORTS_W DetectorParameters {
* are searched. For each detected marker, it returns the 2D position of its corner in the image
* and its corresponding identifier.
* Note that this function does not perform pose estimation.
* @sa estimatePoseSingleMarkers, estimatePoseBoard
* The function returns an estimate of the parameter minMarkerLengthRatioOriginalImg if useAruco3Detection=1. If not it returns 0.0.
* @sa estimatePoseSingleMarkers, estimatePoseBoard
*
*/
CV_EXPORTS_W void detectMarkers(InputArray image, const Ptr<Dictionary> &dictionary, OutputArrayOfArrays corners,
OutputArray ids, const Ptr<DetectorParameters> &parameters = DetectorParameters::create(),
OutputArrayOfArrays rejectedImgPoints = noArray(), InputArray cameraMatrix= noArray(), InputArray distCoeff= noArray());
CV_EXPORTS_W float detectMarkers(InputArray image, const Ptr<Dictionary> &dictionary, OutputArrayOfArrays corners,
OutputArray ids, const Ptr<DetectorParameters> &parameters = DetectorParameters::create(),
OutputArrayOfArrays rejectedImgPoints = noArray(), InputArray cameraMatrix= noArray(), InputArray distCoeff= noArray());



Expand Down
28 changes: 24 additions & 4 deletions modules/aruco/samples/detect_markers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ const char* keys =
"{dp | | File of marker detector parameters }"
"{r | | show rejected candidates too }"
"{refine | | Corner refinement: CORNER_REFINE_NONE=0, CORNER_REFINE_SUBPIX=1,"
"CORNER_REFINE_CONTOUR=2, CORNER_REFINE_APRILTAG=3}";
"CORNER_REFINE_CONTOUR=2, CORNER_REFINE_APRILTAG=3}"
"{ar3vid | | Adapt the paramater tau_i if aruco3 functionality is used. }";
}

/**
Expand Down Expand Up @@ -101,6 +102,12 @@ static bool readDetectorParameters(string filename, Ptr<aruco::DetectorParameter
fs["maxErroneousBitsInBorderRate"] >> params->maxErroneousBitsInBorderRate;
fs["minOtsuStdDev"] >> params->minOtsuStdDev;
fs["errorCorrectionRate"] >> params->errorCorrectionRate;
// new aruco functionality
fs["useAruco3Detection"] >> params->useAruco3Detection;
fs["minSideLengthCanonicalImg"] >> params->minSideLengthCanonicalImg;
fs["minMarkerLengthRatioOriginalImg"] >> params->minMarkerLengthRatioOriginalImg;
fs["cameraMotionSpeed"] >> params->cameraMotionSpeed;
fs["useGlobalThreshold"] >> params->useGlobalThreshold;
return true;
}

Expand All @@ -121,6 +128,7 @@ int main(int argc, char *argv[]) {
bool showRejected = parser.has("r");
bool estimatePose = parser.has("c");
float markerLength = parser.get<float>("l");
bool useAruco3DynamicUpdates = parser.has("ar3vid");

Ptr<aruco::DetectorParameters> detectorParams = aruco::DetectorParameters::create();
if(parser.has("dp")) {
Expand Down Expand Up @@ -165,15 +173,16 @@ int main(int argc, char *argv[]) {
int waitTime;
if(!video.empty()) {
inputVideo.open(video);
waitTime = 0;
waitTime = 1;
} else {
inputVideo.open(camId);
waitTime = 10;
}

double totalTime = 0;
int totalIterations = 0;

float new_marker_length_ratio = 0.0;
size_t total_nr_detected_corners = 0;
while(inputVideo.grab()) {
Mat image, imageCopy;
inputVideo.retrieve(image);
Expand All @@ -185,7 +194,16 @@ int main(int argc, char *argv[]) {
vector< Vec3d > rvecs, tvecs;

// detect markers and estimate pose
aruco::detectMarkers(image, dictionary, corners, ids, detectorParams, rejected);
if (useAruco3DynamicUpdates) {
// if new aruco3 features are used, we can also set the new min
// marker length ratio dymamically from the last frame
detectorParams->minMarkerLengthRatioOriginalImg = new_marker_length_ratio;
if(totalIterations % 30 == 0) {
cout<<"Current tau_i= "<<new_marker_length_ratio<<"\n";
}
}
new_marker_length_ratio = aruco::detectMarkers(image, dictionary, corners, ids, detectorParams, rejected);
total_nr_detected_corners += ids.size();
if(estimatePose && ids.size() > 0)
aruco::estimatePoseSingleMarkers(corners, markerLength, camMatrix, distCoeffs, rvecs,
tvecs);
Expand Down Expand Up @@ -218,5 +236,7 @@ int main(int argc, char *argv[]) {
if(key == 27) break;
}

cout<<"Total number detected corners: "<<total_nr_detected_corners<<"\n";

return 0;
}
9 changes: 9 additions & 0 deletions modules/aruco/samples/detector_params.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,12 @@ perspectiveRemoveIgnoredMarginPerCell: 0.13
maxErroneousBitsInBorderRate: 0.04
minOtsuStdDev: 5.0
errorCorrectionRate: 0.6

# new aruco functionality
useAruco3Detection: 0
minSideLengthCanonicalImg: 32 # 16, 32, 64 --> tau_c from the paper
minMarkerLengthRatioOriginalImg: 0.02 # range [0,0.2] --> tau_i from the paper
cameraMotionSpeed: 0.1 # range [0,1) --> tau_s from the paper
useGlobalThreshold: 0


Loading