Save image overlay with camera captured image underneith - android

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?

Related

Android: Image is too big when using canvas drawBitmap

So here is what my app is doing:
I have a camera preview.
I have an overlay image on top of this camera preview.
When I capture the image I combine both the captured image and the overlay.
I display the combined imaged on an imageview.
However, the overlay is way too big when it's displayed on the imageview.
The overlay image is displayed perfectly when on the camera preview. It looks like the canvas.drawBitmap is scaling it somehow but I want to keep the original size.
IMPORTANT: This works fine on a Samsung S3, the issue occurs when using a Moto G.
Here's some of the code:
protected PictureCallback mPicture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
cameraPreview.getCamera().stopPreview();
Bitmap cameraBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
Matrix matrix = new Matrix();
matrix.preScale(-1, 1);
matrix.postRotate(90);
cameraBitmap = Bitmap.createBitmap(cameraBitmap, 0, 0, cameraBitmap.getWidth(), cameraBitmap.getHeight(), matrix, true);
//Get the overlay image
ImageView imageview = (ImageView)findViewById(R.id.imageView2);
imageview.setDrawingCacheEnabled(true);
imageview.buildDrawingCache();
Bitmap overlayImage = Bitmap.createBitmap(imageview .getDrawingCache());
Bitmap mutableBitmap = cameraBitmap.copy(Bitmap.Config.ARGB_8888, true);
//Here is where I combine both images by drawing the overlay onto the captured bitmap
Canvas canvas = new Canvas(mutableBitmap);
canvas.drawBitmap(overlayImage , 0 , canvas.getHeight() - overlayImage.getHeight(), paint);
//Here is where i set the image so that you can view the finished result - the combined image - but the overlay image is way too big!
ImageView capturedView = (ImageView)findViewById(R.id.capturedImageView);
capturedView.setImageBitmap(mutableBitmap);
Solved it! The solution was to forget about the canvas altogether and just get a screenshot of capturedView since it already had the captured image and the overlay on it.
Also, I had make sure to bring the overlay to front before getting the screenshot of capturedView.
// This is the imageview where I first display the
// image which was captured from the camera.
capturedView.bringToFront();
// This is the overlay imageview,
// by bringing this in front of the capturedView
// I can take a screenshot of it and get both the captured
// image and the overlay image.
imageview.bringToFront();
capturedView.setDrawingCacheEnabled(true);
capturedView.buildDrawingCache();
// And now you have a bitmap with both images combined :-)
combinedBitmap = capturedView.getDrawingCache();

How rotate picture from "onPictureTaken" without Out of memory Exception?

I read many posts there? But i don't find correctly answer.
I try do something this:
#Override
public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera) {
try {
Bitmap bitmap = BitmapFactory.decodeByteArray(paramArrayOfByte, 0,
paramArrayOfByte.length);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
FileOutputStream os = new ileOutputStream(Singleton.mPushFilePath);
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,
height, matrix, false);
resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 95, os);
os.close();
...
Is there a way to rotate picture, without using BitmapFactory? I want rotate picture without loss of quality!
Perhaps you can take the picture already rotated as you desire using Camera.setDisplayOrientation? Check Android camera rotate. Further, investigate Camera.Parameters.setRotation(). One of these techniques should do the trick for you.
Otherwise your code looks fine except for using parameter 95 on Bitmap.compress, you need to use 100 for lossless compression.
To avoid out-of-memory exception, use Camera.Parameters.setPictureSize() to take a lower resolution picture (e.g. 3Mpx). i.e. do you really need an 8Mpx photo? Make sure to use Camera.Parameters.getSupportedPictureSizes() to determine the supported sizes on your device.

Set large image as SurfaceView's background so that it suits the display height in Android

