Android OpenCV基础(三、图片处理)

一、概述

  图片处理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;
}

  运行结果如下所示:
image.png

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;
}

  运行结果如下所示:
image.png

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;
}

  运行结果如下所示:
image.png

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;
}

  运行结果如下所示(右边两张是放大后的局部对比):
image.png

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)相邻的元素。
  • 寻找图像中的明显的极大值区域或极小值区域。
      腐蚀与膨胀的效果如下,膨胀效果下图像中的亮区开始扩展,而腐蚀效果下图像中的亮区(背景)变细,而黑色区域(字母)则变大了:
    image.png
      腐蚀与膨胀需要指定操作的内核,可以通过以下方式获取操作内核:
/**
* @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;
}

  运行结果如下所示:
image.png

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;
}

  运行结果如下所示:
image.png

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)=srcopen(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
      各效果与原图对比效果如下(左为原图,右为结果图):
    image.png

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官方文档

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值