Android平台下使用OpenCV灰度化图片的两种方式

OpenCV 专栏收录该内容
5 篇文章 0 订阅

在OpenCV中灰度化图像是比较常用的操作之一,这篇文章介绍了两种方法对图像进行灰度化并显示出来:

1.通过灰度化公式 Y(亮度)=0.299*R+0.587*G+0.114*B 对图片每个像素操作,从而得到灰度化的图片

2.通过调用cvtColor函数来灰度化图像


方法1:

Java核心代码部分,大体意思就是拿到图片像素数组,传入native层处理,结果返回Java层并显示,其中OpenCVHelper.gray()为native方法

Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(R.drawable.food)).getBitmap();
int w = bitmap.getWidth(), h = bitmap.getHeight();
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);

//灰度算法
int[] resultPixes = OpenCVHelper.gray(pix, w, h);

Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
result.setPixels(resultPixes, 0, w, 0, 0, w, h);
imageView.setImageBitmap(result);

native核心代码,这个完全是套用公式,这里不再赘述

JNIEXPORT jintArray JNICALL Java_yu_myself_opencv_jni_OpenCVHelper_gray(
        JNIEnv *env, jclass obj, jintArray buf, int w, int h) {

    jint *cbuf;
    cbuf = env->GetIntArrayElements(buf, JNI_FALSE);
    if (cbuf == NULL) {
        return 0;
    }

    Mat imgData(h, w, CV_8UC4, (unsigned char *) cbuf);

    uchar* ptr = imgData.ptr(0);
    for(int i = 0; i < w*h; i ++){
        //计算公式:Y(亮度) = 0.299*R + 0.587*G + 0.114*B
        //对于一个int四字节,其彩色值存储方式为:BGRA
        int grayScale = (int)(ptr[4*i+2]*0.299 + ptr[4*i+1]*0.587 + ptr[4*i+0]*0.114);
        ptr[4*i+1] = grayScale;
        ptr[4*i+2] = grayScale;
        ptr[4*i+0] = grayScale;
    }

    int size = w * h;
    jintArray result = env->NewIntArray(size);
    env->SetIntArrayRegion(result, 0, size, cbuf);
    env->ReleaseIntArrayElements(buf, cbuf, 0);
    return result;
}


方法2:

讲道理,调用OpenCV提供给我我们的API应该更方便代码量更少,但是这里有几个小问题需要简单说明一下,先上代码。

Java层代码:

//屏幕缩放比例
//DisplayMetrics dm = new DisplayMetrics();
//getWindowManager().getDefaultDisplay().getMetrics(dm);
//float scale = dm.density;

Bitmap bitmap = ((BitmapDrawable) getResources().getDrawable(
                R.drawable.food)).getBitmap();

//获得实际尺寸的Bitmap
//bitmap = Bitmap.createScaledBitmap(bitmap, (int) (bitmap.getWidth() / scale), (int) (bitmap.getHeight() / scale), false);

int w = bitmap.getWidth(), h = bitmap.getHeight();
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);

//传入正常尺寸的像素数组
int[] resultPixes = OpenCVHelper.canny(pix, w, h);

Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
result.setPixels(resultPixes, 0, w, 0, 0, w, h);
imageView.setImageBitmap(result);

native层代码:
JNIEXPORT jintArray JNICALL Java_yu_myself_opencv_jni_OpenCVHelper_canny(
    JNIEnv *env, jclass obj, jintArray buf, int w, int h){

    jint *cbuf;
    cbuf = env->GetIntArrayElements(buf, JNI_FALSE);
    if (cbuf == NULL) {
        return 0;
    }

    Mat srcImage(h, w, CV_8UC4, (unsigned char *) cbuf);

    Mat grayImage;
    cvtColor(srcImage, grayImage, COLOR_BGRA2GRAY);
    cvtColor(grayImage, grayImage, COLOR_GRAY2BGRA);
    jint* ptr = grayImage.ptr<jint>(0);

    int size = w * h;

    jintArray result = env->NewIntArray(size);
    env->SetIntArrayRegion(result, 0, size, ptr);
    env->ReleaseIntArrayElements(buf, cbuf, 0);

    return result;
}


问题1:为什么在Java层注释掉了一部分代码,貌似是Bitmap缩放问题。

我们知道Android手机的DPI都是不一样的,当然他们的缩放比例也是不一样的,详细的说明可百度mdpi、hdpi、xhdpi、xxhdpi。不同dpi对应不同的屏幕尺寸,但是如果都堆弃在drawable中都会采用默认的mdpi对图片进行处理,像我使用的nexus 5 为 xxhdpi,因此图片的长宽分别扩大了三倍。虽说这样也能正常把图片灰度化,但是无疑增大了内存使用,利弊自行斟酌。


问题2:为什么在native层图片先转换成灰度图再转换成ARGB?

感谢Daniil Osokin解决我的问题,开始我也是只转换一次的,但是灰度图是单通道的,每个像素仅用一个char表示,但是Android中并不支持单通道的灰度图,因此我们拿到灰色的图片的Mat后,还需要再次转换成ARGB,这样传导Java层才可以解析成Android支持的Bitmap。


文章最后上一张转化后的灰度图,让大家乐呵乐呵^_^


  • 0
    点赞
  • 2
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值