天天看點

OpenCV 帶掩膜的Gauss平滑/濾波

OpenCV 提供完整而豐富的圖像平滑濾波算進接口,但這些平滑都是對整幅圖像或者整幅圖像中的某個ROI區域進行的,而有些時候我們需要僅僅對某個掩膜區域内的亮度或者資料進行平滑或者濾波,下面給出了常用的Gauss平滑濾波在帶有掩膜時的一種實作(圖像的資料類型為CV_32F1,其它類型的圖像資料可仿照實作)

void MaskGaussianBlur(cv::InputArray _src, cv::InputArray _mask, cv::OutputArray _dst, cv::Size ksize, double sigmaX, double sigmaY)
{
	cv::Mat src = _src.getMat();
	cv::Mat mask = _mask.getMat();

	_dst.create(src.size(), src.type());
	cv::Mat dst = _dst.getMat();

	if (src.channels() != 1)
		return;

	if (ksize.width == 1 && ksize.height == 1)
	{
		src.copyTo(dst);
		return;
	}
	else
	{
		if (ksize.width == 1)
		{
			MaskGuassianBlur1D(src, mask, dst, ksize.height, sigmaY, true);
			return;
		}
		if (ksize.height == 1)
		{
			MaskGuassianBlur1D(src, mask, dst, ksize.width,  sigmaX, false);
			return;
		}
		cv::Mat temp(src.size(), src.type());
		MaskGuassianBlur1D(src, mask, temp, ksize.height, sigmaY, true);
		MaskGuassianBlur1D(temp, mask, dst, ksize.width,  sigmaX, false);
	}
}


void GuassianKernel(double *kernel, int size, double sigma)
{
	double *data = kernel;
	for (int i = 0; i < size; i ++)
	{
		double index = (size / 2) - i;
		*(data + i) = exp(-(index * index) / (2 * sigma * sigma + 1e-6));
	}
}

void MaskGuassianBlur1D(cv::Mat img, cv::Mat imgMask, cv::Mat &dst, int size, double sigma, bool flgCol)
{
	double *kernel = new double[size];
	GuassianKernel(kernel, size, sigma);

	double thre = 10;

	cv::Mat src, mask;

	//轉置
	if(flgCol)
	{
		src = img.t();
		mask = imgMask.t();
	}
	else
	{
		src = img;
		mask = imgMask;
	}

	int w = src.cols;
	int h = src.rows;

	int tw = w - size;
	cv::Mat imgDst = cv::Mat::zeros(h, w, CV_32F);

	for (int i = 0; i < h; i ++)
	{
		uchar *datMask = mask.ptr<uchar>(i);
		float *datSrc = src.ptr<float>(i);
		float *datDst = imgDst.ptr<float>(i);

		for (int j = size; j < tw; j ++)
		{
			if(datMask[j] <= 0)
				continue;

			double sum = 0;
			double kSum = 0;//核計數
			int hsize = size / 2;
			for (int k = -hsize; k <= hsize; k ++)
			{
				if(datMask[j + k] > 0)
				{
					double diff = fabs(datSrc[j] - datSrc[j + k]);
					if(diff < thre)
					{
						sum += kernel[k + hsize] * datSrc[j + k];
						kSum += kernel[k + hsize];
					}
				}
			}
			datDst[j] = float (sum / kSum);
		}
	}

	if(flgCol)
	{
		cv::Mat tDst = imgDst.t();
		dst = tDst.clone();
	}
	else
		dst = imgDst.clone();

	delete [] kernel;
}