jni传递图片

举报
风吹稻花香 发表于 2021/06/05 00:25:53 2021/06/05
【摘要】 ARGB和BGRA Bitmap转Mat转Bitmap ARGB和BGRA Android图片通道顺序为ARGB Opencv图片通道顺序为BGRA Bitmap转Mat转Bitmap   Java API使用,在导出Bitmap图之前,先将BGR转成RGB,就不会出现通道顺序混乱问题     private fun loadBitmap2...


ARGB和BGRA
Bitmap转Mat转Bitmap

ARGB和BGRA
Android图片通道顺序为ARGB
Opencv图片通道顺序为BGRA
Bitmap转Mat转Bitmap
 

Java API使用,在导出Bitmap图之前,先将BGR转成RGB,就不会出现通道顺序混乱问题


  
  1.     private fun loadBitmap2Mat2BitmapByCv() {
  2.         //加载bitmap到mat
  3.         val mat = Utils.loadResource(this, R.drawable.test)
  4.         //导出bitmap前,将格式从BGR转RGB
  5.         Imgproc.cvtColor(mat, mat, Imgproc.COLOR_BGR2RGB)
  6.         //创建一个空的bitmap
  7.         val bitmap = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_4444)
  8.         //mat转bitmap
  9.         Utils.matToBitmap(mat, bitmap)
  10.         iv3.setImageBitmap(bitmap)
  11.     }




