Java OpenCV Sobel

Sobel算子介绍

索贝尔算子(Sobel operator)主要用作边缘检测,在技术上,它是一离散性差分算子,用来运算图像亮度函数的灰度之近似值。在图像的任何一点使用此算子,将会产生对应的灰度矢量或是其法矢量。

Sobel卷积因子为:
在这里插入图片描述
该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以A代表原始图像,Gx及Gy分别代表经横向及纵向边缘检测的图像灰度值,其公式如下:
在这里插入图片描述
具体运算为:

Gx = (-1)*f(x-1, y-1) + 0*f(x,y-1) + 1*f(x+1,y-1)
	+(-2)*f(x-1,y) + 0*f(x,y)+2*f(x+1,y)
	+(-1)*f(x-1,y+1) + 0*f(x,y+1) + 1*f(x+1,y+1)
	= [f(x+1,y-1)+2*f(x+1,y)+f(x+1,y+1)]-[f(x-1,y-1)+2*f(x-1,y)+f(x-1,y+1)] 
Gy =1* f(x-1, y-1) + 2*f(x,y-1)+ 1*f(x+1,y-1)
	+0*f(x-1,y) 0*f(x,y) + 0*f(x+1,y)
	+(-1)*f(x-1,y+1) + (-2)*f(x,y+1) + (-1)*f(x+1, y+1)
	= [f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1)]-[f(x-1, y+1) + 2*f(x,y+1)+f(x+1,y+1)] 

由上述公式计算得到Gy和Gx后,可以计算得到G的值,一个点的G的代表该点的梯度,如果大于某一设定范围则认为该点是边缘点。
在这里插入图片描述
为了提高效率 使用不开平方的近似值:
在这里插入图片描述
如果梯度G大于某一阀值 则认为该点(x,y)为边缘点
在这里插入图片描述
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。

OpenCV的Sobel函数介绍

Imgproc.Sobel(Mat src, Mat dst, int ddepth, int dx, int dy, int ksize, double scale, double delta, int borderType)
src:图像
dst:目标图
ddepth:输出图像的深度。针对不同的输入图像,输出目标图像有不同的深度,具体组合如下:

  • 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
  • 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
  • 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
  • 若src.depth() = CV_64F, 取ddepth = -1/CV_64F

dx:x方向上的差分阶数
dy:y方向上的差分阶数
ksize:sobel核的大小,默认为3。具体说明在下面讲述
scale:缩放因子。是sobel过滤内核因子的缩放因子。而非对影像进行的缩放因子。具体说明在下面讲述
delta:额外加的数值,就是在卷积过程中该数值会添加到每个像素上。具体说明在下面讲述
borderType:边界填充模式,默认BORDER_DEFAULT。边界填充说明

函数使用

	public static void main(String[] args) {
        //读入图片
        Mat src = Imgcodecs.imread("G:\\opencvPhoto\\photo\\picture2.jpg");
        //灰度化
        Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY);
        //横向
        Mat gray_x = new Mat();
        Imgproc.Sobel(src, gray_x, -1, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT);
        Imgcodecs.imwrite("G:\\opencvPhoto\\result\\gray_x.jpg", gray_x);
        //竖向
        Mat gray_y = new Mat();
        Imgproc.Sobel(src, gray_y, -1, 0, 1, 3, 1, 0, Core.BORDER_DEFAULT);
        Imgcodecs.imwrite("G:\\opencvPhoto\\result\\gray_y.jpg", gray_y);
        //横竖向图像融合
        Mat gray_xy = new Mat();
        Core.addWeighted(gray_x, 0.5, gray_y, 0.5, 0, gray_xy);
        Imgcodecs.imwrite("G:\\opencvPhoto\\result\\gray_xy.jpg", gray_xy);
    }

原图
在这里插入图片描述
横向
在这里插入图片描述
竖向
在这里插入图片描述
横竖融合
在这里插入图片描述

函数实现

以横向为例
filter2D原理

