Some Androids Uploading Scrambled Images - android

First I use this code to save the photo to the Android's SD Card:
PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream outStream = null;
currentFilename = String.format("%d", System.currentTimeMillis());
outStream = new FileOutputStream("/sdcard/" + currentFilename + ".jpg");
outStream.write(data);
outStream.close();
}
};
Then I am using this code to upload photos on Android devices:
public void uploadPhoto() {
try {
// Create HttpPost
HttpPost post = new HttpPost("http://www.example.com/upload.php");
HttpClient client = new DefaultHttpClient();
client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
MultipartEntity entity = new MultipartEntity( HttpMultipartMode.BROWSER_COMPATIBLE );
// Add the Picture as "userfile"
entity.addPart( "userfile", new FileBody(
new File( "/sdcard/", currentFilename + ".jpg" ),
"image/jpeg")
);
// Send the data to the server
post.setEntity( entity );
client.execute( post );
} catch (Exception e) {
// catch
} finally {
// finally
}
}
This works on my LG Optimus V running Android 2.2.2 and Droid X running Android 2.3.4
However - a user who has an HTC Evo running 2.3.4 (Same as my Droid X) is experiencing that all of her uploaded photos are scrambled when they are saved to her phone (and when they get to the server). The image looks like an array of jagged colored lines.
Any ideas about what might be going on here and what could be done to remedy the problem?
Update:
I was able to access this phone and when I pull the jpg files off of the sdcard, it says that the files are 3.5 - 4.0 MB in size, but they cannot be opened and may be corrupted... but the files on the other phones work normally.

It looks like problem with picture sizes. Decoding data from array to picture when width and heigh are wrong results with scrambled image.
Pictures might be taken with supported screen sizes:
Parameters params = camera.getParameters();
List<Camera.Size> sizes = params.getSupportedPictureSizes();
try to set one of them, for example the biggest, as picture size:
List<Size> sizes = params.getSupportedPictureSizes();
Size theBiggest=null;
int maxWidth = 0;
for (Size s : sizes) {
if (s.width > maxWidth) {
maxWidth = s.width;
theBiggest = s;
}
}
params.setPictureSize(theBiggest.width, theBiggest.height);
camera.setParameters(params);

Images need to be cached to sdcard in order to achieve full (non-compressed) size. Steps are easy
capture image
save captured image to sdcard
load saved image from sdcard
convert it to byte array and add as a ByteArrayBody, or as you o you might skip step 3 and create a file body with cached file.
Note: Currently i am away from my pc. I will post an detailed answer if there is no detailed post. Good luck.

Compress the bitmap in JPEG format into the output stream.. Assuming data is a Bitmap type
currentFilename = String.format("%d", System.currentTimeMillis());
outStream = new FileOutputStream("/sdcard/" + currentFilename + ".jpg");
data.compress(Bitmap.CompressFormat.JPEG, 90, outStream );
outStream.close();
You can change the compression ratio parameter to the compress api.. for example, pass 80 to get 80% compression.
This post may have some relation to your problem.

Related

Android EXIF data always 0, how to change it?

