OpenCV:任意方向的梯度算子生成方法

    在使用梯度算子对图像进行强化计算时候,有可能用到任意方向的梯度核;

    看code有点压抑,看看文章:OpenCV索贝尔算子原理与实现...

     此外,这篇文章写得比较多一点:中值滤波与图像锐化....

     想起很久之前的一篇文章:Sobel算子替代:特定方向上的边缘检测算子.............  

以下是C++代码:

	//生成核//根据向量方向,修改sobel算子
	std::array<cv::Mat, 2> colorWish::genKernel( cv::Vec4f  &axis, int kerRadius, float order,bool isRefine)
	{
		std::array<cv::Mat, 2>  mKs;
		cv::Mat   mK1, mK2, A0, Ar, ArVis;
		const double pi = 3.141592653;

		//计算偏离角度-使用第一个方向//注意角度,一定限制在180度之内
		double alpha = arccos(0, 0, axis[0], axis[1]);
		alpha = alpha > pi ? 2 * pi - alpha : alpha;
		//alpha = 0;
		double alphaVis = arccos(0, 0, axis[2], axis[3]);
		alphaVis = alphaVis > pi ? 2 * pi - alphaVis : alphaVis;

		//测试-标准核-0角度
		A0 = cv::Mat::zeros(kerRadius, kerRadius, CV_32FC1);
		const float r = kerRadius / 2;//直接取整
		const int c = r;
		A0.at<float>(c, c) = 0;//只取第一象限,四象限根据对称规则来取
		for (int i = 0; i < r; ++i) {
			for (int j = 0; j < r; ++j) {
				A0.at<float>(c + 1 + j, c + 1 + i) = (j + 1)*(c - i) / r;//使用线性核
			}
		}

		//旋转//根据角度进行判断,不能使用一个公式//注意下标
		Ar = cv::Mat::zeros(kerRadius, kerRadius, CV_32FC1);
		ArVis = cv::Mat::zeros(kerRadius, kerRadius, CV_32FC1);

		// 注意坐标旋转方向--象限的对应关系
		double x, y;
		for (int i = 0; i < kerRadius; ++i) {
			y = i - c;
			double d = sqrt(y *y + y*y);// *sqrt(2) / 2;//平方核
			//d = abs(y) * 1;//线性核//修改距离判断,使符合线性叠加!
			//d = r;//不修改线性,
			for (int j = 0; j < kerRadius; ++j) {
				x = j - c;
				d = sqrt(x *x + y*y);// 
				d = pow(d, order);
				//d*=sqrt(2) / 2;
				double Beta = arccos(c, c, i, j);//已约束y值为正方向//需要修改顺序
				double AReal = Beta + alpha;
				double ARealVis = Beta + alphaVis;;// Beta - alphaVis;
				 //使用线性核//y= sin ,x = cos;
				if (AReal > 0 && AReal < pi / 2){
					Ar.at< float >(j, i) = (d*cos(AReal) + 0)*(r + 1 - d*sin(AReal)) / r;//同步修改公式
				}
				else {
					if (AReal > pi / 2 && AReal < pi){
						AReal = pi - AReal;
						//std::cout << d*cos(AReal) << "   "; std::cout << d*sin(AReal) << "   ";
						Ar.at< float >(j, i) = 0 - (d*cos(AReal) + 0)*(r + 1 - d*sin(AReal)) / r;// 注意坐标旋转方向--象限的对应关系
					}else {
						if (AReal > pi  && AReal < 3 * pi / 2){
							AReal = AReal - pi;
							//std::cout << d*cos(AReal) << "   "; std::cout << d*sin(AReal) << "   ";
							Ar.at< float >(j, i) = 0 - (d*cos(AReal) + 0)*(r + 1 - d*sin(AReal)) / r;
						}else {
							if (AReal > 2 * pi){
								AReal = AReal - pi * 2;
								//std::cout << d*cos(AReal) << "   "; std::cout << d*sin(AReal) << "   ";
								Ar.at< float >(j, i) = (d*cos(AReal) + 0)*(r + 1 - d*sin(AReal)) / r;
							}else {
								AReal = pi * 2 - AReal;
								//std::cout << d*cos(AReal) << "   "; std::cout << d*sin(AReal) << "   ";
								Ar.at< float >(j, i) = (d*cos(AReal) + 0)*(r + 1 - d*sin(AReal)) / r;
							}
						}//if ( AReal > pi  && AReal < 3*pi/2 )
					}//if (AReal > pi / 2 && AReal < pi)
				}//if (AReal > pi / 2 && AReal < pi )
				 //std::cout << Ar.at<float>(j, i) << "   ";
				if (ARealVis > 0 && ARealVis < pi / 2){
					ArVis.at< float >(j, i) = d*cos(ARealVis)*(r + 1 - d*sin(ARealVis)) / r;
				}else {
					if (ARealVis > pi / 2 && ARealVis < pi){
						ARealVis = pi - ARealVis;
						ArVis.at< float >(j, i) = 0 - d*cos(ARealVis)*(r + 1 - d*sin(ARealVis)) / r;
					}else {
						if (ARealVis > pi  && ARealVis < 3 * pi / 2){
							ARealVis = ARealVis - pi;
							ArVis.at< float >(j, i) = 0 - d*cos(ARealVis)*(r + 1 - d*sin(ARealVis)) / r;
						}else {
							if (ARealVis > 2 * pi)
							{
								ARealVis = ARealVis - pi * 2;
								ArVis.at< float >(j, i) = d*cos(ARealVis)*(r + 1 - d*sin(ARealVis)) / r;//(3*pi/2,pi)
							}
							else {
								ARealVis = pi * 2 - ARealVis;
								ArVis.at< float >(j, i) = d*cos(ARealVis)*(r + 1 - d*sin(ARealVis)) / r;//(3*pi/2,pi)
							}
						}//if ( AReal > pi  && AReal < 3*pi/2 )
					}//if (AReal > pi / 2 && AReal < pi)
				}//if (AReal > pi / 2 && AReal < pi )
				 //std::cout << Ar.at<float>(j, i) << "   ";
			}//for
		}//for

		Ar.at<float>(c, c) = 0;
		ArVis.at<float>(c, c) = 0;

		//转换坐标关系
		cv::transpose(Ar, Ar);
		cv::transpose(ArVis, ArVis);

		for (int i = 0; i < kerRadius; ++i) {
			std::cout << std::endl;
			for (int j = 0; j < kerRadius; ++j) {
				//std::swap(Ar.at<float>(j, i), Ar.at<float>(i, j));
				std::cout << Ar.at<float>(j, i) << "  ";
			}
		}
		std::cout << std::endl;
		for (int i = 0; i < kerRadius; ++i) {
			std::cout << std::endl;
			for (int j = 0; j < kerRadius; ++j) {
				std::cout << ArVis.at<float>(j, i) << "  ";
			}
		}
		std::cout << std::endl;

		mKs[0] = Ar.clone();
		mKs[1] = ArVis.clone();
		return mKs;
	}//genKernel(cv::Vec4f  &axis, int kerRadius)

使用任意方向梯度核计算:下图为两个方向的梯度核的计算结果

 

使用梯度核,强化过后的图像产生了像素偏移,若得到两个方向的边缘,应该同时使用两个相反方向的核;

此外,由于计算精度问题,使用任意方向核计算准确度,始终不如使用XY方向核。因此把原始图像旋转,再进行使用简单的XY方向的梯度核效果更好!


1  2  1
-5.89793e-10  0  6.12323e-17
-1  -2  -1

-1  -5.89793e-10  1
-2  0  2
-1  -5.89793e-10  1

图像结果:

合并结果:

 

 

 

 

阅读更多
换一批

没有更多推荐了,返回首页