Android NDK开发——Android studio使用JNI调用OpenCV处理图像

前言

这里要演示的是使用Android studio 做APP开发,使用JNI与C++交互的demo。

一.创建工程

1.创建一个Native C++工程。
Android NDK开发——Android studio使用JNI调用OpenCV处理图像_第1张图片
2.命令工程和指定交互语言。
Android NDK开发——Android studio使用JNI调用OpenCV处理图像_第2张图片
3.指定C++ 语法版本。
Android NDK开发——Android studio使用JNI调用OpenCV处理图像_第3张图片
4.创建完成之后打开cpp目录下的CMakeLists.txt文件,这里有IDE默认生成的链接路径。
Android NDK开发——Android studio使用JNI调用OpenCV处理图像_第4张图片
5.把编译好的opencv库(ndk r21d,android api 24)复制到CPP目录下,添加依赖。(我这里用的OpenCV是nihui大佬的opencv-mobile,因为小(不到20M)而且不用自己编译)。
Android NDK开发——Android studio使用JNI调用OpenCV处理图像_第5张图片

二.实现代码

1.首先在main.xml布局文件添加控件


<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    ImageView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_open_images"
            android:layout_width="0dp"
            android:text="打开图像"
            android:layout_height="wrap_content"
            android:layout_weight="1">
        Button>

        <Button
            android:id="@+id/btn_gray_image"
            android:layout_width="0dp"
            android:text="处理图像"
            android:layout_height="wrap_content"
            android:layout_weight="1">
        Button>
        <Button
            android:id="@+id/btn_send_image"
            android:layout_width="0dp"
            android:text="交互图像"
            android:layout_height="wrap_content"
            android:layout_weight="1">
        Button>
    LinearLayout>>

LinearLayout>

2.添加一个Java类用来写交互接口
Android NDK开发——Android studio使用JNI调用OpenCV处理图像_第6张图片
2.添加两个接口, 一个是本地读取图像,一个是从资源里面读取图像。
3.MainActivity.java的代码:

package com.dashu.dashuaiip;

import java.io.InputStream;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import java.io.FileNotFoundException;
import java.io.IOException;


public class MainActivity<bitmap2> extends Activity
{
    private static final int SELECT_IMAGE = 1;

    private ImageView imageView;
    private Bitmap bitmap = null;
    private Bitmap temp = null;
    private Bitmap showImage = null;
    private Bitmap bitmapCopy = null;
    private Bitmap dst = null;
    boolean useGPU = true;

    DaShuAPI dashuapi = new DaShuAPI();

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        imageView = (ImageView) findViewById(R.id.image_view);

        //初始化模板
        try
        {
            initTemplate();
            Log.e("MainActivity", "init template file ok!");
        } catch (IOException e) {
            Log.e("MainActivity", "init template file  error!");
        }

