OpenCV——图像形态学

opencv基础篇 ——(六)图像形态学 图像形态学 阅读详情

一、像素的距离

图像中像素之间的距离有多种度量方式,其中常用的有欧式距离、棋盘距离和街区距离。

1. 欧式距离:
欧式距离是指连接两个点的直线距离。欧式距离用数学公式可以表达如下:

//勾股定理计算
d^2 = (x1-x2)^2+(y1-y2)^2

2. 棋盘距离:

棋盘距离也叫切比雪夫距离,棋盘距离可以用国际象棋中王的走法来说明,也就是王从一个位置走到另一个位置最少需要多少步。

例如,王从b3走到g6最少需要几步?即b3->c4->d5->e6->f6->g6,共5步。不难发现,起始位置和终止位置在棋盘上的距离是,横向5格,纵向3格,所以棋盘距离实际上是横向距离与纵向距离之间的较大者,用数学公式表达如下:

//两个向量在任意坐标维度上的最大差值
d = max(|x1-x2|, |y1-y2|)

3. 街区距离:
街区距离也称为曼哈度距离。与棋盘距离不同,在街区距离中只允许横向或纵向移动,不允许斜向移动。那么,上例中王的两个位置即为8步。用数据公式表达如下:

d = |x1-x2| + |y1-y2|
//计算一张图像中非零像素到最近的零像素的距离,即到零像素的最短距离
void Imgproc.distanceTransform(Mat src, Mat dst, int dstanceType, int maskSize)
  • src:输入图像,必须为8位单通道
  • dst:含计算距离的输出图像,图像深度为CV_8U或CV_32F的单通道图像,尺寸与输入图像相同。
  • distanceType:距离类型,可选参数如下:
    • Imgproc.DIST_USER:用户自定义距离
    • Imgproc.DIST_L1:街区距离
    • Imgproc.DIST_L2:欧式距离
    • Imgproc.DIST_C:棋盘距离
    • Imgproc.DIST_L12:d=2(sqrt(1+x*x/2)-1)
    • Imgproc.DIST_FAIR:d=c^2(|x|/c-log(1+|x|/c)),其中c=1.3998
    • Imgproc.DIST_WELSCH:d=c^2/2(1-exp(-(x/c)^2)),其中c=2.9846
    • Imgproc.DIST_HUBER:d=|x|<c ? x^2/2 : c(|x|-c/2),其中c=1.345
  • maskSize:距离变化掩码矩阵尺寸,可选参数如下
    • Imgproc.DIST_MASK_3:数字值=3
    • Imgproc.DIST_MASK_5:数字值=5
    • Imgproc.DIST_MASK_PRECISE

为了使用加速算法,掩码矩阵必须是对称的。在计算欧氏距离时,掩码矩阵为3时只是粗略计算像素距离(水平和垂直方向的变化量为1,对角线方向的变化量为1.4)。

public class DistanceTransform {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //构建一个5*5的小矩阵并导入数据
        Mat src = new Mat(5, 5, CvType.CV_8UC1);
        byte[] data = new byte[] {
            1, 1, 1, 1, 1,
            1, 1, 1, 1, 1,
            1, 1, 0, 1, 1,
            1, 1, 1, 1, 1,
            1, 1,1 ,1 , 1
        };
        src.put(0, 0, data);
        //打印矩阵数据
        System.out.println("输入矩阵:");
        System.out.println(src.dump());
        System.out.println();
        //计算棋盘距离并输出
        Mat dst = new Mat();
        Imgproc.distanceTransform(src, dst, Imgproc.DIST_C, 3);
        System.out.println("棋盘距离:");
        System.out.println(dst.dump());
        System.out.println();
        //计算街区距离并输出
        Imgproc.distanceTransform(src, dst, Imgproc.DIST_L1, 3);
        System.out.println("街区距离:");
        System.out.println(dst.dump());
        System.out.println();
        //计算欧氏距离并输出
        Imgproc.distanceTransform(src, dst, Imgproc.DIST_L2, 3);
        System.out.println("欧氏距离:");
        System.out.println(dst.dump());
        System.out.println();
    }
}
输入矩阵:
[  1,   1,   1,   1,   1;
   1,   1,   1,   1,   1;
   1,   1,   0,   1,   1;
   1,   1,   1,   1,   1;
   1,   1,   1,   1,   1]

棋盘距离:
[2, 2, 2, 2, 2;
 2, 1, 1, 1, 2;
 2, 1, 0, 1, 2;
 2, 1, 1, 1, 2;
 2, 2, 2, 2, 2]

街区距离:
[4, 3, 2, 3, 4;
 3, 2, 1, 2, 3;
 2, 1, 0, 1, 2;
 3, 2, 1, 2, 3;
 4, 3, 2, 3, 4]

欧氏距离:
[2.7385864, 2.324295, 1.9100037, 2.324295, 2.7385864;
 2.324295, 1.3692932, 0.95500183, 1.3692932, 2.324295;
 1.9100037, 0.95500183, 0, 0.95500183, 1.9100037;
 2.324295, 1.3692932, 0.95500183, 1.3692932, 2.324295;
 2.7385864, 2.324295, 1.9100037, 2.324295, 2.7385864]

上面的程序仅用于说明像素距离的计算方法。利用计算出的距离可以实现很多功能。下面示例说明如何用像素距离实现轮廓的细化:

public class Thinning {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //读取图像为灰度图
        Mat image = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/wang.png");
        Mat gray = new Mat();
        Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY);
        //将灰度图反相并显示
        Core.bitwise_not(gray, gray);
        HighGui.imshow("gray", gray);
        HighGui.waitKey(0);
        //进行高斯滤波和二值化处理
        Imgproc.GaussianBlur(gray, gray, new Size(5, 5), 2);
        Imgproc.threshold(gray, gray, 20, 255, Imgproc.THRESH_BINARY);
        HighGui.imshow("Binary", gray);
        HighGui.waitKey(0);
        //计算街区距离
        Mat thin = new Mat(gray.size(), CvType.CV_32FC1);
        Imgproc.distanceTransform(gray, thin, Imgproc.DIST_L1, 3);
        //获取最大的街区距离
        float max = 0;
        for (int i = 0; i < thin.rows(); i++) {
            for (int j = 0; j < thin.cols(); j++) {
                float[] f = new float[3];
                thin.get(i, j, f);
                if (f[0] > max) {
                    max = f[0];
                }
            }
        }
        //定义用于显示结果的矩阵,背景为全黑
        Mat show = Mat.zeros(gray.size(), CvType.CV_8UC1);
        //将距离符合一定条件的像素设为白色
        for (int i = 0; i < thin.rows(); i++) {
            for (int j = 0; j < thin.cols(); j++) {
                float[] f = new float[3];
                thin.get(i, j, f);
                if (f[0] > max / 3) {
                    show.put(i, j, 255);
                }
            }
        }
        //在屏幕上显示最后结果
        HighGui.imshow("thin", show);
        HighGui.waitKey(0);
        System.exit(0);
    }
}

原图灰度图反相:
在这里插入图片描述

二值化:
在这里插入图片描述

通过计算街区距离,细化为原来的1/3:
在这里插入图片描述

二、像素的邻域

像素的邻域是指与某一像素相邻的像素集合。邻域通常分为4邻域、8邻域和D邻域,如下图:

//4邻域,当前像素为中心,上、下、左、右4个像素
   1
 1 P 1
   1

//8邻域,当前像素为中心,上、下、左、右、左上、左下、右上、右下8个像素
1 1 1
1 P 1
1 1 1

//D邻域,当前像素为中心,左上、左下、右上、右下4个像素
1   1
  P
1   1	

8邻域=4邻域+D邻域,OpenCV中有如下函数标记图像中的连通域:

int Imgproc.connectedComponents(Mat image, Mat labels, int connectivity, int ltype)
  • image:需要标记的8位单通道图像
  • labels:标记不同连通域后的输出图像
  • connectivity:标记连通域时的邻域种类,8代表8邻域,4代表4邻域
  • ltype:输出图像的标签类型,目前支持CV_32S和CV_16U
  • int返回值:连通域个数
public class ConnectedComponents {
    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //读取图像并显示
        Mat src = Imgcodecs.imread("F:/IDEAworkspace/opencv/src/main/java/demo2/seed.png");
        HighGui.imshow("src", src);
        HighGui.waitKey(0);
        //将图像转位灰度图并二值化
        Mat gray = new Mat();
        Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
        Core.bitwise_not(gray, gray);//反相操作
        Mat binary = new Mat();
        Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
        //在屏幕显示二值图
        HighGui.imshow("Binary", binary);
        HighGui.waitKey(0);
        //标记连通域
        Mat labels = new Mat(src.size(), CvType.CV_32S);
        //num为连通域个数
        int num = Imgproc.connectedComponents(binary, labels, 8, CvType.CV_32S);
        //定义颜色数组,用于不同的连通域
        Scalar[] colors = new Scalar[num];
        //随机生成颜色
        Random rd = new Random();
        for (int i = 0; i < num; i++) {
            int r = rd.nextInt(256);
            int g = rd.nextInt(256);
            int b = rd.nextInt(256);
            colors[i] = new Scalar(r, g, b);
        }
        //标记各连通域,dst为用于标记的图像
        Mat dst = new Mat(src.size(), src.type(), new Scalar(255, 255, 255));
        int width = src.cols();
        int height = src.rows();
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                //获取标签号
                int label = (int)labels.get(i, j)[0];
                //黑色背景色不变
                if (label == 0) {
                    continue;
                }
                //根据标签号设置颜色
                double[] val = new double[3];
                val[0] = colors[label].val[0];
                val[1] = colors[label].val[1];
                val[2] = colors[label].val[2];
                dst.put(i, j, val);
            }
        }
        //在屏幕上显示最后结果
        HighGui.imshow("labelled", dst);
        HighGui.waitKey(0);
        System.exit(0);
    }
}

原图:
在这里插入图片描述

二值图:
在这里插入图片描述

用不同颜色标记后的连通域:

在这里插入图片描述

//标记图像中的连通域,并输出统计信息
int Imgproc.connectedComponentsWithStats(Mat image, Mat labels, Mat stats, Mat centroids)
  • image:需要标记的8位单通道图像
  • labels:标记不同连通域后的输出图像
  • stats:每个标签的统计信息输出,含背景标签,数据类型为CV_32S
  • centroids:每个连通域的质心坐标,数据类型为CV_64F
