ArMarkerLocalizerOpenCV.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5  *
6  * ArmarX is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * ArmarX is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * @package VisionX::Component
19  * @author David Schiebener (schiebener at kit dot edu)
20  * @date 2015
21  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22  * GNU General Public License
23  */
24 
25 #include <Eigen/Core>
26 
27 #include <opencv2/calib3d.hpp>
28 #include <opencv2/core/eigen.hpp>
29 
31 
32 // IVT
33 #include <Calibration/Calibration.h>
34 #include <Image/IplImageAdaptor.h>
35 
36 #include <SimoxUtility/algorithm/string.h>
37 
39 
41 
44 
45 
46 namespace visionx
47 {
48 
51  {
52  }
53 
55  {
57 
58  defs->optional(p.imageProviderName, "img.ImageProviderName", "Name of the image provider to use.");
59  defs->optional(p.referenceFrame, "img.ReferenceFrameName", "Name of the ReferenceFrameName");
60  defs->optional(p.agentName, "img.AgentName", "Name of the agent");
61 
62  std::string pattern = "[ k3 [, k4, k5, k6 [, s1, s2, s3, s4 ] ] ]";
63  // defs->optional(p.extraDistortionCoeffs, "cam.ExtraDistortionCoeffs",
64  defs->defineOptionalProperty<std::vector<std::string>>("cam.ExtraDistortionCoeffs", {},
65  "Optional extra distortion coefficients (which cannot be retrieved from the image provider)."
66  "\nThe expected parameters are: " + pattern + ""
67  "\nThat is, you must specifiy 1 (k3), 4 (k3-k6), or 8 (k3-k6, s1-s4) elements."
68  "\nNote: k1, k2, p1, p2 are retrieved from the image provider.")
69  .map(pattern, {});
70 
71  defs->defineOptionalProperty<float>("ar.MarkerSize", p.markerSize.load(),
72  "The side length of the marker(s)");
73  defs->optional(p.dictionary, "ar.Dictionary", "The ArUco dictionary.")
74  .map("4X4_50", cv::aruco::DICT_4X4_50)
75  .map("4X4_100", cv::aruco::DICT_4X4_100)
76  .map("4X4_250", cv::aruco::DICT_4X4_250)
77  .map("4X4_1000", cv::aruco::DICT_4X4_1000)
78  .map("5X5_50", cv::aruco::DICT_5X5_50)
79  .map("5X5_100", cv::aruco::DICT_5X5_100)
80  .map("5X5_250", cv::aruco::DICT_5X5_250)
81  .map("5X5_1000", cv::aruco::DICT_5X5_1000)
82  .map("6X6_50", cv::aruco::DICT_6X6_50)
83  .map("6X6_100", cv::aruco::DICT_6X6_100)
84  .map("6X6_250", cv::aruco::DICT_6X6_250)
85  .map("6X6_1000", cv::aruco::DICT_6X6_1000)
86  .map("7X7_50", cv::aruco::DICT_7X7_50)
87  .map("7X7_100", cv::aruco::DICT_7X7_100)
88  .map("7X7_250", cv::aruco::DICT_7X7_250)
89  .map("7X7_1000", cv::aruco::DICT_7X7_1000)
90  .map("ARUCO_ORIGINAL", cv::aruco::DICT_ARUCO_ORIGINAL)
91  ;
92 
93  defs->defineOptionalProperty<std::string>("ar.MarkerGeneration", "https://chev.me/arucogen/",
94  "You can generate ArUco markers here.");
95 
96  defs->defineOptionalProperty<bool>("visu.Enabled", p.visuEnabled.load(),
97  "If true, visualize marker poses.");
98 
99  return defs;
100  }
101 
103  {
104  return "ArMarkerLocalizerOpenCV";
105  }
106 
107 
108 
110  {
111  *arucoDictionary = cv::aruco::getPredefinedDictionary(p.dictionary);
112  p.markerSize = getProperty<float>("ar.MarkerSize").getValue();
113  p.visuEnabled = getProperty<bool>("visu.Enabled").getValue();
114 
115  {
116  std::string propName = "cam.ExtraDistortionCoeffs";
117  std::vector<std::string> coeffsStr;
118  getProperty(coeffsStr, "cam.ExtraDistortionCoeffs");
119 
120  p.extraDistortionCoeffs.clear();
121  for (const auto& coeffStr : coeffsStr)
122  {
123  try
124  {
125  p.extraDistortionCoeffs.push_back(std::stof(coeffStr));
126  }
127  catch (const std::invalid_argument&)
128  {
129  ARMARX_WARNING << "Could not parse '" << coeffStr << "' as float in property " << propName << "."
130  << "\nIgnoring extra parameters.";
131  p.extraDistortionCoeffs.clear();
132  break;
133  }
134  }
135  }
136  }
137 
138 
140  {
141  ARMARX_VERBOSE << "Marker size: " << p.markerSize;
142 
143  visionx::ImageProviderInfo imageProviderInfo = getImageProvider(p.imageProviderName);
144  StereoCalibrationInterfacePrx calibrationProvider = StereoCalibrationInterfacePrx::checkedCast(imageProviderInfo.proxy);
145 
146  std::unique_ptr<CStereoCalibration> stereoCalibration(visionx::tools::convert(calibrationProvider->getStereoCalibration()));
147  CCalibration::CCameraParameters ivtCameraParameters = stereoCalibration->GetLeftCalibration()->GetCameraParameters();
148 
149  this->cameraMatrix = visionx::makeCameraMatrix(ivtCameraParameters);
150  ARMARX_VERBOSE << "Camera matrix: \n" << this->cameraMatrix;
151 
152  {
153  // "Vector of distortion coefficients (k1,k2,p1,p2 [,k3 [,k4,k5,k6], [s1,s2,s3,s4]]) of 4, 5, 8 or 12 elements"
154  // Source: https://docs.opencv.org/3.2.0/d9/d6a/group__aruco.html#ga7da45d2e8139504f3a532d884b4fb4ac
155 
156  std::set<int> allowedSizes = {4, 5, 8, 12};
157  ARMARX_VERBOSE << "Got " << p.extraDistortionCoeffs.size() << " extra distortion coefficients.";
158 
159  int num = int(4 + p.extraDistortionCoeffs.size());
160  ARMARX_CHECK_POSITIVE(allowedSizes.count(num))
161  << "Allowed sizes: " << simox::alg::join(simox::alg::multi_to_string(allowedSizes.begin(), allowedSizes.end()), " ")
162  << "\n num = " << num << " = 4 + " << p.extraDistortionCoeffs.size() << " = 4 + #extra coeffs";
163 
164  cv::Mat distortionParameters(num, 1, cv::DataType<float>::type);
165 
166  if (!calibrationProvider->getImagesAreUndistorted())
167  {
168  // k1, k2, p1, p2
169  for (int i = 0; i < 4; ++i)
170  {
171  distortionParameters.at<float>(i, 0) = ivtCameraParameters.distortion[i];
172  }
173  // extra
174  for (int i = 0; size_t(i) < p.extraDistortionCoeffs.size(); ++i)
175  {
176  distortionParameters.at<float>(4 + i, 0) = p.extraDistortionCoeffs.at(size_t(i));
177  }
178  }
179  else
180  {
181  for (int i = 0; i < distortionParameters.rows; ++i)
182  {
183  distortionParameters.at<float>(i, 0) = 0;
184  }
185  }
186 
187  this->distortionCoeffs = distortionParameters;
188  ARMARX_CHECK_POSITIVE(allowedSizes.count(this->distortionCoeffs.rows));
189  }
190  ARMARX_VERBOSE << "Distortion coefficients: \n" << this->distortionCoeffs;
191 
192 
193  cameraImages = new CByteImage*[2];
194 
195  cameraImages[0] = tools::createByteImage(imageProviderInfo);
196  cameraImages[1] = tools::createByteImage(imageProviderInfo);
197 
200  }
201 
203  {
204  if (waitForImages(500))
205  {
206  getImages(cameraImages);
207  }
208  else
209  {
210  ARMARX_VERBOSE << "Timeout or error in wait for images.";
211  return;
212  }
213 
214  ArMarkerLocalizationResultList result = localizeAllMarkersInternal();
215  {
216  std::unique_lock lock(resultMutex);
217  lastLocalizationResult = result;
218  }
219 
220  {
221  armarx::viz::Layer layer = arviz.layer("Marker Poses");
222  if (p.visuEnabled)
223  {
224  for (const auto& r : result)
225  {
227  .pose(armarx::FramedPosePtr::dynamicCast(r.pose)->toEigen()));
228  }
229  }
230  arviz.commit({layer});
231  }
232  }
233 
234  ArMarkerLocalizationResultList ArMarkerLocalizerOpenCV::localizeAllMarkersInternal()
235  {
236  ARMARX_TRACE;
237 
238  IplImage* imageIpl = IplImageAdaptor::Adapt(cameraImages[0]);
239  cv::Mat imageOpenCV = cv::cvarrToMat(imageIpl);
240 
241  // Detect markers in image.
242 
243  ARMARX_DEBUG << "Calling marker detection";
244 
245  std::vector<int> markerIDs;
246  std::vector<std::vector<cv::Point2f>> markerCorners;
247  cv::aruco::detectMarkers(imageOpenCV, arucoDictionary, markerCorners, markerIDs, arucoParameters);
248 
249 
250  ARMARX_CHECK_EQUAL(markerIDs.size(), markerCorners.size());
251 
252  ARMARX_VERBOSE << "Localized " << markerIDs.size() << " markers: "
253  << simox::alg::join(simox::alg::multi_to_string(markerIDs), " ");
254 
255  // We could draw an visu image:
256  cv::Mat outputImage = imageOpenCV.clone();
257  if (not markerIDs.empty())
258  {
259  cv::aruco::drawDetectedMarkers(outputImage, markerCorners, markerIDs);
260  }
261  // cv::aruco::drawDetectedMarkers(imageOpenCV, markerCorners, markerIDs);
262 
263 
264  // Estimate marker poses.
265  ARMARX_DEBUG << "Estimating marker poses.";
266 
267  std::vector<cv::Vec3d> rvecs, tvecs;
268  cv::aruco::estimatePoseSingleMarkers(markerCorners, p.markerSize, cameraMatrix, distortionCoeffs, rvecs, tvecs);
269  ARMARX_CHECK_EQUAL(rvecs.size(), markerCorners.size());
270  ARMARX_CHECK_EQUAL(tvecs.size(), markerCorners.size());
271  ARMARX_VERBOSE << "Estimated " << rvecs.size() << " marker poses.";
272 
273  // Assemble results
274 
275  visionx::ArMarkerLocalizationResultList resultList;
276 
277  for (size_t i = 0; i < markerIDs.size(); ++i)
278  {
279  visionx::ArMarkerLocalizationResult& result = resultList.emplace_back();
280 
281  const cv::Vec3d& tvec = tvecs.at(i);
282  Eigen::Vector3f position = Eigen::Vector3d(tvec(0), tvec(1), tvec(2)).cast<float>();
283 
284  cv::Mat cvOrientation;
285  try
286  {
287  cv::Rodrigues(rvecs.at(i), cvOrientation);
288  }
289  catch (const std::exception& e)
290  {
291  ARMARX_ERROR << "Caught exception when running cv::aruco::Rodrigues(): \n" << e.what();
292  return {};
293  }
294  ARMARX_CHECK_EQUAL(cvOrientation.rows, 3);
295  ARMARX_CHECK_EQUAL(cvOrientation.cols, 3);
296 
297  Eigen::Matrix3d orientation;
298  cv::cv2eigen(cvOrientation, orientation);
299 
300  result.pose = new armarx::FramedPose(orientation.cast<float>(), position, p.referenceFrame, p.agentName);
301  result.id = markerIDs.at(i);
302 
303  cv::drawFrameAxes(outputImage, cameraMatrix, distortionCoeffs, rvecs[i], tvecs[i], 0.1);
304  }
305 
306  CByteImage* output_cimage = new CByteImage();
307  copyCvMatToIVT(outputImage, output_cimage);
308 
309  CByteImage* result_images[2] = {cameraImages[0], output_cimage};
310  provideResultImages(result_images);
311  delete output_cimage;
312  return resultList;
313  }
314 
315 
316  visionx::ArMarkerLocalizationResultList visionx::ArMarkerLocalizerOpenCV::LocalizeAllMarkersNow(const Ice::Current&)
317  {
318  if (waitForImages(500))
319  {
320  getImages(cameraImages);
321  return localizeAllMarkersInternal();
322  }
323  else
324  {
325  ARMARX_WARNING << "No new Images available!";
326  return {};
327  }
328  }
329 
330 
331  ArMarkerLocalizationResultList ArMarkerLocalizerOpenCV::GetLatestLocalizationResult(const Ice::Current&)
332  {
333  std::unique_lock lock(resultMutex);
334  return lastLocalizationResult;
335  }
336 
338  {
339  using namespace armarx::RemoteGui::Client;
340 
341  GridLayout grid;
342  int row = 0;
343 
344  tab.markerSize.setRange(1.0, 1000.0);
345  tab.markerSize.setSteps(1000);
346  tab.markerSize.setDecimals(1);
347  tab.markerSize.setValue(p.markerSize);
348 
349  tab.visuEnabled.setValue(p.visuEnabled);
350 
351  grid.add(Label("Marker size:"), {row, 0}).add(tab.markerSize, {row, 1});
352  row++;
353  grid.add(Label("Enable visu:"), {row, 0}).add(tab.visuEnabled, {row, 1});
354  row++;
355 
356  VBoxLayout root = {grid, VSpacer()};
357  RemoteGui_createTab(getName(), root, &tab);
358  }
359 
361  {
362  if (tab.markerSize.hasValueChanged())
363  {
364  p.markerSize = tab.markerSize.getValue();
365  }
366  if (tab.visuEnabled.hasValueChanged())
367  {
368  p.visuEnabled = tab.visuEnabled.getValue();
369  }
370  }
371 
372 }
visionx::ArMarkerLocalizerOpenCV::onInitImageProcessor
void onInitImageProcessor() override
Setup the vision component.
Definition: ArMarkerLocalizerOpenCV.cpp:109
visionx::ArMarkerLocalizerOpenCV::getDefaultName
std::string getDefaultName() const override
Definition: ArMarkerLocalizerOpenCV.cpp:102
armarx::viz::Client::commit
CommitResult commit(StagedCommit const &commit)
Definition: Client.cpp:80
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
visionx::ArMarkerLocalizerOpenCV::createRemoteGuiTab
void createRemoteGuiTab()
Definition: ArMarkerLocalizerOpenCV.cpp:337
visionx
ArmarX headers.
Definition: OpenPoseStressTest.h:38
visionx::ArMarkerLocalizerOpenCVPropertyDefinitions
Definition: ArMarkerLocalizerOpenCV.h:44
visionx::ArMarkerLocalizerOpenCV::RemoteGui_update
void RemoteGui_update() override
Definition: ArMarkerLocalizerOpenCV.cpp:360
armarx::VariantType::FramedPose
const VariantTypeId FramedPose
Definition: FramedPose.h:37
visionx::ArMarkerLocalizerOpenCV::process
void process() override
Process the vision component.
Definition: ArMarkerLocalizerOpenCV.cpp:202
armarx::RemoteGui::Client::VBoxLayout
Definition: Widgets.h:167
armarx::RemoteGui::Client::GridLayout::add
GridLayout & add(Widget const &child, Pos pos, Span span=Span{1, 1})
Definition: Widgets.cpp:412
visionx::ImageProcessor::getImageProvider
ImageProviderInfo getImageProvider(std::string name, ImageType destinationImageType=eRgb, bool waitForProxy=false)
Select an ImageProvider.
Definition: ImageProcessor.cpp:152
visionx::ArMarkerLocalizerOpenCV::LocalizeAllMarkersNow
visionx::ArMarkerLocalizationResultList LocalizeAllMarkersNow(const Ice::Current &) override
Definition: ArMarkerLocalizerOpenCV.cpp:316
visionx::ArMarkerLocalizerOpenCV::createPropertyDefinitions
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition: ArMarkerLocalizerOpenCV.cpp:54
armarx::viz::Layer::add
void add(ElementT const &element)
Definition: Layer.h:29
armarx::RemoteGui::Client::VSpacer
Definition: Widgets.h:204
visionx::tools::createByteImage
CByteImage * createByteImage(const ImageFormatInfo &imageFormat, const ImageType imageType)
Creates a ByteImage for the destination type specified in the given imageProviderInfo.
visionx::ArMarkerLocalizerOpenCV::onConnectImageProcessor
void onConnectImageProcessor() override
Implement this method in the ImageProcessor in order execute parts when the component is fully initia...
Definition: ArMarkerLocalizerOpenCV.cpp:139
visionx::ImageProviderInfo
Definition: ImageProcessor.h:466
visionx::tools::convert
CByteImage::ImageType convert(const ImageType visionxImageType)
Converts a VisionX image type into an image type of IVT's ByteImage.
Definition: TypeMapping.cpp:95
ArMarkerLocalizerOpenCV.h
visionx::voxelgrid::Label
uint32_t Label
Type of an object label.
Definition: types.h:7
armarx::RemoteGui::Client::GridLayout
Definition: Widgets.h:186
ARMARX_TRACE
#define ARMARX_TRACE
Definition: trace.h:69
FramedPose.h
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:177
GfxTL::Vec3d
VectorXD< 3, double > Vec3d
Definition: VectorXD.h:695
armarx::conversions::cv2eigen
Eigen::Vector2f cv2eigen(const cv::Point2f &pt)
Definition: opencv_eigen.h:47
visionx::ArMarkerLocalizerOpenCV::GetLatestLocalizationResult
visionx::ArMarkerLocalizationResultList GetLatestLocalizationResult(const Ice::Current &) override
Definition: ArMarkerLocalizerOpenCV.cpp:331
armarx::viz::Pose
Definition: Elements.h:179
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:189
visionx::ImageProcessor::getImages
int getImages(CByteImage **ppImages)
Poll images from provider.
Definition: ImageProcessor.cpp:351
ARMARX_CHECK_POSITIVE
#define ARMARX_CHECK_POSITIVE(number)
This macro evaluates whether number is positive (> 0) and if it turns out to be false it will throw a...
Definition: ExpressionException.h:145
armarx::to_string
const std::string & to_string(const std::string &s)
Definition: StringHelpers.h:40
armarx::LightweightRemoteGuiComponentPluginUser::RemoteGui_startRunningTask
void RemoteGui_startRunningTask()
Definition: LightweightRemoteGuiComponentPlugin.cpp:110
ExpressionException.h
armarx::Component::getConfigIdentifier
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition: Component.cpp:74
visionx::copyCvMatToIVT
void copyCvMatToIVT(cv::Mat const &input, CByteImage *output)
Copies the contents of an OpenCV matrix to an IVT CByteImage.
Definition: OpenCVUtil.cpp:13
visionx::ImageProviderInfo::proxy
ImageProviderInterfacePrx proxy
proxy to image provider
Definition: ImageProcessor.h:472
armarx::viz::ElementOps::pose
DerivedT & pose(Eigen::Matrix4f const &pose)
Definition: ElementOps.h:159
armarx::ArVizComponentPluginUser::arviz
armarx::viz::Client arviz
Definition: ArVizComponentPlugin.h:43
armarx::PropertyUser::getProperty
Property< PropertyType > getProperty(const std::string &name)
Property creation and retrieval.
Definition: PropertyUser.h:179
armarx::LightweightRemoteGuiComponentPluginUser::RemoteGui_createTab
void RemoteGui_createTab(std::string const &name, RemoteGui::Client::Widget const &rootWidget, RemoteGui::Client::Tab *tab)
Definition: LightweightRemoteGuiComponentPlugin.cpp:95
IceUtil::Handle< class PropertyDefinitionContainer >
OpenCVUtil.h
visionx::ImageProcessorPropertyDefinitions
Definition: ImageProcessor.h:61
ImageUtil.h
visionx::ImageProcessor::provideResultImages
void provideResultImages(CByteImage **images, armarx::MetaInfoSizeBasePtr info=nullptr)
sends result images for visualization
Definition: ImageProcessor.cpp:245
armarx::ManagedIceObject::getName
std::string getName() const
Retrieve name of object.
Definition: ManagedIceObject.cpp:107
visionx::makeCameraMatrix
cv::Mat makeCameraMatrix(const visionx::CameraParameters &cameraParams)
Definition: opencv_conversions.cpp:25
ARMARX_CHECK_EQUAL
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
Definition: ExpressionException.h:130
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:186
armarx::viz::Client::layer
Layer layer(std::string const &name) const
Definition: Client.cpp:73
armarx::RemoteGui::Client
Definition: EigenWidgets.cpp:8
armarx::viz::Layer
Definition: Layer.h:12
visionx::ArMarkerLocalizerOpenCVPropertyDefinitions::ArMarkerLocalizerOpenCVPropertyDefinitions
ArMarkerLocalizerOpenCVPropertyDefinitions(std::string prefix)
Definition: ArMarkerLocalizerOpenCV.cpp:49
visionx::ImageProcessor::waitForImages
bool waitForImages(int milliseconds=1000)
Wait for new images.
Definition: ImageProcessor.cpp:275