int main(int argc, char** argv)
{
	// initialize variables
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_original (new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_unaltered (new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_objects (new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_planeInliers (new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_planeOutliers (new pcl::PointCloud<pcl::PointXYZ>);             
	pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients());                  
	pcl::PointIndices::Ptr indices (new pcl::PointIndices());
	pcl::PCDReader reader;
	pcl::PCDWriter writer;
	int result = 0;  // catch function return

	// enable variables for time logging
	boost::posix_time::ptime time_before_execution;
	boost::posix_time::ptime time_after_execution;
	boost::posix_time::time_duration difference;
	
	// read point cloud through command line
	if (argc != 2)
	{
		std::cout << "usage: " << argv[0] << " <filename>\n";
		return 0;
	}
	else
	{	// read cloud and display its size
		std::cout << "Reading Point Cloud" << std::endl;
		reader.read(argv[1], *cloud_original);
		#ifdef DEBUG
		std::cout << "size of original cloud: " 
		          << cloud_original->points.size() << " points" << std::endl;
		#endif
	}
	
	//**************************************************************************
	// test passthroughFilter() function
	#ifdef PASSTHROUGH_FILTER_TEST
	std::cout << "RUNNING PASSTHROUGH FILTER TESTS" << std::endl;
	log_passthroughFilter(cloud_original, cloud_filtered, "../pictures/T01_passthrough_01.pcd",
	                      "passthrough filter, custom 1: ","z", -2.5, 0);
	log_passthroughFilter(cloud_original, cloud_filtered, "../pictures/T01_passthrough_02.pcd",
	                      "passthrough filter, custom 2: ","y", -3.0, 3.0);
	log_passthroughFilter(cloud_original, cloud_filtered, "../pictures/T01_passthrough_03.pcd", 
	                      "passthrough filter, custom 3: ","x", -3.0, 3.0);
	#endif
	
	//**************************************************************************
	// test voxelFilter() function
	#ifdef VOXEL_FILTER_TEST
	std::cout << "RUNNING VOXEL FILTER LEAF SIZE TEST" << std::endl;
	test_voxelFilter_leafSize(cloud_original);	
	#endif			
	
	//**************************************************************************
	// test removeNoise() function
	#ifdef NOISE_REMOVAL_TEST
	std::cout << "RUNNING NOISE REMOVAL NEAREST NEIGHBORS TEST" << std::endl;
	test_removeNoise_neighbors(cloud_original);
	std::cout << "RUNNING NOISE REMOVAL THRESHOLD TEST" << std::endl;
	test_removeNoise_stdDeviation(cloud_original);		
	#endif	
				 
	//**************************************************************************
	// test getPrism() function
	#ifdef EXTRACT_PRISM_DATA_TEST
	std::cout << "RUNNING EXTRACT PRISM ITERATIONS TEST" << std::endl;
	test_getPrism_iterations(cloud_original);
	std::cout << "RUNNING EXTRACT PRISM THRESHOLD TEST" << std::endl;
	test_getPrism_threshold(cloud_original);
	#endif
	
	//**************************************************************************
	// test objectSegmentation() function
	#ifdef SEGMENT_OBJECTS_TEST
	std::cout << "RUNNING OBJECT SEGMENTATION TEST" << std::endl;
	test_segmentObjects(cloud_original);
	#endif
	
	//**************************************************************************
	// test objectSegmentation() function
	#ifdef EUCLIDEAN_CLUSTER_TEST
	std::cout << "RUNNING EUCLIDEAN CLUSTER TEST" << std::endl;
	test_getClusters(cloud_original);
	#endif

	return 0;
}
int main(int argc, char** argv)
{
  // initialize variables
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_original (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_unaltered (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_objects (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_planeInliers (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_planeOutliers (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::ModelCoefficients::Ptr coefficients (new pcl::ModelCoefficients());
  pcl::PointIndices::Ptr indices (new pcl::PointIndices());
  pcl::PCDReader reader;
  pcl::PCDWriter writer;
  int result = 0;  // catch function return
  
  // enable variables for time logging
  boost::posix_time::ptime time_before_execution;
  boost::posix_time::ptime time_after_execution;
  boost::posix_time::time_duration difference;
  
  // read point cloud through command line
  if (argc != 2)
  {
    std::cout << "usage: " << argv[0] << " <filename>\n";
    return 0;
  }
  else
  {	// read cloud and display its size
    std::cout << "Reading Point Cloud" << std::endl;
    reader.read(argv[1], *cloud_original);
#ifdef DEBUG
    std::cout << "size of original cloud: "
    << cloud_original->points.size() << " points" << std::endl;
#endif
  }
  
  //**************************************************************************
  // test passthroughFilter() function
  std::cout << "RUNNING PASSTHROUGH FILTER TESTS" << std::endl;
  log_passthroughFilter(cloud_original, cloud_filtered, "../pictures/T01_passthrough_01.pcd",
                        "passthrough filter, custom 1: ","z", -2.5, 0);
  log_passthroughFilter(cloud_original, cloud_filtered, "../pictures/T01_passthrough_02.pcd",
                        "passthrough filter, custom 2: ","y", -3.0, 3.0);
  log_passthroughFilter(cloud_original, cloud_filtered, "../pictures/T01_passthrough_03.pcd",
                        "passthrough filter, custom 3: ","x", -3.0, 3.0);
  
  //**************************************************************************
  // test voxelFilter() function
  std::cout << "RUNNING VOXEL FILTER TEST" << std::endl;
  log_voxelFilter(cloud_original, cloud_filtered, "../pictures/T02_voxel_01.pcd",
                  "voxel filter, custom 1:", 0.01);
  log_voxelFilter(cloud_original, cloud_filtered, "../pictures/T02_voxel_02.pcd",
                  "voxel filter, custom 2:", 0.02);
  log_voxelFilter(cloud_original, cloud_filtered, "../pictures/T02_voxel_03.pcd",
                  "voxel filter, custom 3:", 0.04);
  log_voxelFilter(cloud_original, cloud_filtered, "../pictures/T02_voxel_04.pcd",
                  "voxel filter, custom 4:", 0.08);
  
  //**************************************************************************
  // test removeNoise() function
  std::cout << "RUNNING NOISE REMOVAL TEST" << std::endl;
  log_removeNoise(cloud_original, cloud_filtered, "../pictures/T03_noise_01.pcd",
                  "noise removal, custom 1: ", 50, 1.0);
  log_removeNoise(cloud_original, cloud_filtered, "../pictures/T03_noise_01.pcd",
                  "noise removal, custom 1: ", 100, 1.0);
  log_removeNoise(cloud_original, cloud_filtered, "../pictures/T03_noise_01.pcd",
                  "noise removal, custom 1: ", 10, 1.0);
  log_removeNoise(cloud_original, cloud_filtered, "../pictures/T03_noise_01.pcd",
                  "noise removal, custom 1: ", 50, 1.9);
  log_removeNoise(cloud_original, cloud_filtered, "../pictures/T03_noise_01.pcd",
                  "noise removal, custom 1: ", 50, 0.1);
  
  //**************************************************************************
  // test getPlane() function
  std::cout << "RUNNING PLANE SEGMENTATION TEST" << std::endl;
  log_getPlane(cloud_original, cloud_planeInliers, cloud_planeOutliers,
               coefficients, indices, "../pictures/T04_planeInliers_01.pcd",
               "plane segmentation, custom 1: ", 0, 1.57, 1000, 0.01);
  log_getPlane(cloud_original, cloud_planeInliers, cloud_planeOutliers,
               coefficients, indices, "../pictures/T04_planeInliers_02.pcd",
               "plane segmentation, custom 2: ", 0, 1.57, 1, 0.01);
  log_getPlane(cloud_original, cloud_planeInliers, cloud_planeOutliers,
               coefficients, indices, "../pictures/T04_planeInliers_03.pcd",
               "plane segmentation, custom 3: ", 0, 1.57, 2000, 0.01);
  log_getPlane(cloud_original, cloud_planeInliers, cloud_planeOutliers,
               coefficients, indices, "../pictures/T04_planeInliers_04.pcd",
               "plane segmentation, custom 4: ", 0, 0.76, 1000, 0.01);
  log_getPlane(cloud_original, cloud_planeInliers, cloud_planeOutliers,
               coefficients, indices, "../pictures/T04_planeInliers_05.pcd",
               "plane segmentation, custom 5: ", 0, 2.09, 1000, 0.01);
  log_getPlane(cloud_original, cloud_planeInliers, cloud_planeOutliers,
               coefficients, indices, "../pictures/T04_planeInliers_06.pcd",
               "plane segmentation, custom 6: ", 0.35, 1.57, 1000, 0.01);
  log_getPlane(cloud_original, cloud_planeInliers, cloud_planeOutliers,
               coefficients, indices, "../pictures/T04_planeInliers_07.pcd",
               "plane segmentation, custom 7: ", 0.79, 1.57, 1000, 0.01);
  log_getPlane(cloud_original, cloud_planeInliers, cloud_planeOutliers,
               coefficients, indices, "../pictures/T04_planeInliers_08.pcd",
               "plane segmentation, custom 8: ", 0, 1.57, 1000, 0.001);
  log_getPlane(cloud_original, cloud_planeInliers, cloud_planeOutliers,
               coefficients, indices, "../pictures/T04_planeInliers_09.pcd",
               "plane segmentation, custom 9: ", 0, 1.57, 1000, 0.01);
  
  //**************************************************************************
  // test getPrism() function

   std::cout << "RUNNING EXTRACT PRISM TEST" << std::endl;
   log_getPrism(cloud_original, cloud_objects, "../pictures/T05_objects_01.pcd",
   "polygonal prism, custom 1: ", 0, 1.57, 1000, 0.01, 0.02, 0.2);

  
  // test getPrism() function
  std::cout << "RUNNING EXTRACT PRISM TEST" << std::endl;
  // get output from default plane
  time_before_execution = boost::posix_time::microsec_clock::local_time();  // time before
  result = c44::getPrism(cloud_original, cloud_objects, 0, 1.57, 1000, 0.01, 0.02, 0.2);
  time_after_execution = boost::posix_time::microsec_clock::local_time(); // time after
  if(result < 0)
  {
    std::cout << "ERROR: could not find polygonal prism data" << std::endl;
  }
  else
  {
    writer.write<pcl::PointXYZ> ("../pictures/T05_objects_01.pcd", *cloud_objects); // write out cloud
#ifdef DEBUG
    difference = time_after_execution - time_before_execution;  // get execution time
    std::cout << std::setw(5) << difference.total_milliseconds() << ": "
    << "polygonal prism, default: "
    << cloud_objects->points.size() << " points" << std::endl;
#endif
  }
  
  return 0;
}
Example #3
0
int
main (int argc, char** argv)
{
//     ros::init(argc, argv, "extract_sec");
//     ros::NodeHandle node_handle;

//     pcl::visualization::PCLVisualizer result_viewer("planar_segmentation");
    boost::shared_ptr<pcl::visualization::PCLVisualizer> result_viewer (new pcl::visualization::PCLVisualizer ("planar_segmentation"));
    result_viewer->addCoordinateSystem(0.3, "reference", 0);
    result_viewer->setCameraPosition(-0.499437, 0.111597, -0.758007, -0.443141, 0.0788583, -0.502855, -0.034703, -0.992209, -0.119654);
    result_viewer->setCameraClipDistances(0.739005, 2.81526);
//     result_viewer->setCameraPosition(Position, Focal point, View up);
//     result_viewer->setCameraClipDistances(Clipping plane);

    /***************************************
    *  parse arguments
    ***************************************/
    if(argc<5)
    {
        pcl::console::print_info("Usage: extract_sec DATA_PATH/PCD_FILE_FORMAT START_INDEX END_INDEX DEMO_NAME (opt)STEP_SIZE(1)");
        exit(1);
    }

    int view_id=0;
    int step=1;
    std::string basename_cloud=argv[1];
    unsigned int index_start = std::atoi(argv[2]);
    unsigned int index_end = std::atoi(argv[3]);
    std::string demo_name=argv[4];
    if(argc>5) step=std::atoi(argv[5]);

    /***************************************
    *  set up result directory
    ***************************************/
    mkdir("/home/zhen/Documents/Dataset/human_result", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
    char result_folder[50];
    std::snprintf(result_folder, sizeof(result_folder), "/home/zhen/Documents/Dataset/human_result/%s", demo_name.c_str());
    mkdir(result_folder, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

    std::string basename_pcd = (basename_cloud.find(".pcd") == std::string::npos) ? (basename_cloud + ".pcd") : basename_cloud;
    std::string filename_pcd;

    std::string mainGraph_file;
    mainGraph_file = std::string(result_folder) + "/mainGraph.txt";

    // write video config
    char video_file[100];
    std::snprintf(video_file, sizeof(video_file), "%s/video.txt", result_folder);
    std::ofstream video_config(video_file);
    if (video_config.is_open())
    {
        video_config << index_start << " " << index_end << " " << demo_name << " " << step;
        video_config.close();
    }

    /***************************************
    *  set up cloud, segmentation, tracker, detectors, graph, features
    ***************************************/
    TableObject::Segmentation tableObjSeg;
    TableObject::Segmentation initialSeg;
    TableObject::track3D tracker(false);
    TableObject::colorDetector finger1Detector(0,100,0,100,100,200); //right
    TableObject::colorDetector finger2Detector(150,250,0,100,0,100); //left
    TableObject::touchDetector touchDetector(0.01);
    TableObject::bottleDetector bottleDetector;

    TableObject::mainGraph mainGraph((int)index_start);

    std::vector<manipulation_features> record_features;
    manipulation_features cur_features;

    TableObject::pcdCloud pcdSceneCloud;
    CloudPtr sceneCloud;
    CloudPtr planeCloud(new Cloud);
    CloudPtr cloud_objects(new Cloud);
    CloudPtr cloud_finger1(new Cloud);
    CloudPtr cloud_finger2(new Cloud);
    CloudPtr cloud_hull(new Cloud);
    CloudPtr track_target(new Cloud);
    CloudPtr tracked_cloud(new Cloud);

    std::vector<pcl::PointIndices> clusters;
    pcl::ModelCoefficients coefficients;
    pcl::PointIndices f1_indices;
    pcl::PointIndices f2_indices;
    Eigen::Affine3f toBottleCoordinate;
    Eigen::Affine3f transformation;

    Eigen::Vector3f bottle_init_ori;

    // set threshold of size of clustered cloud
    tableObjSeg.setThreshold(30);
    initialSeg.setThreshold(500);

    // downsampler
    pcl::ApproximateVoxelGrid<pcl::PointXYZRGBA> grid;
    float leaf_size=0.005;
    grid.setLeafSize (leaf_size, leaf_size, leaf_size);

    /***************************************
    *  start processing
    ***************************************/
    unsigned int idx = index_start;
    int video_id=0;
    bool change = false;
    while( idx <= index_end && !result_viewer->wasStopped())
    {
        std::cout << std::endl;
        std::cout << "frame id=" << idx << std::endl;
        filename_pcd = cv::format(basename_cloud.c_str(), idx);

        if(idx==index_start) {
            /***************************************
             *  Intialization:
             * -plane localization
             * -object cluster extraction
             * -bottle cluster localization
             ***************************************/
            initialSeg.resetCloud(filename_pcd, false);
            initialSeg.seg(false);
            initialSeg.getObjects(cloud_objects, clusters);
            initialSeg.getCloudHull(cloud_hull);
            initialSeg.getPlaneCoefficients(coefficients);

            initialSeg.getsceneCloud(pcdSceneCloud);
            initialSeg.getTableTopCloud(planeCloud);
            sceneCloud=pcdSceneCloud.getCloud();

            /***************************************
             *  fingertip, hand_arm removal
             ***************************************/
            //opencv color filtering for fingertip_1
            {
                pcl::ScopeTime t_finger1("Finger 1(blue) detection");
                finger1Detector.setInputCloud(cloud_objects, clusters);
                finger1Detector.filter(f1_indices,cloud_finger1);
            }
            finger1Detector.showDetectedCloud(result_viewer, "finger1");

            //opencv color filtering for fingertip_2
            {
                pcl::ScopeTime t_finger2("Finger 2(orange) detection");
                finger2Detector.setInputCloud(cloud_objects, clusters);
                finger2Detector.filter(f2_indices,cloud_finger2);
            }
            finger2Detector.showDetectedCloud(result_viewer, "finger2");

            // remove hand (include cluster that contains the detected fingertips and also the other clusters that are touching the cluster)
            std::vector<int> hand_arm1=TableObject::findHand(cloud_objects, clusters, f1_indices);

            for(int i=hand_arm1.size()-1; i>=0; i--)
            {
                clusters.erase(clusters.begin()+hand_arm1[i]);
                std::cout << "removing hand_arm : cluster index = " << hand_arm1[i] << std::endl;
            }
            std::vector<int> hand_arm2=TableObject::findHand(cloud_objects, clusters, f2_indices);
            for(int i=hand_arm2.size()-1; i>=0; i--)
            {
                clusters.erase(clusters.begin()+hand_arm2[i]);
                std::cout << "removing hand_arm : cluster index = " << hand_arm2[i] << std::endl;
            }

// DEBUG
// pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGBA> plane(planeCloud);
// result_viewer->addPointCloud<RefPointType>(planeCloud, plane, "tabletop");
// CloudPtr debug(new Cloud);
// initialSeg.getOutPlaneCloud(debug);
// pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGBA> out_plane(debug);
// result_viewer->addPointCloud<RefPointType>(debug, out_plane, "out_plane");

// choose bottle_id at 1st frame & confirm fitted model is correct
            TableObject::view3D::drawClusters(result_viewer, cloud_objects, clusters, true);

            while (!result_viewer->wasStopped ())
            {
                result_viewer->spinOnce (100);
                boost::this_thread::sleep (boost::posix_time::microseconds (100000));
            }

            std::cout << "cluster size = " << clusters.size() << std::endl;
            /***************************************
             *  Localizing cylinder
             ***************************************/
            CloudPtr cluster_bottle (new Cloud);
            int bottle_id = 0;
            pcl::copyPointCloud (*cloud_objects, clusters[bottle_id], *cluster_bottle);
            bottleDetector.setInputCloud(cluster_bottle);
            bottleDetector.fit();
            bottleDetector.getTransformation(toBottleCoordinate);
            bottle_init_ori= bottleDetector.getOrientation();

            float x, y, z, roll, pitch, yaw;
            pcl::getTranslationAndEulerAngles(toBottleCoordinate.inverse(), x, y, z, roll, pitch, yaw);

            result_viewer->removeCoordinateSystem("reference", 0);
            result_viewer->addCoordinateSystem(0.3, toBottleCoordinate.inverse(), "reference", 0);
            bottleDetector.drawOrientation(result_viewer);

            /***************************************
             *  Record features
             ***************************************/
            bottle_features cur_bottle_features;
            cur_bottle_features.loc[0] = x;
            cur_bottle_features.loc[1] = y;
            cur_bottle_features.loc[2] = z;
            cur_bottle_features.ori[0] = roll;
            cur_bottle_features.ori[1] = pitch;
            cur_bottle_features.ori[2] = yaw;
            cur_bottle_features.color[0] = bottleDetector.getCenter().r;
            cur_bottle_features.color[1] = bottleDetector.getCenter().g;
            cur_bottle_features.color[2] = bottleDetector.getCenter().b;
            cur_bottle_features.size[0] = bottleDetector.getHeight();
            cur_bottle_features.size[1] = bottleDetector.getRadius();
            cur_features.bottle = cur_bottle_features;

            pcl::PointXYZ center_finger1 = TableObject::computeObjCentroid(cloud_finger1);
            pcl::PointXYZ center_finger2 = TableObject::computeObjCentroid(cloud_finger2);
            center_finger1 = pcl::transformPoint<pcl::PointXYZ>(center_finger1, toBottleCoordinate);
            center_finger2 = pcl::transformPoint<pcl::PointXYZ>(center_finger2, toBottleCoordinate);
            cur_features.gripper_1.loc[0] = center_finger1.x;
            cur_features.gripper_1.loc[1] = center_finger1.y;
            cur_features.gripper_1.loc[2] = center_finger1.z;
            cur_features.gripper_2.loc[0] = center_finger2.x;
            cur_features.gripper_2.loc[1] = center_finger2.y;
            cur_features.gripper_2.loc[2] = center_finger2.z;

            record_features.push_back(cur_features);

            /***************************************
             *  Tracking initialization
             ***************************************/
            {
                pcl::ScopeTime t_track("Tracker initialization");
                tracker.setTarget(cluster_bottle, bottleDetector.getCenter());
                tracker.initialize();
            }

            /***************************************
             *  Touch detection
             ***************************************/
            std::vector<CloudPtr> touch_clouds;
            touch_clouds.push_back(cluster_bottle);
            touch_clouds.push_back(cloud_finger1);
            touch_clouds.push_back(cloud_finger2);

            // touch detection between each pair of objects (including fingertips, tabletop objects and tabletop)
            for(int i=0; i<touch_clouds.size(); i++)
            {
                int j;
                bool touch;
                for(j=i+1; j<touch_clouds.size(); j++)
                {
                    // touch detection between object_i and object_j
                    char relation [50];
                    std::sprintf(relation, "object%d_object%d", i, j);
                    std::cout << relation << std::endl;

                    {
                        pcl::ScopeTime t("Touch detection");
                        touch=touchDetector.detect(touch_clouds[i], touch_clouds[j]);
                    }
//                     touchDetector.showTouch(result_viewer, relation, 100+250*(j-i-1), 40+20*i);

                    // relational scene graph -> main graph
                    if(touch) {
                        mainGraph.addInitialRelationalGraph(2);
                    } else {
                        mainGraph.addInitialRelationalGraph(0);
                    }
                }

                // touch detection between each objects and tabletop
                char relation [50];
                std::sprintf (relation, "object%d_object%d", i, (int)touch_clouds.size());
                std::cout << relation << std::endl;
                {
                    pcl::ScopeTime t("Touch detection");
                    touch=touchDetector.detectTableTouch(touch_clouds[i], coefficients);
                }
//                 touchDetector.showTouch(result_viewer, relation, 100+250*(j-i-1), 40+20*i);

                // relational scene graph -> main graph
                if(touch) {
                    mainGraph.addInitialRelationalGraph(2);
                } else {
                    mainGraph.addInitialRelationalGraph(0);
                }
            }

            /***************************************
             *  Visualization
             ***************************************/
            // draw extracted object clusters
//             TableObject::view3D::drawClusters(result_viewer, cloud_objects, touch_clusters);

            // draw extracted plane points
//             pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGBA> plane(planeCloud);
//             result_viewer->addPointCloud<RefPointType>(planeCloud, plane, "tabletop");
//             std::stringstream ss;
//             ss << (int)touch_clusters.size();
//             result_viewer->addText3D(ss.str(), planeCloud->points.at(334*640+78),0.1);

            // draw extracted plane contour polygon
            result_viewer->addPolygon<RefPointType>(cloud_hull, 0, 255, 0, "polygon");

            change = true;
        } else
        {
            /***************************************
             *  object cloud extraction
             ***************************************/
            tableObjSeg.resetCloud(filename_pcd, false);
            tableObjSeg.seg(cloud_hull,false);
            tableObjSeg.getObjects(cloud_objects, clusters);
            tableObjSeg.getsceneCloud(pcdSceneCloud);
            sceneCloud=pcdSceneCloud.getCloud();

            /***************************************
             *  fingertip extraction
             ***************************************/
            //opencv color filtering for fingertip_1
            {
                pcl::ScopeTime t_finger1("Finger 1(blue) detection");
                finger1Detector.setInputCloud(cloud_objects, clusters);
                finger1Detector.filter(f1_indices,cloud_finger1);
            }
            finger1Detector.showDetectedCloud(result_viewer, "finger1");

            //opencv color filtering for fingertip_2
            {
                pcl::ScopeTime t_finger1("Finger 2(orange) detection");
                finger2Detector.setInputCloud(cloud_objects, clusters);
                finger2Detector.filter(f2_indices,cloud_finger2);
            }
            finger2Detector.showDetectedCloud(result_viewer, "finger2");

            /***************************************
             *  filter out black glove cluster & gray sleeve, also update cloud_objects with removed cluster indices
             ***************************************/
            for(int i=0; i<clusters.size(); i++)
            {
                pcl::CentroidPoint<RefPointType> color_points;
                for(int j=0; j<clusters[i].indices.size(); j++)
                {
                    color_points.add(cloud_objects->at(clusters[i].indices[j]));
                }
                RefPointType mean_color;
                color_points.get(mean_color);
                if(mean_color.r>30 & mean_color.r<70 & mean_color.g>30 & mean_color.g<70 & mean_color.b>30 & mean_color.b<70)
                {
                    clusters.erase(clusters.begin()+ i);
                    i=i-1;
                }
            }

            /***************************************
             *  Tracking objects
             ***************************************/
            {
                pcl::ScopeTime t_track("Tracking");
                grid.setInputCloud (sceneCloud);
                grid.filter (*track_target);
                tracker.track(track_target, transformation);
                tracker.getTrackedCloud(tracked_cloud);
            }
            tracker.viewTrackedCloud(result_viewer);
//             tracker.drawParticles(result_viewer);

            /***************************************
             *  compute tracked <center, orientation>
             ***************************************/
            pcl::PointXYZ bottle_loc_point(0,0,0);
            bottle_loc_point = pcl::transformPoint<pcl::PointXYZ>(bottle_loc_point, transformation);
            result_viewer->removeShape("bottle_center");
//             result_viewer->addSphere<pcl::PointXYZ>(bottle_loc_point, 0.05, "bottle_center");

            Eigen::Vector3f bottle_ori;
            pcl::transformVector(bottle_init_ori,bottle_ori,transformation);
            TableObject::view3D::drawArrow(result_viewer, bottle_loc_point, bottle_ori, "bottle_arrow");

            /***************************************
             *  calculate toTrackedBottleCoordinate
             ***************************************/
            Eigen::Affine3f toTrackedBottleCoordinate;
            Eigen::Vector3f p( bottle_loc_point.x, bottle_loc_point.y, bottle_loc_point.z ); // position

            // get a vector that is orthogonal to _orientation ( yc = _orientation x [1,0,0]' )
            Eigen::Vector3f yc( 0, bottle_ori[2], -bottle_ori[1] );
            yc.normalize();
            // get a transform that rotates _orientation into z and moves cloud into origin.
            pcl::getTransformationFromTwoUnitVectorsAndOrigin(yc, bottle_ori, p, toTrackedBottleCoordinate);
            result_viewer->removeCoordinateSystem("reference");
            result_viewer->addCoordinateSystem(0.3, toTrackedBottleCoordinate.inverse(), "reference", 0);

            float x, y, z, roll, pitch, yaw;
            pcl::getTranslationAndEulerAngles(toTrackedBottleCoordinate.inverse(), x, y, z, roll, pitch, yaw);

            /***************************************
            *  setup bottle feature
            ***************************************/
            cur_features = record_features[video_id-1];
            cur_features.bottle.loc[0] = x;
            cur_features.bottle.loc[1] = y;
            cur_features.bottle.loc[2] = z;
            cur_features.bottle.ori[0] = roll;
            cur_features.bottle.ori[1] = pitch;
            cur_features.bottle.ori[2] = yaw;

            pcl::PointXYZ center_finger1 = TableObject::computeObjCentroid(cloud_finger1);
            pcl::PointXYZ center_finger2 = TableObject::computeObjCentroid(cloud_finger2);
            center_finger1 = pcl::transformPoint<pcl::PointXYZ>(center_finger1, toTrackedBottleCoordinate);
            center_finger2 = pcl::transformPoint<pcl::PointXYZ>(center_finger2, toTrackedBottleCoordinate);
            cur_features.gripper_1.loc[0] = center_finger1.x;
            cur_features.gripper_1.loc[1] = center_finger1.y;
            cur_features.gripper_1.loc[2] = center_finger1.z;
            cur_features.gripper_2.loc[0] = center_finger2.x;
            cur_features.gripper_2.loc[1] = center_finger2.y;
            cur_features.gripper_2.loc[2] = center_finger2.z;

            record_features.push_back(cur_features);



            /***************************************
             *  Touch detection
             ***************************************/
            std::vector<CloudPtr> touch_clouds;
            touch_clouds.push_back(tracked_cloud);
            touch_clouds.push_back(cloud_finger1);
            touch_clouds.push_back(cloud_finger2);

            // touch detection between each pair of objects (including fingertips, tabletop objects and tabletop)
            for(int i=0; i<touch_clouds.size(); i++)
            {
                int j;
                bool touch;
                for(j=i+1; j<touch_clouds.size(); j++)
                {
                    // touch detection between object_i and object_j
                    char relation [50];
                    std::sprintf(relation, "object%d_object%d", i, j);
                    std::cout << relation << std::endl;

                    {
                        pcl::ScopeTime t("Touch detection");
                        touch=touchDetector.detect(touch_clouds[i], touch_clouds[j]);
                    }
//                     touchDetector.showTouch(result_viewer, relation, 100+250*(j-i-1), 40+20*i);

                    // relational scene graph -> main graph
                    if(touch) {
                        mainGraph.addRelationalGraph(2);
                    } else {
                        mainGraph.addRelationalGraph(0);
                    }
                }

                // touch detection between each objects and tabletop
                char relation [50];
                std::sprintf (relation, "object%d_object%d", i, (int)touch_clouds.size());
                std::cout << relation << std::endl;
                {
                    pcl::ScopeTime t("Touch detection");
                    touch=touchDetector.detectTableTouch(touch_clouds[i], coefficients);
                }
//                 touchDetector.showTouch(result_viewer, relation, 100+250*(j-i-1), 40+20*i);

                // relational scene graph -> main graph
                if(touch) {
                    mainGraph.addRelationalGraph(2);
                } else {
                    mainGraph.addRelationalGraph(0);
                }
            }

            /***************************************
             *  Visualization
             ***************************************/
            // draw extracted point clusters
//             TableObject::view3D::drawText(result_viewer, cloud_objects, touch_clusters);

            /***************************************
             *  Main Graph
             ***************************************/
            change = mainGraph.compareRelationGraph((int)idx);
        }

        // darw original cloud
        pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGBA> rgb(sceneCloud);
        if(!result_viewer->updatePointCloud<RefPointType>(sceneCloud, rgb, "new frame"))
            result_viewer->addPointCloud<RefPointType>(sceneCloud, rgb, "new frame");

        result_viewer->spinOnce (100);
        boost::this_thread::sleep (boost::posix_time::microseconds (100000));


        //debug
        std::cout << cur_features.bottle.loc[0] << " " << cur_features.bottle.loc[1] << " " << cur_features.bottle.loc[2]
                  << " " << cur_features.bottle.ori[0] << " " << cur_features.bottle.ori[1] << " " << cur_features.bottle.ori[2]
                  << " " << cur_features.bottle.color[0] << " " << cur_features.bottle.color[1] << " " << cur_features.bottle.color[2]
                  << " " << cur_features.bottle.size[0] << " " << cur_features.bottle.size[1]
                  << " " << cur_features.gripper_1.loc[0] << " " << cur_features.gripper_1.loc[1] << " " << cur_features.gripper_1.loc[2]
                  << " " << cur_features.gripper_2.loc[0] << " " << cur_features.gripper_2.loc[1] << " " << cur_features.gripper_2.loc[2]
                  << std::endl;

        if(change)
        {
            char screenshot[100]; // make sure it's big enough
            std::snprintf(screenshot, sizeof(screenshot), "%s/sec_%d.png", result_folder, (int)idx);
            std::cout << screenshot << std::endl;
            result_viewer->saveScreenshot(screenshot);

            //record features
            char feature_file[100]; // make sure it's big enough
            std::snprintf(feature_file, sizeof(feature_file), "%s/features_original.txt", result_folder);
            std::ofstream feature_writer(feature_file, std::ofstream::out | std::ofstream::app);
            feature_writer << cur_features.bottle.loc[0] << " " << cur_features.bottle.loc[1] << " " << cur_features.bottle.loc[2]
                           << " " << cur_features.bottle.ori[0] << " " << cur_features.bottle.ori[1] << " " << cur_features.bottle.ori[2]
                           << " " << cur_features.bottle.color[0] << " " << cur_features.bottle.color[1] << " " << cur_features.bottle.color[2]
                           << " " << cur_features.bottle.size[0] << " " << cur_features.bottle.size[1]
                           << " " << cur_features.gripper_1.loc[0] << " " << cur_features.gripper_1.loc[1] << " " << cur_features.gripper_1.loc[2]
                           << " " << cur_features.gripper_2.loc[0] << " " << cur_features.gripper_2.loc[1] << " " << cur_features.gripper_2.loc[2]
                           << std::endl;
            feature_writer.close();
            std::cout << "features saved at " << feature_file << std::endl;
        }

        char screenshot[200]; // make sure it's big enough
        std::snprintf(screenshot, sizeof(screenshot), "%s/video/sec_%d.png", result_folder, (int)video_id);
        std::cout << screenshot << std::endl;
        result_viewer->saveScreenshot(screenshot);

        idx=idx+step;
        video_id=video_id+1;
    }

    mainGraph.displayMainGraph();
    mainGraph.recordMainGraph(mainGraph_file);


    while (!result_viewer->wasStopped ())
    {
        result_viewer->spinOnce (100);
        boost::this_thread::sleep (boost::posix_time::microseconds (100000));
    }

}
void 
cloud_cb (const sensor_msgs::PointCloud2ConstPtr& input)
{
//	std::cout << "\n\n----------------Received point cloud!-----------------\n";

	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr downsampled (new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_planar (new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_objects (new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_filtered (new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_red (new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_green (new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_blue (new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_yellow (new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_concat_clusters (new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_concat_hulls (new pcl::PointCloud<pcl::PointXYZRGB>);
	sensor_msgs::PointCloud2 downsampled2, planar2, objects2, filtered2, red2, green2, blue2, yellow2, concat_clusters2, concat_hulls2;
	std::vector<pcl::PointCloud<pcl::PointXYZRGB>::Ptr> color_clouds, cluster_clouds, hull_clouds;
	std::vector<pcl::PointXYZRGB> labels;
	
//	fromROSMsg(*input, *cloud);
//	pub_input.publish(*input);

	// Downsample the input PointCloud
	pcl::VoxelGrid<sensor_msgs::PointCloud2> sor;
	sor.setInputCloud (input);
//	sor.setLeafSize (0.01, 0.01, 0.01);	//play around with leafsize (more samples, better resolution?)
	sor.setLeafSize (0.001, 0.001, 0.001);
	sor.filter (downsampled2);
	fromROSMsg(downsampled2,*downsampled);
	pub_downsampled.publish (downsampled2);	
	
	// Segment the largest planar component from the downsampled cloud
	pcl::SACSegmentation<pcl::PointXYZRGB> seg;
	pcl::ModelCoefficients::Ptr coeffs (new pcl::ModelCoefficients ());
	pcl::PointIndices::Ptr inliers (new pcl::PointIndices ());
	seg.setOptimizeCoefficients (true);
	seg.setModelType (pcl::SACMODEL_PLANE);
	seg.setMethodType (pcl::SAC_RANSAC);
	seg.setMaxIterations (100);
	seg.setDistanceThreshold (0.0085);
	seg.setInputCloud (downsampled);
	seg.segment (*inliers, *coeffs);
	
	// Extract the planar inliers from the input cloud
	pcl::ExtractIndices<pcl::PointXYZRGB> extract; 
	extract.setInputCloud (downsampled);
	extract.setIndices (inliers);
	extract.setNegative (false);
	extract.filter (*cloud_planar);
//	toROSMsg(*cloud_planar,planar2);
//	pub_planar.publish (planar2);

	// Remove the planar inliers, extract the rest
	extract.setNegative (true);
	extract.filter (*cloud_objects);
//	toROSMsg(*cloud_objects,objects2);
//	pub_objects.publish (objects2);

	// PassThrough Filter
	pcl::PassThrough<pcl::PointXYZRGB> pass;
	pass.setInputCloud (cloud_objects);
	pass.setFilterFieldName ("z");	//all duplos in pcd1
	pass.setFilterLimits (0.8, 1.0);
	pass.filter (*cloud_filtered);
	toROSMsg(*cloud_filtered,filtered2);
	pub_filtered.publish (filtered2);


//don't passthrough filter, does color filter work too? (cloud_red has many points in top right off the table)

	// Segment filtered PointCloud by color (red, green, blue, yellow)
	for (size_t i = 0 ; i < cloud_filtered->points.size () ; i++)
	{
		if ( (int(cloud_filtered->points[i].r) > 2*int(cloud_filtered->points[i].g)) && (cloud_filtered->points[i].r > cloud_filtered->points[i].b) )
			cloud_red->points.push_back(cloud_filtered->points[i]);
		if ( (cloud_filtered->points[i].g > cloud_filtered->points[i].r) && (cloud_filtered->points[i].g > cloud_filtered->points[i].b) )
			cloud_green->points.push_back(cloud_filtered->points[i]);
		if ( (cloud_filtered->points[i].b > cloud_filtered->points[i].r) && (cloud_filtered->points[i].b > cloud_filtered->points[i].g) )
			cloud_blue->points.push_back(cloud_filtered->points[i]);
		if ( (cloud_filtered->points[i].r > cloud_filtered->points[i].g) && (int(cloud_filtered->points[i].g) - int(cloud_filtered->points[i].b) > 30) )
			cloud_yellow->points.push_back(cloud_filtered->points[i]);
	}
	cloud_red->header.frame_id = "base_link";
	cloud_red->width = cloud_red->points.size ();
	cloud_red->height = 1;
	color_clouds.push_back(cloud_red);
	toROSMsg(*cloud_red,red2);
	pub_red.publish (red2);
	cloud_green->header.frame_id = "base_link";
	cloud_green->width = cloud_green->points.size ();
	cloud_green->height = 1;
	color_clouds.push_back(cloud_green);
	toROSMsg(*cloud_green,green2);
	pub_green.publish (green2);
	cloud_blue->header.frame_id = "base_link";
	cloud_blue->width = cloud_blue->points.size ();
	cloud_blue->height = 1;
	color_clouds.push_back(cloud_blue);
	toROSMsg(*cloud_blue,blue2);
	pub_blue.publish (blue2);
	cloud_yellow->header.frame_id = "base_link";
	cloud_yellow->width = cloud_yellow->points.size ();
	cloud_yellow->height = 1;
	color_clouds.push_back(cloud_yellow);
	toROSMsg(*cloud_yellow,yellow2);
	pub_yellow.publish (yellow2);
	

	// Extract Euclidian clusters from color-segmented PointClouds
	int j(0), num_red (0), num_green(0), num_blue(0), num_yellow(0);
	for (size_t cit = 0 ; cit < color_clouds.size() ; cit++)
	{
		pcl::KdTree<pcl::PointXYZRGB>::Ptr tree (new pcl::KdTreeFLANN<pcl::PointXYZRGB>);
		tree->setInputCloud (color_clouds[cit]);
		std::vector<pcl::PointIndices> cluster_indices;
		pcl::EuclideanClusterExtraction<pcl::PointXYZRGB> ec;
		ec.setClusterTolerance (0.0075); // 0.01
//		ec.setMinClusterSize (12);
//		ec.setMaxClusterSize (75);
		ec.setMinClusterSize (100);
		ec.setMaxClusterSize (4000);
		ec.setSearchMethod (tree);
		ec.setInputCloud (color_clouds[cit]);
		ec.extract (cluster_indices);

		for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin (); it != cluster_indices.end (); ++it)
		{	
			pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_cluster (new pcl::PointCloud<pcl::PointXYZRGB>);
			for (std::vector<int>::const_iterator pit = it->indices.begin () ; pit != it->indices.end () ; pit++)
				cloud_cluster->points.push_back (color_clouds[cit]->points[*pit]);
			cloud_cluster->width = cloud_cluster->points.size ();
			cloud_cluster->height = 1;
			cloud_cluster->is_dense = true;
			cloud_cluster->header.frame_id = "base_link";
			cluster_clouds.push_back(cloud_cluster);
			labels.push_back(cloud_cluster->points[0]);
			if (cit == 0)	num_red++;
			if (cit == 1)	num_green++;
			if (cit == 2)	num_blue++;
			if (cit == 3)	num_yellow++;
			
			// Create ConvexHull for cluster (keep points on perimeter of cluster)
			pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_hull (new pcl::PointCloud<pcl::PointXYZRGB>);
  			pcl::ConvexHull<pcl::PointXYZRGB> chull;
  			chull.setInputCloud (cloud_cluster);
  			chull.reconstruct (*cloud_hull);
			cloud_hull->width = cloud_hull->points.size ();
			cloud_hull->height = 1;
			cloud_hull->header.frame_id = "base_link";
			hull_clouds.push_back(cloud_hull);

			j++;
		}
	}
	std::cout << "Number of RED clusters: " << num_red << std::endl;
	std::cout << "Number of GREEN clusters: " << num_green << std::endl;
	std::cout << "Number of BLUE clusters: " << num_blue << std::endl;
	std::cout << "Number of YELLOW clusters: " << num_yellow << std::endl;
	std::cout << "TOTAL number of clusters: " << j << std::endl;
	
	// Concatenate PointCloud clusters and convex hulls
	for (size_t k = 0 ; k < cluster_clouds.size() ; k++)
	{
		for (size_t l = 0 ; l < cluster_clouds[k]->size() ; l++)
		{
			cloud_concat_clusters->points.push_back(cluster_clouds[k]->points[l]);
			cloud_concat_hulls->points.push_back(hull_clouds[k]->points[l]);
		}
	}
	cloud_concat_clusters->header.frame_id = "base_link";
	cloud_concat_clusters->width = cloud_concat_clusters->points.size ();
	cloud_concat_clusters->height = 1;
	toROSMsg(*cloud_concat_clusters,concat_clusters2);
	pub_concat_clusters.publish (concat_clusters2);
	cloud_concat_hulls->header.frame_id = "base_link";
	cloud_concat_hulls->width = cloud_concat_hulls->points.size ();
	cloud_concat_hulls->height = 1;
	toROSMsg(*cloud_concat_hulls,concat_hulls2);
	pub_concat_hulls.publish (concat_hulls2);

	// Estimate the volume of each cluster
	double height, width;
	std::vector <double> heights, widths;
	std::vector <int>  height_ids, width_ids;
	for (size_t k = 0 ; k < cluster_clouds.size() ; k++)
	{
		// Calculate cluster height
		double tallest(0), shortest(1000), widest(0) ;
		for (size_t l = 0 ; l < cluster_clouds[k]->size() ; l++)
		{
			double point_to_plane_dist;
			point_to_plane_dist = 	   coeffs->values[0] * cluster_clouds[k]->points[l].x + 
						   coeffs->values[1] * cluster_clouds[k]->points[l].y +
						   coeffs->values[2] * cluster_clouds[k]->points[l].z + coeffs->values[3] / 
						   sqrt( pow(coeffs->values[0], 2) + pow(coeffs->values[1], 2)+ pow(coeffs->values[2], 2) );
			if (point_to_plane_dist < 0)		point_to_plane_dist = 0;
			if (point_to_plane_dist > tallest)	tallest = point_to_plane_dist;
			if (point_to_plane_dist < shortest)	shortest = point_to_plane_dist;
		}
		// Calculate cluster width
		for (size_t m = 0 ; m < hull_clouds[k]->size() ; m++)
		{	
			double parallel_vector_dist;
			for (size_t n = m ; n < hull_clouds[k]->size()-1 ; n++)
			{
				parallel_vector_dist = sqrt( pow(hull_clouds[k]->points[m].x - hull_clouds[k]->points[n+1].x,2) + 
  							     pow(hull_clouds[k]->points[m].y - hull_clouds[k]->points[n+1].y,2) +
							     pow(hull_clouds[k]->points[m].z - hull_clouds[k]->points[n+1].z,2) );
				if (parallel_vector_dist > widest)	widest = parallel_vector_dist;	
			}
		}
		// Classify block heights (error +/- .005m)
		height = (shortest < .01) ? tallest : tallest - shortest;	//check for stacked blocks	
		heights.push_back(height);
		if (height > .020 && height < .032)		height_ids.push_back(0);		//0: standing flat
		else if (height > .036 && height < .043)	height_ids.push_back(1);		//1: standing side
		else if (height > .064)				height_ids.push_back(2);		//2: standing long
		else						height_ids.push_back(-1);		//height not classified
		// Classify block widths (error +/- .005m)
		width = widest;
		widths.push_back(widest);
		if (width > .032-.005 && width < .0515+.005)		width_ids.push_back(1);		//1: short
		else if (width > .065-.005 && width < .0763+.005)	width_ids.push_back(2);		//2: medium
		else if (width > .1275-.005 && width < .1554+.005)	width_ids.push_back(4);		//4: long
		else						width_ids.push_back(-1);		//width not classified
	}

	// Classify block size using width information
	std::vector<int> block_ids, idx_1x1, idx_1x2, idx_1x4, idx_unclassified;
	int num_1x1(0), num_1x2(0), num_1x4(0), num_unclassified(0);
	for (size_t p = 0 ; p < width_ids.size() ; p++)	
	{
		if (width_ids[p] == 1)		
		{
			block_ids.push_back(1);		//block is 1x1
			idx_1x1.push_back(p);
			num_1x1++;
		}
		else if (width_ids[p] == 2)	
		{
			block_ids.push_back(2);         //block is 1x2
			idx_1x2.push_back(p);
			num_1x2++;
		}
		else if (width_ids[p] == 4)     
		{
			block_ids.push_back(4);         //block is 1x4
			idx_1x4.push_back(p);
			num_1x4++;
		}
		else	
		{
			block_ids.push_back(-1);	//block not classified
			idx_unclassified.push_back(p);
			num_unclassified++;
		}
	}

	// Determine Duplos of the same size
	std::cout << "\nThere are " << num_1x1 << " blocks of size 1x1 ";
	if (num_1x1>0)	std::cout << "(cluster index: ";
	for (size_t q = 0 ; q < idx_1x1.size() ; q++)
		std::cout << idx_1x1[q] << ", ";
	if (num_1x1>0)  std::cout << ")";
	std::cout << "\nThere are " << num_1x2 << " blocks of size 1x2 ";
        if (num_1x2>0)  std::cout << "(cluster index: ";
	for (size_t q = 0 ; q < idx_1x2.size() ; q++)
		std::cout << idx_1x2[q] << ", ";
	if (num_1x2>0)  std::cout << ")";
	std::cout << "\nThere are " << num_1x4 << " blocks of size 1x4 ";
        if (num_1x4>0)  std::cout << "(cluster index: ";
	for (size_t q = 0 ; q < idx_1x4.size() ; q++)
		std::cout << idx_1x4[q] << ", ";
	if (num_1x4>0)  std::cout << ")";
	std::cout << "\nThere are " << num_unclassified << " unclassified blocks ";
        if (num_unclassified>0)  std::cout << "(cluster index: ";
	for (size_t q = 0 ; q < idx_unclassified.size() ; q++)
		std::cout << idx_unclassified[q] << ", ";
	if (num_unclassified>0)  std::cout << ")";
	std::cout << "\n\n\n";
	return;
}