Camera preview always returns a 320x240 Bitmap - android

I am creating an image preview and I wished to get a very high picture resolution. However, and I don't know why, despite Galaxy Nexus supports a wide range of resolutions (I think the largest one is 1920x1080, but I can't remember if those are the exact values), when I save the picture, it says the info coming from the preview is of 320x240.
pixels.mCamera = getCameraInstance();
Camera.Parameters params = mCamera.getParameters();
List<Size> sizes = params.getSupportedPreviewSizes();
Size biggestSize = sizes.get(0);
double biggestPixels = biggestSize.width + biggestSize.height;
for (Size size : sizes) {
double pixels = size.width * size.height;
if (pixels > biggestPixels) {
biggestSize = size;
biggestPixels = pixels;
}
}
// At this point, biggestSize is 1920x1080
float ratio = ((float) biggestSize.width)
/ ((float) biggestSize.height);
params.setPreviewSize(biggestSize.width, biggestSize.height);
mCamera.setParameters(params);
setCameraDisplayOrientation(this, 0, mCamera);
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
LinearLayout innerWrapper = (LinearLayout) findViewById(R.id.innerWrapper);
LayoutParams layoutParams = (LayoutParams) innerWrapper
.getLayoutParams();
layoutParams.height = (int) (layoutParams.height * ratio);
innerWrapper.setLayoutParams(layoutParams);
preview.addView(mPreview);
That innerWrapper stuff is to show a square picture. setCameraOrientation turns the image to align with the ratio. When taking the picture I use autofocus and then...
mCamera.autoFocus(new AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
mCamera.takePicture(null, null, new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,
data.length, options);
Bitmap finalBitmap;
float ow = bitmap.getWidth();
float oh = bitmap.getHeight();
// At this point, ow and oh are 320 and 240
float ratio = ow / oh;
int fw, fh, x, y, angle;
//It continues, but irrelevant
...
Is this anything related to the layout dimensions of the FrameLayout I use to hold the preview? Because I defined it to be of 350x350 dp, and this the preview should be bigger.

Silly me. The problem is that my PictureCallback is implemented as the jpeg parameter, not the raw one. Where, quoting the documentation:
The raw callback occurs when the raw image data is available (NOTE:
the data will be null if there is no raw image callback buffer
available or the raw image callback buffer is not large enough to hold
the raw image). The postview callback occurs when a scaled, fully
processed postview image is available (NOTE: not all hardware supports
this). The jpeg callback occurs when the compressed image is available.
Moving the whole PictureCallback to the second parameter of mCamera.takePicture instead of the third would be on the path to solve the issue. However, you'd need a CallbackBuffer to do so and change the code to adapt to the use of buffers. So for the first version I'll stick to low quality pictures.

Related

android mediaprojection screenshot contains black frame

I am working on recording my screen with MediaProjection
as follows
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
displayWidth = size.x;
displayHeight = size.y;
imageReader = ImageReader.newInstance(displayWidth, displayHeight, ImageFormat.JPEG, 5);
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
DisplayMetrics metrics = getResources().getDisplayMetrics();
int density = metrics.densityDpi;
mediaProjection.createVirtualDisplay("test", displayWidth, displayHeight, density, flags,
imageReader.getSurface(), null, projectionHandler);
Image image = imageReader.acquireLatestImage();
byte[] data = getDataFromImage(image);
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Problem is that captured images contains black frame like image below.
EDIT
The above issue can be solved with bitmap operations.
However, I am now looking for a solution that can be applied to MediaProjection or to SurfaceView of ImageReader to implement device recording.
I had a similar issue. The following code exhibits this problem.
final DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
final int width = metrics.widthPixels;
final int height = metrics.heightPixels;
final int densityDpi = metrics.densityDpi;
final int MAX_IMAGES = 10;
mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, MAX_IMAGES);
mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCaptureTest",
width, height, densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
Replacing this:
getWindowManager().getDefaultDisplay().getMetrics(metrics);
With this:
getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
Fixes it. The problem is that the decorations around the image corrupt the actual resolution of the screen. getMetrics() returns a height (or width in landscape) that is not accurte, and has the home, back, etc, buttons subtracted. The actual display area available for developers is (1440 x 2326... or something like that). But of course, the captured image is going to be the full 1440 X 2560 screen resolution.
If you do not have control over the image yourself, you can modify it by doing something like, assuming your Bitmap is called image.
Bitmap imageWithBG = Bitmap.createBitmap(image.getWidth(), image.getHeight(),image.getConfig()); // Create another image the same size
imageWithBG.eraseColor(Color.BLACK); // set its background to white, or whatever color you want
Canvas canvas = new Canvas(imageWithBG); // create a canvas to draw on the new image
canvas.drawBitmap(image, 0f, 0f, null); // draw old image on the background
image.recycle();
Based on your comments I think this is what you are looking for
Bitmap bitmap; //field
Bitmap croppedBitmap; // field
Image image;// field
Handler mHandler; //field
new Thread() {
#Override
public void run() {
Looper.prepare();
mHandler = new Handler();
Looper.loop();
}
}.start();
imageAvailableListener = new ImageReader.OnImageAvailableListener {
#Override
public void onImageAvailable(ImageReader reader) {
try {
image = reader.acquireLatestImage();
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Rect rect = image.getCropRect();
croppedBitmap = Bitmap.createBitmap(bitmap,rect.left,rect.top,rect.width(),rect.height());
\\Do whatever here...
image.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (bitmap!=null) {
bitmap.recycle();
}
if (croppedBitmap!=null) {
bitmap.recycle();
}
if (image!=null) {
image.close();
}
}
}
}
imageReader.setOnImageAvailableListener(imageAvailableListener, mHandler);

