00001
00047 #include "re_visionmodelcreator.h"
00048
00049 #include <cv.h>
00050 #include <highgui.h>
00051 #include <boost/foreach.hpp>
00052 #include <QString>
00053 #include <QFile>
00054 #include <QXmlStreamWriter>
00055
00056 #include <Eigen/Geometry>
00057 #include <pcl/features/normal_3d.h>
00058 #include <pcl/kdtree/kdtree_flann.h>
00059 #include <pcl/common/transforms.h>
00060
00061 #include <re_vision/SurfSet.h>
00062 #include <re_vision/BundleCamera.h>
00063 #include <re_vision/PLYFile.h>
00064
00065 using namespace std;
00066
00067 void re_visionModelCreator::createMetaFile(QDir model_dir, QString modelname, QString modeltype, size_t face_count, double scale) {
00068 QFile metaFile(model_dir.filePath("meta.xml"));
00069 metaFile.open(QIODevice::WriteOnly);
00070 QXmlStreamWriter xml(&metaFile);
00071 xml.setAutoFormatting(true);
00072 xml.writeStartDocument();
00073
00074 xml.writeStartElement("model");
00075 xml.writeTextElement("name", modelname);
00076 xml.writeTextElement("type", modeltype);
00077 xml.writeTextElement("faces", QString::number(face_count));
00078 xml.writeStartElement("dimensions");
00079 xml.writeTextElement("scale", QString::number(scale));
00080 xml.writeEndElement();
00081 xml.writeEndElement();
00082 xml.writeEndDocument();
00083 }
00084
00086 Eigen::Affine3f getRe_visionTransformation() {
00087 Eigen::Quaternionf quat(Eigen::AngleAxisf(-0.5*M_PI, Eigen::Vector3f::UnitY()) *
00088 Eigen::AngleAxisf(0.5*M_PI, Eigen::Vector3f::UnitZ()));
00089
00090 Eigen::Affine3f transformation(quat);
00091 return transformation;
00092 }
00093
00094 re_visionModelCreator::re_visionModelCreator(const std::vector<boost::shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > >& pointclouds) : pcls(pointclouds)
00095 {
00096 }
00097
00098 void re_visionModelCreator::createModel(const QDir &parent_dir, const pcl::PointCloud<pcl::PointXYZRGB> &merged_pcl, const QString& modelname) {
00099 QString drawing_model_filename = parent_dir.absoluteFilePath("drawing_model.ply");
00100 saveAsPLY(merged_pcl.makeShared(), drawing_model_filename);
00101
00102 int face_counter=0;
00103 BOOST_FOREACH(const boost::shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> >& pcl_ptr, pcls) {
00104 const pcl::PointCloud<pcl::PointXYZRGB>& pcl = *pcl_ptr;
00105
00106 const QString base_filename = parent_dir.absoluteFilePath(QString("face_%1").arg(face_counter++, 3, 10, QChar('0')));
00107
00108
00109 cv::Mat3b img_color(cv::Size(pcl.width, pcl.height));
00110 cv::Mat1b mask(img_color.size());
00111 cv::Mat3b masked_img_color(img_color.size());
00112 size_t i=0;
00113 for (size_t u = 0; u < pcl.height; ++u)
00114 {
00115 for (size_t v = 0; v < pcl.width; ++v, ++i)
00116 {
00117 int32_t rgb = *(int32_t*)(&pcl.points[i].rgb);
00118 char b = rgb & 0xFF;
00119 char g = (rgb & (0xFF << 8)) >> 8;
00120 char r = (rgb & (0xFF << 16)) >> 16;
00121 img_color(u, v) = cv::Vec3b(b, g, r);
00122
00123 if (isnan (pcl.points[i].x) || isnan (pcl.points[i].y) || isnan (pcl.points[i].z))
00124 mask(u, v) = 0;
00125 else
00126 mask(u, v) = 1;
00127 }
00128 }
00129 std::cerr << "writing " << base_filename.toStdString() << ".png";
00130 cv::imwrite((base_filename+".png").toStdString(), img_color);
00131
00132 cv::floodFill(mask, cv::Point2i(0, 0), 2);
00133 for (int u = 0; u < mask.size().height; ++u)
00134 {
00135 for (int v = 0; v < mask.size().width; ++v)
00136 {
00137 cv::Vec3b color;
00138 if (mask(u, v) == 2) {
00139 color = cv::Vec3b(0, 0, 0);
00140 mask(u, v) = 0;
00141 }
00142 else {
00143 color = img_color.at<cv::Vec3b>(u, v);
00144 mask(u, v) = 1;
00145 }
00146
00147 masked_img_color.at<cv::Vec3b>(u, v) = color;
00148 }
00149 }
00150 cv::Mat1b masked_img_gray(masked_img_color.size());
00151 cv::cvtColor(masked_img_color, masked_img_gray, CV_BGR2GRAY);
00152
00153 cv::GaussianBlur(masked_img_gray, masked_img_gray, cv::Size(3,3), 0);
00154
00155 DVision::SurfSet surf;
00156 const float surf_th = 400.;
00157 surf.Extract(masked_img_gray, surf_th, false);
00158 cout << "got " << surf.keys.size() << " descriptors" << endl;
00159 const int margin = 3;
00160 cv::Mat erosion = cv::Mat::ones(margin*2+1, margin*2+1, CV_8U);
00161 cv::erode(mask, mask, erosion);
00162
00163 cv::Mat1b masked_eroded(masked_img_gray.size());
00164 for (int u = 0; u < mask.size().height; ++u)
00165 {
00166 for (int v = 0; v < mask.size().width; ++v)
00167 {
00168 uchar color;
00169 if (mask(u, v) == 0)
00170 color = 0;
00171 else
00172 color = masked_img_gray.at<uchar>(u, v);
00173
00174 masked_eroded.at<uchar>(u, v) = color;
00175 }
00176 }
00177
00178 cv::imwrite((base_filename + "_masked.png").toStdString(), masked_eroded);
00179
00180 boost::shared_ptr<pcl::PointCloud<pcl::PointXYZRGB> > masked_ply
00181 (new pcl::PointCloud<pcl::PointXYZRGB>());
00182
00183 DVision::SurfSet surf_cleaned;
00184 for (size_t i=0; i<surf.keys.size(); i++) {
00185 const cv::KeyPoint& kp = surf.keys.at(i);
00186 const pcl::PointXYZRGB& pt3d = pcl.at(kp.pt.x, kp.pt.y);
00187 if ((mask(kp.pt) != 0) && (!isnan(pt3d.x)))
00188 {
00189 surf_cleaned.keys.push_back(kp);
00190 for (int j=0; j<surf.GetDescriptorLength(); j++) {
00191 surf_cleaned.descriptors.push_back(surf.descriptors.at(i*surf.GetDescriptorLength()+j));
00192
00193 }
00194 surf_cleaned.laplacians.push_back(surf.laplacians.at(i));
00195 masked_ply->push_back(pt3d);
00196 }
00197 }
00198 cout << "surf descriptors after removing borders: " << surf_cleaned.size() << endl;
00199
00200 surf_cleaned.Save((base_filename + ".key.gz").toStdString());
00201
00202
00203
00204
00205
00206
00207
00208 DVision::Bundle::CameraFile::Camera cam;
00209
00210 cam.f = 525.f;
00211 cam.k1 = 0.f;
00212 cam.k2 = 0.f;
00213 Eigen::Vector4f trans(pcl.sensor_origin_);
00214 Eigen::Quaternionf quat(pcl.sensor_orientation_);
00215
00216 cv::Mat1d camera_translation(3,1);
00217 for (int i=0; i<3; i++) {
00218 camera_translation(i) = trans(i);
00219 }
00220 cam.t = camera_translation;
00221 cout << camera_translation << endl;
00222
00223 cv::Mat1d camera_rotation(3,3);
00224 Eigen::Matrix3f rot_mat(quat);
00225 for(int i=0; i<3; i++)
00226 for(int j=0; j<3; j++)
00227 camera_rotation(i, j) = rot_mat(i,j);
00228 cam.R = camera_rotation;
00229 cout << camera_rotation << endl;
00230
00231 cam.save((base_filename + ".txt").toStdString(), "Generated by re_object_recorder");
00232
00233 saveAsPLY(masked_ply, base_filename + ".ply");
00234 }
00235
00236 createMetaFile(parent_dir, modelname, "3D", pcls.size(), 1);
00237 }
00238
00239 void re_visionModelCreator::saveAsPLY(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr pcl, const QString &filename) {
00240 using DVision::PMVS::PLYFile;
00241
00242 #ifdef ESTIMATE_NORMALS
00243 pcl::KdTreeFLANN<pcl::PointXYZRGB>::Ptr flann(new pcl::KdTreeFLANN<pcl::PointXYZRGB>);
00244 cerr << "estimating normals" << endl;
00245
00246 pcl::PointCloud<pcl::Normal>::Ptr normals_cloud(new pcl::PointCloud<pcl::Normal>);
00247 pcl::NormalEstimation<pcl::PointXYZRGB, pcl::Normal> norm_est;
00248 norm_est.setSearchMethod(flann);
00249 norm_est.setInputCloud(pcl);
00250 norm_est.setRadiusSearch(0.01);
00251 norm_est.compute(*normals_cloud);
00252 #endif
00253
00254 pcl::PointCloud<pcl::PointXYZRGB> transformed_pcl;
00255 pcl::transformPointCloud(*pcl, transformed_pcl, getRe_visionTransformation());
00256
00257 std::vector<PLYFile::PLYPoint> plyPoints;
00258 for(size_t i=0; i<transformed_pcl.size(); i++) {
00259 const pcl::PointXYZRGB& pclPt = transformed_pcl.points.at(i);
00260
00261 if (isnan(pclPt.x))
00262 continue;
00263
00264 PLYFile::PLYPoint plyPt;
00265 plyPt.x = pclPt.x;
00266 plyPt.y = pclPt.y;
00267 plyPt.z = pclPt.z;
00268 #ifdef ESTIMATE_NORMALS
00269 const pcl::Normal& pclNormal = normals_cloud->points.at(i);
00270 plyPt.nx = pclNormal.normal_x;
00271 plyPt.ny = pclNormal.normal_y;
00272 plyPt.nz = pclNormal.normal_z;
00273 #else
00274 plyPt.nx = 0;
00275 plyPt.ny = 0;
00276 plyPt.nz = 0;
00277 #endif
00278 int32_t rgb = *(int32_t*)(&pclPt.rgb);
00279 plyPt.b = rgb & 0xFF;
00280 plyPt.g = (rgb & (0xFF << 8)) >> 8;
00281 plyPt.r = (rgb & (0xFF << 16)) >> 16;
00282 plyPoints.push_back(plyPt);
00283 }
00284 PLYFile::saveFile(filename.toStdString(), plyPoints);
00285 }