public class ConnectedComponentsWithStats {
    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //读取图像并显示
        Mat src = Imgcodecs.imread("F:/IDEAworkspace/opencv/src/main/java/demo2/seed.png");
        HighGui.imshow("src", src);
        HighGui.waitKey(0);
        //将图像转位灰度图并二值化
        Mat gray = new Mat();
        Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
        Core.bitwise_not(gray, gray);//反相操作
        Mat binary = new Mat();
        Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
        //在屏幕显示二值图
        HighGui.imshow("Binary", binary);
        HighGui.waitKey(0);
        //标记连通域
        Mat labels = new Mat(src.size(), CvType.CV_32S);
        Mat stats = new Mat();
        Mat centroids = new Mat();
        int num = Imgproc.connectedComponentsWithStats(binary, labels, stats, centroids);
        //定义颜色数组,用于不同的连通域
        Scalar[] colors = new Scalar[num];
        //随机生成颜色
        Random rd = new Random();
        for (int i = 0; i < num; i++) {
            int r = rd.nextInt(256);
            int g = rd.nextInt(256);
            int b = rd.nextInt(256);
            colors[i] = new Scalar(r, g, b);
        }
        //标记各连通域,dst为用于标记的图像
        Mat dst = new Mat(src.size(), src.type(), new Scalar(255, 255, 255));
        int width = src.cols();
        int height = src.rows();
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                //获取标签号
                int label = (int)labels.get(i, j)[0];
                //将背景以外的连通域设为黑色
                if (label != 0) {
                    double[] val = new double[]{0, 0, 0};
                    dst.put(i, j, val);
                }
            }
        }
        //绘制各连通域的质心和外接矩形
        for (int i = 1; i < num; i++) {
            //获取连通域中心位置
            double cx = centroids.get(i, 0)[0];
            double cy = centroids.get(i, 1)[0];
            int left = (int)stats.get(i, Imgproc.CC_STAT_LEFT)[0];
            int top = (int)stats.get(i, Imgproc.CC_STAT_TOP)[0];
            width = (int)stats.get(i, Imgproc.CC_STAT_WIDTH)[0];
            height = (int)stats.get(i, Imgproc.CC_STAT_HEIGHT)[0];
            //绘制连通域质心
            Imgproc.circle(dst, new Point(cx, cy), 2, new Scalar(0, 0, 255), 2, 8, 0);
            //绘制连通域外接矩形
            Imgproc.rectangle(dst, new Point(left, top), new Point(left + width, top + height), colors[i], 2, 8, 0);
        }

        //在屏幕上显示最后结果
        HighGui.imshow("labelled", dst);
        HighGui.waitKey(0);
        System.exit(0);
    }
}

原图:
在这里插入图片描述

二值化:
在这里插入图片描述

标记质心和外接矩形的连通域图:

在这里插入图片描述

三、膨胀与腐蚀

3.1、结构元素

腐蚀和膨胀是形态学中最基本的操作,其他的形态学操作,如开运算、闭运算、顶帽、黑帽等,本质上都是腐蚀和膨胀的组合运算。形态学一般需要两个输入参数,一个是用于操作的图像,另一个是类似卷积核的元素,称为结构元素。结构元素还有一个用于定位的参考点,称为锚点。

//根据指定的尺寸和形状生成形态学操作的结构元素
Mat Imgproc.getStructuringElement(int shape, Size ksize, Point anchor)
  • shape:结构元素的形状,有下列选项:
    • Imgproc.MORPH_RECT:矩形结构元素
    • Imgproc.MORPH_CROSS:十字型结构元素
    • Imgproc.MORPH_ELLIPSE:椭圆结构元素,矩形的内接椭圆
  • ksize:结构元素的尺寸
  • anchor:结构元素内的锚点,默认值(-1, -1),表示锚点位于结构元素中心。只有十字型的结构元素的形状取决于锚点的位置。其他情况仅仅用于调节形态学操作结果的平移量。
public class StructureElement {
    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //矩形结构元素
        Mat k0 = Imgproc.getStructuringElement(0, new Size(3, 3));
        System.out.println(k0.dump());
        System.out.println();
        //十字型结构元素
        Mat k1 = Imgproc.getStructuringElement(1, new Size(3, 3));
        System.out.println(k1.dump());
        System.out.println();
        //椭圆结构元素
        Mat k2 = Imgproc.getStructuringElement(2, new Size(7, 7));
        System.out.println(k2.dump());
        System.out.println();
    }
}
[  1,   1,   1;
   1,   1,   1;
   1,   1,   1]

[  0,   1,   0;
   1,   1,   1;
   0,   1,   0]

[  0,   0,   0,   1,   0,   0,   0;
   0,   1,   1,   1,   1,   1,   0;
   1,   1,   1,   1,   1,   1,   1;
   1,   1,   1,   1,   1,   1,   1;
   1,   1,   1,   1,   1,   1,   1;
   0,   1,   1,   1,   1,   1,   0;
   0,   0,   0,   1,   0,   0,   0]

3.2、腐蚀

腐蚀是求局部最小值的操作。经过腐蚀操作后,图像中的高亮区域会缩小,就像被腐蚀了一样。

腐蚀运算的原理如下图,原图像标有1的像素为高亮区域,结构元素中心的像素为锚点。腐蚀操作时用结构元素扫描原图像,用结构元素与覆盖区域的像素进行与运算,如果所有像素的运算结果都是1,则该像素值为1,否则为0。

//原图像
0 0 0 0 0 0 
0 0 1 1 1 0
0 1 1 1 1 0
0 1 1 1 1 0
0 1 1 0 0 0
0 0 1 1 0 0

//结构元素
0 1 0
1 1 1
0 1 0

//腐蚀运算结果
0 0 0 0 0 0 
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0

