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
26
27#include <Eigen/Core>
28
29#include <opencv2/calib3d.hpp>
30#include <opencv2/core/eigen.hpp>
31#include <opencv2/highgui.hpp>
32
33// IVT
34#include <SimoxUtility/algorithm/string.h>
35
40
42
46
47#include <Calibration/Calibration.h>
48#include <Calibration/StereoCalibration.h>
49#include <Image/IplImageAdaptor.h>
50
51namespace visionx
52{
53
59
62 {
65
66 defs->optional(
67 p.imageProviderName, "img.ImageProviderName", "Name of the image provider to use.");
68 defs->optional(
69 p.referenceFrame, "img.ReferenceFrameName", "Name of the ReferenceFrameName");
70 defs->optional(p.agentName, "img.AgentName", "Name of the agent");
71 defs->optional(p.cameraIndex, "img.cameraIndex", "Camera index. Left = 0, Right = 1, ...");
72
73 std::string pattern = "[ k3 [, k4, k5, k6 [, s1, s2, s3, s4 ] ] ]";
74 // defs->optional(p.extraDistortionCoeffs, "cam.ExtraDistortionCoeffs",
75 defs->defineOptionalProperty<std::vector<std::string>>(
76 "cam.ExtraDistortionCoeffs",
77 {},
78 "Optional extra distortion coefficients (which cannot be retrieved from the image "
79 "provider)."
80 "\nThe expected parameters are: " +
81 pattern +
82 ""
83 "\nThat is, you must specifiy 1 (k3), 4 (k3-k6), or 8 (k3-k6, s1-s4) elements."
84 "\nNote: k1, k2, p1, p2 are retrieved from the image provider.")
85 .map(pattern, {});
86
87 defs->defineOptionalProperty<float>(
88 "ar.MarkerSize", p.markerSize.load(), "The side length of the marker(s)");
89 defs->optional(p.dictionary, "ar.Dictionary", "The ArUco dictionary.")
90 .map("4X4_50", cv::aruco::DICT_4X4_50)
91 .map("4X4_100", cv::aruco::DICT_4X4_100)
92 .map("4X4_250", cv::aruco::DICT_4X4_250)
93 .map("4X4_1000", cv::aruco::DICT_4X4_1000)
94 .map("5X5_50", cv::aruco::DICT_5X5_50)
95 .map("5X5_100", cv::aruco::DICT_5X5_100)
96 .map("5X5_250", cv::aruco::DICT_5X5_250)
97 .map("5X5_1000", cv::aruco::DICT_5X5_1000)
98 .map("6X6_50", cv::aruco::DICT_6X6_50)
99 .map("6X6_100", cv::aruco::DICT_6X6_100)
100 .map("6X6_250", cv::aruco::DICT_6X6_250)
101 .map("6X6_1000", cv::aruco::DICT_6X6_1000)
102 .map("7X7_50", cv::aruco::DICT_7X7_50)
103 .map("7X7_100", cv::aruco::DICT_7X7_100)
104 .map("7X7_250", cv::aruco::DICT_7X7_250)
105 .map("7X7_1000", cv::aruco::DICT_7X7_1000)
106 .map("ARUCO_ORIGINAL", cv::aruco::DICT_ARUCO_ORIGINAL);
107
108 defs->defineOptionalProperty<std::string>("ar.MarkerGeneration",
109 "https://chev.me/arucogen/",
110 "You can generate ArUco markers here.");
111
112 defs->defineOptionalProperty<bool>(
113 "visu.Enabled", p.visuEnabled.load(), "If true, visualize marker poses.");
114
115 return defs;
116 }
117
119 {
120 addPlugin(virtualRobotReaderPlugin);
121 }
122
123 std::string
125 {
126 return "ArMarkerLocalizerOpenCV";
127 }
128
129 void
131 {
132 ARMARX_INFO << VAROUT(p.dictionary);
133 *arucoDictionary = cv::aruco::getPredefinedDictionary(p.dictionary);
134 p.markerSize = getProperty<float>("ar.MarkerSize").getValue();
135 ARMARX_INFO << VAROUT(p.markerSize);
136
137 p.visuEnabled = getProperty<bool>("visu.Enabled").getValue();
138
139 {
140 std::string propName = "cam.ExtraDistortionCoeffs";
141 std::vector<std::string> coeffsStr;
142 getProperty(coeffsStr, "cam.ExtraDistortionCoeffs");
143
144 p.extraDistortionCoeffs.clear();
145 for (const auto& coeffStr : coeffsStr)
146 {
147 try
148 {
149 p.extraDistortionCoeffs.push_back(std::stof(coeffStr));
150 }
151 catch (const std::invalid_argument&)
152 {
153 ARMARX_WARNING << "Could not parse '" << coeffStr << "' as float in property "
154 << propName << "."
155 << "\nIgnoring extra parameters.";
156 p.extraDistortionCoeffs.clear();
157 break;
158 }
159 }
160 }
161 }
162
163 void
165 {
166 ARMARX_CHECK_NOT_NULL(virtualRobotReaderPlugin);
167
168 robot = virtualRobotReaderPlugin->get().getRobot(p.agentName);
169 ARMARX_CHECK_NOT_NULL(robot) << p.agentName;
170
171 referenceNode = robot->getRobotNode(p.referenceFrame);
172 ARMARX_CHECK_NOT_NULL(referenceNode) << p.referenceFrame;
173
174 ARMARX_VERBOSE << "Marker size: " << p.markerSize;
175
176 visionx::ImageProviderInfo imageProviderInfo = getImageProvider(p.imageProviderName);
177 StereoCalibrationInterfacePrx calibrationProvider =
178 StereoCalibrationInterfacePrx::checkedCast(imageProviderInfo.proxy);
179
180 std::unique_ptr<CStereoCalibration> stereoCalibration(
181 visionx::tools::convert(calibrationProvider->getStereoCalibration()));
182 CCalibration::CCameraParameters ivtCameraParameters =
183 stereoCalibration->GetLeftCalibration()->GetCameraParameters();
184
185 this->cameraMatrix = visionx::makeCameraMatrix(ivtCameraParameters);
186 ARMARX_VERBOSE << "Camera matrix: \n" << this->cameraMatrix;
187
188 {
189 // "Vector of distortion coefficients (k1,k2,p1,p2 [,k3 [,k4,k5,k6], [s1,s2,s3,s4]]) of 4, 5, 8 or 12 elements"
190 // Source: https://docs.opencv.org/3.2.0/d9/d6a/group__aruco.html#ga7da45d2e8139504f3a532d884b4fb4ac
191
192 std::set<int> allowedSizes = {4, 5, 8, 12};
193 ARMARX_VERBOSE << "Got " << p.extraDistortionCoeffs.size()
194 << " extra distortion coefficients.";
195
196 int num = int(4 + p.extraDistortionCoeffs.size());
197 ARMARX_CHECK_POSITIVE(allowedSizes.count(num))
198 << "Allowed sizes: "
199 << simox::alg::join(
200 simox::alg::multi_to_string(allowedSizes.begin(), allowedSizes.end()), " ")
201 << "\n num = " << num << " = 4 + " << p.extraDistortionCoeffs.size()
202 << " = 4 + #extra coeffs";
203
204 cv::Mat distortionParameters(num, 1, cv::DataType<float>::type);
205
206 if (!calibrationProvider->getImagesAreUndistorted())
207 {
208 // k1, k2, p1, p2
209 for (int i = 0; i < 4; ++i)
210 {
211 distortionParameters.at<float>(i, 0) = ivtCameraParameters.distortion[i];
212 }
213 // extra
214 for (int i = 0; size_t(i) < p.extraDistortionCoeffs.size(); ++i)
215 {
216 distortionParameters.at<float>(4 + i, 0) =
217 p.extraDistortionCoeffs.at(size_t(i));
218 }
219 }
220 else
221 {
222 for (int i = 0; i < distortionParameters.rows; ++i)
223 {
224 distortionParameters.at<float>(i, 0) = 0;
225 }
226 }
227
228 this->distortionCoeffs = distortionParameters;
229 ARMARX_CHECK_POSITIVE(allowedSizes.count(this->distortionCoeffs.rows));
230 }
231 ARMARX_VERBOSE << "Distortion coefficients: \n" << this->distortionCoeffs;
232
233
234 cameraImages = new CByteImage*[2];
235
236 cameraImages[0] = tools::createByteImage(imageProviderInfo);
237 cameraImages[1] = tools::createByteImage(imageProviderInfo);
238
241 }
242
243 void
245 {
246 if (waitForImages(500))
247 {
248 armarx::MetaInfoSizeBasePtr info;
249 getImages(p.imageProviderName, cameraImages, info);
250 m_timestamp_last_image =
252
253 // getImages(cameraImages);
254 }
255 else
256 {
257 ARMARX_VERBOSE << "Timeout or error in wait for images.";
258 return;
259 }
260
261 ArMarkerLocalizationResultList result = localizeAllMarkersInternal();
262 {
263 std::unique_lock lock(resultMutex);
264 lastLocalizationResult = result;
265 // m_timestamp_last_result = m_timestamp_last_image;
266 }
267
268 {
269 armarx::viz::Layer layer = arviz.layer("Marker Poses");
270 if (p.visuEnabled)
271 {
272 const Eigen::Isometry3f global_T_frame{referenceNode->getGlobalPose()};
273
274 for (const auto& r : result)
275 {
276 const Eigen::Isometry3f frame_T_marker{
277 armarx::FramedPosePtr::dynamicCast(r.pose)->toEigen()};
278 const Eigen::Isometry3f global_T_marker = global_T_frame * frame_T_marker;
279
280 layer.add(
281 armarx::viz::Pose(std::to_string(r.id)).pose(global_T_marker.matrix()));
282 }
283 }
284 arviz.commit({layer});
285 }
286 }
287
288 ArMarkerLocalizationResultList
289 ArMarkerLocalizerOpenCV::localizeAllMarkersInternal()
290 {
293 virtualRobotReaderPlugin->get().synchronizeRobot(*robot, armarx::Clock::Now()));
294
295 cameraImages[0]->bytesPerPixel = 3;
296 cameraImages[1]->bytesPerPixel = 3;
297
298 IplImage* imageIpl = IplImageAdaptor::Adapt(cameraImages[p.cameraIndex]);
299 cv::Mat imageOpenCV = cv::cvarrToMat(imageIpl);
300
301 // cv::imshow("Input", imageOpenCV);
302 // cv::waitKey(10);
303
304 // Detect markers in image.
305
306 ARMARX_DEBUG << "Calling marker detection";
307
308 std::vector<int> markerIDs;
309 std::vector<std::vector<cv::Point2f>> markerCorners;
310 cv::aruco::detectMarkers(
311 imageOpenCV, arucoDictionary, markerCorners, markerIDs, arucoParameters);
312
313
314 ARMARX_CHECK_EQUAL(markerIDs.size(), markerCorners.size());
315
316 ARMARX_VERBOSE << "Localized " << markerIDs.size() << " markers: "
317 << simox::alg::join(simox::alg::multi_to_string(markerIDs), " ");
318
319 // We could draw an visu image:
320 cv::Mat outputImage = imageOpenCV.clone();
321 if (not markerIDs.empty())
322 {
323 cv::aruco::drawDetectedMarkers(outputImage, markerCorners, markerIDs);
324 }
325 // cv::aruco::drawDetectedMarkers(imageOpenCV, markerCorners, markerIDs);
326
327
328 // Estimate marker poses.
329 ARMARX_DEBUG << "Estimating marker poses.";
330
331 std::vector<cv::Vec3d> rvecs, tvecs;
332 cv::aruco::estimatePoseSingleMarkers(
333 markerCorners, p.markerSize, cameraMatrix, distortionCoeffs, rvecs, tvecs);
334 ARMARX_CHECK_EQUAL(rvecs.size(), markerCorners.size());
335 ARMARX_CHECK_EQUAL(tvecs.size(), markerCorners.size());
336 ARMARX_VERBOSE << "Estimated " << rvecs.size() << " marker poses.";
337
338 // Assemble results
339
340 visionx::ArMarkerLocalizationResultList resultList;
341
342 for (size_t i = 0; i < markerIDs.size(); ++i)
343 {
344 visionx::ArMarkerLocalizationResult& result = resultList.emplace_back();
345
346 const cv::Vec3d& tvec = tvecs.at(i);
347 Eigen::Vector3f position = Eigen::Vector3d(tvec(0), tvec(1), tvec(2)).cast<float>();
348
349 cv::Mat cvOrientation;
350 try
351 {
352 cv::Rodrigues(rvecs.at(i), cvOrientation);
353 }
354 catch (const std::exception& e)
355 {
356 ARMARX_ERROR << "Caught exception when running cv::aruco::Rodrigues(): \n"
357 << e.what();
358 return {};
359 }
360 ARMARX_CHECK_EQUAL(cvOrientation.rows, 3);
361 ARMARX_CHECK_EQUAL(cvOrientation.cols, 3);
362
363 Eigen::Matrix3d orientation;
364 cv::cv2eigen(cvOrientation, orientation);
365
366 result.pose = new armarx::FramedPose(
367 orientation.cast<float>(), position, p.referenceFrame, p.agentName);
368 result.id = markerIDs.at(i);
369
370 armarx::core::time::toIce(result.timestamp, m_timestamp_last_image);
371
372 cv::drawFrameAxes(outputImage, cameraMatrix, distortionCoeffs, rvecs[i], tvecs[i], 0.1);
373 }
374
375 CByteImage* output_cimage =
376 new CByteImage(outputImage.size[0], outputImage.size[1], CByteImage::ImageType::eRGB24);
377 copyCvMatToIVT(outputImage, output_cimage);
378
379 CByteImage* result_images[2] = {cameraImages[0], output_cimage};
380 provideResultImages(result_images);
381 delete output_cimage;
382 return resultList;
383 }
384
385 visionx::ArMarkerLocalizationResultList
387 {
388 if (waitForImages(500))
389 {
390 getImages(cameraImages);
391 return localizeAllMarkersInternal();
392 }
393 else
394 {
395 ARMARX_WARNING << "No new Images available!";
396 return {};
397 }
398 }
399
400 ArMarkerLocalizationResultList
402 {
403 std::unique_lock lock(resultMutex);
404 return lastLocalizationResult;
405 }
406
407 void
409 {
410 using namespace armarx::RemoteGui::Client;
411
412 GridLayout grid;
413 int row = 0;
414
415 tab.markerSize.setRange(1.0, 1000.0);
416 tab.markerSize.setSteps(1000);
417 tab.markerSize.setDecimals(1);
418 tab.markerSize.setValue(p.markerSize);
419
420 tab.visuEnabled.setValue(p.visuEnabled);
421
422 grid.add(Label("Marker size:"), {row, 0}).add(tab.markerSize, {row, 1});
423 row++;
424 grid.add(Label("Enable visu:"), {row, 0}).add(tab.visuEnabled, {row, 1});
425 row++;
426
427 VBoxLayout root = {grid, VSpacer()};
428 RemoteGui_createTab(getName(), root, &tab);
429 }
430
431 void
433 {
434 if (tab.markerSize.hasValueChanged())
435 {
436 p.markerSize = tab.markerSize.getValue();
437 }
438 if (tab.visuEnabled.hasValueChanged())
439 {
440 p.visuEnabled = tab.visuEnabled.getValue();
441 }
442 }
443
444} // namespace visionx
int Label(int n[], int size, int *curLabel, MiscLib::Vector< std::pair< int, size_t > > *labels)
Definition Bitmap.cpp:801
#define VAROUT(x)
static DateTime Now()
Current time on the virtual clock.
Definition Clock.cpp:93
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
static Duration MicroSeconds(std::int64_t microSeconds)
Constructs a duration in microseconds.
Definition Duration.cpp:24
PluginT * addPlugin(const std::string prefix="", ParamsT &&... params)
std::string getName() const
Retrieve name of object.
std::string prefix
Prefix of the properties such as namespace, domain, component name, etc.
Represents a point in time.
Definition DateTime.h:25
DerivedT & pose(Eigen::Matrix4f const &pose)
Definition ElementOps.h:176
void onConnectImageProcessor() override
Implement this method in the ImageProcessor in order execute parts when the component is fully initia...
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void process() override
Process the vision component.
void onInitImageProcessor() override
Setup the vision component.
visionx::ArMarkerLocalizationResultList LocalizeAllMarkersNow(const Ice::Current &) override
visionx::ArMarkerLocalizationResultList GetLatestLocalizationResult(const Ice::Current &) override
std::string getDefaultName() const override
ImageProcessorPropertyDefinitions(std::string prefix)
bool waitForImages(int milliseconds=1000)
Wait for new images.
ImageProviderInfo getImageProvider(std::string name, ImageType destinationImageType=eRgb, bool waitForProxy=false)
Select an ImageProvider.
int getImages(CByteImage **ppImages)
Poll images from provider.
void provideResultImages(CByteImage **images, armarx::MetaInfoSizeBasePtr info=nullptr)
sends result images for visualization
ImageProviderInterfacePrx proxy
proxy to image provider
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#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...
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#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...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
void toIce(dto::ClockType::ClockTypeEnum &dto, const ClockType &bo)
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
CByteImage * createByteImage(const ImageFormatInfo &imageFormat, const ImageType imageType)
Creates a ByteImage for the destination type specified in the given imageProviderInfo.
CByteImage::ImageType convert(const ImageType visionxImageType)
Converts a VisionX image type into an image type of IVT's ByteImage.
ArmarX headers.
cv::Mat makeCameraMatrix(const visionx::CameraParameters &cameraParams)
void copyCvMatToIVT(cv::Mat const &input, CByteImage *output)
Copies the contents of an OpenCV matrix to an IVT CByteImage.
void RemoteGui_createTab(std::string const &name, RemoteGui::Client::Widget const &rootWidget, RemoteGui::Client::Tab *tab)
GridLayout & add(Widget const &child, Pos pos, Span span=Span{1, 1})
Definition Widgets.cpp:438
void add(ElementT const &element)
Definition Layer.h:31
#define ARMARX_TRACE
Definition trace.h:77