Take a picture every 20 seconds forever - android

I have an old Android device (Android 4.1.2, API 16) which I want to use as a "CCTV-like" device. I need to take a picture every 20 seconds and to do this I have implemented the following code on an Activity.
The onCreate() method is as the following:
//To keep the display on (the phone is always attached to the power supply)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
FrameLayout preview = findViewById(R.id.camera_preview);
initializeCamera();
Timer timerPhoto = new Timer();
timerPhoto.schedule(new TimerTask() {
#Override
public void run() {
mCamera.takePicture(null, null, mPictureCallback);
mCamera.startPreview();
}
}, 5000, 20000); //start after 5 seconds, take pic every 20 secs
The following method for the camera initialisation:
private void initializeCamera() {
if (mCamera == null) {
mCamera = getCameraInstance();
Timber.d("Camera initialized");
}
mCameraPreview = new IaCameraHelper(this, mCamera);
preview.addView(mCameraPreview);
}
And this is the callback which handles the picture:
private static Bitmap originalPhotoBitmap = null;
Camera.PictureCallback mPictureCallback = (data, camera) -> {
originalPhotoBitmap = addDateTimeToImage(rotateBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), 180));
//Sending the bitmap string to the server
controller.sendImage(bitmapToString(originalPhotoBitmap));
originalPhotoBitmap.recycle();
BitmapManager.rotatedBitmap.recycle();
BitmapManager.modBitmap.recycle();
originalPhotoBitmap = null;
BitmapManager.rotatedBitmap = null;
BitmapManager.modBitmap = null;
};
In the BitmapManager, I have the following methods which have been used to manipulate the bitmap:
public static Bitmap rotatedBitmap = null;
public static Bitmap modBitmap = null;
public static Bitmap rotateBitmap(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
rotatedBitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
return rotatedBitmap;
}
public static Bitmap addDateTimeToImage(Bitmap originalPhoto) {
modBitmap = Bitmap.createBitmap(originalPhoto.getWidth(), originalPhoto.getHeight(), Bitmap.Config.ARGB_8888);
String dateTime = sdf.format(Calendar.getInstance().getTime());
Canvas cs = new Canvas(modBitmap);
Paint tPaint = new Paint();
tPaint.setTextSize(30);
tPaint.setColor(Color.RED);
tPaint.setStyle(Paint.Style.FILL);
cs.drawBitmap(originalPhoto, 0f, 0f, null);
float height = tPaint.measureText("yY");
cs.drawText(dateTime, 20f, height + 15f, tPaint);
return modBitmap;
}
public static String bitmapToString(Bitmap bitmap) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] b = baos.toByteArray();
return Base64.encodeToString(b, Base64.DEFAULT);
}
As you can see, I send the bitmap string to a controller which sends the image to a local server in the network.
Generally, the application works like a charm, but sometimes (generally after 6-7 hours) I face the following errors (out-of-memory) that I don't know how to solve.
02-21 13:12:03.292 29621-29632/com.rbapp E/dalvikvm-heap: Out of memory on a 127547-byte allocation.
02-21 13:12:03.302 29621-29632/com.rbapp E/Camera-JNI: Couldn't allocate byte array for JPEG data
As you can see, I try to recycle all the generated bitmaps and I set them to null as soon as I send them to the controller, but the error persists.
When the errors occur, the application continues to run (also the camera preview), but the images are not sent (there are try-catches which allows the execution of the app) but as I press the "back" button on the phone, the application doesn't respond at all.

Related

Can not get camera output, face detection android