可以发现,原图像中高亮区域有15像素,经过腐蚀操作后只有3像素了,所以的结构相当于给高亮区域瘦身,瘦身的效果取决于结构元素,而结构元素可以根据需求自行定义。需要注意的是,腐蚀操作及膨胀操作等形态学操作都是针对高亮区域。如果原图像是黑白二值图像,则被腐蚀的是白色区域,如果希望黑色区域被腐蚀,则可以在操作前先进行反向操作。

//用特定的结构元素对图像进行腐蚀操作
void Imgproc.erode(Mat src, Mat dst, Mat kernel, Point anchor, int iterations)
  • src:输入图像,通道数任意,但深度应为CV_8U、CV_16U、CV_16S、CV_32F或CV_64F
  • dst:输出图像,和src具有相同的尺寸和数据类型
  • kernel:用于腐蚀操作的结构元素,可以用getStructuringElement()函数生成
  • anchor:结构元素内锚点的位置,默认(-1, -1),表示锚点位于结构元素中心
  • iterations:腐蚀的次数

此方法可以设置迭代次数,即一次完成多次腐蚀操作。如果只需腐蚀一次,则可将后两个参数省略。

public class Erode {
    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //读取图像并显示
        Mat src = Imgcodecs.imread("F:/IDEAworkspace/opencv/src/main/java/demo/wang.png");
        HighGui.imshow("src", src);
        HighGui.waitKey(0);
        //将图像反向并显示
        Core.bitwise_not(src, src);
        HighGui.imshow("negative", src);
        HighGui.waitKey(0);
        //生成十字型结构元素
        Mat kernel = Imgproc.getStructuringElement(1, new Size(3, 3));
        Point anchor = new Point(-1, -1);
        //腐蚀操作1次并显示
        Mat dst = new Mat();
        Imgproc.erode(src, dst, new Mat());
        HighGui.imshow("erode=1", dst);
        HighGui.waitKey(0);
        //腐蚀操作3次并显示
        Imgproc.erode(src, dst, kernel, anchor, 3);
        HighGui.imshow("erode=3", dst);
        HighGui.waitKey(0);
        System.exit(0);
    }
}

原图:
在这里插入图片描述

反向:
在这里插入图片描述

一次腐蚀:

在这里插入图片描述

三次腐蚀:
在这里插入图片描述
上面的示例只能看到腐蚀的结构,无法对腐蚀的细节进行研究。下面用一个完整的程序说明:

public class Erode2 {
    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //构建一个6*6的小矩阵并导入数据
        Mat src = new Mat(6, 6, CvType.CV_8UC1);
        byte[] data = new byte[] {
                0,0,0,0,0,0,
                0,0,1,1,1,0,
                0,1,1,1,1,0,
                0,1,1,1,1,0,
                0,1,1,0,0,0,
                0,0,1,1,0,0
        };
        src.put(0, 0, data);
        //打印矩阵
        System.out.println(src.dump());
        System.out.println();
        //构建十字型结构元素
        Mat kernel = Imgproc.getStructuringElement(1, new Size(3, 3));
        Mat dst = new Mat(6, 6, CvType.CV_8UC1);
        //进行腐蚀操作并输出腐蚀后的矩阵
        Imgproc.erode(src, dst, kernel);
        System.out.println(dst.dump());
    }
}
[  0,   0,   0,   0,   0,   0;
   0,   0,   1,   1,   1,   0;
   0,   1,   1,   1,   1,   0;
   0,   1,   1,   1,   1,   0;
   0,   1,   1,   0,   0,   0;
   0,   0,   1,   1,   0,   0]

[  0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0;
   0,   0,   1,   1,   0,   0;
   0,   0,   1,   0,   0,   0;
   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0]

3.3、膨胀

与腐蚀相反,膨胀则是求局部最大值的操作。经过膨胀操作后,图像中的高亮区域会扩大,就像受热膨胀一样。膨胀运算原理如下,原图像中标有1的像素为高亮区域,结构元素中心的像素为锚点。进行膨胀操作时用结构元素扫描原图像,用结构元素与覆盖区域的像素进行与运算,如果所有像素的运算结构都是0,则该像素值为0,否则为1。膨胀运算前高亮区域有15像素,经过膨胀操作后扩充为29像素,所以膨胀的结果让高亮区域长胖了。

//原图像
0 0 0 0 0 0
0 0 1 1 1 0
0 1 1 1 1 0
0 1 1 1 1 0
0 1 1 0 0 0
0 0 1 1 0 0

//结构元素
0 1 0
1 1 1
0 1 0

//膨胀运算结果
0 0 1 1 1 0
0 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 0
0 1 1 1 1 0
//用特定的结构元素对图像进行膨胀操作
void Imgproc.dilate(Mat src, Mat dst, Mat kernel, Point anchor, int iterations)
  • src:输入图像,通道数任意,但深度应为CV_8U、CV_16U、CV_16S、CV_32F或CV_64F
  • dst:输出图像,和src具有相同的尺寸和数据类型
  • kernel:用于膨胀操作的结构元素,可以用getStructuringElement()函数生成
  • anchor:结构元素内锚点的位置,默认(-1, -1),表示锚点位于结构元素中心
  • iterations:膨胀的次数