        //打开图像
        Button openFile = (Button) findViewById(R.id.btn_open_images);
        openFile.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View arg0)
            {
                Intent i = new Intent(Intent.ACTION_PICK);
                i.setType("image/*");
                startActivityForResult(i, SELECT_IMAGE);
            }
        });

        Button image_gray = (Button) findViewById(R.id.btn_gray_image);

        image_gray.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View arg0)
            {
                if (showImage == null)
                {
                    return;
                }

                Bitmap bitmap = dashuapi.grayModel(showImage);

                imageView.setImageBitmap(bitmap);
            }
        });

        Button read_assets_image = (Button) findViewById(R.id.btn_send_image);
        read_assets_image.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View arg0)
            {
                if (showImage == null)
                {
                    return;
                }

                Bitmap bitmap = dashuapi.readAssetsImage();

                imageView.setImageBitmap(bitmap);
            }
        });


    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK && null != data) {
            Uri selectedImage = data.getData();

            try
            {
                if (requestCode == SELECT_IMAGE) {
                    bitmap = decodeUri(selectedImage);

                    showImage = bitmap.copy(Bitmap.Config.ARGB_8888, true);
                    bitmapCopy = bitmap.copy(Bitmap.Config.ARGB_8888, true);

                    imageView.setImageBitmap(bitmap);
                }
            }
            catch (FileNotFoundException e)
            {
                Log.e("MainActivity", "FileNotFoundException");
                return;
            }
        }
    }

    private Bitmap decodeUri(Uri selectedImage) throws FileNotFoundException
    {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE = 640;

        // Find the correct scale value. It should be the power of 2.
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp / 2 < REQUIRED_SIZE
               || height_tmp / 2 < REQUIRED_SIZE) {
                break;
            }
            width_tmp /= 2;
            height_tmp /= 2;
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage), null, o2);

        // Rotate according to EXIF
        int rotate = 0;
        try
        {
            ExifInterface exif = new ExifInterface(getContentResolver().openInputStream(selectedImage));
            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_270:
                    rotate = 270;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    rotate = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    rotate = 90;
                    break;
            }
        }
        catch (IOException e)
        {
            Log.e("MainActivity", "ExifInterface IOException");
        }

        Matrix matrix = new Matrix();
        matrix.postRotate(rotate);
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }

    private void initTemplate() throws IOException
    {
        //用io流读取二进制文件,最后存入到byte[]数组中
        InputStream in = getAssets().open("template.jpg");//模板图像文件
        int length = in.available();
        byte[] buffer = new byte[length];
        in.read(buffer);

        //转换为Bitmap
        BitmapFactory.Options opts = new BitmapFactory.Options();
        Bitmap template = BitmapFactory.decodeByteArray(buffer, 0, buffer.length, opts);

        int width = template.getWidth();
        int height = template.getHeight();

        int[] pixArr = new int[width*height];

        template.getPixels(pixArr,0,width,0,0,width,height);

        int rt = dashuapi.sendTemplate(pixArr,width,height);
    }
}

4.jni.cpp的代码

#include 
#include 
#include 
#include 
#include 
cv::Mat cv_template;

void BitmapToMat2(JNIEnv *env, jobject& bitmap, cv::Mat& mat, jboolean needUnPremultiplyAlpha)
{
    AndroidBitmapInfo info;
    void *pixels = 0;
    cv::Mat &dst = mat;

    try {
        CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
        CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
                  info.format == ANDROID_BITMAP_FORMAT_RGB_565);
        CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);
        CV_Assert(pixels);
        dst.create(info.height, info.width, CV_8UC4);
        if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
            cv::Mat tmp(info.height, info.width, CV_8UC4, pixels);
            if (needUnPremultiplyAlpha) cvtColor(tmp, dst, cv::COLOR_mRGBA2RGBA);
            else tmp.copyTo(dst);
        } else {
//             info.format == ANDROID_BITMAP_FORMAT_RGB_565
            cv::Mat tmp(info.height, info.width, CV_8UC2, pixels);
            cvtColor(tmp, dst, cv::COLOR_BGR5652RGBA);
        }
        AndroidBitmap_unlockPixels(env, bitmap);
        return;
    } catch (const 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;
    } catch (...) {
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nBitmapToMat}");
        return;
    }
}

void BitmapToMat(JNIEnv *env, jobject& bitmap, cv::Mat& mat) {
    BitmapToMat2(env, bitmap, mat, false);
}

