// -*- c-basic-offset: 4 -*- /** @file FindLines.cpp * * @brief functions for finding lines * */ /*************************************************************************** * Copyright (C) 2009 by Tim Nugent * * timnugent@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "vigra/edgedetection.hxx" #include "FindLines.h" #include "FindN8Lines.h" #include #include #include #include #include "algorithms/basic/CalculateCPStatistics.h" using namespace vigra; using namespace std; namespace HuginLines { double resize_image(UInt8RGBImage& in, UInt8RGBImage& out, int resize_dimension) { // Re-size to max dimension double sizefactor=1.0; if (in.width() > resize_dimension || in.height() > resize_dimension) { int nw; int nh; if (in.width() >= in.height()) { sizefactor = (double)resize_dimension/in.width(); // calculate new image size nw = resize_dimension; nh = static_cast(0.5 + (sizefactor*in.height())); } else { sizefactor = (double)resize_dimension/in.height(); // calculate new image size nw = static_cast(0.5 + (sizefactor*in.width())); nh = resize_dimension; } // create an image of appropriate size out.resize(nw, nh); // resize the image, using a bi-cubic spline algorithm resizeImageNoInterpolation(srcImageRange(in),destImageRange(out)); } else { out.resize(in.size()); copyImage(srcImageRange(in),destImage(out)); }; return 1.0/sizefactor; } vigra::BImage* detectEdges(UInt8RGBImage input,double scale,double threshold,unsigned int resize_dimension, double& size_factor) { // Resize image UInt8RGBImage scaled; size_factor=resize_image(input, scaled, resize_dimension); input.resize(0,0); // Convert to greyscale BImage grey(scaled.width(), scaled.height()); copyImage(srcImageRange(scaled, RGBToGrayAccessor >()), destImage(grey)); // Run Canny edge detector BImage* image=new BImage(grey.width(), grey.height(), 255); cannyEdgeImage(srcImageRange(grey), destImage(*image), scale, threshold, 0); return image; }; double calculate_focal_length_pixels(double focal_length,double cropFactor,double width, double height) { double pixels_per_mm = 0; if (cropFactor > 1) { pixels_per_mm= (cropFactor/24.0)* ((width>height)?height:width); } else { pixels_per_mm= (24.0/cropFactor)* ((width>height)?height:width); } return focal_length*pixels_per_mm; } Lines findLines(vigra::BImage& edge, double length_threshold, double focal_length,double crop_factor) { unsigned int longest_dimension=(edge.width() > edge.height()) ? edge.width() : edge.height(); double min_line_length_squared=(length_threshold*longest_dimension)*(length_threshold*longest_dimension); int lmin = int(sqrt(min_line_length_squared)); double flpix=calculate_focal_length_pixels(focal_length,crop_factor,edge.width(),edge.height()); BImage lineImage = edgeMap2linePts(edge); Lines lines; int nlines = linePts2lineList( lineImage, lmin, flpix, lines ); return lines; }; void ScaleLines(Lines& lines,const double scale) { for(unsigned int i=0; i VerticalLineVector; //return footpoint of point p on line between point p1 and p2 vigra::Point2D GetFootpoint(vigra::Point2D p, vigra::Point2D p1, vigra::Point2D p2) { hugin_utils::FDiff2D diff=p2-p1; double u=((p.x-p1.x)*(p2.x-p1.x)+(p.y-p1.y)*(p2.y-p1.y))/hugin_utils::sqr(hugin_utils::norm(diff)); diff*=u; return vigra::Point2D(p1.x+diff.x,p1.y+diff.y); }; //linear fit of given line, returns endpoints of fitted line VerticalLine FitLine(SingleLine line) { size_t n=line.line.size(); VerticalLine vl; double s_x=0; double s_y=0; double s_xy=0; double s_x2=0; for(size_t i=0;i0) { for(Lines::const_iterator it=lines.begin(); it!=lines.end(); it++) { if((*it).status==valid_line && (*it).line.size()>2) { VerticalLine vl=FitLine(*it); vigra::Diff2D diff=vl.end-vl.start; if(diff.magnitude()>20) { if(abs((diff.x*cos(DEG_TO_RAD(roll))+diff.y*sin(DEG_TO_RAD(roll)))/diff.magnitude())<0.1) { vertLines.push_back(vl); }; }; }; }; }; return vertLines; }; //function to sort HuginBase::CPVector by error distance bool SortByError(const HuginBase::ControlPoint& cp1, const HuginBase::ControlPoint& cp2) { return cp1.error(5, 0.0)); remappedImage.deleteAllMasks(); remappedImage.setActive(true); //create PanoramaOptions for remapping of image opts.setProjection(HuginBase::PanoramaOptions::EQUIRECTANGULAR); opts.setWidth(MAX_RESIZE_DIM); opts.outputExposureValue=0; //calculate output canvas size HuginBase::Panorama tempPano; tempPano.addImage(remappedImage); tempPano.setOptions(opts); HuginBase::CalculateFitPanorama fitPano(tempPano); fitPano.run(); opts.setHFOV(fitPano.getResultHorizontalFOV()); opts.setHeight(hugin_utils::roundi(fitPano.getResultHeight())); tempPano.setOptions(opts); //finally remap image HuginBase::Nona::RemappedPanoImage* remapped=new HuginBase::Nona::RemappedPanoImage; AppBase::MultiProgressDisplay* progress=new AppBase::DummyMultiProgressDisplay(); remapped->setPanoImage(remappedImage,opts,opts.getROI()); remapped->remapImage(vigra::srcImageRange(image),vigra_ext::INTERP_CUBIC,*progress); vigra::UInt8RGBImage remappedBitmap=remapped->m_image; //detect edges edge=detectEdges(remappedBitmap,2,4,std::max(remappedBitmap.width(),remappedBitmap.height())+10,size_factor); delete remapped; delete progress; }; //detect lines //we need the focal length double focalLength=srcImage.getExifFocalLength(); if(focalLength==0) { focalLength=HuginBase::SrcPanoImage::calcFocalLength( srcImage.getProjection(),srcImage.getHFOV(),srcImage.getExifCropFactor(),srcImage.getSize()); }; Lines foundLines=findLines(*edge,0.05,focalLength,srcImage.getExifCropFactor()); //filter results VerticalLineVector filteredLines=FilterLines(foundLines,roll); //create control points if(filteredLines.size()>0) { //we need to transform the coordinates to image coordinates because the detection //worked on smaller images or in remapped image HuginBase::PTools::Transform transform; if(needsRemap) { transform.createTransform(remappedImage,opts); }; for(size_t i=0; i=0 && cp.x1=0 && cp.y1=0 && cp.x2=0 && cp.y21) { HuginBase::Panorama tempPano; HuginBase::SrcPanoImage tempImage=pano.getSrcImage(imgNr); tempImage.setYaw(0); tempImage.setPitch(0); tempImage.setRoll(0); tempImage.setX(0); tempImage.setY(0); tempImage.setZ(0); tempPano.addImage(tempImage); for(size_t i=0; i imgopt; imgopt.insert("p"); imgopt.insert("r"); optVec.push_back(imgopt); tempPano.setOptimizeVector(optVec); HuginBase::PTools::optimize(tempPano); //first filter stage //we disregard all lines with big error //calculate statistic and determine limit double minError,maxError,mean,var; HuginBase::CalculateCPStatisticsError::calcCtrlPntsErrorStats(tempPano,minError,maxError,mean,var); detectedLines=tempPano.getCtrlPoints(); double limit=mean+sqrt(var); maxError=0; for(int i=detectedLines.size()-1; i>=0; i--) { if(detectedLines[i].error>limit) { detectedLines.erase(detectedLines.begin()+i); } else { //we need the max error of the remaining lines for the next step maxError=std::max(detectedLines[i].error,maxError); }; }; if(detectedLines.size()>0 && maxError>0) //security check, should never be false { //now keep only the best nrLines lines //we are using error and line length as figure of merrit for(size_t i=0;i