├── CMakeLists.txt ├── README.md ├── README.md~ ├── data ├── 1.jpg ├── 2.jpg └── result.jpg └── src ├── IO.cpp ├── IO.h ├── LineMatching.cpp ├── LineMatching.h ├── MatOperation.cpp ├── MatOperation.h ├── PartiallyRecoverConnectivity.cpp ├── PartiallyRecoverConnectivity.h ├── Timer.h ├── lsd.c ├── lsd.h └── main.cpp /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 2 | SET(PROJECT_NAME LSM) 3 | PROJECT(${PROJECT_NAME}) 4 | FIND_PACKAGE(OpenCV REQUIRED) 5 | INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) 6 | MESSAGE(STATUS "Project: ${PROJECT_NAME}") 7 | MESSAGE(STATUS "OpenCV library status:") 8 | MESSAGE(STATUS " version: ${OpenCV_VERSION}") 9 | MESSAGE(STATUS " libraries: ${OpenCV_LIBS}") 10 | MESSAGE(STATUS " include path: ${OpenCV_INCLUDE_DIRS}") 11 | AUX_SOURCE_DIRECTORY(src DIR_SRCS) 12 | MESSAGE(STATUS "Src file: ${DIR_SRCS}") 13 | ADD_EXECUTABLE(${PROJECT_NAME} ${DIR_SRCS}) 14 | TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${OpenCV_LIBS}) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Hierarchical line matching based on Line-Junction-Line structure descriptor and local homography estimation 3 | 4 | 5 | ![Demo](./data/result.jpg "result") 6 | 7 | The code is based on the following two papers. Please cite them if you find the code useful for your research. Please contact Kai Li (li.kai.gml@gmail.com) if you have any question, or you are going to use the code for purpose beyond research. 8 | 9 | @article{li2016hierarchical, 10 | title={Hierarchical line matching based on Line--Junction--Line structure descriptor and local homography estimation}, 11 | author={Li, Kai and Yao, Jian and Lu, Xiaohu and Li, Li and Zhang, Zhichao}, 12 | journal={Neurocomputing}, 13 | volume={184}, 14 | pages={207--220}, 15 | year={2016}, 16 | publisher={Elsevier} 17 | } 18 | 19 | @inproceedings{li2014robust, 20 | title={Robust line matching based on ray-point-ray structure descriptor}, 21 | author={Li, Kai and Yao, Jian and Lu, Xiaohu}, 22 | booktitle={Asian Conference on Computer Vision}, 23 | pages={554--569}, 24 | year={2014}, 25 | organization={Springer} 26 | } 27 | 28 | ## Dependecy 29 | 30 | The code is based on OpenCV (2.*), and requires CMake for compilation. The code has been tested in both Windows and Linux. 31 | Should you meet any problem in compiling the code, feel free to contact with Kai Li (li.kai.gml@gmail.com). 32 | 33 | ## Demo 34 | 35 | ./LSM ./data/1.jpg ./data/2.jpg 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /README.md~: -------------------------------------------------------------------------------- 1 | 2 | # Hierarchical line matching based on Line-Junction-Line structure descriptor and local homography estimation 3 | 4 | 5 | ![Demo](./data/result.jpg "result") 6 | 7 | The code is based on the following two papers. Please cite them if you find the code useful for your research. Please contact Kai Li (li.kai.gml@gmail.com) if you have any question, or you are going to use the code for purpose beyond research. 8 | 9 | @article{li2016hierarchical, 10 | title={Hierarchical line matching based on Line--Junction--Line structure descriptor and local homography estimation}, 11 | author={Li, Kai and Yao, Jian and Lu, Xiaohu and Li, Li and Zhang, Zhichao}, 12 | journal={Neurocomputing}, 13 | volume={184}, 14 | pages={207--220}, 15 | year={2016}, 16 | publisher={Elsevier} 17 | } 18 | 19 | @inproceedings{li2014robust, 20 | title={Robust line matching based on ray-point-ray structure descriptor}, 21 | author={Li, Kai and Yao, Jian and Lu, Xiaohu}, 22 | booktitle={Asian Conference on Computer Vision}, 23 | pages={554--569}, 24 | year={2014}, 25 | organization={Springer} 26 | } 27 | 28 | ## Dependecy 29 | 30 | The code is based on OpenCV (2.*), and you need CMake to build a project from it. The code has been tested in both Windows and Linux. 31 | Should you meet any problem in compiling the code, feel free to contact with Kai Li (li.kai.gml@gmail.com) 32 | 33 | ## Demo 34 | 35 | ./LSM ./data/1.jpg ./data/2.jpg 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /data/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailigo/LineSegmentMatching/459f15269b3d01785721c09ff235a7177f0bc426/data/1.jpg -------------------------------------------------------------------------------- /data/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailigo/LineSegmentMatching/459f15269b3d01785721c09ff235a7177f0bc426/data/2.jpg -------------------------------------------------------------------------------- /data/result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailigo/LineSegmentMatching/459f15269b3d01785721c09ff235a7177f0bc426/data/result.jpg -------------------------------------------------------------------------------- /src/IO.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailigo/LineSegmentMatching/459f15269b3d01785721c09ff235a7177f0bc426/src/IO.cpp -------------------------------------------------------------------------------- /src/IO.h: -------------------------------------------------------------------------------- 1 | #ifndef IO_H 2 | #define IO_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | using namespace cv; 10 | using namespace std; 11 | 12 | class CIO 13 | { 14 | public: 15 | CIO(); 16 | ~CIO(); 17 | int writeData(string fileName, cv::Mat& matData); 18 | int loadData(string fileName, cv::Mat& matData, int matRows = 0, int matCols = 0, int matChns = 0); 19 | }; 20 | #endif // IO_H -------------------------------------------------------------------------------- /src/LineMatching.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kailigo/LineSegmentMatching/459f15269b3d01785721c09ff235a7177f0bc426/src/LineMatching.cpp -------------------------------------------------------------------------------- /src/LineMatching.h: -------------------------------------------------------------------------------- 1 | #ifndef LINEMATCHING_H 2 | #define LINEMATCHING_H 3 | #include "cv.h" 4 | #include 5 | #include "Timer.h" 6 | #include 7 | using namespace cv; 8 | using namespace std; 9 | ////////////////////////////////////////////////////////// 10 | 11 | struct strLine 12 | { 13 | Point2f ps; 14 | Point2f pe; 15 | double direction; 16 | bool intensityDir; 17 | double intenRatio; 18 | int serial; 19 | //bool isMatched; 20 | }; 21 | 22 | struct strBranch 23 | { 24 | Point2f bpt; 25 | double ang; 26 | int lineSerial; 27 | }; 28 | 29 | struct strFanSection 30 | { 31 | Point2f intsection; 32 | int nodeSerail; 33 | strBranch strbranch1, strbranch2; 34 | Mat mDes; 35 | int fanSerial; 36 | //bool isMatched; 37 | }; 38 | 39 | struct strFanMatch 40 | { 41 | int fserial1; 42 | int fserial2; 43 | float dist; 44 | }; 45 | 46 | struct strLineMatch 47 | { 48 | int serLine1; 49 | int serLine2; 50 | float dist; 51 | }; 52 | 53 | struct strPointMatch 54 | { 55 | Point2f point1; 56 | Point2f point2; 57 | }; 58 | 59 | class CLineMatching 60 | { 61 | private: 62 | Mat colorImg1, colorImg2; 63 | Mat nodes1, nodes2; 64 | strLine* strline1, *strline2; 65 | vector vstrLineMatch; 66 | vector vstrFanMatch; 67 | vector vstrPointMatch; 68 | vector vstrFanSection1, vstrFanSection2; 69 | int nline1, nline2; 70 | Mat gMag1, gDir1, gMag2, gDir2; 71 | Mat FMat; 72 | int _nAvgDescriptorDistance; 73 | bool _isTwoLineHomog; 74 | float _donwSampleRatio; 75 | int _nOctave; 76 | int _nOctaveLayer; 77 | float _fmatThr; 78 | float _hmatThr; 79 | int _nNeighborPts; 80 | float _nEnterGroups; 81 | float _desDistThrEpi; 82 | float _rotAngThr; 83 | float _sameSideRatio; 84 | float _regionHeight; 85 | float _junctionDistThr; 86 | float _intensityProfileWidth; 87 | float _radiusPointMatchUnique; 88 | float _difAngThr; 89 | float _rcircle; 90 | bool sflag; 91 | float _truncateThr; 92 | float _fanThr; 93 | string _outFileName; 94 | 95 | void extendIntensityValue(Mat &src1, Mat &src2); 96 | void initialize_providedJunctions(Mat img, strLine* strline, int nline, Mat &node, vector &vstrFanSection); 97 | void initialize_selfGenJunctions(Mat img, strLine* strline, int nline, Mat &node, vector &vstrFanSection); 98 | void intensityDirection(Mat img, strLine* strline, int nline); 99 | void buildGaussianPyramid( Mat& base, vector& pyr, int nOctaves, int nOctaveLayers, float downsampleRatio); 100 | void calcGrad(Mat img, Mat &gMag, Mat &gDir); 101 | 102 | void description(vector &strFanSect, Mat gMag, Mat gDir, bool isOutputDesMat, Mat& desMat); 103 | void description(vector &strFanSect, Mat gMag, Mat gDir); 104 | 105 | void description_fans(vector &vstrFanSection, Mat gMag, Mat gDir, bool isOutputDesMat, Mat& desMat); 106 | void description_fans(vector &vstrFanSection, Mat gMag, Mat gDir); 107 | 108 | 109 | void description(Mat pts, Mat gMag, Mat gDir, Mat &descriptors); 110 | 111 | void description_fanPts(Mat pts, Mat gMag, Mat gDir, Mat &descriptors); 112 | void description_singleFan(Vec4f pt, Mat gMag, Mat gDir, Mat &des); 113 | void describeSingleLine(Vec3f pt, Mat gMag, Mat gDir, Mat &des); 114 | void description_sift(vector &vstrFanSection, Mat gMag, Mat gDir); 115 | void description_sift_single(Vec3f pt, const Mat gMag, const Mat gDir, Mat &des); 116 | void fanMatch(vector v1, vector v2, float distThr, vector &vFanMatch); 117 | void plotPointMatches(Mat colorImg1, Mat colorImg2, vector vstrPointMatch, string imgName); 118 | void concatenateTwoImgs(Mat img1, Mat img2, Mat &outImg); 119 | void findRobustFundamentalMat(vector &PointSet1,vector &PointSet2, Mat &FMat, bool *pbIsKept); 120 | void getPointsonPolarline(vector &PointSet1,vector &PointSet2,Mat_ F,double T, bool *pbIsKept); 121 | void drawEpipolarline(Mat image1, Mat image2,vector pointSet1,vector pointSet2,Mat_ Fmatrix); 122 | void plotLineMatches(Mat img1, Mat img2, vector vStrLineMatch, string imgName); 123 | void sortrows(Mat inMat, Mat &outMat, Mat &sortedComIdx, int primiaryKey, int secondaryKey); 124 | 125 | void uniqueChk(Mat inMat, Vec3i regulation, vector &vKeptIdx, Mat &outMat); 126 | void uniqueChk(Mat inMat, Vec3i regulation, vector &vKeptIdx); 127 | 128 | 129 | int descriptorsMatching(Mat pts1, Mat pts2, Mat des1, Mat des2, float distThr, vector &vFanMatch); 130 | void updatePointMatchFromFanMatches(); 131 | bool isAcceptedToBePointMatch(Vec3f pt1, Vec3f pt2, float desDistThr, float fDistThr, float &desDist); 132 | void getResiduauls(Mat pts1, Mat pts2, Mat FMat, Mat_ &residuals); 133 | void unique(Mat mat, vector &keptIdx); 134 | void uniquePointMatch(vector &vstrPointMatch) ; 135 | void topoFilter(vector &vstrFanMatch, vector vstrPointMatch); 136 | void vpointMatch2Mat(vector tvstrPointMatch, Mat &outMat); 137 | int determinePtLinePos(Point2f pts, Mat_ pline, float refDirection); 138 | float distPt2Line(Point2f pt, Mat_ pline); 139 | void addFansMatch(float desDistThr, float fDistThr); 140 | void groupFans(vector vMatchedFans, vector vUnmatchedFans, int nEnterCluster, 141 | vector< vector > > &vGroupedUnjunc); 142 | bool isConsistentWithFMat(Point2f pt1, Point2f pt2, Mat FMat, float FDistThr); 143 | bool isConsistentWithNeighborPointMatches(strFanSection strFan1, strFanSection strFan2, Mat pointMatch); 144 | void matchSingleLines(float desDistThr, float fDistThr); 145 | void updatePointMatchFromSingleLineMatch(vector vStrFanMatch, vector vStrLineMatch, Mat FMat, vector &vPointMatches); 146 | void nearestMatchedPointsToLine(vector vUnmatchedLines, Mat matchedPoints, int nNearestMatchedPts, Mat &nearestPts); 147 | void adjustLineMatchEndpoints(vector vStrLineMatch); 148 | void groupSingleLines(vector vUnmatchedLines, vector vMatchedFans, int nEnterGroup, vector< vector< vector > > &vEnteredSingleLines); 149 | void bifurcateFans(vector vStrFan1, vector vStrFan2, vector vStrFanMatch, 150 | vector &vMatchedFans1, vector &vMatchedFans2, vector &vUnmatchedFans1, vector &vUnmatchedFans2); 151 | void bifurcateLines(strLine* vStrLine1, strLine* vStrLine2, vector vStrLineMatch, vector &vMatchedLines1, vector &vMatchedLines2, vector &vUnmatchedLines1, vector &vUnmatchedLines2); 152 | float estimateLocalRotateAngle(strFanSection strFan1, strFanSection strFan2); 153 | bool topoFilter_singleLine(strLine curLine1, strLine curLine2, Mat mNPts1, Mat mNPts2, Mat pointMatches); 154 | void formAndMatchNewFans(strLine line1, strLine line2, strFanSection fan1, strFanSection fan2, Mat pointMatches, Mat FMat, 155 | strFanSection &tstrFan1, strFanSection &tstrFan2, float &desDist, float desDistThr, float fDistThr); 156 | Point2f intersectionOfLines(Point2f X1, Point2f Y1, Point2f X2, Point2f Y2); 157 | void formNewFanPair(Point2f intersection1, Point2f intersection2, Point2f junction1, Point2f junction2, Point2f endpt1, Point2f endpt2, strFanSection &tstrFan1, strFanSection &tstrFan2); 158 | float matchNewlyFormedFanPair(strFanSection tstrFan, strFanSection tstrFan2, Mat pointMatches, Mat FMat, float desDistThr, float fDistThr); 159 | void intersectWithOnePairBranches(strLine line1, strLine line2, Vec4f branch1, Vec4f branch2, Mat pointMatches, Mat FMat, strFanSection &tstrFan1, strFanSection &tstrFan2, float &desDist, float desDistThr, float fDistThr); 160 | Mat vStrFanMatch2Mat(vector vStrFanMatch); 161 | void uniqueFanMatch(vector &vStrFanMatch); 162 | Mat vStrLineMatch2Mat(vector vStrLinrMatch); 163 | void uniqueLineMatch(vector &vStrLineMatch, vector &keptIdx); 164 | void fanMatch2LineMatch(vector vStrFanMatch, vector &vLineMatch ); 165 | void topoFilterLine(vector &vStrFanMatch, vector vStrPointMatch, vector &vStrLineMatch); 166 | bool isConsistentWithLocalRotateAngle(strFanSection fan1, strFanSection fan2, float rotAngle); 167 | void nearestMatchedPtsToFan(vector vFans, Mat pointMatch, int nNearPts, Mat &NearestMPts); 168 | bool isCurPairFanConsistentWithNearMPts(strFanSection fan1, strFanSection fan2, Mat nMPts1, Mat nMPts2, Mat pointMatch); 169 | void affineTransformEstimation(Mat &img1, Mat &node1, strLine* strline1, vector &vStrFanSect1, Mat &img2, Mat &node2, 170 | strLine* strline2, vector &vStrFanSect2, vector &vFanMatch); 171 | void buildZoomPyramid( Mat& base, vector& pyr, int nOctaves, float downsampleRatio); 172 | void matchAffineSequence(Mat oriImg, vector oriFan, Mat oriDes, Mat oriPts, Mat transImg, vector transFan, Mat transPts, 173 | vector &maxvMatches, Mat &maxGMag, Mat &maxGDir, vector &maxvPyrFan, Mat &maxTransMat); 174 | void matchSingleLines_homography(float homDistThr); 175 | void calcLocalHomography(strFanSection strfan1, strFanSection strfan2, Mat fmat, Mat &hmat); 176 | void homoLines(Mat inLines, Mat &hoLines); 177 | void calcCorrespondence(Mat pts, Mat lines, Mat fmat, Mat &corPts); 178 | void intersectionOf2Lines_homo(Mat Line1, Mat Line2, Mat &intpt); 179 | void hmat_fmatOnePtAndOneLine(Mat fmat, Mat line1, Mat line2, Mat pt1, Mat pt2, Mat &hmat); 180 | void get_asift_description(Mat img, Mat pts, vector &vDes); 181 | float descriptorDistance(Mat des1, Mat des2, int ncand = 1); 182 | bool isTwoLineOverlap(Point2f pt1, Point2f pt2, Point2f pt3, Point2f pt4, float &dist, float radius=5); 183 | bool isConsistentWithHMat(Point2f pt1, Point2f pt2, Mat hmat, float hDistThr); 184 | void calcLocalHomography_2lineFMat(strFanSection strfan1, strFanSection strfan2, Mat fmat, Mat &hmat); 185 | void lineMatches2Mat(Mat &mline); 186 | bool isLineIntersectRectangle(float linePointX1, float linePointY1, float linePointX2, float linePointY2, float rectangleLeftTopX, 187 | float rectangleLeftTopY, float rectangleRightBottomX, float rectangleRightBottomY); 188 | float distPairPt2HMat(Point2f pt1, Point2f pt2, Mat hmat); 189 | void descriptorEvaluationUniquePtMatches(); 190 | Mat genContinuousMat(int _floor, int _ceil, int step = 1) 191 | { 192 | Mat outMat; 193 | for (int i = _floor; i < _ceil; i += step) 194 | { 195 | outMat.push_back(i); 196 | } 197 | return outMat.t(); 198 | } 199 | float fast_mod(float th) 200 | { 201 | while(th < 0) th += 2*CV_PI ; 202 | while(th > 2*CV_PI) th -= 2*CV_PI ; 203 | return th ; 204 | } 205 | int sign(float fval) 206 | { 207 | if(fval > 0) 208 | return 1; 209 | else if(fval < 0) 210 | return -1; 211 | else 212 | return 0; 213 | } 214 | 215 | public: 216 | CLineMatching(); 217 | CLineMatching(Mat rimg, Mat rline, Mat rnode, Mat qimg, Mat qline, Mat qnode, Mat colorImg1, Mat colorImg2, Mat &mlines, 218 | bool isVerb, bool isBuildingImagePyramids, float nAvgDesDist, bool isProvidedJunctions, bool isTwoLineHomog, 219 | int nOctave, int nOctaveLayer, float desDistThr, float fmatThr, float fmatDesThrProg, float hmatThr, int nNeighborPts, int nEnterGroup, 220 | float rotAngleThr, float sameSideRatio, float regionHeight, float junctionDistThr, float intensityProfileWidth, float radiusPointMatchUnique, float difAngThr, 221 | float rcircle, float truncateThr, float fanThr, string outFileName); 222 | ~CLineMatching(); 223 | }; 224 | 225 | #endif // LINEMATCHING_H 226 | -------------------------------------------------------------------------------- /src/MatOperation.cpp: -------------------------------------------------------------------------------- 1 | #include "MatOperation.h" 2 | 3 | CMatOperation::CMatOperation() 4 | { 5 | 6 | }; 7 | 8 | CMatOperation::~CMatOperation() 9 | { 10 | 11 | }; 12 | 13 | void CMatOperation::all(Mat src, bool *pSign) 14 | { 15 | int rows = src.rows; 16 | int cols = src.cols; 17 | for (int i = 0; i < rows; i++) 18 | { 19 | float* pdat = src.ptr(i); 20 | pSign[i] = 1; 21 | for (int j = 0; j(i); 39 | pSign[i] = 0; 40 | for (int j = 0; j v1, vector v2, vector &vout, vector &keepIdx) 54 | { 55 | int cols1 = v1.size(); 56 | int cols2 = v2.size(); 57 | for (int i = 0; i < cols1; i++) 58 | { 59 | float val1 = v1[i]; 60 | for (int j = 0; j < cols2; j++) 61 | { 62 | float val2 = v2[j]; 63 | if (val1 == val2) 64 | { 65 | vout.push_back(val1); 66 | keepIdx.push_back(i); 67 | break; 68 | } 69 | } 70 | } 71 | } 72 | 73 | void CMatOperation::uniqueVector(vector inVect, vector &outVect, vector &keepIdx) 74 | { 75 | vector sortedVal; 76 | vector sortedIdx; 77 | cv::sort(inVect, sortedVal, SORT_EVERY_ROW); 78 | sortIdx(inVect, sortedIdx, SORT_EVERY_ROW); 79 | 80 | //int *pdat1 = sortedIdx.ptr(0); 81 | //float *pdat2 = sortedVal.ptr(0); 82 | keepIdx.push_back(sortedIdx[0]); 83 | outVect.push_back(sortedVal[0]); 84 | int nelem = inVect.size(); 85 | for (int i = 1; i < nelem; i++) 86 | { 87 | if (sortedVal[i] != sortedVal[i-1]) 88 | { 89 | keepIdx.push_back(sortedIdx[i]); 90 | outVect.push_back(sortedVal[i]); 91 | } 92 | } 93 | } 94 | 95 | void CMatOperation::unionSet(vector v1, vector v2, vector &vout) 96 | { 97 | int cols1 = v1.size(); 98 | int cols2 = v2.size(); 99 | 100 | for (int i = 0; i < cols2; i++) 101 | { 102 | v1.push_back(v2[i]); 103 | } 104 | vector vtmp; 105 | uniqueVector(v1, vout, vtmp); 106 | } 107 | 108 | void CMatOperation::unique(Mat mat, Mat &outMat, vector &keptIdx) 109 | { 110 | outMat.release(); 111 | Mat tmat, sortedIdx; 112 | sortrows(mat, tmat, sortedIdx, 0, 1); 113 | int rows = tmat.rows; 114 | int* pdat = sortedIdx.ptr(0); 115 | keptIdx.push_back(pdat[0]); 116 | bool *pSign = new bool[rows]; 117 | for (int i = 1; i < rows; i++) 118 | { 119 | Mat matSub = tmat.row(i) - tmat.row(i-1); 120 | any(matSub, pSign); 121 | if ( pSign[0] ) 122 | { 123 | keptIdx.push_back(pdat[i]); 124 | outMat.push_back(tmat.row(i)); 125 | } 126 | } 127 | } 128 | 129 | 130 | void CMatOperation::sortrows(Mat inMat, Mat &outMat, Mat &sortedComIdx, int primiaryKey, int secondaryKey) 131 | { 132 | int rows = inMat.rows; 133 | Mat primColumn = inMat.col(primiaryKey).clone(); 134 | Mat secColumn = inMat.col(secondaryKey).clone(); 135 | Mat comCol = primColumn + 0.01*secColumn; 136 | sortIdx(comCol, sortedComIdx, CV_SORT_EVERY_COLUMN); 137 | 138 | outMat = Mat(inMat.size(), inMat.type()); 139 | int nrows = outMat.rows; 140 | for (int i = 0; i < nrows; i++) 141 | { 142 | int *pSer = sortedComIdx.ptr(0); 143 | inMat.row(pSer[i]).copyTo(outMat.row(i)); 144 | } 145 | } 146 | 147 | 148 | ////////////////////////////////////////////////////////////////////////// 149 | 150 | void CMatOperation::intersectSet(Mat v1, Mat v2, Mat &vout, Mat &keepIdx) 151 | { 152 | int nelem1 = v1.cols; 153 | int nelem2 = v2.cols; 154 | int* pdat1 = v1.ptr(0); 155 | int* pdat2 = v2.ptr(0); 156 | 157 | for (int i = 0; i < nelem1; i++) 158 | { 159 | float val1 = pdat1[i]; 160 | for (int j = 0; j < nelem2; j++) 161 | { 162 | float val2 = pdat2[j]; 163 | if (val1 == val2) 164 | { 165 | vout.push_back(val1); 166 | keepIdx.push_back(i); 167 | break; 168 | } 169 | } 170 | } 171 | } 172 | 173 | void CMatOperation::unionSet(Mat v1, Mat v2, Mat &vout) 174 | { 175 | int nelem1 = v1.cols; 176 | int nelem2 = v2.cols; 177 | int* pdat1 = v1.ptr(0); 178 | int* pdat2 = v2.ptr(0); 179 | Mat tv1 = v1.t(); 180 | 181 | for (int i = 0; i < nelem2; i++) 182 | { 183 | tv1.push_back(pdat2[i]); 184 | } 185 | Mat keptIdx; 186 | uniqueVector(tv1.t(), vout, keptIdx); 187 | } 188 | 189 | void CMatOperation::uniqueVector(Mat inMat, Mat &outMat, Mat &keepIdx) 190 | { 191 | Mat sortedVal, sortedIdx; 192 | cv::sort(inMat, sortedVal, SORT_EVERY_ROW); 193 | sortIdx(inMat, sortedIdx, SORT_EVERY_ROW); 194 | 195 | int* pdat1 = sortedIdx.ptr(0); 196 | int* pdat2 = sortedVal.ptr(0); 197 | 198 | keepIdx.push_back(pdat1[0]); 199 | outMat.push_back(pdat2[0]); 200 | int nelem = inMat.cols; 201 | for (int i = 1; i < nelem; i++) 202 | { 203 | if (pdat2[i] != pdat2[i-1]) 204 | { 205 | keepIdx.push_back(pdat1[i]); 206 | outMat.push_back(pdat2[i]); 207 | } 208 | } 209 | keepIdx = keepIdx.t(); 210 | outMat = outMat.t(); 211 | } 212 | 213 | Mat CMatOperation::genContinuousMat(int _floor, int _ceil, int step) 214 | { 215 | Mat outMat; 216 | for (int i = _floor; i < _ceil; i += step) 217 | { 218 | outMat.push_back(i); 219 | } 220 | return outMat.t(); 221 | } 222 | 223 | 224 | 225 | void CMatOperation::diffSet(vector v1, vector v2, vector &vout, vector &keepIdx) 226 | { 227 | int cols1 = v1.size(); 228 | int cols2 = v2.size(); 229 | vout = v1; 230 | int num = 0; 231 | for (int i = 0; i < cols1; i++) 232 | { 233 | bool flag = 1; 234 | float val1 = v1[i]; 235 | for(int j = 0; j < cols2; j++) 236 | { 237 | float val2 = v2[j]; 238 | if (val1 == val2) 239 | { 240 | vout.erase(vout.begin()+(i-num)); 241 | num++; 242 | flag = 0; 243 | break; 244 | } 245 | } 246 | if (flag) 247 | { 248 | keepIdx.push_back(i); 249 | } 250 | } 251 | } 252 | 253 | 254 | void CMatOperation::diffSet(Mat src1, Mat src2, Mat &dst, Mat &keptIdx) 255 | { 256 | int cols1 = src1.cols; 257 | int cols2 = src2.cols; 258 | int num = 0; 259 | int *pdat1 = src1.ptr(0); 260 | int *pdat2 = src2.ptr(0); 261 | for (int i = 0; i < cols1; i++) 262 | { 263 | bool flag = 1; 264 | int val1 = pdat1[i]; 265 | for(int j = 0; j < cols2; j++) 266 | { 267 | int val2 = pdat2[j]; 268 | if (val1 == val2) 269 | { 270 | flag = 0; 271 | break; 272 | } 273 | } 274 | if (flag) 275 | { 276 | keptIdx.push_back(i); 277 | dst.push_back(val1); 278 | } 279 | } 280 | keptIdx = keptIdx.t(); 281 | dst = dst.t(); 282 | } 283 | 284 | void CMatOperation::diffSet(Mat src1, Mat src2, Mat &dst) 285 | { 286 | int cols1 = src1.cols; 287 | int cols2 = src2.cols; 288 | int num = 0; 289 | int *pdat1 = src1.ptr(0); 290 | int *pdat2 = src2.ptr(0); 291 | for (int i = 0; i < cols1; i++) 292 | { 293 | bool flag = 1; 294 | int val1 = pdat1[i]; 295 | for(int j = 0; j < cols2; j++) 296 | { 297 | int val2 = pdat2[j]; 298 | if (val1 == val2) 299 | { 300 | flag = 0; 301 | break; 302 | } 303 | } 304 | if (flag) 305 | { 306 | // keptIdx.push_back(i); 307 | dst.push_back(val1); 308 | } 309 | } 310 | // keptIdx = keptIdx.t(); 311 | dst = dst.t(); 312 | } 313 | 314 | 315 | 316 | 317 | 318 | void CMatOperation::eraseRows(Mat src, Mat eRows, Mat dst) 319 | { 320 | cv::sort(eRows, eRows, CV_SORT_EVERY_ROW); 321 | int nerows = eRows.cols; 322 | int *pdat = eRows.ptr(0); 323 | int num = 0; 324 | for (int i = 0; i < nerows; i++) 325 | { 326 | int ser = pdat[i] - num; 327 | eraseRow(src, ser, dst); 328 | num++; 329 | } 330 | } 331 | 332 | void CMatOperation::eraseRow(Mat src, int n, Mat &dst) 333 | { 334 | if (n = src.cols) 335 | { 336 | src.pop_back(); 337 | dst = src; 338 | return; 339 | } 340 | int rows = src.rows; 341 | int cols = src.cols; 342 | dst.create(rows-1, cols, src.type()); 343 | src.colRange(0, n).copyTo(dst.colRange(0, n)); 344 | src.colRange(n+1, cols).copyTo(dst.colRange(n, cols)); 345 | } -------------------------------------------------------------------------------- /src/MatOperation.h: -------------------------------------------------------------------------------- 1 | #ifndef MATOPERATION_H 2 | #define MATOPERATION_H 3 | 4 | #include 5 | #include 6 | #include 7 | using namespace cv; 8 | using namespace std; 9 | 10 | //template 11 | 12 | class CMatOperation 13 | { 14 | public: 15 | CMatOperation(); 16 | ~CMatOperation(); 17 | void all(Mat inMat, bool *pSign); 18 | void any(Mat inMat, bool *pSign); 19 | void intersectSet(vector v1, vector v2, vector &vout, vector &keepIdx); 20 | void intersectSet(Mat v1, Mat v2, Mat &vout, Mat &keepIdx); 21 | void unionSet(Mat v1, Mat v2, Mat &vout); 22 | void unionSet(vector v1, vector v2, vector &vout); 23 | void unique(Mat mat, Mat &outMat, vector &keptIdx); 24 | void sortrows(Mat inMat, Mat &outMat, Mat &sortedComIdx, int primiaryKey, int secondaryKey); 25 | void uniqueVector(vector inVect, vector &outVect, vector &keepIdx); 26 | void uniqueVector(Mat inVect, Mat &outMat, Mat &keepIdx); 27 | 28 | void diffSet(vector v1, vector v2, vector &vout, vector &keepIdx); 29 | void diffSet(Mat src1, Mat src2, Mat &dst, Mat &keptIdx); 30 | void diffSet(Mat src1, Mat src2, Mat &dst); 31 | 32 | 33 | Mat genContinuousMat(int _floor, int _ceil, int step = 1); 34 | void eraseRow(Mat src, int n, Mat &dst); 35 | void eraseRows(Mat src, Mat eRows, Mat dst); 36 | }; 37 | #endif // MATOPERATION_H -------------------------------------------------------------------------------- /src/PartiallyRecoverConnectivity.cpp: -------------------------------------------------------------------------------- 1 | #include "PartiallyRecoverConnectivity.h" 2 | #include "IO.h" 3 | #include "MatOperation.h" 4 | CPartiallyRecoverConnectivity::CPartiallyRecoverConnectivity() 5 | { 6 | 7 | } 8 | 9 | CPartiallyRecoverConnectivity::~CPartiallyRecoverConnectivity() 10 | { 11 | 12 | } 13 | 14 | CPartiallyRecoverConnectivity::CPartiallyRecoverConnectivity(Mat mLines, float radius, Mat &fans, Mat img, float fanThr) 15 | { 16 | gImg = img; 17 | int rows = mLines.rows; 18 | int cols = mLines.cols; 19 | int imgRows = img.rows; 20 | int imgCols = img.cols; 21 | RotatedRect rotRect; 22 | 23 | Mat mPts; 24 | mLines.colRange(0, 2).copyTo(mPts); 25 | mPts.push_back(mLines.colRange(2,4).clone()); 26 | CMatOperation *pMatOperation = new CMatOperation; 27 | 28 | ///vector vPts; 29 | for (int i = 0; i < rows; i++) 30 | { 31 | float *pdat = mLines.ptr(i); 32 | Point2f cenpt; 33 | cenpt.x =( pdat[0] + pdat[2] ) / 2; 34 | cenpt.y =( pdat[1] + pdat[3] ) / 2; 35 | float dy = pdat[3] - pdat[1]; 36 | float dx = pdat[2] - pdat[0]; 37 | float degAng = fastAtan2(dy, dx); 38 | float arcAng = degAng / 180 * CV_PI; 39 | float length = abs(tan(arcAng)) > 1 ? abs(dy) : abs(dx); 40 | CvSize tsize; 41 | tsize.height = radius * 2; 42 | tsize.width = length + 2 * radius; 43 | rotRect = RotatedRect(cenpt, tsize, degAng); 44 | vector vDropInPts; 45 | ptsDropInRotatedRect(mPts, rotRect, vDropInPts); 46 | 47 | int nKeptPt = vDropInPts.size(); 48 | for (int j = 0; j < nKeptPt; j++ ) 49 | { 50 | int curSer = vDropInPts[j] >= rows ? vDropInPts[j] - rows : vDropInPts[j]; 51 | if (curSer == i) 52 | continue; 53 | 54 | float *pdat1 = mLines.ptr(curSer); 55 | 56 | float dy1 = pdat1[3] - pdat1[1]; 57 | float dx1 = pdat1[2] - pdat1[0]; 58 | float degAng1 = fastAtan2(dy1, dx1); 59 | float arcAng1 = degAng1 / 180 * CV_PI; 60 | float tmp = CV_PI; 61 | float tmpa = fmod( abs(arcAng-arcAng1), tmp); 62 | if(tmpa < fanThr || CV_PI - tmpa < fanThr) 63 | continue; 64 | 65 | Point2f intpt; 66 | intersectionOfLines(Point2f(pdat[0], pdat[1]), Point2f(pdat[2], pdat[3]), Point2f(pdat1[0], pdat1[1]), Point2f(pdat1[2], pdat1[3]), intpt/*rLoc*/); 67 | 68 | if (isPtInRotatedRect(intpt, rotRect) && (intpt.x >= 4 && intpt.x < imgCols-4 && intpt.y >= 4 && intpt.y < imgRows-4) ) 69 | { 70 | float fi = (float) i; 71 | float fCurSer = (float) curSer; 72 | Mat fanElem = (Mat_(1, 4) << intpt.x, intpt.y, fi, fCurSer); 73 | fans.push_back(fanElem); 74 | //vPts.push_back(Point2f(intpt)); 75 | /* 76 | if (rLoc[0] == -1) 77 | { 78 | pdat[0] = intpt.x; 79 | pdat[1] = intpt.y; 80 | }else if (rLoc[0] == 0) 81 | { 82 | ; 83 | }else 84 | { 85 | pdat[2] = intpt.x; 86 | pdat[3] = intpt.y; 87 | } 88 | 89 | if (rLoc[1] == -1) 90 | { 91 | pdat1[0] = intpt.x; 92 | pdat1[1] = intpt.y; 93 | }else if (rLoc[1] == 0) 94 | { 95 | ; 96 | } 97 | else 98 | { 99 | pdat1[2] = intpt.x; 100 | pdat1[3] = intpt.y; 101 | } 102 | */ 103 | } 104 | } 105 | } 106 | Mat tfans, tmat = fans.colRange(2, 4); 107 | int nfan = fans.rows; 108 | for (int i = 0; i < nfan; i++) 109 | { 110 | float *pdat = tmat.ptr(i); 111 | int ser1 = pdat[0]; 112 | int ser2 = pdat[1]; 113 | bool flag = 1; 114 | for (int j = i+1; j < nfan; j++) 115 | { 116 | float *pdat1 = tmat.ptr(j); 117 | int ser3 = (int) pdat1[0]; 118 | int ser4 = (int) pdat1[1]; 119 | if ( (ser1 == ser3 && ser2 == ser4) || (ser1 == ser4 && ser2 == ser3) ) 120 | { 121 | flag = 0; 122 | break; 123 | } 124 | } 125 | if (flag) 126 | { 127 | tfans.push_back(fans.row(i)); 128 | } 129 | } 130 | fans = tfans; 131 | } 132 | 133 | bool CPartiallyRecoverConnectivity::isPtInRotatedRect(Point2f pt, RotatedRect rotRect) 134 | { 135 | bool tbool = false; 136 | float hafWidth = rotRect.size.width / 2; 137 | float hafHeg = rotRect.size.height / 2; 138 | float angle = rotRect.angle * CV_PI / 180; 139 | Point2f cenpt = rotRect.center; 140 | float dsin = sin(angle); 141 | float dcos = cos(angle); 142 | float fposx = dcos * ( pt.x - cenpt.x) + dsin * (pt.y - cenpt.y); 143 | float fposy = dsin * ( pt.x - cenpt.x) - dcos * (pt.y - cenpt.y); 144 | if (-hafWidth <= fposx && fposx < hafWidth && -hafHeg <= fposy && fposy < hafHeg) 145 | tbool = true; 146 | 147 | return tbool; 148 | } 149 | 150 | void CPartiallyRecoverConnectivity::ptsDropInRotatedRect(Mat pts, RotatedRect rotRect, vector &vDropInPts) 151 | { 152 | float hafWidth = rotRect.size.width / 2; 153 | float hafHeg = rotRect.size.height / 2; 154 | float angle = rotRect.angle * CV_PI / 180; 155 | Point2f cenpt = rotRect.center; 156 | float dsin = sin(angle); 157 | float dcos = cos(angle); 158 | Mat fposx = dcos * (pts.col(0) - cenpt.x) + dsin * (pts.col(1) - cenpt.y); 159 | Mat fposy = dsin * (pts.col(0) - cenpt.x) - dcos * (pts.col(1) - cenpt.y); 160 | // cout<(0); 165 | float* pdat2 = fposy.ptr(0); 166 | // vector vpts; 167 | // Point2f tpt; 168 | 169 | for (int i = 0; i < npts; i++) 170 | { 171 | if (-hafWidth <= pdat1[i] && pdat1[i] < hafWidth && -hafHeg <= pdat2[i] && pdat2[i] < hafHeg) 172 | { 173 | vDropInPts.push_back(i); 174 | // tpt.x = pts.at(i, 0); 175 | // tpt.y = pts.at(i, 1); 176 | // vpts.push_back(tpt); 177 | } 178 | } 179 | 180 | int nPts = vDropInPts.size(); 181 | if ( ! nPts ) 182 | return; 183 | /* 184 | int R = 255, G = 0, B = 255; 185 | Point2f vertices[4]; 186 | rotRect.points(vertices); 187 | line(gImg, vertices[0], vertices[1], cvScalar(R,G,B), 2 ); 188 | line(gImg, vertices[1], vertices[2], cvScalar(R,G,B), 2 ); 189 | line(gImg, vertices[2], vertices[3], cvScalar(R,G,B), 2 ); 190 | line(gImg, vertices[3], vertices[0], cvScalar(R,G,B), 2 ); 191 | 192 | for (int i = 0; i < nPts; i++ ) 193 | { 194 | circle(gImg, vpts[i], 3, Scalar(0, 0, 0), -1); 195 | } 196 | imshow("tmp", gImg); 197 | waitKey(); 198 | */ 199 | } 200 | 201 | int CPartiallyRecoverConnectivity::relativeLocationOfIntersection(Point2f pt1, Point2f pt2, Point2f intpt) 202 | { 203 | float dx1 = pt2.x - pt1.x; 204 | float dx2 = intpt.x - pt1.x; 205 | float dy1 = pt2.y - pt1.y; 206 | float dy2 = intpt.y - pt1.y; 207 | float ratio; 208 | 209 | ratio = dx1 != 0 ? dx2 / dx1 : dy2 / dy1; 210 | if (ratio >= 1) 211 | { 212 | return 1; 213 | } 214 | else if (ratio < 1 && ratio > 0) 215 | { 216 | return 0; 217 | } 218 | else 219 | { 220 | return -1; 221 | } 222 | } 223 | 224 | void CPartiallyRecoverConnectivity::intersectionOfLines(Point2f pt1, Point2f pt2, Point2f pt3, Point2f pt4, Point2f &intpt /*, int rLoc[]*/) 225 | { 226 | float A1 = pt1.y - pt2.y; 227 | float B1 = pt2.x - pt1.x; 228 | float C1 = pt2.y * pt1.x - pt1.y * pt2.x; 229 | 230 | float A2 = pt3.y - pt4.y; 231 | float B2 = pt4.x - pt3.x; 232 | float C2 = pt4.y * pt3.x - pt3.y * pt4.x; 233 | Mat_tmat1 = ( Mat_(2,2) << A1, B1, A2, B2 ); 234 | Mat_tmat2 = ( Mat_(2,2) << -C1, B1, -C2, B2 ); 235 | Mat_tmat3 = ( Mat_(2,2) << A1, -C1, A2, -C2); 236 | 237 | float D = determinant(tmat1); 238 | float X = determinant(tmat2) / D; 239 | float Y = determinant(tmat3) / D; 240 | 241 | intpt = Point2f(X, Y); 242 | 243 | // rLoc[0] = relativeLocationOfIntersection(pt1, pt2, intpt); 244 | // rLoc[1] = relativeLocationOfIntersection(pt3, pt4, intpt); 245 | } 246 | -------------------------------------------------------------------------------- /src/PartiallyRecoverConnectivity.h: -------------------------------------------------------------------------------- 1 | #ifndef PARTIALLYRECOVERCONNECTIVITY_H 2 | #define PARTIALLYRECOVERCONNECTIVITY_H 3 | #include 4 | #include 5 | using namespace cv; 6 | using namespace std; 7 | class CPartiallyRecoverConnectivity 8 | { 9 | public: 10 | Mat gImg; 11 | CPartiallyRecoverConnectivity(); 12 | ~CPartiallyRecoverConnectivity(); 13 | CPartiallyRecoverConnectivity(Mat mLines, float radius, Mat &fans, Mat img, float fanThr); 14 | void ptsDropInRotatedRect(Mat pts, RotatedRect rotRect, vector &vDropInPts); 15 | bool isPtInRotatedRect(Point2f pt, RotatedRect rotRect); 16 | void intersectionOfLines(Point2f pt1, Point2f pt2, Point2f pt3, Point2f pt4, Point2f &intpt/*, int rLoc[]*/); 17 | int relativeLocationOfIntersection(Point2f pt1, Point2f pt2, Point2f intpt); 18 | }; 19 | #endif -------------------------------------------------------------------------------- /src/Timer.h: -------------------------------------------------------------------------------- 1 | /* --- --- --- 2 | * Copyright (C) 2008--2010 Idiap Research Institute (.....@idiap.ch) 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | // Timers.h: interface for the CTimer class. 28 | // 29 | ////////////////////////////////////////////////////////////////////// 30 | 31 | #if !defined(_TIMER_H_) 32 | #define _TIMER_H_ 33 | 34 | #include // clock 35 | #include "time.h" 36 | #include 37 | 38 | #ifndef CLOCKS_PER_SEC /* define clocks-per-second if needed */ 39 | #define CLOCKS_PER_SEC 1000000 40 | #endif 41 | 42 | class CTimer 43 | { 44 | public: 45 | /* set the starting time */ 46 | void Start() { 47 | startTime = clock(); 48 | startLocalTime = time(0); 49 | }; 50 | 51 | /* stop the time */ 52 | void Stop(bool local_time=false) { 53 | stopTime = clock(); 54 | stopLocalTime = time(0); 55 | 56 | if ( local_time ) 57 | elapsedTime = difftime(stopLocalTime, startLocalTime); 58 | else 59 | elapsedTime = (stopTime - startTime) / (double)(CLOCKS_PER_SEC); 60 | 61 | if ( elapsedTime <= 0 ) 62 | elapsedTime = (stopTime - startTime) / (double)(CLOCKS_PER_SEC); 63 | }; 64 | 65 | /* get the elapsed hours */ 66 | double GetElapsedHours() { return elapsedTime/3600.0; } 67 | 68 | /* get the elapsed minutes */ 69 | double GetElapsedMinutes() { return elapsedTime/60.0; } 70 | 71 | /* get the elapsed seconds */ 72 | double GetElapsedSeconds() { return elapsedTime; } 73 | 74 | /* print the elapsed time */ 75 | void PrintElapsedTimeMsg(char* msg, 76 | bool used_hours = true, 77 | bool used_minutes = true, 78 | bool used_seconds = true) { 79 | if ( !msg ) return; 80 | 81 | if ( used_hours && used_minutes && used_seconds ) { 82 | int n_hours = (int)GetElapsedHours(); 83 | if ( n_hours > 0 ) { 84 | int n_minutes = (int)fmod(GetElapsedMinutes(),60.0); 85 | if ( n_minutes > 0 ) 86 | sprintf(msg, "%d hours %d minutes %2.4f seconds", 87 | n_hours, n_minutes, fmod(GetElapsedSeconds(),60.0) ); 88 | else 89 | sprintf(msg, "%d hours %2.4f seconds", 90 | n_hours, fmod(GetElapsedSeconds(),60.0) ); 91 | } 92 | else { 93 | int n_minutes = (int)fmod(GetElapsedMinutes(),60.0); 94 | if ( n_minutes > 0 ) 95 | sprintf(msg, "%d minutes %2.4f seconds", 96 | n_minutes, fmod(GetElapsedSeconds(),60.0) ); 97 | else 98 | sprintf(msg, "%2.4f seconds", GetElapsedSeconds() ); 99 | } 100 | } 101 | else if ( used_hours && used_minutes ) { 102 | int n_hours = (int)GetElapsedHours(); 103 | if ( n_hours > 0 ) 104 | sprintf(msg, "%d hours %2.4f minutes", 105 | n_hours, fmod(GetElapsedMinutes(),60.0) ); 106 | else 107 | sprintf(msg, "%2.4f minutes", GetElapsedMinutes() ); 108 | } 109 | else if ( used_hours && used_seconds ) { 110 | int n_hours = (int)GetElapsedHours(); 111 | if ( n_hours > 0 ) 112 | sprintf(msg, "%d hours %2.4f minutes", 113 | n_hours, fmod(GetElapsedSeconds(),3600.0) ); 114 | else 115 | sprintf(msg, "%0.4f seconds", GetElapsedSeconds() ); 116 | } 117 | else if ( used_minutes && used_seconds ) { 118 | int n_minutes = (int)GetElapsedMinutes(); 119 | if ( n_minutes > 0 ) 120 | sprintf(msg, "%d minutes %0.4f seconds", 121 | n_minutes, fmod(GetElapsedSeconds(),60.0) ); 122 | else 123 | sprintf(msg, "%0.4f seconds", GetElapsedSeconds() ); 124 | } 125 | else if ( used_hours ) 126 | sprintf(msg, "%.4f hours", GetElapsedHours() ); 127 | else if ( used_minutes ) 128 | sprintf(msg, "%.4f minutes", GetElapsedMinutes() ); 129 | else if ( used_seconds ) 130 | sprintf(msg, "%.4f seconds", GetElapsedSeconds() ); 131 | }; 132 | 133 | 134 | /* print now local global time using strftime 135 | format_args example: "Now is %Y-%m-%d %H:%M:%S" */ 136 | void PrintLocalTime(char* buffer, int length, const char* format_args) { 137 | time_t rawtime; 138 | struct tm * timeinfo; 139 | 140 | time ( &rawtime ); 141 | timeinfo = localtime ( &rawtime ); 142 | 143 | strftime (buffer, length, format_args, timeinfo); 144 | }; 145 | 146 | CTimer() { elapsedTime = 0; }; 147 | virtual ~CTimer() {}; 148 | 149 | private: 150 | time_t startLocalTime; 151 | time_t stopLocalTime; 152 | 153 | /* starting time */ 154 | clock_t startTime; 155 | clock_t stopTime; 156 | double elapsedTime; 157 | }; 158 | 159 | #endif // !defined(_TIMER_H_) 160 | -------------------------------------------------------------------------------- /src/lsd.c: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------- 2 | 3 | LSD - Line Segment Detector on digital images 4 | 5 | Copyright 2007,2008,2009,2010 rafael grompone von gioi (grompone@gmail.com) 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as 9 | published by the Free Software Foundation, either version 3 of the 10 | License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | ----------------------------------------------------------------------------*/ 21 | 22 | /*---------------------------------------------------------------------------- 23 | 24 | This is an implementation of the Line Segment Detector described in the paper: 25 | 26 | "LSD: A Fast Line Segment Detector with a False Detection Control" 27 | by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel, 28 | and Gregory Randall, IEEE Transactions on Pattern Analysis and 29 | Machine Intelligence, vol. 32, no. 4, pp. 722-732, April, 2010. 30 | 31 | and in more details in the CMLA Technical Report: 32 | 33 | "LSD: A Line Segment Detector, Technical Report", 34 | by Rafael Grompone von Gioi, Jeremie Jakubowicz, Jean-Michel Morel, 35 | Gregory Randall, CMLA, ENS Cachan, 2010. 36 | 37 | HISTORY: 38 | version 1.3 - feb 2010: Multiple bug correction and improved code. 39 | version 1.2 - dic 2009: First full Ansi C Language version. 40 | version 1.1 - sep 2009: Systematic subsampling to scale 0.8 41 | and correction to partially handle "angle problem". 42 | version 1.0 - jan 2009: First complete Megawave2 and Ansi C Language version. 43 | 44 | ----------------------------------------------------------------------------*/ 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include "lsd.h" 51 | 52 | #ifndef M_LN10 53 | #define M_LN10 2.30258509299404568402 54 | #endif /* !M_LN10 */ 55 | 56 | #ifndef M_PI 57 | #define M_PI 3.14159265358979323846 58 | #endif /* !M_PI */ 59 | 60 | #ifndef FALSE 61 | #define FALSE 0 62 | #endif /* !FALSE */ 63 | 64 | #ifndef TRUE 65 | #define TRUE 1 66 | #endif /* !TRUE */ 67 | 68 | #define NOTDEF -1024.0 69 | #define M_3_2_PI 4.71238898038 70 | #define M_2__PI 6.28318530718 71 | #define NOTUSED 0 72 | #define USED 1 73 | 74 | /*----------------------------------------------------------------------------*/ 75 | struct coorlist 76 | { 77 | int x,y; 78 | struct coorlist * next; 79 | }; 80 | 81 | /*----------------------------------------------------------------------------*/ 82 | struct point {int x,y;}; 83 | 84 | 85 | /*----------------------------------------------------------------------------*/ 86 | /*------------------------- Miscellaneous functions --------------------------*/ 87 | /*----------------------------------------------------------------------------*/ 88 | 89 | /*----------------------------------------------------------------------------*/ 90 | /* 91 | Fatal error, print a message to standard-error output and exit. 92 | */ 93 | static void error(char * msg) 94 | { 95 | fprintf(stderr,"LSD Error: %s\n",msg); 96 | exit(EXIT_FAILURE); 97 | } 98 | 99 | /*----------------------------------------------------------------------------*/ 100 | /* 101 | Compare doubles by relative error. 102 | 103 | The resulting rounding error after floating point computations 104 | depend on the specific operations done. The same number computed by 105 | different algorithms could present different rounding errors. For a 106 | useful comparison, an estimation of the relative rounding error 107 | should be considered and compared to a factor times EPS. The factor 108 | should be related to the cumulated rounding error in the chain of 109 | computation. Here, as a simplification, a fixed factor is used. 110 | */ 111 | #define RELATIVE_ERROR_FACTOR 100.0 112 | static int double_equal(double a, double b) 113 | { 114 | double abs_diff,aa,bb,abs_max; 115 | 116 | if( a == b ) return TRUE; 117 | 118 | abs_diff = fabs(a-b); 119 | aa = fabs(a); 120 | bb = fabs(b); 121 | abs_max = aa > bb ? aa : bb; 122 | 123 | /* DBL_MIN is the smallest normalized number, thus, the smallest 124 | number whose relative error is bounded by DBL_EPSILON. For 125 | smaller numbers, the same quantization steps as for DBL_MIN 126 | are used. Then, for smaller numbers, a meaningful "relative" 127 | error should be computed by dividing the difference by DBL_MIN. */ 128 | if( abs_max < DBL_MIN ) abs_max = DBL_MIN; 129 | 130 | /* equal if relative error <= factor x eps */ 131 | return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON); 132 | } 133 | 134 | /*----------------------------------------------------------------------------*/ 135 | /* 136 | Computes Euclidean distance between point (x1,y1) and point (x2,y2). 137 | */ 138 | static double dist(double x1, double y1, double x2, double y2) 139 | { 140 | return sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) ); 141 | } 142 | 143 | 144 | /*----------------------------------------------------------------------------*/ 145 | /*----------------------- 'list of n-tuple' data type ------------------------*/ 146 | /*----------------------------------------------------------------------------*/ 147 | 148 | /*----------------------------------------------------------------------------*/ 149 | /* 150 | Free memory used in n-tuple 'in'. 151 | */ 152 | void free_ntuple_list(ntuple_list in) 153 | { 154 | if( in == NULL || in->values == NULL ) 155 | error("free_ntuple_list: invalid n-tuple input."); 156 | free( (void *) in->values ); 157 | free( (void *) in ); 158 | } 159 | 160 | /*----------------------------------------------------------------------------*/ 161 | /* 162 | Create an n-tuple list and allocate memory for one element. 163 | The parameter 'dim' is the dimension (n) of the n-tuple. 164 | */ 165 | ntuple_list new_ntuple_list(unsigned int dim) 166 | { 167 | ntuple_list n_tuple; 168 | 169 | if( dim <= 0 ) error("new_ntuple_list: 'dim' must be positive."); 170 | 171 | n_tuple = (ntuple_list) malloc( sizeof(struct ntuple_list_s) ); 172 | if( n_tuple == NULL ) error("not enough memory."); 173 | n_tuple->size = 0; 174 | n_tuple->max_size = 1; 175 | n_tuple->dim = dim; 176 | n_tuple->values = (double *) malloc( dim*n_tuple->max_size * sizeof(double) ); 177 | if( n_tuple->values == NULL ) error("not enough memory."); 178 | return n_tuple; 179 | } 180 | 181 | /*----------------------------------------------------------------------------*/ 182 | /* 183 | Enlarge the allocated memory of an n-tuple list. 184 | */ 185 | static void enlarge_ntuple_list(ntuple_list n_tuple) 186 | { 187 | if( n_tuple == NULL || n_tuple->values == NULL || n_tuple->max_size <= 0 ) 188 | error("enlarge_ntuple_list: invalid n-tuple."); 189 | n_tuple->max_size *= 2; 190 | n_tuple->values = 191 | (double *) realloc( (void *) n_tuple->values, 192 | n_tuple->dim * n_tuple->max_size * sizeof(double) ); 193 | if( n_tuple->values == NULL ) error("not enough memory."); 194 | } 195 | 196 | /*----------------------------------------------------------------------------*/ 197 | /* 198 | Add a 5-tuple to an n-tuple list. 199 | */ 200 | static void add_5tuple( ntuple_list out, double v1, double v2, 201 | double v3, double v4, double v5 ) 202 | { 203 | if( out == NULL ) error("add_5tuple: invalid n-tuple input."); 204 | if( out->dim != 5 ) error("add_5tuple: the n-tuple must be a 5-tuple."); 205 | if( out->size == out->max_size ) enlarge_ntuple_list(out); 206 | if( out->values == NULL ) error("add_5tuple: invalid n-tuple input."); 207 | out->values[ out->size * out->dim + 0 ] = v1; 208 | out->values[ out->size * out->dim + 1 ] = v2; 209 | out->values[ out->size * out->dim + 2 ] = v3; 210 | out->values[ out->size * out->dim + 3 ] = v4; 211 | out->values[ out->size * out->dim + 4 ] = v5; 212 | out->size++; 213 | } 214 | 215 | 216 | /*----------------------------------------------------------------------------*/ 217 | /*----------------------------- Image Data Types -----------------------------*/ 218 | /*----------------------------------------------------------------------------*/ 219 | 220 | /*----------------------------------------------------------------------------*/ 221 | /* 222 | Free memory used in image_char 'i'. 223 | */ 224 | void free_image_char(image_char i) 225 | { 226 | if( i == NULL || i->data == NULL ) 227 | error("free_image_char: invalid input image."); 228 | free( (void *) i->data ); 229 | free( (void *) i ); 230 | } 231 | 232 | /*----------------------------------------------------------------------------*/ 233 | /* 234 | Create a new image_char of size 'xsize' times 'ysize'. 235 | */ 236 | image_char new_image_char(unsigned int xsize, unsigned int ysize) 237 | { 238 | image_char image; 239 | 240 | if( xsize == 0 || ysize == 0 ) error("new_image_char: invalid image size."); 241 | 242 | image = (image_char) malloc( sizeof(struct image_char_s) ); 243 | if( image == NULL ) error("not enough memory."); 244 | image->data = (unsigned char *) calloc( xsize*ysize, sizeof(unsigned char) ); 245 | if( image->data == NULL ) error("not enough memory."); 246 | 247 | image->xsize = xsize; 248 | image->ysize = ysize; 249 | 250 | return image; 251 | } 252 | 253 | /*----------------------------------------------------------------------------*/ 254 | /* 255 | Create a new image_char of size 'xsize' times 'ysize', 256 | initialized to the value 'fill_value'. 257 | */ 258 | image_char new_image_char_ini( unsigned int xsize, unsigned int ysize, 259 | unsigned char fill_value ) 260 | { 261 | image_char image = new_image_char(xsize,ysize); 262 | unsigned int N = xsize*ysize; 263 | unsigned int i; 264 | 265 | if( image == NULL || image->data == NULL ) 266 | error("new_image_char_ini: invalid image."); 267 | 268 | for(i=0; idata[i] = fill_value; 269 | 270 | return image; 271 | } 272 | 273 | /*----------------------------------------------------------------------------*/ 274 | /* 275 | Free memory used in image_int 'i'. 276 | */ 277 | void free_image_int(image_int i) 278 | { 279 | if( i == NULL || i->data == NULL ) 280 | error("free_image_int: invalid input image."); 281 | free( (void *) i->data ); 282 | free( (void *) i ); 283 | } 284 | 285 | /*----------------------------------------------------------------------------*/ 286 | /* 287 | Create a new image_int of size 'xsize' times 'ysize'. 288 | */ 289 | image_int new_image_int(unsigned int xsize, unsigned int ysize) 290 | { 291 | image_int image; 292 | 293 | if( xsize == 0 || ysize == 0 ) error("new_image_int: invalid image size."); 294 | 295 | image = (image_int) malloc( sizeof(struct image_int_s) ); 296 | if( image == NULL ) error("not enough memory."); 297 | image->data = (int *) calloc( xsize*ysize, sizeof(int) ); 298 | if( image->data == NULL ) error("not enough memory."); 299 | 300 | image->xsize = xsize; 301 | image->ysize = ysize; 302 | 303 | return image; 304 | } 305 | 306 | /*----------------------------------------------------------------------------*/ 307 | /* 308 | Create a new image_int of size 'xsize' times 'ysize', 309 | initialized to the value 'fill_value'. 310 | */ 311 | image_int new_image_int_ini( unsigned int xsize, unsigned int ysize, 312 | int fill_value ) 313 | { 314 | image_int image = new_image_int(xsize,ysize); 315 | unsigned int N = xsize*ysize; 316 | unsigned int i; 317 | 318 | for(i=0; idata[i] = fill_value; 319 | 320 | return image; 321 | } 322 | 323 | /*----------------------------------------------------------------------------*/ 324 | /* 325 | Free memory used in image_double 'i'. 326 | */ 327 | void free_image_double(image_double i) 328 | { 329 | if( i == NULL || i->data == NULL ) 330 | error("free_image_double: invalid input image."); 331 | free( (void *) i->data ); 332 | free( (void *) i ); 333 | } 334 | 335 | /*----------------------------------------------------------------------------*/ 336 | /* 337 | Create a new image_double of size 'xsize' times 'ysize'. 338 | */ 339 | image_double new_image_double(unsigned int xsize, unsigned int ysize) 340 | { 341 | image_double image; 342 | 343 | if( xsize == 0 || ysize == 0 ) error("new_image_double: invalid image size."); 344 | 345 | image = (image_double) malloc( sizeof(struct image_double_s) ); 346 | if( image == NULL ) error("not enough memory."); 347 | image->data = (double *) calloc( xsize * ysize, sizeof(double) ); 348 | if( image->data == NULL ) error("not enough memory."); 349 | 350 | image->xsize = xsize; 351 | image->ysize = ysize; 352 | 353 | return image; 354 | } 355 | 356 | /*----------------------------------------------------------------------------*/ 357 | /* 358 | Create a new image_double of size 'xsize' times 'ysize', 359 | initialized to the value 'fill_value'. 360 | */ 361 | image_double new_image_double_ini( unsigned int xsize, unsigned int ysize, 362 | double fill_value ) 363 | { 364 | image_double image = new_image_double(xsize,ysize); 365 | unsigned int N = xsize*ysize; 366 | unsigned int i; 367 | 368 | for(i=0; idata[i] = fill_value; 369 | 370 | return image; 371 | } 372 | 373 | 374 | /*----------------------------------------------------------------------------*/ 375 | /*----------------------------- Gaussian filter ------------------------------*/ 376 | /*----------------------------------------------------------------------------*/ 377 | 378 | /*----------------------------------------------------------------------------*/ 379 | /* 380 | Compute a Gaussian kernel of length 'kernel->dim', 381 | standard deviation 'sigma', and centered at value 'mean'. 382 | For example, if mean=0.5, the Gaussian will be centered 383 | in the middle point between values 'kernel->values[0]' 384 | and 'kernel->values[1]'. 385 | */ 386 | static void gaussian_kernel(ntuple_list kernel, double sigma, double mean) 387 | { 388 | double sum = 0.0; 389 | double val; 390 | unsigned int i; 391 | 392 | if( kernel == NULL || kernel->values == NULL ) 393 | error("gaussian_kernel: invalid n-tuple 'kernel'."); 394 | if( sigma <= 0.0 ) error("gaussian_kernel: 'sigma' must be positive."); 395 | 396 | /* compute gaussian kernel */ 397 | if( kernel->max_size < 1 ) enlarge_ntuple_list(kernel); 398 | kernel->size = 1; 399 | for(i=0;idim;i++) 400 | { 401 | val = ( (double) i - mean ) / sigma; 402 | kernel->values[i] = exp( -0.5 * val * val ); 403 | sum += kernel->values[i]; 404 | } 405 | 406 | /* normalization */ 407 | if( sum >= 0.0 ) for(i=0;idim;i++) kernel->values[i] /= sum; 408 | } 409 | 410 | /*----------------------------------------------------------------------------*/ 411 | /* 412 | Subsample image 'in' with Gaussian filtering, to a scale 'scale' 413 | (for example, 0.8 will give a result at 80% of the original size), 414 | using a standard deviation sigma given by: 415 | 416 | sigma = sigma_scale / scale, if scale < 1.0 417 | sigma = sigma_scale, if scale >= 1.0 418 | */ 419 | static image_double gaussian_sampler( image_double in, double scale, 420 | double sigma_scale ) 421 | { 422 | image_double aux,out; 423 | ntuple_list kernel; 424 | unsigned int N,M,h,n,x,y,i; 425 | int xc,yc,j,double_x_size,double_y_size; 426 | double sigma,xx,yy,sum,prec; 427 | 428 | if( in == NULL || in->data == NULL || in->xsize <= 0 || in->ysize <= 0 ) 429 | error("gaussian_sampler: invalid image."); 430 | if( scale <= 0.0 ) error("gaussian_sampler: 'scale' must be positive."); 431 | if( sigma_scale <= 0.0 ) 432 | error("gaussian_sampler: 'sigma_scale' must be positive."); 433 | 434 | /* get memory for images */ 435 | N = (unsigned int) floor( in->xsize * scale ); 436 | M = (unsigned int) floor( in->ysize * scale ); 437 | aux = new_image_double(N,in->ysize); 438 | out = new_image_double(N,M); 439 | 440 | /* sigma, kernel size and memory for the kernel */ 441 | sigma = scale < 1.0 ? sigma_scale / scale : sigma_scale; 442 | /* 443 | The size of the kernel is selected to guarantee that the 444 | the first discarded term is at least 10^prec times smaller 445 | than the central value. For that, h should be larger than x, with 446 | e^(-x^2/2sigma^2) = 1/10^prec. 447 | Then, 448 | x = sigma * sqrt( 2 * prec * ln(10) ). 449 | */ 450 | prec = 3.0; 451 | h = (unsigned int) ceil( sigma * sqrt( 2.0 * prec * log(10.0) ) ); 452 | n = 1+2*h; /* kernel size */ 453 | kernel = new_ntuple_list(n); 454 | 455 | /* auxiliary double image size variables */ 456 | double_x_size = (int) (2 * in->xsize); 457 | double_y_size = (int) (2 * in->ysize); 458 | 459 | /* First subsampling: x axis */ 460 | for(x=0;xxsize;x++) 461 | { 462 | /* 463 | x is the coordinate in the new image. 464 | xx is the corresponding x-value in the original size image. 465 | xc is the integer value, the pixel coordinate of xx. 466 | */ 467 | xx = (double) x / scale; 468 | /* coordinate (0.0,0.0) is in the center of pixel (0,0), 469 | so the pixel with xc=0 get the values of xx from -0.5 to 0.5 */ 470 | xc = (int) floor( xx + 0.5 ); 471 | gaussian_kernel( kernel, sigma, (double) h + xx - (double) xc ); 472 | /* the kernel must be computed for each x because the fine 473 | offset xx-xc is different in each case */ 474 | 475 | for(y=0;yysize;y++) 476 | { 477 | sum = 0.0; 478 | for(i=0;idim;i++) 479 | { 480 | j = xc - h + i; 481 | 482 | /* symmetry boundary condition */ 483 | while( j < 0 ) j += double_x_size; 484 | while( j >= double_x_size ) j -= double_x_size; 485 | if( j >= (int) in->xsize ) j = double_x_size-1-j; 486 | 487 | sum += in->data[ j + y * in->xsize ] * kernel->values[i]; 488 | } 489 | aux->data[ x + y * aux->xsize ] = sum; 490 | } 491 | } 492 | 493 | /* Second subsampling: y axis */ 494 | for(y=0;yysize;y++) 495 | { 496 | /* 497 | y is the coordinate in the new image. 498 | yy is the corresponding x-value in the original size image. 499 | yc is the integer value, the pixel coordinate of xx. 500 | */ 501 | yy = (double) y / scale; 502 | /* coordinate (0.0,0.0) is in the center of pixel (0,0), 503 | so the pixel with yc=0 get the values of yy from -0.5 to 0.5 */ 504 | yc = (int) floor( yy + 0.5 ); 505 | gaussian_kernel( kernel, sigma, (double) h + yy - (double) yc ); 506 | /* the kernel must be computed for each y because the fine 507 | offset yy-yc is different in each case */ 508 | 509 | for(x=0;xxsize;x++) 510 | { 511 | sum = 0.0; 512 | for(i=0;idim;i++) 513 | { 514 | j = yc - h + i; 515 | 516 | /* symmetry boundary condition */ 517 | while( j < 0 ) j += double_y_size; 518 | while( j >= double_y_size ) j -= double_y_size; 519 | if( j >= (int) in->ysize ) j = double_y_size-1-j; 520 | 521 | sum += aux->data[ x + j * aux->xsize ] * kernel->values[i]; 522 | } 523 | out->data[ x + y * out->xsize ] = sum; 524 | } 525 | } 526 | 527 | /* free memory */ 528 | free_ntuple_list(kernel); 529 | free_image_double(aux); 530 | 531 | return out; 532 | } 533 | 534 | 535 | /*----------------------------------------------------------------------------*/ 536 | /*------------------------------ Gradient Angle ------------------------------*/ 537 | /*----------------------------------------------------------------------------*/ 538 | 539 | /*----------------------------------------------------------------------------*/ 540 | /* 541 | Computes the direction of the level line of 'in' at each point. 542 | It returns: 543 | 544 | - an image_double with the angle at each pixel, or NOTDEF if not defined. 545 | - the image_double 'modgrad' (a pointer is passed as argument) 546 | with the gradient magnitude at each point. 547 | - a list of pixels 'list_p' roughly ordered by gradient magnitude. 548 | (the order is made by classing points into bins by gradient magnitude. 549 | the parameters 'n_bins' and 'max_grad' specify the number of 550 | bins and the gradient modulus at the highest bin.) 551 | - a pointer 'mem_p' to the memory used by 'list_p' to be able to 552 | free the memory. 553 | */ 554 | static image_double ll_angle( image_double in, double threshold, 555 | struct coorlist ** list_p, void ** mem_p, 556 | image_double * modgrad, unsigned int n_bins, 557 | double max_grad ) 558 | { 559 | image_double g; 560 | unsigned int n,p,x,y,adr,i; 561 | double com1,com2,gx,gy,norm,norm2; 562 | /* the rest of the variables are used for pseudo-ordering 563 | the gradient magnitude values */ 564 | int list_count = 0; 565 | struct coorlist * list; 566 | struct coorlist ** range_l_s; /* array of pointers to start of bin list */ 567 | struct coorlist ** range_l_e; /* array of pointers to end of bin list */ 568 | struct coorlist * start; 569 | struct coorlist * end; 570 | 571 | /* check parameters */ 572 | if( in == NULL || in->data == NULL || in->xsize <= 0 || in->ysize <= 0 ) 573 | error("ll_angle: invalid image."); 574 | if( threshold < 0.0 ) error("ll_angle: 'threshold' must be positive."); 575 | if( list_p == NULL ) error("ll_angle: NULL pointer 'list_p'."); 576 | if( mem_p == NULL ) error("ll_angle: NULL pointer 'mem_p'."); 577 | if( modgrad == NULL ) error("ll_angle: NULL pointer 'modgrad'."); 578 | if( n_bins <= 0 ) error("ll_angle: 'n_bins' must be positive."); 579 | if( max_grad <= 0.0 ) error("ll_angle: 'max_grad' must be positive."); 580 | 581 | n = in->ysize; 582 | p = in->xsize; 583 | 584 | /* allocate output image */ 585 | g = new_image_double(in->xsize,in->ysize); 586 | 587 | /* get memory for the image of gradient modulus */ 588 | *modgrad = new_image_double(in->xsize,in->ysize); 589 | 590 | /* get memory for "ordered" coordinate list */ 591 | list = (struct coorlist *) calloc(n*p,sizeof(struct coorlist)); 592 | *mem_p = (void *) list; 593 | range_l_s = (struct coorlist **) calloc(n_bins,sizeof(struct coorlist *)); 594 | range_l_e = (struct coorlist **) calloc(n_bins,sizeof(struct coorlist *)); 595 | if( list == NULL || range_l_s == NULL || range_l_e == NULL ) 596 | error("not enough memory."); 597 | for(i=0;idata[(n-1)*p+x] = NOTDEF; 601 | for(y=0;ydata[p*y+p-1] = NOTDEF; 602 | 603 | /*** remaining part ***/ 604 | for(x=0;xdata[adr+p+1] - in->data[adr]; 621 | com2 = in->data[adr+1] - in->data[adr+p]; 622 | gx = com1+com2; 623 | gy = com1-com2; 624 | norm2 = gx*gx+gy*gy; 625 | norm = sqrt( norm2 / 4.0 ); 626 | 627 | (*modgrad)->data[adr] = norm; 628 | 629 | if( norm <= threshold ) /* norm too small, gradient no defined */ 630 | g->data[adr] = NOTDEF; 631 | else 632 | { 633 | /* angle computation */ 634 | g->data[adr] = atan2(gx,-gy); 635 | 636 | /* store the point in the right bin according to its norm */ 637 | i = (unsigned int) (norm * (double) n_bins / max_grad); 638 | if( i >= n_bins ) i = n_bins-1; 639 | if( range_l_e[i] == NULL ) 640 | range_l_s[i] = range_l_e[i] = list+list_count++; 641 | else 642 | { 643 | range_l_e[i]->next = list+list_count; 644 | range_l_e[i] = list+list_count++; 645 | } 646 | range_l_e[i]->x = (int) x; 647 | range_l_e[i]->y = (int) y; 648 | range_l_e[i]->next = NULL; 649 | } 650 | } 651 | 652 | /* Make the list of points "ordered" by norm value. 653 | It starts by the larger bin, so the list starts by the 654 | pixels with higher gradient value. 655 | */ 656 | for(i=n_bins-1; i>0 && range_l_s[i]==NULL; i--); 657 | start = range_l_s[i]; 658 | end = range_l_e[i]; 659 | if( start != NULL ) 660 | for(i--;i>0; i--) 661 | if( range_l_s[i] != NULL ) 662 | { 663 | end->next = range_l_s[i]; 664 | end = range_l_e[i]; 665 | } 666 | *list_p = start; 667 | 668 | /* free memory */ 669 | free( (void *) range_l_s ); 670 | free( (void *) range_l_e ); 671 | 672 | return g; 673 | } 674 | 675 | /*----------------------------------------------------------------------------*/ 676 | /* 677 | Is point (x,y) aligned to angle theta, up to precision 'prec'? 678 | */ 679 | static int isaligned( int x, int y, image_double angles, double theta, 680 | double prec ) 681 | { 682 | double a; 683 | 684 | /* check parameters */ 685 | if( angles == NULL || angles->data == NULL ) 686 | error("isaligned: invalid image 'angles'."); 687 | if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize ) 688 | error("isaligned: (x,y) out of the image."); 689 | if( prec < 0.0 ) error("isaligned: 'prec' must be positive."); 690 | 691 | a = angles->data[ x + y * angles->xsize ]; 692 | 693 | if( a == NOTDEF ) return FALSE; /* there is no risk of double comparison 694 | problem here because we are only 695 | interested in the exact NOTDEF value */ 696 | 697 | /* it is assumed that 'theta' and 'a' are in the range [-pi,pi] */ 698 | theta -= a; 699 | if( theta < 0.0 ) theta = -theta; 700 | if( theta > M_3_2_PI ) 701 | { 702 | theta -= M_2__PI; 703 | if( theta < 0.0 ) theta = -theta; 704 | } 705 | 706 | return theta < prec; 707 | } 708 | 709 | /*----------------------------------------------------------------------------*/ 710 | /* 711 | Absolute value angle difference. 712 | */ 713 | static double angle_diff(double a, double b) 714 | { 715 | a -= b; 716 | while( a <= -M_PI ) a += M_2__PI; 717 | while( a > M_PI ) a -= M_2__PI; 718 | if( a < 0.0 ) a = -a; 719 | return a; 720 | } 721 | 722 | /*----------------------------------------------------------------------------*/ 723 | /* 724 | Signed angle difference. 725 | */ 726 | static double angle_diff_signed(double a, double b) 727 | { 728 | a -= b; 729 | while( a <= -M_PI ) a += M_2__PI; 730 | while( a > M_PI ) a -= M_2__PI; 731 | return a; 732 | } 733 | 734 | 735 | /*----------------------------------------------------------------------------*/ 736 | /*----------------------------- NFA computation ------------------------------*/ 737 | /*----------------------------------------------------------------------------*/ 738 | 739 | /*----------------------------------------------------------------------------*/ 740 | /* 741 | Calculates the natural logarithm of the absolute value of 742 | the gamma function of x using the Lanczos approximation, 743 | see http://www.rskey.org/gamma.htm 744 | 745 | The formula used is 746 | \Gamma(x) = \frac{ \sum_{n=0}^{N} q_n x^n }{ \Pi_{n=0}^{N} (x+n) } 747 | (x+5.5)^(x+0.5) e^{-(x+5.5)} 748 | so 749 | \log\Gamma(x) = \log( \sum_{n=0}^{N} q_n x^n ) + (x+0.5) \log(x+5.5) 750 | - (x+5.5) - \sum_{n=0}^{N} \log(x+n) 751 | and 752 | q0 = 75122.6331530 753 | q1 = 80916.6278952 754 | q2 = 36308.2951477 755 | q3 = 8687.24529705 756 | q4 = 1168.92649479 757 | q5 = 83.8676043424 758 | q6 = 2.50662827511 759 | */ 760 | static double log_gamma_lanczos(double x) 761 | { 762 | static double q[7] = { 75122.6331530, 80916.6278952, 36308.2951477, 763 | 8687.24529705, 1168.92649479, 83.8676043424, 764 | 2.50662827511 }; 765 | double a = (x+0.5) * log(x+5.5) - (x+5.5); 766 | double b = 0.0; 767 | int n; 768 | 769 | for(n=0;n<7;n++) 770 | { 771 | a -= log( x + (double) n ); 772 | b += q[n] * pow( x, (double) n ); 773 | } 774 | return a + log(b); 775 | } 776 | 777 | /*----------------------------------------------------------------------------*/ 778 | /* 779 | Calculates the natural logarithm of the absolute value of 780 | the gamma function of x using Robert H. Windschitl method, 781 | see http://www.rskey.org/gamma.htm 782 | 783 | The formula used is 784 | \Gamma(x) = \sqrt(\frac{2\pi}{x}) ( \frac{x}{e} 785 | \sqrt{ x\sinh(1/x) + \frac{1}{810x^6} } )^x 786 | so 787 | \log\Gamma(x) = 0.5\log(2\pi) + (x-0.5)\log(x) - x 788 | + 0.5x\log( x\sinh(1/x) + \frac{1}{810x^6} ). 789 | 790 | This formula is a good approximation when x > 15. 791 | */ 792 | static double log_gamma_windschitl(double x) 793 | { 794 | return 0.918938533204673 + (x-0.5)*log(x) - x 795 | + 0.5*x*log( x*sinh(1/x) + 1/(810.0*pow(x,6.0)) ); 796 | } 797 | 798 | /*----------------------------------------------------------------------------*/ 799 | /* 800 | Calculates the natural logarithm of the absolute value of 801 | the gamma function of x. When x>15 use log_gamma_windschitl(), 802 | otherwise use log_gamma_lanczos(). 803 | */ 804 | #define log_gamma(x) ((x)>15.0?log_gamma_windschitl(x):log_gamma_lanczos(x)) 805 | 806 | /*----------------------------------------------------------------------------*/ 807 | /* 808 | Computes -log10(NFA) 809 | 810 | NFA stands for Number of False Alarms: 811 | 812 | NFA = NT.b(n,k,p) 813 | 814 | NT - number of tests 815 | b(,,) - tail of binomial distribution with parameters n,k and p 816 | 817 | The value -log10(NFA) is equivalent but more intuitive than NFA: 818 | -1 corresponds to 10 mean false alarms 819 | 0 corresponds to 1 mean false alarm 820 | 1 corresponds to 0.1 mean false alarms 821 | 2 corresponds to 0.01 mean false alarms 822 | ... 823 | 824 | Used this way, the bigger the value, better the detection, 825 | and a logarithmic scale is used. 826 | 827 | Parameters: 828 | n,k,p - binomial parameters. 829 | logNT - logarithm of Number of Tests 830 | */ 831 | #define TABSIZE 100000 832 | static double nfa(int n, int k, double p, double logNT) 833 | { 834 | static double inv[TABSIZE]; /* table to keep computed inverse values */ 835 | double tolerance = 0.1; /* an error of 10% in the result is accepted */ 836 | double log1term,term,bin_term,mult_term,bin_tail,err,p_term; 837 | int i; 838 | 839 | if( n<0 || k<0 || k>n || p<=0.0 || p>=1.0 ) 840 | error("nfa: wrong n, k or p values."); 841 | 842 | if( n==0 || k==0 ) return -logNT; 843 | if( n==k ) return -logNT - (double) n * log10(p); 844 | 845 | p_term = p / (1.0-p); 846 | 847 | /* compute the first term of the series */ 848 | /* 849 | binomial_tail(n,k,p) = sum_{i=k}^n bincoef(n,i) * p^i * (1-p)^{n-i} 850 | where bincoef(n,i) are the binomial coefficients. 851 | But 852 | bincoef(n,k) = gamma(n+1) / ( gamma(k+1) * gamma(n-k+1) ). 853 | We use this to compute the first term. Actually the log of it. 854 | */ 855 | log1term = log_gamma( (double) n + 1.0 ) - log_gamma( (double) k + 1.0 ) 856 | - log_gamma( (double) (n-k) + 1.0 ) 857 | + (double) k * log(p) + (double) (n-k) * log(1.0-p); 858 | term = exp(log1term); 859 | 860 | /* in some cases no more computations are needed */ 861 | if( double_equal(term,0.0) ) /* the first term is almost zero */ 862 | { 863 | if( (double) k > (double) n * p ) /* at begin or end of the tail? */ 864 | return -log1term / M_LN10 - logNT; /* end: use just the first term */ 865 | else 866 | return -logNT; /* begin: the tail is roughly 1 */ 867 | } 868 | 869 | /* compute more terms if needed */ 870 | bin_tail = term; 871 | for(i=k+1;i<=n;i++) 872 | { 873 | /* 874 | As 875 | term_i = bincoef(n,i) * p^i * (1-p)^(n-i) 876 | and 877 | bincoef(n,i)/bincoef(n,i-1) = n-1+1 / i, 878 | then, 879 | term_i / term_i-1 = (n-i+1)/i * p/(1-p) 880 | and 881 | term_i = term_i-1 * (n-i+1)/i * p/(1-p). 882 | 1/i is stored in a table as they are computed, 883 | because divisions are expensive. 884 | p/(1-p) is computed only once and stored in 'p_term'. 885 | */ 886 | bin_term = (double) (n-i+1) * ( ii. 896 | Then, the error on the binomial tail when truncated at 897 | the i term can be bounded by a geometric series of form 898 | term_i * sum mult_term_i^j. */ 899 | err = term * ( ( 1.0 - pow( mult_term, (double) (n-i+1) ) ) / 900 | (1.0-mult_term) - 1.0 ); 901 | 902 | /* One wants an error at most of tolerance*final_result, or: 903 | tolerance * abs(-log10(bin_tail)-logNT). 904 | Now, the error that can be accepted on bin_tail is 905 | given by tolerance*final_result divided by the derivative 906 | of -log10(x) when x=bin_tail. that is: 907 | tolerance * abs(-log10(bin_tail)-logNT) / (1/bin_tail) 908 | Finally, we truncate the tail if the error is less than: 909 | tolerance * abs(-log10(bin_tail)-logNT) * bin_tail */ 910 | if( err < tolerance * fabs(-log10(bin_tail)-logNT) * bin_tail ) break; 911 | } 912 | } 913 | return -log10(bin_tail) - logNT; 914 | } 915 | 916 | 917 | /*----------------------------------------------------------------------------*/ 918 | /*--------------------------- Rectangle structure ----------------------------*/ 919 | /*----------------------------------------------------------------------------*/ 920 | 921 | /*----------------------------------------------------------------------------*/ 922 | struct rect /* line segment with width */ 923 | { 924 | double x1,y1,x2,y2; /* first and second point of the line segment */ 925 | double width; /* rectangle width */ 926 | double x,y; /* center of the rectangle */ 927 | double theta; /* angle */ 928 | double dx,dy; /* vector with the line segment angle */ 929 | double prec; /* tolerance angle */ 930 | double p; /* probability of a point with angle within 'prec' */ 931 | }; 932 | 933 | /*----------------------------------------------------------------------------*/ 934 | /* 935 | Copy one rectangle structure to another. 936 | */ 937 | static void rect_copy(struct rect * in, struct rect * out) 938 | { 939 | if( in == NULL || out == NULL ) error("rect_copy: invalid 'in' or 'out'."); 940 | out->x1 = in->x1; 941 | out->y1 = in->y1; 942 | out->x2 = in->x2; 943 | out->y2 = in->y2; 944 | out->width = in->width; 945 | out->x = in->x; 946 | out->y = in->y; 947 | out->theta = in->theta; 948 | out->dx = in->dx; 949 | out->dy = in->dy; 950 | out->prec = in->prec; 951 | out->p = in->p; 952 | } 953 | 954 | /*----------------------------------------------------------------------------*/ 955 | /* 956 | Rectangle points iterator. 957 | */ 958 | typedef struct 959 | { 960 | double vx[4]; 961 | double vy[4]; 962 | double ys,ye; 963 | int x,y; 964 | } rect_iter; 965 | 966 | /*----------------------------------------------------------------------------*/ 967 | /* 968 | Rectangle points iterator auxiliary function. 969 | */ 970 | static double inter_low(double x, double x1, double y1, double x2, double y2) 971 | { 972 | if( x1 > x2 || x < x1 || x > x2 ) 973 | { 974 | fprintf(stderr,"inter_low: x %g x1 %g x2 %g.\n",x,x1,x2); 975 | error("impossible situation."); 976 | } 977 | if( double_equal(x1,x2) && y1y2 ) return y2; 979 | return y1 + (x-x1) * (y2-y1) / (x2-x1); 980 | } 981 | 982 | /*----------------------------------------------------------------------------*/ 983 | /* 984 | Rectangle points iterator auxiliary function. 985 | */ 986 | static double inter_hi(double x, double x1, double y1, double x2, double y2) 987 | { 988 | if( x1 > x2 || x < x1 || x > x2 ) 989 | { 990 | fprintf(stderr,"inter_hi: x %g x1 %g x2 %g.\n",x,x1,x2); 991 | error("impossible situation."); 992 | } 993 | if( double_equal(x1,x2) && y1y2 ) return y1; 995 | return y1 + (x-x1) * (y2-y1) / (x2-x1); 996 | } 997 | 998 | /*----------------------------------------------------------------------------*/ 999 | /* 1000 | Free memory used by a rectangle iterator. 1001 | */ 1002 | static void ri_del(rect_iter * iter) 1003 | { 1004 | if( iter == NULL ) error("ri_del: NULL iterator."); 1005 | free( (void *) iter ); 1006 | } 1007 | 1008 | /*----------------------------------------------------------------------------*/ 1009 | /* 1010 | Check if the iterator finished the full iteration. 1011 | */ 1012 | static int ri_end(rect_iter * i) 1013 | { 1014 | if( i == NULL ) error("ri_end: NULL iterator."); 1015 | return (double)(i->x) > i->vx[2]; 1016 | } 1017 | 1018 | /*----------------------------------------------------------------------------*/ 1019 | /* 1020 | Increment a rectangle iterator. 1021 | */ 1022 | static void ri_inc(rect_iter * i) 1023 | { 1024 | if( i == NULL ) error("ri_inc: NULL iterator."); 1025 | 1026 | if( (double) (i->x) <= i->vx[2] ) i->y++; 1027 | 1028 | while( (double) (i->y) > i->ye && (double) (i->x) <= i->vx[2] ) 1029 | { 1030 | /* new x */ 1031 | i->x++; 1032 | 1033 | if( (double) (i->x) > i->vx[2] ) return; /* end of iteration */ 1034 | 1035 | /* update lower y limit for the line */ 1036 | if( (double) i->x < i->vx[3] ) 1037 | i->ys = inter_low((double)i->x,i->vx[0],i->vy[0],i->vx[3],i->vy[3]); 1038 | else i->ys = inter_low((double)i->x,i->vx[3],i->vy[3],i->vx[2],i->vy[2]); 1039 | 1040 | /* update upper y limit for the line */ 1041 | if( (double)i->x < i->vx[1] ) 1042 | i->ye = inter_hi((double)i->x,i->vx[0],i->vy[0],i->vx[1],i->vy[1]); 1043 | else i->ye = inter_hi((double)i->x,i->vx[1],i->vy[1],i->vx[2],i->vy[2]); 1044 | 1045 | /* new y */ 1046 | i->y = (int) ceil(i->ys); 1047 | } 1048 | } 1049 | 1050 | /*----------------------------------------------------------------------------*/ 1051 | /* 1052 | Create and initialize a rectangle iterator. 1053 | */ 1054 | static rect_iter * ri_ini(struct rect * r) 1055 | { 1056 | double vx[4],vy[4]; 1057 | int n,offset; 1058 | rect_iter * i; 1059 | 1060 | if( r == NULL ) error("ri_ini: invalid rectangle."); 1061 | 1062 | i = (rect_iter *) malloc(sizeof(rect_iter)); 1063 | if( i == NULL ) error("ri_ini: Not enough memory."); 1064 | 1065 | vx[0] = r->x1 - r->dy * r->width / 2.0; 1066 | vy[0] = r->y1 + r->dx * r->width / 2.0; 1067 | vx[1] = r->x2 - r->dy * r->width / 2.0; 1068 | vy[1] = r->y2 + r->dx * r->width / 2.0; 1069 | vx[2] = r->x2 + r->dy * r->width / 2.0; 1070 | vy[2] = r->y2 - r->dx * r->width / 2.0; 1071 | vx[3] = r->x1 + r->dy * r->width / 2.0; 1072 | vy[3] = r->y1 - r->dx * r->width / 2.0; 1073 | 1074 | if( r->x1 < r->x2 && r->y1 <= r->y2 ) offset = 0; 1075 | else if( r->x1 >= r->x2 && r->y1 < r->y2 ) offset = 1; 1076 | else if( r->x1 > r->x2 && r->y1 >= r->y2 ) offset = 2; 1077 | else offset = 3; 1078 | 1079 | for(n=0; n<4; n++) 1080 | { 1081 | i->vx[n] = vx[(offset+n)%4]; 1082 | i->vy[n] = vy[(offset+n)%4]; 1083 | } 1084 | 1085 | /* starting point */ 1086 | i->x = (int) ceil(i->vx[0]) - 1; 1087 | i->y = (int) ceil(i->vy[0]); 1088 | i->ys = i->ye = -DBL_MAX; 1089 | 1090 | /* advance to the first point */ 1091 | ri_inc(i); 1092 | 1093 | return i; 1094 | } 1095 | 1096 | /*----------------------------------------------------------------------------*/ 1097 | /* 1098 | Compute a rectangle's NFA value. 1099 | */ 1100 | static double rect_nfa(struct rect * rec, image_double angles, double logNT) 1101 | { 1102 | rect_iter * i; 1103 | int pts = 0; 1104 | int alg = 0; 1105 | 1106 | if( rec == NULL ) error("rect_nfa: invalid rectangle."); 1107 | if( angles == NULL ) error("rect_nfa: invalid 'angles'."); 1108 | 1109 | for(i=ri_ini(rec); !ri_end(i); ri_inc(i)) 1110 | if( i->x >= 0 && i->y >= 0 && 1111 | i->x < (int) angles->xsize && i->y < (int) angles->ysize ) 1112 | { 1113 | ++pts; 1114 | if( isaligned(i->x, i->y, angles, rec->theta, rec->prec) ) ++alg; 1115 | } 1116 | ri_del(i); 1117 | 1118 | return nfa(pts,alg,rec->p,logNT); 1119 | } 1120 | 1121 | 1122 | /*----------------------------------------------------------------------------*/ 1123 | /*---------------------------------- Regions ---------------------------------*/ 1124 | /*----------------------------------------------------------------------------*/ 1125 | 1126 | /*----------------------------------------------------------------------------*/ 1127 | /* 1128 | Compute a region's angle. 1129 | */ 1130 | static double get_theta( struct point * reg, int reg_size, double x, double y, 1131 | image_double modgrad, double reg_angle, double prec ) 1132 | { 1133 | double lambda1,lambda2,tmp,theta,weight,sum; 1134 | double Ixx = 0.0; 1135 | double Iyy = 0.0; 1136 | double Ixy = 0.0; 1137 | int i; 1138 | 1139 | /* check parameters */ 1140 | if( reg == NULL ) error("get_theta: invalid region."); 1141 | if( reg_size <= 1 ) error("get_theta: region size <= 1."); 1142 | if( modgrad == NULL || modgrad->data == NULL ) 1143 | error("get_theta: invalid 'modgrad'."); 1144 | if( prec < 0.0 ) error("get_theta: 'prec' must be positive."); 1145 | 1146 | /*----------- theta ---------------------------------------------------*/ 1147 | /* 1148 | Region inertia matrix A: 1149 | Ixx Ixy 1150 | Ixy Iyy 1151 | where 1152 | Ixx = \sum_i y_i^2 1153 | Iyy = \sum_i x_i^2 1154 | Ixy = -\sum_i x_i y_i 1155 | 1156 | lambda1 and lambda2 are the eigenvalues, with lambda1 >= lambda2. 1157 | They are found by solving the characteristic polynomial 1158 | det(\lambda I - A) = 0. 1159 | 1160 | To get the line segment direction we want to get the eigenvector of 1161 | the smaller eigenvalue. We have to solve a,b in: 1162 | a.Ixx + b.Ixy = a.lambda2 1163 | a.Ixy + b.Iyy = b.lambda2 1164 | We want the angle theta = atan(b/a). I can be computed with 1165 | any of the two equations: 1166 | theta = atan( (lambda2-Ixx) / Ixy ) 1167 | or 1168 | theta = atan( Ixy / (lambda2-Iyy) ) 1169 | 1170 | When |Ixx| > |Iyy| we use the first, otherwise the second 1171 | (just to get better numeric precision). 1172 | */ 1173 | sum = 0.0; 1174 | for(i=0; idata[ reg[i].x + reg[i].y * modgrad->xsize ]; 1177 | Ixx += ( (double) reg[i].y - y ) * ( (double) reg[i].y - y ) * weight; 1178 | Iyy += ( (double) reg[i].x - x ) * ( (double) reg[i].x - x ) * weight; 1179 | Ixy -= ( (double) reg[i].x - x ) * ( (double) reg[i].y - y ) * weight; 1180 | sum += weight; 1181 | } 1182 | if( sum <= 0.0 ) error("get_theta: weights sum less or equal to zero."); 1183 | Ixx /= sum; 1184 | Iyy /= sum; 1185 | Ixy /= sum; 1186 | lambda1 = ( Ixx + Iyy + sqrt( (Ixx-Iyy)*(Ixx-Iyy) + 4.0*Ixy*Ixy ) ) / 2.0; 1187 | lambda2 = ( Ixx + Iyy - sqrt( (Ixx-Iyy)*(Ixx-Iyy) + 4.0*Ixy*Ixy ) ) / 2.0; 1188 | if( fabs(lambda1) < fabs(lambda2) ) 1189 | { 1190 | fprintf(stderr,"Ixx %g Iyy %g Ixy %g lamb1 %g lamb2 %g - lamb1 < lamb2\n", 1191 | Ixx,Iyy,Ixy,lambda1,lambda2); 1192 | tmp = lambda1; 1193 | lambda1 = lambda2; 1194 | lambda2 = tmp; 1195 | } 1196 | 1197 | if( fabs(Ixx) > fabs(Iyy) ) 1198 | theta = atan2( lambda2-Ixx, Ixy ); 1199 | else 1200 | theta = atan2( Ixy, lambda2-Iyy ); 1201 | 1202 | /* The previous procedure don't cares about orientation, 1203 | so it could be wrong by 180 degrees. Here is corrected if necessary. */ 1204 | if( angle_diff(theta,reg_angle) > prec ) theta += M_PI; 1205 | 1206 | return theta; 1207 | } 1208 | 1209 | /*----------------------------------------------------------------------------*/ 1210 | /* 1211 | Computes a rectangle that covers a region of points. 1212 | */ 1213 | static void region2rect( struct point * reg, int reg_size, 1214 | image_double modgrad, double reg_angle, 1215 | double prec, double p, struct rect * rec ) 1216 | { 1217 | double x,y,dx,dy,l,w,theta,weight,sum,l_min,l_max,w_min,w_max; 1218 | int i; 1219 | 1220 | /* check parameters */ 1221 | if( reg == NULL ) error("region2rect: invalid region."); 1222 | if( reg_size <= 1 ) error("region2rect: region size <= 1."); 1223 | if( modgrad == NULL || modgrad->data == NULL ) 1224 | error("region2rect: invalid image 'modgrad'."); 1225 | if( rec == NULL ) error("region2rect: invalid 'rec'."); 1226 | 1227 | /* center */ 1228 | x = y = sum = 0.0; 1229 | for(i=0; idata[ reg[i].x + reg[i].y * modgrad->xsize ]; 1232 | x += (double) reg[i].x * weight; 1233 | y += (double) reg[i].y * weight; 1234 | sum += weight; 1235 | } 1236 | if( sum <= 0.0 ) error("region2rect: weights sum equal to zero."); 1237 | x /= sum; 1238 | y /= sum; 1239 | 1240 | /* theta */ 1241 | theta = get_theta(reg,reg_size,x,y,modgrad,reg_angle,prec); 1242 | 1243 | /* length and width */ 1244 | dx = cos(theta); 1245 | dy = sin(theta); 1246 | l_min = l_max = w_min = w_max = 0.0; 1247 | for(i=0; i l_max ) l_max = l; 1253 | if( l < l_min ) l_min = l; 1254 | if( w > w_max ) w_max = w; 1255 | if( w < w_min ) w_min = w; 1256 | } 1257 | 1258 | /* store values */ 1259 | rec->x1 = x + l_min * dx; 1260 | rec->y1 = y + l_min * dy; 1261 | rec->x2 = x + l_max * dx; 1262 | rec->y2 = y + l_max * dy; 1263 | rec->width = w_max - w_min; 1264 | rec->x = x; 1265 | rec->y = y; 1266 | rec->theta = theta; 1267 | rec->dx = dx; 1268 | rec->dy = dy; 1269 | rec->prec = prec; 1270 | rec->p = p; 1271 | 1272 | if( rec->width < 1.0 ) rec->width = 1.0; 1273 | } 1274 | 1275 | /*----------------------------------------------------------------------------*/ 1276 | /* 1277 | Found a region of points that share the same angle, up to a tolerance 'prec', 1278 | starting at point (x,y). 1279 | */ 1280 | static void region_grow( int x, int y, image_double angles, struct point * reg, 1281 | int * reg_size, double * reg_angle, image_char used, 1282 | double prec ) 1283 | { 1284 | double sumdx,sumdy; 1285 | int xx,yy,i; 1286 | 1287 | /* check parameters */ 1288 | if( x < 0 || y < 0 || x >= (int) angles->xsize || y >= (int) angles->ysize ) 1289 | error("region_grow: (x,y) out of the image."); 1290 | if( angles == NULL || angles->data == NULL ) 1291 | error("region_grow: invalid image 'angles'."); 1292 | if( reg == NULL ) error("region_grow: invalid 'reg'."); 1293 | if( reg_size == NULL ) error("region_grow: invalid pointer 'reg_size'."); 1294 | if( reg_angle == NULL ) error("region_grow: invalid pointer 'reg_angle'."); 1295 | if( used == NULL || used->data == NULL ) 1296 | error("region_grow: invalid image 'used'."); 1297 | 1298 | /* first point of the region */ 1299 | *reg_size = 1; 1300 | reg[0].x = x; 1301 | reg[0].y = y; 1302 | *reg_angle = angles->data[x+y*angles->xsize]; 1303 | sumdx = cos(*reg_angle); 1304 | sumdy = sin(*reg_angle); 1305 | used->data[x+y*used->xsize] = USED; 1306 | 1307 | /* try neighbors as new region points */ 1308 | for(i=0; i<*reg_size; i++) 1309 | for(xx=reg[i].x-1; xx<=reg[i].x+1; xx++) 1310 | for(yy=reg[i].y-1; yy<=reg[i].y+1; yy++) 1311 | if( xx>=0 && yy>=0 && xx<(int)used->xsize && yy<(int)used->ysize && 1312 | used->data[xx+yy*used->xsize] != USED && 1313 | isaligned(xx,yy,angles,*reg_angle,prec) ) 1314 | { 1315 | /* add point */ 1316 | used->data[xx+yy*used->xsize] = USED; 1317 | reg[*reg_size].x = xx; 1318 | reg[*reg_size].y = yy; 1319 | ++(*reg_size); 1320 | 1321 | /* update region's angle */ 1322 | sumdx += cos( angles->data[xx+yy*angles->xsize] ); 1323 | sumdy += sin( angles->data[xx+yy*angles->xsize] ); 1324 | *reg_angle = atan2(sumdy,sumdx); 1325 | } 1326 | } 1327 | 1328 | /*----------------------------------------------------------------------------*/ 1329 | /* 1330 | Try some rectangles variations to improve NFA value. 1331 | Only if the rectangle is not meaningful (i.e., log_nfa <= eps). 1332 | */ 1333 | static double rect_improve( struct rect * rec, image_double angles, 1334 | double logNT, double eps ) 1335 | { 1336 | struct rect r; 1337 | double log_nfa,log_nfa_new; 1338 | double delta = 0.5; 1339 | double delta_2 = delta / 2.0; 1340 | int n; 1341 | 1342 | log_nfa = rect_nfa(rec,angles,logNT); 1343 | 1344 | if( log_nfa > eps ) return log_nfa; 1345 | 1346 | /* try finer precisions */ 1347 | rect_copy(rec,&r); 1348 | for(n=0; n<5; n++) 1349 | { 1350 | r.p /= 2.0; 1351 | r.prec = r.p * M_PI; 1352 | log_nfa_new = rect_nfa(&r,angles,logNT); 1353 | if( log_nfa_new > log_nfa ) 1354 | { 1355 | log_nfa = log_nfa_new; 1356 | rect_copy(&r,rec); 1357 | } 1358 | } 1359 | 1360 | if( log_nfa > eps ) return log_nfa; 1361 | 1362 | /* try to reduce width */ 1363 | rect_copy(rec,&r); 1364 | for(n=0; n<5; n++) 1365 | { 1366 | if( (r.width - delta) >= 0.5 ) 1367 | { 1368 | r.width -= delta; 1369 | log_nfa_new = rect_nfa(&r,angles,logNT); 1370 | if( log_nfa_new > log_nfa ) 1371 | { 1372 | rect_copy(&r,rec); 1373 | log_nfa = log_nfa_new; 1374 | } 1375 | } 1376 | } 1377 | 1378 | if( log_nfa > eps ) return log_nfa; 1379 | 1380 | /* try to reduce one side of the rectangle */ 1381 | rect_copy(rec,&r); 1382 | for(n=0; n<5; n++) 1383 | { 1384 | if( (r.width - delta) >= 0.5 ) 1385 | { 1386 | r.x1 += -r.dy * delta_2; 1387 | r.y1 += r.dx * delta_2; 1388 | r.x2 += -r.dy * delta_2; 1389 | r.y2 += r.dx * delta_2; 1390 | r.width -= delta; 1391 | log_nfa_new = rect_nfa(&r,angles,logNT); 1392 | if( log_nfa_new > log_nfa ) 1393 | { 1394 | rect_copy(&r,rec); 1395 | log_nfa = log_nfa_new; 1396 | } 1397 | } 1398 | } 1399 | 1400 | if( log_nfa > eps ) return log_nfa; 1401 | 1402 | /* try to reduce the other side of the rectangle */ 1403 | rect_copy(rec,&r); 1404 | for(n=0; n<5; n++) 1405 | { 1406 | if( (r.width - delta) >= 0.5 ) 1407 | { 1408 | r.x1 -= -r.dy * delta_2; 1409 | r.y1 -= r.dx * delta_2; 1410 | r.x2 -= -r.dy * delta_2; 1411 | r.y2 -= r.dx * delta_2; 1412 | r.width -= delta; 1413 | log_nfa_new = rect_nfa(&r,angles,logNT); 1414 | if( log_nfa_new > log_nfa ) 1415 | { 1416 | rect_copy(&r,rec); 1417 | log_nfa = log_nfa_new; 1418 | } 1419 | } 1420 | } 1421 | 1422 | if( log_nfa > eps ) return log_nfa; 1423 | 1424 | /* try even finer precisions */ 1425 | rect_copy(rec,&r); 1426 | for(n=0; n<5; n++) 1427 | { 1428 | r.p /= 2.0; 1429 | r.prec = r.p * M_PI; 1430 | log_nfa_new = rect_nfa(&r,angles,logNT); 1431 | if( log_nfa_new > log_nfa ) 1432 | { 1433 | log_nfa = log_nfa_new; 1434 | rect_copy(&r,rec); 1435 | } 1436 | } 1437 | 1438 | return log_nfa; 1439 | } 1440 | 1441 | /*----------------------------------------------------------------------------*/ 1442 | /* 1443 | Reduce the region size, by elimination the points far from the 1444 | starting point, until that leads to rectangle with the right 1445 | density of region points or to discard the region if too small. 1446 | */ 1447 | static int reduce_region_radius( struct point * reg, int * reg_size, 1448 | image_double modgrad, double reg_angle, 1449 | double prec, double p, struct rect * rec, 1450 | image_char used, image_double angles, 1451 | double density_th, double logNT, double eps ) 1452 | { 1453 | double density,rad1,rad2,rad,xc,yc,log_nfa; 1454 | int i; 1455 | 1456 | /* check parameters */ 1457 | if( reg == NULL ) error("refine: invalid pointer 'reg'."); 1458 | if( reg_size == NULL ) error("refine: invalid pointer 'reg_size'."); 1459 | if( prec < 0.0 ) error("refine: 'prec' must be positive."); 1460 | if( rec == NULL ) error("refine: invalid pointer 'rec'."); 1461 | if( used == NULL || used->data == NULL ) 1462 | error("refine: invalid image 'used'."); 1463 | if( angles == NULL || angles->data == NULL ) 1464 | error("refine: invalid image 'angles'."); 1465 | 1466 | /* compute region points density */ 1467 | density = (double) *reg_size / 1468 | ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width ); 1469 | 1470 | if( density >= density_th ) return TRUE; 1471 | 1472 | /* compute region radius */ 1473 | xc = (double) reg[0].x; 1474 | yc = (double) reg[0].y; 1475 | rad1 = dist( xc, yc, rec->x1, rec->y1 ); 1476 | rad2 = dist( xc, yc, rec->x2, rec->y2 ); 1477 | rad = rad1 > rad2 ? rad1 : rad2; 1478 | 1479 | while( density < density_th ) 1480 | { 1481 | rad *= 0.75; 1482 | 1483 | /* remove points from the region and update 'used' map */ 1484 | for(i=0; i<*reg_size; i++) 1485 | if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) > rad ) 1486 | { 1487 | /* point not kept, mark it as NOTUSED */ 1488 | used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED; 1489 | /* remove point from the region */ 1490 | reg[i].x = reg[*reg_size-1].x; /* if i==*reg_size-1 copy itself */ 1491 | reg[i].y = reg[*reg_size-1].y; 1492 | --(*reg_size); 1493 | --i; /* to avoid skipping one point */ 1494 | } 1495 | 1496 | /* reject if the region is too small. 1497 | 2 is the minimal region size for 'region2rect' to work. */ 1498 | if( *reg_size < 2 ) return FALSE; 1499 | 1500 | /* re-compute rectangle */ 1501 | region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec); 1502 | 1503 | /* try to improve the rectangle and compute NFA */ 1504 | log_nfa = rect_improve(rec,angles,logNT,eps); 1505 | 1506 | /* re-compute region points density */ 1507 | density = (double) *reg_size / 1508 | ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width ); 1509 | } 1510 | 1511 | /* if the final rectangle is meaningful accept, otherwise reject */ 1512 | if( log_nfa > eps ) return TRUE; 1513 | else return FALSE; 1514 | } 1515 | 1516 | /*----------------------------------------------------------------------------*/ 1517 | /* 1518 | Refine a rectangle. For that, an estimation of the angle tolerance is 1519 | performed by the standard deviation of the angle at points near the 1520 | region's starting point. Then, a new region is grown starting from the 1521 | same point, but using the estimated angle tolerance. 1522 | If this fails to produce a rectangle with the right density of 1523 | region points, 'reduce_region_radius' is called to try to 1524 | satisfy this condition. 1525 | */ 1526 | static int refine( struct point * reg, int * reg_size, image_double modgrad, 1527 | double reg_angle, double prec, double p, struct rect * rec, 1528 | image_char used, image_double angles, double density_th, 1529 | double logNT, double eps ) 1530 | { 1531 | double angle,ang_d,mean_angle,tau,density,xc,yc,ang_c,sum,s_sum,log_nfa; 1532 | int i,n; 1533 | 1534 | /* check parameters */ 1535 | if( reg == NULL ) error("refine: invalid pointer 'reg'."); 1536 | if( reg_size == NULL ) error("refine: invalid pointer 'reg_size'."); 1537 | if( prec < 0.0 ) error("refine: 'prec' must be positive."); 1538 | if( rec == NULL ) error("refine: invalid pointer 'rec'."); 1539 | if( used == NULL || used->data == NULL ) 1540 | error("refine: invalid image 'used'."); 1541 | if( angles == NULL || angles->data == NULL ) 1542 | error("refine: invalid image 'angles'."); 1543 | 1544 | /* compute region points density */ 1545 | density = (double) *reg_size / 1546 | ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width ); 1547 | 1548 | if( density >= density_th ) return TRUE; 1549 | 1550 | /*------ First try: reduce angle tolerance ------*/ 1551 | 1552 | /* compute the new mean angle and tolerance */ 1553 | xc = (double) reg[0].x; 1554 | yc = (double) reg[0].y; 1555 | ang_c = angles->data[ reg[0].x + reg[0].y * angles->xsize ]; 1556 | sum = s_sum = 0.0; 1557 | n = 0; 1558 | for(i=0; i<*reg_size; i++) 1559 | { 1560 | used->data[ reg[i].x + reg[i].y * used->xsize ] = NOTUSED; 1561 | if( dist( xc, yc, (double) reg[i].x, (double) reg[i].y ) < rec->width ) 1562 | { 1563 | angle = angles->data[ reg[i].x + reg[i].y * angles->xsize ]; 1564 | ang_d = angle_diff_signed(angle,ang_c); 1565 | sum += ang_d; 1566 | s_sum += ang_d * ang_d; 1567 | ++n; 1568 | } 1569 | } 1570 | mean_angle = sum / (double) n; 1571 | tau = 2.0 * sqrt( (s_sum - 2.0 * mean_angle * sum) / (double) n 1572 | + mean_angle*mean_angle ); /* 2 * standard deviation */ 1573 | 1574 | /* find a new region from the same starting point and new angle tolerance */ 1575 | region_grow(reg[0].x,reg[0].y,angles,reg,reg_size,®_angle,used,tau); 1576 | 1577 | /* if the region is too small, reject */ 1578 | if( *reg_size < 2 ) return FALSE; 1579 | 1580 | /* re-compute rectangle */ 1581 | region2rect(reg,*reg_size,modgrad,reg_angle,prec,p,rec); 1582 | 1583 | /* try to improve the rectangle and compute NFA */ 1584 | log_nfa = rect_improve(rec,angles,logNT,eps); 1585 | 1586 | /* re-compute region points density */ 1587 | density = (double) *reg_size / 1588 | ( dist(rec->x1,rec->y1,rec->x2,rec->y2) * rec->width ); 1589 | 1590 | /*------ Second try: reduce region radius ------*/ 1591 | if( density < density_th ) 1592 | return reduce_region_radius( reg, reg_size, modgrad, reg_angle, prec, p, 1593 | rec, used, angles, density_th, logNT, eps ); 1594 | 1595 | /* if the final rectangle is meaningful accept, otherwise reject */ 1596 | if( log_nfa > eps ) return TRUE; 1597 | else return FALSE; 1598 | } 1599 | 1600 | 1601 | /*----------------------------------------------------------------------------*/ 1602 | /*-------------------------- Line Segment Detector ---------------------------*/ 1603 | /*----------------------------------------------------------------------------*/ 1604 | 1605 | /*----------------------------------------------------------------------------*/ 1606 | /* 1607 | LSD full interface 1608 | */ 1609 | ntuple_list LineSegmentDetection( image_double image, double scale, 1610 | double sigma_scale, double quant, 1611 | double ang_th, double eps, double density_th, 1612 | int n_bins, double max_grad, 1613 | image_int * region ) 1614 | { 1615 | ntuple_list out = new_ntuple_list(5); 1616 | image_double scaled_image,angles,modgrad; 1617 | image_char used; 1618 | struct coorlist * list_p; 1619 | void * mem_p; 1620 | struct rect rec; 1621 | struct point * reg; 1622 | int reg_size,min_reg_size,i; 1623 | unsigned int xsize,ysize; 1624 | double rho,reg_angle,prec,p,log_nfa,logNT; 1625 | int ls_count = 0; /* line segments are numbered 1,2,3,... */ 1626 | 1627 | 1628 | /* check parameters */ 1629 | if( image==NULL || image->data==NULL || image->xsize<=0 || image->ysize<=0 ) 1630 | error("invalid image input."); 1631 | if( scale <= 0.0 ) error("'scale' value must be positive."); 1632 | if( sigma_scale <= 0.0 ) error("'sigma_scale' value must be positive."); 1633 | if( quant < 0.0 ) error("'quant' value must be positive."); 1634 | if( ang_th <= 0.0 || ang_th >= 180.0 ) 1635 | error("'ang_th' value must be in the range (0,180)."); 1636 | if( density_th < 0.0 || density_th > 1.0 ) 1637 | error("'density_th' value must be in the range [0,1]."); 1638 | if( n_bins <= 0 ) error("'n_bins' value must be positive."); 1639 | if( max_grad <= 0.0 ) error("'max_grad' value must be positive."); 1640 | 1641 | 1642 | /* angle tolerance */ 1643 | prec = M_PI * ang_th / 180.0; 1644 | p = ang_th / 180.0; 1645 | rho = quant / sin(prec); /* gradient magnitude threshold */ 1646 | 1647 | 1648 | /* scale image (if necessary) and compute angle at each pixel */ 1649 | if( scale != 1.0 ) 1650 | { 1651 | scaled_image = gaussian_sampler( image, scale, sigma_scale ); 1652 | angles = ll_angle( scaled_image, rho, &list_p, &mem_p, 1653 | &modgrad, (unsigned int) n_bins, max_grad ); 1654 | free_image_double(scaled_image); 1655 | } 1656 | else 1657 | angles = ll_angle( image, rho, &list_p, &mem_p, &modgrad, 1658 | (unsigned int) n_bins, max_grad ); 1659 | xsize = angles->xsize; 1660 | ysize = angles->ysize; 1661 | logNT = 5.0 * ( log10( (double) xsize ) + log10( (double) ysize ) ) / 2.0; 1662 | min_reg_size = (int) (-logNT/log10(p)); /* minimal number of points in region 1663 | that can give a meaningful event */ 1664 | 1665 | 1666 | /* initialize some structures */ 1667 | if( region != NULL ) /* image to output pixel region number, if asked */ 1668 | *region = new_image_int_ini(angles->xsize,angles->ysize,0); 1669 | used = new_image_char_ini(xsize,ysize,NOTUSED); 1670 | reg = (struct point *) calloc(xsize * ysize, sizeof(struct point)); 1671 | if( reg == NULL ) error("not enough memory!"); 1672 | 1673 | 1674 | /* search for line segments */ 1675 | for(;list_p; list_p = list_p->next ) 1676 | if( used->data[ list_p->x + list_p->y * used->xsize ] == NOTUSED && 1677 | angles->data[ list_p->x + list_p->y * angles->xsize ] != NOTDEF ) 1678 | /* there is no risk of double comparison problem here 1679 | because we are only interested in the exact NOTDEF value */ 1680 | { 1681 | /* find the region of connected point and ~equal angle */ 1682 | region_grow( list_p->x, list_p->y, angles, reg, ®_size, 1683 | ®_angle, used, prec ); 1684 | 1685 | /* reject small regions */ 1686 | if( reg_size < min_reg_size ) continue; 1687 | 1688 | /* construct rectangular approximation for the region */ 1689 | region2rect(reg,reg_size,modgrad,reg_angle,prec,p,&rec); 1690 | 1691 | /* Check if the rectangle exceeds the minimal density of 1692 | region points. If not, try to improve the region. 1693 | The rectangle will be rejected if the final one does 1694 | not fulfill the minimal density condition. 1695 | This is an addition to the original LSD algorithm published in 1696 | "LSD: A Fast Line Segment Detector with a False Detection Control" 1697 | by R. Grompone von Gioi, J. Jakubowicz, J.M. Morel, and G. Randall. 1698 | The original algorithm is obtained with density_th = 0.0. 1699 | */ 1700 | if( !refine( reg, ®_size, modgrad, reg_angle, prec, p, 1701 | &rec, used, angles, density_th, logNT, eps ) ) continue; 1702 | 1703 | /* compute NFA value */ 1704 | log_nfa = rect_improve(&rec,angles,logNT,eps); 1705 | if( log_nfa <= eps ) continue; 1706 | 1707 | /* A New Line Segment was found! */ 1708 | ++ls_count; /* increase line segment counter */ 1709 | 1710 | /* 1711 | The gradient was computed with a 2x2 mask, its value corresponds to 1712 | points with an offset of (0.5,0.5), that should be added to output. 1713 | The coordinates origin is at the center of pixel (0,0). 1714 | */ 1715 | rec.x1 += 0.5; rec.y1 += 0.5; 1716 | rec.x2 += 0.5; rec.y2 += 0.5; 1717 | 1718 | /* scale the result values if a subsampling was performed */ 1719 | if( scale != 1.0 ) 1720 | { 1721 | rec.x1 /= scale; rec.y1 /= scale; 1722 | rec.x2 /= scale; rec.y2 /= scale; 1723 | rec.width /= scale; 1724 | } 1725 | 1726 | /* add line segment found to output */ 1727 | add_5tuple(out, rec.x1, rec.y1, rec.x2, rec.y2, rec.width); 1728 | 1729 | /* add region number to 'region' image if needed */ 1730 | if( region != NULL ) 1731 | for(i=0; idata[reg[i].x+reg[i].y*(*region)->xsize] = ls_count; 1733 | } 1734 | 1735 | 1736 | /* free memory */ 1737 | free_image_double(angles); 1738 | free_image_double(modgrad); 1739 | free_image_char(used); 1740 | free( (void *) reg ); 1741 | free( (void *) mem_p ); 1742 | 1743 | return out; 1744 | } 1745 | 1746 | /*----------------------------------------------------------------------------*/ 1747 | /* 1748 | LSD Simple Interface 1749 | */ 1750 | ntuple_list lsd(image_double image) 1751 | { 1752 | /* LSD parameters */ 1753 | double scale = 0.8; /* Scale the image by Gaussian filter to 'scale'. */ 1754 | double sigma_scale = 0.6; /* Sigma for Gaussian filter is computed as 1755 | sigma = sigma_scale/scale. */ 1756 | double quant = 2.0; /* Bound to the quantization error on the 1757 | gradient norm. */ 1758 | double ang_th = 22.5; /* Gradient angle tolerance in degrees. */ 1759 | double eps = 0.0; /* Detection threshold, -log10(NFA). */ 1760 | double density_th = 0.7; /* Minimal density of region points in rectangle. */ 1761 | int n_bins = 1024; /* Number of bins in pseudo-ordering of gradient 1762 | modulus. */ 1763 | double max_grad = 255.0; /* Gradient modulus in the highest bin. The 1764 | default value corresponds to the highest 1765 | gradient modulus on images with gray 1766 | levels in [0,255]. */ 1767 | 1768 | return LineSegmentDetection( image, scale, sigma_scale, quant, ang_th, eps, 1769 | density_th, n_bins, max_grad, NULL ); 1770 | } 1771 | /*----------------------------------------------------------------------------*/ 1772 | -------------------------------------------------------------------------------- /src/lsd.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------- 2 | 3 | LSD - Line Segment Detector on digital images 4 | 5 | Copyright 2007,2008,2009,2010 rafael grompone von gioi (grompone@gmail.com) 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Affero General Public License as 9 | published by the Free Software Foundation, either version 3 of the 10 | License, or (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Affero General Public License for more details. 16 | 17 | You should have received a copy of the GNU Affero General Public License 18 | along with this program. If not, see . 19 | 20 | ----------------------------------------------------------------------------*/ 21 | #ifndef LSD_HEADER 22 | #define LSD_HEADER 23 | 24 | 25 | /*----------------------------------------------------------------------------*/ 26 | /*----------------------- 'list of n-tuple' data type ------------------------*/ 27 | /*----------------------------------------------------------------------------*/ 28 | 29 | /* 30 | The i component, of the n-tuple number j, of an n-tuple list 'ntl' 31 | is accessed with: 32 | 33 | ntl->values[ i + j * ntl->dim ] 34 | 35 | The dimension of the n-tuple (n) is: 36 | 37 | ntl->dim 38 | 39 | The number of number of n-tuples in the list is: 40 | 41 | ntl->size 42 | 43 | The maximum number of n-tuples that can be stored in the 44 | list with the allocated memory at a given time is given by: 45 | 46 | ntl->max_size 47 | */ 48 | typedef struct ntuple_list_s 49 | { 50 | unsigned int size; 51 | unsigned int max_size; 52 | unsigned int dim; 53 | double * values; 54 | } * ntuple_list; 55 | 56 | void free_ntuple_list(ntuple_list in); 57 | ntuple_list new_ntuple_list(unsigned int dim); 58 | 59 | 60 | /*----------------------------------------------------------------------------*/ 61 | /*----------------------------- Image Data Types -----------------------------*/ 62 | /*----------------------------------------------------------------------------*/ 63 | /* 64 | The pixel value at (x,y) is accessed by: 65 | 66 | image->data[ x + y * image->xsize ] 67 | 68 | with x and y integer. 69 | */ 70 | 71 | /*----------------------------------------------------------------------------*/ 72 | /* 73 | char image data type 74 | */ 75 | typedef struct image_char_s 76 | { 77 | unsigned char * data; 78 | unsigned int xsize,ysize; 79 | } * image_char; 80 | 81 | void free_image_char(image_char i); 82 | image_char new_image_char(unsigned int xsize, unsigned int ysize); 83 | image_char new_image_char_ini( unsigned int xsize, unsigned int ysize, 84 | unsigned char fill_value ); 85 | 86 | /*----------------------------------------------------------------------------*/ 87 | /* 88 | int image data type 89 | */ 90 | typedef struct image_int_s 91 | { 92 | int * data; 93 | unsigned int xsize,ysize; 94 | } * image_int; 95 | 96 | void free_image_int(image_int i); 97 | image_int new_image_int(unsigned int xsize, unsigned int ysize); 98 | image_int new_image_int_ini( unsigned int xsize, unsigned int ysize, 99 | int fill_value ); 100 | 101 | /*----------------------------------------------------------------------------*/ 102 | /* 103 | double image data type 104 | */ 105 | typedef struct image_double_s 106 | { 107 | double * data; 108 | unsigned int xsize,ysize; 109 | } * image_double; 110 | 111 | void free_image_double(image_double i); 112 | image_double new_image_double(unsigned int xsize, unsigned int ysize); 113 | image_double new_image_double_ini( unsigned int xsize, unsigned int ysize, 114 | double fill_value ); 115 | 116 | 117 | /*----------------------------------------------------------------------------*/ 118 | /*-------------------------- Line Segment Detector ---------------------------*/ 119 | /*----------------------------------------------------------------------------*/ 120 | 121 | /*----------------------------------------------------------------------------*/ 122 | /* LSD Full Interface */ 123 | /*----------------------------------------------------------------------------*/ 124 | /* 125 | Input: 126 | 127 | image: Input image 128 | 129 | scale: When different than 1.0, LSD will scale the image by 130 | Gaussian filtering. 131 | Example: is scale=0.8, the input image will be subsampled 132 | to 80% of its size, and then the line segment detector 133 | will be applied. 134 | Suggested value: 0.8 135 | 136 | sigma_scale: When scale!=1.0, the sigma of the Gaussian filter is: 137 | sigma = sigma_scale / scale, if scale < 1.0 138 | sigma = sigma_scale, if scale >= 1.0 139 | Suggested value: 0.6 140 | 141 | quant: Bound to the quantization error on the gradient norm. 142 | Example: if gray level is quantized to integer steps, 143 | the gradient (computed by finite differences) error 144 | due to quantization will be bounded by 2.0, as the 145 | worst case is when the error are 1 and -1, that 146 | gives an error of 2.0. 147 | Suggested value: 2.0 148 | 149 | ang_th: Gradient angle tolerance in the region growing 150 | algorithm, in degrees. 151 | Suggested value: 22.5 152 | 153 | eps: Detection threshold, -log10(NFA). 154 | The bigger, the more strict the detector is, 155 | and will result in less detections. 156 | (Note that the 'minus sign' makes that this 157 | behavior is opposite to the one of NFA.) 158 | The value -log10(NFA) is equivalent but more 159 | intuitive than NFA: 160 | -1.0 corresponds to 10 mean false alarms 161 | 0.0 corresponds to 1 mean false alarm 162 | 1.0 corresponds to 0.1 mean false alarms 163 | 2.0 corresponds to 0.01 mean false alarms 164 | Suggested value: 0.0 165 | 166 | density_th: Minimal proportion of region points in a rectangle. 167 | Suggested value: 0.7 168 | 169 | n_bins: Number of bins used in the pseudo-ordering of gradient 170 | modulus. 171 | Suggested value: 1024 172 | 173 | max_grad: Gradient modulus in the highest bin. For example, 174 | for images with integer gray levels in [0,255], 175 | the maximum possible gradient value is 255.0. 176 | Suggested value: 255.0 177 | 178 | region: Optional output: an int image where the pixels used 179 | in some line support region are marked. Unused pixels 180 | have the value '0' while the used ones have the 181 | number of the line segment, numbered 1,2,3,... If desired, 182 | a non NULL pointer to an image_int should be used. 183 | The resulting image has the size of the image used 184 | for the processing, that is, the size of the input 185 | image scaled by the given factor 'scale'. 186 | Suggested value: NULL 187 | 188 | Return value: A 5-tuple list, where each 5-tuple corresponds to a 189 | detected line segment. The five values are: 190 | x1,y1,x2,y2,width 191 | for a line segment from (x1,y1) to (x2,y2) and 192 | a width 'width'. 193 | */ 194 | ntuple_list LineSegmentDetection( image_double image, double scale, 195 | double sigma_scale, double quant, 196 | double ang_th, double eps, double density_th, 197 | int n_bins, double max_grad, 198 | image_int * region ); 199 | 200 | /*----------------------------------------------------------------------------*/ 201 | /* LSD Simple Interface */ 202 | /*----------------------------------------------------------------------------*/ 203 | /* 204 | input: an image 205 | output: a 5-tuple list of detected line segments. 206 | */ 207 | ntuple_list lsd(image_double image); 208 | 209 | 210 | #endif /* !LSD_HEADER */ 211 | /*----------------------------------------------------------------------------*/ 212 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | extern "C" 3 | { 4 | #include "lsd.h" 5 | }; 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "IO.h" 14 | #include "LineMatching.h" 15 | #include "PartiallyRecoverConnectivity.h" 16 | 17 | // #include "CmdLine.h" 18 | 19 | using namespace std; 20 | using namespace cv; 21 | 22 | 23 | void detectLine(char* imfile, Mat &mLines, float minLineLength) 24 | { 25 | IplImage* im = cvLoadImage(imfile,CV_LOAD_IMAGE_GRAYSCALE); 26 | image_double image = new_image_double(im->width, im->height); 27 | unsigned char* im_src = (unsigned char*)im->imageData; 28 | int xsize = image->xsize; 29 | int ysize = image->ysize; 30 | int y,x; 31 | for (y = 0;y < ysize;y++) 32 | { 33 | for (x = 0;x < xsize;x++) 34 | { 35 | image->data[y * xsize + x] = im_src[y * im->widthStep + x]; 36 | } 37 | } 38 | ntuple_list detected_lines = lsd(image); 39 | free_image_double(image); 40 | 41 | int nLines = detected_lines->size; 42 | int nCount = 0; 43 | int i,j; 44 | int dim = detected_lines->dim; 45 | 46 | Mat tmat; 47 | for (i = 0;i < nLines;i++) 48 | { 49 | float a1 = detected_lines->values[i*dim+0]; 50 | float a2 = detected_lines->values[i*dim+1]; 51 | float a3 = detected_lines->values[i*dim+2]; 52 | float a4 = detected_lines->values[i*dim+3]; 53 | if (sqrt( (a3-a1)*(a3-a1)+ (a4-a2)*(a4-a2) ) >= minLineLength) 54 | { 55 | tmat = (Mat_(1, 4)<(i); 75 | int color_num=rand()%6; 76 | int R=color[color_num][0]; 77 | int G=color[color_num][1]; 78 | int B=color[color_num][2]; 79 | 80 | Point2f spt1 = Point2f( pdat[0], pdat[1]); 81 | Point2f ept1 = Point2f( pdat[2], pdat[3] ); 82 | line(Img, spt1, ept1, cvScalar(R,G,B), 2 ); 83 | } 84 | 85 | imshow(imgName, Img); 86 | // imwrite(imgName, Img); 87 | waitKey(20); 88 | } 89 | 90 | void drawPartiallyConnectedLine(Mat Img, Mat mLines, string imgName, Mat fans) 91 | { 92 | int color[6][3]; 93 | color[0][0]=255;color[0][1]=0; color[0][2]=0; 94 | color[1][0]=0; color[1][1]=255;color[1][2]=0; 95 | color[2][0]=0; color[2][1]=0; color[2][2]=255; 96 | color[3][0]=255;color[3][1]=255;color[3][2]=0; 97 | color[4][0]=0;color[4][1]=255; color[4][2]=255; 98 | color[5][0]=255;color[5][1]=0; color[5][2]=255; 99 | 100 | int nline = mLines.rows; 101 | for (int i = 0; i < nline; i++) 102 | { 103 | float *pdat = mLines.ptr(i); 104 | int color_num=rand()%6; 105 | int R=color[color_num][0]; 106 | int G=color[color_num][1]; 107 | int B=color[color_num][2]; 108 | 109 | Point2f spt1 = Point2f( pdat[0], pdat[1] ); 110 | Point2f ept1 = Point2f( pdat[2], pdat[3] ); 111 | line(Img, spt1, ept1, cvScalar(R,G,B), 2 ); 112 | } 113 | int nFan = fans.rows; 114 | for (int i = 0; i < nFan; i++ ) 115 | { 116 | int color_num=rand()%6; 117 | int R=color[color_num][0]; 118 | int G=color[color_num][1]; 119 | int B=color[color_num][2]; 120 | float *pdat = fans.ptr(i); 121 | circle(Img, Point2f(pdat[0], pdat[1]), 5, cvScalar(R,G,B), -1); 122 | } 123 | imshow(imgName, Img); 124 | waitKey(20); 125 | } 126 | 127 | 128 | 129 | int main(int argc, char** argv) 130 | { 131 | char* imgName1 = argv[1]; 132 | char* imgName2 = argv[2]; 133 | 134 | string outLineMatchesFileName = "result.txt"; 135 | if (argc == 4) 136 | outLineMatchesFileName = argv[3]; 137 | 138 | // show intermediate results or not 139 | bool isVerbose = true; 140 | 141 | // whether build image pyramids to deal with scale change betwen images to be matched; 142 | // Set false when you know there is not scale or only slight scale change between the images, 143 | // This can tremendously accelerate the matching process. 144 | bool isBuildingImagePyramids = true; 145 | 146 | // Ture, if load junctions generated by yourself; otherwise false, the program will generate junction by itself 147 | bool isProvideJunc = false; 148 | // the paths of your junction files, if load junctions generated by yourself. 149 | char* providedJunc1; 150 | char* providedJunc2; 151 | 152 | 153 | // Ture, if load line segments generated by yourself; otherwise false, the program will use LSD to generate segments 154 | bool isProvideLines = false; 155 | // the paths of your segment files, if load segments generated by yourself. 156 | char* providedLines1; 157 | char* providedLines2; 158 | 159 | 160 | // the length requirement for the detected segments used for matching 161 | float minLineLength = 0; 162 | 163 | // the threshold controling the size of thte impact region of a segment, see paper for detail 164 | float expandWidth =20.0; 165 | 166 | int nAvgDesDist = 2; 167 | bool isScaleChangeExisting = false; 168 | 169 | 170 | bool isTwoLineHomography = true; 171 | 172 | // the number of octaves used in the image pyramids 173 | int nOctave = 4; 174 | // the number of layers in the each octave 175 | int nOctaveLayer = 2; 176 | 177 | 178 | float desDistThrEpi = 0.4; 179 | float desDistThrProg = 0.5; 180 | 181 | float fmatThr = 3.0; 182 | float hmatThr = 5.0; 183 | 184 | int nNeighborPts = 10; 185 | int nEnterGroup = 4; 186 | float rotAngThr = 30*CV_PI/180; 187 | float sameSideRatio = 0.8; 188 | float regionHeight = 4; 189 | float junctionDistThr =5.0; 190 | 191 | float intensityProfileWidth = 3; 192 | float radiusPointMatchUnique = 0; 193 | float difAngThr = 20*CV_PI/180; 194 | float rcircle = 10; 195 | float truncateThr = 0.3; 196 | float fanThr = 1.0/4 * CV_PI; 197 | 198 | 199 | Mat colorImg1= imread(imgName1, 3); 200 | Mat colorImg2= imread(imgName2, 3); 201 | Mat img1, img2; 202 | cvtColor(colorImg1, img1, CV_RGB2GRAY); 203 | img1.convertTo(img1, CV_32FC1); 204 | cvtColor(colorImg2, img2, CV_RGB2GRAY); 205 | img2.convertTo(img2, CV_32FC1); 206 | 207 | Mat nodes1, nodes2, lines1, lines2; 208 | Mat keyPoints1, keyPoints2; 209 | if (isProvideLines) 210 | { 211 | CIO io; 212 | io.loadData(providedLines1, lines1); 213 | io.loadData(providedLines2, lines2); 214 | 215 | if (minLineLength != 0) 216 | { 217 | Mat tlines1, tlines2; 218 | Mat ta = lines1.row(2) - lines1.row(0); 219 | Mat tb = lines1.row(3) - lines1.row(1); 220 | Mat tc = ta.mul(ta) + tb.mul(tb); 221 | float *pdat = tc.ptr(0); 222 | int nline1 = lines1.rows; 223 | for (int i = 0; i < nline1; i++ ) 224 | { 225 | if (pdat[i] < minLineLength) 226 | continue ; 227 | 228 | tlines1.push_back(lines1.row(i)); 229 | } 230 | lines1 = tlines1; 231 | 232 | ta = lines2.row(2) - lines2.row(0); 233 | tb = lines2.row(3) - lines2.row(1); 234 | tc = ta.mul(ta) + tb.mul(tb); 235 | pdat = tc.ptr(0); 236 | int nline2 = lines2.rows; 237 | for (int i = 0; i < nline2; i++) 238 | { 239 | if (pdat[i] < minLineLength) 240 | continue; 241 | 242 | tlines2.push_back(lines2.row(i)); 243 | } 244 | lines2 = tlines2; 245 | } 246 | } 247 | else 248 | { 249 | detectLine(imgName1, lines1, minLineLength); 250 | detectLine(imgName2, lines2, minLineLength); 251 | } 252 | if (isVerbose) 253 | { 254 | drawDectectedLine(colorImg1.clone(), lines1, "Detected lines in the first image"); 255 | drawDectectedLine(colorImg2.clone(), lines2, "Detected lines in the second image"); 256 | } 257 | 258 | 259 | if (isProvideJunc) 260 | { 261 | CIO io; 262 | io.loadData(providedJunc1, nodes1); 263 | io.loadData(providedJunc2, nodes2); 264 | } 265 | else 266 | { 267 | CPartiallyRecoverConnectivity p1(lines1, expandWidth, nodes1, colorImg1, fanThr); 268 | CPartiallyRecoverConnectivity p2(lines2, expandWidth, nodes2, colorImg2, fanThr); 269 | } 270 | cout<< "Line segments detected in the two images: ("<writeData(outLineMatchesFileName, mlines); 292 | 293 | delete pIO; 294 | pIO = NULL; 295 | delete pLineMatching; 296 | pLineMatching = NULL; 297 | return 0; 298 | } --------------------------------------------------------------------------------