I am trying to take a picture using the Android camera. I have a requirement to capture a 1600 (w) x 1200 (h) image (3rd party vendor requirement). My code seems to work fine for many phone cameras but the setPictureSize causes a crash on some phones (Samsung Galaxy S4, Samsung Galaxy Note) and causes a streaked picture on others (Nexus 7 Tablet). On at least the Nexus the size I desire is showing up in the getSupportPictureSizes list.
I have tried specifying the orientation but it didn't help. Taking the picture with the default picture size works fine.
Here is an example of the streaking:
For my image capture I have a requirement of 1600x1200, jpg, 30% compression, so I am capturing a JPG file.
I think I have three choices:
1) Figure out how to capture the 1600x1200 size without a crash or streaking, or
2) Figure out how to change the size of the default picture size to a JPG that is 1600x1200.
3) Something else that is currently unknown to me.
I have found some other postings that have similar issues but not quite the same. I am in my 2nd day of trying things but am not finding a solution. Here is one posting that got close:
Camera picture to Bitmap results in messed up image (none of the suggestions helped me)
Here is the section of my code that worked fine for until I ran into the S4/Note/Nexus 7. I have added a bit of debugging code for now:
Camera.Parameters parameters = mCamera.getParameters();
Camera.Size size = getBestPreviewSize(width, height, parameters);
if (size != null) {
int pictureWidth = 1600;
int pictureHeight = 1200;
// testing
Camera.Size test = parameters.getPictureSize();
List<Camera.Size> testSizes = parameters.getSupportedPictureSizes();
for ( int i = 0; i < testSizes.size(); i++ ) {
test = testSizes.get(i);
}
test = testSizes.get(3);
// get(3) is 1600 x 1200
pictureWidth = test.width;
pictureHeight = test.height;
parameters.setPictureFormat(ImageFormat.JPEG);
parameters.setPictureSize(pictureWidth, pictureHeight);
parameters.setJpegQuality(30);
parameters.setPreviewSize(size.width, size.height);
// catch any exception
try {
// make sure the preview is stopped
mCamera.stopPreview();
mCamera.setParameters(parameters);
didConfig = true;
catch(Exception e) {
// some error presentation was removed for brevity
// since didConfig not set to TRUE it will fail gracefully
}
}
Here is the section of my code that saves the JPG file:
PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
if ( data.length > 0 ) {
String fileName = "image.jpg";
File file = new File(getFilesDir(), fileName);
String filePath = file.getAbsolutePath();
boolean goodWrite = false;
try {
OutputStream os = new FileOutputStream(file);
os.write(data);
os.close();
goodWrite = true;
} catch (IOException e) {
goodWrite = false;
}
if ( goodWrite ) {
// go on to the Preview
} else {
// TODO return an error to the calling activity
}
}
Log.d(TAG, "onPictureTaken - jpeg");
}
};
Any suggestions on how to correctly set up the camera parameters for taking photos or how to crop or resize the resulting photo would be great. Especially if it will work with older cameras (API level 8 or later)! Based on needing the full width of the picture I can only crop off the top.
Thanks!
EDIT: Here is what I ended up doing:
I started by processing the Camera.Parameters getSupportedPictureSizes to use the first one that had the height and width both greater than my desired size, AND the same width:height ratio. I set the Camera parameters to that picture size.
Then once the picture was taken:
BitmapFactory.Options options = new BitmapFactory.Options();;
options.inPurgeable = true;
// convert the byte array to a bitmap, taking care to allow for garbage collection
Bitmap original = BitmapFactory.decodeByteArray(input , 0, input.length, options);
// resize the bitmap to my desired scale
Bitmap resized = Bitmap.createScaledBitmap(original, 1600, 1200, true);
// create a new byte array and output the bitmap to a compressed JPG
ByteArrayOutputStream blob = new ByteArrayOutputStream();
resized.compress(Bitmap.CompressFormat.JPEG, 30, blob);
// recycle the memory since bitmaps seem to have slightly different garbage collection
original.recycle();
resized.recycle();
byte[] desired = blob.toByteArray();
Then I write out the desired jpg to a file for upload.
test = testSizes.get(3);
// get(3) is 1600 x 1200
There is no requirement that the array have 4+ elements, let alone that the fourth element be 1600x1200.
1) Figure out how to capture the 1600x1200 size without a crash or streaking
There is no guarantee that every device is capable of taking a picture with that exact resolution. You cannot specify arbitrary values for the resolution -- it must be one of the supported picture sizes. Some devices support arbitrary values, while other devices will give you corrupted output (as is the case here) or will flat-out crash.
2) Figure out how to change the size of the default picture size to a JPG that is 1600x1200
I am not aware that there is a "default picture size", and, beyond that, such a size will be immutable, since it is the default. Changing the picture size is your option #1 above.
3) Something else that is currently unknown to me.
For devices that support a resolution that is bigger on both axes, take a picture in that resolution, then crop to 1600x1200.
For all other devices, where one or both axes are smaller than desired, take a picture in whatever resolution suits you (largest, closest match to 4:3 aspect ratio, etc.), and then stretch/crop to get to 1600x1200.
Related
I am staring to develop an app which monitors the camera preview, and does some image processing on it and displays int on a canvas. Just as a diagnostic I have the following code:
camera = Camera.open();
ImageFormat imf = new ImageFormat();
Camera.Parameters param = camera.getParameters();
param.setPreviewSize(128, 128);
preview_format = param.getPreviewFormat();
Camera.Size sz = param.getPreviewSize();
myimage = new int[sz.width*sz.height];
At run time it reports that preview_format is 17 which I understand is "NV21".
Later I have:
camera.setPreviewCallback(new PreviewCallback()
{
public void onPreviewFrame(byte[] _data, Camera _camera)
{
YUV_NV21_TO_RGB(myimage , _data, 128, 128) ;
}
});
The function YUV_NV21_TO_RGB was taken from here.
Meanwhile in another thread I have:
canvas.drawBitmap(
myimage, // the int array
0, // where to start in the array
128, // the stride ???
200, // x coord of where to display
200, // y coord of where to display
128, // wid
128, // ht
false, // alpha used?
null); // the paint used
The resulting image can be seen amongst other diagnostics in the square below. The stripes change as I move the phone around and appear to in some way correspond to what the camera is pointing at, but clearly it has been mangled. I tried using an alternative function found here, and another from wikipedia, but with seemingly identical results. Any ideas?
EDIT: One thought I had was that perhaps NV21 may not completely specify the format - maybe its a class of formats, where you need to go on and specify the bits per pixel or similar.
EDIT: An extra clue - if I cover the camera completely, the square goes entirely pure green.
Your preview size is not 128 by 128 because you fail to set it. You set it on the Camera.Parameters instance but you don't apply it to the camera.
You need to add the following line:
camera.setParameters(param);
And it's probably safe to get the parameters directly from the Camera instance:
preview_format = camera.getPreviewFormat();
Camera.Size sz = camera.getPreviewSize();
Working on an application in which we capture images and upload over server. Application is in Android And I Phone. When we post image from Android, they are of quantity in Kilo Bytes but when we Post image from I Phone they are of MB size.
When we the images posted from IPHONE 5 with URL on browser, they appear good as they supposed to appear but when we download that image in Android device and show in an IMAGE VIEW, they appears 90 deg tilt to the left hand side.
I am not using, any rotation code after downloading the images in Android or in I Phone.
In I Phone the images are appearing fine.
IMages captured from Android are also visible straight. Images of low resolution capture from I Phone are also visible straight in Android.
Image uploaded from Android:
https://s3.amazonaws.com/WeddingApp/Weddingimage/933_6_stan.jpg
Image uploaded from I Phone:
https://s3.amazonaws.com/WeddingApp/Weddingimage/937_6_stan.jpg
public static boolean downloadFile(final String fileURL,File directory,Context CONTEXT){
try{
URL url = new URL(fileURL);
URLConnection ucon = url.openConnection();
ucon.setReadTimeout(35000);
ucon.setConnectTimeout(10000);
InputStream is = ucon.getInputStream();
BufferedInputStream inStream = new BufferedInputStream(is, 1024 * 5);
File file = directory;
if (file.exists())
{
file.delete();
}
file.createNewFile();
FileOutputStream outStream = new FileOutputStream(file);
byte[] buff = new byte[5 * 1024];
int len;
while ((len = inStream.read(buff)) != -1)
{
outStream.write(buff, 0, len);
}
outStream.flush();
outStream.close();
inStream.close();
}
catch (IOException e){ //IF SDCARD NOT EXIST THEN PASS RESPONSE TRUE`
e.printStackTrace();
return false;
}catch(Exception e){
e.printStackTrace();
return false;
}
return true;
}
Please suggest me.
This is because of iPhone's silly new camera implementation. I found below on research (sources given below):
1. The iPhone camera's interpretation of "up" direction is rotated 90 degrees from the actual "up" direction we know. What you and I call "up", is iphone's "left" or "right". The camera's orientation has been changed. This was done so that they could support taking pictures with the volume button.
2. But, being aware that other devices and cameras have normal orientation, and so that those devices and browsers display the images properly, iOS adds EXIF data to the uploaded images. This is added on image upload. EXIF, as mentioned in the link, is a metadata that contains all the information of how the image is actually supposed to look.
3. The target platform on which the image is downloaded is supposed to read the EXIF data, and then show the correct representation of the image. So, if the target platform reads the EXIF, it will realize that the image received is 90 degrees rotated, and that it should rely on EXIF data, and not on what's received. This is why iPhone camera apps can show the image properly.
4 However, not everybody reads EXIF. Apps and browsers that are aware of EXIF, read the EXIF data of the image, and show it properly. But those that are not aware of EXIF, don't read that data, and show the image exactly as received --> 90 degrees rotated
5 People have worked around this problem, by rotating their iPhone 90 degrees, before taking a picture (so that the camera is oriented right, before the picture is taken)
Resources:
1. Source 1
2. Source 2
Others with same problem:
1. SO Post 1
2. SO Post 2
The problem seems to be related to EXIF data found on the images. We must process it before displaying the image. It appears to be that not every camera outputs EXIF data 'cause this issues only happens to me in some android handsets.
Take a look at: http://commons.wikimedia.org/wiki/Commons:Exif#Orientation_.28rotation_and_mirroring.29
EDIT:
We could implement something like:
public Bitmap getBitmap(String path){
Bitmap tmpBitmap = BitmapFactory.decodeFile(path);
Bitmap bitmap = null;
if(path != null){
try {
ExifInterface ei = new ExifInterface(path);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Matrix mtx = new Matrix();
int w = tmpBitmap.getWidth();
int h = tmpBitmap.getHeight();
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
//rotate CCW
mtx.preRotate(-90);
bitmap = Bitmap.createBitmap(tmpBitmap, 0, 0, w, h, mtx, true);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
//rotate CW
mtx.preRotate(90);
bitmap = Bitmap.createBitmap(tmpBitmap, 0, 0, w, h, mtx, true);
break;
//CONSIDER OTHER CASES HERE....
default:
bitmap = tmpBitmap;
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return bitmap;
}
Regards.
I am in the process of implementing expansion files for my Android app.
So far, I've placed several images and audio files into the main expansion file (I am not using the patch expansion file). None of the files have been compressed.
Through the app, I am able to download the expansion file, and play the audio files without any problems. At the same time an audio file in the expansion file is played, I am also displaying an image from the expansion file. However, the image is considerably smaller than I expected.
The image is 320x400px. Before implementing the expansion files, it was displayed as expected in my app. However, after implementation, it looks like the image shrank to about 50px wide (the height shrank in proportion).
I then tried the solution offered in How to create a drawable from a stream without resizing it. While the image does appear slightly larger, it is still much smaller than what I want it to be (looks like it's about 100x125 px now). Currently, my code for displaying the image looks like this:
public void displayImageFromExpansionFile(){
Bitmap b = BitmapFactory.decodeStream(fileStream);
b.setDensity(Bitmap.DENSITY_NONE);
Drawable d = new BitmapDrawable(this.getResources(), b);
imageToDisplay.setImageDrawable(d);
}
public void showImg(int imgNum){
switch(imgNum){
case(1):
try{
if((getResources().getConfiguration().screenLayout &
Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_SMALL){
fileStream = expansionFile.getInputStream("filepath inside expansion file for small image");
}
else if((getResources().getConfiguration().screenLayout &
Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_NORMAL){
fileStream = expansionFile.getInputStream("filepath inside expansion file for normal image");
}
else if((getResources().getConfiguration().screenLayout &
Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE){
fileStream = expansionFile.getInputStream("filepath inside expansion file for large image");
}
else{
fileStream = expansionFile.getInputStream("filepath inside expansion file for xlarge image");
}
displayImageFromExpansionFile();
} catch (Exception e) {
e.printStackTrace();
}
break;
// more cases here
}
It still seems as if the image is not being displayed at its actual size. When I examine the image inside the expansion file, I can see that it is still at 320x400px. However, the app is not displaying the image at these dimensions.
What could I do to get the app to display the image at its correct dimensions?
Thanks!
---UPDATE---
I've also tried the code below, with no difference in results. It still looks to be about 100x125px, instead of 320x400px, like its original size.
public void displayImageFromExpansionFile(int bWidth, int bHeight){
BitmapFactory.Options bfo = new BitmapFactory.Options();
bfo.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap b = BitmapFactory.decodeStream(fileStream, null, bfo);
b.setDensity(Bitmap.DENSITY_NONE);
imageToDisplay.setImageBitmap(b);
}
The only thing that's worked so far is Bitmap.createScaledBitmap(b, bWidth, bHeight, true);
On my phone, doubling the image's original dimensions (to 640x800 px) using the above method brings the image up to its expected size, but I would imagine that the image might appear at different sizes on different phones (probably because of screen density/size). When I tried doubling the xlarge image dimensions and viewed it on my tablet, the image appears larger than it should.
In the end, I fiddled around with the layout XML file that was displaying the shrunken image. I changed the attributes of the imageView like so:
Width / Height: fill_parent (both)
Scale Type: fitCenter
Now, the images are slightly bigger than I initially expected, but I think it looks better this way. And now the images are of a consistent size. Problem solved!
If anyone else is having trouble with what I've been experiencing, hope this helps.
thats worked for me:
public static Bitmap getBitmapExt(Context context, String name){
Bitmap bitmap = null;
try {
if(expansionFile == null){
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
expansionFile = APKExpansionSupport.getAPKExpansionZipFile(context, pInfo.versionCode, 0);
}
InputStream is = expansionFile.getInputStream("images/" + name + ".png");
bitmap = BitmapFactory.decodeStream(is);
}
catch (Exception e){
e.printStackTrace();
}
return bitmap;
}
https://gist.github.com/uebi-belli/3c07323c8055a3b66ccac0d388b2b013
Hope that helps =)
Scenario:
I need to take a picture as fast as possible and save it to SD Card. It would be fantastic if I could do it in around 0.2 seconds both taking the picture and saving it.
What I did so far:
As normal I've created a SurfaceView to handle the Camera preview and initialized the camera object. The quality of the image doesn't need to be very high, that's why I am not using the largest resolution possible and also no autofocus is required.
I set the parameters like this:
Parameters parameters = camera.getParameters();
parameters.set("jpeg-quality", 70);
parameters.setPictureFormat(ImageFormat.JPEG);
List<Camera.Size> sizes = parameters.getSupportedPictureSizes();
Size size = sizes.get(Integer.valueOf((sizes.size()-1)/2)); //choose a medium resolution
parameters.setPictureSize(size.width, size.height);
camera.setParameters(parameters);
camera.setDisplayOrientation(90);
List<Size> sizes2 = parameters.getSupportedPreviewSizes();
Size size2 = sizes.get(0);
parameters.setPreviewSize(size2.width, size2.height);
camera.setPreviewDisplay(holder);
camera.startPreview();
I save the image to SD card very simple with:
PictureCallback handlePictureStorage = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream outStream = null;
try {
outStream = new FileOutputStream(String.format("/sdcard/%d.jpg", System.currentTimeMillis()));
outStream.write(data);
outStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
};
After making a few tests, on my Galaxy Nexus, the result looks like:
Setting picture size to : wigth=1600 height=1200
Jpeg quality : 70, Picture format JPEG
Fire take picture at: 00:13:23.603
Start saving picture on SD Card at: 00:13:23.956
Finished saving picture on SD Card at: 00:13:23.990
This is almost 0.4 seconds.
Is there a way to tweak the Camera parameters even more to gain some faster speed ? The resolution is OK, the quality of the picture also. I know that there are apps on market that have 30 pictures per second but I think they use buffering to achieve that speed. However, as you may see the biggest time is lost with taking the picture rather than saving it to card. It would be great if I could tweak this a bit more.
After I did a bit of testing with multiple parameters, conclusion is that not much is left to be done. Here are some parameters I've set:
//set color efects to none
cameraParameters.setColorEffect(Camera.Parameters.EFFECT_NONE);
//set antibanding to none
if (cameraParameters.getAntibanding() != null) {
cameraParameters.setAntibanding(Camera.Parameters.ANTIBANDING_OFF);
}
// set white ballance
if (cameraParameters.getWhiteBalance() != null) {
cameraParameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT);
}
//set flash
if (cameraParameters.getFlashMode() != null) {
cameraParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
}
//set zoom
if (cameraParameters.isZoomSupported()) {
cameraParameters.setZoom(0);
}
//set focus mode
cameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY);
However, the best idea is to get the full string list of parameters supported by the camera, and try to tweak them. To get the string, use the flatten method of Camera.Parameters - http://developer.android.com/reference/android/hardware/Camera.Parameters.html#flatten()
But in order to get images really quick, I had to use preview with buffer, and for each frame taken, try to save it on sd-card in a thread. The picture quality isn't fantastic, but it's a start.
If quality doesn't matter, maybe you could look into using something other than JPEG and compare execution times:
http://developer.android.com/reference/android/graphics/ImageFormat.html
I am trying to load an image in my app having size 495KB. If I load this image than heap size increases from 25MB to 35MB which is causing real memory issues in my app. If i dont load this image than heap size stays at 25MB. Can anybody tell why it is taking so much heap size?
Image is below
Code that I am using to load an image is
InputStream s4 = getResources().openRawResource(R.drawable.parallax_layer4);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
System.gc();
if(bitmap4 != null) {
bitmap4.recycle();
}
s3 = null;
System.gc();
bitmap4 = bitmap(s4);
layer4Back = new ImageView(this);
layer4Back.setImageBitmap(bitmap4);
layer4Back.setScaleType(ScaleType.FIT_XY);
parallaxLayout.addView(layer4Back, 3, lp);
try {
s4.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
s4 = null;
System.gc();
private static Bitmap bitmap(final InputStream is) {
Bitmap bitmap = null;
System.gc();
Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inSampleSize = 1;
try {
// bitmap = BitmapFactory.decodeStream(is);
bitmap = BitmapFactory.decodeStream(is, null, options);
} catch (Error e) {
// TODO: handle exception
e.printStackTrace();
}
return bitmap;
}
Thanks
Ayaz Alavi
The image file is uncompressed when it's loaded into memory. At 5600 x 480 pixels and 32 bits per pixel, your image works out as almost exactly 10 MB when uncompressed.
My recommendation would be to cut it into smaller sections and only load the sections you need.
5,600px * 480px * 4 bytes = 10,752,000 bytes, so this isn't surprising.
There is some good guidance in the Displaying Bitmaps Efficiently article, as well as quite a few questions here on SO discussing good solutions.
Well what did you expect? The image is 5600 x 480 px, now in memory every single pixel takes 32 bits (plz correct me someone). Do the math and you get a good idea why it is a problem. You need to use a smaller image OR cut the image up in parts and load the parts that is needed when they are needed and discard the unnecessary parts.
I had a similar problem which is discussed here: Android app crash out of memory on relaunch
There is also a long discussion about the subject and possible solutions here: Strange out of memory issue while loading an image to a Bitmap object
BitmapFactory will auto-scale your bitmap if you load it and have a high DPI screen resolution. I think on devices with the largest DPI value it gets scaled by 2x in both directions.
This may explain some of the unexpected increase. Try putting your images in folder called drawable-nodpi and they will not be scaled.