I am trying face detection and adding mask(graphic overlay) using google vision api ,the problem is i could not get the ouptut from camera after detecting and adding mask.so far I have tried this solution from github , https://github.com/googlesamples/android-vision/issues/24 ,based on this issue i have added a custom detector class,
Mobile Vision API - concatenate new detector object to continue frame processing . and added this on mydetector class How to create Bitmap from grayscaled byte buffer image? .
MyDetectorClass
class MyFaceDetector extends Detector<Face>
{
private Detector<Face> mDelegate;
MyFaceDetector(Detector<Face> delegate) {
mDelegate = delegate;
}
public SparseArray<Face> detect(Frame frame) {
// *** add your custom frame processing code here
ByteBuffer byteBuffer = frame.getGrayscaleImageData();
byte[] bytes = byteBuffer.array();
int w = frame.getMetadata().getWidth();
int h = frame.getMetadata().getHeight();
YuvImage yuvimage=new YuvImage(bytes, ImageFormat.NV21, w, h, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
yuvimage.compressToJpeg(new Rect(0, 0, w, h), 100, baos); // Where 100 is the quality of the generated jpeg
byte[] jpegArray = baos.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(jpegArray, 0, jpegArray.length);
Log.e("got bitmap","bitmap val " + bitmap);
return mDelegate.detect(frame);
}
public boolean isOperational() {
return mDelegate.isOperational();
}
public boolean setFocus(int id) {
return mDelegate.setFocus(id);
}
}
frame processing
public SparseArray<Face> detect(Frame frame)
{
// *** add your custom frame processing code here
ByteBuffer byteBuffer = frame.getGrayscaleImageData();
byte[] bytes = byteBuffer.array();
int w = frame.getMetadata().getWidth();
int h = frame.getMetadata().getHeight();
YuvImage yuvimage=new YuvImage(bytes, ImageFormat.NV21, w, h, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
yuvimage.compressToJpeg(new Rect(0, 0, w, h), 100, baos); // Where 100 is the quality of the generated jpeg
byte[] jpegArray = baos.toByteArray();
Bitmap bitmap = BitmapFactory.decodeByteArray(jpegArray, 0, jpegArray.length);
Log.e("got bitmap","bitmap val " + bitmap);
return mDelegate.detect(frame);
}
i am getting a rotated bitmap ,that is without the mask (graphic overlay) i have added .How can i get the camera output with mask .
Thanks in advance.
The simple answer is: You can't.
Why? Android camera output frames in NV21 ByteBuffer. And you must generate your masks based on the landmarks points in a separated Bitmap, then join them.
Sorry but, that's how the Android Camera API work. Nothing can be done. You must do it manually.
Also, I wouldn't get the camera preview then convert it to YuvImage then to Bitmap. That process consumes a lot of resources and makes preview very very slow. Instead I would use this method which will be a lot faster and rotates your preview internally so you don't loose time doing it:
outputFrame = new Frame.Builder().setImageData(mPendingFrameData, mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.NV21)
.setId(mPendingFrameId)
.setTimestampMillis(mPendingTimeMillis)
.setRotation(mRotation)
.build();
mDetector.receiveFrame(outputFrame);
All the code can be found in CameraSource.java

How to get frame of SurfaceView each second?

I want to take screenshot from camera preview each second.
I show preview of my Camera using SurfaceView. I need to get preview each second(screenshot) but not using photo taking.
I know about method mCamera.setPreviewCallbackWithBuffer but I can take frame only one time from it. To make it updating every second I need to start MediaRecorder and record video. But For video I need to set outputFile, which means that it can use a lot of memory.
mCamera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
Camera.Parameters parameters = camera.getParameters();
int width = parameters.getPreviewSize().width;
int height = parameters.getPreviewSize().height;
YuvImage yuv = new YuvImage(data, parameters.getPreviewFormat(), width, height, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0, 0, width, height), 50, out);
byte[] bytes = out.toByteArray();
final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
}
});
How can I do it without setting outputfile and taking photo each second?
If you are using SurfaceView to display your camera preview you may try calling Camera#takePicture every second.
To schedule it approximately every one second, you may use postDelayed method of any view. For example:
private Runnable capturePreview = new Runnable() {
#Override
public void run() {
camera.takePicture(null, null, callback);
// Run again after approximately 1 second.
surfaceView.postDelayed(this, 1000);
}
}
private Camera.PictureCallback callback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
// Do whatever you need with your bitmap.
// Consider freeing the memory afterwards.
bitmap.recycle();
}
}
And you can start it whenever preview is ready by calling:
surfaceView.postDelayed(capturePreview, 1000);
And stop whenever preview is no longer displayed:
surfaceView.removeCallbacks(capturePreview);
If you are using TextureView you can simply use getBitmap() which allows you to easily grab its current contents.
Therefore the code above becomes something like:
private Runnable capturePreview = new Runnable() {
#Override
public void run() {
Bitmap preview = textureView.getBitmap();
// Do whatever you need with the bitmap.
// Run again after approximately 1 second.
textureView.postDelayed(this, 1000);
}
}, 1000);
And again start:
textureView.postDelayed(capturePreview, 1000);
and stop:
textureView.removeCallbacks(capturePreview);

