2
package com.example.cppinandroid;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import org.opencv.core.Mat;
import java.io.File;
import static org.opencv.imgcodecs.Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE;
import org.opencv.imgcodecs.Imgcodecs;

public class MainActivity extends AppCompatActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tv = findViewById( R.id.sample_text );

        Mat image;
        image = Imgcodecs.imread( "/home/<myName>/a.png", CV_LOAD_IMAGE_GRAYSCALE);
        if ( image.size().height > 0 )
        {
            tv.setText( "No datfa in image!");
        }
        else
        {
            tv.setText( "xxff " + image.size().height);
        }
    }
}

I am NOT using any drawable or external media. Image is present in home folder and can be opened by a normal opencv c++ program by giving the exact same path.

Someone here told me that native C++ NDK will not be able to read Linux paths. Alright. Here it is all Java.

When I execute this, it always goes in else statement and shows the height as 0.0.

I have removed the extra code.

What is the way to read a normal png from home folder in this program on Linux?

Mayank Kumar Chaudhari
  • 16,027
  • 10
  • 55
  • 122
Aquarius_Girl
  • 21,790
  • 65
  • 230
  • 411

5 Answers5

4

All Android devices or emulators don't have access to storages outside like your Linux storage partition, they have access to their internal storage or sdcard. In the case of the emulator, their internal storage is emulated using a file with a certain format that cannot be easily read. In an emulator or a device that has Developer options enabled, one could use the adb command found within the Android SDK platform-tools folder to transfer files into it as such:

adb push file.jpg /sdcard/file.jpg

After that, you'll need to change the path of the file your using in the code to match and also enable permissions to READ_EXTERNAL_STORAGE (here external means external to the application your running, but still internal to the device).

Someone here told me that native C++ NDK will not be able to read Linux paths. Alright. Here it is all Java.

