首页 文章

带掩码的OpenCV阈值

提问于
浏览
6

我只是想用遮罩(任何形状)做这个功能,以便在计算过程中忽略外部(背景)功能(更具体的是 THRESH_OTSU ) .

图像是单通道(必须如此),红色波纹仅用于标记图像上的示例多边形 .

我尝试使用 adaptiveThreshold ,但有一些问题使我的情况不合适 .

enter image description here

1 回答

  • 17

    通常,您可以使用 cv::threshold 简单地计算阈值,然后使用反转的 mask 复制 src 上的 src 图像 .

    // Apply cv::threshold on all image
    thresh = cv::threshold(src, dst, thresh, maxval, type);
    
    // Copy original image on inverted mask
    src.copyTo(dst, ~mask);
    

    但是,使用 THRESH_OTSU ,您还需要仅在蒙版图像上计算阈值 . 以下代码是 thresh.cppstatic double getThreshVal_Otsu_8u(const Mat& _src) 的修改版本:

    double otsu_8u_with_mask(const Mat1b src, const Mat1b& mask)
    {
        const int N = 256;
        int M = 0;
        int i, j, h[N] = { 0 };
        for (i = 0; i < src.rows; i++)
        {
            const uchar* psrc = src.ptr(i);
            const uchar* pmask = mask.ptr(i);
            for (j = 0; j < src.cols; j++)
            {
                if (pmask[j])
                {
                    h[psrc[j]]++;
                    ++M;
                }
            }
        }
    
        double mu = 0, scale = 1. / (M);
        for (i = 0; i < N; i++)
            mu += i*(double)h[i];
    
        mu *= scale;
        double mu1 = 0, q1 = 0;
        double max_sigma = 0, max_val = 0;
    
        for (i = 0; i < N; i++)
        {
            double p_i, q2, mu2, sigma;
    
            p_i = h[i] * scale;
            mu1 *= q1;
            q1 += p_i;
            q2 = 1. - q1;
    
            if (std::min(q1, q2) < FLT_EPSILON || std::max(q1, q2) > 1. - FLT_EPSILON)
                continue;
    
            mu1 = (mu1 + i*p_i) / q1;
            mu2 = (mu - q1*mu1) / q2;
            sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2);
            if (sigma > max_sigma)
            {
                max_sigma = sigma;
                max_val = i;
            }
        }
        return max_val;
    }
    

    然后,您可以将所有函数包装在一个函数中,此处称为 threshold_with_mask ,它将为您包装所有不同的案例 . 如果没有遮罩,或遮罩是全白的,则使用 cv::threshold . 否则,使用上述方法之一 . 请注意,此包装器仅适用于 CV_8UC1 图像(为简单起见,您可以轻松地将其展开以与其他类型一起使用,如果需要),并接受所有 THRESH_XXX 组合作为原始 cv::threshold .

    double threshold_with_mask(Mat1b& src, Mat1b& dst, double thresh, double maxval, int type, const Mat1b& mask = Mat1b())
    {
        if (mask.empty() || (mask.rows == src.rows && mask.cols == src.cols && countNonZero(mask) == src.rows * src.cols))
        {
            // If empty mask, or all-white mask, use cv::threshold
            thresh = cv::threshold(src, dst, thresh, maxval, type);
        }
        else
        {
            // Use mask
            bool use_otsu = (type & THRESH_OTSU) != 0;
            if (use_otsu)
            {
                // If OTSU, get thresh value on mask only
                thresh = otsu_8u_with_mask(src, mask);
                // Remove THRESH_OTSU from type
                type &= THRESH_MASK;
            }
    
            // Apply cv::threshold on all image
            thresh = cv::threshold(src, dst, thresh, maxval, type);
    
            // Copy original image on inverted mask
            src.copyTo(dst, ~mask);
        }
        return thresh;
    }
    

    以下是完整的参考代码:

    #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace std;
    using namespace cv;
    
    // Modified from thresh.cpp
    // static double getThreshVal_Otsu_8u(const Mat& _src)
    
    double otsu_8u_with_mask(const Mat1b src, const Mat1b& mask)
    {
        const int N = 256;
        int M = 0;
        int i, j, h[N] = { 0 };
        for (i = 0; i < src.rows; i++)
        {
            const uchar* psrc = src.ptr(i);
            const uchar* pmask = mask.ptr(i);
            for (j = 0; j < src.cols; j++)
            {
                if (pmask[j])
                {
                    h[psrc[j]]++;
                    ++M;
                }
            }
        }
    
        double mu = 0, scale = 1. / (M);
        for (i = 0; i < N; i++)
            mu += i*(double)h[i];
    
        mu *= scale;
        double mu1 = 0, q1 = 0;
        double max_sigma = 0, max_val = 0;
    
        for (i = 0; i < N; i++)
        {
            double p_i, q2, mu2, sigma;
    
            p_i = h[i] * scale;
            mu1 *= q1;
            q1 += p_i;
            q2 = 1. - q1;
    
            if (std::min(q1, q2) < FLT_EPSILON || std::max(q1, q2) > 1. - FLT_EPSILON)
                continue;
    
            mu1 = (mu1 + i*p_i) / q1;
            mu2 = (mu - q1*mu1) / q2;
            sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2);
            if (sigma > max_sigma)
            {
                max_sigma = sigma;
                max_val = i;
            }
        }
    
        return max_val;
    }
    
    double threshold_with_mask(Mat1b& src, Mat1b& dst, double thresh, double maxval, int type, const Mat1b& mask = Mat1b())
    {
        if (mask.empty() || (mask.rows == src.rows && mask.cols == src.cols && countNonZero(mask) == src.rows * src.cols))
        {
            // If empty mask, or all-white mask, use cv::threshold
            thresh = cv::threshold(src, dst, thresh, maxval, type);
        }
        else
        {
            // Use mask
            bool use_otsu = (type & THRESH_OTSU) != 0;
            if (use_otsu)
            {
                // If OTSU, get thresh value on mask only
                thresh = otsu_8u_with_mask(src, mask);
                // Remove THRESH_OTSU from type
                type &= THRESH_MASK;
            }
    
            // Apply cv::threshold on all image
            thresh = cv::threshold(src, dst, thresh, maxval, type);
    
            // Copy original image on inverted mask
            src.copyTo(dst, ~mask);
        }
        return thresh;
    }
    
    
    int main()
    {
        // Load an image
        Mat1b img = imread("D:\\SO\\img\\nice.jpg", IMREAD_GRAYSCALE);
    
        // Apply OpenCV version
        Mat1b cvth;
        double cvth_value = threshold(img, cvth, 100, 255, THRESH_OTSU);
    
        // Create a binary mask
        Mat1b mask(img.rows, img.cols, uchar(0));
        rectangle(mask, Rect(100, 100, 200, 200), Scalar(255), CV_FILLED);
    
        // Apply threshold with a mask
        Mat1b th;
        double th_value = threshold_with_mask(img, th, 100, 255, THRESH_OTSU, mask);
    
        // Show results
        imshow("cv::threshod", cvth);
        imshow("threshold_with_balue", th);
        waitKey();
    
        return 0;
    }
    

相关问题