Combine two images into one orientation issue

I want to make an app where user can take picture with back camera, and then with front camera.
So, after that I get two bitmaps and I want to combine them into one image.
This code I use for front Camera parameters:
//Set up picture orientation for saving...
Camera.Parameters parameters = theCamera.getParameters();
parameters.setRotation(90);
frontCamera.setParameters(parameters);
//Set up camera preview and set orientation to PORTRAIT
frontCamera.stopPreview();
frontCamera.setDisplayOrientation(90);
frontCamera.setPreviewDisplay(holder);
frontCamera.startPreview();
This code I use for taking picture with front camera
cameraObject.takePicture(shutterCallback, rawCallback, jpegCallback);
Callback for taking picture with back camera
PictureCallback jpegCallback = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
backBitmap = decodeBitampFromByte(data, 0, 800, 800);
frontCameraObject.release();
initFrontCamera();
}
};
NOTE: Similar code is for taking picture with front camera. I get two bitmaps, and then I try to combine them with code below, but I get saved bitmap with wrong orientation.
This code I use for combing two bitamps: frontBitmap, backBitmap.
public Bitmap combineImages(Bitmap c, Bitmap s, String loc)
{
Bitmap cs = null;
int w = c.getWidth() + s.getWidth();
int h;
if(c.getHeight() >= s.getHeight()){
h = c.getHeight();
}else{
h = s.getHeight();
}
cs = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas comboImage = new Canvas(cs);
comboImage.drawBitmap(c, 0f, 0f, null);
comboImage.drawBitmap(s, c.getWidth, 0f, null);
String tmpImg = String.valueOf(System.currentTimeMillis()) + ".png";
OutputStream os = null;
try {
os = new FileOutputStream(loc + tmpImg);
cs.compress(CompressFormat.PNG, 100, os);
} catch (IOException e) {
Log.e("combineImages", "problem combining images", e);
}
return cs;
}
NOTE Image with bottle of the water is taken with back camera, and other is with front camera.
Try changing comboImage.drawBitmap(s, c.getWidth, 0f, null); to
comboImage.drawBitmap(s, 0f,c.getHeigh, null);

Rotating images on android. Is there a better way?

