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.
Related
Can I know what is the best approach to form complex tables like shown below. Is it possible to use Webview with CSS as a source instead of html? Any other suggestions for this? I want to form the table and bind data to it and send the formed table using base64 image string in the payload. I use xamarin android for my app.
[Image Table with slant border]:
[1]: https://i.stack.imgur.com/WN871.jpg
You can load the image file into a Bitmap (which works with all image-formatss supported by android) and encode the Bitmap byte-Array with base64.
int IMG_QUALITY_CONVERSIOM_FOR_NON_LOSSLESS_FORMATS = 60; // choose between 0 and 100
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(/*photoPath*/imageFile.getPath(), options);
// you can also scale the image here if needed
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] imageBytes = null;
try{
bitmap.compress(Bitmap.CompressFormat.PNG, IMG_QUALITY_CONVERSIOM_FOR_NON_LOSSLESS_FORMATS, stream);
imageBytes = stream.toByteArray();
}finally{
bitmap.recycle();
stream.close();
}
if(imageBytes!=null){
String bytesB64 = Base64.encodeToString(imageBytes, Base64.NO_WRAP);
}
else{...}
One additional hint: you can read ANY image file that is supported by android this way, however if you pass the base64 or bitmap onto a website use "image/png" as mimetype!
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 select image from gallery, convert into Base64 and sends to server. For JPEG image, it works fine; the image I upload from gallery on server same gets shown in server folder. However, when I upload PNG format image from mobile gallery, it doesn't show same on server; instead it creates black edges around it. I really don't know what's going wrong?
Also, my actual image is as equal as given JPEG image.
Reference images:
JPEG:
PNG:
I just want to get rid of BLACK borders which should not appear for PNG format images.
Below is my code snippet
FileInputStream mFileInputStream = null;
try {
mFileInputStream = new FileInputStream(imagePathFromSDCard);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int bytesRead = 0;
while ((bytesRead = mFileInputStream.read(b)) != -1) {
bos.write(b, 0, bytesRead);
}
Bitmap bitmap = safeImageProcessing.decodeFile(uri);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
byte[] ba = bos.toByteArray();
String encodedImage = Base64.encodeToString(ba, Base64.NO_WRAP);
//this line sends image base64 to server & there i decode into original
new ImageAsync().sendImageProcess(getActivity(), encodedImage, this);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
You want to upload image files. But you use BitmapFactory to make a Bitmap out of them first. Then you compress the bitmap to a jpg or png byte array. After that you base64 encode the bytes to a string which you upload.
What a waiste. And you have changed the files. Do away with the intermediate bitmap.
Instead load the files directly in a byte array. Then continue with the byte array as usual.
Having said that i think its a bad idea to base64 encode the bytes of a file first as it increases the amount of bytes that have to be transferred with 30%.
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
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();