OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
EdgeHoleBasedLedExtractor.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
27 #include "OptionalStream.h"
28 #include "cvUtils.h"
29 
30 #ifdef OSVR_USE_REALTIME_LAPLACIAN
31 #include "RealtimeLaplacian.h"
32 #endif
33 
34 // Library/third-party includes
35 #ifdef OSVR_UVBI_CORE
36 #include <osvr/Common/Tracing.h>
38 #endif
39 
40 // Standard includes
41 #include <iostream>
42 #include <utility>
43 
44 namespace osvr {
45 namespace vbtracker {
46  static const std::uint8_t MAX_JPG_EDGEDETECT_NOISE = 20;
47  static const auto PREFIX = "[EdgeHoleBasedLedExtractor] ";
48  EdgeHoleParams::EdgeHoleParams()
49  : preEdgeDetectionBlurSize(3), laplacianKSize(3), laplacianScale(5),
50  edgeDetectErosion(false),
51  erosionKernelValue(MAX_JPG_EDGEDETECT_NOISE),
52  postEdgeDetectionBlur(true), postEdgeDetectionBlurSize(3),
53  postEdgeDetectionBlurThreshold(80) {}
54 
55  static const int EDGE_DETECT_DEST_DEPTH = CV_8U;
56 
57  EdgeHoleBasedLedExtractor::EdgeHoleBasedLedExtractor(
58  EdgeHoleParams const &extractorParams)
59  : extParams_(extractorParams)
60 #ifdef OSVR_USE_REALTIME_LAPLACIAN
61  ,
62  laplacianImpl_(new RealtimeLaplacian(EDGE_DETECT_DEST_DEPTH,
63  extParams_.laplacianKSize,
64  extParams_.laplacianScale))
65 #endif
66  {
67 
68  compressionArtifactRemovalKernel_ =
69  cv::Mat::ones(cv::Size(3, 3), CV_8U) *
70  static_cast<std::uint8_t>(extractorParams.erosionKernelValue);
71 #ifdef OSVR_OPENCV_2
72  compressionArtifactRemoval_ = cv::createMorphologyFilter(
73  cv::MORPH_ERODE, CV_8U, compressionArtifactRemovalKernel_);
74 #endif
75  }
76 #ifdef OSVR_UVBI_CORE
77  namespace tracing = ::osvr::common::tracing;
78  class BlobExtraction
79  : public tracing::TracingRegion<tracing::MainTracePolicy> {
80  public:
81  BlobExtraction()
82  : tracing::TracingRegion<tracing::MainTracePolicy>(
83  "BlobExtraction") {}
84  };
85 #endif
86 
87  LedMeasurementVec const &EdgeHoleBasedLedExtractor::
88  operator()(cv::Mat const &gray, BlobParams const &p,
89  bool verboseBlobOutput) {
90  reset();
91 
92 #ifdef OSVR_UVBI_CORE
93  BlobExtraction trace;
94 #endif
95 
96  verbose_ = verboseBlobOutput;
97 
98  gray.copyTo(gray_);
99 
101  auto rangeInfo = ImageRangeInfo(gray_);
102  if (rangeInfo.maxVal < p.absoluteMinThreshold) {
104  return measurements_;
105  }
106 
107  auto thresholdInfo = ImageThresholdInfo(rangeInfo, p);
108  minBeaconCenterVal_ =
109  static_cast<std::uint8_t>(thresholdInfo.minThreshold);
110 
114  // MatType blurred;
115 
116  cv::GaussianBlur(gray_, blurred_,
117  cv::Size(extParams_.preEdgeDetectionBlurSize,
118  extParams_.preEdgeDetectionBlurSize),
119  0, 0);
120 
121 #ifdef OSVR_USE_REALTIME_LAPLACIAN
122  laplacianImpl_->apply(blurred_, edge_);
125 #else
126  cv::Laplacian(blurred_, edge_, CV_8U, extParams_.laplacianKSize,
128  extParams_.laplacianScale);
129 #endif
130 
132  if (extParams_.edgeDetectErosion) {
133 #ifdef OSVR_OPENCV_2
134  compressionArtifactRemoval_->apply(edge_, edge_);
135 #else
136  cv::erode(edge_, edge_, compressionArtifactRemovalKernel_);
137 #endif
138  }
139 
140  // turn the edge detection into a binary image.
141  if (extParams_.postEdgeDetectionBlur) {
142  cv::GaussianBlur(edge_, edgeTemp_,
143  cv::Size(extParams_.postEdgeDetectionBlurSize,
144  extParams_.postEdgeDetectionBlurSize),
145  0, 0);
146  cv::threshold(edgeTemp_, edgeBinary_,
147  extParams_.postEdgeDetectionBlurThreshold, 255,
148  cv::THRESH_BINARY);
149  } else {
150  cv::threshold(edge_, edgeBinary_,
151  extParams_.postEdgeDetectionBlurThreshold, 255,
152  cv::THRESH_BINARY);
153  }
154 
156 
157  // The lambda ("continuation") is called with each "hole" in the edge
158  // detection image, it's up to us what to do with the contour we're
159  // given. We examine it for suitability as an LED, and if it passes our
160  // checks, add a derived measurement to our measurement vector and the
161  // contour itself to our list of contours for debugging display.
162  edgeBinary_.copyTo(binTemp_);
163  consumeHolesOfConnectedComponents(
164  binTemp_, contoursTempStorage_, hierarchyTempStorage_,
165  [&](ContourType &&contour) { checkBlob(std::move(contour), p); });
166  return measurements_;
167  }
170 
171  void EdgeHoleBasedLedExtractor::reset() {
172  contours_.clear();
173  measurements_.clear();
174  rejectList_.clear();
175  contourId_ = 0;
176  }
177  void EdgeHoleBasedLedExtractor::checkBlob(ContourType &&contour,
178  BlobParams const &p) {
179 
180  auto data = getBlobDataFromContour(contour);
181  auto debugStream = [&] {
182 #ifdef OSVR_DEBUG_CONTOUR_CONDITIONS
183  return outputIf(std::cout, true);
184 #else
185  return outputIf(std::cout, verbose_);
186 #endif
187  };
188  auto myId = contourId_;
189  contourId_++;
190  debugStream() << "\nContour ID " << myId << " centered at "
191  << data.center;
192  debugStream() << " - diameter: " << data.diameter;
193  debugStream() << " - area: " << data.area;
194  debugStream() << " - circularity: " << data.circularity;
195  debugStream() << " - bounding box size: " << data.bounds.size();
196  if (data.area < p.minArea) {
197  debugStream() << "Reject based on area: " << data.area << " < "
198  << p.minArea << "\n";
199 
200  addToRejectList(myId, RejectReason::Area, data);
201  return;
202  }
203 
204  {
207  cv::Mat patch;
208  cv::getRectSubPix(gray_, cv::Size(1, 1),
209  castPointToFloat(data.center), patch);
210  auto centerPointValue = patch.at<unsigned char>(0, 0);
211  if (centerPointValue < minBeaconCenterVal_) {
212  debugStream() << "Reject based on center point value: "
213  << int(centerPointValue) << " < "
214  << int(minBeaconCenterVal_) << "\n";
215  addToRejectList(myId, RejectReason::CenterPointValue, data);
216  return;
217  }
218  }
219 
220  if (p.filterByCircularity) {
221  if (data.circularity < p.minCircularity) {
222  debugStream()
223  << "Reject based on circularity: " << data.circularity
224  << " < " << p.minCircularity << "\n";
225  addToRejectList(myId, RejectReason::Circularity, data);
226  return;
227  }
228  }
229  if (p.filterByConvexity) {
230  auto convexity = getConvexity(contour, data.area);
231  debugStream() << " - convexity: " << convexity;
232  if (convexity < p.minConvexity) {
233  debugStream() << "Reject based on convexity: " << convexity
234  << " < " << p.minConvexity << "\n";
235  addToRejectList(myId, RejectReason::Convexity, data);
236 
237  return;
238  }
239  }
240 
241  debugStream() << "Accepted!\n";
242  {
243  auto newMeas =
244  LedMeasurement(castPointToFloat(data.center),
245  static_cast<float>(data.diameter), gray_.size(),
246  static_cast<float>(data.area));
247  newMeas.circularity = static_cast<float>(data.circularity);
248  newMeas.setBoundingBox(data.bounds);
249 
250  measurements_.emplace_back(std::move(newMeas));
251  }
252  contours_.emplace_back(std::move(contour));
253  }
254 } // namespace vbtracker
255 } // namespace osvr
~EdgeHoleBasedLedExtractor()
out of line for unique_ptr-based pimpl.
Header.
Header.
Blob detection configuration parameters.
Definition: BlobParams.h:40
LedMeasurementVec const & operator()(cv::Mat const &gray, BlobParams const &p, bool verboseBlobOutput=false)