bool Segmentation::Ransac_for_buildings(float dem_spacing, double ransac_threshold, cv::Mat original_tiles_merged)
{   
	StepResource pStep("Computing RANSAC on all the identified buildings", "app", "a2beb9b8-218e-11e4-969b-b2227cce2b54");
	   
	ProgressResource pResource("ProgressBar");
	Progress *pProgress = pResource.get(); 
	pProgress-> setSettingAutoClose(true);
	pProgress->updateProgress("Computing RANSAC on all buildings", 0, NORMAL);
	Ransac_buildings = Ransac(Segmentation::path);
	cv::Mat roof_image = cv::Mat::zeros(original_tiles_merged.size(), CV_8UC3);
		
	buildingS.resize(blobs.size());
	buildingS_inliers.resize(blobs.size());
	buildingS_outliers.resize(blobs.size());
	buildingS_plane_coefficients.resize(blobs.size());
	buldingS_number_inliers.resize(blobs.size());

	std::ofstream building_file;
	std::ofstream cont_file;
	cont_file.open (std::string(path) + "/Results/Number_of_RANSAC_applications.txt"); 
	for(int i = 0; i < blobs.size(); i++)
	{// i index is the building (blob) index
		pProgress->updateProgress("Computing RANSAC on all buildings\nBuilding "+ StringUtilities::toDisplayString(i) + " on "+ StringUtilities::toDisplayString(blobs.size()), static_cast<double>(static_cast<double>(i)/blobs.size()*100), NORMAL);
		building_file.open (std::string(path) + "/Results/Building_" + StringUtilities::toDisplayString(i)+".txt");
		building_file << 'i' << '\t' << 'j' << '\t' << 'X' << '\t' << 'Y' << '\t' << 'Z' << '\n'; 
		buildingS[i].setConstant(blobs[i].size(), 3, 0.0);
		
		// the j loop retrieves the  X, Y, Z coordinate for each pixel of all the buildings
		for(int j = 0; j < blobs[i].size(); j++) 
		{// j index is the pixel index for the single building
		 // loop on all the pixel of the SINGLE building
		    int pixel_column = blobs[i][j].x;
            int pixel_row = blobs[i][j].y;			

			double x_building =  pixel_column * dem_spacing;// xMin + pixel_column * dem_spacing // object coordinate 
			double y_building =  pixel_row * dem_spacing;// yMin + pixel_row * dem_spacing // object coordinate
			double z_building = original_tiles_merged.at<float>(pixel_row, pixel_column);//object coordinate
			
			buildingS[i](j,0) = x_building;
			buildingS[i](j,1) = y_building;
			buildingS[i](j,2) = z_building;
			 
			building_file << pixel_row+1 <<  '\t' << pixel_column+1 <<  '\t' << buildingS[i](j,0) << '\t' << buildingS[i](j,1) << '\t' << buildingS[i](j,2) << '\n'; //+1 on the imae coordinates to verify with opticks' rasters (origin is 1,1)
		}

		building_file.close();

		std::ofstream inliers_file;
		std::ofstream parameters_file;
		inliers_file.open (std::string(path) + "/Results/Inliers_building_" + StringUtilities::toDisplayString(i)+".txt");
		parameters_file.open (std::string(path) + "/Results/plane_parameters_building_" + StringUtilities::toDisplayString(i)+".txt");
		//parameters_file << "a\tb\tc\td\tmean_dist\tstd_dist\n";
		int cont = 0;
		Ransac_buildings.ransac_msg += "\n____________Building number " + StringUtilities::toDisplayString(i) +"____________\n";
		Ransac_buildings.ransac_msg += "\nITERATION NUMBER " + StringUtilities::toDisplayString(cont) +"\n";
		Ransac_buildings.ComputeModel(buildingS[i], ransac_threshold);
		
		buldingS_number_inliers[i]= Ransac_buildings.n_best_inliers_count;
		buildingS_inliers[i] = Ransac_buildings.final_inliers;
		buildingS_outliers[i] = Ransac_buildings.final_outliers;
		buildingS_plane_coefficients[i] = Ransac_buildings.final_model_coefficients;
		double inliers_percentage = static_cast<double>( (Ransac_buildings.n_best_inliers_count) ) / static_cast<double> (buildingS[i].rows());
		int inliers_so_far = Ransac_buildings.n_best_inliers_count;
		std::vector<int> old_final_outliers = Ransac_buildings.final_outliers;
		 

		// DRAWS THE ROOFS yellow
		for (int k = 0; k < Ransac_buildings.n_best_inliers_count; k++)
		{
			int pixel_row = static_cast<int>(buildingS[i](Ransac_buildings.final_inliers[k], 1) /  dem_spacing);
            int pixel_column = static_cast<int>(buildingS[i](Ransac_buildings.final_inliers[k], 0) /  dem_spacing);

			unsigned char r = 255;// unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
            unsigned char g = 255;// unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
            unsigned char b = 0;//unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
		
			roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[0] = b;
            roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[1] = g;
            roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[2] = r;
		}

		while (inliers_percentage < 0.90)
		{
			cont ++;
			Ransac_buildings.ransac_msg += "\nITERATION NUMBER " + StringUtilities::toDisplayString(cont) +"\n";
			Eigen::Matrix<double,  Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> building_outliers;
			building_outliers.setConstant(buildingS[i].rows() - inliers_so_far, 3, 0.0);
			
		   	//* forse il metodo va già bene così, perchè riempio la matrice deglio outlier in maniera ordinata,
			//* solo che gli indici degli inlier/outlier non sono più indicativi rispetto alla matrice di building originale, ma rispetto alla matrice di innput
			//* devo riporatre gli ID degli indici alla loro posizione originale
			for (int w = 0; w <building_outliers.rows(); w++)
			{
				building_outliers(w, 0) = buildingS[i](old_final_outliers[w], 0);
			    building_outliers(w, 1) = buildingS[i](old_final_outliers[w], 1);
			    building_outliers(w, 2) = buildingS[i](old_final_outliers[w], 2);
				
				//Ransac_buildings.ransac_msg += "\n" + StringUtilities::toDisplayString(pixel_row+1) + "\t" + StringUtilities::toDisplayString(pixel_column+1) + "\t" + StringUtilities::toDisplayString(final_outliers[w]) + "\t" + StringUtilities::toDisplayString(building_outliers(w, 0))+ "\t"+ StringUtilities::toDisplayString(building_outliers(w, 1)) + "\t" + StringUtilities::toDisplayString(building_outliers(w, 2))+"\n"; // needed for tesing (test passed at first iteration)
			}
			

			Ransac_buildings.ransac_msg += "\n";
			//Ransac_buildings.ransac_msg += "\nprova "+ StringUtilities::toDisplayString(inliers_percentage*100)+"\n";
			Ransac_buildings.ComputeModel(building_outliers, ransac_threshold);
			//inliers_percentage = inliers_percentage + static_cast<double>( (n_best_inliers_count) ) / static_cast<double> (building_outliers.rows());
			inliers_percentage = inliers_percentage + static_cast<double>( (Ransac_buildings.n_best_inliers_count) ) / static_cast<double> (buildingS[i].rows());

			Ransac_buildings.ransac_msg += "\nINLIERS IN RELATION TO GLOBAL INDEX ("+ StringUtilities::toDisplayString(Ransac_buildings.n_best_inliers_count) + ")\n";
	        for(size_t i = 0; i < Ransac_buildings.n_best_inliers_count; i++)
	        {
		       Ransac_buildings.ransac_msg +=  StringUtilities::toDisplayString(old_final_outliers[Ransac_buildings.final_inliers[i]])+" ";
			   inliers_file << old_final_outliers[Ransac_buildings.final_inliers[i]] << "\t";
	        }
			Ransac_buildings.ransac_msg += "\n";
			inliers_file << "\n";

			//old_final_outliers.resize(building_outliers.rows() - Ransac_buildings.n_best_inliers_count);
			Ransac_buildings.ransac_msg += "\nOUTLIERS IN RELATION TO GLOBAL INDEX("+ StringUtilities::toDisplayString(building_outliers.rows() - Ransac_buildings.n_best_inliers_count) + ")\n";
			for(size_t i = 0; i < building_outliers.rows() - Ransac_buildings.n_best_inliers_count; i++)
			{
				Ransac_buildings.ransac_msg +=  StringUtilities::toDisplayString(old_final_outliers[Ransac_buildings.final_outliers[i]])+" ";
				old_final_outliers[i] = old_final_outliers[Ransac_buildings.final_outliers[i]];// in this way I refer the outliers indexes to the global indexes (those referred to the original eigen matrix)
			}
			
			//parameters_file << Ransac_buildings.final_model_coefficients[0] << "\t" << Ransac_buildings.final_model_coefficients[1] << "\t" << Ransac_buildings.final_model_coefficients[2] << "\t" << Ransac_buildings.final_model_coefficients[3] << "\t" << Ransac_buildings.mean_distances << "\t"<< Ransac_buildings.std_distances << "\n";
			parameters_file << Ransac_buildings.final_model_coefficients[0] << "\t" << Ransac_buildings.final_model_coefficients[1] << "\t" << Ransac_buildings.final_model_coefficients[2] << "\t" << Ransac_buildings.final_model_coefficients[3] << "\n";


			if (cont == 1)
			{
				// DRAWS THE ROOFS blue
			   for (int k = 0; k < Ransac_buildings.n_best_inliers_count; k++)
			   {
					int pixel_row = static_cast<int>(buildingS[i](old_final_outliers[Ransac_buildings.final_inliers[k]], 1) /  dem_spacing);
					int pixel_column = static_cast<int>(buildingS[i](old_final_outliers[Ransac_buildings.final_inliers[k]], 0) /  dem_spacing);

					unsigned char r = 0;// unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
					unsigned char g = 0;// unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
					unsigned char b = 255;//unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
		
					roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[0] = b;
					roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[1] = g;
					roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[2] = r;
				}
			}

			if (cont ==2)
			{
				// DRAWS THE ROOFS green
			   for (int k = 0; k < Ransac_buildings.n_best_inliers_count; k++)
			   {
					int pixel_row = static_cast<int>(buildingS[i](old_final_outliers[Ransac_buildings.final_inliers[k]], 1) /  dem_spacing);
					int pixel_column = static_cast<int>(buildingS[i](old_final_outliers[Ransac_buildings.final_inliers[k]], 0) /  dem_spacing);

					unsigned char r = 0;// unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
					unsigned char g = 255;// unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
					unsigned char b = 0;//unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
		
					roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[0] = b;
					roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[1] = g;
					roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[2] = r;
				}
			}

			if (cont ==3)
			{
				// DRAWS THE ROOFS brown
			   for (int k = 0; k < Ransac_buildings.n_best_inliers_count; k++)
			   {
					int pixel_row = static_cast<int>(buildingS[i](old_final_outliers[Ransac_buildings.final_inliers[k]], 1) /  dem_spacing);
					int pixel_column = static_cast<int>(buildingS[i](old_final_outliers[Ransac_buildings.final_inliers[k]], 0) /  dem_spacing);

					unsigned char r = 128;// unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
					unsigned char g = 0;// unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
					unsigned char b = 0;//unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
		
					roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[0] = b;
					roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[1] = g;
					roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[2] = r;
				}
			}

			//if (cont == 4)
			//{
			//	// DRAWS THE ROOFS white
			//   for (int k = 0; k < Ransac_buildings.n_best_inliers_count; k++)
			//   {
			//		int pixel_row = static_cast<int>(buildingS[i](old_final_outliers[Ransac_buildings.final_inliers[k]], 1) /  dem_spacing);
			//		int pixel_column = static_cast<int>(buildingS[i](old_final_outliers[Ransac_buildings.final_inliers[k]], 0) /  dem_spacing);

			//		unsigned char r = 255;// unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
			//		unsigned char g = 255;// unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
			//		unsigned char b = 255;//unsigned char(255 * (rand()/(1.0 + RAND_MAX)));
		
			//		roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[0] = b;
			//		roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[1] = g;
			//		roof_image.at<cv::Vec3b>(pixel_row, pixel_column)[2] = r;
			//	}
			//}


			Ransac_buildings.ransac_msg += "\n"; 
			inliers_so_far += Ransac_buildings.n_best_inliers_count;  
			
		    
		}// fine while
		Ransac_buildings.ransac_msg += "__________________________________________________________________\n";
		//boh_file.close();

		cont_file << i << "\t" << cont << "\n";
	}
	building_file.close();
	cont_file.close();
	cv::imshow("roofs", roof_image);
	
	cv::imwrite(path + "/Results/building_roofs.png", roof_image);
	cv::waitKey(0);
	
	pProgress->updateProgress("All buildings have been processed with RANSAC.", 100, NORMAL);
    pStep->finalize();
	return true;
}
Exemplo n.º 2
0
int DetectorBarcode::Detect() {
    std::cout << "DetectorBarcode::Detect()" << std::endl;

    assert(this->image_.data != NULL);

    bool debugstripecode = true; //@TODO MAKE SURE TO SET ME TO FALSE IN PRODUCTION
    bool useAdaptiveThersholding = true;

    int dpi = 400; //this works well for all scales and sizes..

    Mat matImageK;
    cvtColor(this->image_, matImageK, cv::COLOR_BGR2GRAY);
    cv::Mat matThres;

    // VARIABLES //
    double bar_height_mm_min = 3.7; //[7.5mm=our NMNH c39] [10.7mm=NMNH cover c39]
    double bar_height_mm_max = 20;

    double bar_ar_min = 4;
    double bar_ar_max = 110;

    int min_characters = 5; //minimum characters in barcode string

    double bar_dist_group_mm_max = 9.0; //Maximum distance between any grouped bar to be part of the bar group

    // COMPUTE //
    double bar_height_px_min = bar_height_mm_min/25.4*dpi;
    double bar_height_px_max = bar_height_mm_max/25.4*dpi;

    double bar_area_px_min = bar_height_px_min*(bar_height_px_min*1.0/bar_ar_max);

    //Dont allow the area to be less than 1px row
    bar_area_px_min = bar_area_px_min < bar_height_px_min ? bar_height_px_min : bar_area_px_min;

    double bar_area_px_max = bar_height_px_max*(bar_height_px_max*1.0/bar_ar_min);

    double bar_dist_group_px_max = bar_dist_group_mm_max/25.4*dpi;

    if (useAdaptiveThersholding) {
        //int AT_blocksize = dpi*0.05; 
        int AT_blocksize = bar_height_px_min*0.5;
        int AT_iseven=AT_blocksize%2;
        AT_blocksize += 1+AT_iseven; //Makes sure the blocksize is an even number
        //cout << "AT_blocksize=" << AT_blocksize << endl;
        adaptiveThreshold(matImageK, matThres, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, AT_blocksize, 20);
    } else {
        threshold(matImageK, matThres, 127, 255, THRESH_BINARY_INV);
    }

    if (debugstripecode) {
        //cout << "dpi=" << dpi << endl;
        imwrite("/Users/tzaman/Desktop/bc/matImage.tif", this->image_);
        imwrite("/Users/tzaman/Desktop/bc/matThres.tif", matThres);
    }

    vector< vector<Point> > contours;
    vector<Vec4i> hierarchy;
    findContours( matThres, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE );
    
    //cout << "contours.size()=" << contours.size() << endl;
    
    if (contours.size() == 0) {
        string strErr = "No contours found.";
        cout << strErr << endl;
        return RET_NONE_FOUND;
    }

    //RANSAC vars
    int min_inliers = (min_characters+2)*5*0.75; //+2 (start&stop), *5 (stripes per char), *0.x (margin)
    double max_px_dist = (bar_height_px_min+bar_height_px_max)*0.5*0.05; //Maximum distance from RANSAC line to a point

    vector<RotatedRect> stripeCandidates;
    for(int i = 0; i >= 0; i = hierarchy[i][0] ) {
        double cArea = contourArea( contours[i],false); 
        if (cArea < bar_area_px_min*0.5){
            continue;
        }
        if (cArea > bar_area_px_max){
            continue;
        }
        //cout << "[" << i << "]" << " cArea=" << cArea << endl;

        RotatedRect rotRect= minAreaRect(contours[i]);
        double ar = max(double(rotRect.size.width),double(rotRect.size.height)) / min(double(rotRect.size.width),double(rotRect.size.height));

        if (ar < bar_ar_min){
            continue;
        }
        if (ar > bar_ar_max){
            continue;
        }

        double width = std::min(rotRect.size.width, rotRect.size.height);
        double height = std::max(rotRect.size.width, rotRect.size.height);

        //Check the length
        if (height < bar_height_px_min){
            //Stripe too small
            continue;
        }

        if (height > bar_height_px_max ){
            //Stripe too long
            continue;
        }


        //cout << i << " rotRect: sz=" << rotRect.size << " cp=" << rotRect.center << " a=" << rotRect.angle << " ar=" << ar << endl;


        Rect rCrop = boundingRect(contours[i]);

        //Below parameter is dynamic, plz note
        double min_area_fill=0.15;// = 0.25 ;// 0.4 means 40% of the bounding rectangle of the contour needs to be filled
        //The min_area_fill threshold should be dependent on the width in pixels, because there's more noise in thinner ones
        if (width<3){
            min_area_fill = 0.05;
        } else if (width <5){
            min_area_fill = 0.10;
        }


        //Check if the rectangle is actually filled well
        int fullarea = rCrop.area();

        
        if ( (double(cArea)/double(fullarea)) < min_area_fill){
            continue;
        }

        //cout << i << " fullarea=" << fullarea << " carea=" << cArea << endl;

        if (debugstripecode){
            imwrite("/Users/tzaman/Desktop/seg/" + std::to_string(i) +  ".tif", matImageK(rCrop));
        }
        stripeCandidates.push_back(rotRect);
    }


    if (debugstripecode){
        Mat matBarcodeFull = this->image_.clone();
        for (int j=0; j<stripeCandidates.size(); j++){
            util::rectangle(matBarcodeFull, stripeCandidates[j], cv::Scalar(255,0,0), 2);
        }
        imwrite("/Users/tzaman/Desktop/bc/_candidates.tif", matBarcodeFull);
    }


    //cout << "stripeCandidates.size()=" << stripeCandidates.size() << endl;

    if (stripeCandidates.size() < min_inliers){
        string strErr = "Code 39 did not find enough bars to accurately make a code.";
        cout << strErr << endl;
        return RET_NONE_FOUND;
    }
    
    std::vector<Point> vecPtRectCenter = util::vecrotrect2vecpt(stripeCandidates);
    std::vector<std::vector<int> > vecGroupIdxs = util::groupPoints(vecPtRectCenter, bar_dist_group_px_max, min_inliers);
    //std::vector<std::vector<cv::Point> > vecGroupPts(vecGroupIdxs.size());
    std::vector<std::vector<cv::RotatedRect> > vecGroupRects(vecGroupIdxs.size());

    //Relate indexes to points and add to group vector
    for (int i=0; i<vecGroupIdxs.size(); i++){
        //vecGroupPts[i].resize(vecGroupIdxs[i].size());
        vecGroupRects[i].resize(vecGroupIdxs[i].size());
        for (int j=0; j<vecGroupIdxs[i].size(); j++){
            //cout << i << "," << j << endl;
            //vecGroupPts[i][j] = vecPtRectCenter[vecGroupIdxs[i][j]];
            vecGroupRects[i][j] = stripeCandidates[vecGroupIdxs[i][j]];
        }
    }

    //Draw all groups
    //if(debugstripecode){
    //  for (int i=0; i<vecGroupPts.size(); i++){
    //      Mat matGroup = matImage.clone();
    //      for (int j=0; j<vecGroupPts[i].size(); j++){
    //          circle(matGroup, vecGroupPts[i][j], 5, Scalar(255,0,255), 1, CV_AA,0);              
    //      }
    //      imwrite("/Users/tzaman/Desktop/bc/_group_" + std::to_string(i) + ".tif", matGroup);
    //  }
    //}
    //exit(-1);

    //cout << "vecGroupPts.size()=" << vecGroupPts.size() << endl;
    //Erase small groups
    //for (int i=vecGroupPts.size()-1; i>=0; i--){
    //  if (vecGroupPts[i].size() < min_inliers){
    //      //Skipping group, too small.
    //      vecGroupIdxs.erase(vecGroupIdxs.begin()+i);
    //      vecGroupPts.erase(vecGroupPts.begin()+i);
    //  }
    //}
    //cout << "vecGroupPts.size()=" << vecGroupPts.size() << endl;

    if (vecGroupIdxs.size() == 0) {
        string strErr = "Code 39 failed to ransac bars in a line.";
        cout << strErr << endl;
        return RET_NONE_FOUND;
    }

    //Now cycle over the groups
    vector<vector<int> > vecVecInlierIdx;
    vector<Vec4f> vecLines;
    vector<int> vecFromGroup; //Keeps track of which group the vecvecInlierIdx belongs to
    for (int i = 0; i < vecGroupRects.size(); i++) {
        Ransac(vecGroupRects[i], min_inliers, max_px_dist, vecVecInlierIdx, vecLines, this->image_);
        vecFromGroup.resize(vecVecInlierIdx.size(), i);
    }

    if (vecLines.size() == 0) {
        string strErr = "Code 39 failed to ransac bars in a line.";
        cout << strErr << endl;
        return RET_NONE_FOUND;
    } else {
        //cout << "Code39 ransac succesfull" << endl;
    }

    //for (int i=0; i<vecGroupIdxs.size(); i++){
    //  cout << "Group " << i << " (" << vecGroupIdxs[i].size() << ") : ";
    //  for (int j=0; j<vecGroupIdxs[i].size(); j++){
    //      cout << vecGroupIdxs[i][j] << " ";
    //  }
    //  cout << endl;
    //}


    //Convert back vecVecInlierIdx to original indices
    for (int i=0; i<vecVecInlierIdx.size(); i++){
        //cout << "vecVecInlierIdx[" << i << "] is from group " << vecFromGroup[i] << endl;
        for (int j=0; j<vecVecInlierIdx[i].size(); j++){
            //cout << " " << vecVecInlierIdx[i][j] << " -> " << vecGroupIdxs[vecFromGroup[i]][vecVecInlierIdx[i][j]] << endl;
            vecVecInlierIdx[i][j] = vecGroupIdxs[vecFromGroup[i]][vecVecInlierIdx[i][j]];
        }
    }


    for (int i=0; i < vecLines.size(); i++){
        int numpts = vecVecInlierIdx[i].size();
        cout << "Potential barcode #" << i << " with " << numpts << " points." << endl;

        
        //double angle=atan2(vecLines[i][1],vecLines[i][0])*180/M_PI; //For some reason it clips from [-90,90]
        double angle_rad = atan2(vecLines[i][1],vecLines[i][0]); //For some reason it clips from [-90,90]
        double angle_deg = angle_rad*180.0/M_PI;
        //cout << " angle_deg=" << angle_deg << endl;

        vector<double> bar_heights(numpts);
        vector<double> bar_widths(numpts);
        vector<double> coords_x(numpts);
        //Loop over all found and ransac-verified stripes in this barcode
        vector<cv::RotatedRect> stripesVerified(numpts);
        for (int j=0; j < numpts; j++){
            //cout << vecVecInlierIdx[i][j] << endl;
            //cout << "checking out stripecandidate[" << vecVecInlierIdx[i][j] << "] #" << vecVecInlierIdx[i][j] << endl;
            stripesVerified[j] = stripeCandidates[vecVecInlierIdx[i][j]];
            double dim_smallest = min(stripesVerified[j].size.width, stripesVerified[j].size.height); //For rotation invariance
            double dim_tallest  = max(stripesVerified[j].size.width, stripesVerified[j].size.height); //For rotation invariance
            bar_heights[j] = dim_tallest;
            bar_widths[j]  = dim_smallest;

            //Rotate the points straight
            Point2f ptRot = util::rotatePoint(stripesVerified[j].center, Point(matImageK.cols, matImageK.rows), angle_rad);
            //cout << ptRot << endl;
            coords_x[j]    = ptRot.x;
        }
        
        double height_median = util::calcMedian(bar_heights);
        double width_mean = util::calcMean(bar_widths);
        //cout << "height_median=" << height_median <<" width_mean=" << width_mean << endl;

        //Find the start and end position for reading
        vector<size_t> coords_sorted_index;
        vector<double> coords_x_sorted;
        sort(coords_x, coords_x_sorted, coords_sorted_index);
        //cout << coords_x_sorted[0] << " -> " << coords_x_sorted[coords_x_sorted.size()-1] << endl;

        //Get extrema-stripes
        Point2f pt_stripe_left = stripeCandidates[vecVecInlierIdx[i][coords_sorted_index[0]]].center;
        Point2f pt_stripe_right = stripeCandidates[vecVecInlierIdx[i][coords_sorted_index[coords_sorted_index.size() - 1]]].center;
        //cout << "pt_stripe_left=" << pt_stripe_left << endl;
        //cout << "pt_stripe_right=" << pt_stripe_right << endl;

        Point2f pt_barcode_center = (pt_stripe_left + pt_stripe_right) * 0.5;
        //cout << "pt_barcode_center=" << pt_barcode_center << endl;

        //Calculate width of the barcode
        double barcode_width = util::pointDist(pt_stripe_left, pt_stripe_right);
        //cout << "barcode_width=" << barcode_width << endl;

        //Make the rotated rectangle around the barcode
        cv::RotatedRect rotrect_candidate(pt_barcode_center, Size2f(barcode_width, height_median), angle_deg);

        const double add_width_on_sides_before_decoding = 7.0; // this number will be multiplied by average bar width

        //Add margin (of a few median widths)
        rotrect_candidate.size += Size2f(width_mean * add_width_on_sides_before_decoding, 0);

        const double height_retrainer_for_collapse = 0.25;

        //Extract the barcode itself
        cv::RotatedRect rotrect_candidate_thin = rotrect_candidate;

        //Crop off some margin in thickness because we dont want to collapse the entire barcode.
        if (rotrect_candidate_thin.size.width < rotrect_candidate_thin.size.height) {
            rotrect_candidate_thin.size.width *= height_retrainer_for_collapse;
        } else {
            rotrect_candidate_thin.size.height *= height_retrainer_for_collapse;
        }

        openbarcode::code code_candidate;
        code_candidate.rotrect = rotrect_candidate_thin;

        this->code_candidates_.push_back(code_candidate);
    }

    return RET_SUCCESS;
}