public static void main(String[] args) {
        OpencvTest opencvTest = new OpencvTest();
        //读入图片
        Mat src = Imgcodecs.imread("G:\\opencvPhoto\\photo\\picture2.jpg");
        //灰度化
        Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY);
        
        //对比图
        Mat temp = src.clone();

        //sobel横向内核
        Mat kernelX = new Mat(new Size(3,3), CvType.CV_32F, new Scalar(255));
        kernelX.put(0, 0, -1); kernelX.put(0, 1, 0); kernelX.put(0, 2, 1);
        kernelX.put(1, 0, -2); kernelX.put(1, 1, 0); kernelX.put(1, 2, 2);
        kernelX.put(2, 0, -1); kernelX.put(2, 1, 0); kernelX.put(2, 2, 1);

        //自己实现Sobel横向
        Mat dst = new Mat();
        Imgproc.filter2D(src, dst, -1, kernelX, new Point(-1, -1), 0, Core.BORDER_DEFAULT);
        Core.convertScaleAbs(dst, dst, 1, 0);

        //OpenCV Sobel横向
        Imgproc.Sobel(temp, temp, -1, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT);

        //结果图对比
        boolean flag = true;
        for (int row = 0; row < src.rows(); row++) {
            for (int col = 0; col < src.cols(); col++) {
                if (dst.get(row, col)[0] != temp.get(row, col)[0]) {
                    flag = false;
                    break;
                }
            }
            if (!flag){

                break;
            }
        }
        System.out.println(flag);

    }

结果输出
true

OpenCV Sobel 参数说明

ksize

ksize<0那么使用scharr内核过滤因子。scharr的内核过滤因子大小为3。dx大于等于0,dy大于等于0,并且dx+dy==1
X方向的内核因子为:
在这里插入图片描述
Y方向的内核因子为
在这里插入图片描述
ksize>0 : ksize必须为奇数,并且大小应该小于31
当 ( xorder = 1, yorder = 0, ksize = 3) 时,内核因子(即:X方向的检测因子)为:
在这里插入图片描述
当 ( xorder = 0, yorder = 1, ksize = 3) 时,内核因子(即Y方向上检测因子)为:
在这里插入图片描述
当 ( xorder = 1, yorder = 0, ksize = 5) 时,内核因子(即X方向上检测因子)为:
在这里插入图片描述

scale

	public static void main(String[] args) {

        //图片
        Mat src = new Mat(new Size(3, 3), CvType.CV_8UC1);
        src.put(0, 0, 1); src.put(0, 1, 2); src.put(0, 2, 3);
        src.put(1, 0, 1); src.put(1, 1, 2); src.put(1, 2, 3);
        src.put(2, 0, 1); src.put(2, 1, 2); src.put(2, 2, 3);


        Mat sobel1 = new Mat();
        Imgproc.Sobel(src, sobel1, -1, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT);
        System.out.println("--------------scale=1---------------");
        for (int row = 0; row < sobel1.rows(); row++) {
            for (int col = 0; col < sobel1.cols(); col++) {
                System.out.print((int)sobel1.get(row, col)[0] + "   ");
            }
            System.out.println();
        }

        Mat sobel2 = new Mat();
        Imgproc.Sobel(src, sobel2, -1, 1, 0, 3, 2, 0, Core.BORDER_DEFAULT);
        System.out.println("--------------scale=2---------------");
        for (int row = 0; row < sobel2.rows(); row++) {
            for (int col = 0; col < sobel2.cols(); col++) {
                System.out.print((int)sobel2.get(row, col)[0] + "   ");
            }
            System.out.println();
        }
        //delta = 1
        Mat sobel3 = new Mat();
        Imgproc.Sobel(src, sobel3, -1, 1, 0, 3, 3, 0, Core.BORDER_DEFAULT);
        System.out.println("--------------scale=3---------------");
        for (int row = 0; row < sobel3.rows(); row++) {
            for (int col = 0; col < sobel3.cols(); col++) {
                System.out.print((int)sobel3.get(row, col)[0] + "   ");
            }
            System.out.println();
        }

    }

结果输出

