I am developing an application which loads images from local device storage.
I used compression technique code:
FileOutputStream fo = new FileOutputStream(file);
picture.compress(Bitmap.CompressFormat.JPEG, 90, fo);
fo.flush();
fo.close();
where picture is a bitmap.
This shows a low quality unreadable image.
Is there any efficient compression technique.
Also, how does android gallery show thumbnail images which are readable and of smaller file size?
Solved it using ThumbnailUtils:
Bitmap picture = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(_path_to_your_file), FINAL_WIDTH, FINAL_HEIGHT);
Image size is reduced much
Hope it will help you
Picasso.with(context)
.load(imageUri) // will come from gallery
.resize(imageWidth, imageWidth).centerInside()
.into(imageview);
I have done this
public static final int GRID_PADDING = 8; // in dp
public static final int NUM_OF_COLUMNS = 3;
Resources r = getResources();
float padding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,GRID_PADDING,r.getDisplayMetrics());
imageWidth = (int) ((getScreenWidth()) - ((NUM_OF_COLUMNS + 1) * padding)) / NUM_OF_COLUMNS);
Related
I'm showing a simple ImageView in my activity:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.package.name.EditPicture">
<ImageView
android:id="#+id/problem_picture"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</RelativeLayout>
In my activity class, this is how I'm setting the image:
//first calculate the width and height of the screen
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int height = displaymetrics.heightPixels;
int width = displaymetrics.widthPixels;
//Then, resize the image's width and height equal to that of the screen:
Picasso.with(this).load(new File(pictureLocation)).rotate(90f).resize(height,width).into(imageView);
The problem is, I'm getting the desired result in the emulator, but in my real android phone, nothing is shown. Whole screen is blank.
As I'm already resizing down the image to that of the screen-size, there shouldn't be any issues on loading a high-resolution image. Why is my screen blank then in the real device?
First check image available on device.
File file = new File(pictureLocation);
if (file.exists()) {
Picasso.with(this).load(new File(pictureLocation)).into(imageView);
} else {
Log.d("Result", "Image not available");
}
I would suggest you to use Content Provider in this scenario.
I had the same issue where the image loaded in the view was black. Storage Access Framework is the solution here
https://developer.android.com/guide/topics/providers/document-provider.html
After a little research, here's the reason and a solution:
The screen is going blank in a real device because the ImageView is unable to load a big-ass image (with camera being 13MP, the images were 3-4 MB). I tried a smaller image (~100 KB) and that worked pretty well. It's sad that neither Picasso nor Glide could do the thing.
Therefore, I first resized the images and then compressed them to fall in the 100 KB range (You need a different approach if you want the full HD image):
/**
* getting the screen height and width, so that we could resize the image accordingly
*/
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int height = displaymetrics.heightPixels;
int width = displaymetrics.widthPixels;
/**
* Getting the old photo and then resizing it to the size of the screen.
* We are also compressing it. 70 is a number between 0 to 100.
* You see, close to 0 means very low quality but very small in size image
* Close to 100 means very high quality, but the size will be big.
*/
Bitmap photo = BitmapFactory.decodeFile(pictureLocation);
photo = Bitmap.createScaledBitmap(photo, width, height, false);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
photo.compress(Bitmap.CompressFormat.JPEG, 70, bytes);
/**
* fetching the location where this has to be saved. folder location is the location of my Pictures folder.
*/
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String smallFileLocation = folderLocation + File.separator + "IMG_" + timeStamp + ".jpg";
/**
* New file is saved at this place now.
*/
File f = new File(smallFileLocation);
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
fo.close();
/**
* Later, we can simply put the picture in our ImageView using Picasso or just imageView.setImageBitmap
*/
Picasso.with(this).load(new File(smallFileLocation)).rotate(90f).resize(height,width).into(imageView);
Because your image load have a large size (ex: 4000px x 3000px).
Using Picasso:
int width = new DeviceSize(mContext).widthPixels();
Picasso.get().load(fileUploadPath)
.resize(width, 0)
.onlyScaleDown()
.into(imageView);
--Class DeviceSize--
import android.content.Context;
import android.util.DisplayMetrics;
public class DeviceSize {
private Context mContext;
private int widthPixels;
private int heightPixels;
public DeviceSize(Context context) {
mContext = context;
}
public int widthPixels() {
DisplayMetrics lDisplayMetrics = mContext.getResources().getDisplayMetrics();
widthPixels = lDisplayMetrics.widthPixels;
return widthPixels;
}
public int heightPixels() {
DisplayMetrics lDisplayMetrics = mContext.getResources().getDisplayMetrics();
heightPixels = lDisplayMetrics.heightPixels;
return heightPixels;
}
}
So (or SO), my requirements are:
Display 4 photo placeholders
Click one placeholder to take a photo
Display the 4 thumbnails of the taken pictures in a fragment
Upload 4 photos (one unique HTTP request, max size per photo 250kb, total 1 MB)
I initially thought this was a standard/quite easy task, but I finally changed
my mind. I'm proceeding this way:
When a user click on a placeholder:
file = createImageFile();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
startActivityForResult(takePictureIntent);
private File createImageFile() throws IOException {
// fileName is just a unique string...
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File file = File.createTempFile(fileName, ".jpg", storageDir);
return file;
}
From Google Docs
I set the thumbnail for the placeholder:
private void setThumbnail(File file, ImageView target) {
int targetWidth = target.getWidth();
int targetHeight = target.getHeight();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int thumbWidth = options.outWidth;
int thumbHeight = options.outHeight;
int scaleFactor = Math.min(thumbWidth / targetWidth, thumbHeight
/ targetHeight);
options.inJustDecodeBounds = false;
options.inSampleSize = scaleFactor;
options.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(),
options);
target.setImageBitmap(bitmap);
}
Server side my PHP API requires 4 JPEG files 250kb max each.
I'm using Loopj library (1.4.6) to upload them.
Loopj set request parameters whit method put, and we can use three
way to upload a file:
params.put("picture", file); // Upload a File
params.put("picture", inputStream); // Upload an InputStream
params.put("picture", new ByteArrayInputStream(bytes)); // Upload some bytes
The pictures saved from camera intent are too big for the memory reserved to the app and
I cannot just "load" them in RAM (or I'll get a OOM Exception).
Should I scale them down (or compress don't know) iteratively to 250kb (or some specific pixels size) and
then overwrite the saved files? Can you please suggest something to point me in the right direction?
Important: if possile I'd like to avoid rewriting a camera app, I'm just going with the intent, I've not enough time.
Did you try to add in your manifest.xml that code:
<application
android:largeHeap="true">
That allows you to manage properly huge photo by allocating more memory to your app.
I've got a serious performance issue in my app, when loading up bitmaps it seems to take up way to much memory.
I have a drawable folder which contains the bitmap sizes for all android devices, these bitmaps are of high quality. Basically it goes though each bitmap and makes a new one for the device depending on the size. (Decided to do it this way because it supports the correct orientation and any device). It works but it's taking up way to much memory and takes along time to load. Can anyone make any suggestions on the following code.
public Bitmap getBitmapSized(String name, int percentage, int screen_dimention, int frames, int rows, Object params)
{
if(name != "null")
{
_tempInt = _context.getResources().getIdentifier(name, "drawable", _context.getPackageName());
_tempBitmap = (BitmapFactory.decodeResource(_context.getResources(), _tempInt, _BM_options_temp));
}
else
{
_tempBitmap = (Bitmap) params;
}
_bmWidth = _tempBitmap.getWidth() / frames;
_bmHeight = _tempBitmap.getHeight() / rows;
_newWidth = (screen_dimention / 100.0f) * percentage;
_newHeight = (_newWidth / _bmWidth) * _bmHeight;
//Round up to closet factor of total frames (Stops juddering within animation)
_newWidth = _newWidth * frames;
//Output the created item
/*
Log.w(name, "Item");
Log.w(Integer.toString((int)_newWidth), "new width");
Log.w(Integer.toString((int)_newHeight), "new height");
*/
//Create new item and recycle bitmap
Bitmap newBitmap = Bitmap.createScaledBitmap(_tempBitmap, (int)_newWidth, (int)_newHeight, false);
_tempBitmap.recycle();
return newBitmap;
}
There's an excellent guide over on the Android Training site:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
It's about efficient loading of bitmap images - highly recommended!
This will save space. If not using Alpha colors it would be better not to use one with the A channel.
Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
// or Bitmap.Config.RGB_565 ;
// or Bitmap.Config.ARGB_4444 ;
Bitmap newBitmap = Bitmap.createScaledBitmap(_tempBitmap, (int)_newWidth, (int)_newHeight, options);
If I have the minimum target SDK for my app set to 4 then all calls to Drawable.createFromStream resize images.
e.g. if the source image is 480px wide and the Android device my app is running on has a density of 1.5 then the following code returns a BitmapDrawable with a width of 320
URL url = new URL("http://www.examples.com/something.png");
Drawable d = Drawable.createFromStream(url.openStream(), "something.png");
Is there a method to force it to be unchanged or specify the scale (ldpi/mdpi/hdpi etC) to return an image from an InputStream?
Edit: Solution from below.
Bitmap b = BitmapFactory.decodeStream(inputStream);
b.setDensity(Bitmap.DENSITY_NONE);
Drawable d = new BitmapDrawable(b);
You can load image as Bitmap. This way its size will be unchanged.
BitmapFactory.decodeStream(...)
Or you can try BitmapDrawable(InputStream is) constructor. It is deprecated but I guess it should do the job.
Loading images from local resource in ImageView Adapter
I was stuck with OOM error in ion by Koushik Dutta, for as low as 2 images, loading from the resource folder. Even he mentioned it may happen since they are not streams of data. and suggested to load them as InputStream.
I chucked ion Library since i wanted just 4 images from the local.
here is the solution with simple ImageView.
inside the RecyclerViewAdapter/ or any where
InputStream inputStream = mContext.getResources().openRawResource(R.drawable.your_id);
Bitmap b = BitmapFactory.decodeStream(inputStream);
b.setDensity(Bitmap.DENSITY_NONE);
Drawable d = new BitmapDrawable(b);
holder.mImageView.setImageDrawable(d);
Although Drawable d = new BitmapDrawable(b); is deprecated but, does the job quite well.
so instead we can directly get drawable from InputStream just by this line..
Drawable d = Drawable.createFromStream(inputStream,"src");
Hope it helps.
After a long time, I came across the following solution. It works. It retrieves creates bitmap from file at locallink.
private Drawable getDrawableForStore(String localLink) {
Bitmap thumbnail = null;
try {
File filePath = this.getFileStreamPath(localLink);
FileInputStream fi = new FileInputStream(filePath);
thumbnail = BitmapFactory.decodeStream(fi);
} catch (Exception ex) {
Log.e("getThumbnail() on internal storage", ex.getMessage());
return null;
}
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
float scaledDensity = metrics.density;
int width = thumbnail.getWidth();
int height = thumbnail.getHeight();
if(scaledDensity<1){
width = (int) (width *scaledDensity);
height = (int) (height *scaledDensity);
}else{
width = (int) (width +width *(scaledDensity-1));
height = (int) (height +height *(scaledDensity-1));
}
thumbnail = Bitmap.createScaledBitmap(thumbnail, width, height, true);
Drawable d = new BitmapDrawable(getResources(),thumbnail);
return d;
}
I'm having an odd problem with my map pin sizes. To preserve dynamic-ness, the map pins for different categories are stored on a site's server so that they can be changed at any point even after the app is published.
I'm caching the pins every time I download them and I only ever re-download them if the server sends back a bit saying that one has changed since last I downloaded it. The first time I grab the pins, I use the bitmaps before I save them to files and the map markers are the correct size. Every time after that I'm loading a saved version of the pins straight from the image file. These are displaying considerably smaller than they are when using the bitmaps from the first download.
At first, I thought it was a problem with the way I'm saving the PNGs, but their sizes are correct (64 x 64). Is this a dip/px issue or do I need to decompress the image files with some sort of option?
Here's how I grab the images the first time:
public static Bitmap loadMapPin(String category, int width, int height) {
URL imageUrl;
category = category.toLowerCase().replace(" ", "");
try {
imageUrl = new URL(PIN_URL+category+".png");
InputStream is = (InputStream) imageUrl.getContent();
Options options = new Options();
options.inJustDecodeBounds = true; //Only find the dimensions
//Decode without downloading to find dimensions
BitmapFactory.decodeStream(is, null, options);
boolean scaleByHeight = Math.abs(options.outHeight - height) >= Math.abs(options.outWidth - width);
if(options.outHeight * options.outWidth >= width * height){
// Load, scaling to smallest power of 2 that'll get it <= desired dimensions
double sampleSize = scaleByHeight
? options.outHeight / height
: options.outWidth / width;
options.inSampleSize =
(int)Math.pow(2d, Math.floor(
Math.log(sampleSize)/Math.log(2d)));
}
options.inJustDecodeBounds = false; //Download image this time
is.close();
is = (InputStream) imageUrl.getContent();
Bitmap img = BitmapFactory.decodeStream(is, null, options);
return img;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
And here's how I'm loading them from the cached file:
BitmapFactory.decodeFile(filepath);
Thanks in advance!
I've found that, by default, decompressing an image to a bitmap doesn't scale with high density screens. You have to set the density to none. In other words, you specify that the image is meant for an unknown density.
Solution:
Bitmap b = BitmapFactory.decodeFile(filepath);
b.setDensity(Bitmap.DENSITY_NONE);