I'm using a SurfaceView in Android. The application is always in landscape mode.
In my res/drawable folder I have a large background that is also in landscape mode but larger than most display sizes.
Now I would like to set it as the background of the SurfaceView so that it perfectly fits into the display. So the image's height should be equal to the display's height.
How do I do that? Do I have to use alternative resources for ldpi, mdpi, hdpi and xhdpi? But even then I don't know the exact height of the display.
I'm currently using:
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.background), 0, 0, null);
I tried to resize the bitmap using a matrix. But then the quality of the image becomes really poor, doesn't it?
(Of course, the solution should be performant, too.)
protected Bitmap scaled = null;
public void surfaceCreated(SurfaceHolder arg0) {
Bitmap background = BitmapFactory.decodeResource(getResources(), R.drawable.background);
float scale = (float)background.getHeight()/(float)getHeight();
int newWidth = Math.round(background.getWidth()/scale);
int newHeight = Math.round(background.getHeight()/scale);
Bitmap scaled = Bitmap.createScaledBitmap(background, newWidth, newHeight, true);
}
public void onDraw(Canvas canvas) {
canvas.drawBitmap(scaled, 0, 0, null); // draw the background
}

Change Camera settings from another App's Activity

I have an Android app that calls the native camera app to take a picture and returns the image for further manipulation. My problem, is that I run into memory leaks if the camera is set to 2(+) megapixels. Ideally, I want it set to the lowest (VGA) since image quality is not a concern with this app.
Is there a way from my app to change the settings of the native device's camera app? Here is the code I am using:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
mImageCaptureUri =
Uri.fromFile(new file(Environment.getExternalStorageDirectory(),
"fname_" + String.valueOf(System.currentTimeMillis()) + ".jpg"));
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
Any help would be appreciated.
Unfortunately there is no way of telling the camera application what picture resolution you want to take the picture at.
But you can however do something about it yourself in your app by acesssing some bitmap functionalities like (2nd option would be more suited for your needs)
Downsampling. Sample size should be greater than 1. Try 2 and 4.
BitmapFactoryOptions.inSampleSize = sampleSize;
Creating a new bitmap with the size that you require from the original bitmap..
// calculate the change in scale
float scaleX = ((float) newWidth_that_you_want) / originalBitmap.width();
float scaleY = ((float) newHeight_that_you_want) / originalBitmap.height();
// createa matrix for the manipulation
Matrix matrix = new Matrix();
matrix.postScale(scaleX , scaleY );
Bitmap newBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, width, height, matrix, true);
//since you don't need this bitmap anymore, mark it so that GC can reclaim it.
//note: after recycle you should not use the originalBitmap object anymore.
//if you do then it will result in an exception.
originalBitmap.recycle();

Bitmap resizing and rotating: linear noise

I am resizing image and rotating it using Matrix:
Matrix mtx = new Matrix();
if(orientation>0) {
mtx.postRotate(orientation);
Log.d(TAG,"image rotated: "+orientation);
}
if(scale<1) {
mtx.postScale(scale,scale);
Log.d(TAG,"image scaled: "+scale);
}
bmp = Bitmap.createBitmap(bm_orig, 0, 0, width, height, mtx, true);
bm_orig.recycle();
bmp.compress(Bitmap.CompressFormat.JPEG,95,output);
bmp.recycle();
When bmp_orig is taken, used 3.2 Mpx Camera, image resized and rotated looks normal.
But when source is 4 Mpx or bigger, result after resizing has barely-noticeable linear noise
I dont know, why does this noise appear, and how to remove it.
Any idea?
May be another way to resize and rotate?
Found that this problem is related with source and resulting image size.
Solved it, when check image size before loading it, and then load halfsized image, if source image size is more than 2 times bigger than resulting size is needed.
BitmapFactory.Options options_to_get_size = new BitmapFactory.Options();
options_to_get_size.inJustDecodeBounds = true;
BitmapFactory.decodeStream(input, null, options_to_get_size);
int load_scale = 1; // load 100% sized image
int width_tmp=options_to_get_size.outWidth
int height_tmp=options_to_get_size.outHeight;
while(width_tmp/2>maxW && height_tmp/2>maxH){
width_tmp/=2;//load half sized image
height_tmp/=2;
load_scale*=2;
}
Log.d(TAG,"load inSampleSize: "+ load_scale);
//Now load image with precalculated scale. scale must be power of 2 (1,2,4,8,16...)
BitmapFactory.Options option_to_load = new BitmapFactory.Options();
option_to_load.inSampleSize = load_scale;
((FileInputStream)input).getChannel().position(0); # reset input stream to read again
Bitmap bm_orig = BitmapFactory.decodeStream(input,null,option_to_load);
input.close();
//now you can resize and rotate image using matrix as stated in question

Categories

Resources