Android像素二维数组转图像的实现

使用纯Java和jni OpenCV 的方式来实现 二维数组转图像的功能

Android上需要生成图片首先想到的是 createBitmap API,看了一下之后,实现了第一种方法

纯Java实现

private Bitmap createBitmap(int[][] dataArray) {
        // 转一维数组
        byte[] newArray = new byte[dataArray.length * dataArray[0].length];

        int index = 0;
        for (int row = 0; row < dataArray.length; row++) {
            for (int column = 0; column < dataArray[0].length; column++) {
                newArray[index++] = (byte)dataArray[row][column];
            }
        }
    
        // 注意 width,height 与 二维数组 row,colume 的对应关系
        Bitmap bitmap = Bitmap.createBitmap(dataArray[0].length, dataArray.length, Bitmap.Config.ARGB_8888);

        int pixels[] = new int[dataArray[0].length * dataArray.length];
        for (int i = 0; i < pixels.length; ++i) {
            //关键代码,生产灰度图
            pixels[i] = newArray[i] * 256 * 256 + newArray[i] * 256 + newArray[i] + 0xFF000000;
        }
    
        // 设置像素值
        bitmap.setPixels(pixels, 0, 128, 0, 0, 128, 19500);

        if (bitmap != null) {
            // 只是做图片缩放
            bitmap = zoomBitmap(bitmap, 440, 590);
        }

        return bitmap;

    }

    /**
     * 图片缩放
     * @param
     * @param bitmap 对象
     * @param w 要缩放的宽度
     * @param h 要缩放的高度
     * @return newBmp 新 Bitmap对象
     */
     public static Bitmap zoomBitmap (Bitmap bitmap,int w, int h){
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Matrix matrix = new Matrix();
        float scaleWidth = ((float) w / width);
        float scaleHeight = ((float) h / height);
        matrix.postScale(scaleWidth, scaleHeight);
        Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0, width, height,
                matrix, true);
        return newBmp;
     }

这里的二维数组是直接拿来用的,像素数组我是拿到的一个 csv文件解析出来的,这里就不赘述了,用的是 jxl 库。

完成之后发现实在是太慢了,出去解析 csv 的时间,光生成图片的时间都是以 秒 来计算的。

然后就换成了 OpenCV 来实现。结合 在现有AS项目中新增OpenCV c++库的支持,现在缺少的就只有 实现 转换图片的函数就可以了。

OpenCV Jni实现

public static native Bitmap array2Bitmap(int width, int height, int[][] original_data, Bitmap bitmap);

现在的 AS 都已经支持可以根据提示 自动在 cpp 文件生成对应的函数了

extern "C"
JNIEXPORT jobject JNICALL
Java_xyz_jimbray_healthydemo_JNITools_array2Bitmap(JNIEnv *env, jclass type, jint width, jint height,
                                                              jobjectArray original_data, jobject bitmap) {

    jint rows = env->GetArrayLength(original_data);
    jarray array = (jintArray)(env->GetObjectArrayElement(original_data, 0));

    jint cols = env->GetArrayLength(array);

    // 转换二维数组
    vector<vector<int>> swp(rows, vector<int>(cols));

    for (jint i = 0; i < rows; i++) {
        array = (jintArray)(env->GetObjectArrayElement(original_data, i));

        // 获取指针
        jint *col_data = env->GetIntArrayElements((jintArray)array, 0);

        for (jint j = 0; j < cols; j++) {
            swp[i][j] = col_data[j];
        }

        // 释放指针
        env->ReleaseIntArrayElements((jintArray)array, col_data, 0);
    }

    // 注意 width,height 的参数位置
    cv::Mat mat = cv::Mat(height, width, CV_8U);

    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            mat.at<uchar>(i, j) = swp[i][j];
        }
    }

    // 缩放图片以方便展示
    cv::resize(mat, mat, cv::Size(440, 590), 0, 0, CV_INTER_LINEAR);

    //get source bitmap's config
    jclass java_bitmap_class = (jclass)env->FindClass("android/graphics/Bitmap");
    jmethodID mid = env->GetMethodID(java_bitmap_class, "getConfig", "()Landroid/graphics/Bitmap$Config;");
    jobject bitmap_config = env->CallObjectMethod(bitmap, mid);
    jobject _bitmap = mat_to_bitmap(env, mat, false, bitmap_config);

    AndroidBitmap_unlockPixels(env, bitmap);

    return _bitmap;

}

// 不要忘记定义哦
jobject mat_to_bitmap(JNIEnv * env, cv::Mat& src, bool needPremultiplyAlpha, jobject bitmap_config){

    jclass java_bitmap_class = (jclass)env->FindClass("android/graphics/Bitmap");
    jmethodID mid = env->GetStaticMethodID(java_bitmap_class,
                                           "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");

    jobject bitmap = env->CallStaticObjectMethod(java_bitmap_class,
                                                 mid, src.size().width, src.size().height, bitmap_config);
    AndroidBitmapInfo  info;
    void* pixels = 0;

    try {
        //validate
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);

        //type mat
        if(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888){
            cv::Mat tmp(info.height, info.width, CV_8UC4, pixels);
            if(src.type() == CV_8UC1){
                cvtColor(src, tmp, CV_GRAY2RGBA);
            } else if(src.type() == CV_8UC3){
                cvtColor(src, tmp, CV_RGB2RGBA);
            } else if(src.type() == CV_8UC4){
                if(needPremultiplyAlpha){
                    cvtColor(src, tmp, COLOR_RGBA2mRGBA);
                }else{
                    src.copyTo(tmp);
                }
            }

        } else{
            cv::Mat tmp(info.height, info.width, CV_8UC2, pixels);
            if(src.type() == CV_8UC1){
                cvtColor(src, tmp, CV_GRAY2BGR565);
            } else if(src.type() == CV_8UC3){
                cvtColor(src, tmp, CV_RGB2BGR565);
            } else if(src.type() == CV_8UC4){
                cvtColor(src, tmp, CV_RGBA2BGR565);
            }
        }
        AndroidBitmap_unlockPixels(env, bitmap);
        return bitmap;
    } catch(cv::Exception e){
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("org/opencv/core/CvException");
        if(!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
        return bitmap;
    } catch (...){
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
        return bitmap;
    }
}

在 Java 层调用

private Bitmap createBitmap(int[][] dataArray) {
    Bitmap src = Bitmap.createBitmap(dataArray[0].length, dataArray.length, Bitmap.Config.ARGB_8888);
            Bitmap bitmap = JNITools.array2Bitmap(dataArray[0].length, dataArray.length, dataArray, src);
    return bitmap;
}

完成。

相对前面的 纯Java版本,速度现在是以 毫秒 计,根本没有对比的资格。

 
comments powered by Disqus