1515#include < tagCustom48h12.h>
1616#include < tagStandard41h12.h>
1717#include < tagStandard52h13.h>
18+ #include < opencv2/opencv.hpp>
1819
1920using HammHist = std::array<int , 10 >;
2021using Eigen::AngleAxisd;
@@ -30,7 +31,7 @@ PluginAprilTag::PluginAprilTag(FrameBuffer *buffer,
3031 std::shared_ptr<TeamTags> blue_team_tags,
3132 std::shared_ptr<TeamTags> yellow_team_tags,
3233 CMPattern::TeamSelector &blue_team_selector,
33- CMPattern::TeamSelector &yellow_team_selector)
34+ CMPattern::TeamSelector &yellow_team_selector, ConvexHullImageMask& mask )
3435 : VisionPlugin(buffer), settings(new VarList(" AprilTag" )),
3536 v_enable(new VarBool(" enable" , true )),
3637 v_quad_size(new VarDouble(" quad size (mm)" , 70.0 )),
@@ -43,7 +44,8 @@ PluginAprilTag::PluginAprilTag(FrameBuffer *buffer,
4344 camera_params (camera_params), blue_team_tags(blue_team_tags),
4445 yellow_team_tags(yellow_team_tags),
4546 blue_team_selector(blue_team_selector),
46- yellow_team_selector(yellow_team_selector) {
47+ yellow_team_selector(yellow_team_selector),
48+ image_mask(mask){
4749
4850 v_family.reset (new VarStringEnum (" Tag Family" , " tagCircle21h7" ));
4951 v_family->addItem (" tag36h11" );
@@ -74,11 +76,13 @@ ProcessResult PluginAprilTag::process(FrameData *data, RenderOptions *options) {
7476 if (!v_enable->getBool ()) {
7577 return ProcessingOk;
7678 }
79+ image_mask.lock ();
7780 if (!tag_family || v_family->getString () != tag_family->name ) {
7881 makeTagFamily ();
7982 if (!tag_family) {
8083 std::cerr << " AprilTag: Failed to create tag family '"
8184 << v_family->getString () << " '\n " ;
85+ image_mask.unlock ();
8286 return ProcessingFailed;
8387 }
8488
@@ -91,6 +95,7 @@ ProcessResult PluginAprilTag::process(FrameData *data, RenderOptions *options) {
9195 makeTagDetector ();
9296 if (!tag_detector) {
9397 std::cerr << " AprilTag: Failed to create tag detector\n " ;
98+ image_mask.unlock ();
9499 return ProcessingFailed;
95100 }
96101 }
@@ -104,20 +109,55 @@ ProcessResult PluginAprilTag::process(FrameData *data, RenderOptions *options) {
104109 reinterpret_cast <Image<raw8> *>(data->map .get (" greyscale" ));
105110 if (img_greyscale == nullptr ) {
106111 std::cerr << " AprilTag: No greyscale image in frame data\n " ;
112+ image_mask.unlock ();
107113 return ProcessingFailed;
108114 }
109115
116+ Image<raw8> *img_masked;
117+ if ((img_masked = reinterpret_cast <Image<raw8> *>(
118+ data->map .get (" masked" ))) == nullptr ) {
119+ img_masked = reinterpret_cast <Image<raw8> *>(
120+ data->map .insert (" masked" , new Image<raw8>()));
121+ }
122+ img_masked->allocate (img_greyscale->getWidth (), img_greyscale->getHeight ());
123+
124+ // TODO(dschwab): In the interest of speed on hi-res images, it
125+ // might be useful to compute the bounding box of the convex hull
126+ // and if it is x% smaller than original image, apply the mask and
127+ // then crop the image so that the detection needs to work with less
128+ // pixels.
129+
130+ Image<raw8> *input_image = img_greyscale;
131+ const auto & hull = image_mask.getConvexHull ();
132+ // there must be at least 3 points in the convex hull for the mask to have restricted
133+ // the image size. If there are less than 3 skip masking the
134+ // greyscale image.
135+ if (hull.getNumPoints () >= 3 ) {
136+ const auto &mask = image_mask.getMask ();
137+ cv::Mat cv_mask (mask.getHeight (), mask.getWidth (), CV_8UC1, mask.getData ());
138+ cv::Mat cv_greyscale (img_greyscale->getHeight (), img_greyscale->getWidth (),
139+ CV_8UC1, img_greyscale->getData ());
140+ cv::Mat cv_masked (img_masked->getHeight (), img_masked->getWidth (), CV_8UC1, img_masked->getData ());
141+
142+ cv::Mat& input1 = cv_mask;
143+ cv::Mat& input2 = cv_greyscale;
144+ cv::Mat& output = cv_masked;
145+ cv::bitwise_and (input1, input2, output);
146+
147+ input_image = img_masked;
148+ }
149+
110150 const int maxiters = v_iters->getInt ();
111151 for (int iter = 0 ; iter < maxiters; iter++) {
112152 if (maxiters > 1 ) {
113153 std::cout << " iter " << iter + 1 << " / " << maxiters << " \n " ;
114154 }
115155
116156 // zero-copy. Just use the already allocated buffer data.
117- image_u8_t im{.width = img_greyscale ->getWidth (),
118- .height = img_greyscale ->getHeight (),
119- .stride = img_greyscale ->getWidth (),
120- .buf = img_greyscale ->getData ()};
157+ image_u8_t im{.width = input_image ->getWidth (),
158+ .height = input_image ->getHeight (),
159+ .stride = input_image ->getWidth (),
160+ .buf = input_image ->getData ()};
121161
122162 detections.reset (apriltag_detector_detect (tag_detector.get (), &im));
123163 data->map .update (" apriltag_detections" , detections.get ());
@@ -229,6 +269,7 @@ ProcessResult PluginAprilTag::process(FrameData *data, RenderOptions *options) {
229269 }
230270 }
231271
272+ image_mask.unlock ();
232273 return ProcessingOk;
233274}
234275
0 commit comments