I have a custom camera application that is used to take pictures and crop them to square, now I want to know how to write Exif data for the final output image (specially the orientation)
Here are the important parts of my code:
captureButton = (Button) findViewById(R.id.button_capture);
captureButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Take a picture
mCamera.takePicture(null, null, mPicture);
}
});
and this is the call back function:
PictureCallback mPicture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile();
if (pictureFile == null) {
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
}
};
Update:
I Added the following to the onPictureTaken method but nothing changed:
ExifInterface exif;
exif = new ExifInterface(pictureFile.getAbsolutePath());
// Notice getOrientation method gets an Integer with the angle : 0 , 90 , 180 , 270 ..etc
exif.setAttribute(ExifInterface.TAG_ORIENTATION, String.valueOf(getOrientation()) );
exif.saveAttributes();
I know this is old but I had similar issue so thought I'd fill this in first.
If im right in understanding you are trying to store the angle in the .TAG_ORIENTATION exif attribute. That's incorrect,
Adjust your 'getOrientation' method to give you one of the following Orientation Constants specific to the ExifInterface class.
By putting in the specific angle the exif data will be incorrectly read by Image Viewers and how I read your question that is what you are doing.
int ORIENTATION_FLIP_HORIZONTAL int ORIENTATION_FLIP_VERTICAL
int ORIENTATION_NORMAL int ORIENTATION_ROTATE_180
int ORIENTATION_ROTATE_270 int ORIENTATION_ROTATE_90
int ORIENTATION_TRANSPOSE int ORIENTATION_TRANSVERSE
int ORIENTATION_UNDEFINED
Related
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.
I'm doing an app, that needs the device(usually a tablet) to be in landscape, but the picture has to be shown in portrait in the screen.
Until here, I have done it. Bu now, when I take a picture the "preview" image, is showed in landsacape and looks very strange.
See image to see what I mean:
How you see before take image:
And thats after take picture:
And I don't know how to fix it
Thats surfaceView:
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// stop Preview Before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignored: is trying to stop a non-existent preview
}
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
Log.d("CAMERAPREVIEW", "Error starting camera preview : " + e.getMessage());
}
}
And the method overrided
private PictureCallback mPicture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null) {
Log.d(TAG, "Error creating media file, check storage permissions: ");
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
// Not used for the moment
// Intent returnIntent = new Intent();
// setResult(RESULT_CANCELED, returnIntent);
// finish();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};
You can use Camera.setDisplayOrientaion() to rotate the preview on the SurfaceView. But this does not effect the captured image. Your post does not reveal how you display the captured image from file (I assume this is what you show in the second picture). If you load it as a bitmap into an ImageView, you can request rotation for the bitmap.
It also looks like your picture dimensions have different aspect ratio from the preview. I suggest that you look at a recent discussion here.
But maybe I misunderstand what you are doing. Maybe your problem is that you don't restart preview in onPicturetaken()?
How can i capture a picture from front camera without preview and save it to SD card.
Kindly help me with source code.
public void takePictureNoPreview(Context context){
// open back facing camera by default
Camera myCamera=Camera.open();
if(myCamera!=null){
try{
//set camera parameters if you want to
//...
// here, the unused surface view and holder
SurfaceView dummy=new SurfaceView(context)
myCamera.setPreviewDisplay(dummy.getHolder());
myCamera.startPreview();
myCamera.takePicture(null, null, getJpegCallback()):
}finally{
myCamera.close();
}
}else{
//booo, failed!
}
private PictureCallback getJpegCallback(){
PictureCallback jpeg=new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream fos;
try {
fos = new FileOutputStream("test.jpeg");
fos.write(data);
fos.close();
} catch (IOException e) {
//do something about it
}
}
};
}
}
Not possible, however, there's work-arounds.
See this previous answer, and please, try searching before asking in the future: https://stackoverflow.com/a/3881027/181002
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.
I'm trying to capture an image using Android Camera via simple activity.
Image is clicked and stored. But the problem is, image is either distorted or fragments of older image is concatenated with the currently clicked image. Image is too dark. Here's the CODE : -
public class Cameras extends Activity {
public Camera camera;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
camera=Camera.open();
camera.lock();
Parameters parameters = camera.getParameters();
parameters.setJpegQuality(1);
parameters.setJpegThumbnailQuality(1);
parameters.setJpegThumbnailSize(0,0);
parameters.setSceneMode("night");
parameters.setFocusMode("fixed");
parameters.setPictureSize(640,480);
camera.setParameters(parameters);
camera.takePicture(null,null, jpegCallback);
}
PictureCallback jpegCallback = new PictureCallback() { // <8>
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream outStream = null;
try {
// Write to SD Card
outStream = new FileOutputStream(String.format("/sdcard/%d.jpg",System.currentTimeMillis())); // <9>
outStream.write(data);
outStream.close();
camera.unlock();
camera.release();
Toast.makeText(Cameras.this,"Picture Taken",Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally
{
}
}
};
}
Please help regarding this...
I want a neat and clean small size image every time i execute the code.
Thanks... :-)
parameters.setJpegQuality(1);
parameters.setJpegThumbnailQuality(1);
You are requesting very low quality. Try using higher values for quality (like 70)