chessboard_calibration.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. #include <pcl\io\ply_io.h>
  2. #include <pcl\visualization\cloud_viewer.h>
  3. #include <pcl\filters\crop_box.h>
  4. #include <pcl\filters\radius_outlier_removal.h>
  5. #include <pcl\filters\statistical_outlier_removal.h>
  6. #include <pcl\filters\voxel_grid.h>
  7. #include <pcl\common\common.h>
  8. #include <pcl\features\moment_of_inertia_estimation.h>
  9. #include <pcl\segmentation\sac_segmentation.h>
  10. #include <pcl\segmentation\extract_clusters.h>
  11. #include <math.h>
  12. #include <pcl\features\boundary.h>
  13. #include <pcl\features\integral_image_normal.h>
  14. #include <pcl\features\normal_3d.h>
  15. #include "chessboard_calibration.h"
  16. #include "utils.h"
  17. #define PI std::acos(-1)
  18. namespace graft_cv {
  19. CChessboardCalibration::CChessboardCalibration(ConfigParam&cp, CGcvLogger*pLog/*=0*/)
  20. :m_cparam(cp),
  21. m_pLogger(pLog),
  22. m_dtype(0),
  23. m_pcdId(""),
  24. m_ppImgSaver(0),
  25. m_cloud_mean_dist(0.0)
  26. {
  27. }
  28. CChessboardCalibration::~CChessboardCalibration() {}
  29. float* CChessboardCalibration::get_raw_point_cloud(int &data_size)
  30. {
  31. data_size = m_raw_cloud->width * m_raw_cloud->height;
  32. if (data_size == 0) {
  33. return 0;
  34. }
  35. else {
  36. pcl::PointXYZ* pp = m_raw_cloud->points.data();
  37. return (float*)pp->data;
  38. }
  39. }
  40. int CChessboardCalibration::load_data(
  41. float*pPoint,
  42. int pixel_size,
  43. int pt_size,
  44. int dtype,
  45. const char* fn/* = 0*/)
  46. {
  47. //数据加载功能实现,并生成imageid,保存原始数据到文件
  48. int rst = 0;
  49. m_dtype = dtype;
  50. //generate image id
  51. if (m_dtype == 0) {
  52. m_pcdId = getImgId(img_type::sola_sc_pcd);
  53. }
  54. else {
  55. m_pcdId = getImgId(img_type::sola_rs_pcd);
  56. }
  57. //1 get point cloud data
  58. if (pPoint != 0 && pt_size>0) {
  59. //read point
  60. m_raw_cloud.reset(new pcl::PointCloud<pcl::PointXYZ>);
  61. int step = pixel_size;
  62. for (int i = 0; i < pt_size; ++i) {
  63. pcl::PointXYZ pt = { pPoint[i*step], pPoint[i*step + 1] , pPoint[i*step + 2] };
  64. m_raw_cloud->push_back(pt);
  65. }
  66. rst = m_raw_cloud->width * m_raw_cloud->height;
  67. if (m_pLogger) {
  68. stringstream buff;
  69. buff << m_pcdId << ": load raw point cloud " << rst << " data points";
  70. m_pLogger->INFO(buff.str());
  71. }
  72. }
  73. else if (fn != 0) {
  74. //read file
  75. rst = this->read_ply_file(fn);
  76. }
  77. else {//error
  78. if (m_pLogger) {
  79. m_pLogger->ERRORINFO(m_pcdId + ": no valid input");
  80. return (-1);
  81. }
  82. }
  83. if (m_ppImgSaver && *m_ppImgSaver) {
  84. (*m_ppImgSaver)->saveBinPly(m_raw_cloud, m_pcdId);
  85. }
  86. if (m_cparam.image_show) {
  87. pcl::visualization::PCLVisualizer viewer(m_pcdId + std::string(": raw point cloud"));
  88. viewer.addCoordinateSystem();
  89. viewer.addPointCloud<pcl::PointXYZ>(m_raw_cloud, "raw_cloud");
  90. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 1, 1, "raw_cloud");
  91. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "raw_cloud");
  92. float xmin, ymin, zmin, xmax, ymax, zmax;
  93. xmin = m_cparam.rs_grab_xmin;
  94. ymin = m_cparam.rs_grab_ymin;
  95. zmin = m_cparam.rs_grab_zmin;
  96. xmax = m_cparam.rs_grab_xmax;
  97. ymax = m_cparam.rs_grab_ymax;
  98. zmax = m_cparam.rs_grab_zmax;
  99. if (m_dtype == 0) {
  100. xmin = m_cparam.sc_grab_xmin;
  101. ymin = m_cparam.sc_grab_ymin;
  102. zmin = m_cparam.sc_grab_zmin;
  103. xmax = m_cparam.sc_grab_xmax;
  104. ymax = m_cparam.sc_grab_ymax;
  105. zmax = m_cparam.sc_grab_zmax;
  106. }
  107. viewer.addCube(xmin, xmax, ymin, ymax, zmin, zmax, 0.75, 0.0, 0.0, "AABB_");
  108. viewer.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
  109. pcl::visualization::PCL_VISUALIZER_REPRESENTATION_WIREFRAME, "AABB_");
  110. while (!viewer.wasStopped()) {
  111. viewer.spinOnce(100);
  112. boost::this_thread::sleep(boost::posix_time::microseconds(100000));
  113. }
  114. }
  115. return rst;
  116. }
  117. int CChessboardCalibration::cross_detect(
  118. PositionInfo& posinfo
  119. )
  120. {
  121. // 抓取位置识别入口函数,实现整个抓取位置识别功能,返回抓取位置信息
  122. // dtype == 0, scion
  123. // dtype != 0, rootstock
  124. // 输入,穗苗--0, 砧木--1
  125. if (m_raw_cloud->width * m_raw_cloud->height < 1) {
  126. if (m_pLogger) {
  127. stringstream buff;
  128. buff << m_pcdId << ": m_raw_cloud point cloud " << m_raw_cloud->width * m_raw_cloud->height << " data points";
  129. m_pLogger->ERRORINFO(buff.str());
  130. }
  131. return 1;
  132. }
  133. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  134. //1 crop filter,剪裁需要的部分点云
  135. if (m_pLogger) {
  136. m_pLogger->INFO(m_pcdId + ": begin crop");
  137. }
  138. pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_inbox(new pcl::PointCloud<pcl::PointXYZ>);
  139. pcl::CropBox<pcl::PointXYZ> box_filter;
  140. if (m_dtype != 0) {//rootstock
  141. box_filter.setMin(Eigen::Vector4f(m_cparam.rs_grab_xmin, m_cparam.rs_grab_ymin, m_cparam.rs_grab_zmin, 1));
  142. box_filter.setMax(Eigen::Vector4f(m_cparam.rs_grab_xmax, m_cparam.rs_grab_ymax, m_cparam.rs_grab_zmax, 1));
  143. }
  144. else {//scion
  145. box_filter.setMin(Eigen::Vector4f(m_cparam.sc_grab_xmin, m_cparam.sc_grab_ymin, m_cparam.sc_grab_zmin, 1));
  146. box_filter.setMax(Eigen::Vector4f(m_cparam.sc_grab_xmax, m_cparam.sc_grab_ymax, m_cparam.sc_grab_zmax, 1));
  147. }
  148. box_filter.setNegative(false);
  149. box_filter.setInputCloud(m_raw_cloud);
  150. box_filter.filter(*cloud_inbox);
  151. if (m_pLogger) {
  152. stringstream buff;
  153. buff << m_pcdId << ": CropBox croped point cloud " << cloud_inbox->width * cloud_inbox->height << " data points";
  154. m_pLogger->INFO(buff.str());
  155. }
  156. if (cloud_inbox->width * cloud_inbox->height < 1) {
  157. return 1;
  158. }
  159. if (m_cparam.image_show) {
  160. viewer_cloud(cloud_inbox, std::string("cloud_inbox"));
  161. }
  162. if (m_pLogger) {
  163. m_pLogger->INFO(m_pcdId + ": end crop");
  164. }
  165. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  166. //2 filtler with radius remove, 去除孤立点
  167. if (m_pLogger) {
  168. m_pLogger->INFO(m_pcdId + ": begin ror");
  169. }
  170. int nb_points = 20;
  171. double stem_radius = m_cparam.rs_grab_stem_diameter / 2.0;
  172. if (m_dtype == 0) {
  173. stem_radius = m_cparam.sc_grab_stem_diameter / 2.0;
  174. }
  175. double remove_radius = stem_radius * 2.0;
  176. pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_ror(new pcl::PointCloud<pcl::PointXYZ>);
  177. pcl::RadiusOutlierRemoval<pcl::PointXYZ> ror(false);
  178. ror.setInputCloud(cloud_inbox);
  179. ror.setRadiusSearch(remove_radius);
  180. ror.setMinNeighborsInRadius(nb_points);
  181. ror.filter(*cloud_ror);
  182. if (m_pLogger) {
  183. stringstream buff;
  184. buff << m_pcdId << ": RadiusOutlierRemoval filtered point cloud "
  185. << cloud_ror->width * cloud_ror->height
  186. << " data points. param stem_radius=" <<
  187. stem_radius << ", nb_points=" << nb_points << "(if < 50, return)";
  188. m_pLogger->INFO(buff.str());
  189. }
  190. if (cloud_ror->width * cloud_ror->height < 50) {
  191. return 1;
  192. }
  193. if (m_cparam.image_show) {
  194. viewer_cloud(cloud_ror, std::string("cloud_ror"));
  195. }
  196. if (m_pLogger) {
  197. m_pLogger->INFO(m_pcdId + ": end ror");
  198. }
  199. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  200. //3 原来的降采样没有意义,改成统计平均距离
  201. m_cloud_mean_dist = 0.0;
  202. cloud_mean_dist(cloud_ror, m_cloud_mean_dist);
  203. if (m_pLogger) {
  204. stringstream buff;
  205. buff << m_pcdId << ": cloud_mean_dist = " << m_cloud_mean_dist;
  206. m_pLogger->INFO(buff.str());
  207. }
  208. //获取点云范围
  209. pcl::PointXYZ min_v;
  210. pcl::PointXYZ max_v;
  211. pcl::getMinMax3D(*cloud_ror, min_v, max_v);
  212. //4 边界检测
  213. pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree_ptr(new pcl::search::KdTree<pcl::PointXYZ>);
  214. pcl::PointCloud<pcl::Normal>::Ptr normal(new pcl::PointCloud<pcl::Normal>);
  215. pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
  216. ne.setInputCloud(cloud_ror);
  217. ne.setSearchMethod(kdtree_ptr);
  218. ne.setRadiusSearch(3.0);
  219. ne.compute(*normal);
  220. pcl::PointCloud<pcl::Boundary>::Ptr boundaries(new pcl::PointCloud<pcl::Boundary>);
  221. boundaries->resize(cloud_ror->size());
  222. pcl::BoundaryEstimation < pcl::PointXYZ, pcl::Normal, pcl::Boundary>boundary_estimation;
  223. boundary_estimation.setInputCloud(cloud_ror);
  224. boundary_estimation.setInputNormals(normal);
  225. boundary_estimation.setSearchMethod(kdtree_ptr);
  226. boundary_estimation.setKSearch(30);
  227. boundary_estimation.setAngleThreshold(M_PI*0.6);
  228. boundary_estimation.compute(*boundaries);
  229. pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_lines(new pcl::PointCloud<pcl::PointXYZ>);
  230. pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_visual(new pcl::PointCloud<pcl::PointXYZRGB>);
  231. cloud_visual->resize(cloud_ror->size());
  232. for (size_t i = 0; i < cloud_ror->size(); ++i) {
  233. cloud_visual->points.at(i).x = cloud_ror->points.at(i).x;
  234. cloud_visual->points.at(i).y = cloud_ror->points.at(i).y;
  235. cloud_visual->points.at(i).z = cloud_ror->points.at(i).z;
  236. if (boundaries->points.at(i).boundary_point != 0) {
  237. cloud_visual->points.at(i).r = 255;
  238. cloud_visual->points.at(i).g = 0;
  239. cloud_visual->points.at(i).b = 0;
  240. cloud_lines->points.push_back(cloud_ror->points.at(i));
  241. }
  242. else {
  243. cloud_visual->points.at(i).r = 255;
  244. cloud_visual->points.at(i).g = 255;
  245. cloud_visual->points.at(i).b = 255;
  246. }
  247. }
  248. if (m_cparam.image_show) {
  249. viewer_cloud(cloud_visual, string("boundary"));
  250. viewer_cloud(cloud_lines, string("cloud_lines"));
  251. }
  252. //# 直线抽取
  253. float line_radius = stem_radius * 0.75;
  254. std::vector<pcl::PointCloud<pcl::PointXYZ>::Ptr> lines;
  255. std::vector<pcl::ModelCoefficients::Ptr> lines_model;
  256. pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_remain(new pcl::PointCloud<pcl::PointXYZ>);
  257. pcl::copyPointCloud(*cloud_lines, *cloud_remain);
  258. while (cloud_remain->size() > 100) {
  259. pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
  260. pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
  261. pcl::SACSegmentation<pcl::PointXYZ> seg;
  262. seg.setOptimizeCoefficients(true);
  263. seg.setModelType(pcl::SACMODEL_LINE);
  264. seg.setDistanceThreshold(line_radius);
  265. seg.setInputCloud(cloud_remain);
  266. seg.segment(*inliers, *coefficients);
  267. pcl::PointCloud<pcl::PointXYZ>::Ptr stem_cloud(new pcl::PointCloud<pcl::PointXYZ>);
  268. pcl::copyPointCloud(*cloud_remain, *inliers, *stem_cloud);
  269. if (stem_cloud->size() > 100) {
  270. lines.push_back(stem_cloud);
  271. lines_model.push_back(coefficients);
  272. }
  273. //显示得到的直线
  274. //if (m_cparam.image_show) {
  275. // pcl::visualization::PCLVisualizer viewer("line");
  276. // viewer.addCoordinateSystem();
  277. // viewer.addPointCloud<pcl::PointXYZ>(cloud_remain, "cloud_remain");//????????????????
  278. // viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 1, 1, "cloud_remain");
  279. // viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_remain");
  280. // viewer.addPointCloud<pcl::PointXYZ>(stem_cloud, "line_cloud");//????????????????
  281. // viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "line_cloud");
  282. // viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "line_cloud");
  283. // //viewer.addLine(online_points.front(), online_points.back(), 1.0, 0.0, 0.0);
  284. // while (!viewer.wasStopped()) {
  285. // viewer.spinOnce(100);
  286. // boost::this_thread::sleep(boost::posix_time::microseconds(100000));
  287. // }
  288. //}
  289. //更新cloud_remain
  290. pcl::PointCloud<pcl::PointXYZ>::Ptr tmp_cloud(new pcl::PointCloud<pcl::PointXYZ>);
  291. std::vector<int> remain_idx;
  292. for (size_t i = 0; i < cloud_remain->size(); ++i) {
  293. if (std::find(inliers->indices.begin(), inliers->indices.end(), i) == inliers->indices.end()) {
  294. remain_idx.push_back(i);
  295. }
  296. }
  297. pcl::copyPointCloud(*cloud_remain, remain_idx, *tmp_cloud);
  298. cloud_remain = tmp_cloud;
  299. }
  300. //计算 直线间的交点
  301. std::vector<pcl::PointXYZ> cross_points;
  302. int padding = 10;
  303. for (int i = 0; i < lines.size() - 1; ++i) {
  304. for (int j = i + 1; j < lines.size(); ++j) {
  305. //计算两个点云简单最小距离 https://blog.csdn.net/qq_29600745/article/details/111310214
  306. double dist = min_dist_of_lines(lines_model.at(i), lines_model.at(j));
  307. if (dist > 5) { continue; }
  308. //计算公垂线中点坐标: https://blog.csdn.net/qq_42648534/article/details/124100036
  309. pcl::PointXYZ cross_pt;
  310. virtual_cross_of_lines(lines_model.at(i), lines_model.at(j), cross_pt);
  311. if (cross_pt.x > max_v.x-padding || cross_pt.x<min_v.x+padding ||
  312. cross_pt.y>max_v.y - padding || cross_pt.y<min_v.y + padding ||
  313. cross_pt.z>max_v.z - padding || cross_pt.z < min_v.z + padding)
  314. {
  315. continue;
  316. }
  317. cross_points.push_back(cross_pt);
  318. }
  319. }
  320. // 显示在点云中
  321. if (m_cparam.image_show) {
  322. pcl::visualization::PCLVisualizer viewer("cross");
  323. viewer.addCoordinateSystem();
  324. viewer.addPointCloud<pcl::PointXYZ>(cloud_lines, "cloud_lines");//????????????????
  325. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 1, 1, "cloud_lines");
  326. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud_lines");
  327. pcl::PointCloud<pcl::PointXYZ>::Ptr cross_cloud(new pcl::PointCloud<pcl::PointXYZ>);
  328. for (auto& cp : cross_points) {
  329. cross_cloud->points.push_back(cp);
  330. }
  331. viewer.addPointCloud<pcl::PointXYZ>(cross_cloud, "cross_cloud");//????????????????
  332. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 0, 0, "cross_cloud");
  333. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "cross_cloud");
  334. while (!viewer.wasStopped()) {
  335. viewer.spinOnce(100);
  336. boost::this_thread::sleep(boost::posix_time::microseconds(100000));
  337. }
  338. }
  339. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  340. ////7 保存处理结果到图片
  341. cv::Mat rst_img = cv::Mat::zeros(cv::Size(1280, 1280), CV_8UC3);
  342. gen_result_img(cloud_ror, cloud_lines, cross_points, rst_img);
  343. if (m_ppImgSaver && *m_ppImgSaver) {
  344. (*m_ppImgSaver)->saveImage(rst_img, m_pcdId + "_rst_0");
  345. }
  346. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  347. ////8 debug 显示结果
  348. if (m_cparam.image_show) {
  349. imshow_wait("rst_img", rst_img);
  350. }
  351. return 0;
  352. }
  353. double CChessboardCalibration::min_dist_of_lines(
  354. pcl::ModelCoefficients::Ptr line_1,
  355. pcl::ModelCoefficients::Ptr line_2
  356. )
  357. {
  358. pcl::PointXYZ m;
  359. m.x = line_1->values.at(0) - line_2->values.at(0);
  360. m.y = line_1->values.at(1) - line_2->values.at(1);
  361. m.z = line_1->values.at(2) - line_2->values.at(2);
  362. pcl::PointXYZ e;
  363. e.x = line_1->values.at(4) * line_2->values.at(5) - line_1->values.at(5) * line_2->values.at(4);
  364. e.y = line_1->values.at(5) * line_2->values.at(3) - line_1->values.at(3) * line_2->values.at(5);
  365. e.z = line_1->values.at(3) * line_2->values.at(4) - line_1->values.at(4) * line_2->values.at(3);
  366. double dist = std::fabs(m.x * e.x + m.y * e.y + m.z * e.z);
  367. double q = std::sqrt(e.x*e.x + e.y*e.y+ e.z*e.z);
  368. dist /= q;
  369. return dist;
  370. }
  371. void CChessboardCalibration::virtual_cross_of_lines(
  372. pcl::ModelCoefficients::Ptr line_l, //input
  373. pcl::ModelCoefficients::Ptr line_r,//input
  374. pcl::PointXYZ& cross_pt//output
  375. )
  376. {
  377. //计算两条直线的虚交点--公垂线的中点
  378. pcl::PointXYZ pl, pr;//直线上的点
  379. pl.x = line_l->values.at(0);
  380. pl.y = line_l->values.at(1);
  381. pl.z = line_l->values.at(2);
  382. pr.x = line_r->values.at(0);
  383. pr.y = line_r->values.at(1);
  384. pr.z = line_r->values.at(2);
  385. pcl::PointXYZ p_rl, ll,lr;
  386. p_rl.x = line_r->values.at(0) - line_l->values.at(0);
  387. p_rl.y = line_r->values.at(1) - line_l->values.at(1);
  388. p_rl.z = line_r->values.at(2) - line_l->values.at(2);
  389. ll.x = line_l->values.at(3);
  390. ll.y = line_l->values.at(4);
  391. ll.z = line_l->values.at(5);
  392. lr.x = line_r->values.at(3);
  393. lr.y = line_r->values.at(4);
  394. lr.z = line_r->values.at(5);
  395. float pll = p_rl.x * ll.x + p_rl.y * ll.y + p_rl.z * ll.z;
  396. float plr = p_rl.x * lr.x + p_rl.y * lr.y + p_rl.z * lr.z;
  397. float ll_lr = ll.x * lr.x + ll.y * lr.y + ll.z * lr.z;
  398. float t1 = (pll - plr * ll_lr) / (1.0 - ll_lr*ll_lr);
  399. float t2 = (pll * ll_lr - plr ) / (1.0 - ll_lr*ll_lr);
  400. pcl::PointXYZ ml, mr;
  401. ml.x = pl.x + t1 * ll.x;
  402. ml.y = pl.y + t1 * ll.y;
  403. ml.z = pl.z + t1 * ll.z;
  404. mr.x = pr.x + t2 * lr.x;
  405. mr.y = pr.y + t2 * lr.y;
  406. mr.z = pr.z + t2 * lr.z;
  407. cross_pt.x = 0.5 * (ml.x + mr.x);
  408. cross_pt.y = 0.5 * (ml.y + mr.y);
  409. cross_pt.z = 0.5 * (ml.z + mr.z);
  410. }
  411. //生成结果图片
  412. void CChessboardCalibration::gen_result_img(
  413. pcl::PointCloud<pcl::PointXYZ>::Ptr in_cloud_raw,//输入,未经过滤的整体点云in_cloud_raw,
  414. pcl::PointCloud<pcl::PointXYZ>::Ptr in_cloud_edge, //输入,边缘点云,
  415. std::vector<pcl::PointXYZ>& cross_points, //输入,交叉点,
  416. cv::Mat& rst_img //输出,图片, 1280*1280
  417. )
  418. {
  419. if (rst_img.empty()) { return; }
  420. if (rst_img.rows != 1280 && rst_img.cols != 1280) { return; }
  421. int cx = 640; //图像中心点0
  422. int cy = 640; //图像中心点0
  423. float res = 0.2; //分辨率,1个像素0.333mm
  424. //绘制坐标轴
  425. int arrow_len = 20;
  426. cv::line(rst_img, cv::Point(0, cy), cv::Point(cx, cy), cv::Scalar(128, 128, 128));
  427. cv::line(rst_img, cv::Point(0, cy), cv::Point(arrow_len, cy - int(arrow_len / 2)), cv::Scalar(128, 128, 128));
  428. cv::line(rst_img, cv::Point(0, cy), cv::Point(arrow_len, cy + int(arrow_len / 2)), cv::Scalar(128, 128, 128));
  429. cv::line(rst_img, cv::Point(cx, 0), cv::Point(cx, cy), cv::Scalar(128, 128, 128));
  430. cv::line(rst_img, cv::Point(cx, 0), cv::Point(cx - int(arrow_len / 2), arrow_len), cv::Scalar(128, 128, 128));
  431. cv::line(rst_img, cv::Point(cx, 0), cv::Point(cx + int(arrow_len / 2), arrow_len), cv::Scalar(128, 128, 128));
  432. cv::putText(rst_img, std::string("x"), cv::Point(20, cy - 10), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(128, 128, 128));
  433. cv::putText(rst_img, std::string("y"), cv::Point(cx + 10, 20), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(128, 128, 128));
  434. //绘制所有点
  435. int x, y;
  436. for (auto&pt : in_cloud_raw->points) {
  437. x = cx - int(pt.x / res + 0.5);
  438. y = cy - int(pt.y / res + 0.5);
  439. if (x < 0 || x >= rst_img.cols) { continue; }
  440. if (y < 0 || y >= rst_img.rows) { continue; }
  441. rst_img.at<cv::Vec3b>(y, x)[0] = 64;
  442. rst_img.at<cv::Vec3b>(y, x)[1] = 64;
  443. rst_img.at<cv::Vec3b>(y, x)[2] = 64;
  444. }
  445. for (auto&pt : in_cloud_edge->points) {
  446. x = cx - int(pt.x / res + 0.5);
  447. y = cy - int(pt.y / res + 0.5);
  448. if (x < 0 || x >= rst_img.cols) { continue; }
  449. if (y < 0 || y >= rst_img.rows) { continue; }
  450. rst_img.at<cv::Vec3b>(y, x)[0] = 0;
  451. rst_img.at<cv::Vec3b>(y, x)[1] = 180;
  452. rst_img.at<cv::Vec3b>(y, x)[2] = 180;
  453. }
  454. //绘制抓取点坐标
  455. int radius = 10;
  456. int row_sep = 15;
  457. float fsize = 0.5;
  458. for (int i = 0; i < cross_points.size(); ++i) {
  459. pcl::PointXYZ&pt = cross_points.at(i);
  460. int grab_x = cx - int(pt.x / res + 0.5);
  461. int grab_y = cy - int(pt.y / res + 0.5);
  462. cv::line(rst_img, cv::Point(grab_x - radius, grab_y - radius), cv::Point(grab_x + radius, grab_y + radius), cv::Scalar(0, 215, 255));
  463. cv::line(rst_img, cv::Point(grab_x - radius, grab_y + radius), cv::Point(grab_x + radius, grab_y - radius), cv::Scalar(0, 215, 255));
  464. stringstream buff;
  465. buff << i << ":";
  466. cv::putText(rst_img, buff.str(), cv::Point(grab_x + 10, grab_y), cv::FONT_HERSHEY_SIMPLEX, fsize, cv::Scalar(0, 220, 0));
  467. buff.str("");
  468. buff.setf(ios::fixed);
  469. buff.precision(2);
  470. buff << pt.x;
  471. cv::putText(rst_img, buff.str(), cv::Point(grab_x +10, grab_y+ row_sep), cv::FONT_HERSHEY_SIMPLEX, fsize, cv::Scalar(0, 220, 0));
  472. buff.str("");
  473. buff << pt.y;
  474. cv::putText(rst_img, buff.str(), cv::Point(grab_x + 10, grab_y+ 2* row_sep), cv::FONT_HERSHEY_SIMPLEX, fsize, cv::Scalar(0, 220, 0));
  475. buff.str("");
  476. buff << pt.z;
  477. cv::putText(rst_img, buff.str(), cv::Point(grab_x + 10, grab_y + 3 * row_sep), cv::FONT_HERSHEY_SIMPLEX, fsize, cv::Scalar(0, 220, 0));
  478. }
  479. }
  480. int CChessboardCalibration::read_ply_file(const char* fn)
  481. {
  482. m_raw_cloud.reset(new pcl::PointCloud<pcl::PointXYZ>);
  483. if (pcl::io::loadPLYFile<pcl::PointXYZ>(fn, *m_raw_cloud) == -1) {
  484. if (m_pLogger) {
  485. m_pLogger->ERRORINFO(m_pcdId + ": could not load file: " + std::string(fn));
  486. }
  487. return (-1);
  488. }
  489. if (m_pLogger) {
  490. stringstream buff;
  491. buff << m_pcdId << ": load raw point cloud " << m_raw_cloud->width * m_raw_cloud->height << " data points";
  492. m_pLogger->INFO(buff.str());
  493. }
  494. return m_raw_cloud->width * m_raw_cloud->height;
  495. }
  496. double CChessboardCalibration::compute_nearest_neighbor_distance(pcl::PointCloud<pcl::PointXYZ>::Ptr pcd)
  497. {
  498. pcl::KdTreeFLANN<pcl::PointXYZ> tree;
  499. tree.setInputCloud(pcd);
  500. int k = 2;
  501. double res = 0.0;
  502. int n_points = 0;
  503. for (size_t i = 0; i < pcd->size(); ++i) {
  504. std::vector<int> idx(k);
  505. std::vector<float> sqr_distances(k);
  506. if (tree.nearestKSearch(i, k, idx, sqr_distances) == k) {
  507. for (int id = 1; id < k; ++id) {
  508. res += sqrt(sqr_distances.at(id));
  509. ++n_points;
  510. }
  511. }
  512. }
  513. if (n_points > 0) {
  514. res /= (double)n_points;
  515. }
  516. return res;
  517. }
  518. void CChessboardCalibration::cloud_mean_dist(
  519. pcl::PointCloud<pcl::PointXYZ>::Ptr in_cloud, //input 输入点云数据
  520. double& mean_dist
  521. )
  522. {
  523. mean_dist = 0.0;
  524. int n_points = 0;
  525. int nres;
  526. std::vector<int> indices(2);
  527. std::vector<float> sqr_distances(2);
  528. pcl::search::KdTree<pcl::PointXYZ> tree;
  529. tree.setInputCloud(in_cloud);
  530. for (size_t i = 0; i < in_cloud->size(); ++i)
  531. {
  532. //Considering the second neighbor since the first is the point itself.
  533. nres = tree.nearestKSearch(i, 2, indices, sqr_distances);
  534. if (nres == 2)
  535. {
  536. mean_dist += std::sqrt(sqr_distances[1]);
  537. ++n_points;
  538. }
  539. }
  540. if (n_points != 0)
  541. {
  542. mean_dist /= n_points;
  543. }
  544. }
  545. void CChessboardCalibration::viewer_cloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, std::string&winname)
  546. {
  547. pcl::visualization::CloudViewer viewer(winname);
  548. //viewer.runOnVisualizationThreadOnce(viewerOneOff);
  549. viewer.showCloud(cloud);
  550. while (!viewer.wasStopped()) {
  551. boost::this_thread::sleep(boost::posix_time::microseconds(100000));
  552. }
  553. }
  554. void CChessboardCalibration::viewer_cloud(pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud, std::string&winname)
  555. {
  556. pcl::visualization::CloudViewer viewer(winname);
  557. //viewer.runOnVisualizationThreadOnce(viewerOneOff);
  558. viewer.showCloud(cloud);
  559. while (!viewer.wasStopped()) {
  560. boost::this_thread::sleep(boost::posix_time::microseconds(100000));
  561. }
  562. }
  563. void CChessboardCalibration::viewer_cloud_debug(
  564. pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud,
  565. pcl::PointXYZ &p, //抓取点
  566. pcl::PointXYZ &p_ref,//分叉点
  567. pcl::PointXYZ &root_pt,
  568. std::string&winname)
  569. {
  570. pcl::visualization::PCLVisualizer viewer(winname);
  571. //viewer.runOnVisualizationThreadOnce(viewerOneOff);
  572. viewer.addPointCloud(cloud);
  573. viewer.addCoordinateSystem();
  574. pcl::PointXYZ p0, x1, y1, p00, x0, y0, root_px0, root_px1, root_py0, root_py1;
  575. p0.x = p.x;
  576. p0.y = p.y;
  577. p0.z = p.z;
  578. x1.x = p.x + 100.0;
  579. x1.y = p.y;
  580. x1.z = p.z;
  581. y1.x = p.x;
  582. y1.y = p.y + 20.0;
  583. y1.z = p.z;
  584. p00.x = 0.0;
  585. p00.y = 0.0;
  586. p00.z = p.z;
  587. x0.x = 600.0;
  588. x0.y = 0;
  589. x0.z = p.z;
  590. y0.x = 0.0;
  591. y0.y = 300.0;
  592. y0.z = p.z;
  593. root_px0.x = root_pt.x - 5.0;
  594. root_px0.y = root_pt.y;
  595. root_px0.z = root_pt.z;
  596. root_px1.x = root_pt.x + 5.0;
  597. root_px1.y = root_pt.y;
  598. root_px1.z = root_pt.z;
  599. root_py0.x = root_pt.x;
  600. root_py0.y = root_pt.y;
  601. root_py0.z = root_pt.z - 5.0;
  602. root_py1.x = root_pt.x;
  603. root_py1.y = root_pt.y;
  604. root_py1.z = root_pt.z + 5.0;
  605. //茎节点
  606. pcl::PointXYZ fork_px0, fork_px1, fork_py0, fork_py1;
  607. fork_px0.x = p_ref.x - 5.0;
  608. fork_px0.y = p_ref.y;
  609. fork_px0.z = p_ref.z;
  610. fork_px1.x = p_ref.x + 5.0;
  611. fork_px1.y = p_ref.y;
  612. fork_px1.z = p_ref.z;
  613. fork_py0.x = p_ref.x;
  614. fork_py0.y = p_ref.y;
  615. fork_py0.z = p_ref.z - 5.0;
  616. fork_py1.x = p_ref.x;
  617. fork_py1.y = p_ref.y;
  618. fork_py1.z = p_ref.z + 5.0;
  619. viewer.addLine(p0, x1, 255, 0, 0, "x");
  620. viewer.addLine(p0, y1, 0, 255, 0, "y");
  621. viewer.addLine(p00, x0, 255, 0, 0, "x0");
  622. viewer.addLine(p00, y0, 0, 255, 0, "y0");
  623. viewer.addLine(root_px0, root_px1, 255, 0, 0, "rootx");
  624. viewer.addLine(root_py0, root_py1, 0, 255, 0, "rooty");
  625. viewer.addLine(fork_px0, fork_px1, 255, 0, 0, "forkx");
  626. viewer.addLine(fork_py0, fork_py1, 0, 255, 0, "forky");
  627. while (!viewer.wasStopped()) {
  628. viewer.spinOnce(100);
  629. boost::this_thread::sleep(boost::posix_time::microseconds(100000));
  630. }
  631. }
  632. void CChessboardCalibration::viewer_cloud_cluster(
  633. pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud,
  634. std::vector<pcl::PointXYZ>cluster_center,
  635. std::string&winname)
  636. {
  637. pcl::visualization::PCLVisualizer viewer(winname);
  638. viewer.addPointCloud(cloud);
  639. viewer.addCoordinateSystem();
  640. int cnt = 0;
  641. char buf[8];
  642. for (auto& p : cluster_center) {
  643. pcl::PointXYZ px0, px1, py1, py0;
  644. px0.x = p.x - 5.0;
  645. px0.y = p.y;
  646. px0.z = p.z;
  647. px1.x = p.x + 5.0;
  648. px1.y = p.y;
  649. px1.z = p.z;
  650. py0.x = p.x;
  651. py0.y = p.y - 5.0;
  652. py0.z = p.z;
  653. py1.x = p.x;
  654. py1.y = p.y + 5.0;
  655. py1.z = p.z;
  656. viewer.addLine(px0, px1, 255, 0, 0, "x" + string(_itoa(cnt, buf, 10)));
  657. viewer.addLine(py0, py1, 0, 255, 0, "y" + string(_itoa(cnt, buf, 10)));
  658. cnt += 1;
  659. }
  660. while (!viewer.wasStopped()) {
  661. viewer.spinOnce(100);
  662. boost::this_thread::sleep(boost::posix_time::microseconds(100000));
  663. }
  664. }
  665. void CChessboardCalibration::viewer_cloud_cluster_box(
  666. pcl::PointCloud<pcl::PointXYZ>::Ptr cloud,
  667. std::vector<pcl::PointXYZ>&cluster_center,
  668. std::vector<pcl::PointXYZ>&cluster_box,
  669. std::vector<std::vector<int> >& clt_line_idx,
  670. std::string&winname)
  671. {
  672. pcl::visualization::PCLVisualizer viewer(winname);
  673. viewer.addCoordinateSystem();
  674. viewer.addPointCloud<pcl::PointXYZ>(cloud, "all_cloud");
  675. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 1, 1, 1, "all_cloud");
  676. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "all_cloud");
  677. int cnt = 0;
  678. char buf[8];
  679. for (size_t i = 0; i < cluster_center.size(); ++i) {
  680. pcl::PointXYZ &p = cluster_center.at(i);
  681. pcl::PointXYZ px0, px1, py1, py0;
  682. px0.x = p.x - 5.0;
  683. px0.y = p.y;
  684. px0.z = p.z;
  685. px1.x = p.x + 5.0;
  686. px1.y = p.y;
  687. px1.z = p.z;
  688. py0.x = p.x;
  689. py0.y = p.y - 5.0;
  690. py0.z = p.z;
  691. py1.x = p.x;
  692. py1.y = p.y + 5.0;
  693. py1.z = p.z;
  694. viewer.addLine(px0, px1, 255, 0, 0, "x" + string(_itoa(cnt, buf, 10)));
  695. viewer.addLine(py0, py1, 0, 255, 0, "y" + string(_itoa(cnt, buf, 10)));
  696. //box
  697. pcl::PointXYZ & min_pt = cluster_box.at(2 * i);
  698. pcl::PointXYZ & max_pt = cluster_box.at(2 * i + 1);
  699. viewer.addCube(min_pt.x, max_pt.x, min_pt.y, max_pt.y, min_pt.z, max_pt.z, 0.5, 0.5, 0.0, "AABB_" + string(_itoa(cnt, buf, 10)));
  700. viewer.setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION,
  701. pcl::visualization::PCL_VISUALIZER_REPRESENTATION_WIREFRAME, "AABB_" + string(_itoa(cnt, buf, 10)));
  702. if (clt_line_idx.size()>i && clt_line_idx.at(i).size() > 0) {
  703. pcl::PointCloud<pcl::PointXYZ>::Ptr line_cloud(new pcl::PointCloud<pcl::PointXYZ>);
  704. pcl::copyPointCloud(*cloud, clt_line_idx.at(i), *line_cloud);
  705. viewer.addPointCloud(line_cloud, "fit_line" + string(_itoa(cnt, buf, 10)));
  706. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_COLOR, 0, 1, 0, "fit_line" + string(_itoa(cnt, buf, 10)));
  707. viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "fit_line" + string(_itoa(cnt, buf, 10)));
  708. }
  709. cnt += 1;
  710. }
  711. while (!viewer.wasStopped()) {
  712. viewer.spinOnce(100);
  713. boost::this_thread::sleep(boost::posix_time::microseconds(100000));
  714. }
  715. }
  716. };