Low Quality after scaling Bitmap to lower resolution

I am trying to scale down a Bitmap but the edges are not clear as the original image, its little blurred
I have checked out the below link
Bad image quality after resizing/scaling bitmap
Please help
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,data.length);
Matrix m=new Matrix();
m.postRotate(90);
//m.postScale((float)0.5,(float) 0.5);
//Added for merging
//Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
Bitmap mutableBitmap = Bitmap.createBitmap(bitmap,0,0,600,400,m,false);
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
Canvas canvas = new Canvas(mutableBitmap);
View v=(View)relLay.getParent();
v.setDrawingCacheEnabled(true);
v.buildDrawingCache();
Options options = new BitmapFactory.Options();
options.inScaled = false;
//options.inJustDecodeBounds=true;
options.inSampleSize=2;
//Bitmap viewCapture = v.getDrawingCache().copy(Bitmap.Config.ARGB_8888, true);
// Bitmap newImage = Bitmap.createScaledBitmap(viewCapture, viewCapture.getWidth()/2, viewCapture.getHeight()/2, true);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
(v.getDrawingCache().copy(Bitmap.Config.ARGB_8888, true)).compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
Bitmap viewCapture= BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, options);
v.setDrawingCacheEnabled(false);
Rect src = new Rect(0, 0, viewCapture.getWidth(), viewCapture.getHeight());
Log.d("TEST",""+viewCapture.getWidth()+" "+viewCapture.getHeight());
//Destination RectF sized to the camera picture
Rect dst = new Rect(0, 0, mutableBitmap.getWidth(), mutableBitmap.getHeight());
Log.d("Test",""+mutableBitmap.getWidth()+" "+mutableBitmap.getHeight());
canvas.drawBitmap(viewCapture, src, dst, paint);
// Bitmap newImage = Bitmap.createScaledBitmap(viewCapture, 400,600, true);
mutableBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
The viewCapture element gets blurred out if I try to scale
Let me try to help you.
First of all, If you your desired Camera Captured Picture size is 600*400 or similar size then
I would say set the camera param for your desired size (which is supported by camera) and you will get the small image in Camera's Picture taken method
Note : but make sure first you need to check what ate picture sizes supported by Device camera. then set one of them.
Here is the one example I tested for Galaxy Nexus
Galaxy Nexus Camera Supported Picture sizes
Supported Picture Size. Width: 2592 * height : 1944
Supported Picture Size. Width: 2592 * height : 1728
Supported Picture Size. Width: 2592 * height : 1458
Supported Picture Size. Width: 2048 * height : 1536
Supported Picture Size. Width: 1600 * height : 1200
Supported Picture Size. Width: 1280 * height : 1024
Supported Picture Size. Width: 1152 * height : 864
Supported Picture Size. Width: 1280 * height : 960
Supported Picture Size. Width: 640 * height : 480
Supported Picture Size. Width: 320 * height : 240
below is the sample code will help you
CameraActivity.java
public class CameraActivity extends Activity implements SurfaceHolder.Callback,
OnClickListener
{
Camera camera;
SurfaceHolder surfaceHolder;
boolean previewing = false;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getWindow().setFormat(PixelFormat.UNKNOWN);
surfaceView = (SurfaceView) findViewById(R.id.surfaceview);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3)
{
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder arg0)
{
// TODO Auto-generated method stub
Log.v(TAG, "surfaceCreated get called");
camera = Camera.open();
camera.setDisplayOrientation(90); //to get portrait display
if (camera != null) {
try {
//Here is the main logic
// We are setting camera parameters as our desired picture size
Camera.Parameters parameters = camera.getParameters();
List<Size> sizes = parameters.getSupportedPictureSizes();
Camera.Size result = null;
for (int i=0;i<sizes.size();i++)
{
result = (Size) sizes.get(i);
Log.i("PictureSize", "Supported Size. Width: " + result.width + "height : " + result.height);
if(result.width == 640)
{
parameters.setPreviewSize(result.width, result.height);//640*480
parameters.setPictureSize(result.width, result.height);
//Now if camera support for 640*480 pictures size you will get captured image as same size
}
else
{
//to do here
}
}
camera.setParameters(parameters);
camera.setPreviewDisplay(surfaceHolder);
camera.startPreview();
previewing = true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
// TODO Auto-generated method stub
Log.v(TAG, "surfaceDestroyed get called");
camera.stopPreview();
camera.release();
camera = null;
previewing = false;
}
}
Let me know your comment in this.

S3(GT-I9300) Camera preview holder bug

We created a custom camera based on android.hardware.Camera class.
When we press the button "take a photo", the expected behavior is to take the photo and see the still captured photo on a Review Screen. BUT , the camera still works / the viewfinder is showing a 'live' shot (instead of the still Review) when we get to the Review Screen.
It looks like the holder doesn't work.
This has been observed on some Samsung Galaxy S3 phones (GT-I9300); but strangely on all other models everything works fine (the still image appears in the Review Screen as it should).
My solution up to this point is to create a layout where a view takes up the same view as the surfaceview.
Then I get the view bounds for the preview (in my case it's the size of the screen)
//Get our screen size
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
DisplayMetrics metrics = getResources().getDisplayMetrics();
Point size = new Point();
try {
display.getSize(size);
w_width = size.x;
w_height = size.y;
} catch (NoSuchMethodError e) {
w_width = display.getWidth();
w_height = display.getHeight();
}
Then I set the callback to be:
callback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] bytes, Camera camera) {
//Get the view bounds to determine how to manipulate this image
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
//If the image is smaller than the screen, don't make the image bigger
int finalWidth = (w_width < options.outWidth) ? w_width : options.outWidth;
int finalHeight = (w_height < options.outHeight) ? w_height : options.outHeight;
int inSampleSize = 1;
options = new BitmapFactory.Options();
if (finalHeight > w_height || finalWidth > w_width) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) finalHeight / (float) w_height);
final int widthRatio = Math.round((float) finalWidth / (float) w_width);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
options.inSampleSize = inSampleSize;
//Decode the smaller image
Bitmap b = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
//Rotate the image correctly
Matrix mat = new Matrix();
mat.postRotate(orientation);
Bitmap newBitmap = Bitmap.createBitmap(b, 0, 0, options.outWidth, options.outHeight, mat, true);
imagePreview.setBackgroundDrawable(new BitmapDrawable(newBitmap));
imagePreview.setVisibility(View.VISIBLE);
}
};
So basically the surface view never stops showing the preview, but the imageview hides it so the user can't see it.
Then if the user decides not to keep the image then I just re-hide the view and the surfaceView will keep showing the live preview. Luckily the GS3 does not seem to mind someone calling "camera.startPreview();" even when the preview is already happening.
I really don't like this answer but it's all I have at the moment that works on the galaxy S3. It has worked on older devices (2.3 and up) that I have tried but it is definitely just a work-around.

