jni传递图片


ARGB和BGRA
Bitmap转Mat转Bitmap

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

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

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




Java代码

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



native函数

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

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

C++代码

返回jintarray数组:

JNIEXPORT jintArray JNICALL Java_main_getIntArray(JNIEnv *env, jclass c) {
        jintArray intArray = env->NewIntArray(5);
        jint values[5] = {69, 69, 69, 69, 69};

        env->SetIntArrayRegion(intArray, 0, 5, values);
        //env->ReleaseIntArrayElements(intArray, values, NULL); 加上这句报错
        return intArray;
    }

返回initarray图片: 

#include <jni.h>
#include <android/log.h>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <string.h>

using namespace cv;

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_aib_mat_BitmapAndMatActivity_loadBitmap2Mat(JNIEnv *env, jobject obj,
                                                     jint rows, jint cols, jintArray data) {
    //获取数组的长度
    int length = env->GetArrayLength(data);
    //数组指针
    int *pixels = env->GetIntArrayElements(data, JNI_FALSE);

    //加载到Mat
    Mat mat = Mat(rows, cols, CV_8UC4, pixels);

    //下面2步需要按照:多通道->单通道->多通道,Android端才能正常显示
    //开始我也是只转换一次的,但是灰度图是单通道的
    //每个像素仅用一个char表示,但是Android中并不支持单通道的灰度图
    //因此我们拿到灰色的图片的Mat后,还需要再次转换成ARGB,这样传导Java层才可以解析成Android支持的Bitmap

    //1.转GRAY
    cvtColor(mat, mat, COLOR_BGRA2GRAY);
    //2.转回BGRA
    cvtColor(mat, mat, COLOR_GRAY2BGRA);
    jint *newData = (jint *) mat.ptr(0);

    //创建新的空数组
    jintArray newPixels = env->NewIntArray(length);

    //将数据写到空的数组中
    env->SetIntArrayRegion(newPixels, 0, length, newData);

    return newPixels;
}


 

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

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

//将bitmap转化为数组,保存到pixels中
 Bitmap mOriginalBmp = BitmapFactory.decodeResource(getResources(), R.drawable.test);
 int w = mOriginalBmp.getWidth();
 int h = mOriginalBmp.getHeight();
 int[] pixels = new int[w * h];
 mOriginalBmp.getPixels(pixels, 0, w, 0, 0, w, h);
  
 //调用Native方法获得处理过后的数组,转化为bitmap
 int[] resultInt = ImageProc.grayPoc(pixels, w, h);
 Bitmap mBuildedBmp = Bitmap.createBitmap(resultInt,w, h,mOriginalBmp.getConfig());
 mImageView.setImageBitmap(mBuildedBmp);
  
 //native方法定义
 public static native int[] grayPoc(int[] pixels,int w,int h);
  
 //native的实现,将图片转化为灰度图
 JNIEXPORT jintArray JNICALL Java_com_dengxy_opencvtest_ImageProc_grayPoc(
             JNIEnv * env, jclass obj, jintArray buf, int w, int h) {
         LOGD("Java_com_dengxy_opencvtest_ImageProc_grayPoc:start");
         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++) {
  
             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);
         LOGD("Java_com_dengxy_opencvtest_ImageProc_grayPoc:end");
         return result;
     }
 

 

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

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

 

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

 

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI算法网奇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值