I have an app that captures photos using the native Camera and then uploads them to a server. My problem is that all the photos have an EXIF orientation value of 0, and this messes up the display elsewhere.
How can I change the EXIF orientation? I'm not looking for a way to correct it for every circumstance, just change it to a different value.
I'm using a Samsung Galaxy Note 4
I tried this solution that sets the camera orientation before taking photos: Setting Android Photo EXIF Orientation
Camera c = Camera.open();
c.setDisplayOrientation(90);
Camera.Parameters params = mCamera.getParameters();
params.setRotation(0); // tried 0, 90, 180
c.setParameters(params);
but it doesn't influence the resulting EXIF data, its still always 0
I also tried these solutions where the image is rotated after it is taken: EXIF orientation tag value always 0 for image taken with portrait camera app android
and while this rotates the photo, the EXIF orientation is still always 0.
I also tried setting the EXIF data directly: How to save Exif data after bitmap compression in Android
private Camera.PictureCallback mPicture = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
final File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE, "");
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
ExifInterface exif = new ExifInterface(pictureFile.toString());
exif.setAttribute(ExifInterface.TAG_ORIENTATION, "3");
exif.saveAttributes();
fos.write(data);
fos.close();
//upload photo..
}
}
}
but EXIF Orientation is still 0 after uploading.
I have also looked at these solutions:
Exif data TAG_ORIENTATION always 0
How to write exif data to image in Android?
How to get the Correct orientation of the image selected from the Default Image gallery
how to set camera Image orientation?
but they all involve correcting the orientation by rotating, which doesn't influence the EXIF data, or setting the EXIF data directly which doesn't seem to work.
How can I change the file's EXIF orientation data from 0 to 3?
UPDATE:
here is my upload code:
Bitmap sBitmap = null;
final File sResizedFile = getOutputMediaFile(MEDIA_TYPE_IMAGE, "_2");
try {
sBitmap = BitmapFactory.decodeStream(new FileInputStream(pictureFile), null, options);
} catch (FileNotFoundException e) {
Log.e("App", "[MainActivity] unable to convert pictureFile to bitmap");
e.printStackTrace();
return;
}
// ... compute sw and sh int values
Bitmap sOut = Bitmap.createScaledBitmap(sBitmap, sw, sh, false);
Bitmap rotatedBitmap = rotateBitmap(sOut, 3);
FileOutputStream sfOut;
try {
sfOut = new FileOutputStream(sResizedFile);
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, sfOut);
sfOut.flush();
sfOut.close();
sBitmap.recycle();
sOut.recycle();
rotatedBitmap.recycle();
} catch (Exception e) {
Log.e("App", "[MainActivity] unable to save thumbnail");
e.printStackTrace();
return;
}
// upload small thumbnail
TransferObserver sObserver = transferUtility.upload(
"stills/small", /* The bucket to upload to */
filename + ".jpg", /* The key for the uploaded object */
sResizedFile /* The file where the data to upload exists */
);
As you can see, the The EXIF information is not reliable on Android (especially Samsung devices).
However the phone SQL database holding the references to Media object is reliable. I would propose going this way.
Getting the orientation from the Uri:
private static int getOrientation(Context context, Uri photoUri) {
Cursor cursor = context.getContentResolver().query(photoUri,
new String[]{MediaStore.Images.ImageColumns.ORIENTATION}, null, null, null);
if (cursor.getCount() != 1) {
cursor.close();
return -1;
}
cursor.moveToFirst();
int orientation = cursor.getInt(0);
cursor.close();
cursor = null;
return orientation;
}
Then initialize rotated Bitmap:
public static Bitmap rotateBitmap(Context context, Uri photoUri, Bitmap bitmap) {
int orientation = getOrientation(context, photoUri);
if (orientation <= 0) {
return bitmap;
}
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
return bitmap;
}
If you want to change the orientation of the image, try the following snippet:
public static boolean setOrientation(Context context, Uri fileUri, int orientation) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.ORIENTATION, orientation);
int rowsUpdated = context.getContentResolver().update(fileUri, values, null, null);
return rowsUpdated > 0;
}
If you set the orientation of the image, later it will be constantly set at the correct orientation. There is need to make use of ExifInterface later, because the image is already rotated in proper way.
If this method is not satisfactory, then you could try this method
You have accepted your own answer as solution. My rant is just useful side-info, anyways...
The "However..." in your Answer suggests while you now know the cause, you don't have a fix.
Turns out my code was able to set the EXIF data, but there is a
discrepancy between how Android interprets this data and how iOS...
interprets it.
This could be an endianness issue. You can try manually changing the endianness setting of Exif by opening your jpeg in a hex editor and finding...
The bytes 45 78 69 66 (makes "Exif" text) followed by two zero bytes 00 00.
Then it should be 49 49 (makes "II" text) which means read data as little endian format.
If you replace it with 4D 4D (or "MM" text) then the reading side
will consider data as big endian.
Test this in iOS to see if numbers are now correct.
and regarding this...
However, setting 3 shows up as0 on iOS and the image is sideways in
Chrome.
Setting 6 shows up as 3 on iOS and the image looks right in Chrome.
Only thing I can add is that iOS Mac is Big Endian* and Android/PC is Little Endian. Essentially Little Endian reads/writes bytes as right-to-left whilst Big Endian is opposite.
In binary : 011 means 3 and 110 means 6. The difference between 3 and 6 is simply the reading order of those bits of ones & zeroes. So a system that reads as zero-one-one gets a result of 3 but the other Endian system will read a byte with same bits as one-one-zero and tell you result is a 6. I can't explain why "3 shows up as 0" without a test file to analyse bytes but it's a strange result to me.
</end rant>
<sleep>
*note: While Macs are Big Endian, double-checking says iOS uses Little Endian system after all. Your numbers still suggest a Big vs Little Endian issue though.
Turns out my code was able to set the EXIF data, but there is a discrepancy between how Android interprets this data and how iOS and Chrome on a Mac (where I was checking the resulting file) interprets it.
This is the only code needed to set EXIF orientation:
ExifInterface exif = new ExifInterface(pictureFile.toString());
exif.setAttribute(ExifInterface.TAG_ORIENTATION, "3");
exif.saveAttributes();
However, setting 3 shows up as 0 on iOS and the image is sideways in Chrome.
setting 6 shows up as 3 on iOS and the image looks right in Chrome.
Refer this GitHub project https://github.com/pandiaraj44/Camera. It has the custom camera activity where EXIF TAG_ORIENTATION was handled correctly. You can clone the project and check. For code details please refer https://github.com/pandiaraj44/Camera/blob/master/app/src/main/java/com/pansapp/cameraview/CameraFragment.java
There is one class to read and update these information of images.
To update the attributes you can use like this
ExifInterface ef = new ExifInterface(filePath);
ef.setAttribute(MAKE_TAG, MAKE_TAG);
ef.setAttribute(ExifInterface.TAG_ORIENTATION, orientation+"");
ef.saveAttributes();
and for reading you can use like this
ExifInterface exif = null;
try {
exif = new ExifInterface(absolutePath+path);
} catch (IOException e) {
e.printStackTrace();
}
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
I hope it will help you