Android WallpaperManager crops image

I am working on a simple wallpaper app of some images that I have. They are .png files in drawable folders.
Here are some code snippets:
WallpaperManager myWallpaperManager = WallpaperManager.getInstance(getApplicationContext());
....
myWallpaperManager.setResource(R.drawable.image1);
No matter what size or resolution I seem to use, when the wallpaper is set it crops the original image. When I use the same image as a background it is the correct size and shows the entire image. I thought it might be a problem with my emulator so I have tried running it on an actual device (HTC eris) and I am having the same problem. I have made the image the screen size and resolution for the eris and it is still cropping it. I then made the image only one inch high and a resolution of 100 dpi. It was very pixelated on the eris, but still cropped the image.
Any help would be greatly appreciated.
I attempted to add some images to show the before and after, but as a newer user I was prevented from doing so.
Check the values returned by http://developer.android.com/reference/android/app/WallpaperManager.html#getDesiredMinimumWidth() and http://developer.android.com/reference/android/app/WallpaperManager.html#getDesiredMinimumHeight() and try to use these values as the dimensions of your wallpaper.
Maybe I can help.
// 1. Get screen size.
DisplayMetrics metrics = new DisplayMetrics();
Display display = getWindowManager().getDefaultDisplay();
display.getMetrics(metrics);
final int screenWidth = metrics.widthPixels;
final int screenHeight = metrics.heightPixels;
// 2. Make the wallpaperManager fit the screen size.
final WallpaperManager wallpaperManager = WallpaperManager.getInstance(ViewWallpaperActivity.this);
wallpaperManager.suggestDesiredDimensions(screenWidth, screenHeight);
// 3. Get the desired size.
final int width = wallpaperManager.getDesiredMinimumWidth();
final int height = wallpaperManager.getDesiredMinimumHeight();
// 4. Scale the wallpaper.
Bitmap bitmap = getBitmap(); // getBitmap(): Get the image to be set as wallpaper.
Bitmap wallpaper = Bitmap.createScaledBitmap(bitmap, width, height, true);
// 5. Set the image as wallpaper.
try {
wallpaperManager.setBitmap(wallpaper);
} catch (IOException e) {
e.printStackTrace();
}
Note that you should call suggestDesiredDimensions, then call getDesiredMinimumWidth and getDesiredMinimumHeight to get the size to be scaled to.
I had the same problem. I made an image that is the size of the screen and adding padding to the image so that it is as large as the WallpaperManager getDesiredMinimumWidth() and getDesiredMinimumHeight().
It seemed better to have some code automatically add the padding so that is what I used below. Make sure the image is the same size as the screen.
private void setWallpaper() {
try {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
//import non-scaled bitmap wallpaper
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap wallpaper = BitmapFactory.decodeResource(getResources(), R.drawable.wallpaper, options);
if (wallpaperManager.getDesiredMinimumWidth() > wallpaper.getWidth() &&
wallpaperManager.getDesiredMinimumHeight() > wallpaper.getHeight()) {
//add padding to wallpaper so background image scales correctly
int xPadding = Math.max(0, wallpaperManager.getDesiredMinimumWidth() - wallpaper.getWidth()) / 2;
int yPadding = Math.max(0, wallpaperManager.getDesiredMinimumHeight() - wallpaper.getHeight()) / 2;
Bitmap paddedWallpaper = Bitmap.createBitmap(wallpaperManager.getDesiredMinimumWidth(), wallpaperManager.getDesiredMinimumHeight(), Bitmap.Config.ARGB_8888);
int[] pixels = new int[wallpaper.getWidth() * wallpaper.getHeight()];
wallpaper.getPixels(pixels, 0, wallpaper.getWidth(), 0, 0, wallpaper.getWidth(), wallpaper.getHeight());
paddedWallpaper.setPixels(pixels, 0, wallpaper.getWidth(), xPadding, yPadding, wallpaper.getWidth(), wallpaper.getHeight());
wallpaperManager.setBitmap(paddedWallpaper);
} else {
wallpaperManager.setBitmap(wallpaper);
}
} catch (IOException e) {
Log.e(TAG,"failed to set wallpaper");
}
}

