├── deploy.sh ├── skeleton.h ├── images ├── concept.jpg ├── concept1.jpg ├── result0.jpg └── result50.jpg ├── main.cpp ├── README.md └── thin.h /deploy.sh: -------------------------------------------------------------------------------- 1 | git add . 2 | git commit -m "update" 3 | git push -f origin master -------------------------------------------------------------------------------- /skeleton.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEO-LIKE/SkeletonExtraction/HEAD/skeleton.h -------------------------------------------------------------------------------- /images/concept.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEO-LIKE/SkeletonExtraction/HEAD/images/concept.jpg -------------------------------------------------------------------------------- /images/concept1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEO-LIKE/SkeletonExtraction/HEAD/images/concept1.jpg -------------------------------------------------------------------------------- /images/result0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEO-LIKE/SkeletonExtraction/HEAD/images/result0.jpg -------------------------------------------------------------------------------- /images/result50.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GEO-LIKE/SkeletonExtraction/HEAD/images/result50.jpg -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "skeleton.h" 3 | #include "thin.h" 4 | 5 | int main() { 6 | // Create a matrix and initialization 7 | cv::Mat src = GenerateMat(); 8 | cv::Mat grayImage, binaryImage, distImage, gradientImage,ridge; 9 | cv::cvtColor(src, grayImage,CV_BGR2GRAY); 10 | // Binarization 11 | cv::threshold(grayImage, binaryImage,150, 255, cv::THRESH_BINARY); 12 | // Thinning 13 | thinning(binaryImage, ridge); 14 | 15 | cv::imshow("Skeleton", ridge); 16 | cv::imwrite("Skeleton.jpg", ridge); 17 | cv::waitKey(0); 18 | cv::destroyAllWindows(); 19 | 20 | return(0); 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SkeletonExtraction 2 | Extract the skeleton of an binary image 3 | 4 | ### Environment configuration 5 | - vs2015 6 | - openCV 3.4.1 7 | 8 | ### Basic concepts 9 | 10 | ![Structure Element B](https://github.com/GEO-LIKE/SkeletonExtraction/raw/master/images/concept1.jpg) 11 | 12 | Sequential thinning by use structuring element L 13 | 14 | ![Structure Element L](https://github.com/GEO-LIKE/SkeletonExtraction/raw/master/images/concept.jpg) 15 | 16 | ### Utilizing GenerateMat to produce the following image: 17 | 18 | ![Initial Shapes](https://github.com/GEO-LIKE/SkeletonExtraction/raw/master/images/result0.jpg) 19 | 20 | ### Extract skeleton by thinning: 21 | 22 | ![Skeleton](https://github.com/GEO-LIKE/SkeletonExtraction/raw/master/images/result50.jpg) 23 | 24 | #### Reference 25 | [1] Image Processing, Analysis, and Machine Vision, Fourth Edition, Milan Sonka, Vaclav Hlavac, Roger Boyle, CENGAGE Learning. 26 | 27 | [2] Digital Image Processing, Third Edition, Rafael C. Gonzalez, Richard E. Woods, Pearson. -------------------------------------------------------------------------------- /thin.h: -------------------------------------------------------------------------------- 1 | #ifndef THIN 2 | #define THIN 3 | 4 | #include 5 | #include 6 | #include 7 | //define patterns 8 | //patterns defined in "Image Processing, Analysis, and Machine Vision Fourth Edition, Milan Sonka et. al." 9 | const int L1[3][3] = { 10 | {0,0,0}, 11 | {2,1,2}, 12 | {1,1,1} }; 13 | const int L2[3][3] = { 14 | { 1,2,0 }, 15 | { 1,1,0 }, 16 | { 1,2,0 } }; 17 | const int L3[3][3] = { 18 | { 1,1,1 }, 19 | { 2,1,2 }, 20 | { 0,0,0 } }; 21 | const int L4[3][3] = { 22 | { 0,2,1 }, 23 | { 0,1,1 }, 24 | { 0,2,1 } }; 25 | /* 26 | const int L5[3][3] = { 27 | { 2,0,0 }, 28 | { 1,1,0 }, 29 | { 2,1,2 } }; 30 | const int L6[3][3] = { 31 | { 2,1,2 }, 32 | { 1,1,0 }, 33 | { 2,0,0 } }; 34 | const int L7[3][3] = { 35 | { 2,1,2 }, 36 | { 0,1,1 }, 37 | { 0,0,2 } }; 38 | const int L8[3][3] = { 39 | { 0,0,2 }, 40 | { 0,1,1 }, 41 | { 2,1,2 } };*/ 42 | auto L_Set = { L1,L2,L3,L4 };// , L5, L6, L7, L8 }; 43 | // 44 | //bool dirExists(const std::string& dirName_in) 45 | //{ 46 | // DWORD ftyp = GetFileAttributesA(dirName_in.c_str()); 47 | // if (ftyp == INVALID_FILE_ATTRIBUTES) 48 | // return false; //something is wrong with your path! 49 | // 50 | // if (ftyp & FILE_ATTRIBUTE_DIRECTORY) 51 | // return true; // this is a directory! 52 | // 53 | // return false; // this is not a directory! 54 | //} 55 | 56 | void createFolder(std::string dirName) { 57 | std::string commandMd = "mkdir " + dirName; 58 | std::string commandRd = "rmdir /s /q " + dirName; 59 | if (std::system(commandMd.c_str())) 60 | { 61 | std::cout << "remove dir:output, and re-makedir" << std::endl; 62 | std::system(commandRd.c_str()); 63 | } 64 | std::system(commandMd.c_str()); 65 | } 66 | 67 | bool compare(cv::Mat& src, const int pattern[][3]) { 68 | bool flag = false; 69 | for (int i = 0; i < 3; i++) { 70 | for (int j = 0; j < 3; j++) { 71 | if (!src.at(i, j) && pattern[i][j] == 1) { 72 | flag = false; 73 | return flag; 74 | }else if (src.at(i, j) && pattern[i][j] == 0) { 75 | flag = false; 76 | return flag; 77 | }else 78 | flag = true; 79 | } 80 | } 81 | return flag; 82 | } 83 | void findContour(cv::Mat& src, std::vector& outlines) { 84 | //std::string dirName = "./output_Contours"; 85 | //createFolder(dirName); 86 | cv::Mat temp = src.clone(); 87 | const int B[][3] = { {1,1,1},{1,1,1},{1,1,1} }; 88 | for (int i = 1; i < src.rows-1; i++) { 89 | for (int j = 1; j < src.cols-1; j++) { 90 | cv::Mat roi = src(cv::Range(i - 1, i + 2), cv::Range(j - 1, j + 2)); 91 | if (compare(roi, B)) 92 | temp.at(i, j) = 0; 93 | } 94 | } 95 | for (int i = 1; i < src.rows - 1; i++) { 96 | for (int j = 1; j < src.cols - 1; j++) { 97 | if (temp.at(i,j)) 98 | outlines.push_back(cv::Point2i(i, j)); 99 | } 100 | } 101 | //cv::imwrite(dirName+"/outlines.jpg", temp); 102 | } 103 | bool thinning_it(cv::Mat& src,cv::Mat& ridge) { 104 | ridge = src.clone(); 105 | int flag = false; 106 | for (auto pattern : L_Set) { 107 | if (flag) 108 | return flag; 109 | else 110 | flag = true; 111 | 112 | std::vector outlines; 113 | findContour(src, outlines); 114 | if (outlines.empty()) 115 | { 116 | std::cout << "No skeleton" << std::endl; 117 | return true; 118 | } 119 | for (cv::Point2i point : outlines) { 120 | int i = point.x; 121 | int j = point.y; 122 | cv::Mat roi = src(cv::Range(i - 1, i + 2), cv::Range(j - 1, j + 2)); 123 | if (src.at(i, j) && compare(roi, pattern)) 124 | { 125 | ridge.at(i, j) = 0; 126 | flag = false; 127 | } 128 | 129 | } 130 | } 131 | return flag; 132 | } 133 | 134 | void thinning(cv::Mat& src, cv::Mat& ridge) { 135 | std::string dirName = "output"; 136 | createFolder(dirName); 137 | int i = 0; 138 | bool flag = false; 139 | ridge = src.clone(); 140 | do{ 141 | flag = thinning_it(ridge.clone(), ridge); 142 | std::cout << i << std::endl; 143 | std::string name = dirName+"/result"+ std::to_string(i++)+".jpg"; 144 | cv::imwrite(name, ridge); 145 | } while (!flag); 146 | 147 | } 148 | 149 | 150 | #endif // !THIN 151 | 152 | --------------------------------------------------------------------------------