如何用opencv实现任意联通区域的最大内接圆

如何用opencv实现任意联通区域的最大内接圆,第1张

代码 1)Two-pass算法的一种实现说明:基于OpenCV和C++实现,领域:4-领域。实现与算法肢腔描述稍有差别(具体为记录具有相等关系的label方法实现上)。 // Connected Component Analysis/Labeling By Two-Pass Algorithm // Author: www.icvpr.com// Blog : http://blog.csdn.net/icvpr #include <iostream> #include <string> #include <list> #include <vector> #include <map> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/御慧highgui.hpp> void icvprCcaByTwoPass(const cv::Mat&_binImg, cv::Mat&_lableImg) { /镇饥答/ connected component analysis (4-component) // use two-pass algorithm // 1. first pass: label each foreground pixel with a label // 2. second pass: visit each labeled pixel and merge neighbor labels // // foreground pixel: _binImg(x,y) = 1 // background pixel: _binImg(x,y) = 0 if (_binImg.empty() || _binImg.type() != CV_8UC1) { return }// 1. first pass_lableImg.release() _binImg.convertTo(_lableImg, CV_32SC1)int label = 1 // start by 2 std::vector<int>labelSet labelSet.push_back(0) // background: 0 labelSet.push_back(1) // foreground: 1int rows = _binImg.rows - 1 int cols = _binImg.cols - 1 for (int i = 1i <rowsi++) { int* data_preRow = _lableImg.ptr<int>(i-1) int* data_curRow = _lableImg.ptr<int>(i) for (int j = 1j <colsj++) { if (data_curRow[j] == 1) { std::vector<int>neighborLabels neighborLabels.reserve(2) int leftPixel = data_curRow[j-1] int upPixel = data_preRow[j] if ( leftPixel >1) { neighborLabels.push_back(leftPixel) } if (upPixel >1) { neighborLabels.push_back(upPixel) }if (neighborLabels.empty()) { labelSet.push_back(++label) // assign to a new label data_curRow[j] = label labelSet[label] = label } else { std::sort(neighborLabels.begin(), neighborLabels.end()) int smallestLabel = neighborLabels[0]data_curRow[j] = smallestLabel// save equivalence for (size_t k = 1k <neighborLabels.size()k++) { int tempLabel = neighborLabels[k] int&oldSmallestLabel = labelSet[tempLabel] if (oldSmallestLabel >smallestLabel) { labelSet[oldSmallestLabel] = smallestLabel oldSmallestLabel = smallestLabel } else if (oldSmallestLabel <smallestLabel) { labelSet[smallestLabel] = oldSmallestLabel } } } } } }// update equivalent labels // assigned with the smallest label in each equivalent label set for (size_t i = 2i <labelSet.size()i++) { int curLabel = labelSet[i] int preLabel = labelSet[curLabel] while (preLabel != curLabel) { curLabel = preLabel preLabel = labelSet[preLabel] } labelSet[i] = curLabel } // 2. second pass for (int i = 0i <rowsi++) { int* data = _lableImg.ptr<int>(i) for (int j = 0j <colsj++) { int&pixelLabel = data[j] pixelLabel = labelSet[pixelLabel] } } } 2)Seed-Filling种子填充方法说明:基于OpenCV和C++实现;领域:4-领域。 // Connected Component Analysis/Labeling By Seed-Filling Algorithm // Author: www.icvpr.com// Blog : http://blog.csdn.net/icvpr #include <iostream> #include <string> #include <list> #include <vector> #include <map> #include <stack> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> void icvprCcaBySeedFill(const cv::Mat&_binImg, cv::Mat&_lableImg) { // connected component analysis (4-component) // use seed filling algorithm // 1. begin with a foreground pixel and push its foreground neighbors into a stack // 2. pop the top pixel on the stack and label it with the same label until the stack is empty // // foreground pixel: _binImg(x,y) = 1 // background pixel: _binImg(x,y) = 0 if (_binImg.empty() || _binImg.type() != CV_8UC1) { return }_lableImg.release() _binImg.convertTo(_lableImg, CV_32SC1)int label = 1 // start by 2int rows = _binImg.rows - 1 int cols = _binImg.cols - 1 for (int i = 1i <rows-1i++) { int* data= _lableImg.ptr<int>(i) for (int j = 1j <cols-1j++) { if (data[j] == 1) { std::stack<std::pair<int,int>>neighborPixels neighborPixels.push(std::pair<int,int>(i,j)) // pixel position: <i,j> ++label // begin with a new label while (!neighborPixels.empty()) { // get the top pixel on the stack and label it with the same label std::pair<int,int>curPixel = neighborPixels.top() int curX = curPixel.first int curY = curPixel.second _lableImg.at<int>(curX, curY) = label// pop the top pixel neighborPixels.pop()// push the 4-neighbors (foreground pixels) if (_lableImg.at<int>(curX, curY-1) == 1) {// left pixel neighborPixels.push(std::pair<int,int>(curX, curY-1)) } if (_lableImg.at<int>(curX, curY+1) == 1) {// right pixel neighborPixels.push(std::pair<int,int>(curX, curY+1)) } if (_lableImg.at<int>(curX-1, curY) == 1) {// up pixel neighborPixels.push(std::pair<int,int>(curX-1, curY)) } if (_lableImg.at<int>(curX+1, curY) == 1) {// down pixel neighborPixels.push(std::pair<int,int>(curX+1, curY)) } } } } } } 3)颜色标记(用于显示) // Connected Component Analysis/Labeling -- Color Labeling // Author: www.icvpr.com// Blog : http://blog.csdn.net/icvpr #include <iostream> #include <string> #include <list> #include <vector> #include <map> #include <stack> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> cv::Scalar icvprGetRandomColor() { uchar r = 255 * (rand()/(1.0 + RAND_MAX)) uchar g = 255 * (rand()/(1.0 + RAND_MAX)) uchar b = 255 * (rand()/(1.0 + RAND_MAX)) return cv::Scalar(b,g,r) } void icvprLabelColor(const cv::Mat&_labelImg, cv::Mat&_colorLabelImg) { if (_labelImg.empty() || _labelImg.type() != CV_32SC1) { return }std::map<int, cv::Scalar>colorsint rows = _labelImg.rows int cols = _labelImg.cols_colorLabelImg.release() _colorLabelImg.create(rows, cols, CV_8UC3) _colorLabelImg = cv::Scalar::all(0)for (int i = 0i <rowsi++) { const int* data_src = (int*)_labelImg.ptr<int>(i) uchar* data_dst = _colorLabelImg.ptr<uchar>(i) for (int j = 0j <colsj++) { int pixelValue = data_src[j] if (pixelValue >1) { if (colors.count(pixelValue) <= 0) { colors[pixelValue] = icvprGetRandomColor() } cv::Scalar color = colors[pixelValue] *data_dst++ = color[0] *data_dst++ = color[1] *data_dst++ = color[2] } else { data_dst++ data_dst++ data_dst++ } } } } 4)测试程序 // Connected Component Analysis/Labeling -- Test code // Author: www.icvpr.com// Blog : http://blog.csdn.net/icvpr #include <iostream> #include <string> #include <list> #include <vector> #include <map> #include <stack> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> int main(int argc, char** argv) { cv::Mat binImage = cv::imread("../icvpr.com.jpg", 0) cv::threshold(binImage, binImage, 50, 1, CV_THRESH_BINARY_INV)// connected component labeling cv::Mat labelImg icvprCcaByTwoPass(binImage, labelImg) //icvprCcaBySeedFill(binImage, labelImg)// show result cv::Mat grayImg labelImg *= 10 labelImg.convertTo(grayImg, CV_8UC1) cv::imshow("labelImg", grayImg)cv::Mat colorLabelImg icvprLabelColor(labelImg, colorLabelImg) cv::imshow("colorImg", colorLabelImg) cv::waitKey(0)return 0 }

