00001
00047 #include "comthread.h"
00048 #include <ros/ros.h>
00049 #include <QDebug>
00050 #include <QDir>
00051 #include <QPoint>
00052 #include <QImage>
00053 #include <pcl/io/pcd_io.h>
00054 #include <pcl/visualization/cloud_viewer.h>
00055 #include <pcl/filters/passthrough.h>
00056
00057 #include "re_visionmodelcreator.h"
00058
00059 ComThread::ComThread(QObject *parent) :
00060 QThread(parent)
00061 {
00062 ros::NodeHandle nh;
00063 sub = nh.subscribe<sensor_msgs::PointCloud2>("resulting_pcl",1,boost::bind(&ComThread::pointCloudReceived,this,_1));
00064 isRecording = false;
00065 npoints = 0;
00066 max_pointclouds = 100;
00067
00068 cloudViewer = new pcl::visualization::CloudViewer("object point cloud");
00069 }
00070
00071 ComThread::~ComThread() {
00072 delete cloudViewer;
00073 }
00074
00075 void ComThread::run() {
00076 ros::Rate r(2);
00077 while (ros::ok() && !cloudViewer->wasStopped()) {
00078 ros::spinOnce();
00079 r.sleep();
00080 }
00081 }
00082
00083 void ComThread::recording(bool startstop) {
00084 isRecording = startstop;
00085 }
00086
00087 void ComThread::reset() {
00088 mutex.lock();
00089 npoints = 0;
00090 pcl::PointCloud<PointType> emptycloud;
00091
00092 for (std::map<int,boost::shared_ptr<pcl::PointCloud<PointType> > >::const_iterator it = pcls.begin(); it != pcls.end(); ++it) {
00093 cloudViewer->showCloud(emptycloud.makeShared(),boost::lexical_cast<std::string>(it->first));
00094 }
00095
00096 pcls.clear();
00097
00098 cloudViewer->showCloud(emptycloud.makeShared());
00099
00100 mutex.unlock();
00101 Q_EMIT newCloudReceived("-");
00102 }
00103
00104 void ComThread::pointCloudReceived(const sensor_msgs::PointCloud2ConstPtr &pcl_msg) {
00105 ROS_INFO("Received PointCloud");
00106
00107 boost::shared_ptr<pcl::PointCloud<PointType> > cloud(new pcl::PointCloud<PointType>);
00108 pcl::fromROSMsg(*pcl_msg,*cloud);
00109
00110
00111 geometry_msgs::PoseStamped origin, markerToRGBCamera;
00112 origin.header.frame_id = pcl_msg->header.frame_id;
00113 origin.header.stamp = pcl_msg->header.stamp;
00114 origin.pose.orientation.w = 1.f;
00115 try {
00116 tf.transformPose("/camera_rgb_optical_frame", origin, markerToRGBCamera);
00117
00118 cloud->sensor_origin_ = Eigen::Vector4f(markerToRGBCamera.pose.position.x,
00119 markerToRGBCamera.pose.position.y,
00120 markerToRGBCamera.pose.position.z,
00121 0);
00122 cloud->sensor_orientation_ = Eigen::Quaternionf(markerToRGBCamera.pose.orientation.w,
00123 markerToRGBCamera.pose.orientation.x,
00124 markerToRGBCamera.pose.orientation.y,
00125 markerToRGBCamera.pose.orientation.z);
00126 } catch(const std::exception& e) {
00127 ROS_WARN("could not get camera pose: %s", e.what());
00128 return;
00129 }
00130
00131 if (!isRecording) {
00132
00133 BOOST_FOREACH(PointType& pt, cloud->points) {
00134 pt.rgb = 25590;
00135 }
00136 cloudViewer->showCloud(cloud,"current");
00137 } else {
00138 addPointCloud(cloud);
00139 }
00140 }
00141
00150 int read_binary_PCL(const std::string& filename, pcl::PointCloud<PointType>::Ptr cloud) {
00151 std::ifstream in_file(filename.c_str());
00152 std::string token("DATA binary\n");
00153
00154 const size_t BUFFER_SIZE = 4096;
00155 char buffer[BUFFER_SIZE];
00156 in_file.read(buffer, BUFFER_SIZE);
00157 if (in_file.gcount() < BUFFER_SIZE) {
00158 std::cerr << "could not read whole file" << std::endl;
00159 return -1;
00160 }
00161
00162 bool found_binary = false;
00163 bool null_after_header = false;
00164 size_t matched_chars = 0;
00165 size_t header_length = 0;
00166 for(size_t i=0; i<BUFFER_SIZE; i++) {
00167 if (buffer[i] == token[matched_chars]) {
00168 matched_chars++;
00169 } else
00170 matched_chars = 0;
00171
00172 if (matched_chars == token.length()) {
00173 found_binary = true;
00174
00175 header_length = i;
00176 size_t num_nulls = 0;
00177 for(size_t j=header_length+1; j<BUFFER_SIZE; j++) {
00178 if (buffer[j] == '\0')
00179 num_nulls++;
00180 else
00181 break;
00182 }
00183 null_after_header = num_nulls > 10;
00184 if (null_after_header) {
00185 std::cout << "null bytes found after header, converting from old PCD binary format" << std::endl;
00186 }
00187 break;
00188 }
00189 }
00190
00191 std::string tmp_filename;
00192 int temp_file_fd;
00193 if (null_after_header) {
00194 char tmp_name[] = "/tmp/pcd_file_XXXXXX";
00195
00196 temp_file_fd = mkstemp(tmp_name);
00197 if (temp_file_fd < 0) {
00198 std::cerr << "could not create temporary file" << std::endl;
00199 return -1;
00200 }
00201 tmp_filename = tmp_name;
00202
00203 std::ofstream tmp_file(tmp_filename.c_str());
00204 tmp_file.write(buffer, header_length+1);
00205
00206 in_file.seekg(4096, std::ios::beg);
00207 while(in_file.good()) {
00208 in_file.read(buffer, BUFFER_SIZE);
00209 tmp_file.write(buffer, in_file.gcount());
00210 }
00211 in_file.close();
00212 tmp_file.close();
00213 pcl::io::loadPCDFile(tmp_filename, *cloud);
00214 remove(tmp_filename.c_str());
00215 } else {
00216 in_file.close();
00217
00218 pcl::io::loadPCDFile(filename, *cloud);
00219 }
00220
00221 return 0;
00222 }
00223
00224 void ComThread::loadPointCloud(const QString &filename) {
00225 boost::shared_ptr<pcl::PointCloud<PointType> > cloud(new pcl::PointCloud<PointType>);
00226
00227 read_binary_PCL(filename.toStdString(), cloud);
00228
00229 cloud->header.frame_id = "/PATTERN_BASE";
00230
00231 int pcl_id = addPointCloud(cloud);
00232 Q_EMIT addStatusMessage("Loaded '" + filename + "' as scan number " + QString::number(pcl_id) + ".");
00233 }
00234
00235 int ComThread::addPointCloud(boost::shared_ptr<pcl::PointCloud<PointType> > cloud) {
00236 QMutexLocker lock(&mutex);
00237
00238 if (pcls.size() >= max_pointclouds) {
00239 Q_EMIT addStatusMessage(QString("Received a new point cloud, but we have already %1 pointclouds! Ignoring this one.").arg(pcls.size()));
00240 return -1;
00241 }
00242
00243 npoints += cloud->width * cloud->height;
00244
00245 int count = 0;
00246 while (pcls.find(count) != pcls.end()) {
00247 count ++;
00248 }
00249 pcls.insert(std::make_pair(count, cloud));
00250
00251 Q_EMIT newCloudReceived(QString::number(count));
00252 cloudViewer->showCloud(cloud,boost::lexical_cast<std::string>(count));
00253 Q_EMIT addStatusMessage("Accumulated " + QString::number(npoints) + " points in " + QString::number(pcls.size()) + " point clouds");
00254 return count;
00255 }
00256
00257 void ComThread::removeCloud(QStringList ids) {
00258 QMutexLocker lock(&mutex);
00259
00260 Q_FOREACH(QString id, ids) {
00261 Q_EMIT addStatusMessage("Removing cloud " + id);
00262 if(pcls.find(id.toInt())!=pcls.end()) {
00263 pcls[id.toInt()]->points.clear();
00264 cloudViewer->showCloud(pcls[id.toInt()],id.toStdString());
00265 pcls.erase(pcls.find(id.toInt()));
00266 }
00267 }
00268 }
00269
00270 void ComThread::selectCloud(QStringList newSelections) {
00271 QMutexLocker lock(&mutex);
00272
00273 static QStringList alreadySelected;
00274
00275 QStringList notselectedanymore;
00276 QStringList alreadyselectedandstillis;
00277
00278 Q_FOREACH(QString id, alreadySelected) {
00279 if (newSelections.contains(id)) {
00280 alreadyselectedandstillis.append(id);
00281 newSelections.removeAll(id);
00282 } else {
00283 notselectedanymore.append(id);
00284 }
00285 }
00286
00287 Q_FOREACH(QString id, notselectedanymore) {
00288
00289 if(pcls.find(id.toInt())!=pcls.end()){
00290 cloudViewer->showCloud(pcls[id.toInt()],id.toStdString());
00291 } else {
00292 pcl::PointCloud<PointType> cloud;
00293 cloudViewer->showCloud(cloud.makeShared(),id.toStdString());
00294 }
00295 }
00296
00297 Q_FOREACH(QString id, newSelections) {
00298 pcl::PointCloud<PointType> copy;
00299 if(pcls.find(id.toInt())!=pcls.end()){
00300 copy = *pcls[id.toInt()];
00301 BOOST_FOREACH(PointType& pt, copy.points) {
00302 pt.rgb = 255;
00303 }
00304 }
00305 cloudViewer->showCloud(copy.makeShared(),id.toStdString());
00306 }
00307
00308 alreadySelected = newSelections;
00309 alreadySelected.append(alreadyselectedandstillis);
00310 }
00311
00312 void ComThread::getMergedPointCloud(pcl::PointCloud<PointType> &merged_pcl) {
00313 merged_pcl.points.clear();
00314 for (std::map<int,boost::shared_ptr<pcl::PointCloud<PointType> > >::const_iterator it = pcls.begin(); it != pcls.end(); ++it) {
00315 pcl::PointCloud<PointType> cleaned_cloud;
00316
00317 pcl::PassThrough<PointType> nan_remover;
00318 nan_remover.setInputCloud(it->second);
00319 nan_remover.setFilterFieldName("z");
00320 nan_remover.setFilterLimits(0.0, 10.0);
00321 nan_remover.filter(cleaned_cloud);
00322
00323 if (!merged_pcl.points.size())
00324 merged_pcl = cleaned_cloud;
00325 else
00326 merged_pcl += cleaned_cloud;
00327 }
00328 }
00329
00330 void ComThread::saveMergedPCL(QString filename) {
00331 pcl::PointCloud<PointType> mergedpcls;
00332 getMergedPointCloud(mergedpcls);
00333
00334 pcl::io::savePCDFileBinary(filename.toStdString(), mergedpcls);
00335 }
00336
00337 void ComThread::saveFaces(QDir model_dir) {
00338 int i=0;
00339 for (std::map<int,boost::shared_ptr<pcl::PointCloud<PointType> > >::const_iterator it = pcls.begin(); it != pcls.end(); ++it, ++i) {
00340 QString recording = QString("dense_face_%1.pcd").arg(i, 3, 10, QChar('0'));
00341 QString recording_file = model_dir.absoluteFilePath(recording);
00342
00343 pcl::io::savePCDFileBinary(recording_file.toStdString(), *it->second);
00344 }
00345 }
00346
00347 void ComThread::changedBoxSize(double size) {
00348 pcl::PointXYZ minp(-size,-size,.01);
00349 pcl::PointXYZ maxp(size,size,2*size+.01);
00350
00351 pcl::PointCloud<pcl::PointXYZ> boundingboxcloud;
00352 boundingboxcloud.push_back(pcl::PointXYZ(minp.x,minp.y,minp.z));
00353 boundingboxcloud.push_back(pcl::PointXYZ(minp.x,maxp.y,minp.z));
00354 boundingboxcloud.push_back(pcl::PointXYZ(minp.x,minp.y,maxp.z));
00355 boundingboxcloud.push_back(pcl::PointXYZ(minp.x,maxp.y,maxp.z));
00356
00357 boundingboxcloud.push_back(pcl::PointXYZ(maxp.x,minp.y,minp.z));
00358 boundingboxcloud.push_back(pcl::PointXYZ(maxp.x,maxp.y,minp.z));
00359 boundingboxcloud.push_back(pcl::PointXYZ(maxp.x,minp.y,maxp.z));
00360 boundingboxcloud.push_back(pcl::PointXYZ(maxp.x,maxp.y,maxp.z));
00361 cloudViewer->showCloud(boundingboxcloud.makeShared(), "boundingbox");
00362 }
00363
00364 void ComThread::createRe_visionModel(QDir &parent_dir, const QString& model_name) {
00365 QMutexLocker lock(&mutex);
00366
00367 std::vector<boost::shared_ptr<pcl::PointCloud<PointType> > > vec;
00368 for (std::map<int,boost::shared_ptr<pcl::PointCloud<PointType> > >::const_iterator it = pcls.begin(); it != pcls.end(); ++it) {
00369 vec.push_back(it->second);
00370 }
00371
00372 pcl::PointCloud<PointType> merged_pcl;
00373 getMergedPointCloud(merged_pcl);
00374
00375 re_visionModelCreator zmg(vec);
00376 zmg.createModel(parent_dir, merged_pcl, model_name);
00377 }
00378
00379 void ComThread::createKinectModel(QDir &parent_dir, const QString &model_name) {
00380 QMutexLocker lock(&mutex);
00381
00382 QString merged_file = parent_dir.absoluteFilePath("merged.pcd");
00383 qDebug() << "saving merged.pcd";
00384 saveMergedPCL(merged_file);
00385 qDebug() << "saving faces";
00386 saveFaces(parent_dir);
00387
00388 qDebug() << "creating meta file";
00389 re_visionModelCreator::createMetaFile(parent_dir, model_name, "kinect_pcl", pcls.size(), 1);
00390 }
00391
00392 void ComThread::maxScanNumberChanged(int value) {
00393 max_pointclouds = value;
00394 }
00395
00396 void ComThread::markerSizeChanged(double value)
00397 {
00398 ros::param::set("/marker_size", value);
00399 return;
00400 }
00401
00402 void ComThread::markerSpacingChanged(double value)
00403 {
00404 ros::param::set("/marker_spacing", value);
00405 return;
00406 }
00407
00408 bool ComThread::createPreview(QDir& model_dir) {
00409 QMutexLocker lock(&mutex);
00410
00411 size_t max_area = 0;
00412 boost::shared_ptr<pcl::PointCloud<PointType> > best_face;
00413 QPoint best_minp, best_maxp;
00414
00415
00416 for (std::map<int,boost::shared_ptr<pcl::PointCloud<PointType> > >::const_iterator it = pcls.begin(); it != pcls.end(); ++it) {
00417 const pcl::PointCloud<PointType>& pcl = *it->second;
00418 size_t i=0;
00419 QPoint minp, maxp;
00420 minp.setX(pcl.width);
00421 minp.setY(pcl.height);
00422 for (size_t u = 0; u < pcl.height; ++u)
00423 {
00424 for (size_t v = 0; v < pcl.width; ++v, ++i)
00425 {
00426
00427 if (!(isnan (pcl.points[i].x) || isnan (pcl.points[i].y) || isnan (pcl.points[i].z))) {
00428 if (v < minp.x())
00429 minp.setX(v);
00430 if (v > maxp.x())
00431 maxp.setX(v);
00432 if (u < minp.y())
00433 minp.setY(u);
00434 if (u > maxp.y())
00435 maxp.setY(u);
00436 }
00437 }
00438 }
00439
00440 size_t area = (maxp.x() - minp.x() +1) * (maxp.y() - minp.y()+1);
00441 if (area > max_area) {
00442 max_area = area;
00443 best_face = it->second;
00444 best_minp = minp;
00445 best_maxp = maxp;
00446 }
00447 }
00448
00449 if (max_area < 100)
00450 return false;
00451
00452 const pcl::PointCloud<PointType>& pcl = *best_face;
00453 QImage img_color(best_maxp.x() - best_minp.x() +1, best_maxp.y() - best_minp.y()+1, QImage::Format_RGB32);
00454 size_t i = 0;
00455
00456 for (size_t u = 0; u < pcl.height; ++u)
00457 {
00458 for (size_t v = 0; v < pcl.width; ++v, ++i)
00459 {
00460 if ((u >= best_minp.y()) && (u <= best_maxp.y()) &&
00461 (v >= best_minp.x()) && (v <= best_maxp.x())) {
00462 int32_t rgb = *(int32_t*)(&pcl.points[i].rgb);
00463 char b = rgb & 0xFF;
00464 char g = (rgb & (0xFF << 8)) >> 8;
00465 char r = (rgb & (0xFF << 16)) >> 16;
00466
00467 img_color.setPixel(v - best_minp.x(), u - best_minp.y(), qRgb(r, g, b));
00468 }
00469 }
00470 }
00471
00472 return img_color.save(model_dir.filePath("preview.png"));
00473 }