我只是想用遮罩(任何形状)做这个功能,以便在计算过程中忽略外部(背景)功能(更具体的是 THRESH_OTSU ) .
THRESH_OTSU
图像是单通道(必须如此),红色波纹仅用于标记图像上的示例多边形 .
我尝试使用 adaptiveThreshold ,但有一些问题使我的情况不合适 .
adaptiveThreshold
通常,您可以使用 cv::threshold 简单地计算阈值,然后使用反转的 mask 复制 src 上的 src 图像 .
cv::threshold
mask
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) 的修改版本:
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 .
threshold_with_mask
CV_8UC1
THRESH_XXX
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; }
1 回答
通常,您可以使用
cv::threshold
简单地计算阈值,然后使用反转的mask
复制src
上的src
图像 .但是,使用
THRESH_OTSU
,您还需要仅在蒙版图像上计算阈值 . 以下代码是thresh.cpp
中static double getThreshVal_Otsu_8u(const Mat& _src)
的修改版本:然后,您可以将所有函数包装在一个函数中,此处称为
threshold_with_mask
,它将为您包装所有不同的案例 . 如果没有遮罩,或遮罩是全白的,则使用cv::threshold
. 否则,使用上述方法之一 . 请注意,此包装器仅适用于CV_8UC1
图像(为简单起见,您可以轻松地将其展开以与其他类型一起使用,如果需要),并接受所有THRESH_XXX
组合作为原始cv::threshold
.以下是完整的参考代码: