java 如何讀圖 怎樣傳給 C++

有讀者問到 “”“從安卓模擬器路徑用opencv讀取圖像失敗”,您這個問題解決了嗎?”

我剛開始將c++算法嵌入到安卓或java的時候也遇到過這個基本的問題,因爲兩者機制不一樣,很容易搞混。

在安卓中是不能直接用opencv的imread函數的,必須以安卓自己的方式讀圖,然後將buffer傳遞給接口函數。我的算法接口用了jni封裝,下面做個一簡單的例子進行示例說明,希望對像我這樣的安卓初學者有點幫助。

 

java讀圖並轉換到字節數組中

 

方式一: 從res文件夾讀取(傳入c++接口有問題)

/*

* test_image爲圖像的名稱, 測試圖像放在app\src\main\res\drawable文件夾下

*/

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test_image);

int height = bitmap.getHeight();

int width = bitmap.getWidth();

byte[] data = bitmap.getNinePatchChunk();

 

進入c++函數後,測試一下data數組是否正確,不行的話就用下面的轉換一下(將Bitmap對象讀到字節數組中):

ByteArrayOutputStream baos = new ByteArrayOutputStream();

bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

byte[] datas = baos.toByteArray();

 

存在一個問題: 默認讀入的圖像格式是ARGB_8888類型,且每個通道佔1個byte

 

方式二:從assets文件夾讀取(傳入c++接口測試ok, 與VS中的結果一致)

/*

* images/lena_gray.jpg , 測試圖像放在app\src\main\assets\images文件夾下

* 用到的接口函數爲ReadImageFromLocal

*/

package com.example.builtinalgorithm;

import androidx.appcompat.app.AppCompatActivity;

import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'textdetection-lib' library on application startup.
    //static {
    //    System.loadLibrary("textdetection-lib");
    //}

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String str = stringFromJNI();

        AssetManager assetManager = getAssets();
        String[] files = null;
        try {
            //files = assetManager.list("image"); //image 表示assets文件夾下的子文件夾名稱
            files = assetManager.list("");
        } catch (IOException e) {
            Log.e("tag", e.getMessage());
            str += e.getMessage();
            str += "\n";
        }

        //InputStream in = getAssets().open("file:///android_asset/frozen_east_text_detection.pb");
        // Example of a call to a native method
        TextView tv = findViewById(R.id.sample_text);

        String assetFile = "file number is: " + Integer.toString (files.length) +" First file name is: "+ files[0] + "\n";
        str += assetFile;
        assetFile = "file number is: " + Integer.toString (files.length) +" Second file name is: "+ files[1] + "\n";
        str += assetFile;
        assetFile = "file number is: " + Integer.toString (files.length) +" Third file name is: "+ files[2] + "\n";
        str += assetFile;

        //str += ReadNetFromLocal(files[0]); //F:/text_detect/models/east/frozen_east_text_detection.pb "file:///android_asset/frozen_east_text_detection.pb"
        //Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lena_gray);
        InputStream inputStream = null;
        try {
            inputStream = getResources().getAssets().open("images/lena_gray.jpg");
        } catch (IOException e) {
            e.printStackTrace();
            str += e.getMessage();
            str += "\n";
        }
        // 直接讀取 ARGB_8888 佔4個byte
        //Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
        // 灰度圖像只需要一個通道表示:轉換爲一個通道,佔1byte,這時只有alpha通道
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.ALPHA_8;
        Rect rect = new Rect();
        Bitmap bitmap = BitmapFactory.decodeStream(inputStream, rect, options);
        // lena_gray.jpg大小爲512*512,第一個像素值爲163
        int height = bitmap.getHeight();
        int width = bitmap.getWidth();
        int color = bitmap.getPixel(0,0);
        int a = Color.alpha(color);
        int bytes = bitmap.getByteCount();
        str += Integer.toString(width) + " " + Integer.toString(height) +" " + Integer.toString(bytes) +" " + Integer.toString(a) + "\n";
        
        // Bitmap轉換爲byte[]
        ByteBuffer buf = ByteBuffer.allocate(bytes);
        bitmap.copyPixelsToBuffer(buf);
        byte[] datas = buf.array();
        str += Integer.toString(datas.length) + "\n";
        str += ReadImageFromLocal(datas, width, height,1);

        tv.setText(str);
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
    public native String ReadNetFromLocal(String path);
    public native String ReadImageFromLocal(byte[] imgdata, int width, int height, int channels);

 