public class Dilate {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //读取图像并显示
        Mat src = Imgcodecs.imread("F:/IDEAworkspace/opencv/src/main/java/demo/wang.png");
        HighGui.imshow("src", src);
        HighGui.waitKey(0);
        //将图像反向并显示
        Core.bitwise_not(src, src);
        HighGui.imshow("negative", src);
        HighGui.waitKey(0);
        //生成十字型结构元素
        Mat kernel = Imgproc.getStructuringElement(1, new Size(3, 3));
        Point anchor = new Point(-1, -1);
        //膨胀操作1次并显示
        Mat dst = new Mat();
        Imgproc.dilate(src, dst, new Mat());
        HighGui.imshow("dilate=1", dst);
        HighGui.waitKey(0);
        //腐蚀操作3次并显示
        Imgproc.dilate(src, dst, kernel, anchor, 3);
        HighGui.imshow("dilate=3", dst);
        HighGui.waitKey(0);
        System.exit(0);
    }
}

原图:
在这里插入图片描述

反向:

在这里插入图片描述

膨胀一次:
在这里插入图片描述

膨胀三次:
在这里插入图片描述

四、形态学操作

腐蚀和膨胀操作时图像形态学的基础。通过对腐蚀和膨胀操作进行不同的组合可以实现图像的开运算、闭运算、形态学梯度、顶帽运算、黑帽运算和击中击不中等操作。

这些操作在OpenCV中都使用morphologyEx()函数实现,只是其中的参数不同。该函数如下:

//对图像进行基于腐蚀和膨胀的高级形态学操作
void Imgproc.morphologyEx(Mat src, Mat dst, int op, Mat kernel, Point anchor, int iterations)
  • src:输入图像,通道数任意,但深度应为CV_8U、CV_16U、CV_16S、CV_32F或CV_64F
  • dst:输出图像,和src具有相同的尺寸和数据类型
  • op:形态学操作的类型,具体有以下几种:
    • Imgproc.MORPH_ERODE:腐蚀操作
    • Imgproc.MORPH_DILATE:膨胀操作
    • Imgproc.MORPH_OPEN:开运算
    • Imgproc.MORPH_CLOSE:闭运算
    • Imgproc.MORPH_GRADIENT:形态学梯度
    • Imgproc.MORPH_TOPHAT:顶帽运算
    • Imgproc.MORPH_BLACKHAT:黑帽运算
    • Imgproc.MORPH_HITMISS:击中击不中。只支持CV_8UC1类型的二值图像
  • kernel:结构元素,可以用getStructuringElement函数生成
  • anchor:结构元素内锚点的位置,负数表示锚点位于结构元素中心
  • iterations:腐蚀和膨胀的次数

4.1、开运算和闭运算

开运算是对图像先腐蚀后膨胀的过程,它可以用来去除噪声、去除细小的形状(如毛刺)或在轻微连接处分离物体等。腐蚀操作同样能去掉毛刺,但是腐蚀操作后高亮区域整个廋了一圈,形态发生了明显变化,而开运算能在去掉毛刺的同时又保持原来的大小。

与开运算相反的操作是闭预算。闭运算是对图像先膨胀后腐蚀的过程。闭算远可以去除小型空洞,还能将狭窄的缺口连接起来。

public class MorphologyEx1 {
    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //读取图像并显示
        Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/butterfly2.png");
        HighGui.imshow("src", src);
        HighGui.waitKey(0);
        Mat dst = new Mat();
        //闭运算1次并显示
        Point anchor = new Point(-1, -1);
        Imgproc.morphologyEx(src, dst, Imgproc.MORPH_CLOSE, new Mat(), anchor, 1);
        HighGui.imshow("Close-1", dst);
        HighGui.waitKey(0);
        //闭运算3次并显示
        Imgproc.morphologyEx(src, dst, Imgproc.MORPH_CLOSE, new Mat(), anchor, 3);
        HighGui.imshow("Close-3", dst);
        HighGui.waitKey(0);
        //在3次闭运算的基础上进行开运算并显示
        Imgproc.morphologyEx(dst, dst, Imgproc.MORPH_OPEN, new Mat(), anchor, 1);
        HighGui.imshow("Open", dst);
        HighGui.waitKey(0);
        System.exit(0);
    }
}

原图:
在这里插入图片描述

闭运算1次:
在这里插入图片描述

闭运算3次:
在这里插入图片描述

开运算一次:
在这里插入图片描述

输入图像是一只蝴蝶的轮廓图,经过1次闭运算后部分空洞消失,3次闭运算后大量空洞消失,在此基础上进行1次开运算使很多轮廓线消失。

4.2、顶帽和黑帽

顶帽运算也称为礼貌运算,是计算原图像与开运算结果之差的操作。由于开运算后放大了裂缝或者局部低亮度的区域,从原图中减去开运算后的图像后就突出了比原图像轮廓周边区域更明亮的区域。

黑帽运算则是计算闭运算结果与原图像之差的操作。黑帽运算后突出了比原图像轮廓周边区域更暗的区域。

public class MorphologyEx2 {
    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //读取图像并显示
        Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/shaomai.png");
        HighGui.imshow("src", src);
        HighGui.waitKey(0);
        Mat dst = new Mat();
        //转换为二值图并显示
        Imgproc.threshold(src, src, 120, 255, Imgproc.THRESH_BINARY);
        HighGui.imshow("Binary", src);
        HighGui.waitKey(0);
        //顶帽运算3次并显示
        Point anchor = new Point(-1, -1);
        Imgproc.morphologyEx(src, dst, Imgproc.MORPH_TOPHAT, new Mat(), anchor, 3);
        HighGui.imshow("Tophat", dst);
        HighGui.waitKey(0);
        //黑帽运算3次并显示
        Imgproc.morphologyEx(src, dst, Imgproc.MORPH_BLACKHAT, new Mat(), anchor, 3);
        HighGui.imshow("Blackhat", dst);
        HighGui.waitKey(0);
        System.exit(0);
    }
}

