SLProject  4.3.020
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
SENSCamera.cpp
Go to the documentation of this file.
1 #include "SENSCamera.h"
2 #include <opencv2/imgproc.hpp>
3 #include <SENSUtils.h>
4 #include <Utils.h>
5 
6 //-----------------------------------------------------------------------------
7 bool isEqualToOne(float value)
8 {
9  return std::abs(1.f - value) <= 0.00001f;
10 }
11 //-----------------------------------------------------------------------------
12 // searches for best machting size and returns it
13 int SENSCameraDeviceProps::findBestMatchingConfig(cv::Size requiredSize) const
14 {
15  if (_streamConfigs.size() == 0)
17  "No stream configuration available!",
18  __LINE__,
19  __FILE__);
20 
21  std::vector<std::pair<float, int>> matchingSizes;
22 
23  // calculate score for
24  for (int i = 0; i < _streamConfigs.size(); ++i)
25  {
27  // stream size has to have minimum the size of the required size
28  if (config.widthPix >= requiredSize.width &&
29  config.heightPix >= requiredSize.height)
30  {
31  float crop = 0.f;
32  float scaleFactor = 1.f;
33  // calculate necessary crop to adjust stream image to required size
34  if (((float)requiredSize.width / (float)requiredSize.height) > ((float)config.widthPix / (float)config.heightPix))
35  {
36  scaleFactor = (float)requiredSize.width / (float)config.widthPix;
37  float heightScaled = config.heightPix * scaleFactor;
38  crop += heightScaled - requiredSize.height;
39  crop += (float)config.widthPix * scaleFactor - (float)requiredSize.width;
40  }
41  else
42  {
43  scaleFactor = (float)requiredSize.height / (float)config.heightPix;
44  float widthScaled = config.widthPix * scaleFactor;
45  crop += widthScaled - requiredSize.width;
46  crop += (float)config.heightPix * scaleFactor - (float)requiredSize.height;
47  }
48 
49  float cropScaleScore = crop;
50  if (!isEqualToOne(scaleFactor))
51  cropScaleScore += (config.widthPix * config.heightPix);
52  matchingSizes.push_back(std::make_pair(cropScaleScore, i));
53  }
54  }
55  // sort by crop
56  std::sort(matchingSizes.begin(), matchingSizes.end());
57 
58  return matchingSizes.front().second;
59 }
60 //-----------------------------------------------------------------------------
61 bool SENSCaptureProps::containsDeviceId(const std::string& deviceId) const
62 {
63  return std::find_if(begin(), end(), [&](const SENSCameraDeviceProps& comp)
64  { return comp.deviceId() == deviceId; }) != end();
65 }
66 //-----------------------------------------------------------------------------
68 {
69  return std::find_if(begin(), end(), [&](const SENSCameraDeviceProps& comp)
70  { return comp.facing() == facing; }) != end();
71 }
72 //-----------------------------------------------------------------------------
73 const SENSCameraDeviceProps* SENSCaptureProps::camPropsForDeviceId(const std::string& deviceId) const
74 {
75  auto camPropsIt = std::find_if(begin(), end(), [&](const SENSCameraDeviceProps& comp)
76  { return comp.deviceId() == deviceId; });
77  if (camPropsIt != end())
78  return &*camPropsIt;
79  else
80  return nullptr;
81 }
82 //-----------------------------------------------------------------------------
83 //! Helper function to find a camera device and stream configuration with certain characteristics.
84 /*! Returns a pair on nullptrs if characteristics were not fulfilled.
85  * Possible reasons: the facing is not available or if the transferred width
86  * and height would require an extrapolation.
87  */
88 std::pair<const SENSCameraDeviceProps* const, const SENSCameraStreamConfig* const>
90  const float horizFov,
91  const int width,
92  const int height) const
93 {
94  struct SortElem
95  {
96  // corresponding camera characteristics
97  const SENSCameraDeviceProps* camChars;
98  // corresponding index in stream configurations
99  const SENSCameraStreamConfig* streamConfig;
100  int streamConfigIdx;
101  // cropped width (using stream configuration referenced by streamConfigIdx)
102  int widthCropped;
103  // cropped height (using stream configuration referenced by streamConfigIdx)
104  int heightCropped;
105  // horizontal and vertical field of view using cropped width
106  float horizFov = -1.f;
107  // scale factor between stream config and target resolutions
108  float scale;
109 
110  // difference to target field of view
111  float fovScore = 0.f;
112  // summed crop (used as later as score to make decision)
113  int cropScore;
114  // score that respecs necessary scale complexity
115  int scaleScore;
116 
117  void print(int idx) const
118  {
119  std::stringstream ss;
120  ss << "Sort Elem " << idx;
121  ss << " fovV: " << horizFov;
122  ss << " fovV score: " << fovScore;
123  ss << " crop score: " << cropScore;
124  ss << " scale score " << scaleScore;
125  ss << " widthCropped: " << widthCropped;
126  ss << " heightCropped: " << heightCropped;
127  ss << " scale: " << scale;
128  ss << " width orig " << camChars->streamConfigs()[streamConfigIdx].widthPix;
129  ss << " height orig " << camChars->streamConfigs()[streamConfigIdx].heightPix;
130  Utils::log("SENSCaptureProps", ss.str().c_str());
131  }
132  };
133  auto printSortElems = [](const std::vector<SortElem>& sortElems, std::string id)
134  {
135  std::cout << id << std::endl;
136  for (int i = 0; i < sortElems.size(); ++i)
137  sortElems.at(i).print(i);
138  };
139 
140  std::vector<SortElem> sortElems;
141 
142  // make cropped versions of all stream configurations
143  for (auto itChars = begin(); itChars != end(); ++itChars)
144  {
145  if (facing != itChars->facing())
146  continue;
147 
148  const auto& streams = itChars->streamConfigs();
149  float targetWidthDivHeight = (float)width / (float)height;
150 
151  for (auto itStream = streams.begin(); itStream != streams.end(); ++itStream)
152  {
153  const SENSCameraStreamConfig& config = *itStream;
154 
155  SortElem sortElem;
156  int cropW, cropH;
157  SENS::calcCrop({config.widthPix,
158  config.heightPix},
159  targetWidthDivHeight,
160  cropW,
161  cropH,
162  sortElem.widthCropped,
163  sortElem.heightCropped);
164  sortElem.scale = (float)width / (float)sortElem.widthCropped;
165 
166  if (sortElem.scale <= 1.f)
167  {
168  sortElem.camChars = &*itChars;
169  sortElem.streamConfigIdx = (int)(&*itStream - &*streams.begin());
170  sortElem.streamConfig = &*itStream;
171  if (config.focalLengthPix > 0)
172  {
173  // we have to use the cropped and unscaled width of the stream config because of the resolution of the focalLengthPix
174  sortElem.horizFov = SENS::calcFOVDegFromFocalLengthPix(config.focalLengthPix,
175  sortElem.widthCropped);
176  sortElem.fovScore = std::abs(sortElem.horizFov - horizFov);
177  }
178  sortElem.cropScore = cropW + cropH;
179  // if we have to scale add a score for it
180  if (!isEqualToOne(sortElem.scale))
181  sortElem.scaleScore = width * height;
182  else
183  sortElem.scaleScore = 0.f;
184 
185  sortElems.push_back(sortElem);
186  }
187  }
188  }
189 
190  if (sortElems.size())
191  {
192  // sort by difference to target fovV
193  std::sort(sortElems.begin(), sortElems.end(), [](const SortElem& lhs, const SortElem& rhs) -> bool
194  { return lhs.fovScore < rhs.fovScore; });
195  // printSortElems(sortElems, "sortElems");
196 
197  // extract all in a range of +-3 degree compared to the closest to target fovV
198  std::vector<SortElem> closeFovSortElems;
199  float maxFovDiff = sortElems.front().fovScore + 3;
200  for (SortElem elem : sortElems)
201  {
202  if (elem.fovScore < maxFovDiff)
203  closeFovSortElems.push_back(elem);
204  }
205 
206  // now extract the one with the best scale score from the remaining
207  std::sort(closeFovSortElems.begin(), closeFovSortElems.end(), [](const SortElem& lhs, const SortElem& rhs) -> bool
208  { return lhs.scale > rhs.scale; });
209  // printSortElems(closeFovSortElems, "closeFovSortElems");
210 
211  const SortElem& bestSortElem = closeFovSortElems.front();
212  return std::make_pair(bestSortElem.camChars, bestSortElem.streamConfig);
213  }
214  else
215  {
216  return std::make_pair(nullptr, nullptr);
217  }
218 }
219 //-----------------------------------------------------------------------------
220 void SENSBaseCamera::updateFrame(cv::Mat bgrImg,
221  cv::Mat intrinsics,
222  int width,
223  int height,
224  bool intrinsicsChanged)
225 {
226  // estimate time before running into lock
227  SENSTimePt timePt = SENSClock::now();
228 
229  // inform listeners
230  {
231  std::lock_guard<std::mutex> lock(_listenerMutex);
232  if (_listeners.size())
233  {
234  for (SENSCameraListener* l : _listeners)
235  l->onFrame(timePt, bgrImg.clone());
236  }
237  }
238 
239  {
240  std::lock_guard<std::mutex> lock(_frameMutex);
241  _frame = std::make_shared<SENSFrameBase>(timePt,
242  bgrImg,
243  intrinsics,
244  width,
245  height);
246  }
247 }
248 //-----------------------------------------------------------------------------
250 {
251  std::lock_guard<std::mutex> lock(_listenerMutex);
252  if (std::find(_listeners.begin(), _listeners.end(), listener) == _listeners.end())
253  _listeners.push_back(listener);
254 
255  if (_started)
256  {
257  // inform listeners about camera infos
258  for (SENSCameraListener* l : _listeners)
259  l->onCameraConfigChanged(_config);
260  }
261 }
262 //-----------------------------------------------------------------------------
264 {
265  std::lock_guard<std::mutex> lock(_listenerMutex);
266  for (auto it = _listeners.begin(); it != _listeners.end(); ++it)
267  {
268  if (*it == listener)
269  {
270  _listeners.erase(it);
271  break;
272  }
273  }
274 }
275 //-----------------------------------------------------------------------------
277 {
279 
280  if (!_started)
281  {
282  SENS_WARN("SENSBaseCamera latestFrame: Camera is not started!");
283  return latestFrame;
284  }
285 
286  {
287  std::lock_guard<std::mutex> lock(_frameMutex);
289  }
290 
291  return latestFrame;
292 }
293 //-----------------------------------------------------------------------------
295 {
296  {
297  std::lock_guard<std::mutex> lock(_listenerMutex);
298  // inform listeners about camera infos
299  for (SENSCameraListener* l : _listeners)
300  l->onCameraConfigChanged(_config);
301  }
302 }
303 //-----------------------------------------------------------------------------
#define SENS_WARN(...)
Definition: SENS.h:64
std::chrono::high_resolution_clock::time_point SENSTimePt
Definition: SENS.h:57
bool isEqualToOne(float value)
Definition: SENSCamera.cpp:7
SENSCameraFacing
Definition of camera facing.
Definition: SENSCamera.h:28
std::shared_ptr< SENSFrameBase > SENSFrameBasePtr
Definition: SENSFrame.h:27
void registerListener(SENSCameraListener *listener) override
Definition: SENSCamera.cpp:249
std::vector< SENSCameraListener * > _listeners
Definition: SENSCamera.h:220
SENSCameraConfig _config
indicates what is currently running
Definition: SENSCamera.h:218
SENSFrameBasePtr latestFrame() override
Get the latest captured frame. If no frame was captured the frame will be empty (null).
Definition: SENSCamera.cpp:276
void unregisterListener(SENSCameraListener *listener) override
Definition: SENSCamera.cpp:263
SENSFrameBasePtr _frame
current frame
Definition: SENSCamera.h:222
std::atomic< bool > _started
flags if camera was started
Definition: SENSCamera.h:217
std::mutex _frameMutex
Definition: SENSCamera.h:223
void updateFrame(cv::Mat bgrImg, cv::Mat intrinsics, int width, int height, bool intrinsicsChanged)
Definition: SENSCamera.cpp:220
std::mutex _listenerMutex
Definition: SENSCamera.h:221
void processStart()
call from start function to do startup preprocessing
Definition: SENSCamera.cpp:294
int findBestMatchingConfig(cv::Size requiredSize) const
Definition: SENSCamera.cpp:13
const SENSCameraFacing & facing() const
Definition: SENSCamera.h:86
std::vector< SENSCameraStreamConfig > _streamConfigs
Definition: SENSCamera.h:109
const std::string & deviceId() const
Definition: SENSCamera.h:85
const std::vector< SENSCameraStreamConfig > & streamConfigs() const
Definition: SENSCamera.h:84
bool containsDeviceId(const std::string &deviceId) const
Definition: SENSCamera.cpp:61
std::pair< const SENSCameraDeviceProps *const, const SENSCameraStreamConfig *const > findBestMatchingConfig(SENSCameraFacing facing, const float horizFov, const int width, const int height) const
Helper function to find a camera device and stream configuration with certain characteristics.
Definition: SENSCamera.cpp:89
const SENSCameraDeviceProps * camPropsForDeviceId(const std::string &deviceId) const
Definition: SENSCamera.cpp:73
bool supportsCameraFacing(const SENSCameraFacing &facing) const
Definition: SENSCamera.cpp:67
Config config
The configuration set in App::run.
Definition: AppAndroid.cpp:34
float calcFOVDegFromFocalLengthPix(const float focalLengthPix, const int imgLength)
Definition: SENSUtils.cpp:192
bool calcCrop(cv::Size inputSize, float targetWdivH, int &cropW, int &cropH, int &width, int &height)
Definition: SENSUtils.cpp:9
T abs(T a)
Definition: Utils.h:249
void log(const char *tag, const char *format,...)
logs a formatted string platform independently
Definition: Utils.cpp:1100