OpenCV4.3 Java 编程入门:图像运算及 Core 组件中运算相关的方法

本文详细介绍了OpenCV中的一些关键图像运算,包括算术运算(如加、减、乘、除)、逻辑运算(如与、或、非、异或)、矩阵差值的绝对值、图像加权求和、沿对角线复制、缩放并计算绝对值、绘制边框、图像翻转、最大值和最小值计算、矩阵合并与拆分以及归一化。这些运算在图像处理和计算机视觉任务中起着至关重要的作用,例如图像增强、噪声消除、运动检测等。
摘要由CSDN通过智能技术生成

图像运算是指以图像为单位进行的操作,对图像中的所有像素进行操作,具体的运算主要包括算术和逻辑运算,它们通过改变像素的值来实现图像增强的效果。

算术和逻辑运算中,每次只涉及一个空间像素的位置,也就是说,单个像素的转换结果只取决于像素本身,而不受其他位置像素值的影响。运算时,(x,y)位置的运算结果,保存在目标图像中的相同位置。

1 算术运算

算术运算一般用于灰度图像,两个像素 p 和 q 之间的基本算术运算包括以下几种:

  1. 加法: p + q
  2. 减法:p - q;
  3. 乘法: p*q
  4. 除法: p/q

上面个运算的含义是指通过算术运算从两个像素的灰度值得到一个新的灰度值,新的灰度值有可能超出原图像的动态范围,此时需要进行灰度映射,以将运算结果的灰度值限制在图像允许的范围内。

下面,通过一个例子,来展示图像加法运算的过程和效果:

    @Test
    public void test() {
        Mat img1 = new Mat(new Size(5,5), CV_8U);
        Mat img2 = new Mat(new Size(5,5), CV_8U);

        // 1 创建灰度连续变化的图像;
        for (int i = 0; i < img1.rows(); i++) {
            for (int j = 0; j < img1.cols(); j++) {
                img1.put(i, j, i);
                img2.put(i, j, j);
            }
        }

        Mat dst = new Mat();
        Core.add(img1, img2, dst);

        System.out.println(img1.dump());
        System.out.println(img2.dump());
        System.out.println(dst.dump());
    }

上例中,三个像素矩阵的数据,如下图:
在这里插入图片描述

图像加法可用于图像平均以减少和去除图像采集中混入的噪声。在图像采集的过程中,由于各种原因,常常会混入一些干扰和噪声,由于噪声具有互不相关,且均值为零的统计特征,因此,可以通过将一系列采集的图像相加,然后平均来消除随机噪声。
用图像平均消除随机噪声
上图,为用图像平均法消除随机噪声。

图像减法常用在医学图像处理中,以消除背景,是医学成像中的基本方法。另外,图像相减在运动检测中很有用,比如,在序列图像(一段时间内采集到的同一场景的多张图像)中,通过逐像素比较可直接求取前后两帧图像之间的差别。假如,照明条件在多帧图像间基本不变化,那么图像相减后,不为零的地方表明该处发生了运动。

下面,是一个通过减法实现运动检测的例子:

    @Test
    public void test() {
        Mat img = Imgcodecs.imread("images/yd.PNG", CV_8U);

        // 1. 这里通过使用不同的偏移量截图,来模拟运动的效果
        Mat a = img.submat(0, img.rows() -10, 0, img.cols() -10);
        Mat b = img.submat(10, img.rows(), 10, img.cols());

        Mat dst = new Mat();

        // 2. 求差
        Core.subtract(a,b, dst);

        // 3. 二次求差,是图像为亮色
        Mat max = new Mat(dst.size(), dst.type(), new Scalar(255));
        Mat dst2 = new Mat();
        Core.subtract(max,dst, dst2);

        HighGui.imshow("dst", dst);
        HighGui.imshow("dst2", dst2);
        HighGui.waitKey();
    }

在这里插入图片描述

2 逻辑运算

逻辑运算只可直接用于二值(0 和 1)图像,两个像素 p 和 q 之间最基本的逻辑运算包括:

  1. 与(AND)运算: pq
  2. 或(OR)运算: p+q
  3. 非(NOT)运算 !q
  4. 异或(XOR)运算:
  5. 与非(NOT pq)运算:
  6. 或非(NOT (p+q))运算:

下面,通过一个示例,演示逻辑运算的过程和效果:

    @Test
    public void logicTest() {
        Mat img1 = new Mat(new Size(5,5), CV_8U, new Scalar(1));
        Mat img2 = new Mat(new Size(5,5), CV_8U, new Scalar(0));
        Mat img3 = Mat.eye(new Size(5,5), CV_8U);

        System.out.println(img1.dump());
        System.out.println(img2.dump());
        System.out.println(img3.dump());

        Mat dst = new Mat();
        Core.bitwise_not(img3, dst);
        System.out.println(dst.dump());

        Core.bitwise_or(img2, img3, dst);
        System.out.println(dst.dump());

        Core.bitwise_not(img1, img3, dst);
        System.out.println(dst.dump());
    }

计算结果,如下图:
在这里插入图片描述

在实际应用中,可以使用逻辑运算实现目标提取,比如,下图所示,先对一副细胞图像进行分割后,得到二值图像,然后对二值图像进行和运算,就可以将特定目标提取出来了。

在这里插入图片描述

3 矩阵差值的绝对值 :absdiff()

运算公式:
在这里插入图片描述

方法示例:

    @Test
    public void test() {
        Mat img1 = new Mat(new Size(5,5), CV_8U, new Scalar(8));
        Mat img2 = new Mat(new Size(5,5), CV_8U, new Scalar(10));

        Mat dst = new Mat();

        Core.absdiff(img1, img2, dst);
        System.out.println(dst.dump());

        Core.absdiff(img2, img1, dst);
        System.out.println(dst.dump());
    }

结果为:
在这里插入图片描述

4 图像加权求和:addWeighted()

运算公式:
在这里插入图片描述

方法示例:

    @Test
    public void addWeightedTest() {
        Mat ren = Imgcodecs.imread("images/ren.PNG");
        Mat yd = Imgcodecs.imread("images/yd.PNG").submat(new Rect(0, 0, ren.width(), ren.height())); // 裁剪为相同尺寸

        Mat dst = new Mat();
        Core.addWeighted(yd, 0.8, ren, 0.2, 0, dst);

        HighGui.imshow("test", dst);
        HighGui.waitKey();
    }

效果图:
在这里插入图片描述

5 沿对角线复制:completeSymm()

该方法能将方阵的下半部分或上半部分复制到另一半,矩阵对角线保持不变

演示代码:

    @Test
    public void completeSymmTest() {
        Mat yd = Imgcodecs.imread("images/yd.PNG").submat(new Rect(0, 0, 500, 500));

        Core.completeSymm(yd);

        HighGui.imshow("test", yd);
        HighGui.waitKey();
    }

需要注意,因为是沿对角线复制,所以给定的图像必须宽高一致!
在这里插入图片描述

6 缩放并计算绝对值: convertScaleAbs()

对于输入数组的每个元素,convertScaleAb s函数按顺序执行三个操作:缩放、获取绝对值、转换为无符号8位类型:
在这里插入图片描述
示例代码:

    @Test
    public void addWeightedTest2() {
        Mat mat = new Mat(new Size(5,5), CV_8U, new Scalar(8));

        Mat dst = new Mat();

        Core.convertScaleAbs(mat, dst, 0.5, 3); // 8*0.5 + 3 = 7
        System.out.println(dst.dump());
    }

输出结果:
在这里插入图片描述

7 绘制边框:copyMakeBorder()

该函数将源图像复制到目标图像的中间。复制的源图像左侧、右侧、上方和下方的区域将填充外推像素。这不是基于它的过滤函数所做的(它们会动态地外推像素),而是其他更复杂的函数,包括您自己的函数,可以用来简化图像边界处理。

	/**
     *
     * @param src 原图像
     * @param dst 目标图像,类型与原图像相同, 尺寸为 Size(src.cols+left+right, src.rows+top+bottom) .
     * @param top 上部像素数目
     * @param bottom 底部像素数目
     * @param left 左侧像素数目
     * @param right 左侧像素数目
     * @param borderType 边框类型
     * @param value 当 borderType==BORDER_CONSTANT 时的边框值
     */
	public static void copyMakeBorder(Mat src, Mat dst, int top, int bottom, int left, int right, int borderType, Scalar value);