原图:

在这里插入图片描述

二值图:

在这里插入图片描述

顶帽3次:

在这里插入图片描述

黑帽3次:

在这里插入图片描述

4.3、形态学梯度

形态学梯度是计算膨胀结果与腐蚀结果之差的操作,其结果看上去就像图像的轮廓。

public class MorphologyEx3 {
    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //读取图像1并显示
        Mat src1 = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/shaomai.png");
        HighGui.imshow("src1", src1);
        HighGui.waitKey(0);
        //形态学梯度并显示
        Point anchor = new Point(-1, -1);
        Mat dst = new Mat();
        Imgproc.morphologyEx(src1, dst, Imgproc.MORPH_GRADIENT, new Mat(), anchor, 1);
        HighGui.imshow("Gradient1", dst);
        HighGui.waitKey(0);

        //读取图像2并显示
        Mat src2 = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/wang.png", Imgcodecs.IMREAD_GRAYSCALE);
        HighGui.imshow("src2", src2);
        HighGui.waitKey(0);
        //形态学梯度并显示
        Imgproc.morphologyEx(src2, dst, Imgproc.MORPH_GRADIENT, new Mat(), anchor, 1);
        HighGui.imshow("Gradient2", dst);
        HighGui.waitKey(0);
        System.exit(0);
    }
}

原图1:
在这里插入图片描述

形态学梯度:

在这里插入图片描述

原图2:
在这里插入图片描述

形态学梯度:

在这里插入图片描述

4.4、击中击不中

击中击不中运算常用于二值图像,它的要求比腐蚀操作还要严格。只有当结构元素与其覆盖的区域完全相同时,该像素才为1,否则为0,如下:

//原图
0 0 0 0 0 0
0 1 1 1 1 0
0 1 1 0 1 0
0 1 1 1 1 1
0 0 0 0 0 0

//结构元素
1 1 1
1 0 1
1 1 1

//击中击不中结果
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 1 0 0 
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
public class HitMiss {
    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //构建一个6*6的小矩阵并导入数据
        Mat src = new Mat(6, 6, CvType.CV_8UC1);
        byte[] data = new byte[] {
                0, 0, 0, 0, 0, 0,
                0, 0, 1, 1, 1, 0,
                0, 1, 1, 1, 1, 0,
                0, 1, 1, 1, 1, 0,
                0, 1, 1, 0, 0, 0,
                0, 0, 1, 1, 0, 0
        };
        src.put(0, 0, data);
        //打印矩阵输出
        System.out.println(src.dump());
        System.out.println();
        //构建矩形结构元素并输出
        Mat kernel = Imgproc.getStructuringElement(0, new Size(3, 3));
        System.out.println(kernel.dump());
        System.out.println();
        //击中击不中测试并输出
        Point anchor = new Point(-1, -1);
        Mat dst = new Mat(6, 6, CvType.CV_8UC1);
        Imgproc.morphologyEx(src, dst, Imgproc.MORPH_HITMISS, kernel, anchor, 1);
        System.out.println(dst.dump());
    }
}
[  0,   0,   0,   0,   0,   0;
   0,   0,   1,   1,   1,   0;
   0,   1,   1,   1,   1,   0;
   0,   1,   1,   1,   1,   0;
   0,   1,   1,   0,   0,   0;
   0,   0,   1,   1,   0,   0]

[  1,   1,   1;
   1,   1,   1;
   1,   1,   1]

[  0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0;
   0,   0,   0,   1,   0,   0;
   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0]
OpenCV快速入门:图像形态学操作 图像形态学是一门强大而有趣的技术,它通过对图像进行形态学操作,使图像更适合后续处理步骤。在本文中,我们将深入探讨OpenCV中的图像形态学操作,快速入门这一关键领域。图像形态学作为数字图像处理的一个分支,致力于通过形态学操作实现对图像特征的提取、噪音的去除以及目标的分割。这一领域的研究为计算机视觉、图像识别和医学图像处理等领域提供了强大的工具和方法。 阅读详情

相关推荐

opencv(C++) 变换图像与形态学操作

1)腐蚀和膨胀操作的基本原理与OpenCV实现;2)开运算和闭运算的组合应用;3)灰度图像上的高级形态学操作(形态学梯度和黑帽变换);4)分水岭算法和MSER区域提取方法。

seaeress的博客 1366

opencv介绍及环境搭建

本篇文章开始学习opencv的知识,opencv主要用于图像处理和识别,在生活中到处都是可以见到的,那么本篇文章就正式带大家来学习opencv。OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,提供了丰富的函数和工具,用于处理图像和视频数据。它是一个跨平台的库,可在多个操作系统上运行,包括Windows、Linux、macOS、Android和iOS。OpenCV最初是由Intel于1999年开发,现已由一群开发者和贡献者维护和更新。

m0_49476241的博客 8596

OpenCV之图像形态学运算

图像形态学运算主要包括一下几类:     腐蚀和膨胀这两类形态学运算的原理性解释网上很多,稍微搜索一下就可以获得比较全面的了解,而且在实际应用中很少有单独使用腐蚀或膨胀运算的,通常是将两者组合起来使用,也就是先腐蚀后膨胀的开运算、先膨胀后腐蚀的闭运算以及两者相减的形态学梯度运算,其中,开运算主要用来去除图像中的孤立点;闭运算则可用来填充物体内细小空洞、连接邻近连通域、平滑其边界,同时又不明显改变

每一刻都是崭新的 1418

图像处理中距离度量方法总结

