概述
文章目录
- 1 算术运算
- 2 逻辑运算
- 3 矩阵差值的绝对值 :absdiff()
- 4 图像加权求和:addWeighted()
- 5 沿对角线复制:completeSymm()
- 6 缩放并计算绝对值: convertScaleAbs()
- 7 绘制边框:copyMakeBorder()
- 8 图像翻转:flip()
- 9 最大值:max()
- 10 最小值:min()
- 11 合并与拆分:merge() & split()
- 12 归一化: normalize()
图像运算是指以图像为单位进行的操作,对图像中的所有像素进行操作,具体的运算主要包括算术和逻辑运算,它们通过改变像素的值来实现图像增强的效果。
算术和逻辑运算中,每次只涉及一个空间像素的位置,也就是说,单个像素的转换结果只取决于像素本身,而不受其他位置像素值的影响。运算时,(x,y)位置的运算结果,保存在目标图像中的相同位置。
1 算术运算
算术运算一般用于灰度图像,两个像素 p 和 q 之间的基本算术运算包括以下几种:
- 加法: p + q
- 减法:p - q;
- 乘法: p*q
- 除法: 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 之间最基本的逻辑运算包括:
- 与(AND)运算: pq
- 或(OR)运算: p+q
- 非(NOT)运算 !q
- 异或(XOR)运算:
- 与非(NOT pq)运算:
- 或非(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四种。
- 在 NORM_MINMAX 模式下,alpha表示归一化后的最小值,beta表示归一化后的最大值。
- 在NORM_L1、NORM_L2、NORM_INF 模式下,alpha表示执行相应归一化后矩阵的范数值,beta不使用。
- 稀疏矩阵归一化仅支持非零像素
示例 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();
}
}
归一化的结果:
最后
以上就是义气吐司为你收集整理的OpenCV4.3 Java 编程入门:图像运算及 Core 组件中运算相关的方法1 算术运算2 逻辑运算3 矩阵差值的绝对值 :absdiff()4 图像加权求和:addWeighted()5 沿对角线复制:completeSymm()6 缩放并计算绝对值: convertScaleAbs()7 绘制边框:copyMakeBorder()8 图像翻转:flip()9 最大值:max()10 最小值:min()11 合并与拆分:merge() & split()12 归一化: normalize的全部内容,希望文章能够帮你解决OpenCV4.3 Java 编程入门:图像运算及 Core 组件中运算相关的方法1 算术运算2 逻辑运算3 矩阵差值的绝对值 :absdiff()4 图像加权求和:addWeighted()5 沿对角线复制:completeSymm()6 缩放并计算绝对值: convertScaleAbs()7 绘制边框:copyMakeBorder()8 图像翻转:flip()9 最大值:max()10 最小值:min()11 合并与拆分:merge() & split()12 归一化: normalize所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复