I have an app that displays quite a few images for the user, and we've been seeing a lot of error reports with OutOfMemoryError exception.
What we currently do is this:
// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
// Rotate it to show as a landscape
Matrix m = image.getImageMatrix();
m.postRotate(90);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
}
image.setImageBitmap(bmp);
The obvious problem with this is that we have to recreate the bitmap from the image on memory and rotate the matrix, this is quite expensive for the memory.
My question is simple:
Is there a better way to rotate images without causing OutOfMemoryError?
2 methods of rotating a large image:
using JNI , like on this post.
using a file : it's a very slow way (depending on the input and the device , but still very slow) , which puts the decoded rotated image into the disk first , instead of putting it into the memory .
code of using a file is below:
private void rotateCw90Degrees()
{
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),INPUT_IMAGE_RES_ID);
// 12 => 7531
// 34 => 8642
// 56 =>
// 78 =>
final int height=bitmap.getHeight();
final int width=bitmap.getWidth();
try
{
final DataOutputStream outputStream=new DataOutputStream(new BufferedOutputStream(openFileOutput(ROTATED_IMAGE_FILENAME,Context.MODE_PRIVATE)));
for(int x=0;x<width;++x)
for(int y=height-1;y>=0;--y)
{
final int pixel=bitmap.getPixel(x,y);
outputStream.writeInt(pixel);
}
outputStream.flush();
outputStream.close();
bitmap.recycle();
final int newWidth=height;
final int newHeight=width;
bitmap=Bitmap.createBitmap(newWidth,newHeight,bitmap.getConfig());
final DataInputStream inputStream=new DataInputStream(new BufferedInputStream(openFileInput(ROTATED_IMAGE_FILENAME)));
for(int y=0;y<newHeight;++y)
for(int x=0;x<newWidth;++x)
{
final int pixel=inputStream.readInt();
bitmap.setPixel(x,y,pixel);
}
inputStream.close();
new File(getFilesDir(),ROTATED_IMAGE_FILENAME).delete();
saveBitmapToFile(bitmap); //for checking the output
}
catch(final IOException e)
{
e.printStackTrace();
}
}
you can try:
image.setImageBitmap(null);
// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
// Rotate it to show as a landscape
Matrix m = image.getImageMatrix();
m.postRotate(90);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
}
BitmapDrawable bd = new BitmapDrawable(mContext.getResources(), bmp);
bmp.recycle();
bmp = null;
setImageDrawable(bd);
bd = null;
When working with lots of Bitmaps be sure to call recycle() on them as soon as they are not needed. This call will instantly free memory associated with a particular bitmap.
In your case if you do not need the original bitmap after rotation, then recycle it. Something along the lines of:
Bitmap result = bmp;
// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
// Rotate it to show as a landscape
Matrix m = image.getImageMatrix();
m.postRotate(90);
result = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
// rotating done, original not needed => recycle()
bmp.recycle();
}
image.setImageBitmap(result);

Function to Overlay Bitmap not working properly

I am Using the Function to Mearge the Two Bitmap File on One Another and it also overlay.
I am using this Function to Overlay it on OneAnother.
public static Bitmap combineImages(Bitmap cameraImage, Bitmap visionImage) { // can add a 3rd parameter 'String loc' if you want to save the new image - left some code to do that at the bottom
Bitmap finalImage = null;
int width, height = 0;
width = cameraImage.getWidth();
height = cameraImage.getHeight();
finalImage = Bitmap.createBitmap(width, height, cameraImage.getConfig());
Canvas canvas = new Canvas(finalImage);
canvas.drawBitmap(cameraImage, new Matrix(), null);
canvas.drawBitmap(visionImage, new Matrix(), null);
// this is an extra bit I added, just incase you want to save the new image somewhere and then return the location
/*String tmpImg = String.valueOf(System.currentTimeMillis()) + ".png";
OutputStream os = null;
try {
os = new FileOutputStream(loc + tmpImg);
finalImage.compress(CompressFormat.PNG, 100, os);
} catch(IOException e) {
Log.e("combineImages", "problem combining images", e);
}*/
return finalImage;
}
But After saving this Image I show that images to be combine with each other. it is not overlay. I want it to be Overlay on One Another.
Please tell me where i am wrong in this Function ??
Thanks.
this is the function to overlay two bitmap,s
private Bitmap overlayMark(Bitmap bmp1, Bitmap bmp2) {
int bh = originalBitmap.getHeight();
int bw = originalBitmap.getWidth();
Bitmap bmOverlay = Bitmap.createBitmap(bw,bh,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bmp1, 0, 0, null);
canvas.drawBitmap(bmp2, 0,0, null);
return bmOverlay;
}

Categories

Resources