ValveAttention.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package VisionX::ArmarXObjects::ValveAttention
17 * @author Markus Grotz ( markus dot grotz at kit dot edu )
18 * @date 2016
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "ValveAttention.h"
24
26
27#include <pcl/common/point_tests.h>
28
31
33
34#include <Image/ImageProcessor.h>
35
36namespace armarx
37{
38
39
46
47 void
49 {
50 usingProxy(getProperty<std::string>("ViewSelectionName").getValue());
51 usingProxy(getProperty<std::string>("RobotStateComponentName").getValue());
52
53 providerName = getProperty<std::string>("providerName").getValue();
54 usingImageProvider(providerName);
55 usingPointCloudProvider(providerName);
56
57 std::string graphFileName = "RobotAPI/ViewSelection/graph40k.gra";
58
59 armarx::CMakePackageFinder finder("RobotAPI");
61
62 if (ArmarXDataPath::getAbsolutePath(graphFileName, graphFileName))
63 {
64 saliencyEgosphereGraph = new CIntensityGraph(graphFileName);
65 ARMARX_VERBOSE << "Created egosphere graph with "
66 << saliencyEgosphereGraph->getNodes()->size() << "nodes";
67 graphLookupTable = new CGraphPyramidLookupTable(9, 18);
68 graphLookupTable->buildLookupTable(saliencyEgosphereGraph);
69 }
70 else
71 {
72 ARMARX_ERROR << "Could not find required graph file";
74 }
75
76 headFrameName = getProperty<std::string>("HeadFrameName").getValue();
77 hog = new HoG();
78 hog->loadTrainingData(getProperty<std::string>("TrainingData").getValue());
79 hog->setParameters(true, true);
80 }
81
82 void
84 {
85
86 visionx::ImageType imageDisplayType = visionx::tools::typeNameToImageType("rgb");
87
88 visionx::ImageProviderInfo imageProviderInfo =
89 getImageProvider(providerName, imageDisplayType);
90
91 numImages = imageProviderInfo.numberImages;
92
93 images = new CByteImage*[numImages];
94 for (int i = 0; i < numImages; i++)
95 {
96 images[i] = visionx::tools::createByteImage(imageProviderInfo);
97 }
98
99 numResultImages = 3;
100 result = new CByteImage*[numResultImages];
101 result[0] = images[0];
102 result[1] = visionx::tools::createByteImage(imageProviderInfo);
103 result[2] = visionx::tools::createByteImage(imageProviderInfo);
104
105
106 visionx::ImageDimension dimension(images[0]->width, images[0]->height);
107 enableResultImages(numResultImages, dimension, visionx::tools::typeNameToImageType("rgb"));
108
109 robotStateComponent = getProxy<RobotStateComponentInterfacePrx>(
110 getProperty<std::string>("RobotStateComponentName").getValue());
111
113 robotStateComponent, VirtualRobot::RobotIO::RobotDescription::eFull);
114
116 getProperty<std::string>("ViewSelectionName").getValue());
117 }
118
119 void
123
124 void
126 {
127 for (int i = 0; i < numImages; i++)
128 {
129 delete images[i];
130 }
131 delete[] images;
132
133 for (int i = 0; i < numResultImages; i++)
134 {
135 delete result[i];
136 }
137 delete[] result;
138
139 delete graphLookupTable;
140 delete saliencyEgosphereGraph;
141 delete hog;
142 }
143
144 void
146 {
147 ARMARX_IMPORTANT << "process";
148
149 std::lock_guard<std::mutex> lock(mutex);
150
151 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr currentPointCloud(
152 new pcl::PointCloud<pcl::PointXYZRGBA>());
153
154
155 ARMARX_VERBOSE << "waiting for images";
156 if (!waitForImages(providerName))
157 {
158 ARMARX_WARNING << "Timeout while waiting for images";
159 return;
160 }
161
162 ARMARX_VERBOSE << "waiting for pointcloud";
163 if (!waitForPointClouds(providerName))
164 {
165 ARMARX_WARNING << "Timeout while waiting for pointcloud";
166 return;
167 }
168
169 ARMARX_VERBOSE << "getting images";
170 if (getImages(images) != numImages)
171 {
172 ARMARX_WARNING << "Unable to transfer or read images";
173 return;
174 }
175
176 ARMARX_VERBOSE << "getting pointcloud";
177 if (!getPointClouds(currentPointCloud))
178 {
179 ARMARX_WARNING << "Unable to transfer or read point cloud";
180 return;
181 }
182
183
184 RemoteRobot::synchronizeLocalClone(robot, robotStateComponent);
185
186 saliencyEgosphereGraph->set(0.0);
187
188 const int scaleFactor = 4; //images[0]->width / 255;
189
190 int width = images[0]->width / scaleFactor;
191 int height = images[0]->height / scaleFactor;
192 CByteImage* image = new CByteImage(width, height, CByteImage::eRGB24);
193 ::ImageProcessor::Resize(images[0], image);
194
195 ARMARX_VERBOSE << "downscaling image to: " << width << "x" << height;
196
197 //CByteImage* saliencyImage = result[0]; //new CByteImage(width, height, CByteImage::eRGB24);
198 CFloatImage* saliencyImage = new CFloatImage(width, height, 1);
199
200
201 ARMARX_VERBOSE << "computing saliency map";
202 IceUtil::Time startTime = TimeUtil::GetTime();
203
204 hog->findSalientRegions(image, saliencyImage);
205 //*************************test for Cluster**************************/
206 std::vector<cv::Point2f> points;
207 cv::Mat outputLable;
208 cv::Mat centers;
209 std::map<int, int> saliencyHist;
210 float priorityProcent = 0.9;
211 int clusterNum = 3;
212 int intensityThreshold = 255;
213 int saliencySum = 0;
214 int saliencyImageWidth = saliencyImage->width;
215
216 for (int i = 0; i < saliencyImage->height; i++)
217 {
218 for (int j = 0; j < saliencyImageWidth; j++)
219 {
220 saliencyHist[(int)(saliencyImage->pixels[i * saliencyImageWidth + j] * 255.0)]++;
221 }
222 }
223
224 // determine 90% number of non-zero salient values
225 int usefulSampleAmount =
226 priorityProcent * (saliencyImage->width * saliencyImage->height - saliencyHist[0]);
227 for (int i = 1; i <= 255; i++)
228 {
229 if (!saliencyHist.count(i))
230 {
231 continue;
232 }
233 saliencySum += saliencyHist[i];
234 if (saliencySum > usefulSampleAmount)
235 {
236 intensityThreshold = i;
237 break;
238 }
239 }
240
241 for (int i = 0; i < saliencyImage->height; i++)
242 {
243 for (int j = 0; j < saliencyImageWidth; j++)
244 {
245 if (saliencyImage->pixels[i * saliencyImageWidth + j] * 255.0 > intensityThreshold)
246 {
247 points.push_back(cv::Point2f(j, i));
248 }
249 }
250 }
251
252 cv::Mat samples(points.size(), 2, CV_32F);
253 for (size_t i = 0; i < points.size(); i++)
254 {
255 samples.at<float>(i, 0) = points[i].x;
256 samples.at<float>(i, 1) = points[i].y;
257 }
258
259 if (points.size())
260 {
261 cv::kmeans(samples,
262 clusterNum,
263 outputLable,
264 cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 20, 0.1),
265 10,
266 cv::KMEANS_PP_CENTERS,
267 centers);
268 }
269
270 // calculate the number of elements in each cluster and get first two biggest cluster
271 std::map<int, int> numberOfEachCluster;
272 for (int i = 0; i < samples.rows; i++)
273 {
274 numberOfEachCluster[outputLable.at<int>(i, 0)]++;
275 }
276 size_t maxCluster = 0;
277 int secondCluster;
278 for (size_t i = 0; i < numberOfEachCluster.size(); i++)
279 {
280 if (numberOfEachCluster[i] > numberOfEachCluster[maxCluster])
281 {
282 maxCluster = i;
283 }
284 }
285 if (maxCluster != 0)
286 {
287 secondCluster = 0;
288 }
289 else
290 {
291 secondCluster = 1;
292 }
293 for (size_t i = 0; i < numberOfEachCluster.size(); i++)
294 {
295 if ((i != maxCluster) && numberOfEachCluster[i] > numberOfEachCluster[secondCluster])
296 {
297 secondCluster = i;
298 }
299 }
300
301 //*************************test for Cluster END**************************/
302
303 ARMARX_VERBOSE << "done computing saliency map. took "
304 << (TimeUtil::GetTime() - startTime).toMilliSeconds() << "ms";
305
306 delete image;
307
308#pragma omp parallel for
309 for (int i = 0; i < images[0]->height; i++)
310 {
311 for (int j = 0; j < images[0]->width; j++)
312 {
313 int idx = i * images[0]->width + j;
314
315 float saliency =
316 saliencyImage->pixels[(i / scaleFactor * width) + (j / scaleFactor)];
317
318 result[1]->pixels[3 * idx + 0] = 255 * saliency;
319 result[1]->pixels[3 * idx + 1] = 255 * saliency;
320 result[1]->pixels[3 * idx + 2] = 255 * saliency;
321
322 result[2]->pixels[3 * idx + 0] =
323 (0.2 + 0.8 * saliency) * result[0]->pixels[3 * idx + 0];
324 result[2]->pixels[3 * idx + 1] =
325 (0.2 + 0.8 * saliency) * result[0]->pixels[3 * idx + 1];
326 result[2]->pixels[3 * idx + 2] =
327 (0.2 + 0.8 * saliency) * result[0]->pixels[3 * idx + 2];
328 }
329 }
330
331
332 /*********transform cluster daten (the points near cluster centers) from pointcloud to sphere **********/
333 /*
334 for (int i = 0; i < samples.rows; i++)
335 {
336 int downScaleIdx = ((int)samples.at<float>(i, 1) * width) + ((int)samples.at<float>(i, 0));
337 int idx = (int)samples.at<float>(i, 1) * scaleFactor * images[0]->width + (int)samples.at<float>(i, 0) * scaleFactor;
338 float saliency = saliencyImage->pixels[downScaleIdx];
339 int clusterLabel = outputLable.at<int>(i, 0);
340 float distanceToClusterCenter = sqrt(pow((centers.at<float>(clusterLabel, 0) - samples.at<float>(i, 0)), 2) + pow((centers.at<float>(clusterLabel, 1) - samples.at<float>(i, 1)), 2));
341
342 if (saliency > 0 && (distanceToClusterCenter < (width / 5)))
343 {
344 if (idx >= (int)currentPointCloud->points.size())
345 {
346 continue;
347 }
348
349 Eigen::Vector3f vec = currentPointCloud->points[idx].getVector3fMap();
350
351 if (!pcl::isFinite(currentPointCloud->points[idx]))
352 {
353 continue;
354 }
355 TSphereCoord positionInSphereCoordinates;
356 //armarx::GlobalFrame
357 FramedPositionPtr currentViewTarget = new FramedPosition(vec, "DepthCamera", robotStateComponent->getSynchronizedRobot()->getName());
358 currentViewTarget->changeFrame(robot, headFrameName);
359
360 MathTools::convert(currentViewTarget->toEigen(), positionInSphereCoordinates);
361 int closestNodeIndex = graphLookupTable->getClosestNode(positionInSphereCoordinates);
362 CIntensityNode* currentNode = ((CIntensityNode*)saliencyEgosphereGraph->getNodes()->at(closestNodeIndex));
363
364 float currentSaliency = currentNode->getIntensity();
365
366 // give priority to the cluster which has the most elements, because this cluster always near valve
367 if (clusterLabel == maxCluster)
368 {
369 //consider about history? & predict --> consider about observation & predict
370 currentSaliency = std::min(1.0f, currentSaliency + saliency * (float)1.3);
371
372 //consider only about predict
373 //currentSaliency = std::min(1.0f, saliency * (float)1.3);
374 //std::cout << "currentSaliency!! " << currentSaliency << std::endl;
375 }
376 else
377 {
378 if (outputLable.at<int>(i, 0) == secondCluster)
379 {
380 currentSaliency = std::min(1.0f, currentSaliency + saliency * (float)1.02);
381 //currentSaliency = std::min(1.0f, saliency * (float)1.02);
382 }
383 }
384 currentNode->setIntensity(currentSaliency);
385 }
386 }
387
388 */
389 /*********transform cluster daten from pointcloud to sphere ***** END*****/
390
391 /*********transform cluster centers from pointcloud to sphere ***** *****/
392 for (int i = 0; i < centers.rows; i++)
393 {
394 int downScaleIdx =
395 ((int)centers.at<float>(i, 1) * width) + ((int)centers.at<float>(i, 0));
396 int idx = (int)centers.at<float>(i, 1) * scaleFactor * images[0]->width +
397 (int)centers.at<float>(i, 0) * scaleFactor;
398 //attention: center's saliency may shouldn't directly get from saliency map, because it's saliency can be low
399 float saliency = saliencyImage->pixels[downScaleIdx];
400
401 if (idx >= (int)currentPointCloud->points.size())
402 {
403 continue;
404 }
405
406 Eigen::Vector3f vec = currentPointCloud->points[idx].getVector3fMap();
407
408 if (!pcl::isFinite(currentPointCloud->points[idx]))
409 {
410 continue;
411 }
412 TSphereCoord positionInSphereCoordinates;
413 //armarx::GlobalFrame
414 FramedPositionPtr currentViewTarget = new FramedPosition(
415 vec, "DepthCamera", robotStateComponent->getSynchronizedRobot()->getName());
416 currentViewTarget->changeFrame(robot, headFrameName);
417
418 MathTools::convert(currentViewTarget->toEigen(), positionInSphereCoordinates);
419 int closestNodeIndex = graphLookupTable->getClosestNode(positionInSphereCoordinates);
420
421 float halfCameraOpeningAngle = 12.0 * M_PI / 180.0;
422 float modifiedHalfCameraOpeningAngle = halfCameraOpeningAngle;
423 float distance = currentViewTarget->toEigen().norm();
424
425
426 if (distance > 6000)
427 {
428 ARMARX_WARNING << "position is too far away." << distance;
429 return;
430 }
431
432 const float distanceThreshold = 1500;
433 if (distance < distanceThreshold)
434 {
435 modifiedHalfCameraOpeningAngle = (distance - 0.1f * distanceThreshold) /
436 (0.9f * distanceThreshold) *
437 halfCameraOpeningAngle;
438 modifiedHalfCameraOpeningAngle = std::max(0.0f, modifiedHalfCameraOpeningAngle);
439 }
440
441
442 std::vector<bool> visitedNodes(saliencyEgosphereGraph->getNodes()->size(), false);
443 addSaliencyRecursive(closestNodeIndex,
444 visitedNodes,
445 saliency,
446 positionInSphereCoordinates,
447 modifiedHalfCameraOpeningAngle);
448 }
449 /*********transform cluster centers from pointcloud to sphere ***** END*****/
450
451 //*************************test for Cluster**************************/
452 int index;
453 for (std::size_t row = 0; centers.rows > 0 && row < static_cast<std::size_t>(centers.rows);
454 row++)
455 {
456 for (std::size_t i = 0; i < 8; i++)
457 {
458 for (std::size_t j = 0; j < 8; j++)
459 {
460 index =
461 3 * (((int)centers.at<float>(row, 1) * scaleFactor + i) * images[0]->width +
462 ((int)centers.at<float>(row, 0) * scaleFactor + j));
463 result[2]->pixels[index] = 255;
464 result[2]->pixels[index + 1] = 255 * (row == maxCluster);
465 result[2]->pixels[index + 2] =
466 255 * (row == static_cast<std::size_t>(secondCluster));
467 }
468 }
469 }
470
471 //*************************test for Cluster END**************************/
472
473 ARMARX_VERBOSE << "mapping took !!!!! "
474 << (TimeUtil::GetTime() - startTime).toMilliSeconds() << "ms";
475
476 provideResultImages(result);
477
478 SaliencyMapBasePtr primitiveSaliency = new SaliencyMapBase();
479 primitiveSaliency->name = "ValveAttention";
480 saliencyEgosphereGraph->graphToVec(primitiveSaliency->map);
481 viewSelection->updateSaliencyMap(primitiveSaliency);
482
483 ARMARX_IMPORTANT << "process finished";
484 }
485
486 void
487 ValveAttention::addSaliencyRecursive(const int currentNodeIndex,
488 std::vector<bool>& visitedNodes,
489 const float saliency,
490 const TSphereCoord objectSphereCoord,
491 const float maxDistanceOnArc)
492 {
493
494 // distance on arc between object projection center and node,
495 // normalized by the maximal viewing angle of the camera (=> in [0,1])
496 float normalizedDistance =
498 objectSphereCoord,
499 saliencyEgosphereGraph->getNodes()->at(currentNodeIndex)->getPosition()) /
500 maxDistanceOnArc;
501
502 // increase value of node
503 float newValue = ((CIntensityNode*)saliencyEgosphereGraph->getNodes()->at(currentNodeIndex))
504 ->getIntensity() +
505 (1.0f - 0.5f * normalizedDistance * normalizedDistance) * saliency;
506
507 if (newValue > 1.0)
508 {
509 ARMARX_INFO << "saliency is greater than 1.0: " << newValue;
510 newValue = std::min(1.0f, newValue);
511 }
512
513 ((CIntensityNode*)saliencyEgosphereGraph->getNodes()->at(currentNodeIndex))
514 ->setIntensity(newValue);
515
516 // mark node as visited for this object
517 visitedNodes.at(currentNodeIndex) = true;
518
519 // recurse on neighbours if they were not yet visited and close enough to the object projection center
520 int neighbourIndex;
521
522 for (size_t i = 0; i < saliencyEgosphereGraph->getNodeAdjacency(currentNodeIndex)->size();
523 i++)
524 {
525 neighbourIndex = saliencyEgosphereGraph->getNodeAdjacency(currentNodeIndex)->at(i);
526
527 if (!visitedNodes.at(neighbourIndex))
528 {
530 objectSphereCoord,
531 saliencyEgosphereGraph->getNodes()->at(neighbourIndex)->getPosition()) <=
532 maxDistanceOnArc)
533 {
534 addSaliencyRecursive(neighbourIndex,
535 visitedNodes,
536 saliency,
537 objectSphereCoord,
538 maxDistanceOnArc);
539 }
540 else
541 {
542 visitedNodes.at(neighbourIndex) = true;
543 }
544 }
545 }
546 }
548
549} // namespace armarx
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
uint8_t index
#define M_PI
Definition MathTools.h:17
TNodeList * getNodes()
std::vector< int > * getNodeAdjacency(int nIndex)
Definition hog.h:48
static float getDistanceOnArc(TSphereCoord p1, TSphereCoord p2)
static void convert(TSphereCoord in, Eigen::Vector3d &out)
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
static void addDataPaths(const std::string &dataPathList)
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
The FramedPosition class.
Definition FramedPose.h:158
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
static VirtualRobot::RobotPtr createLocalCloneFromFile(RobotStateComponentInterfacePrx robotStatePrx, VirtualRobot::RobotIO::RobotDescription loadMode=VirtualRobot::RobotIO::eFull)
This is a convenience function for createLocalClone, which automatically gets the filename from the R...
static bool synchronizeLocalClone(VirtualRobot::RobotPtr robot, RobotStateComponentInterfacePrx robotStatePrx)
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
Definition TimeUtil.cpp:42
Brief description of class ValveAttention.
void onDisconnectPointCloudAndImageProcessor() override
Implement this method in the PointCloudAndImageProcessor in order to execute parts when the component...
void onConnectPointCloudAndImageProcessor() override
Implement this method in your PointCloudAndImageProcessor in order execute parts when the component i...
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void process() override
Process the vision component.
void onInitPointCloudAndImageProcessor() override
Setup the vision component.
static std::string GetDefaultName()
void onExitPointCloudAndImageProcessor() override
Exit the ImapeProcessor component.
void enableResultImages(int numberImages, ImageDimension imageDimension, ImageType imageType, const std::string &name="")
Enables visualization.
void usingImageProvider(std::string name)
Registers a delayed topic subscription and a delayed provider proxy retrieval which all will be avail...
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
int numberImages
Number of images.
int getPointClouds(const PointCloudPtrT &pointCloudPtr)
Poll PointClouds from provider.
void usingPointCloudProvider(std::string providerName)
Registers a delayed topic subscription and a delayed provider proxy retrieval which will be available...
bool waitForPointClouds(int milliseconds=1000)
Wait for new PointClouds.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#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
This file offers overloads of toIce() and fromIce() functions for STL container types.
void handleExceptions()
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
IceInternal::Handle< FramedPosition > FramedPositionPtr
Definition FramedPose.h:149
ImageType typeNameToImageType(const std::string &imageTypeName)
Converts an image type name as string into an ImageType integer.
CByteImage * createByteImage(const ImageFormatInfo &imageFormat, const ImageType imageType)
Creates a ByteImage for the destination type specified in the given imageProviderInfo.
double distance(const Point &a, const Point &b)
Definition point.hpp:95