android byte[] data modification - android

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.

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 onPictureTaken resulting in poor quality/wrong rotated images

I have an issue when saving images in my application. I am using Android camera api 1 (pre api 21) on android version 4.4.4. Device is a OnePlus One.
When I take a picture my in-built camera in my app in seems to save the images in poor quality and also rotated 90 degrees counter-clockwise (-90).
Here is an example with images.
Portrait view with default android camera app (saved image):
Portrait view with in-built app camera:
Picture when saved with in-built app camera (saved image):
First problem, rotation orientation
Now the rotation I am guessing is due to this (if I don't change the setDisplayOrientation the camera is skewed in my app):
public void refreshCamera(Camera camera) {
if (holder.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
camera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
int rotation = ((WindowManager)activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
int degrees = 0;
// specifically for back facing camera
switch (rotation) {
case Surface.ROTATION_0:
degrees = 90;
break;
case Surface.ROTATION_90:
degrees = 0;
break;
case Surface.ROTATION_180:
degrees = 270;
break;
case Surface.ROTATION_270:
degrees = 180;
break;
}
camera.setDisplayOrientation(degrees);
setCamera(camera);
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception e) {
Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
}
}
To fix this I guess I could rotate the images when I have saved the image, seems like a waste of code writing such a method though.
Second problem, the quality
This I am clueless as to why the quality is so bad, I'm guessing it has to do with this:
private PictureCallback getPictureCallback() {
return new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
// save picture on seperate thread so camera can refresh quicker
new Thread(new SavePicThread(data)).start();
// refresh camera to continue preview
cameraPreview.refreshCamera(camera);
}
};
}
public class SavePicThread implements Runnable {
byte[] data;
public SavePicThread(byte[] data) {
this.data = data;
}
public void run() {
// make a new picture file
File pictureFile = getOutputMediaFile();
if (pictureFile == null) {
return;
}
try {
// write to the file
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.flush();
fos.close();
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Toast toast = Toast.makeText(getActivity(), "Picture saved", Toast.LENGTH_SHORT);
toast.show();
}
});
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// make the picture visible to the rest of the device
galleryAddPic(pictureFile);
}
}
// make picture and save to a folder
private File getOutputMediaFile() {
// make a new file directory inside the "sdcard" folder
// File mediaStorageDir = new File("/sdcard/", "fela"); // private pic for app
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "fela");
// if the directory does not exist
if (!mediaStorageDir.exists()) {
// if you cannot make this directory return
if (!mediaStorageDir.mkdirs()) {
return null;
}
}
// take the current timeStamp
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
// and make a media file:
mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
return mediaFile;
}
/**
* makes the image visible for the device (gallery)
* #param pic file
*/
private void galleryAddPic(File file) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.fromFile(file);
mediaScanIntent.setData(contentUri);
getActivity().sendBroadcast(mediaScanIntent);
}
Been looking at tutorials, this is pretty much what they cover!
I understand that you found the solution for image quality. Indeed, you cannot assume that the default PictureSize is the best quality available on the device; you should use getSupportedPictureSizes() and after that call setPictureSize().
Regarding rotation, the situation is more cumbersome. Camera parameters support the setRotation() method which
Sets the clockwise rotation angle in degrees relative to the orientation of the camera. This affects the pictures returned from JPEG Camera.PictureCallback. The camera driver may set orientation in the EXIF header without rotating the picture. Or the driver may rotate the picture and the EXIF thumbnail. If the Jpeg picture is rotated, the orientation in the EXIF header will be missing or 1 (row #0 is top and column #0 is left side).
As you can see from the description, some devices will actually save the JPEG image oriented correctly, but other devices will only save the EXIF header.
Unfortunately, the orientation flag is not enough for many image viewer apps, for example - for Windows built-in image preview. Performing rotation of the full-size image programmatically is possible, but it is a time- and (more importantly) memory-consuming task. The easiest way is to create a bitmap and rotate it.
Many people use scaled bitmap to make rotation faster and use less memory. But this will inevitably defeat your purpose of getting the best picture quality.

How to take picture using inside Camera app

I want to take picture using camera app built in the device with touch event even though device doesn't support that function.
What i want to realize is following.
1) When I open the native or any other camera app,
2) Take a picture with touch event instead of camera button ( This part is what i want to develop)
Below code is What I try for this.
I tried to call transparent Activity on the camera app,
and When I get a touch event on the that Activity,
I call Take_picture() function.
But camera.takePicture() function in the Take_picture doesn't work. ( actually it doesn't call jpegCallback function)
private void Take_picture(){
camera = Camera.open();
if(camera != null)
{
camera.takePicture(null, null, jpegCallback);
}
}
PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
new SaveImageTask().execute(data);
}
};
private class SaveImageTask extends AsyncTask<byte[], Void, Void> {
#Override
protected Void doInBackground(byte[]... data) {
FileOutputStream outStream = null;
System.out.println("66666");
// Write to SD Card
try {
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File (sdCard.getAbsolutePath() + "/camtest");
dir.mkdirs();
String fileName = String.format("%d.jpg", System.currentTimeMillis());
File outFile = new File(dir, fileName);
outStream = new FileOutputStream(outFile);
outStream.write(data[0]);
outStream.flush();
outStream.close();
//Log.d(TAG, "onPictureTaken - wrote bytes: " + data.length + " to " + outFile.getAbsolutePath());
//refreshGallery(outFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
return null;
}
}
I couldn't get any information How to I control native camera app for take picture instantly.
Please help.
How to I control native camera app for take picture instantly.
You can't. You are welcome to create your own camera app that takes pictures however you want. The authors of other camera applications are welcome to implement their camera apps however they want, and they do not have to provide any means for other developers to dictate when and how the pictures are taken.
But camera.takePicture() function in the Take_picture doesn't work
Your app should be crashing, as you should not have a valid Camera object. Only one app can use the camera at a time.

Some Androids Uploading Scrambled Images

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.

Android:I can't take a photo with the front facing camera on SurfaceView (Samsung Galaxy S GT-i9000 )

I get a green screen with the front camera from samsung galaxy s, but the preview is correct. With the Back-Camera can I make photos.
Where is the problem?
This is my PictureCallback:
public void onIvButtonShutterClick(View view) {
PictureCallback pictureCallback = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
File picture = new File(My_Camera.this.getFilesDir()
+ "/bild.jpg");
FileOutputStream pictureOut = new FileOutputStream(picture);
pictureOut.write(data);
pictureOut.flush();
pictureOut.close();
Toast.makeText(
My_Camera.this,
getResources().getString(
R.string.tx_my_camera_save)
+ "\n" + picture.getAbsolutePath(),
Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
//mCamera.startPreview(); // Preview wird weiter ausgeführt
}
};
mCamera.takePicture(null, null, pictureCallback);
}
I access on the front camera with:
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
mParameters = mCamera.getParameters();
mParameters.set("camera-id", 2);
mParameters.setPictureFormat(PixelFormat.JPEG);
mCamera.setDisplayOrientation(270);
mCamera.setParameters(mParameters);
mCamera.startPreview();
}
I had the same problem but never found a real solution.
What I ended up using was to grab frames from the camera preview. This has some issues – like, pictures are much more likely to be blurry.
The relevant method for grabbing preview frames is Camera.setOneShotPreviewCallback.
I think the problem might be the processing of the result. I'm guessing you are sending the originally captured byte array containing the raw image data to a file with jpg extension, but the byte array is not in jpg data (i.e. missing headers and with uncompressed content).
Here's a relevant piece from some code I have (the MeToo project that you saw), imageData being the raw content:
Bitmap backCameraImage = BitmapFactory.decodeByteArray(imageData,
0, imageData.length).copy(Bitmap.Config.RGB_565, true);
FileOutputStream out = null;
out = new FileOutputStream(file);
backCameraImage.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.close();
Hope this helps...
Did you try to set this parameter:
mParameters.setPreviewFormat(PixelFormat.JPEG);
It helped me, but on Motorola Droid causing problems.

Categories

Resources