31 #include <boost/assert.hpp>
32 #include <opencv2/features2d/features2d.hpp>
33 #include <opencv2/imgproc/imgproc.hpp>
40 #define OSVR_NEW_BLOB_CODE
46 class KeypointDetailer {
50 using ContourType = std::vector<cv::Point2i>;
52 augmentKeypoints(cv::Mat
const &grayImage,
53 std::vector<cv::KeyPoint>
const &foundKeyPoints) {
55 LedMeasurementVec ret;
56 cv::Mat greyCopy = grayImage.clone();
61 cv::Mat::zeros(grayImage.rows + 2, grayImage.cols + 2, CV_8UC1);
62 m_floodFillMask.row(0) = 1;
63 m_floodFillMask.row(m_floodFillMask.rows - 1) = 1;
64 m_floodFillMask.col(0) = 1;
65 m_floodFillMask.col(m_floodFillMask.cols - 1) = 1;
66 m_origBounds = cv::Rect(1, 1, m_floodFillMask.cols - 2,
67 m_floodFillMask.rows - 2);
68 for (
auto const &keypoint : foundKeyPoints) {
69 ret.push_back(augmentKeypoint(greyCopy, keypoint));
75 cv::Mat getDebugImage() {
return m_floodFillMask(m_origBounds); }
78 LedMeasurement augmentKeypoint(cv::Mat grayImage,
79 cv::KeyPoint origKeypoint) {
81 cv::bitwise_not(m_floodFillMask, m_scratchNotMask);
85 auto m_area = cv::floodFill(
86 grayImage, m_floodFillMask, origKeypoint.pt, 255,
87 &m_filledBounds, loDiff, upDiff,
88 CV_FLOODFILL_MASK_ONLY | ( 4) |
93 cv::bitwise_and(m_scratchNotMask, m_floodFillMask,
99 auto ret = LedMeasurement{origKeypoint, grayImage.size()};
104 m_binarySubImage = m_perPointResults(m_filledBounds);
106 m_moments = haveContour() ? cv::moments(m_contour,
false)
107 : cv::moments(m_binarySubImage, true);
112 ret.diameter = diameter();
116 ret.circularity = circularity();
119 ret.knowBoundingBox =
true;
121 cv::Size2f(m_filledBounds.width, m_filledBounds.height);
126 void computeContour() {
128 m_haveContour =
false;
131 auto expandedBounds = m_filledBounds;
132 if (expandedBounds.x > 0) {
134 expandedBounds.width++;
136 if (expandedBounds.y > 0) {
138 expandedBounds.height++;
140 if (expandedBounds.br().x < m_perPointResults.cols) {
141 expandedBounds.width++;
143 if (expandedBounds.br().y < m_perPointResults.rows) {
144 expandedBounds.height++;
147 cv::Mat input = m_perPointResults(expandedBounds).clone();
148 std::vector<ContourType> contours;
149 cv::findContours(input, contours, CV_RETR_EXTERNAL,
150 CV_CHAIN_APPROX_NONE,
151 cv::Point(-1, -1) + expandedBounds.tl());
153 if (contours.size() != 1) {
154 std::cout <<
"Weird, we have " << contours.size()
155 <<
" contours!" << std::endl;
158 if (!contours.empty()) {
159 m_contour = cv::Mat(contours[0],
true);
160 m_haveContour =
true;
162 m_haveContour =
false;
166 bool haveContour() {
return m_haveContour; }
168 double area()
const {
return static_cast<double>(m_area); }
171 double diameter()
const {
return 2 * std::sqrt(area() / CV_PI); }
174 double perimeter()
const {
return cv::arcLength(m_contour,
true); }
177 double circularity()
const {
178 auto perim = perimeter();
179 return 4 * CV_PI * area() / (perim * perim);
181 cv::Rect m_origBounds;
182 cv::Mat m_scratchNotMask;
183 cv::Mat m_floodFillMask;
187 cv::Mat m_perPointResults;
188 cv::Mat m_binarySubImage;
189 cv::Rect m_filledBounds;
190 bool m_haveContour =
false;
192 cv::Moments m_moments;
198 : m_algo(Algo::SimpleBlobDetector), m_params(blobParams) {
202 m_sbdParams.minDistBetweenBlobs = p.minDistBetweenBlobs;
204 m_sbdParams.minArea = p.minArea;
207 m_sbdParams.filterByColor =
false;
210 m_sbdParams.filterByInertia =
215 m_sbdParams.filterByCircularity =
216 p.filterByCircularity;
217 m_sbdParams.minCircularity =
221 m_sbdParams.filterByConvexity =
223 m_sbdParams.minConvexity = p.minConvexity;
228 : m_algo(Algo::EdgeHoleExtractor), m_params(blobParams),
229 m_extractor(extParams) {}
235 LedMeasurementVec
const &
237 m_latestMeasurements.clear();
238 m_lastGrayImage = grayImage.clone();
239 m_debugThresholdImageDirty =
true;
240 m_debugBlobImageDirty =
true;
241 getKeypoints(grayImage);
249 cv::threshold(grayImage, thresholded, m_sbdParams.minThreshold, 255,
252 m_latestMeasurements =
253 m_keypointDetailer->augmentKeypoints(thresholded, m_keyPoints);
255 cv::Size sz = grayImage.size();
256 if (Algo::SimpleBlobDetector == m_algo) {
259 m_latestMeasurements.resize(m_keyPoints.size());
261 begin(m_latestMeasurements),
262 [sz](cv::KeyPoint
const &kp) {
266 return m_latestMeasurements;
269 void SBDBlobExtractor::getKeypoints(cv::Mat
const &grayImage) {
270 if (Algo::EdgeHoleExtractor == m_algo) {
271 m_latestMeasurements = m_extractor(m_lastGrayImage, m_params);
273 }
else if (Algo::SimpleBlobDetector == m_algo) {
280 auto rangeInfo = ImageRangeInfo(grayImage);
281 if (rangeInfo.maxVal < p.absoluteMinThreshold) {
285 auto thresholdInfo = ImageThresholdInfo(rangeInfo, p);
289 m_sbdParams.minThreshold =
290 static_cast<float>(thresholdInfo.minThreshold);
291 m_sbdParams.maxThreshold =
292 static_cast<float>(thresholdInfo.maxThreshold);
293 m_sbdParams.thresholdStep =
294 static_cast<float>(thresholdInfo.thresholdStep);
301 #if CV_MAJOR_VERSION == 2
302 cv::Ptr<cv::SimpleBlobDetector> detector =
303 new cv::SimpleBlobDetector(m_sbdParams);
304 #elif CV_MAJOR_VERSION == 3
305 auto detector = cv::SimpleBlobDetector::create(m_sbdParams);
307 #error "Unrecognized OpenCV version!"
309 detector->detect(grayImage, m_keyPoints);
324 cv::Mat SBDBlobExtractor::generateDebugThresholdImage()
const {
325 if (Algo::EdgeHoleExtractor == m_algo) {
326 return m_extractor.getEdgeDetectedImage().clone();
328 BOOST_ASSERT(Algo::SimpleBlobDetector == m_algo);
331 auto getCurrentThresh = [&](
int i) {
332 return i * m_sbdParams.thresholdStep + m_sbdParams.minThreshold;
336 cv::threshold(m_lastGrayImage, ret, m_sbdParams.minThreshold, 255,
339 for (
int i = 1; getCurrentThresh(i) < m_sbdParams.maxThreshold; ++i) {
340 auto currentThresh = getCurrentThresh(i);
341 cv::threshold(m_lastGrayImage, temp, currentThresh, currentThresh,
343 cv::addWeighted(ret, 0.5, temp, 0.5, 0, tempOut);
350 if (m_debugThresholdImageDirty) {
351 m_debugThresholdImage = generateDebugThresholdImage();
352 m_debugThresholdImageDirty =
false;
354 return m_debugThresholdImage;
357 cv::Mat SBDBlobExtractor::generateDebugBlobImage()
const {
359 if (Algo::EdgeHoleExtractor == m_algo) {
361 ret = drawSingleColoredContours(m_lastGrayImage,
362 m_extractor.getContours(),
365 }
else if (Algo::SimpleBlobDetector == m_algo) {
367 cv::cvtColor(m_lastGrayImage, tempColor, CV_GRAY2BGR);
369 cv::drawKeypoints(tempColor, m_keyPoints, ret,
371 cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
375 cv::Mat
const &SBDBlobExtractor::getDebugBlobImage() {
376 if (m_debugBlobImageDirty) {
377 m_debugBlobImage = generateDebugBlobImage();
378 m_debugBlobImageDirty =
false;
380 return m_debugBlobImage;
383 cv::Mat
const &SBDBlobExtractor::getDebugExtraImage() {
385 m_extraImage = m_keypointDetailer->getDebugImage().clone();
392 : m_params(blobParams) {
396 m_sbdParams.minDistBetweenBlobs = p.minDistBetweenBlobs;
398 m_sbdParams.minArea = p.minArea;
401 m_sbdParams.filterByColor =
false;
404 m_sbdParams.filterByInertia =
409 m_sbdParams.filterByCircularity =
410 p.filterByCircularity;
411 m_sbdParams.minCircularity =
415 m_sbdParams.filterByConvexity =
417 m_sbdParams.minConvexity = p.minConvexity;
419 cv::Mat SBDGenericBlobExtractor::generateDebugThresholdImage_()
const {
422 auto getCurrentThresh = [&](
int i) {
423 return i * m_sbdParams.thresholdStep + m_sbdParams.minThreshold;
427 cv::threshold(getLatestGrayImage(), ret, m_sbdParams.minThreshold, 255,
430 for (
int i = 1; getCurrentThresh(i) < m_sbdParams.maxThreshold; ++i) {
431 auto currentThresh = getCurrentThresh(i);
432 cv::threshold(getLatestGrayImage(), temp, currentThresh,
433 currentThresh, CV_THRESH_BINARY);
434 cv::addWeighted(ret, 0.5, temp, 0.5, 0, tempOut);
441 cv::Mat SBDGenericBlobExtractor::generateDebugBlobImage_()
const {
444 cv::cvtColor(getLatestGrayImage(), tempColor, CV_GRAY2BGR);
446 cv::drawKeypoints(tempColor, m_keyPoints, ret,
cv::Scalar(255, 0, 0),
447 cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
452 getKeypoints(getLatestGrayImage());
453 cv::Size sz = getLatestGrayImage().size();
456 LedMeasurementVec ret;
457 ret.resize(m_keyPoints.size());
459 [sz](cv::KeyPoint
const &kp) {
464 void SBDGenericBlobExtractor::getKeypoints(cv::Mat
const &grayImage) {
472 if (rangeInfo.maxVal < p.absoluteMinThreshold) {
476 auto thresholdInfo = ImageThresholdInfo(rangeInfo, p);
480 m_sbdParams.minThreshold =
481 static_cast<float>(thresholdInfo.minThreshold);
482 m_sbdParams.maxThreshold =
483 static_cast<float>(thresholdInfo.maxThreshold);
484 m_sbdParams.thresholdStep =
485 static_cast<float>(thresholdInfo.thresholdStep);
492 #if CV_MAJOR_VERSION == 2
493 cv::Ptr<cv::SimpleBlobDetector> detector =
494 new cv::SimpleBlobDetector(m_sbdParams);
495 #elif CV_MAJOR_VERSION == 3
496 auto detector = cv::SimpleBlobDetector::create(m_sbdParams);
498 #error "Unrecognized OpenCV version!"
500 detector->detect(grayImage, m_keyPoints);
514 BlobExtractorPtr makeBlobExtractor(BlobParams
const &blobParams) {
515 auto extractor = std::make_shared<SBDGenericBlobExtractor>(blobParams);
t_< detail::transform_< List, Fun >> transform
Blob detection configuration parameters.
double Scalar
Common scalar type.