我正在尝试使用OpenCV的cv :: threshold函数(更具体的THRESH_OTSU),只是我想用掩码(任何形状)来做,所以在计算过程中忽略外部(背景).
图像是单通道(必须如此),红色波纹仅用于标记图像上的示例多边形.
我尝试使用adaptiveThreshold,但有一些问题使我的情况不合适.
解决方法
通常,您可以使用cv :: threshold简单地计算阈值,然后使用反转掩码在dst上复制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.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; }
然后,您可以将所有函数包装在一个函数中,此处称为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,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,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,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,~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,200,200),Scalar(255),CV_FILLED); // Apply threshold with a mask Mat1b th; double th_value = threshold_with_mask(img,th,THRESH_OTSU,mask); // Show results imshow("cv::threshod",cvth); imshow("threshold_with_balue",th); waitKey(); return 0; }