示例代码:

    @Test
    public void copyMakeBorderTest() {
        Mat src = new Mat(new Size(4,4), CV_8U, new Scalar(0));
        Mat dst = new Mat();

        Core.copyMakeBorder(src, dst, 1,1,1,1, Core.BORDER_CONSTANT, new Scalar(1));
        System.out.println(dst.dump());
    }

输出结果
在这里插入图片描述

8 图像翻转:flip()

围绕垂直、水平或两个轴翻转二维阵列。

示例代码:

    @Test
    public void flipTest() {
        Mat src = new Mat(new Size(4,4), CV_8U);
        // 1 创建灰度连续变化的图像;
        for (int i = 0; i < src.rows(); i++) {
            for (int j = 0; j < src.cols(); j++) {
                src.put(i, j, (i*4+j));
            }
        }
        System.out.println(src.dump());
        Mat dst = new Mat();

        Core.flip(src, dst, 0);
        System.out.println(dst.dump());

        Core.flip(src, dst, 1);
        System.out.println(dst.dump());

        Core.flip(src, dst, -1);
        System.out.println(dst.dump());
    }

输出结果:
在这里插入图片描述
从图片上,大概是:
在这里插入图片描述

9 最大值:max()

求两个矩阵相同位置元素的最大值!

示例代码:

    @Test
    public void maxTest() {
        Mat ren = Imgcodecs.imread("images/ren.PNG");
        Mat yd = Imgcodecs.imread("images/yd.PNG").submat(new Rect(0, 0, ren.width(), ren.height()));

        Mat dst = new Mat();
        Core.max(yd, ren, dst);

        HighGui.imshow("test", dst);
        HighGui.waitKey();
    }

在这里插入图片描述

10 最小值:min()

求两个矩阵相同位置元素的最小值!

代码参考 max() 示例!

结果:
在这里插入图片描述

11 合并与拆分:merge() & split()

merge() 从多个单通道阵列中创建一个多通道阵列,与 split() 的作用相反。

12 归一化: normalize()

通俗地讲就是将矩阵的值通过某种方式变到某一个区间内.

函数定义:

	/**
     * @param src 输入矩阵
     * @param dst 输出矩阵
     * @param alpha 1. 值规范化:值;2. 范围规范: 下限边界;
     * @param beta 只能用作范围规范的上限边界;
     * @param norm_type 规范化类型,对应不同的数学公式
     */
    public static void normalize(Mat src, Mat dst, double alpha, double beta, int norm_type);

规范化的类型:
在这里插入图片描述
norm_type有NORM_INF, NORM_MINMAX,NORM_L1和NORM_L2四种。

  1. 在 NORM_MINMAX 模式下,alpha表示归一化后的最小值,beta表示归一化后的最大值。
  2. 在NORM_L1、NORM_L2、NORM_INF 模式下,alpha表示执行相应归一化后矩阵的范数值,beta不使用。
  3. 稀疏矩阵归一化仅支持非零像素

示例 1:

@Test
    public void normalizeTest() {
        Mat src = new Mat(new Size(10,10), CvType.CV_8UC1);
        // 1 创建灰度连续变化的图像;
        for (int i = 0; i < src.rows(); i++) {
            for (int j = 0; j < src.cols(); j++) {
                src.put(i, j, (i*10+j));
            }
        }

        System.out.println("原队列 = \n"+src.dump());
        Core.normalize(src, src, 100, 200, NORM_MINMAX, CvType.CV_8UC1);
        System.out.println("\n归一化后 = \n"+src.dump());
    }

在这里插入图片描述

示例 2: bytedeco

    @Test
    public void normalizeTest() {
        Mat mat = new Mat(new Size(3,3), CV_8UC3, new Scalar(5,4,3,0));
        Mat dst = new Mat();
        opencv_core.normalize(mat, dst, 0,100, NORM_MINMAX, -1, null);

        for (int i = 0; i < mat.rows(); i++) {
            for (int j = 0; j < mat.cols(); j++) {
                byte[] values = new byte[mat.channels()];
                dst.ptr(i,j).get(values);
                System.out.print(Arrays.toString(values) + " ");
            }
            System.out.println();
        }
    }

归一化的结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值