1.Euclidean Distance Euclidean Distance(欧氏距离)是一种常用的度量方式,是点和点之间坐标的均方根。通常情况下人们所说到的距离,指的就是欧式距离,它的定义如下: 2.Manhattan Distance Manhattan Distance(曼哈顿距离)也称为街区距离,表示对点与点之间在不同维度上的绝对距离的叠加,它的定义如下: 3.C

bianlongpeng的博客 1万+

图像形态学操作:原理与OpenCV应用

本文还有配套的精品资源,点击获取 简介:图像形态学操作是图像处理的关键技术,特别在二值图像分析中。本主题通过OpenCV库,深入探讨形态学操作的原理和实际应用。二值图像是一种每个像素只有两种值的特殊灰度图像,广泛用于文字识别、边缘检测等。形态学操作如膨胀、腐蚀、开闭运算等都基于结构元素对图像进行迭代。OpenCV提供完整的API实现这些操作,通过压缩包中的图像文件比较,可以...

weixin_42560991的博客 1758

Kaamel白皮书:IoT设备安全隐私评估实践

明确评估的目的和预期结果。

kaamelai的博客 1318

Opencv基础知识

Opencv基础知识一、Opencv基础知识图像读取、显示图像拆分、显示与保存图像加法与类型转换图像融合:图像缩放resize():图像旋转、翻转图像平移二、图像进阶知识阈值化处理高斯滤波形态学6种操作掩摸与感兴趣区域灰度直方图频域滤波 这篇文章是对前段时间所学习opencv知识的一个系统整理,主要是对代码的整理和保存,方便以后的查阅。 本次学习主要参考了以下几篇文章: Python+OpenCV图像处理(一篇全). 一、Opencv基础知识 图像读取、显示 #读取图片 img = cv2.imread("

ml1211449109的博客 773

安卓/苹果/支付宝sdk拉起支付宝h5/app支付

代码】安卓/苹果/支付宝sdk拉起支付宝h5/app支付。

m0_68973987的博客 5387

OpenCV官方教程中文版——学习笔记(一)

4.1 读入图像 cv2.imread() 4.2 显示图像 cv2.imshow() cv2.waitKey() 是一个键盘绑定函数。需要指出的是它的时间尺度是毫秒级。函数等待特定的几毫秒,看是否有键盘输入。特定的几毫秒之内,如果按下任意键,这个函数会返回按键的 ASCII 码值,程序将会继续运行。如果没有键盘输入,返回值为 -1,如果我们设置这个函数的参数为 0,那它将会无限期的等待键...

weixin_43355451的博客 5919

《QGIS快速入门与应用基础》230:添加图例工具

本文系统讲解QGIS图例制作的核心方法与优化技巧。首先强调图例与地图框的绑定逻辑,提出三种绑定状态及适用场景。详细拆解三种图例添加方法:布局工具栏一键添加法(新手首选)、菜单栏添加法和右键快捷创建法,并给出单地图框和多地图框两种典型场景的规范技巧。重点解决新手常见的图例杂乱、样式不规范、内容脱节等问题,帮助用户从基础图例制作进阶到符合行业标准的专业图例设计。通过分步操作图示和实用技巧,使读者能够快速掌握图例创建的核心流程与优化方法。

LuckyHanMo的博客 552

Vue3 - [兼容PC和手机H5] 详细监听浏览器刷新 / 关闭 / 前进后退事件,用户点击关闭和刷新页面前 “拦截“ 操作并弹出提示框(实时监听用户关闭或刷新网页,触发时文字提醒并执行自定义操作)

vue3监听页面关闭和刷新,vue3中如何监听页面刷新和关闭事件,vue3中监听浏览器窗口关闭事件,并在窗口关闭进行操作,vue3怎么只在某个页面监听,其他页面不监听,vue3监听刷新和关闭怎么离开页面时注销释放,vue3怎么阻止浏览器前进和后退并提示,页面跳转离开后依旧可以监听到刷新和关闭,vue3怎么知道用户关闭了网页,vue3如何监听刷新网页操作并截断,vue3移动端手机h5网页手机网站监听浏览器刷新关闭,vue3当页面关闭或刷新时怎么提示用户离开不保存数据,vue3项目监听浏览器窗口或标签页关闭,并

🏆 前端领域优质创作者 1万+

OpenCV图像形态学详解

图像形态学(Image Morphology)是数字图像处理领域的一个重要分支,主要研究基于形状的图像处理技术。它最初源于对生物体形态的研究,后来被引入到图像分析中,成为提取图像中有用形状特征的有效工具。在OpenCV中,形态学操作主要基于二值图像(黑白图像)进行处理,通过结构元素(kernel)与图像进行特定操作来实现各种效果,广泛应用于边缘检测、噪声去除、形状分析等领域。OpenCV中的形态学操作是图像处理中极为强大的工具,通过合理组合基本操作可以解决许多实际问题。理解结构元素对结果的影响。

zdx2585503940的博客 1506

基于YOLOv5/v8/v10的垃圾分类识别系统:设计实现与性能优化(附代码+数据集)

做完这个毕设,你已经掌握了从数据准备、模型训练到界面开发的全流程。把模型部署到树莓派上,做个真正的“智能垃圾桶”硬件装置;加入语音提示功能,检测到垃圾后自动语音播报类别;试试融合多个YOLO模型的优势,让分类精度再上一个台阶。按照这个教程一步步做,你的毕设不仅能顺利完成,还能做出很多亮点。要是过程中遇到啥问题,别慌,慢慢琢磨或者咱们一起聊聊,肯定能把这个毕设做成让你满意的作品。

