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.
Related
I have a picture file captured by the camera.
The path is
/storage/emulated/0/Android/data/com.xxxxxxxxxxxx.yyyyyyyyyyyyy/files/Pictures/yyyyyyyyyyyyyyy_overviewPicture_17417005982933606358.jpg
With File file = new File(pathName) I get a valid file and a size about 25 MB :-)
Code
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName,bmOptions);
int inSampleSize = Math.min(bmOptions.outWidth/App.PICTURE_WIDTH_MAX,bmOptions.outHeight/App.PICTURE_HEIGHT_MAX);
I try to get the x and y values to scale it down.
But only -1 for both values is returned. Any ideas?
App-Level is 23-28 with destination 28.
The size of the picture files were 0, so they could not be encoded. I never expected that. I recommend to check with
if(new File(pathName).length() == 0) return false
or similar before trying to decode. Thanx for reading / comment!
In my app, i send some image to server.
images max size is 300kb, i'll resize file.
this is my resize code.
public static void resize_filesize(Context context, Uri uri, int maxSize) {
int filesize;
int compressRate;
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
filesize = inputStream.available();
inputStream.close();
compressRate = ((100*maxSize) / filesize);
File storageDir = new File(Environment.getExternalStorageDirectory() + context.getString(R.string.photoPath));
File image = new File(storageDir,"resize.jpg");
FileOutputStream outputStream = new FileOutputStream(image);
Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, compressRate, outputStream);
outputStream.close();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(context,
BuildConfig.APPLICATION_ID + ".provider", image);
} else {
uri = Uri.fromFile(image);
}
} catch (Exception e) {
e.printStackTrace();
}
}
i think this.
if original image size is 500kb, i need 300kb.
than i set quality:60, image will 60% compress and it become 300kb.
but i try it, so much compress work.
this is my log.
original : 831210(811kb)
maxsize : 307200(300kb)
rate : 35
final size : 95053(92kb)
what happen???
ofcource i want resize, but it's too small!
what did i do wrong?
'quality' isnt mean % ? than how can i get 'near' maxsize image?
in android document, just say it mean compress size...
i think this. if original image size is 500kb, i need 300kb. than i set quality:60, image will 60% compress and it become 300kb.
No.
in android document, just say it mean compress size...
No. The documentation has this for the quality parameter to compress():
Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting.
If you compress() to a PNG, the quality value is ignored. If you compress() to a JPEG, then it is used to control JPEG compression, which commonly uses a 0-100 range to express the desired quality. However, that does not mean that a value of 0 gives you a photo that takes no space.
than how can i get 'near' maxsize image?
There is no way of knowing the size of a JPEG compressed image without compressing it. Ideally, you do not worry about getting it "near" some particular compression ratio. Instead, either let the user choose the quality value, or just pick some quality value and move on.
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);
Days, I've spent working on this. Weeks, perhaps. Literally. :(
So I've got an image on an SD card that more than likely came out of the built-in camera. I want to take that image and downsample it to an arbitrary size (but always smaller and never larger). My code uses standard Android Bitmap methods to decode, resize, recompress, and save the image. Everything works fine as long as the final image is smaller than 3MP or so. If the image is larger, or if I try to do several of these at once, the application crashes with an OutOfMemoryError. I know why that's happening, and I know it's happening for a perfectly legitimate reason, I just want it to not happen anymore.
Look, I'm not trying to launch a rocket here. All I want to do is resize a camera image and dump it to an OutputStream or even a temporary file. Surely someone out there must have done such a thing. I don't need you to write my code for me, and I don't need my hand held. But between my various programming abortions and days of obsessed Googling, I don't even know which direction to head in. Roughly speaking, does anyone know how to decode a JPEG, downsample it, re-compress it in JPEG, and send it out on an OutputStream without allocating a massive amount of memory?
Ok I know it's a little bit late but, I had this problem and I found solution. It is actually easy and I am sure it supports back to api 10(I have no idea about before 10). I tried this with my phone. It is a samsung galaxy s2 with an 8mp camera and the code perfectly resized camera images to the 168x168 as well as images i found on web. I checked the images by using file manager too. I never tried resizing images to bigger resoulation.
private Bitmap resize(Bitmap bp, int witdh, int height){
return Bitmap.createScaledBitmap(bp, width, height, false);
}
you can save it like this
private void saveBitmap(Bitmap bp) throws FileNotFoundException{
String state = Environment.getExternalStorageState();
File folder;
//if there is memory card available code choose that
if (Environment.MEDIA_MOUNTED.equals(state)) {
folder=Environment.getExternalStorageDirectory();
}else{
folder=Environment.getDataDirectory();
}
folder=new File(folder, "/aaaa");
if(!folder.exists()){
folder.mkdir();
}
File file=new File(folder, (int)(Math.random()*10000)+".jpg");
FileOutputStream os=new FileOutputStream(file);
bp.compress(Bitmap.CompressFormat.JPEG, 90, os);
}
thanks to this link
The following code is from my previous project. Key point is "options.inSampleSize".
public static Bitmap makeBitmap(String fn, int minSideLength, int maxNumOfPixels) {
BitmapFactory.Options options;
try {
options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fn, options);
if (options.mCancel || options.outWidth == -1
|| options.outHeight == -1) {
return null;
}
options.inSampleSize = computeSampleSize(
options, minSideLength, maxNumOfPixels);
options.inJustDecodeBounds = false;
//Log.e(LOG_TAG, "sample size=" + options.inSampleSize);
options.inDither = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
return BitmapFactory.decodeFile(fn, options);
} catch (OutOfMemoryError ex) {
Log.e(LOG_TAG, "Got oom exception ", ex);
return null;
}
}
private static int computeInitialSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
(int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
(int) Math.min(Math.floor(w / minSideLength),
Math.floor(h / minSideLength));
if (upperBound < lowerBound) {
// return the larger one when there is no overlapping zone.
return lowerBound;
}
if ((maxNumOfPixels == UNCONSTRAINED) &&
(minSideLength == UNCONSTRAINED)) {
return 1;
} else if (minSideLength == UNCONSTRAINED) {
return lowerBound;
} else {
return upperBound;
}
}
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);