C++ OpenCV imread not working in Android - android

I am trying to read an image in my C++ code
LOGD("Loading image '%s' ...\n", (*inFile).c_str());;
Mat img = imread(*inFile, CV_LOAD_IMAGE_GRAYSCALE);
CV_Assert(img.data != 0);
and get the following output:
09-25 17:08:24.798: D/IRISREC(12120): Loading image '/data/data/com.example.irisrec/files/input/osoba1.jpg' ...
09-25 17:08:24.798: E/cv::error()(12120): OpenCV Error: Assertion failed (img.data != 0) in int wahet_main(int, char**), file jni/wahet.cpp, line 4208
The file exists. But strange is, that if I try to preview the image using Root File Browser it is just black. I copied the files there manually.
EDIT:
The code works fine under Windows with .png and .jpg format. I am just trying to port an existing C++ project for Iris Recognition to Android.

imread() determines the type of file based on its content not by the file extension. If the header of the file is corrupted, it makes sense that the method fails.
Here are a few things you could try:
Copy those images back to the computer and see if they can be opened by other apps. There's a chance that they are corrupted in the device;
Make sure there is a file at that location and that your user has permission to read it;
Test with types of images (jpg, png, tiff, bmp, ...);
For testing purposes it's always better to be more direct. Get rid of inFile:
Example:
Mat img = imread("/data/data/com.example.irisrec/files/input/osoba1.jpg", CV_LOAD_IMAGE_GRAYSCALE);
if (!img.data) {
// Print error message and quit
}

When debugging, first try to get more data on the problem.
It's an unfortunate design that imread() doesn't provide any error info. The docs just say that it'll fail "because of missing file, improper permissions, unsupported or invalid format".
Use the debugger to step into the code if you can. Can you tell where it fails?
Search for known problems, stackoverflow.com/search?q=imread, e.g. imread not working in OpenCV.
Then generate as many hypotheses as you can. For each one, think of a way to test it. E.g.
The image file is malformed (as #karlphillip offered). -- See if other software can open the file.
The image file is not a supported format. -- Verify the file format on your desktop. Test that desktop OpenCV can read it. Check the docs to verify the image formats that AndroidCV can read.
The image file is not at the expected path. -- Write code to test if there's a file at that path, and verify its length.
The image file does not have read permission. -- Write code to open the file for reading.
A problem with the imread() arguments. -- Try defaulting the second argument.

I was able to solve this issue only by copying the image files in code.I stored them in my asset folder first and copied them to internal storage following this example.
If someone can explain this to me please do this.

It could be a permission issue.You would have to request the permission from Java code in your Activity class like this in Android 6.0 or above. Also make sure that in your AndroidManifest.xml, you have the the following line :
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
In your activity file add this:
if (PermissionUtils.requestPermission(
this,
HOME_SCREEN_ACTIVITY,
Manifest.permission.READ_EXTERNAL_STORAGE)) {
Mat image = Imgcodecs.imread(filePath,Imgcodecs.IMREAD_COLOR);
}
I struggled a long time to find this and I was getting Mat object null for all the time before.

Related

What is the best practice to modify an external image file of MediaStore in place?

I am writing an android app in Android Q. I tried to modify an external image file acquired from the MediaStore in place.
The simplified logic is:
contentResolver.openInputStream(uri).use {
buffer = it.readBytes()
}
edit(buffer)
contentResolver.openOutputStream(uri, "wt").use {
it?.write(buffer)
}
Also, android:requestLegacyExternalStorage="true"> is set. The image can be modified in place successfully, but I realize there is also some problem:
MediaStore is not updated immediately after the file is updated. e.g. the size of the file is still the old size.
Sometime calling contentResolver.loadThumbnail right after modifying the file will result in
W/MediaStore: Failed to obtain thumbnail for content://media/external/images/media/31
java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)
These problems lead me to think whether modifying the MediaStore file in place is an allowed action? If yes, what is the best practice to do this?

Python: push file to Android device pass but the file is empty

I have application that i am testing and i want to push file into my android device (real device)
This is what i have try:
self.driver.push_file('/mnt/sdcard/Pictures/photo.png', r'C:\photo.png')
So this operation is pass and i can see the file on my device but its size is 1kb and when i try to open it i have this message:
Its looks like we dont support this file format
What i am doing wrong ?
Pay attention that using Appium with the Python language, when you call the self.driver.push_file() method, the second parameter is the Contents of file in base64 (and not the path of the file on your machine).
That means that you first have to read the file, convert it to base64 (and decode it using utf-8) and only then pass it to this method:
import base64
...
with open(r'C:\photo.png','rb') as file:
driver.push_file('/mnt/sdcard/Pictures/photo.png',
base64.b64encode(file.read()).decode('utf-8'))
Alternatively, you can simply use the following command (adding just source_path= to your snippet):
self.driver.push_file('/mnt/sdcard/Pictures/photo.png', source_path=r'C:\photo.png')
..since the push_file() method was recently updated to support also source path (see Pull #270):
def push_file(self, destination_path, base64data=None, source_path=None)
I would recommend on the source_path parameter of course :)

How to read an image into a Mat object using opencv c++ and android studio ndk?

I have searched for a few hours now and can't seem to find an answer to my question(s). I have written the following lines of code in the android ndk(c++) and I am using the needed opencv libraries to accomplish the task.
void opening_images(){
Mat image ;
sillyString = "I have changed";
String imagePath = "//drawable//ring.png";
image = imread(imagePath,IMREAD_COLOR);
if(image.empty()){
sillyString = "Image not loaded";
}
else {
sillyString = "Image loaded";
}
}
I have tested this code in Qt with opencv and it works fine.At the moment the program in android studio returns the "Image not loaded" string. I think the main problem which is present is, the fact that I don't completely understand how to work with the file paths? In android studio I have included a picture under res/drawable/ring.png. I am able to view this image using the java side of the app.
Question 1: Is the specified imagePath = "//drawable//ring.png" correct to access the ring.png file ?
Question 2: Is there any permissions needed allowing the ndk to access res folders ?
Question 3: Is there any similar methods to assign an image to a Mat object?
Any help will be appreciated.
Edit:
If you take a look at how BitmapFactory decode resource works - you will see that getting bitmap from drawable still requires unpacking of a compressed image.
So answer to your q1: No it is not correct way to access ring.png, you will either have to download resource to your device or unpack it to byteArray and use imdecode instead of imread
Since this was my first time using Android Studio there was much to learn. It took me a while but here is the answers to the questions that I posted.
Question 1: Is the specified imagePath = "//drawable//ring.png" correct to access the ring.png file ?
This is most definitely not the correct path to use when accessing images for the purpose of image processing etc.The drawable folder can still be used to update ex. an image view by setting the src of the image view to the image
imageView.setImageResource(R.drawable.ring);
When working with images and Mat objects, I found it best to use the Android debugging bridge to copy the files to the SD card of the device. This link will provided you with the necessary steps to install the adb https://www.howtogeek.com/125769/how-to-install-and-use-abd-the-android-debug-bridge-utility/
When the images are done copying to the SD card. The file path can be found by making use of the built in Java functionEnvironment.getExternalStorageDirectory(). Which looks something like /storage/emulated/0/YOUR_file it depends on the location which was used to copy the files to.
Useful tip: Download ES File Explorer for the device to help navigate through the external or internal storage.
Question 2: Is there any permissions needed allowing the ndk to access res folders ?
The method which I used didn't need any permissions. However at the moment the NDK side cannot directly read an image from the SD card, the image must be passed from the Java side by making use of assets or by passing the address of the image which was read into the Mat object(Java side).
Read and write permission is needed in order to access the SD card. This must be set in the manifest.xml and must be correctly implemented in the code. There are many great tutorials on YouTube.
Question 3: Is there any similar methods to assign an image to a Mat object?
This question seems redundant now, there are many ways to skin a cat.
In short, I think it is easier to stick to the Java side when using Opencv4Android and some form of image processing is needed.To get you started in java here is a small snippet from my code.
Mat image;
String imageInSD = Environment.getExternalStorageDirectory()+"/Pictures/Data/Birds/"(ImageFolders[i])+"/"+String.valueOf(id)+".png";
image = Imgcodecs.imread(imageInSD,Imgcodecs.IMREAD_COLOR);
Good luck!!
Another way to use saved image in NDK is follows,
Then drawable folder, you can save it in assets folder. This helps to access multiple images also easily.
Then BitmapFactory.decodeStream helps to take it as bitmap and Utils.bitmapToMat is used to convert bitmap image to Mat file.
Then this Mat file, you can pass to NDK and process it using OpenCV C++.
Thanks

How to get actual path to video from onActivityResult

I have a video that I save to .../Movies/MyApp/abcde.mp4. So I know where it is. When I load it through my app using an implicit intent to ACTION_GET_CONTENT, the path is returned as content:/media/external/video/media/82 when I do
data.getData().toString()
The problem with that path is that it works when I try to access it with MediaRecorder as
mVideoView.setVideoURI(Uri.parse(mVideoStringPath))
However if I try to convert it to a path in another thread (for a job queue), the file is not found
new File(mVideoStringPath)
when I use the technique (copy and paste) described at How to get file path in onActivityResult in Android 4.4, still get the error
java.lang.RuntimeException: Invalid image file
Also per my logging, the new technique shows the path to the video as
video path: /storage/emulated/0/Movies/MyApp/abc de.mp4
notice the space in abc de.mp4. that indeed is the name of the file. And the phone's camera app has no trouble playing
However if I try to convert it to a path in another thread (for a job queue), the file is not found
That is because it is not a path to a file. It is a Uri, which is an opaque handle to some data.
How to get actual path to video from onActivityResult
You don't. You use the Uri. There is no requirement that the Uri point to a file. There is no requirement that the Uri, if it happens to represent a file, represent one that you have direct filesystem access to.
you need to escape the space the the file path in order to construct a File object from it.
filepath.replace(" ", "\\ ");

Titanium 3.X getFile() from local storage

Using Titanium on Android 4+ I want to access a jpeg file which has been taken with the camera. I need to achieve 2 objectives, namely, return the EXIF data and transfer the bytes to an API endpoint. My problem is I'm unable to access the file...
I'm using a 3rd party module to handle the file selection (Multi Image Picker) which returns a list of file locations, using the File Manager app on the emulator (GenyMotion) I can confirm the location on disk is correct. However, the following always returns false...
var file = Ti.Filesystem.getFile('/mnt/sdcard/DCIM/Camera/IMG_20140901_083735.jpg');
Ti.API.info('Do we have a file? ' (file.exists()? 'YES' : 'NO'));
The output for the above would be... Do we have a file? NO
Further reading shows Titanium has 5 predefined folder locations which can be passed into the getFile() method and one possible reason for the above code not working would be it is defaulting to the 'Resouces' folder location? That said all but one folder location is app specific, the exception being externalStorageLocation. Now my understanding of an Android device is that any image taken with the camera will be stored on the internal storage system unless an SD card is present. This is true in my case as the following lists 0 files...
var extDir = Ti.Filesystem.getExternalStorageDirectory();
var dir = Ti.Filesystem.getFile(extDir);
var dir_files = dir.getDirectoryListing();
Ti.API.info('External files... ' + dir_files.length);
The output for the above would be... External files... 0
So am I right in thinking Appcelerator have simply not included the ability to access local storage (outside of any app specific folders) within their API? Or am I missing something and there is in fact another way?
Thanks to #Bharal I was able to find a solution...
By using the Ti.Media.openPhotoGallery() method I was able to identify the correct native path for the image by inspecting the event object returned from the success callback.
The path was missing 'file://' at the beginning, I couldn't be 100% sure but I suspect this forces the getFile() method to use an absolute path and not a relative path from within the Resources folder.
To confirm, the following will return a file object...
var file = Ti.Filesystem.getFile('file://[path]');
Where [path] is the folder location as reported within the File Manager app on the device, for example '/mnt/sdcard/DCIM/Camera/IMG_20140901_083735.jpg'
Yah mon, i dunno.
Here is wat i used when i was doin pictures on my Ti app, but then i got rid of that section because i realised i didn't need to be doin pictures. Pictures mon, dey ain' what you want sometimes, yo?
Ti.Media.openPhotoGallery({ //dissall jus' open up a piccha selectin' ting. ez.
success:function(event){
var image = event.media;
if (event.mediaType==Ti.Media.MEDIA_TYPE_PHOTO){
//so image.nativePath is the path to the image.
// profileImg be jus' some Ti.UI.createImageView ting yo be puttin in yo' page.
//meyybe yo be wantin' alert(image.nativePath); here too, dat be helpin?
profileImg.image = image.nativePath;
}
},
cancel:function(){
//we cancelled out, why we doin' that?
}
});
Now that isn't going to really be helpin' you, but yo can use that to see wat the native path yo piccha be usin' be, and then be seein' if maybe what yo be puttin' in yo code be sam ting.
Jus' wrap the above as an addEventListener("click", function(){ ... } ); on sam ting in yo page, and jus' add sam element to put th' piccha in if yo be wantin' to see the piccha but i be tellin' you picchas mon, sometimes dey ain' worth time.
But meyybe yo wantin' use not an emulator for dis ting, dey can be actin' weird yo should be usin some small phone maybe? Dat way you can be findin' if yo got dem memory leeks and meyybe some memory sprouts, an memory onions too.

Categories

Resources