Looking at your question and the answer, for start this is the same problem, trying to access a file that is not part of the device/emulator internal storage. However the answer isn't entirely true, the C/C++ code can access files and directories of the internal storage as long as permission is granted to the application. I would suggest you first try fixing the problem using Java and then switch back to the code you had in your other question but with the corrected path. With Java, you'll be using the Java wrapper for the OpenCV APIs, hence you'll need to call OpenCVLoader.initDebug() to load the wrapper lib. When using pure NDK, you'll only need to load the compiled lib (System.loadLibrary(<libname>) you created with the native C/C++ code.

ahasbini
  • 6,761
  • 2
  • 29
  • 45
3

When you use Imgcodecs.imread(...), it will read the path on your machine, which running your application.

So, if you run the Java Application, it will run on your JVM within your computer, that mean it read the path like ~/home/... on your computer and that path exist, so it can get somethings.

But, Android App will run on DVM within Android Device, that mean when you read ~/home/.., it will take that path on Android Device, but it wasn't exist on Android Devices. So you can't get anything.

The best practice, you should use Imgcodecs.imread(...) with the External Storage Path like some guys suggest you above.

Sometime, you maybe can use Imgcodecs.imread(...) on /mtn/..,path of SD Card, but it isn't correct completely.

dinhlam
  • 708
  • 3
  • 14
  • but right now I am on my desktop and it is not reading anything. – Aquarius_Girl May 27 '20 at 03:42
  • you can check: [link](https://github.com/dinhlamvn/oxscanner/blob/master/src/img/proc/BubbleSheetMultipleScanner.java). I had read an image, that's ok. See at: ```Imgcodecs.imread(path + name + extend, Imgcodecs.CV_LOAD_IMAGE_COLOR);``` – dinhlam May 27 '20 at 04:07
2

The first problem mentioned in the comment below the question is that you must load the native library that implements the image loading. This can be done with the following code:

    static {
        // TODO: use OpenCVLoader.initAsync for a real application
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Failed to initialize OpenCV.");
        }
    }

In a real application you would use the initAsync function such that the loading of the libraries does not block the main thread. In a simple example this does not matter.

Another problem is, that file IO on Android requires a permission if you want to access files in arbitrary directories. The permission must be declared in your manifest file. This can be done by adding the following two lines above the application tag in the manifest file.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

These permissions must be requested at runtime. To check if the permission was granted already the following code can be used:

if (ContextCompat.checkSelfPermission(this,
    android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    // do something with the permission
}

If the permission is not available, it can be requested as follows:

ActivityCompat.requestPermissions(this, new String[]{
    android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, RW_PERMISSION_REQUEST_CODE);

Note: we only request the write permission because they are grouped and if the user grants the write permission we automatically also obtain the read permission.

To handle the result the onRequestPermissionsResult callback in the activity class should be overwritten as seen in the full code example below. Because the permission system is quite complex take a look at the official documentation. For info on requesting permissions look here.

Finally to make the loading work the file path must be correct. The user-accessible memory locations depend on the phone manufacturer, therefore it is good to use the system methods provided by Android to find the correct path, for example getExternalStorageDirectory(). More information on the various storage locations can be found here.

Here the full code:


import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.os.Environment.getExternalStorageDirectory;

public class MainActivity extends AppCompatActivity {

    private static String TAG = "MainActivity";
    private static final int RW_PERMISSION_REQUEST_CODE = 123;

    static {
        // TODO: use OpenCVLoader.initAsync for a real application
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Failed to initialize OpenCV.");
        }
    }

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

        if (ContextCompat.checkSelfPermission(
                this, WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionDenied();
        } else {
            permissionGranted();
        }
    }

    private void permissionDenied() {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, WRITE_EXTERNAL_STORAGE)) {
            new AlertDialog.Builder(this)
                    .setTitle("Read/Write permission required to read an image file.")
                    .setCancelable(false)
                    .setPositiveButton("Grant", (dialog, which) ->
                            ActivityCompat.requestPermissions(this, new String[]{
                                    WRITE_EXTERNAL_STORAGE}, RW_PERMISSION_REQUEST_CODE))
                    .setNegativeButton("Deny", (dialog, which) -> {
                        Toast.makeText(this,
                                "App cannot work without permission.", Toast.LENGTH_LONG).show();
                        this.finish();
                    })
                    .create()
                    .show();
        } else {
            ActivityCompat.requestPermissions(
                    this, new String[]{WRITE_EXTERNAL_STORAGE}, RW_PERMISSION_REQUEST_CODE);
        }
    }

    private void permissionGranted() {
        String path = getExternalStorageDirectory().getAbsolutePath() + "/a.png";

        Mat image = Imgcodecs.imread(path, Imgcodecs.IMREAD_GRAYSCALE);
        if (image.empty()) {
            Toast.makeText(this, "Failed image", Toast.LENGTH_LONG).show();
        } else {
            Size size = image.size();
            Toast.makeText(this, "Loaded image " + size.height, Toast.LENGTH_LONG).show();

            // the following code is only necessary to display the image in an ImageView
            ImageView iv = findViewById(R.id.imageView);
            Mat tmp = new Mat((int) size.height, (int) size.width, CvType.CV_8U, new Scalar(4));
            try {
                Imgproc.cvtColor(image, tmp, Imgproc.COLOR_GRAY2RGBA, 4);
                Bitmap bmp = Bitmap.createBitmap(tmp.cols(), tmp.rows(), Bitmap.Config.ARGB_8888);
                Utils.matToBitmap(tmp, bmp);
                iv.setImageBitmap(bmp);
            } catch (CvException e) {
                Log.d(TAG, e.getMessage());
                Toast.makeText(this, "Couldn't convert image.", Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(
            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == RW_PERMISSION_REQUEST_CODE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                permissionGranted();
            } else {
                permissionDenied();
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

To make this code work add an ImageView with id imageView to your activity_main.xml layout file.

Jannik
  • 1,583
  • 1
  • 14
  • 22
2

see this example code, maybe help you, i tested this code and work for me, and i can get width and height of any image.

1) first you need import OpenCVLibrary to your project: see this Link - Link

2) then you need set READ_EXTERNAL_STORAGE permision to your application: plz add this command on your AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

3) you need file picker for get specific file i use ru.bartwell:exfilepicker: Link

implementation 'ru.bartwell:exfilepicker:2.1'

4) and at the end you just add this simple code to your MainActivity:

    private static final int READ_STORAGE_PERMISSION_REQUEST_CODE = 1;
    private static final int EX_FILE_PICKER_RESULT = 0;

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

        initLoadOpenCV();

        if (!checkPermissionForReadExtertalStorage()) {
            requestPermissionForReadExtertalStorage();
        }

    }

    private void initLoadOpenCV() {
        boolean isDebug = OpenCVLoader.initDebug();
        if (isDebug) {
            Log.i("init Opencv", "init openCV success!!");
        } else {
            Log.e("init Opencv", "init openCV failure!!");
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == EX_FILE_PICKER_RESULT) {
            ExFilePickerResult result = ExFilePickerResult.getFromIntent(data);
            if (result != null && result.getCount() > 0) {
                // Here is object contains selected files names and path
                Log.i("folderLocation", result.getPath() + result.getNames().get(0));

                Mat srcMat1 = Imgcodecs.imread(result.getPath() + result.getNames().get(0));
                if (srcMat1.empty()) {
                    return;
                }

                int width = srcMat1.width();
                int height = srcMat1.height();
                int type = srcMat1.type();
                Log.i("width", srcMat1.width() + "");
                Log.i("height", srcMat1.height() + "");
                Log.i("type", srcMat1.type() + "");

            }
        }
    }

    public boolean checkPermissionForReadExtertalStorage() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int result = getApplicationContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
            return result == PackageManager.PERMISSION_GRANTED;
        }
        return false;
    }

    public void requestPermissionForReadExtertalStorage() {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                READ_STORAGE_PERMISSION_REQUEST_CODE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case READ_STORAGE_PERMISSION_REQUEST_CODE:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.e("value", "Permission Granted, Now you can use local drive .");

                    ExFilePicker exFilePicker = new ExFilePicker();
                    exFilePicker.start(this, EX_FILE_PICKER_RESULT);
                } else {
                    Log.e("value", "Permission Denied, You cannot use local drive .");
                    requestPermissionForReadExtertalStorage();
                }
                break;
        }
    }
