实用指南:学习 Android (二十) 学习 OpenCV (五)
学习 Android (二) 学习 OpenCV (五)
在上一章节,我们对于 OpenCV 中的边缘检测有了进一步的学习和了解,这一章节我们将对图像形态学操作进行学习和了解;
26. 图像腐蚀
26.1 什么是图像腐蚀
腐蚀(Erosion)是图像处理中一种基本的形态学操作,它通过使用结构元素(内核)对图像进行扫描,根据结构元素与图像的交集关系来"收缩"或"细化"图像中的前景对象。
数学定义
从数学角度,腐蚀操作可以定义为:
A ⊖ B = {z | (B)z ⊆ A}其中:
A是输入图像(二值图像或灰度图像)B是结构元素(内核)(B)z表示将结构元素B平移z结果是所有使B完全包含在A中的平移位置z的集合
操作过程
腐蚀操作的工作流程:
定义结构元素:选择一个形状和大小合适的结构元素(内核)
扫描图像:将结构元素的中心对准图像中的每个像素
应用规则:
对于二值图像:如果结构元素覆盖的所有像素都是前景(1),则中心像素保留为前景,否则变为背景(0)
原始图像: [1, 1, 1, 0, 0] [1, 1, 1, 0, 0] [1, 1, 1, 0, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0] 3×3 矩形结构元素 结构元素: [1, 1, 1] [1, 1, 1] [1, 1, 1] 腐蚀过程 我们可以理解为,原始图像中,以每个像素坐标为中心点,能够完全映射结构元素,即像素保留前期1,否则变为背景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]对于灰度图像:取结构元素覆盖区域内像素的最小值作为中心像素的值
原始图像: [90 85 80 75 70] [95 90 85 80 75] [100 95 90 85 80] [105 100 95 90 85] [110 105 100 95 90] 3×3 矩形结构元素 结构元素: [1, 1, 1] [1, 1, 1] [1, 1, 1] 腐蚀过程 和二值图像过程一样,不过是将能够完全映射结构元素的中心点,变为该映射矩阵中最小的值,需要留意的是,在切换中心点进行下一个腐蚀操作时,映射到的矩阵的值要以原始图像为准,不能带入上一个已经腐蚀过的中心点值 腐蚀结果 [× × × × ×] [× 80 75 70 ×] [× 85 80 75 ×] [× 90 85 80 ×] [× × × × ×]
输出结果:生成腐蚀后的图像
结构元素
结构元素是腐蚀操作的核心,常见的形状有:
矩形:MORPH_RECT
椭圆形:MORPH_ELLIPSE
十字形:MORPH_CROSS
结构元素的大小决定了腐蚀的程度,较大的结构元素会产生更强烈的腐蚀效果。
26.2 关键代码分析
腐蚀核心函数:
Imgproc.erode(binaryMat, erosionMat, kernel, new Point(-1, -1), ITERATIONS);
参数说明:
binaryMat: 输入图像(二值图像或灰度图像)erosionMat: 输出图像kernel: 结构元素(定义腐蚀操作的形状和大小)new Point(-1, -1): 锚点位置(默认中心点)ITERATIONS: 迭代次数(腐蚀操作执行的次数)
结构元素创建:
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(KERNEL_SIZE, KERNEL_SIZE));
结构元素类型:
Imgproc.MORPH_RECT: 矩形结构元素 : 产生规则的腐蚀效果,各方向均匀收缩Imgproc.MORPH_ELLIPSE: 椭圆形结构元素 : 产生更自然的圆形腐蚀效果Imgproc.MORPH_CROSS: 十字形结构元素 : 主要影响水平和垂直方向
结构元素大小:
较小尺寸:轻微腐蚀,保留更多细节
较大尺寸:强烈腐蚀,可能丢失重要特征
迭代次数的影响:
较少迭代:轻微腐蚀效果
多次迭代:累积腐蚀效果,相当于使用更大的结构元素
处理流程:
图像加载:从资源文件加载图像
预处理:转换为灰度图并进行二值化,创建明显的目标对象
创建结构元素:定义腐蚀操作使用的内核形状和大小
应用腐蚀:使用erode函数对图像进行腐蚀操作
结果显示:将原图和腐蚀结果显示在ImageView中
26.2 应用场景
腐蚀操作在图像处理中有多种应用场景:
去除小噪声点
消除图像中的孤立小白点(椒盐噪声)
断开细小的连接
分离相连物体
将轻微接触的物体分离开
减少物体尺寸以便更好地计数
边缘检测预处理
先腐蚀再膨胀(开运算)可以平滑物体轮廓
去除细小毛刺
文本图像处理
使文本笔画变细,提高OCR识别率
分离粘连字符
医学图像处理
分离接触的细胞或组织
提取特定形状的结构
特点:
使前景对象变小
消除小的孤立点
平滑对象边界
对二值图像和灰度图像都有效
26.3 示例
ErosionActivity.java
public class ErosionActivity
extends AppCompatActivity {
private ActivityErosionBinding mBinding;
static {
System.loadLibrary("opencv_java4");
}
private Mat originalMat;
private Mat binaryMat;
// 新增:用于存储二值化图像
private Mat erosionRectMat;
private Mat erosionEllipseMat;
private Mat erosionCrossMat;
private Mat grayMat;
// 腐蚀参数
private static final int KERNEL_SIZE = 4;
private static final int ITERATIONS = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = ActivityErosionBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
try {
// 加载原始图像
originalMat = Utils.loadResource(this, R.drawable.lxh);
if (originalMat == null || originalMat.empty()) {
Toast.makeText(this, "Failed to load image", Toast.LENGTH_SHORT).show();
return;
}
// 显示原图
OpenCVHelper.showMat(mBinding.ivOriginal, originalMat);
// 转换为灰度图
grayMat = new Mat();
Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);
// 二值化处理,创建明显的目标对象
binaryMat = new Mat();
// 使用新的Mat对象存储二值化结果
Imgproc.threshold(grayMat, binaryMat, 127, 255, Imgproc.THRESH_BINARY);
// 应用不同类型的腐蚀
applyRectErosion();
applyEllipseErosion();
applyCrossErosion();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
/**
* 应用矩形结构元素的腐蚀操作
*/
private void applyRectErosion() {
applyErosion(Imgproc.MORPH_RECT, mBinding.ivErosionRect, "矩形");
}
/**
* 应用椭圆结构元素的腐蚀操作
*/
private void applyEllipseErosion() {
applyErosion(Imgproc.MORPH_ELLIPSE, mBinding.ivErosionEllipse, "椭圆");
}
/**
* 应用十字形结构元素的腐蚀操作
*/
private void applyCrossErosion() {
applyErosion(Imgproc.MORPH_CROSS, mBinding.ivErosionCross, "十字形");
}
/**
* 通用的腐蚀操作方法
*
* @param shape 结构元素形状
* @param imageView 显示结果的ImageView
* @param shapeName 形状名称(用于错误提示)
*/
private void applyErosion(int shape, ImageView imageView, String shapeName) {
Mat kernel = null;
Mat resultMat = null;
try {
// 创建结构元素
kernel = Imgproc.getStructuringElement(shape, new Size(KERNEL_SIZE, KERNEL_SIZE));
// 创建结果Mat
resultMat = new Mat();
// 应用腐蚀操作
Imgproc.erode(binaryMat, resultMat, kernel, new Point(-1, -1), ITERATIONS);
// 显示结果
OpenCVHelper.showMat(imageView, resultMat);
// 根据形状保存结果
switch (shape) {
case Imgproc.MORPH_RECT:
erosionRectMat = resultMat;
break;
case Imgproc.MORPH_ELLIPSE:
erosionEllipseMat = resultMat;
break;
case Imgproc.MORPH_CROSS:
erosionCrossMat = resultMat;
break;
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, shapeName + "腐蚀操作失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
// 确保异常时也释放资源
if (resultMat != null) {
resultMat.release();
}
} finally {
// 释放内核资源
if (kernel != null) {
kernel.release();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 释放所有Mat资源
if (originalMat != null) OpenCVHelper.safeRelease(originalMat);
if (binaryMat != null) OpenCVHelper.safeRelease(binaryMat);
if (grayMat != null) OpenCVHelper.safeRelease(grayMat);
if (erosionRectMat != null) OpenCVHelper.safeRelease(erosionRectMat);
if (erosionEllipseMat != null) OpenCVHelper.safeRelease(erosionEllipseMat);
if (erosionCrossMat != null) OpenCVHelper.safeRelease(erosionCrossMat);
}
}


作者的示例图并不能很明显的体现出各个结构元素模式的差异,希望读者能够自己处理(不是因为作者懒得去找图)
27. 图像膨胀
27.1 什么是图像膨胀
图像膨胀(Dilation)是形态学操作中的一种基本操作,与腐蚀操作相反。它通过使用结构元素(内核)对图像进行扫描,根据结构元素与图像的并集关系来"扩大"或"增厚"图像中的前景对象。
数学定义
膨胀操作可以定义为:
A ⊕ B = {z | (B)z ∩ A ≠ ∅}其中:
A是输入图像(二值图像或灰度图像)B是结构元素(内核)(B)z表示将结构元素B平移z结果是所有使B与A有交集的平移位置z的集合
操作过程
膨胀操作的工作流程完全和腐蚀相反:
定义结构元素:选择一个形状和大小合适的结构元素(内核)
扫描图像:将结构元素的中心对准图像中的每个像素
应用规则:
对于二值图像:如果结构元素覆盖的区域中至少有一个像素是前景(1),则中心像素设置为前景(1)
对于灰度图像:取结构元素覆盖区域内像素的最大值作为中心像素的值
输出结果:生成膨胀后的图像
与腐蚀操作的关系
膨胀和腐蚀是形态学操作中的一对基本操作:
腐蚀:缩小前景区域,消除小物体
膨胀:扩大前景区域,填充小孔洞
两者结合可以形成更复杂的形态学操作(如开运算、闭运算)
27.2 关键代码分析
函数原型
public static void dilate(Mat src, Mat dst, Mat kernel, Point anchor, int iterations, int borderType, Scalar borderValue)
参数详解
src:输入图像,可以是二值或灰度图像dst:输出图像,与输入图像相同的尺寸和类型kernel:结构元素,可以通过getStructuringElement创建anchor:锚点位置,默认为中心点(-1, -1)iterations:膨胀操作的迭代次数borderType:边界处理类型(如BORDER_CONSTANT)borderValue:边界值,当边界类型为BORDER_CONSTANT时使用
27.3 应用场景
膨胀操作在图像处理中有多种应用场景:
连接断裂部分
修复断裂的文字笔画
连接断开的边缘线
填充小孔洞
填充物体内部的小孔
平滑物体边界
增加物体尺寸
使细小的物体变得更明显
增加边缘厚度
与腐蚀操作结合
开运算:先腐蚀后膨胀,用于去除小物体和平滑边界
闭运算:先膨胀后腐蚀,用于填充小孔和连接断点
边缘检测后处理
使检测到的边缘更加连续
增加边缘的明显度
27.4 示例
DilationDemoActivity.java
public class DilationDemoActivity
extends AppCompatActivity {
private ActivityDilationDemoBinding mBinding;
static {
System.loadLibrary("opencv_java4");
}
private Mat originalMat;
private Mat binaryMat;
// 新增:用于存储二值化图像
private Mat dilationRectMat;
private Mat dilationEllipseMat;
private Mat dilationCrossMat;
private Mat grayMat;
// 膨胀参数
private static final int KERNEL_SIZE = 3;
private static final int ITERATIONS = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = ActivityDilationDemoBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
try {
originalMat = Utils.loadResource(this, R.drawable.lxh);
if (originalMat.empty()) {
Toast.makeText(this, "Failed to load image", Toast.LENGTH_SHORT).show();
return;
}
showMat(mBinding.ivOriginal, originalMat);
// 转换为灰度图
grayMat = new Mat();
Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);
// 二值化处理,创建明显的目标对象
binaryMat = new Mat();
// 使用新的Mat对象存储二值化结果
Imgproc.threshold(grayMat, binaryMat, 127, 255, Imgproc.THRESH_BINARY);
// 应用不同类型的腐蚀
applyRectErosion();
applyEllipseErosion();
applyCrossErosion();
} catch (Exception e) {
e.printStackTrace();
}
}
private void applyRectErosion() {
try {
// 创建结构元素(内核)
// 使用矩形结构元素,大小5×5
Mat kernel = Imgproc.getStructuringElement(
Imgproc.MORPH_RECT,
new Size(KERNEL_SIZE, KERNEL_SIZE)
);
// 应用膨胀操作
// 参数说明:
// - binaryMat: 输入图像(二值图像)
// - dilationMat: 输出图像
// - kernel: 结构元素
// - anchor: 锚点位置(默认中心点(-1,-1))
// - ITERATIONS: 迭代次数(膨胀操作的执行次数)
dilationRectMat = new Mat();
Imgproc.dilate(binaryMat, dilationRectMat, kernel, new Point(-1, -1), ITERATIONS);
// 显示结果
showMat(mBinding.ivDilationRect, dilationRectMat);
} catch (Exception e) {
e.printStackTrace();
Log.e("TAG", "Error in dilation operation: " + e.getMessage());
}
}
private void applyEllipseErosion() {
try {
Mat kernel = Imgproc.getStructuringElement(
Imgproc.MORPH_ELLIPSE,
new Size(KERNEL_SIZE, KERNEL_SIZE)
);
dilationEllipseMat = new Mat();
Imgproc.dilate(binaryMat, dilationEllipseMat, kernel, new Point(-1, -1), ITERATIONS);
// 显示结果
showMat(mBinding.ivDilationEllipse, dilationEllipseMat);
} catch (Exception e) {
e.printStackTrace();
Log.e("TAG", "Error in dilation operation: " + e.getMessage());
}
}
private void applyCrossErosion() {
try {
Mat kernel = Imgproc.getStructuringElement(
Imgproc.MORPH_CROSS,
new Size(KERNEL_SIZE, KERNEL_SIZE)
);
dilationCrossMat = new Mat();
Imgproc.dilate(binaryMat, dilationCrossMat, kernel, new Point(-1, -1), ITERATIONS);
// 显示结果
showMat(mBinding.ivDilationCross, dilationCrossMat);
} catch (Exception e) {
e.printStackTrace();
Log.e("TAG", "Error in dilation operation: " + e.getMessage());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (originalMat != null) OpenCVHelper.safeRelease(originalMat);
if (binaryMat != null) OpenCVHelper.safeRelease(binaryMat);
if (dilationRectMat != null) OpenCVHelper.safeRelease(dilationRectMat);
if (dilationEllipseMat != null) OpenCVHelper.safeRelease(dilationEllipseMat);
if (dilationCrossMat != null) OpenCVHelper.safeRelease(dilationCrossMat);
if (grayMat != null) OpenCVHelper.safeRelease(grayMat);
}
}


28. 开运算与闭运算
开运算(Opening)和闭运算(Closing)是形态学图像处理中的两种重要操作,它们都是由基本的腐蚀和膨胀操作组合而成的。
28.1 什么是开运算
定义:先腐蚀后膨胀
开运算(A) = 膨胀(腐蚀(A))
A ∘ B = (A ⊖ B) ⊕ B
作用:
消除小物体(在腐蚀阶段被移除)
平滑物体轮廓(在膨胀阶段部分恢复)
断开细小的连接
保持大体形状不变
28.2 什么是闭运算
定义:先膨胀后腐蚀
闭运算(A) = 腐蚀(膨胀(A))
A • B = (A ⊕ B) ⊖ B
作用:
填充小孔洞(在膨胀阶段被填充)
连接邻近物体
平滑轮廓
保持大体形状不变
28.3 关键代码分析
OpenCV提供了专门的函数来进行开运算和闭运算:
// 形态学操作通用函数
public static void morphologyEx(Mat src, Mat dst, int op, Mat kernel, Point anchor, int iterations, int borderType, Scalar borderValue)
参数详解
src:输入图像dst:输出图像op:操作类型Imgproc.MORPH_OPEN:开运算Imgproc.MORPH_CLOSE:闭运算
kernel:结构元素anchor:锚点位置(默认-1,-1)iterations:操作次数borderType:边界类型borderValue:边界值
28.4 应用场景
开运算的应用场景
去除小噪声点
消除图像中的孤立小点
去除椒盐噪声
物体分离
分离轻微接触的物体
断开细小的连接
边缘平滑
平滑物体边界
去除毛刺
文本处理
去除文档图像中的小噪点
改善OCR识别效果
闭运算的应用场景
填充孔洞
填充物体内部的小孔
修复不完整的轮廓
连接断裂
连接断开的边缘
修复断裂的文字笔画
平滑轮廓
使轮廓更加连续
减少轮廓上的缺口
前景提取
填充前景物体中的小空隙
改善分割结果
28.5 示例
MorphologyOpsActivity.java
public class MorphologyOpsActivity
extends AppCompatActivity {
private ActivityMorphologyOpsBinding mBinding;
static {
System.loadLibrary("opencv_java4");
}
private Mat originalMat;
// 形态学操作参数
private static final int KERNEL_SIZE = 5;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = ActivityMorphologyOpsBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
try {
// 从资源加载图像
originalMat = Utils.loadResource(this, R.drawable.lena);
if (originalMat.empty()) {
Toast.makeText(this, "Failed to load image", Toast.LENGTH_SHORT).show();
return;
}
// 显示原图
showMat(mBinding.ivOriginal, originalMat);
// 创建测试图像(包含小物体和小孔洞)
Mat testImage = createTestImage();
// 应用开运算和闭运算
applyMorphologyOperations(testImage);
// 释放测试图像
testImage.release();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建包含小物体和小孔洞的测试图像
*/
private Mat createTestImage() {
// 转换为灰度图
Mat grayMat = new Mat();
Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);
// 二值化处理
Mat binary = new Mat();
Imgproc.threshold(grayMat, binary, 127, 255, Imgproc.THRESH_BINARY);
// 添加一些小物体(噪声)
Mat withNoise = binary.clone();
addSmallObjects(withNoise);
// 添加一些小孔洞
Mat withHoles = withNoise.clone();
addSmallHoles(withHoles);
// 释放临时资源
grayMat.release();
binary.release();
withNoise.release();
return withHoles;
}
/**
* 添加小物体(模拟噪声)
*/
private void addSmallObjects(Mat image) {
// 在随机位置添加小白色方块(模拟噪声)
for (int i = 0; i <
20; i++) {
int x = (int) (Math.random() * (image.cols() - 5));
int y = (int) (Math.random() * (image.rows() - 5));
Imgproc.rectangle(image,
new Point(x, y),
new Point(x + 3, y + 3),
new Scalar(255), -1);
}
}
/**
* 添加小孔洞
*/
private void addSmallHoles(Mat image) {
// 在随机位置添加小黑色方块(模拟孔洞)
for (int i = 0; i <
15; i++) {
int x = (int) (Math.random() * (image.cols() - 5));
int y = (int) (Math.random() * (image.rows() - 5));
Imgproc.rectangle(image,
new Point(x, y),
new Point(x + 3, y + 3),
new Scalar(0), -1);
}
}
private void applyMorphologyOperations(Mat testImage) {
try {
// 显示结果
demonstrateKernelEffects(testImage);
} catch (Exception e) {
e.printStackTrace();
Log.e("TAG", "Error in morphology operations: " + e.getMessage());
}
}
/**
* 演示不同结构元素的影响
*/
private void demonstrateKernelEffects(Mat testImage) {
// 创建不同形状的结构元素
Mat rectKernel = Imgproc.getStructuringElement(
Imgproc.MORPH_RECT, new Size(KERNEL_SIZE, KERNEL_SIZE));
Mat ellipseKernel = Imgproc.getStructuringElement(
Imgproc.MORPH_ELLIPSE, new Size(KERNEL_SIZE, KERNEL_SIZE));
Mat crossKernel = Imgproc.getStructuringElement(
Imgproc.MORPH_CROSS, new Size(KERNEL_SIZE, KERNEL_SIZE));
Mat openRect = new Mat();
Mat openEllipse = new Mat();
Mat openCross = new Mat();
Mat closeRect = new Mat();
Mat closeEllipse = new Mat();
Mat closeCross = new Mat();
try {
// 应用不同结构元素的开运算
Imgproc.morphologyEx(testImage, openRect, Imgproc.MORPH_OPEN, rectKernel);
Imgproc.morphologyEx(testImage, openEllipse, Imgproc.MORPH_OPEN, ellipseKernel);
Imgproc.morphologyEx(testImage, openCross, Imgproc.MORPH_OPEN, crossKernel);
// 应用不同结构元素的闭运算
Imgproc.morphologyEx(testImage, closeRect, Imgproc.MORPH_CLOSE, rectKernel);
Imgproc.morphologyEx(testImage, closeEllipse, Imgproc.MORPH_CLOSE, ellipseKernel);
Imgproc.morphologyEx(testImage, closeCross, Imgproc.MORPH_CLOSE, crossKernel);
showMat(mBinding.ivOpen, openRect);
showMat(mBinding.ivClose, closeRect);
} finally {
// 释放资源
rectKernel.release();
ellipseKernel.release();
crossKernel.release();
openRect.release();
openEllipse.release();
openCross.release();
closeRect.release();
closeEllipse.release();
closeCross.release();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (originalMat != null) OpenCVHelper.safeRelease(originalMat);
}
}

29. 顶帽与黑帽
顶帽(Top-hat)和黑帽(Black-hat)是形态学图像处理中的两种高级操作,它们都是基于基础的开运算和闭运算衍生而来的。
29.1 什么是顶帽
定义:原图像与开运算结果的差
顶帽 = 原图像 - 开运算(原图像)
顶帽(A) = A - (A ∘ B)
物理意义:
突出比背景亮的细小物体
增强图像中的亮细节特征
在均匀背景上提取微小亮物体
29.2 什么是黑帽
定义:闭运算结果与原图像的差
黑帽 = 闭运算(原图像) - 原图像
黑帽(A) = (A • B) - A
物理意义:
突出比背景暗的细小物体
增强图像中的暗细节特征
在均匀背景上提取微小暗物体
29.3 关键代码分析
OpenCV使用统一的 morphologyEx 函数来实现顶帽和黑帽运算:
public static void morphologyEx(Mat src, Mat dst, int op, Mat kernel, Point anchor, int iterations, int borderType, Scalar borderValue)
参数详解
src:输入图像(通常是灰度图像)dst:输出图像op:操作类型Imgproc.MORPH_TOPHAT:顶帽运算Imgproc.MORPH_BLACKHAT:黑帽运算
kernel:结构元素anchor:锚点位置(默认-1,-1)iterations:操作次数(通常为1)borderType:边界类型borderValue:边界值
29.4 应用场景
顶帽运算的应用场景
光照不均匀校正
提取并去除不均匀的背景光照
增强在明亮背景下的暗细节
微小物体检测
检测图像中的小亮点、小物体
工业检测中的缺陷检测
文本提取
从复杂背景中提取文字
增强文档图像中的文字区域
医学图像处理
提取细胞图像中的微小结构
增强X光图像中的细微特征
黑帽运算的应用场景
暗细节增强
突出图像中的暗区域和小孔洞
增强阴影区域的细节
缺陷检测
检测产品表面的暗缺陷
工业质量控制的瑕疵检测
血管检测
在医学图像中增强血管结构
提取视网膜图像中的血管网络
目标提取
从明亮背景中提取暗目标
夜间图像中的目标检测
29.5 示例
HatTransformsActivity.java
public class HatTransformsActivity
extends AppCompatActivity {
private ActivityHatTransformsBinding mBinding;
static {
System.loadLibrary("opencv_java4");
}
private Mat originalMat, grayMat;
// 形态学操作参数
private static final int KERNEL_SIZE = 15;
private static final int ITERATIONS = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = ActivityHatTransformsBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
try {
// 从资源加载图像
originalMat = Utils.loadResource(this, R.drawable.twgx);
if (originalMat.empty()) {
Toast.makeText(this, "Failed to load image", Toast.LENGTH_SHORT).show();
return;
}
// 转换为灰度图
grayMat = new Mat();
Imgproc.cvtColor(originalMat, grayMat, Imgproc.COLOR_BGR2GRAY);
// 显示原图
showMat(mBinding.ivOriginal, grayMat);
// 应用顶帽和黑帽运算
applyHatTransforms();
// 演示文本增强应用
demonstrateTextEnhancement();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "Error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void applyHatTransforms() {
// 创建结构元素
Mat kernel = Imgproc.getStructuringElement(
Imgproc.MORPH_RECT,
new Size(KERNEL_SIZE, KERNEL_SIZE)
);
Mat tophatResult = new Mat();
Mat blackhatResult = new Mat();
try {
// 应用顶帽运算(突出亮细节)
Imgproc.morphologyEx(grayMat, tophatResult, Imgproc.MORPH_TOPHAT, kernel,
new Point(-1, -1), ITERATIONS);
// 应用黑帽运算(突出暗细节)
Imgproc.morphologyEx(grayMat, blackhatResult, Imgproc.MORPH_BLACKHAT, kernel,
new Point(-1, -1), ITERATIONS);
// 显示结果
showMat(mBinding.ivTophat, tophatResult);
showMat(mBinding.ivBlackhat, blackhatResult);
} catch (Exception e) {
e.printStackTrace();
Log.e("TAG", "Error in hat transforms: " + e.getMessage());
} finally {
// 释放资源
kernel.release();
tophatResult.release();
blackhatResult.release();
}
}
/**
* 演示文本增强应用
*/
private void demonstrateTextEnhancement() {
Mat kernel = Imgproc.getStructuringElement(
Imgproc.MORPH_RECT,
new Size(KERNEL_SIZE, KERNEL_SIZE)
);
Mat enhancedText = new Mat();
try {
// 使用顶帽运算增强文本(假设文本比背景暗)
// 对于暗文本 on 亮背景,使用顶帽运算可以增强文本
Imgproc.morphologyEx(grayMat, enhancedText, Imgproc.MORPH_TOPHAT, kernel);
// 增强对比度使文本更清晰
Core.multiply(enhancedText, new Scalar(2.0), enhancedText);
// 显示增强后的文本
showMat(mBinding.ivTextEnhanced, enhancedText);
} catch (Exception e) {
e.printStackTrace();
Log.e("TAG", "Error in text enhancement: " + e.getMessage());
} finally {
kernel.release();
enhancedText.release();
}
}
/**
* 演示光照校正应用
*/
private void demonstrateIlluminationCorrection() {
Mat kernel = Imgproc.getStructuringElement(
Imgproc.MORPH_RECT,
new Size(KERNEL_SIZE * 2, KERNEL_SIZE * 2) // 使用更大的核
);
Mat background = new Mat();
Mat corrected = new Mat();
try {
// 使用开运算估计背景
Imgproc.morphologyEx(grayMat, background, Imgproc.MORPH_OPEN, kernel);
// 从原图中减去背景(类似顶帽运算)
Core.subtract(grayMat, background, corrected);
// 可以显示或保存校正结果
} catch (Exception e) {
e.printStackTrace();
Log.e("TAG", "Error in illumination correction: " + e.getMessage());
} finally {
kernel.release();
background.release();
corrected.release();
}
}
/**
* 演示不同结构元素的影响
*/
private void demonstrateKernelEffects() {
// 创建不同形状的结构元素
Mat rectKernel = Imgproc.getStructuringElement(
Imgproc.MORPH_RECT, new Size(KERNEL_SIZE, KERNEL_SIZE));
Mat ellipseKernel = Imgproc.getStructuringElement(
Imgproc.MORPH_ELLIPSE, new Size(KERNEL_SIZE, KERNEL_SIZE));
Mat tophatRect = new Mat();
Mat tophatEllipse = new Mat();
try {
// 应用不同结构元素的顶帽运算
Imgproc.morphologyEx(grayMat, tophatRect, Imgproc.MORPH_TOPHAT, rectKernel);
Imgproc.morphologyEx(grayMat, tophatEllipse, Imgproc.MORPH_TOPHAT, ellipseKernel);
// 比较不同结构元素的效果
// 矩形核:产生更规则的响应
// 椭圆核:产生更平滑的响应
} finally {
// 释放资源
rectKernel.release();
ellipseKernel.release();
tophatRect.release();
tophatEllipse.release();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (originalMat != null) safeRelease(originalMat);
if (grayMat != null) safeRelease(grayMat);
}
}



浙公网安备 33010602011771号