Here's the thing that's causing a bit of headscratching, maybe someone can shed some light on the situation. I'm using the Camera intent to snap a picture (well, any number of pictures really), like so:
ImageView imgPhoto = (ImageView)findViewById(R.id.imgButtonPhoto);
imgPhoto.setBackgroundColor(Color.rgb(71,117,255));
imgPhoto.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
++snapNumber;
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
lastPicSaved = String.valueOf(gd.getDeliveryId()) + "_" + String.valueOf(snapNumber) + ".jpg";
Uri imageUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), lastPicSaved));
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, GooseConsts.IMAGE_CAPTURE_INTENT);
}
});
Once the activity has finished I snag the result like so:
case GooseConsts.IMAGE_CAPTURE_INTENT:
try
{
String newCompressedImage = Environment.getExternalStorageDirectory() + "/" + lastPicSaved;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
//options.inDensity = DisplayMetrics.DENSITY_MEDIUM;
Bitmap b = BitmapFactory.decodeFile(newCompressedImage, options);
FileOutputStream fos;
fos = new FileOutputStream(newCompressedImage);
b.compress(CompressFormat.JPEG, 60, fos);
fos.flush();
fos.close();
Image i = new Image();
i.setReported(0);
i.setReportedFull(0);
i.setImage(newCompressedImage);
//i.setImageData(b);
dbHelper.insertImageReference(i, gd.getDeliveryId());
}
Simple stuff really. As you can see I'm using the options.inSampleSize and reducing quality upon compression, to reduce the size of the end image, so as to maintain a small image capture to send back to hq, via an XMPP packet.
Here comes the fun part!
On the filesystem this resulting image is around the 50Kb size, possibly a bit more but never more than 60Kb. Which is fine, that sends via XMPP and I can process and display it in a custom connected client I've also written.
I figured it'd probably be best to keep the images, just in case sending fails for whatever reason, but didn't want them getting lost in the file system, so added a BLOB field to my local device database. I wondered if I could just send them directly from said DB and do away with the file system completely, so I tried it, suddenly NO images were being sent/received by my client bot. Strange! After a bit of digging, I noticed that the images that had been saved into the db BLOB are now (amazingly) 3x the size of the original. Same dimensions (486x684) and same quality (as I've adb pulled a few to test against the ones stored on the SD card).
Can anyone tell me why this is the case? I've been using BLOB fields for years and have never seen such a dramatic increase in file size before. A couple of Kb here and there, sure, but not jumping from 50(ish)Kb to over 160Kb?!
Many thanks.
After you compress the image convert the Image to a byte array instead of using a blob
Bitmap b = BitmapFactory.decodeFile(newCompressedImage, options);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
b.compress(Bitmap.CompressFormat.PNG, 60, stream);
byte [] byteArray = stream.toByteArray();
This should keep the file size to a minimum. You will have to convert the byte array back to a bitmap for display, of course. But that should be straight forward.
I believe byte array's have a size limit though. Initialize it instead as
byte [] byteArray = new byte [1024];
// then
byteArray = stream.toByteArray();
Related
I have an app with a version for iOS and a version for android. The app sends a image (jpeg in this case) from the gallery or camera to a server using a multipart upload.
The images start life identical in size, resolution and file size, and when I run an imagemagick compare they are identical.
When they reach the server, they look identical to the naked eye, have identical numbers of pixels and dimensions, both 320 x 483 with a resolution of 72 pixels. However, they have a different filesize, despite loading each with no compression specified. When I run imagemagick compare they are obviously different.
These are the original images:
Image on iPhone
Image on Android
These are the images when uploaded:
Image uploaded from iPhone
Image uploaded from Android
imagemagick compare image
IOS Code (using AFnetworking)
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
NSData *imageData = UIImageJPEGRepresentation(img, 1.0);
UIGraphicsEndImageContext(); ...
[requestSerializer setValue:#"multipart/form-data" forHTTPHeaderField:#"content-type"];
NSMutableURLRequest *request = [requestSerializer multipartFormRequestWithMethod:#"POST" URLString: ServerPath
parameters:sendDictionary constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData: imageData name:#"file" fileName:#"temp.jpeg" mimeType:#"image/jpeg"];
} error:nil];
Android Code (using loopj for Async request)
RequestParams params = new RequestParams();
params = new RequestParams();
//use bytearray
BitmapFactory.Options BMoptions = new BitmapFactory.Options();
BMoptions.inSampleSize = 1; // Example, there are also ways to calculate an optimal value.
InputStream in;
try {
in = getContentResolver().openInputStream(Uri.fromFile(new File(Globals.getImagePath())));
Bitmap bitmap = BitmapFactory.decodeStream(in, null, BMoptions);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap = scaleToActualAspectRatio2(bitmap);
//
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
bitmapdata = baos.toByteArray();
params.put("file", new ByteArrayInputStream(bitmapdata), "androidMobile.jpg");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I presume that it is the pixel intensities that makes the difference.
Can someone explain why the images arrive different? Do I have to save the Android image to a file first and upload it as a file?
Under Android you are not uploading your jpeg file.
Instead you mess around with converting your jpeg file to a bitmap and compressing that bitmap to some bytes which you will finally upload.
No wonder that you end up with different bytes.
Do away with the bitmap. Just upload the bytes of the jpg file directly.
params.put("file", new ByteArrayInputStream(bitmapdata), "androidMobile.jpg");
Change to
params.put("file", in, "androidMobile.jpg");
Remove the bitmap code directly as that input stream can only be read once.
I'm working on an android app which needs a photo to be taken from camera and compress it and then store it to the given path.I'm a beginner in android ,so I'll be thankful for any kind of help .I'm able to take the picture and store it on sdcard ,but can't find a way to compress it .I want to upload the image to server so want to compress the image as low as 50kb.I tried many solutions on the internet but none could do the thing .
You can refer this link to achieve image compression.
I have implemented this code in my project and able to compress an image by maintaining quality.
FileOutputStream out = null;
String filename = getFilename();
try {
out = new FileOutputStream(filename);
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
You can change number 80 as per your requirements.
This number indicates quality of the compressed image.
quality must be in the range 0-100.
0 meaning compress for small size, 100 meaning compress for max quality.
Thanks
You can use bitmap compression 0 = MAX compression and 100 = Least Compression. I hope this will help you
public Uri getImageUri(Context inContext, Bitmap inImage) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
inImage.compress(Bitmap.CompressFormat.WEBP, 0, bytes);
String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
return Uri.parse(path);
}
I am implementing the below method, but Logcat warn : "AndroidMediaUtils: Image_getJpegSize: No JPEG header detected, defaulting to size=width=6000000"`
private ImageReader.OnImageAvailableListener mOnImageAvailableListener =
new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireLatestImage();
// get image bytes
ByteBuffer imageBuf = image.getPlanes()[0].getBuffer();
final byte[] imageBytes = new byte[imageBuf.remaining()];
imageBuf.get(imageBytes);
image.close();
onPictureTaken(imageBytes);
}
};
How can I resolve it? I'am very appreciate if you can help me. Thank you
I'm not sure what hardware you're trying to run this code on, but my own project involves a Raspberry Pi 3 and Android Things.
I have asked a couple of questions regarding this in the Google IoT community https://plus.google.com/communities/107507328426910012281 but didn't get any response on this particular question.
I have spent several hours trying to combat this, and have come to the conclusion that you can't really do anything about the warning if you're capturing images in JPEG format.
This means that you will be handling a 6MB byte array, likely unnecessarily. The largest resolution you can capture on a Raspberry Pi 3 with a camera v2 module and Android Things is 640x480, which really shouldn't create 6MB large arrays.
What I can do, however, is to post process this and output something that isn't 6MB in size, by creating a Bitmap from the raw buffer, which seem to be able to sort out the final result quite well. This is in no way optimised code, as I just now found a solution to my specific problem, and would like to share it with you:
Image image = reader.acquireLatestImage();
ByteBuffer imageBuf = image.getPlanes()[0].getBuffer();
final byte[] imageBytes = new byte[imageBuf.remaining()];
imageBuf.get(imageBytes);
final Bitmap bmp = BitmapFactory.decodeByteArray(imageBytes,0,imageBytes.length);
int bytes = bmp.getByteCount();
ByteBuffer buffer = ByteBuffer.allocate(bytes); //Create a new buffer
bmp.copyPixelsToBuffer(buffer); //Move the byte data to the buffer
FileOutputStream outStream = null;
File file = new File(getExternalFilesDir(null), "mypic.jpg");
try {
// Write to SD Card
outStream = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
outStream.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
image.close();
}
A bug report has been created that can be followed/read here: https://issuetracker.google.com/issues/62018053
I am creating vCard files from my android app.
I am storing data manually(without using any libray)
I am able to write the data,read and parse it in my app.But when I save Image .I have gor two issues.
1)I am not able to save the image captured using camera..which throws an Out of memoery exception while writing the base 64 encoded string into vcard.
2)I am able to save the base 64 encoded string of some image which I took from gallery,but while reading it doesn't get me image.I am reding all the data from vCard line by line and base64 encoded string is not coming as a single line.(Please note that I stored each value to the file using \r\n)
Please let me know the proper way of doing this.
Code Snippets
Encoding
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream .toByteArray();
encodedProfileImage = Base64.encodeToString(byteArray, Base64.DEFAULT);
Writing
fw = new FileWriter(vcfFile);
...
fw.write("PHOTO;ENCODING=BASE64;TYPE=PNG:"+encodedProfileImage + "\r\n");
Reading and decoding
else if(strline[0].equals("PHOTO;ENCODING=BASE64;TYPE=PNG")){
String imagestr=strline[1];
byte[] byteArray = Base64.decode(imagestr, Base64.DEFAULT);
card.profileImage = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
}
When reading it in you can use BitmapFactory.Options
http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html
Use the decodeByteArray call that takes BitmapFactory.Options as the last argument.
if you set inSampleSize = 2
It will reduce the size of the incoming image to something managable.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2; //Scale it down
options.inPreferredConfig = Bitmap.Config.RBG_565; // Less memory
i am working on an wallpaper app in which i am receiving images from Picasa Web Album in GridView and Full Screen ImageView.
i want to save ImageView loaded image using image bytes size, to prevent duplicate save, but i don't knowhow to get image bytes size from ImageView.`
this is my code:
int intHeight = fullImageView.getHeight();
int intWidth = fullImageView.getWidth();
String dirname2 = "/Amazing Wallpapers HD/";
File myDir2 = new File(Environment.getExternalStorageDirectory()
.getPath() + dirname2);
myDir2.mkdirs();
String fname2 = "image" + intHeight+ intWidth +".jpeg";
File file2 = new File(myDir2, fname2);
if (file2.exists())
file2.delete();
try {
FileOutputStream out = new FileOutputStream(file2);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
Toast.makeText(_context, "failed", Toast.LENGTH_SHORT).show();
}
Note: i am asking about size of image in bytes or kilobytes.
If you are working with Picasa you can simply get photo description which will gives you a lot of unique features in order to use it in your work:
photo.getDescription().getPlainText()
you will find more details here.
You can use BitmapFactory.Options for that. It'll only extract the meta data of the image not the image as a whole.You should be able to get enough data from there to ensure you wont have a duplicate.
For further reading: Android Developers