--------------scale=1---------------
0   8   0   
0   8   0   
0   8   0   
--------------scale=2---------------
0   16   0   
0   16   0   
0   16   0   
--------------scale=3---------------
0   24   0   
0   24   0   
0   24   0   

结论:scale为目标图片在计算后得出的结果上再翻scale倍

delta

测试代码
以横向为例

public static void main(String[] args) {

        //图片
        Mat src = new Mat(new Size(3, 3), CvType.CV_8UC1);
        src.put(0, 0, 1); src.put(0, 1, 2); src.put(0, 2, 3);
        src.put(1, 0, 1); src.put(1, 1, 2); src.put(1, 2, 3);
        src.put(2, 0, 1); src.put(2, 1, 2); src.put(2, 2, 3);

        //delta = -1
        Mat sobel1 = new Mat();
        Imgproc.Sobel(src, sobel1, -1, 1, 0, 3, 1, -1, Core.BORDER_DEFAULT);
        System.out.println("--------------delta = -1---------------");
        for (int row = 0; row < sobel1.rows(); row++) {
            for (int col = 0; col < sobel1.cols(); col++) {
                System.out.print((int)sobel1.get(row, col)[0] + "   ");
            }
            System.out.println();
        }
        //delta =0
        Mat sobel2 = new Mat();
        Imgproc.Sobel(src, sobel2, -1, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT);
        System.out.println("--------------delta = 0---------------");
        for (int row = 0; row < sobel2.rows(); row++) {
            for (int col = 0; col < sobel2.cols(); col++) {
                System.out.print((int)sobel2.get(row, col)[0] + "   ");
            }
            System.out.println();
        }
        //delta = 1
        Mat sobel3 = new Mat();
        Imgproc.Sobel(src, sobel3, -1, 1, 0, 3, 1, 1, Core.BORDER_DEFAULT);
        System.out.println("--------------delta = 1---------------");
        for (int row = 0; row < sobel3.rows(); row++) {
            for (int col = 0; col < sobel3.cols(); col++) {
                System.out.print((int)sobel3.get(row, col)[0] + "   ");
            }
            System.out.println();
        }

    }

结果输出
为0是因为Mat的值>=0

--------------delta = -1---------------
0   7   0   
0   7   0   
0   7   0   
--------------delta = 0---------------
0   8   0   
0   8   0   
0   8   0   
--------------delta = 1---------------
1   9   1   
1   9   1   
1   9   1     

结论:delta为目标图片在计算后得出的结果上再加delta

scale和delta综合验证

public static void main(String[] args) {
        //图片
        Mat src = new Mat(new Size(3, 3), CvType.CV_8UC1);
        src.put(0, 0, 1); src.put(0, 1, 2); src.put(0, 2, 3);
        src.put(1, 0, 1); src.put(1, 1, 2); src.put(1, 2, 3);
        src.put(2, 0, 1); src.put(2, 1, 2); src.put(2, 2, 3);

        Mat sobel1 = new Mat();
        Imgproc.Sobel(src, sobel1, -1, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT);
        System.out.println("--------------scale=1,delta=0---------------");
        for (int row = 0; row < sobel1.rows(); row++) {
            for (int col = 0; col < sobel1.cols(); col++) {
                System.out.print((int)sobel1.get(row, col)[0] + "   ");
            }
            System.out.println();
        }

        Mat sobel2 = new Mat();
        Imgproc.Sobel(src, sobel2, -1, 1, 0, 3, 2, 3, Core.BORDER_DEFAULT);
        System.out.println("--------------scale=2,delta=3---------------");
        for (int row = 0; row < sobel2.rows(); row++) {
            for (int col = 0; col < sobel2.cols(); col++) {
                System.out.print((int)sobel2.get(row, col)[0] + "   ");
            }
            System.out.println();
        }

    }

结果输出

--------------scale=1,delta=0---------------
0   8   0   
0   8   0   
0   8   0   
--------------scale=2,delta=3---------------
3   19   3   
3   19   3   
3   19   3   