Save image overlay with camera captured image underneith

My application has a "photobooth" feature which will allow the user to take a picture with the camera and at the same time show an overlay image on top of the camera view. After the picture is taken, i need to save what the user saw while taking the picture to the filesystem.
I have experienced 1 big problem while developing a solution to this: capturing an image with the compatible dimensions in which i can attach an overlay image to resulting in what the user saw while taking the picture.
It seems i cannot capture an image from the camera with defined dimensions(i have to basically pick from a list of them). Some phones only can produce certain dimensions.
Since i cannot choose the size of the captured image, it seems as though i will be required to include many different sizes of the overlay image, and attach the best match to the captured image. I can't just slap any old overlay on top of the camera image and make it look right.
Questions:
Am i over-complicating this "camera image + overlay image creation" process?
What suggestions do you have in completing this task without the need of including several different sizes overlay images?
Edit:
Here is my solution(brief). Please realize this is not a perfect and maybe not most efficient way to do this, but it works. Some things may be unnecessary/redundant but whatever!
Notes:
this doesn't work too great on tablet devices.
the overlay image needs to be rotated to be in landscape mode(even though you will be taking the image holding the phone in portrait)
overlay size is 480x320
you need to force the activity to landscape mode while taking the picture(now the overlay looks like its portrait!)
i add the overlay image view using addContentView(overlayImageView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
...
final Camera.PictureCallback jpegCallback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap mutableBitmap = null;
try {
//for a PORTRAIT overlay and taking the image holding the phone in PORTRAIT mode
mutableBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options).copy(Bitmap.Config.RGB_565, true);
Matrix matrix = new Matrix();
int width = mutableBitmap.getWidth();
int height = mutableBitmap.getHeight();
int newWidth = overlayImage.getDrawable().getBounds().width();
int newHeight = overlayImage.getDrawable().getBounds().height();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
matrix.postScale(scaleWidth, scaleHeight);
matrix.postRotate(90);
Bitmap resizedBitmap = Bitmap.createBitmap(mutableBitmap, 0, 0, mutableBitmap.getWidth(), mutableBitmap.getHeight(), matrix, true);
finalBitmap = resizedBitmap.copy(Bitmap.Config.RGB_565, true);
Canvas canvas = new Canvas(finalBitmap);
Bitmap overlayBitmap = BitmapFactory.decodeResource(getResources(), overlay);
matrix = new Matrix();
matrix.postRotate(90);
Bitmap resizedOverlay = Bitmap.createBitmap(overlayBitmap, 0, 0, overlayBitmap.getWidth(), overlayBitmap.getHeight(), matrix, true);
canvas.drawBitmap(resizedOverlay, 0, 0, new Paint());
canvas.scale(50, 0);
canvas.save();
//finalBitmap is the image with the overlay on it
}
catch(OutOfMemoryError e) {
//fail
}
}
}
I think this is a question of how you manipulate your overlays. You can crop it according to the captured image size and resize it to fit, preserving its ratio. You can place the overlay, by comparing its ratio to the backround ratio, to its optimal position.
I would keep overlays big enough, with a wide border (bleed), to easily size them to an image using filters to draw it with good qaulity. I guess overlays are something which you would design and have transparent parts, like an image of a clown without a face so the user can snap somebody elses face into it?

Categories

Resources