如何在 C++ 中使用 OpenCV 4.2 计算二进制图像的周长
Posted
技术标签:
【中文标题】如何在 C++ 中使用 OpenCV 4.2 计算二进制图像的周长【英文标题】:How to calculate perimeter of a binary image using OpenCV 4.2 in C++ 【发布时间】:2020-04-21 15:06:28 【问题描述】:我想计算 512*512 维二值图像中白色斑点的周长。图像将只有一个斑点。我早些时候在 OpenCV 3 中使用了以下代码,但不知何故它在 OpenCV 4.2 中不起作用。 IplImage
在最新版本中已弃用。而且我不能将 Mat 对象直接传递给 cvFindContours
函数。我是opencv的新手,我不知道它是如何工作的。其他有关周界的相关问题仍未得到解答。
总而言之,以下在 opencv 3 中有效,但在当前的 opencv 版本 (4.2) 中无效。
int getPerimeter(unsigned char* inImagePtr, int inW, int inH)
int sumEven = 0; int sumOdd = 0;
int sumCorner = 0; int prevCode = 0;
//create a mat input Image
cv::Mat inImage(inH, inW, CV_8UC1, inImagePtr);
//create four connected structuring element
cv::Mat element = cv::Mat::zeros(3, 3, CV_8UC1);
element.data[1] = 1; element.data[3] = 1;
element.data[4] = 1; element.data[5] = 1;
element.data[7] = 1;
//erode input image
cv::Mat erodeImage;
erode(inImage, erodeImage, element);
//Invert eroded Image
cv::threshold(erodeImage, erodeImage, 0, 255, THRESH_BINARY_INV);
//multiply with original binary Image to get the edge Image
cv::Mat edge = erodeImage.mul(inImage);
//Get chain code of the blob
CvChain* chain = 0;
CvMemStorage* storage = 0;
storage = cvCreateMemStorage(0);
auto temp = new IplImage(edge);
cvFindContours(temp, storage, (CvSeq**)(&chain), sizeof(*chain), CV_RETR_EXTERNAL, CV_CHAIN_CODE);
delete temp;
for (; chain != NULL; chain = (CvChain*)chain->h_next)
CvSeqReader reader;
int i, total = chain->total;
cvStartReadSeq((CvSeq*)chain, &reader, 0);
for (i = 0; i < total; i++)
char code;
CV_READ_SEQ_ELEM(code, reader);
if (code % 2 == 0)
sumEven++;
else
sumOdd++;
if (i > 0)
if (code != prevCode)
sumCorner++;
prevCode = code;
float perimeter = (float)sumEven*0.980 + (float)sumOdd*1.406 - (float)sumCorner*0.091;
return (roundf(perimeter));
【问题讨论】:
那是因为旧的 C API,包括IplImage
,在大约十年前就被弃用了。如果你搜索“OpenCV find contours C++”,你会找到a more current tutorial。
【参考方案1】:
这对我来说效果很好!
int getPerimeter(unsigned char* inImagePtr, int inW, int inH)
// create a mat input Image
cv::Mat inImage(inH, inW, CV_8UC1, inImagePtr);
// create four connected structuring element
cv::Mat element = cv::Mat::zeros(3, 3, CV_8UC1);
element.data[1] = 1;
element.data[3] = 1;
element.data[4] = 1;
element.data[5] = 1;
element.data[7] = 1;
// erode input image
cv::Mat erodeImage;
erode(inImage, erodeImage, element);
// Invert eroded Image
cv::threshold(erodeImage, erodeImage, 0, 255, THRESH_BINARY_INV);
// multiply with original binary Image to get the edge Image
cv::Mat edge = erodeImage.mul(inImage);
vector<vector<Point>> contours;
findContours(edge, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); // Retrieve only external contour
int preValue[2];
int nextValue[2];
int sumEven = 0;
int sumOdd = 0;
//vector<Point>::iterator itr;
for (int ii = 0; ii < contours[0].size(); ii++)
Point pt = contours[0].at(ii);
preValue[0] = pt.x;
preValue[1] = pt.y;
if (ii != contours[0].size() - 1)
Point pt_next = contours[0].at(ii + 1);
nextValue[0] = pt_next.x;
nextValue[1] = pt_next.y;
else
Point pt_next = contours[0].at(0);
nextValue[0] = pt_next.x;
nextValue[1] = pt_next.y;
if ((preValue[0] == nextValue[0]) or (preValue[1] == nextValue[1]))
sumEven = sumEven + abs(nextValue[0] - preValue[0]) + abs(nextValue[1] - preValue[1]);
else
sumOdd = sumOdd + abs(nextValue[0] - preValue[0]);
int sumCorner = contours[0].size() - 1;
float perimeter = round(sumEven * 0.980 + sumOdd * 1.406 - sumCorner * 0.091);
return (roundf(perimeter));
【讨论】:
以上是关于如何在 C++ 中使用 OpenCV 4.2 计算二进制图像的周长的主要内容,如果未能解决你的问题,请参考以下文章
如何将 opencv c++ 代码集成到使用 c# 以统一 3D 开发的移动应用程序中