兩種影象骨架提取演算法的研究(2)實現程式碼
阿新 • • 發佈:2019-02-14
1 K3M
#include <opencv2/imgproc/imgproc.hpp> #include <opencv2/objdetect/objdetect.hpp> #include <opencv2/highgui/highgui.hpp> #include<list> #include<iostream> #include<algorithm> #include<set> using namespace std; set<int> GetAi(int a[], int length)//獲取A0~A5 { set<int> vec; int neighbour[] = { 1,2,4,8,16,32,64,128,1,2,4,8,16,32,64 }; for (int i = 0; i<length; i++) for (int j = 0; j<8; j++) { int sum = 0; for (int k = j; k <= j + a[i]; k++) sum += neighbour[k]; vec.insert(sum); std::cout << sum << " "; } std::cout << std::endl; return vec; } //迭代腐蝕 bool erodephase(list<cv::Point> &border, cv::Mat&Input, int neighbour[][3], const set<int>& A) { auto pt = border.begin(); bool result = false; while(pt!= border.end()) { int weight = 0; for (int j = -1; j <= 1; ++j) for (int k = -1; k <= 1; k++) weight += neighbour[j + 1][k + 1] * Input.at<uchar>(pt->y + j, pt->x + k); if (std::find(A.begin(), A.end(), weight) != A.end()) { Input.at<uchar>(pt->y , pt->x ) = 0; pt=border.erase(pt); result = true; } else ++pt; } return result; } //找邊界 void findborder(list<cv::Point2i>& border, const cv::Mat&Input) { int cnt = 0; int rows = Input.rows; int cols = Input.cols; cv::Mat bordermat = Input.clone(); for (int row = 1; row<rows - 1; ++row) for (int col = 1; col<cols - 1; ++col) { int weight = 0; for (int j = -1; j <= 1; ++j) for (int k = -1; k <= 1; k++) { if (Input.at<uchar>(row + j, col + k) == 1) ++cnt; } if (cnt == 9) bordermat.at<uchar>(row, col) = 0; cnt = 0; } for (int row = 1; row<rows - 1; ++row) for (int col = 1; col < cols - 1; ++col) { if (bordermat.at<uchar>(row, col) == 1) border.push_back(cv::Point2i(col, row)); } } //最後一步,得到骨架 void finalerode( cv::Mat&Input ,int neighbour[][3], const set<int>& A) { int rows = Input.rows; int cols = Input.cols; for (int m = 1; m<rows - 1; ++m) for (int n = 1; n<cols - 1; ++n) { int weight = 0; for (int j = -1; j <= 1; ++j) for (int k = -1; k <= 1; k++) { weight += neighbour[j + 1][k + 1] * Input.at<uchar>(m + j, n + k); } if (std::find(A.begin(), A.end(), weight) != A.end()) Input.at<uchar>(m, n) = 0; } } void thin(cv::Mat &Input) //Input是二值影象 { int a0[] = { 1,2,3,4,5,6 }; int a1[] = { 2 }; int a2[] = { 2,3 }; int a3[] = { 2,3,4 }; int a4[] = { 2,3,4,5 }; int a5[] = { 2,3,4,5,6 }; set<int> A0 = GetAi(a0, 6); set<int> A1 = GetAi(a1, 1); set<int> A2 = GetAi(a2, 2); set<int> A3 = GetAi(a3, 3); set<int> A4 = GetAi(a4, 4); set<int> A5 = GetAi(a5, 5); list<cv::Point2i> border; bool continue_ = true; int neighbour[3][3] = { { 128,1,2 }, { 64,0,4 }, { 32,16,8 } }; while (continue_) { //找邊界,對原文方法做了小改變,但影響不大。 continue_ = false; findborder(border, Input);//Phase0 //可以在下面每一步列印結果,看每一步對提取骨架的貢獻 erodephase(border, Input, neighbour,A1);//Phase1 erodephase(border, Input, neighbour, A2);//Phase2 erodephase(border, Input, neighbour, A3);//Phase3 erodephase(border, Input, neighbour, A4);//Phase4 continue_ =erodephase(border, Input, neighbour, A5);//Phase5 border.clear(); } finalerode(Input, neighbour, A0);//最後一步 } int main() { cv::Mat raw = cv::imread("test.png",0); cv::Mat binaryImage; cv::threshold(raw, binaryImage, 180, 1, CV_THRESH_BINARY_INV); cv::imshow("二值化影象", binaryImage * 255); thin(binaryImage); cv::imshow("骨架影象", binaryImage*255); cv::cvtColor(raw, raw, CV_GRAY2BGR); for(int row=0;row<binaryImage.rows;row++) for (int col = 0; col < binaryImage.cols; col++) { if (binaryImage.at<uchar>(row, col) == 1) { raw.at<cv::Vec3b>(row, col)[0] = 0; raw.at<cv::Vec3b>(row, col)[1] = 255; raw.at<cv::Vec3b>(row, col)[1] = 0; } } cv::imshow("骨架在原圖的位置", raw); cv::waitKey(0); return 0; }
2 Zhang-Suen algorithm
/** * Code for thinning a binary image using Zhang-Suen algorithm. * * Author: Nash (nash [at] opencv-code [dot] com) * Website: http://opencv-code.com */ #include <opencv2/opencv.hpp> #include<iostream> /** * Perform one thinning iteration. * Normally you wouldn't call this function directly from your code. * * Parameters: * im Binary image with range = [0,1] * iter 0=even, 1=odd */ void thinningIteration(cv::Mat& img, int iter) { CV_Assert(img.channels() == 1); CV_Assert(img.depth() != sizeof(uchar)); CV_Assert(img.rows > 3 && img.cols > 3); cv::Mat marker = cv::Mat::zeros(img.size(), CV_8UC1); int nRows = img.rows; int nCols = img.cols; if (img.isContinuous()) { nCols *= nRows; nRows = 1; } int x, y; uchar *pAbove; uchar *pCurr; uchar *pBelow; uchar *nw, *no, *ne; // north (pAbove) uchar *we, *me, *ea; uchar *sw, *so, *se; // south (pBelow) uchar *pDst; // initialize row pointers pAbove = NULL; pCurr = img.ptr<uchar>(0); pBelow = img.ptr<uchar>(1); for (y = 1; y < img.rows-1; ++y) { // shift the rows up by one pAbove = pCurr; pCurr = pBelow; pBelow = img.ptr<uchar>(y+1); pDst = marker.ptr<uchar>(y); // initialize col pointers no = &(pAbove[0]); ne = &(pAbove[1]); me = &(pCurr[0]); ea = &(pCurr[1]); so = &(pBelow[0]); se = &(pBelow[1]); for (x = 1; x < img.cols-1; ++x) { // shift col pointers left by one (scan left to right) nw = no; no = ne; ne = &(pAbove[x+1]); we = me; me = ea; ea = &(pCurr[x+1]); sw = so; so = se; se = &(pBelow[x+1]); int A = (*no == 0 && *ne == 1) + (*ne == 0 && *ea == 1) + (*ea == 0 && *se == 1) + (*se == 0 && *so == 1) + (*so == 0 && *sw == 1) + (*sw == 0 && *we == 1) + (*we == 0 && *nw == 1) + (*nw == 0 && *no == 1); int B = *no + *ne + *ea + *se + *so + *sw + *we + *nw; int m1 = iter == 0 ? (*no * *ea * *so) : (*no * *ea * *we); int m2 = iter == 0 ? (*ea * *so * *we) : (*no * *so * *we); if (A == 1 && (B >= 2 && B <= 6)&& m1 == 0 && m2 == 0)//腐蝕四個條件 pDst[x] = 1; } } img &= ~marker; } /** * Function for thinning the given binary image * * Parameters: * src The source image, binary with range = [0,255] * dst The destination image */ void thinning(const cv::Mat& src, cv::Mat& dst) { dst = src.clone(); dst /= 255; // convert to binary image cv::Mat prev = cv::Mat::zeros(dst.size(), CV_8UC1); cv::Mat diff; do { thinningIteration(dst, 0); thinningIteration(dst, 1); cv::absdiff(dst, prev, diff); dst.copyTo(prev); static int ii = 0; std::cout << ++ii << std::endl; } while (cv::countNonZero(diff) >0);//迭代終止條件 dst *= 255; } /** * This is an example on how to call the thinning funciton above */ int main() { cv::Mat src = cv::imread("test.png",0); if (!src.data) return -1; cv::Mat binary; cv::threshold(src, binary, 180, 255, CV_THRESH_BINARY_INV); cv::imshow("二值化", binary); thinning(binary, binary); cv::imshow("src", src); cv::imshow("dst", binary); cv::waitKey(); return 0; }