您的位置:首页 > 移动开发 > Android开发

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

2016-07-07 16:58 597 查看
在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。

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android native opencv