一、概述
图片处理imgproc模块包含基本的图像转换,包括滤波以及类似的卷积操作。本章来介绍下imgproc模块常用的几个功能。
二、颜色转换
图片处理模块提供了不同颜色空间互相转换的API。例如:转换灰度图直接调用图片处理模块提供的API即可:
// rgba转灰度
cv::cvtColor(rgbMat, dst, CV_RGBA2GRAY);
// yuv转rgba
cv::cvtColor(yuvMat, rgbMat, CV_YUV420sp2RGBA, 4);
// 灰度转rgba
cv::cvtColor(src, tmp, CV_GRAY2RGBA);
// 灰度转bgr565
cv::cvtColor(src, tmp, CV_GRAY2BGR565);
三、平滑与模糊
平滑也称模糊, 是一项简单且使用频率很高的图像处理方法。平滑处理的用途有很多(减少噪声等)。平滑处理时需要用到一个滤波器。最常的滤波器有归一化滤波器、高斯滤波器、中值滤波、双边滤波、线性滤波等。
3.1 归一化滤波
首先声明JNI接口如下:
public class OpenCVSample {
static {
try {
System.loadLibrary("native-lib");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
public static native Bitmap blurBitmap(Bitmap bitmap, int kernelLength);
}
然后在native层调用imgproc模块的滤波函数:
extern "C"
JNIEXPORT jobject JNICALL
Java_com_bc_sample_OpenCVSample_blurBitmap(
JNIEnv *env,
jclass thiz, jobject bitmap, jint kernel_length) {
cv::Mat rgbMat;
bitmapToMat(env, bitmap, rgbMat);
cv::Mat dst = cv::Mat(rgbMat.size(), rgbMat.type());
cv::Mat result = rgbMat.clone();
for (int i = 1; i < kernel_length; i = i + 2) {
// 均值平滑
cv::blur(rgbMat, result, cv::Size(i, i), cv::Point(-1, -1));
}
jobject resultBitmap = createBitmap(env, result.cols, result.rows, "ARGB_8888");
matToBitmap(env, result, resultBitmap);
return resultBitmap;
}
运行结果如下所示:
3.2 高斯滤波
高斯滤波的使用方式与归一化滤波类似,只需要把cv::blur()改为cv::GaussianBlur()即可:
extern "C"
JNIEXPORT jobject JNICALL
Java_com_bc_sample_OpenCVSample_blurBitmap(
JNIEnv *env,
jclass thiz, jobject bitmap, jint kernel_length) {
cv::Mat rgbMat;
bitmapToMat(env, bitmap, rgbMat);
cv::Mat dst = cv::Mat(rgbMat.size(), rgbMat.type());
cv::Mat result = rgbMat.clone();
for (int i = 1; i < kernel_length; i = i + 2) {
// 高斯平滑
cv::GaussianBlur(rgbMat, result, cv::Size(i, i), 0, 0);
}
jobject resultBitmap = createBitmap(env, result.cols, result.rows, "ARGB_8888");
matToBitmap(env, result, resultBitmap);
return resultBitmap;
}
运行结果如下所示:
3.3 中值滤波
中值滤波的使用方式与归一化滤波类似,只需要把cv::blur()改为cv::medianBlur()即可:
extern "C"
JNIEXPORT jobject JNICALL
Java_com_bc_sample_OpenCVSample_blurBitmap(
JNIEnv *env,
jclass thiz, jobject bitmap, jint kernel_length) {
cv::Mat rgbMat;
bitmapToMat(env, bitmap, rgbMat);
cv::Mat dst = cv::Mat(rgbMat.size(), rgbMat.type());
cv::Mat result = rgbMat.clone();
for (int i = 1; i < kernel_length; i = i + 2) {
// 中值平滑
cv::medianBlur(rgbMat, result, i);
}
jobject resultBitmap = createBitmap(env, result.cols, result.rows, "ARGB_8888");
matToBitmap(env, result, resultBitmap);
return resultBitmap;
}
运行结果如下所示:
3.4 双边滤波
前面几个滤波器都是为了平滑图像,但是一些滤波器不仅仅削弱了噪声,连带着把边缘也给磨掉了。为避免这样的情形,我们可以使用双边滤波。双边滤波只支持1通道或者3通道,此外双边滤波的使用方式与之前几个滤波类似:
extern "C"
JNIEXPORT jobject JNICALL
Java_com_bc_sample_OpenCVSample_blurBitmap(
JNIEnv *env,
jclass thiz, jobject bitmap, jint kernel_length) {
cv::Mat rgbMat;
bitmapToMat(env, bitmap, rgbMat);
cv::Mat dst = cv::Mat(rgbMat.size(), rgbMat.type());
cv::Mat result = rgbMat.clone();
// 双边平滑只支持1通道或者3通道
cv::cvtColor(rgbMat, rgbMat, CV_RGBA2RGB);
for (int i = 1; i < kernel_length; i = i + 2) {
// 双边平滑
cv::bilateralFilter(rgbMat, result, i, i * 2, i / 2);
}
jobject resultBitmap = createBitmap(env, result.cols, result.rows, "ARGB_8888");
matToBitmap(env, result, resultBitmap);
return resultBitmap;
}
运行结果如下所示(右边两张是放大后的局部对比):
3.5 自定义线性滤波
OpenCV提供了函数filter2D(),供开发者创建自己的线性滤波器:
extern "C"
JNIEXPORT jobject JNICALL
Java_com_bc_sample_OpenCVSample_filter2DBitmap(
JNIEnv *env,
jclass thiz, jobject bitmap, jint kernel_size) {
cv::Mat rgbMat;
bitmapToMat(env, bitmap, rgbMat);
cv::Mat dst = cv::Mat(rgbMat.size(), rgbMat.type());
cv::Mat result = rgbMat.clone();
/// 初始化滤波器参数
cv::Point anchor = cv::Point(-1, -1);
double delta = 0;
int ddepth = -1;
/// 更新归一化块滤波器的核大小
cv::Mat kernel =
cv::Mat::ones(kernel_size, kernel_size, CV_32F) / (float) (kernel_size * kernel_size);
/// 使用滤波器
filter2D(rgbMat, dst, ddepth, kernel, anchor, delta, cv::BORDER_DEFAULT);
jobject resultBitmap = createBitmap(env, result.cols, result.rows, "ARGB_8888");
matToBitmap(env, result, resultBitmap);
return resultBitmap;
}
四、膨胀与腐蚀
腐蚀与膨胀(Erosion 与 Dilation)是两种最基本的形态学操作。他们的运用广泛:
- 消除噪声
- 分割(isolate)独立的图像元素,以及连接(join)相邻的元素。
- 寻找图像中的明显的极大值区域或极小值区域。
腐蚀与膨胀的效果如下,膨胀效果下图像中的亮区开始扩展,而腐蚀效果下图像中的亮区(背景)变细,而黑色区域(字母)则变大了:
腐蚀与膨胀需要指定操作的内核,可以通过以下方式获取操作内核:
/**
* @param shape 内核形状;有三种:矩形: MORPH_RECT
* 交叉形: MORPH_CROSS
* 椭圆形: MORPH_ELLIPSE
* @param ksize 内核大小
* @param anchor 锚点位置,默认锚点在内核中心位置
**/
CV_EXPORTS_W Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));
4.1 腐蚀Erode
首先声明JNI接口如下:
public class OpenCVSample {
static {
try {
System.loadLibrary("native-lib");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
public static native Bitmap erodeBitmap(Bitmap bitmap, int ksize);
}
然后在native层调用imgproc模块的腐蚀函数:
extern "C"
JNIEXPORT jobject JNICALL
Java_com_bc_sample_OpenCVSample_erodeBitmap(JNIEnv *env,
jclass thiz,
jobject bitmap,
jint ksize
) {
cv::Mat rgbMat;
bitmapToMat(env, bitmap, rgbMat);
cv::Mat dst;
// 腐蚀操作的内核;如果不指定,默认为一个简单的 3x3 矩阵
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(ksize, ksize));
// 腐蚀操作
cv::erode(rgbMat, dst, element);
jobject resultBitmap = createBitmap(env, dst.cols, dst.rows, "ARGB_8888");
matToBitmap(env, dst, resultBitmap);
return resultBitmap;
}
运行结果如下所示:
4.2 膨胀Dilate
首先声明JNI接口如下:
public class OpenCVSample {
static {
try {
System.loadLibrary("native-lib");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
public static native Bitmap dilateBitmap(Bitmap bitmap, int ksize);
}
然后在native层调用imgproc模块的膨胀函数:
extern "C"
JNIEXPORT jobject JNICALL
Java_com_bc_sample_OpenCVSample_dilateBitmap(JNIEnv *env,
jclass thiz,
jobject bitmap,
jint ksize
) {
cv::Mat rgbMat;
bitmapToMat(env, bitmap, rgbMat);
cv::Mat dst;
// 膨胀操作的内核;如果不指定,默认为一个简单的 3x3 矩阵
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(ksize, ksize));
// 膨胀操作
cv::dilate(rgbMat, dst, element);
jobject resultBitmap = createBitmap(env, dst.cols, dst.rows, "ARGB_8888");
matToBitmap(env, dst, resultBitmap);
return resultBitmap;
}
运行结果如下所示:
4.3 更多形态学变换
在腐蚀与膨胀的基础上,进行组合可以实现更多的形态学变化。
- 开运算:能够排除小团块物体(假设物体较背景明亮);
d s t = o p e n ( s r c , e l e m e n t ) = d i l a t e ( e r o d e ( s r c , e l e m e n t ) ) dst = open(src, element) = dilate(erode(src, element)) dst=open(src,element)=dilate(erode(src,element)) - 闭运算:能够排除小型黑洞(黑色区域);
d s t = c l o s e ( s r c , e l e m e n t ) = e r o d e ( d i l a t e ( s r c , e l e m e n t ) ) dst = close(src, element) = erode(dilate(src, element)) dst=close(src,element)=erode(dilate(src,element)) - 形态梯度:能够保留物体的边缘轮廓;
d s t = m o r p h g r a d ( s r c , e l e m e n t ) = d i l a t e ( s r c , e l e m e n t ) − e r o d e ( s r c , e l e m e n t ) dst = morph_{grad}(src, element) = dilate(src, element) - erode(src, element) dst=morphgrad(src,element)=dilate(src,element)−erode(src,element) - 顶帽:原图像与开运算结果图之差;
d s t = t o p h a t ( s r c , e l e m e n t ) = s r c − o p e n ( s r c , e l e m e n t ) dst = tophat(src, element) = src - open(src, element) dst=tophat(src,element)=src−open(src,element) - 黑帽:闭运算结果图与原图像之差;
d s t = b l a c k h a t ( s r c , e l e m e n t ) = c l o s e ( s r c , e l e m e n t ) − s r c dst = blackhat(src, element) = close(src, element) - src dst=blackhat(src,element)=close(src,element)−src
各效果与原图对比效果如下(左为原图,右为结果图):
The End
欢迎关注我,一起解锁更多技能:BC的掘金主页~💐 BC的CSDN主页~💐💐
OpenCV官网:https://opencv.org/releases/
OpenCV github:https://github.com/opencv/opencv/tree/master
BC的OpenCV专栏
LearnOpenCV学习资料
OpenCV 4.5.5官方文档
OpenCV 2.3.2官方文档