OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
BlobExtractor.cpp
Go to the documentation of this file.
1 
11 // Copyright 2016 Sensics, Inc.
12 //
13 // Licensed under the Apache License, Version 2.0 (the "License");
14 // you may not use this file except in compliance with the License.
15 // You may obtain a copy of the License at
16 //
17 // http://www.apache.org/licenses/LICENSE-2.0
18 //
19 // Unless required by applicable law or agreed to in writing, software
20 // distributed under the License is distributed on an "AS IS" BASIS,
21 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 // See the License for the specific language governing permissions and
23 // limitations under the License.
24 
25 // Internal Includes
26 #include "BlobExtractor.h"
27 #include "cvUtils.h"
28 
29 // Library/third-party includes
30 #include <opencv2/imgproc/imgproc.hpp> // for moments, boundingRect, arcLength...
31 
32 // Standard includes
33 #include <cmath>
34 #include <utility>
35 
36 namespace osvr {
37 namespace vbtracker {
38 
40  public:
41  explicit ContourInterrogation(ContourType const &contour)
42  : m_contour(contour), m_moms(cv::moments(m_contour)),
43  m_area(m_moms.m00),
44  m_center(m_moms.m10 / m_moms.m00, m_moms.m01 / m_moms.m00),
45  m_rect(cv::boundingRect(m_contour)) {}
46 
47  double area() const { return m_area; }
48 
50  double diameter() const { return 2 * std::sqrt(area() / CV_PI); }
51 
53  double perimeter() const { return cv::arcLength(m_contour, true); }
54 
56  double circularity() const {
57  auto perim = perimeter();
58  return 4 * CV_PI * area() / (perim * perim);
59  }
60  cv::Point2d center() const { return m_center; }
61  cv::Rect boundingRectangle() const { return m_rect; }
62 
63  ContourType &&moveOutContour() { return std::move(m_contour); }
64 
65  private:
66  ContourType m_contour;
67  cv::Moments m_moms;
68  double m_area;
69  cv::Point2d m_center;
70  cv::Rect m_rect;
71  };
72 
73  BlobData getBlobDataFromContour(ContourType const &contour) {
74  ContourInterrogation contourDetails(contour);
75  auto ret =
76  BlobData{contourDetails.center(), contourDetails.area(),
77  contourDetails.circularity(), contourDetails.diameter(),
78  contourDetails.boundingRectangle()};
80  ret.contour = std::move(contourDetails.moveOutContour());
81  return ret;
82  }
83 
84  BlobBasics getContourBasicDetails(ContourType const &contour) {
85  ContourInterrogation contourDetails(contour);
86  return BlobBasics{contourDetails.center(), contourDetails.area(),
87  contourDetails.boundingRectangle()};
88  }
89 
90  double getConvexity(ContourType const &contour, const double area) {
91  ContourType hull;
92  cv::convexHull(contour, hull);
93  auto hullArea = cv::contourArea(hull);
94  return area / hullArea;
95  }
96 
97  LedMeasurementVec BasicThresholdBlobDetector::
98  operator()(cv::Mat const &gray,
99  cv::SimpleBlobDetector::Params const &params) {
100  grayImage_ = gray.clone();
101  auto thresh =
102  static_cast<int>((params.minThreshold + params.maxThreshold) / 2);
103  LedMeasurementVec ret;
104  for (auto &contour : binarizeAndGetSolidComponents(thresh)) {
105  auto data = getBlobDataFromContour(contour);
106  if (params.filterByArea) {
107  if (data.area < params.minArea || data.area > params.maxArea) {
108  continue;
109  }
110  }
111  if (params.filterByCircularity) {
112  if (data.circularity < params.minCircularity) {
113  continue;
114  }
115  }
116  ret.push_back(LedMeasurement(castPointToFloat(data.center),
117  static_cast<float>(data.diameter),
118  grayImage_.size()));
119  }
120  return ret;
121  }
122  std::vector<ContourType>
123  BasicThresholdBlobDetector::binarizeAndGetSolidComponents(int thresh) {
124  cv::Mat binarized;
125  cv::threshold(grayImage_, binarized, thresh, 255, cv::THRESH_BINARY);
126  std::vector<ContourType> contours;
127  std::vector<cv::Vec4i> hierarchy;
128  cv::findContours(binarized, contours, hierarchy, CV_RETR_CCOMP,
129  CV_CHAIN_APPROX_NONE);
130  auto n = contours.size();
131  std::vector<ContourType> ret;
132  for (std::size_t i = 0; i < n; ++i) {
133  // If this contour has no first child, then it's solid (no
134  // holes).
135  if (hierarchy[i][2] < 0) {
136  ret.emplace_back(std::move(contours[i]));
137  }
138  }
139  return ret;
140  }
141 
142  void BasicThresholdBlobDetector::makeFloodFillMask(cv::Mat const &gray) {
143  floodFillMask_.create(gray.rows + 2, gray.cols + 2, CV_8UC1);
144  origBoundsInFloodFill_ =
145  cv::Rect(1, 1, floodFillMask_.cols - 2, floodFillMask_.rows - 2);
146  }
147  void BasicThresholdBlobDetector::augmentPoint(cv::Point peakCenter,
148  const int loDiff,
149  const int upDiff) {
150  // Saving this now before we monkey with floodFillMask_
151  cv::Mat scratchNot = ~floodFillMask_;
152  cv::Mat grayClone = grayImage_.clone();
153  cv::Rect filledBounds;
154  auto m_area = cv::floodFill(
155  grayClone, floodFillMask_, peakCenter, 255, &filledBounds, loDiff,
156  upDiff, CV_FLOODFILL_MASK_ONLY | (/* connectivity 4 or 8 */ 4) |
157  (/* value to write in to mask */ 255 << 8));
158  // Now floodFillMask_ contains the mask with both our point
159  // and all other points so far. We need to split them by ANDing with
160  // the NOT of the old flood-fill mask we saved earlier.
161  cv::Mat thisPointResults = scratchNot & floodFillMask_;
163  }
164 } // namespace vbtracker
165 } // namespace osvr
double perimeter() const
Gets perimeter - requires contour.
double circularity() const
As used by OpenCV, return value in [0, 1] - requires contour.
Header.
double diameter() const
Approximation of a diameter based on assumption of circularity.