void MatToBitmap2
        (JNIEnv *env, cv::Mat& mat, jobject& bitmap, jboolean needPremultiplyAlpha)
        {
    AndroidBitmapInfo info;
    void *pixels = 0;
    cv::Mat &src = mat;

   try {
       CV_Assert(AndroidBitmap_getInfo(env, bitmap, &info) >= 0);
       CV_Assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 ||
                 info.format == ANDROID_BITMAP_FORMAT_RGB_565);
       CV_Assert(src.dims == 2 && info.height == (uint32_t) src.rows &&
                 info.width == (uint32_t) src.cols);
       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);
       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::COLOR_GRAY2RGBA);
           }
           else if (src.type() == CV_8UC3)
           {
               cvtColor(src, tmp, cv::COLOR_RGB2RGBA);
           }
           else if (src.type() == CV_8UC4)
           {
               if (needPremultiplyAlpha)
               {
                   cvtColor(src, tmp, cv::COLOR_RGBA2mRGBA);
               }
               else{
                   src.copyTo(tmp);
               }
           }
       } else {
           // info.format == ANDROID_BITMAP_FORMAT_RGB_565
           cv::Mat tmp(info.height, info.width, CV_8UC2, pixels);
           if (src.type() == CV_8UC1)
           {
               cvtColor(src, tmp, cv::COLOR_GRAY2BGR565);
           }
           else if (src.type() == CV_8UC3)
           {
               cvtColor(src, tmp, cv::COLOR_RGB2BGR565);
           }
           else if (src.type() == CV_8UC4)
           {
               cvtColor(src, tmp, cv::COLOR_RGBA2BGR565);
           }
       }
       AndroidBitmap_unlockPixels(env, bitmap);
       return;
   }catch (const 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;
    } catch (...) {
        AndroidBitmap_unlockPixels(env, bitmap);
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {nMatToBitmap}");
        return;
    }
}

jobject generateBitmap(JNIEnv *env, uint32_t width, uint32_t height)
{

    jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
    jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls,
                                                            "createBitmap",
                                                            "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
    jstring configName = env->NewStringUTF("ARGB_8888");
    jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");
    jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(
            bitmapConfigClass, "valueOf",
            "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");

    jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass,
                                                       valueOfBitmapConfigFunction, configName);

    jobject newBitmap = env->CallStaticObjectMethod(bitmapCls,createBitmapFunction,
                                                    width,
                                                    height, bitmapConfig);
    return newBitmap;
}


void MatToBitmap(JNIEnv *env, cv::Mat& mat, jobject& bitmap)
{
    MatToBitmap2(env, mat, bitmap, false);
}


extern "C" JNIEXPORT jobject JNICALL
Java_com_dashu_dashuaiip_DaShuAPI_grayModel(JNIEnv *env,jobject, jobject image)
{

    cv::Mat cv_src,cv_gray;
    //bitmap转化成mat
    BitmapToMat(env,image,cv_src);
    cv::cvtColor(cv_src,cv_gray,cv::COLOR_BGRA2GRAY);

    MatToBitmap(env,cv_gray,image);
    return image;
}

extern "C" JNIEXPORT jobject JNICALL
        Java_com_dashu_dashuaiip_DaShuAPI_readAssetsImage(JNIEnv *env,jobject)
{
    jobject dst = generateBitmap(env,cv_template.cols,cv_template.rows);
    MatToBitmap(env,cv_template,dst);
    return dst;
}

extern "C"
JNIEXPORT int JNICALL
Java_com_dashu_dashuaiip_DaShuAPI_sendTemplate(JNIEnv *env, jobject instance, jintArray pix_, jint w, jint h)
{
    jint *pix = env->GetIntArrayElements(pix_, NULL);
    if (pix == NULL)
    {
        return -1;
    }

    //将c++图片转成Opencv图片
    cv::Mat cv_temp(h, w, CV_8UC4, (unsigned char *) pix);

    if(cv_temp.empty())
    {
        return -2;
    }

    cv::cvtColor(cv_temp,cv_template,cv::COLOR_BGRA2BGR);

    return 0;
}

5.在assets目录下添加一张用来测试(这里是为了测试从资源目录读入图像,可以不用)。
Android NDK开发——Android studio使用JNI调用OpenCV处理图像_第7张图片
6.运行测试Demo。

你可能感兴趣的:(android,android,studio,opencv)