Android BitmapFactory decodeResource Out of Memory Exception - android

I'm new to android and developing an app that saves large images from drawable folder to phone storage. These files have resolution of 2560x2560 and I want to save these files without loosing image quality.
I use following method to save images and it gives me Out of Memory Exception. I have seen many answers how to load a large bitmap efficiently. But I cant really find an answer for this problem.
In my code, I use
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), imageId);
File file = new File(root.getAbsolutePath() + "/Pictures/" + getResources().getString(R.string.app_name) + "/" + timeStamp + ".jpg");
file.createNewFile();
FileOutputStream oStream = new FileOutputStream(file);
bitmap.compress(CompressFormat.JPEG, 100, oStream);
oStream.close();
bitmap.recycle();
Is there anything wrong with my code? This works without any exception for smaller images.
If I use android:largeHeap="true", this does not throw any exception. But I know it is not a good practice to use android:largeHeap="true".
Is there any efficient way to save large images from drawable folder without an exception?
Thank you in advance.

If you just want to copy the image file, you shouldn't decode it into a bitmap in the first place.
You can copy a raw resource file with this for example:
InputStream in = getResources().openRawResource(imageId);
String path = root.getAbsolutePath() + "/Pictures/" + getResources().getString(R.string.app_name) + "/" + timeStamp + ".jpg";
FileOutputStream out = new FileOutputStream(path);
try {
byte[] b = new byte[4096];
int len = 0;
while ((len = in.read(b)) > 0) {
out.write(b, 0, len);
}
}
finally {
in.close();
out.close();
}
Note that you have to store your image in the res/raw/ directory instead of res/drawable/.

Related

Load images with OpenCV from assets folder in Android

I'm stuck trying to load an image placed in assets folder with OpenCV 3.0 in Android. I've read a lot of answers here, but I can't figure out what I'm doing wrong.
"my image.jpg" is place directly in the assets folder created by Android Studio.
This is the code I'm using. I've checked and the library has been loaded correctly.
Mat imgOr = Imgcodecs.imread("file:///android_asset/myimage.jpg");
int height = imgOr.height();
int width = imgOr.width();
String h = Integer.toString(height);
String w = Integer.toString(width);
if (imgOr.dataAddr() == 0) {
// If dataAddr() is different from zero, the image has been loaded
// correctly
Log.d(TAG, "WRONG UPLOAD");
}
Log.d(h, "height");
Log.d(w, "width");
When I try to run my app, this is what I get:
08-21 18:13:32.084 23501-23501/com.example.android D/MyActivity: WRONG UPLOAD
08-21 18:13:32.085 23501-23501/com.example.android D/0: height
08-21 18:13:32.085 23501-23501/com.example.android D/0: width
It seems like the image has no dimensions. I guess because it has not been loaded correctly. I'va also tried to load it placing it in the drawable folder, but it doesn't work anyway and I'd prefer to use the assets one.
Anyone can please help me and tell me how to find the right path of the image?
Thanks
Problem: imread needs absolute path and your assets are inside a apk, and the underlying c++ classes cannot read from there.
Option 1: load image into Mat without using imread from drawable folder.
InputStream stream = null;
Uri uri = Uri.parse("android.resource://com.example.aaaaa.circulos/drawable/bbb_2");
try {
stream = getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bmp = BitmapFactory.decodeStream(stream, null, bmpFactoryOptions);
Mat ImageMat = new Mat();
Utils.bitmapToMat(bmp, ImageMat);
Option 2: copy image to cache and load from absolute path.
File file = new File(context.getCacheDir() + "/" + filename);
if (!file.exists())
try {
InputStream is = context.getAssets().open(filename);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
FileOutputStream fos = new FileOutputStream(file);
fos.write(buffer);
fos.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
if (file.exists()) {
image = cvLoadImage(file.getAbsolutePath(), type);
}

Android picture has red tint after decoding and pulling the .png file from the emulator to pc

I am transferring some data from a server ( java app ) to client ( android app ).
The data gets Base64 encoded, sent, received correct, decoded ( correct ? ) and stored to the device ( correct ? )
I am using android studio and an AVD to simulate it. I take the pictures via DDMS from the virtual device folder to my computers harddisk in order to take a look at them. Is maybe there the problem?
now in the following code sections the picture files get decoded and stored to the device.
Cant figure out where the mistake is.
Would be glad about any hint.
byte[] imageBackToByt = Base64.decode(parts[9], Base64.DEFAULT);
Bitmap bitmapImage = BitmapFactory.decodeByteArray(imageBackToByt, 0, imageBackToByt.length);
File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
+ "/Android/data/"
+ ctx.getApplicationContext().getPackageName()
+ "/Files");
File imageFile = new File(mediaStorageDir.getPath() + File.separator + voReceived.name + ".png");
try {
FileOutputStream fos = new FileOutputStream(imageFile);
bitmapImage.compress(Bitmap.CompressFormat.PNG, 90, fos);
fos.close();
} catch (FileNotFoundException e) {
Log.d(ctx.getString(R.string.SLDMP), "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(ctx.getString(R.string.SLDMP), "Error accessing file: " + e.getMessage());
}
This is how i encode it on the server in JAVA:
BufferedImage originalPicture = null;
ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();
byte[] pictureInByte = null;
String pictureEncoded = null;
try {
// load the original picture from the filepath
originalPicture = ImageIO.read(picturFile);
// Convert the original picture from .png format to byte array (byte []) format
ImageIO.write(originalPicture, "jpg", byteArrayOS );
pictureInByte = byteArrayOS.toByteArray();
// Encode the byte array pictureInByte to String based on Base64 encoding
pictureEncoded = Base64.getEncoder().encodeToString(pictureInByte);
} catch (IOException e) {
e.printStackTrace();
// If picture failed to load / encode store string "PICTUREERROR" as an error code
pictureEncoded = "PICTUREERROR";
}
The server puts the bytes of the image file in a buffer and sends the contents of the base 64 encoded buffer to the client. Now on client side you should directly decode base 64 all bytes and write all the resulting bytes to file. In this way you have exactly the same file. All bytes are the same and file size would be equal too.
Instead you use BitmapFactory to construct a Bitmap and then compress it to PNG. That all makes no sense.
If you want to transfer a file then do not use BitmapFactory and Bitmap.
Having said that.. Mmmmm nice filter! The result is wonderfull!

saving bitmap from arraylist to SD card - saved file unreadable

My situation is as follows: I'm saving multiple bitmaps from an arraylist to a specific folder in my devices SD Card (with success), however, the saved file- when clicked- prompts a message from the phone, stating: "Unable to find application to perform this action." The file size of this file is proportional to that of the bitmap image being saved, so I'm a bit confused, as the device has no problems opening image files, yet cannot open (or identifiy) these as a media file.
Question: What would cause the saved image file (presuming that I have saved it correctly) to exhibit this type of behavior in a device, and how should I resolve this issue?
Extra: the thumbnail of the file is the system provided thumbnail of the two papers on top of each other. The arraylist is being passed from one activity to its current one where the method provided is supplied.
Here is the method invoking the saving of the files to the specified folder /filesdestination:
private void saveImages(){
// to retrieve bitmaps
ArrayList<Bitmap> images = getIntent().getParcelableArrayListExtra("images key");
//to retrieve bitmaps and save in specific order, while also naming them in that order
int loopVal = 0;
int postVal = 9;
while ( loopVal < 9) {
Bitmap Image = images.get(loopVal);
try {
String filedestination = new String(Environment.getExternalStorageDirectory() + "/filedestination");
String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
File file = new File(filedestination, postVal + ".post_order" + ".jpg" + timeStamp);
File picfile = file;
FileOutputStream fos = new FileOutputStream(picfile);
Image.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.close();
} catch (Throwable e) {
e.printStackTrace();
}
postVal--;
loopVal++;
}
}
Any insight would be appreciated,
-Lucas
i think it cannot read the file type because the timestamp is after the file extension jpg and you are also compressing it as a png, so you might want to change either or, something like this
File file = new File(filedestination, postVal + timeStamp +".post_order" + ".png");
It seems that you are saving a .jpg file compressed as a PNG. That can make the image reader app to misbehave.
Either change Image.compress(Bitmap.CompressFormat.PNG, 100, fos);
to
Image.compress(Bitmap.CompressFormat.JPEG, 100, fos);
or change
File file = new File(filedestination, postVal + ".post_order" + ".jpg" + timeStamp);
to
File file = new File(filedestination, postVal + ".post_order" + ".png" + timeStamp);

Android SQLite read blob

I have an sqlite database which is written to from a service running on windows(C++). I am now trying to read from this same sqlite database which contains some blob data. I have some code as follows:
String tileQuery = "SELECT * FROM '" + layerName + "' WHERE zoom_level=?";
Cursor tileCursor = database.rawQuery(tileQuery, new String[] {zoom_level});
if( tileCursor.moveToFirst() )
{
while( !tileCursor.isAfterLast() )
{
int tileRow = tileCursor.getInt(tileCursor.getColumnIndex("tile_row"));
int tileColumn = tileCursor.getInt(tileCursor.getColumnIndex("tile_column"));
byte[] tileData = tileCursor.getBlob(tileCursor.getColumnIndex("tile_data"));
//Write tile to file
String fileName = layerName + "_" + zoom_level + "_" + tileRow + "_" + tileColumn + ".jpeg";
try {
/*
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + TILE_STORAGE_PATH + "/" + fileName));
bos.write(tileData);
bos.flush();
bos.close();
*/
ByteBuffer bb = ByteBuffer.wrap(tileData);
bb.order(ByteOrder.LITTLE_ENDIAN);
FileOutputStream fos = new FileOutputStream(Environment.getExternalStorageDirectory().getPath() + TILE_STORAGE_PATH + "/" + fileName);
byte[] toWrite = new byte[bb.remaining()];
bb.get(toWrite, 0 , toWrite.length);
fos.write(toWrite);
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
tileCursor.moveToNext();
}
}
As shown, I am attempting to write the blobs to disk as jpeg images. No matter what I do, the images appear to be corrupt, as in I cannot view them on any image viewer within android. The same images can be written to file on windows and viewed correctly, which made me think that it was an endianess issue(due to the fact that the blob was written to the database via a service running on windows). I have tried changing the byte order and writing to disk again, but I get the same result. Could anyone suggest what I might be doing wrong/missing. Any help is greatly appreciated.
To make this work there are a few different steps. Assuming your database connection is working and those are the correct columns you are looking in with your Cursor
(1) Convert the blob to a Bitmap. You can use the blob you get back, assuming you actually downloaded and stored it to your local database, as the byte[] you will decode.
Bitmap bm = BitmapFactory.decodeByteArray(tileData, 0 ,tileData.length);
(2) Create a new file in the approprite directory and write to that file. You can do that with something like the code below. The idea is to get the local directory
private void storeBitmap(Bitmap myBitmap){
String root = Environment.getExternalStorageDirectory().toString();
File myDir = new File(root + "/your_directory_name");
String fname = "your_file_name.jpg";
File file = new File (myDir, fname);
if (file.exists ()) file.delete ();
try {
FileOutputStream out = new FileOutputStream(file);
myBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
If you want to add the images to gallery or you just want a different (and potentially easier) way to add the file, look into using MediaScanner which will add the files as though you took the picture with your camers

Android: how to delete internal image file

What i want to do: delete an image file from the private internal storage in my app. I save images in internal storage so they are deleted on app uninstall.
I have successfully created and saved:
String imageName = System.currentTimeMillis() + ".jpeg";
FileOutputStream fos = openFileOutput(imageName, Context.MODE_PRIVATE);
bitmap.compress(Bitmap.CompressFormat.JPEG, 35, fos);
an image that i receive through
bitmap = BitmapFactory.decodeStream(inputStream);
I am able to retrieve the image later for display:
FileInputStream fis = openFileInput(imageName);
ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
DataOutputStream outWriter = new DataOutputStream(bufStream);
int ch;
while((ch = fis.read()) != -1)
outWriter.write(ch);
outWriter.close();
byte[] data = bufStream.toByteArray();
bufStream.close();
fis.close();
imageBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
I now want to delete this file permanently. I have tried creating a new file and deleting it, but the file is not found:
File file = new File(imageName);
file.delete();
I have read on the android developer website that i must open private internal files using the openFileInput(...) method which returns an InputStream allowing me to read the contents, which i don't really care about - i just want to delete it.
can anyone point me in the right direction for deleting a file which is stored in internal storage?
Erg, I found the answer myself. Simple answer too :(
All you have to do is call the deleteFile(imageName) method.
if(activity.deleteFile(imageName))
Log.i(TAG, "Image deleted.");
Done!

Categories

Resources