下面展示一下native-cpp中的接口函數。這部分是用來測試傳入的buffer是否正確。主要查看通道、pixel,然後對傳入的buffer進行簡單的均值方差計算,驗證與c++版本下的結果是否一樣即可。

#include <jni.h>
#include <string>

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
using namespace cv::dnn;

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_builtinalgorithm_MainActivity_ReadImageFromLocal(
        JNIEnv *env,
        jobject /* this */, jbyteArray imgdata, jint width, jint height, jint channels) {
//    jboolean iscopy;
//    const char* nativeString = env->GetStringUTFChars(js, &iscopy);
//    string file(nativeString);
    std::string tips = "In cpp function: \n";
    int ch= channels;
    int cols = width;
    int rows = height;
    jsize len = env->GetArrayLength(imgdata);
    tips += "GetArrayLength: ";
    tips += to_string((int)len);
    tips += "\n";
//    jbyte *jbarray = (jbyte *)malloc(len * sizeof(jbyte));
//    env->GetByteArrayRegion(imgdata, 0, len, jbarray);
    jbyte* jbarray = env->GetByteArrayElements(imgdata,0);
    unsigned char *dDate = (unsigned char*)jbarray;

    try {
        //Mat img = imread(file);
        Mat img;
        int type = CV_8UC1;
        if(ch==3){
            type = CV_8UC3;
            Mat tmp(rows,cols,type,dDate);
            cvtColor(tmp,img,COLOR_RGB2GRAY);
        }
        else if(ch==1){
            type = CV_8UC1;
            Mat tmp(rows,cols,type,dDate);
            img = tmp;
        }
        else if(ch==4){
            type = CV_8UC4;
            Mat tmp(rows,cols,type,dDate);
            cvtColor(tmp,img,COLOR_RGBA2GRAY);
        }
        else{
            tips += "param:channels is error";
            return env->NewStringUTF(tips.c_str());
        }

        tips += to_string(cols);
        tips += "  ";
        tips += to_string(rows);
        tips += "  ";
        int pixl = *(img.ptr<uchar>(0)+0);
        tips += to_string(pixl);
        tips += "\n";
        Scalar a,b;
        meanStdDev(img, a,b);
        tips += "Load image is ok: ";
        tips += to_string(a[0]);
        tips += "  ";
        tips += to_string(b[0]);
        tips += "\n";
    }catch(cv::Exception e)
    {
        String error = e.msg;
        tips += error;
        tips += "Load image error. \n";
    }

    return env->NewStringUTF(tips.c_str());
}

關於CMakeList.txt中的opencv配置這裏再重複一遍,我當前用的native-lib名稱改了一下:textdetection-lib,其他出現opencv字眼的地方都是opencv的配置

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# 2019.10.31
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(opencv_411_dir E:/opencv-4.1.1-android-sdk/OpenCV-android-sdk/sdk/native/jni)
set(app_dir F:/android/AndroidStudioProjects/BuiltinAlgorithm/app)

include_directories(${opencv_411_dir}/include)
add_library(opencv-lib SHARED IMPORTED)
set_target_properties(opencv-lib PROPERTIES IMPORTED_LOCATION ${app_dir}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java4.so)


# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        textdetection-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        textdetection-lib.cpp)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        textdetection-lib
        opencv-lib #2019.10.31

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章