Taher Fattahi
  • 951
  • 1
  • 6
  • 14
2

Before you read on further I would like to clarify a few things

  1. From question it is not clear where the code is running? It seems that the user is running this android app on a virtual machine or emulator.

1.1 - From the question it seems that the file she wants to open is on the home directory of Linux machine and not in the emulator's storage or on an Android device - in this case Please note that android apps running on emulator can not access files from your computer.

---- so if you were trying to access file on your Linux pc from within android emulator or vm, please note that it is not possible. Instead copy and put the file in the android emulator or device on which your app will be running.

Please clarify question and let us know whether you have file on the emulator storage (or android device) or it is on your pc and code running on emulator.

  1. If you have file on emulator or android device, please make sure you have right permissions declared in manifest and you have also requested user for permission to read storage before trying to read the image.

Update

Thank you for the response in comments.

How to put file on emulator?

To add a file to the emulated device, drag the file onto the emulator screen. The file is placed in the /sdcard/Download/ directory. You can view the file from Android Studio using the Device File Explorer, or find it from the device using the Downloads or Files app, depending on the device version

source https://developer.android.com/studio/run/emulator

For permissions related stuff you can refer easy to follow documentation on official website - https://developer.android.com/training/permissions/requesting

or check out this question - Android marshmallow request permission?

You can also check -- https://stackoverflow.com/a/30434631/9640177

Depreciation note

Please check https://stackoverflow.com/a/59812593/9640177


To avoid dealing with permissions and external directories, you can also transfer your file to your app's internal storage using android studio -- explore device storage.


Update 2

Refer this answer please - https://stackoverflow.com/a/5990806/9640177

If you want to access them in runtime and not push the files there you have to have a public IP on the internet in which you can access and expose the files on HTTP with something like Apache Tomcat or FTP and then access that from the emulator.

You can also access it locally without exposing it to the whole internet, by accessing from the Android emulator to the "localhost" IP. You'll have to search which IP this is.

So your use case necessitates accessing files from location on your pc, you can use something like tomcat to create a local http server.

Mayank Kumar Chaudhari
  • 16,027
  • 10
  • 55
  • 122