过程说明

0*2+3   8*2+3  0*2+3
0*2+3   8*2+3  0*2+3
0*2+3   8*2+3  0*2+3

参考链接

https://blog.csdn.net/zhanggusheng/article/details/88936232
https://blog.csdn.net/m1109048058/article/details/77411373

扩展链接

核旋转效果

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Java中,可以使用OpenCV库来进行图像模糊检测。首先,我们需要将要检测的图像读取为OpenCV的`Mat`对象。然后,可以使用OpenCV的`blur`函数来对图像进行模糊处理。 `blur`函数的使用方法如下: ```java Imgproc.blur(src, dst, ksize); ``` 其中,`src`是输入图像的`Mat`对象,`dst`是输出图像的`Mat`对象,`ksize`是模糊核的大小。模糊核的大小越大,图像的模糊程度越高。 在进行模糊检测时,可以通过计算输入图像与模糊后的图像之间的差异来判断图像是否模糊。一种常用的方法是计算图像的梯度值,然后判断梯度值的平均值是否小于某个阈值。 下面是一个简单的示例代码: ```java import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.MatOfFloat; import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.core.CvException; import org.opencv.core.Canny; import org.opencv.core.Sobel; import org.opencv.highgui.HighGui; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; public class ImageBlurDetection { public static void main(String[] args) { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); // 读取图像 Mat image = Imgcodecs.imread("path_to_image"); // 对图像进行模糊处理 Mat blurredImage = new Mat(); Imgproc.blur(image, blurredImage, new Size(3, 3)); // 计算图像的梯度 Mat gradX = new Mat(); Mat gradY = new Mat(); Mat gradient = new Mat(); Imgproc.Sobel(blurredImage, gradX, CvType.CV_32F, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT); Imgproc.Sobel(blurredImage, gradY, CvType.CV_32F, 0, 1, 3, 1, 0, Core.BORDER_DEFAULT); Core.subtract(gradX, gradY, gradient); // 计算梯度的平均值 Scalar meanGradient = Core.mean(gradient); double averageGradient = (meanGradient.val[0] + meanGradient.val[1] + meanGradient.val[2]) / 3.0; // 判断图像是否模糊 double blurThreshold = 50.0; if (averageGradient < blurThreshold) { System.out.println("图像模糊"); } else { System.out.println("图像清晰"); } } } ``` 以上代码首先加载了OpenCV库,并导入了必要的类。然后通过`Imgcodecs.imread`函数读取图像,并使用`blur`函数对图像进行模糊处理。 接下来,使用`Sobel`函数计算模糊后图像的梯度,并通过`Core.subtract`函数计算梯度之差。然后,使用`Core.mean`函数计算梯度的平均值。 最后,判断平均梯度是否小于某个阈值,如果小于阈值,则图像模糊;否则,图像清晰。 需要注意的是,以上只是一个简单的示例代码,实际应用中可能需要根据实际情况进行调整和优化。 ### 回答2: Java OpenCV可以用于图像模糊检测。模糊检测是图像处理中常用的技术,用于检测图像中的模糊区域。 在使用Java OpenCV进行图像模糊检测前,首先需要加载图像并将其转换为灰度图像。可以使用OpenCV的灰度转换方法将彩色图像转换为灰度图像。 接下来,可以使用OpenCV中的高斯模糊方法对图像进行模糊处理。高斯模糊是一种常用的模糊滤波技术,它可以平滑图像并减少图像中的噪声。 在对图像进行高斯模糊后,可以计算图像中每个像素的梯度。梯度表示了图像中每个像素的变化情况,而模糊区域通常具有较小的梯度值。 通过对图像的梯度进行阈值处理,可以得到模糊区域的二进制掩码。该掩码可以用于标记图像中的模糊区域。 最后,可以通过对图像的二进制掩码进行形态学操作,如膨胀和腐蚀,来进一步增强模糊区域的检测效果。 使用Java OpenCV进行图像模糊检测可以帮助我们自动识别模糊图像,从而改进图像质量和视觉感受。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值