给你提供个思路吧。

首先我假设你提取出的圆轮廓还算比较歼数规整,圆度不至于偏差太大。

你只需要求出最大内切圆和最小外接圆即可。算法比较容易设计,简单来看可以分为三部分:

1)确定圆心。首先,你先填充原来的轮廓图,获得一个实心的圆。然后,统计这个圆的质心。

程序写起来很容易,就是x放心和y放心各自按照坐标加权平均而已。

2)求最大内切圆。在上一步获得的圆心基础上,计算出最大的半径值,使得以此半径构成的圆被最大地包含差改轮在原始的轮廓圆内部。这种包含虚信关系直接使用俩图做差看剩余像素点是否在内切圆内即可。

3)最小外接圆。基本思路同2)。只不过这里要找一个最小的半径值,使得以此半径构成的圆最小地包含原始的轮廓圆。包含关系与2)类似,不再啰嗦。

2)、3)完成之后,问题就解决了。用matlab写成代码应该比较容易,你自己应该能做。

当然,以上的算法求出的圆心可能需要微调一下,即你在进行2)和3)时,需要在算出的圆心附近做一下简单的搜索,以获得一个最优的未知,这个是小trick,注意下即可。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12548347.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-26
下一篇 2023-05-26

发表评论

登录后才能评论

评论列表(0条)

保存