如何在 C++ 中使用 OpenCV 4.2 计算二值图像的周长

How to calculate perimeter of a binary image using OpenCV 4.2 in C++

我想计算 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));
    }

这对我来说效果很好!

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));
    }