Java代码


  
  1.         //加载图片资源
  2.         val bitmap = BitmapFactory.decodeResource(resources, R.drawable.test)
  3.         //声明一个数组保存图片数据,长度即为图片的宽x高
  4.         val data = IntArray(bitmap.width * bitmap.height)
  5.         //获取bitmap的像数值数据
  6.         bitmap.getPixels(data, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
  7.         //native函数,注意传入的宽对应cols,高对应rows
  8.         val fromJniData = loadBitmap2Mat(bitmap.height, bitmap.width, data)
  9.         //创建一个空的bitmap
  10.         val newBitmap = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.RGB_565)
  11.         //将native函数返回的图片数据设置到空的bitmap中
  12.         newBitmap.setPixels(fromJniData, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
  13.         iv2.setImageBitmap(newBitmap)



native函数

native函数传入3个参数,分别是行数,列数,图片像素数据,返回的是图片像素数据

 external fun loadBitmap2Mat(rows: Int, cols: Int, data: IntArray): IntArray

C++代码

返回jintarray数组:


  
  1. JNIEXPORT jintArray JNICALL Java_main_getIntArray(JNIEnv *env, jclass c) {
  2. jintArray intArray = env->NewIntArray(5);
  3. jint values[5] = {69, 69, 69, 69, 69};
  4. env->SetIntArrayRegion(intArray, 0, 5, values);
  5. //env->ReleaseIntArrayElements(intArray, values, NULL); 加上这句报错
  6. return intArray;
  7. }

返回initarray图片: 


  
  1. #include <jni.h>
  2. #include <android/log.h>
  3. #include <opencv2/core.hpp>
  4. #include <opencv2/imgproc.hpp>
  5. #include <opencv2/imgproc/types_c.h>
  6. #include <string.h>
  7. using namespace cv;
  8. extern "C"
  9. JNIEXPORT jintArray JNICALL
  10. Java_com_aib_mat_BitmapAndMatActivity_loadBitmap2Mat(JNIEnv *env, jobject obj,
  11.                                                      jint rows, jint cols, jintArray data) {
  12.     //获取数组的长度
  13.     int length = env->GetArrayLength(data);
  14.     //数组指针
  15.     int *pixels = env->GetIntArrayElements(data, JNI_FALSE);
  16.     //加载到Mat
  17.     Mat mat = Mat(rows, cols, CV_8UC4, pixels);
  18.     //下面2步需要按照:多通道->单通道->多通道,Android端才能正常显示
  19.     //开始我也是只转换一次的,但是灰度图是单通道的
  20.     //每个像素仅用一个char表示,但是Android中并不支持单通道的灰度图
  21.     //因此我们拿到灰色的图片的Mat后,还需要再次转换成ARGB,这样传导Java层才可以解析成Android支持的Bitmap
  22.     //1.转GRAY
  23.     cvtColor(mat, mat, COLOR_BGRA2GRAY);
  24.     //2.转回BGRA
  25.     cvtColor(mat, mat, COLOR_GRAY2BGRA);
  26.     jint *newData = (jint *) mat.ptr(0);
  27.     //创建新的空数组
  28.     jintArray newPixels = env->NewIntArray(length);
  29.     //将数据写到空的数组中
  30.     env->SetIntArrayRegion(newPixels, 0, length, newData);
  31.     return newPixels;
  32. }


 

java层的图片如何传递到c/c+层处理,处理完之后如何传回java层,下面总结了一下用到的三种方法。

1.将Bitmap转为int[]数组对象,将数组作为参数传递到C/C++层,处理完之后再以int[]数组返回。


  
  1. //将bitmap转化为数组,保存到pixels中
  2. Bitmap mOriginalBmp = BitmapFactory.decodeResource(getResources(), R.drawable.test);
  3. int w = mOriginalBmp.getWidth();
  4. int h = mOriginalBmp.getHeight();
  5. int[] pixels = new int[w * h];
  6. mOriginalBmp.getPixels(pixels, 0, w, 0, 0, w, h);
  7. //调用Native方法获得处理过后的数组,转化为bitmap
  8. int[] resultInt = ImageProc.grayPoc(pixels, w, h);
  9. Bitmap mBuildedBmp = Bitmap.createBitmap(resultInt,w, h,mOriginalBmp.getConfig());
  10. mImageView.setImageBitmap(mBuildedBmp);
  11. //native方法定义
  12. public static native int[] grayPoc(int[] pixels,int w,int h);
  13. //native的实现,将图片转化为灰度图
  14. JNIEXPORT jintArray JNICALL Java_com_dengxy_opencvtest_ImageProc_grayPoc(
  15. JNIEnv * env, jclass obj, jintArray buf, int w, int h) {
  16. LOGD("Java_com_dengxy_opencvtest_ImageProc_grayPoc:start");
  17. jint *cbuf;
  18. cbuf = env->GetIntArrayElements(buf, JNI_FALSE);
  19. if (cbuf == NULL) {
  20. return 0;
  21. }
  22. Mat imgData(h, w, CV_8UC4, (unsigned char *) cbuf);
  23. uchar* ptr = imgData.ptr(0);
  24. for (int i = 0; i < w * h; i++) {
  25. int grayScale = (int) (ptr[4 * i + 2] * 0.299 + ptr[4 * i + 1] * 0.587 +
  26. ptr[4 * i + 0] * 0.114);
  27. ptr[4 * i + 1] = grayScale;
  28. ptr[4 * i + 2] = grayScale;
  29. ptr[4 * i + 0] = grayScale;
  30. }
  31. int size = w * h;
  32. jintArray result = env->NewIntArray(size);
  33. env->SetIntArrayRegion(result, 0, size, cbuf);
  34. env->ReleaseIntArrayElements(buf, cbuf, 0);
  35. LOGD("Java_com_dengxy_opencvtest_ImageProc_grayPoc:end");
  36. return result;
  37. }
 

 

 

这种方法需要重复的拷贝,转化图片数据,空间和时间复杂度较高,效率较低。

2.直接将Bitmap对象传递到底层,C/C++获得Bitmap数据的指针,再转化为Mat,这种方法为底层直接操作bitmap的内存空间,操作前后会锁住该地址空间,完了java层刷新界面就可以了,
这里是一个Sobel边缘检测。用的时候发现要是内存空间有拷贝,操作的不是当前图片所在的内存空间的话,图片是改变不了的。

 


  
  1. 1 Bitmap mBuildedBmp = BitmapFactory.decodeResource(getResources(), R.drawable.test);
  2. 2 ImageProc.getSobel(mBuildedBmp);
  3. 3 mImageView.setImageBitmap(mBuildedBmp);
  4. 4
  5. 5 //java接口函数
  6. 6 private static native int getSobel(Bitmap in,Bitmap out);
  7. 7
  8. 8 //对应的C++文件需要引入头文件 bitmap.h
  9. 9 #include <android/bitmap.h>
  10. 10
  11. 11 //对应C++函数
  12. 12 JNIEXPORT void JNICALL Java_com_dengxy_opencvtest_ImageProc_getSobel(
  13. 13 JNIEnv * env, jclass obj, jobject bmpIn) {
  14. 14 AndroidBitmapInfo inBmpInfo;
  15. 15 void* inPixelsAddress;
  16. 16 int ret;
  17. 17 if ((ret = AndroidBitmap_getInfo(env, bmpIn, &inBmpInfo)) < 0) {
  18. 18 LOGD("AndroidBitmap_getInfo() failed ! error=%d", ret);
  19. 19 return;
  20. 20 }
  21. 21 LOGI("original image :: width is %d; height is %d; stride is %d; format is %d;flags is %d,stride is %u", inBmpInfo.width, inBmpInfo.height, inBmpInfo.stride, inBmpInfo.format, inBmpInfo.flags, inBmpInfo.stride);
  22. 22 if ((ret = AndroidBitmap_lockPixels(env, bmpIn, &inPixelsAddress)) < 0) {
  23. 23 LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
  24. 24 }
  25. 25 Mat inMat(inBmpInfo.height, inBmpInfo.width,
  26. 26 CV_8UC4, inPixelsAddress);
  27. 27 Sobel(inMat, inMat, inMat.depth(), 1, 1);
  28. 28 AndroidBitmap_unlockPixels(env, bmpIn);
  29. 29 LOGI("Return !! ");
  30. 30 return;
  31. 31
  32. 32 }

 

3.直接将Bitmap转化为Mat后,获取mat内存地址传到底层,处理后再返回内存地址到java层,根据地址加载Mat对象转化为bitmap。这种方法较上一种主要是内存空间有改变有可以,但是用的时候发现系统一GC回收图片,底层就出现了空指针,一看是底层MAT的析构函数没做非空判断,于是尝试着自己编译opencv4android,折腾了两天始终编译没通过,泪渀


  
  1. 1 //Java层代码
  2. 2 Bitmap oldBmp mBuildedBmp = BitmapFactory.decodeResource(getResources(), R.drawable.test);
  3. 3 Mat bmpMat = new Mat();
  4. 4 Utils.bitmapToMat(mBuildedBmp, bmpMat);
  5. 5 long resultAddress = -1;
  6. 6 resultAddress = ImageProc.getLaplacian(bmpMat.getNativeObjAddr());
  7. 7 Log.d(TAG, "doLaplacian:resultAddress="+resultAddress);
  8. 8 if(resultAddress<0){
  9. 9 return ;
  10. 10 }
  11. 11 Mat resultLaplacianMat = new Mat(resultAddress);
  12. 12 Utils.matToBitmap(resultLaplacianMat, mBuildedBmp);
  13. 13 mImageView.setImageBitmap(mBuildedBmp);
  14. 14
  15. 15 //jni接口
  16. 16 public static native long getLaplacian(long bitmap);
  17. 17
  18. 18 //c++实现
  19. 19 JNIEXPORT jlong JNICALL Java_com_dengxy_opencvtest_ImageProc_getLaplacian
  20. 20 (JNIEnv * env, jclass obj, jlong bmAddress){
  21. 21 LOGD("Java_com_dengxy_opencvtest_ImageProc_getLaplacian:start");
  22. 22 Mat *bitmpaMat = (Mat*) bmAddress;
  23. 23 if (NULL == bitmpaMat) {
  24. 24 LOGD("Java_com_dengxy_opencvtest_ImageProc_getLaplacian:the bitmpaMat is Null");
  25. 25 return -1;
  26. 26 }
  27. 27 Laplacian(*bitmpaMat,*bitmpaMat,bitmpaMat->depth());
  28. 28 jlong resultAddress = (jlong)bitmpaMat;
  29. 29 LOGD("Java_com_dengxy_opencvtest_ImageProc_getLaplacian:end");
  30. 30 return resultAddress;
  31. 31 }

文章来源: blog.csdn.net,作者:网奇,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/jacke121/article/details/114668633

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。