android byte[] data modification

I did my own camera app on Android.
1) Configuring camera and preview :
Camera.Parameters parameters = camera.getParameters();
// My camera takes landscape picture by befault (Samsung GT-9300).
// But my app is only in portrait mode.
camera.setDisplayOrientation(90);
// Here to rotate final pict
parameters.set("rotation", 90);
// Some code to define best preview resolution and best picture resolution
... some code ...
// Apply
camera.setParameters(parameters);
2) StartPreview
// Here I see what i want to see... Is there no problem here.
camera.startPreview();
3) GetOutputMediaFile()
// private function to create empty file which will receive data
private static File getOutputMediaFile(){
String NewFolder = "/TEST";
String StorageDirectory;
StorageDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
File mediaStorageDir = new File(StorageDirectory + NewFolder);
if (!mediaStorageDir.exists()){
if (!mediaStorageDir.mkdirs()){
Log.d("myApp", "failed to create directory");
return null;
} else {
mediaStorageDir.mkdir();
}
}
String date = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss", Locale.FRANCE).format(new Date());
File photo = new File(StorageDirectory + NewFolder, "photo_" + date + ".jpg");
return photo;
}
4) Here my problem
// camera.takePicture(null, null, mPicture) called on onCreate function
// Here this callback
private PictureCallback mPicture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
pictureFile = getOutputMediaFile();
if (pictureFile == null){
Log.d(TAG, "Error creating media file, check storage permissions: " + e.getMessage());
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
};
On my phone, if i go on my gallery, or original my files app, i have thumbnail and photo correctly oriented.
Now if I go, with Root File Manager, on this picture folder, thumbnail is oriented by default (real camera orientation), and same as if I look my picture with my computer.
Then I think my data var (byte[] data) on my onPictureTaken function is not good.
I think data is like that :
How I think my var data is
But I would like to have that as my var data :
How I would like to have
I know my var data is only byte[] but these cat pictures is to shows how I see my data var.
Now my questions :
A) Have I right on how my data variable is ?
B) If yes, can you say me how to do 90° rotation on this "array" ?
You should not change the image bits to account for rotation.
Rotation is stored in the JPG EXIF image metadata orientation value, so you just need to set the metadata value to match the camera orientation. Read more about this EXIF value and others here.
To manage the EXIF data you can use framework class ExifInterface.
If the picture shows correctly in your phone's gallery application, it is likely correctly oriented. You can experiment with different values for the Parameters.setRotation and see if they affect what you see in the gallery app.
Some picture viewing programs do not correctly apply the EXIF orientation field, which can lead to them drawing the image incorrectly rotated. If that's the case, changing the orientation field does nothing, since those programs will not work with any orientation value. You'll have to actually rotate the JPEG image, instead of just setting the metadata field, to get them to look correct there.
If you want to do this inside Android, you'll have to decode the byte[] you receive from the camera to a Bitmap (with a BitmapFactory.decodeByteArray, rotate the bitmap, and save the result as a JPEG. You'll lose all the other EXIF metadata (date/time taken, etc) unless you use ExifInterface to write them back. It's also possible to losslessly rotate a JPEG, but I don't know if there are Android libraries for that.

