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.
Related
I am trying to use default camera with Intent for both image and video capture.
when i turn to front camera and capture the image or video preview (captured image or video ) is inverted . So now I want to put preScale in frontCamera. But didn't know where to find Camera ID in default Camera.!!
Note : this is not Custom Camera.!!
Problem is : Captured Image from Front camera getting image is mirror image. Look into it if there any problem ? I didnt get ! Thx
Even stored captured image in SDCard also mirror image! Now how to preScale or any other idea to correct iT??
Help!
photo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
captureImage();
}
});
CatureImage method
private void captureImage() {
Context context = this;
PackageManager packageManager = context.getPackageManager();
if (packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA) == false
&& packageManager
.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) == false) {
Toast.makeText(CameraRolling2.this, "camera not available'",
Toast.LENGTH_SHORT).show();
return;
}
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent
.resolveActivity(CameraRolling2.this.getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
Toast.makeText(CameraRolling2.this, "Error connecting camera",
Toast.LENGTH_SHORT).show();
ex.printStackTrace();
}
// Continue only if the File was successfully created
if (photoFile != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
startActivityForResult(takePictureIntent,
CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
showImg.setImageBitmap(null);
}
}
}
#SuppressLint("SimpleDateFormat")
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
.format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(imageFileName, ".jpg", storageDir);
mCurrentPhotoPath = image.getAbsolutePath();
Log.v("createImageFile", "" + mCurrentPhotoPath);
return image;
}
and My Activity Result
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE
&& resultCode == Activity.RESULT_OK) {
File imgFile = new File(mCurrentPhotoPath);
if (imgFile.exists()) {
BitmapFactory.Options bitmap_options = new BitmapFactory.Options();
bitmap_options.inSampleSize = 4;
bitmap_options.outHeight = 200;
bitmap_options.outWidth = 200;
Bitmap myBitmap = BitmapFactory.decodeFile(
imgFile.getAbsolutePath(), bitmap_options);
showImg.setImageBitmap(myBitmap);
if (showImg.getDrawable() == null) {
Log.e("IMAGEVIEW", "NULL IMAGE");
imageflag = false;
} else {
Log.e("IMAGEVIEW", "IMAGE VIEW UPDATED");
imageflag = true;
}
}
}
if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(CameraRolling2.this, "Camera option canceled",
Toast.LENGTH_SHORT).show();
imageflag = false;
}
I am trying to use default camera with Intent for both image and video capture
There is no single "default camera with Intent" in Android. There are thousands of Android device models. These ship with hundreds of different "default camera with Intent" apps. Users can install other "default camera with Intent" apps from the Play Store or other places. Any one of those apps could be what the user chooses to use to take the picture (ACTION_IMAGE_CAPTURE) or record the video (ACTION_VIDEO_CAPTURE).
when i turn to front camera and capture the image or video preview (captured image or video ) is inverted
I am going to guess that by "inverted", you mean "rotated".
There are hundreds of different possible camera apps. Many will save the image or video with metadata that tells the viewer app to rotate the image (e.g., JPEG photos might have an orientation EXIF header). Some will not, electing to rotate the content themselves.
Also, the images or videos from any camera (front-facing, rear-facing, external) could be rotated.
Hence, you should not be assuming that the rotation is tied to the particular camera that the user used. Instead, examine the actual content and react accordingly. This sample project demonstrates this for rotating a JPEG image, using a couple of different techniques.
So now I want to put preScale in frontCamera.
You cannot control this.
But didn't know where to find Camera ID in default Camera.!!
You cannot control this.
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.
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()?
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.
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