know_F0727的博客 464

OpenCV--图像形态学

图像形态学以数学形态学为基础,通过对图像中的像素进行特定的集合运算,改变图像中物体的形状和结构。其核心操作是使用一个称为结构元素(也叫核)的小矩阵,在图像上滑动,对每个像素及其邻域进行操作,从而实现对图像的处理。在 OpenCV 中,提供了丰富的函数和工具,帮助我们进行各种图像形态学处理。本文详细介绍了 OpenCV 中的图像形态学处理技术,包括腐蚀、膨胀、开运算、闭运算、梯度运算、顶帽运算和黑帽运算。每种操作都有其独特的原理和适用场景。

2201_75491841的博客 1058

【NodeRed】node-red-contrib-opcua实现PLC读写

在之前的博文【Node-RED】node-red-contrib-opcua-server模块使用(1)中,我们介绍了用node-red-contrib-opcua-server模块实现OPCUA服务端的,以及二次开发的内容。本节博文中我们采用官方社区自带的node-red-contrib-opcua组件开展对OPCUA客户端的介绍,详细阐述用该组件中OpcUa-Client节点实现客户端对PLC的读写,以及实现过程中遇到的问题。由于官方社区中并没有找到该组件的介绍实例,这也是本博文编写的第1个原因。

weixin_48618536的博客 960

华为OD机试双机位C卷-敌情监控 (Py/Java/C/C++/Js/Go)

华为OD机试(机考)双机位C卷 【敌情监控】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试 - 敌情监控。

qq_45776114的博客 938

7:OpenCV—图像形态学处理

​ 用3×3的核去扫描二值图像,仅当核的与前景像素有**完全重合区域**时,将二值图像中对应的卷积核中心位置的像素保留,其余情况下,将中心位置的像素置0。拓展:卷积核可以**为任意形状(除1×1)**,且重置点可以选用卷积核中的**任意位置**,有‘**交集‘**就对重置点位置像素置1。包括图像的**腐蚀**、**膨胀**、**开**、**闭**、**形态学梯度、顶帽、黑帽、分支主题、结构元素**等操作。​ 拓展:卷积核可以**为任意形状**,且**重置点**可以选用卷积核中的**任意位置。

学习笔记 1501

【Windows Server 2019】DNS服务器的配置与管理——DNS正向解析

博文介绍了如何使用正向解析区,包括创建和添加并验证,以及术语 主机记录,别名,邮件交换记录的解释。

VMware, Windows, Linux, Cisco, Container, Kubernetes, Kali 1万+

OpenCV-图像形态学处理

OpenCV使用图像形态学操作,对图像进行处理

小白的学习笔记 1503

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

形态学图像处理OpenCV实现

0. 使用OpenCV函数实现 1. 二值形态学 2. 灰度形态学

zfoox的摘记 1176

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

OpenCV基础——图像滤波和形态学操作

高斯滤波某种程度上反映了和正态分布类似的思想:离我越近的则关系越紧密,也即和当前值相似度越大则在取加权平均时的权值越大,这和正态分布中离均值越近的概率越大的性质不谋而合。

软工科班,擅长Matlab、C++、微信小程序开发 1597

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

OpenCV图像形态学操作指南

图像形态学

ai学习博客 1165

OpenCV 图像形态学操作

OpenCV图像形态学操作教程 本教程介绍OpenCV中的图像形态学操作,包括腐蚀、膨胀、开运算、闭运算和形态学梯度等核心功能。通过结构元素(核)处理二值或灰度图像,形态学操作能有效去除噪声、连接区域、填充孔洞和提取边缘。教程提供清晰的Python代码示例,涵盖基本操作原理、应用场景和实现步骤,适合OpenCV初学者快速掌握形态学处理技术。 核心内容: 腐蚀(cv2.erode):缩小前景区域,去除噪声 膨胀(cv2.dilate):扩大前景区域,连接断开部分 开运算与闭运算(cv2.morphologyE

likuoelie的博客 1147

OpenCV——图像形态学操作

原理:选一个卷积核矩阵,卷积核里面都为一个值255时,则不进行操作,若又有255 又有0,则进行腐蚀操作。闭运算是先膨胀后腐蚀的组合操作。闭运算主要用于填补前景对象中的小孔或连接断裂的对象。开运算是先腐蚀后膨胀的组合操作。开运算主要用于去除图像中的小噪声或分离连接的对象。先将“迪哥”的毛刺去掉,但字体变细了,再进行膨胀操作。黑帽 = 闭运算 - 原始输入。即膨胀图像与腐蚀图像的差值。即将膨胀和腐蚀组合在一起。作用的通常是二值图像。

2401_88068407的博客 1083

OpenCV17-图像形态学操作

根据两者的特性,将图像腐蚀和膨胀适当结合,便可以既去除图像中的噪声,又不缩小图像中主要区域的面积;在定义结构元素之后,将结构元素的中心点依次放到图像中每一个非零元素处,如果原图中某个元素被结构元素覆盖,但是该像素的像素值不与结构元素中心点对应的像素点的像素值相同,那么将原图中该像素的像素值修改为结构元素中心点对应点的像素值。对于输出图像中的每个像素位置(x, y),将结构元素B的中心与输入图像I的对应位置(x, y)对齐,然后在结构元素B覆盖的区域内选择输入图像中的最小像素值作为输出图像中的像素值。

SHLYYY 1801
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值