Android change photo orientation for upload

I have found lots of solution to change photo orientation for DISPLAY, and succeeded. But now I need to upload that photo by using file.path. Is there anyway to directly change the photo orientation but not just display differently?
CustomMultiPartEntity multipartContent = new CustomMultiPartEntity(
new CustomMultiPartEntity.ProgressListener() {
#Override
public void transferred(long num) {
Constant.Process = (int) ((num / (float) totalSize11) * 100);}
});
multipartContent.addPart("key", new StringBody(Constant.Key));
multipartContent.addPart("userfile", new FileBody(new File(up.FilePath)));
httpPost.setEntity(multipartContent);
HttpResponse response1 = httpClient.execute(httpPost, httpContext);
response = EntityUtils.toString(response1.getEntity());
Something like that, I need to insert a FileBody by a FilePath into that 'multipartContent', then upload, is there anyway to upload a correct orientation photo??
Thank you very much!
EDIT: This is my onclick method to have camera, how can I add code to rotate image before it was saved?
public void onClick(View v) {
if (v == btn_camera) {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Date date = new Date();
DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
String newPicFile = "Miuzz"+ df.format(date) + ".jpg";
String outPath = "/sdcard/" + newPicFile;
File outFile = new File(outPath);
mUri = Uri.fromFile(outFile);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mUri);
startActivityForResult(cameraIntent, 1);
}
}
There is unfortunately no way of modifying the orientation of the photo file other other than to load the image, rotate it manually and re-save it in it's correct orientation.
See this question for some details on how to save the image once it's rotated: Android Rotate Picture before saving
Edit: Ok, so what you'll want to do is in your onActivityResult() method, load the image and rotate it as you already do. After rotating it, you should have a correctly orientated Bitmap object, then all you need to do is either overwrite your existing photo, or create a new file like this:
try {
FileOutputStream out = new FileOutputStream(filename);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
Also worth noting, is that if you decide to create a new file, be sure to delete it after your upload is complete otherwise you'll be unnecessarily filling up the user's internal storage.

How to resize a photo already taken and stored in a file?

I read about this in several forum questions. But always, people end up using the method "Bitmap createScaledBitmap (Bitmap src, int dstWidth, int dstHeight, boolean filter)", but I can not apply this method because, when dealing with large images, it fails with a message of outofmemory.
I want to know how I can resize a large image already taken. I've readed the android developer section of large bitmaps but I haven't been able to implement it.
I'm using the next code to capture a photo.
public void openTakePicture() {
String date = new SimpleDateFormat("dd-MM-yyyy_HH-mm-ss").format(new Date());
photoname = "pic_" + date + ".jpg";
// Create an output file.
file = new File(Environment.getExternalStorageDirectory(), photoname);
outputFileUri = Uri.fromFile(file);
// Generate the Intent.
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
// Launch the camera app.
startActivityForResult(intent, TAKE_PICTURE);
}
Thanks for your comments
Depending on your needs you could capture a smaller picture and then manipulate it. I was using something similar to the below but ran into issues where some cameras didn't support the setPictureSize so I ended up using the scaled bitmap approach.
You may need to test to see if the camera supports your desired size before calling setPictureSize.
parameters.setPictureFormat(ImageFormat.JPEG);
parameters.setPictureSize(1600, 1200);
parameters.setJpegQuality(30);
mCamera.setParameters(parameters);

Bitmap compress PNG -> JPEG and vice versa in Android

I have problem with size of picture when I'm doing conversion from PNG to JPEG, and then JPEG to PNG.
public void onClick(View v) {
String imageFileName = "/sdcard/Penguins2.png";
File imageFile = new File(imageFileName);
if (imageFile.exists()) {
// Load the image from file
myBitmap = BitmapFactory.decodeFile(imageFileName);
// Display the image in the image viewer
myImageView = (ImageView) findViewById(R.id.my_image_view);
if (myImageView != null) {
myImageView.setImageBitmap(myBitmap);
}
}
}
Conversion:
private void processImage() {
try {
String outputPath = "/sdcard/Penguins2.jpg";
int quality = 100;
FileOutputStream fileOutStr = new FileOutputStream(outputPath);
BufferedOutputStream bufOutStr = new BufferedOutputStream(
fileOutStr);
myBitmap.compress(CompressFormat.JPEG, quality, bufOutStr);
bufOutStr.flush();
bufOutStr.close();
} catch (FileNotFoundException exception) {
Log.e("debug_log", exception.toString());
} catch (IOException exception) {
Log.e("debug_log", exception.toString());
}
myImageView.setImageBitmap(myBitmap);
After processing this operation I just change these lines:
String imageFileName = "/sdcard/Penguins2.png";
to
String imageFileName = "/sdcard/Penguins2.jpg";
and
String outputPath = "/sdcard/Penguins2.jpg";
(...)
myBitmap.compress(CompressFormat.JPEG, quality, bufOutStr);
to
String outputPath = "/sdcard/Penguins2.png";
(...)
myBitmap.compress(CompressFormat.PNG, quality, bufOutStr);
Size of image changed from 585847 to 531409 (in DDMS)
I want to do such thing because I'd like to use PNG which is lossless for some image processing.
Then converse image to jpeg and send as MMS, I'm not sure but I think JPEG is only format which is support by all devices in MMS. Receiver would open image and converse it back to png without losing data.
In addition to the #Sherif elKhatib answer, if you check the documentation: http://developer.android.com/reference/android/graphics/Bitmap.html#compress%28android.graphics.Bitmap.CompressFormat,%20int,%20java.io.OutputStream%29
You can see that the PNG images don't use the quality paremeter:
quality: 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
This is not doable! Once you convert to JPG you lost the "lossless state of PNG".
Anyway png is supported by everyone.
+In your case, you want the receiver to change it back to PNG to retrieve the lossless image. This means that the receiver also supports PNG. What is the point of changing it to JPG before sending and then changing it back to PNG when received. Just some extra computations?

Categories

Resources