I am developing a camerax app for the image analysis use case. I built it and ran on my phone and it worked perfectly. I also tried it on a few other phones and it worked on them too. We actually plan to use the app on our raspberry pi 3B which has lineageOS installed and has necessary cameras attached and we have also tried running other apps that use camera on it and it worked perfectly. But, when I ran our app on it, it started, showed the animation that it was supposed to show and nothing else happened, like the app didn't crash, we also noticed the camera turning on and off a few times, but that's it. From the logs, I could see the app never runs the code in the setAnalyzer() block. Here's the code for the relevant part.
model=new LocalModel.Builder()
.setAssetFilePath("model.tflite")
.build();
CustomImageLabelerOptions customImageLabelerOptions=new CustomImageLabelerOptions.Builder(model)
.setConfidenceThreshold(.98f)
.setMaxResultCount(1)
.build();
labeler= ImageLabeling.getClient(customImageLabelerOptions);
analysis=new ImageAnalysis.Builder().setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setTargetResolution(new Size(1280, 720))
.build();
Log.i(TAG, "before the analysis starts");
analysis.setAnalyzer(ContextCompat.getMainExecutor(this), image -> {
Log.i(TAG, "Analysis started");
int rotationDegrees = image.getImageInfo().getRotationDegrees();
#SuppressLint("UnsafeOptInUsageError") Image mediaImage=image.getImage();
if(mediaImage!=null){
InputImage i=InputImage.fromMediaImage(mediaImage, rotationDegrees);
labeler.process(i).addOnSuccessListener(new OnSuccessListener<List<ImageLabel>>() {
#Override
public void onSuccess(#NonNull #NotNull List<ImageLabel> imageLabels) {
Log.i(TAG, "successfully started to process the image");
String labels="";
for(ImageLabel label: imageLabels){
Log.i(TAG, label.getText());
labels=label.getText();
Log.i(TAG, labels);
}
if(labels.equalsIgnoreCase("dew")) {
isDetectingBottles=true;
d.stop();
imageView.setBackgroundResource(R.drawable.unnffffamed);
}
else{
if(isDetectingBottles){
isDetectingBottles=false;
imageView.setBackgroundResource(R.drawable.blink_animation);
d= (AnimationDrawable) imageView.getBackground();
d.start();
}
}
image.close();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull #NotNull Exception e) {
Log.i(TAG, "ImageLabeling process failed");
e.printStackTrace();
image.close();
}
});
}
});
CameraSelector selector= CameraSelector.DEFAULT_BACK_CAMERA;
Log.i(TAG, "before binding to lifecycle");
ListenableFuture<ProcessCameraProvider> m=ProcessCameraProvider.getInstance(this);
m.addListener(new Runnable() {
#Override
public void run() {
try {
m.get().bindToLifecycle((LifecycleOwner) MainActivity.this, selector, analysis);
}
catch(InterruptedException e){
e.printStackTrace();
}
catch(ExecutionException e){
e.printStackTrace();
}
}
}, ContextCompat.getMainExecutor(this));
Here are a few thing I tried:
At the beginning I didn't set any target resolution and left it to camerax but it still didn't work back then.
I tried using different cameras with the selector, using the front camera leads to a crash.
Tried calling image.close() at the end of setAnalyzer block, but that's obviously not the issue since it works on other devices.
NOTE: the logs don't show any exceptions as for me to get an idea of what's happening.
Related
Let me start by saying that if image shooting interval is anything more than 1 second it works. For example taking a picture every 2 seconds works perfectly fine. But taking a picture every second sometimes throws java.lang.RuntimeException: takePicture failed. What could be causing this kind of a behaviour?
Here is the code I use and it is in Service:
#Override
public void onCreate()
{
super.onCreate();
prefs = getSharedPreferences("general",Context.MODE_PRIVATE);
handler = new Handler();
shotInterval = prefs.getInt(getString(R.string.prefs_int_imageShootingFrequency),1);
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
Toast.makeText(this, "No camera on this device", Toast.LENGTH_LONG).show();
} else {
cameraId = findBackFacingCamera();
if (cameraId < 0) {
Toast.makeText(this, "No front facing camera found.",Toast.LENGTH_LONG).show();
} else {
camera = Camera.open(cameraId);
}
}
cameraParameters = camera.getParameters();
cameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); //set camera to continuously auto-focus
camera.setParameters(cameraParameters);
pictureTaker.run(); // Start looping
}
Runnable pictureTaker = new Runnable() {
#Override
public void run() {
try {
takePicture();
} finally {
// 100% guarantee that this always happens, even if
// your update method throws an exception
handler.postDelayed(pictureTaker, shotInterval*1000);
}
}
};
private void takePicture(){
SurfaceView view = new SurfaceView(this);
try {
camera.setPreviewDisplay(view.getHolder());
camera.startPreview();
camera.takePicture(null, null,new PhotoHandler(getApplicationContext()));
} catch (IOException e) {
e.printStackTrace();
}
}
You should launch postDelayed() from the onPictureTaken() callback. You can check the system timer on call to takePicture() and reduce the delay respectively, to keep 1000ms repetition, but maybe once in a while, this delay will reach 0.
This is the code I am using for face detection, the problem is when I debug this code with android studio the onFaceDetection method is called multiple times and face is detected perfectly(When i put a break point inside the method). But when I run it without any break points the method is called only 2-3 times and face detection doesn't take place. Any help regarding this would be much appreciated, as you can see from the code I've tried stopping and starting face detection.
void setFaceDetectionListener() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mFaceDetectionListener = new Camera.FaceDetectionListener() {
Handler faceDetectionHandler;
#Override
public void onFaceDetection(final Camera.Face[] faces, final Camera camera) {
if(faceDetectionHandler == null){//Initialize
faceDetectionHandler = new Handler();
Toast.makeText(HWTestActivity.this,
UiMessages.MSG_SHOW_YOUR_FACE.toString(),
Toast.LENGTH_SHORT).show();
}
faceDetectionHandler.post(new Runnable() {
#Override
public void run() {
Log.e("faceDetect", "No of faces = " + faces.length);
if (!is_face_detected) {
Toast.makeText(HWTestActivity.this,
UiMessages.MSG_DETECTING_YOUR_FACE.toString(),
Toast.LENGTH_SHORT).show();
is_face_detected = faces.length > 0;
}
if (faces.length > 0) {
Toast.makeText(HWTestActivity.this,
UiMessages.MSG_FACE_DETECTED.toString(),
Toast.LENGTH_SHORT).show();
camera.stopFaceDetection();
} else {
camera.stopFaceDetection();
camera.startFaceDetection();
}
}
});
}
};
}
}
This was ignorance on my part, apparently you can't have face detection running while the media recorder is running, So guys don't try to run face detection while you are recording with the camera simultaneously.
If you really wanted to detect faces while recording then you should use the
onPreviewFrame(byte[] pixelData, Camera camera)
method in
Camera.PreviewCallback()
convert the pixelData to RGB_565 bitmap and supply it to the FaceDetector.findfaces method. But in my experience I find this method to be very unreliable.
I am trying to make camera application which takes 3 continuous shots.
i have tried to call "takePicture" several times by putting it in a loop.
but no success.
please help on this matter.
a little help will be appreciated.
You never should call PictureCallback.onPictureTaken() from your code; this callback receives data from the system when it is ready, as response to Camera.takePicture().
The latter call will only succeed if the camera is opened and preview is started. Therefore, simply calling Camera.takePicture() in a loop will not work (see e.g. Android 2.3.1 Camera takePicture() Multiple images with one button click). The correct way to handle this is to keep a counter of shots processed in your onPictureTaken(), and if it is less than 3, then restart camera preview and issue (synchroneously) another Camera.takePicture(). After this, onPictureTaken() should return, to allow processing of the next captured frame.
I use it like this when doing a PhotoBurst. It is also handling the FRameLayout holding the preview to start the PhotoBurst:
PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream outStream = null;
try {
Parameters param = camera.getParameters();
param.setPictureSize(640, 480);
camera.setParameters(param);
// Or write to sdcard
outStream = new FileOutputStream(String.format(
Environment.getExternalStorageDirectory().getPath()+"/foto%d.jpg",
System.currentTimeMillis()));
outStream.write(data);
outStream.close();
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.fromFile(Environment.getExternalStorageDirectory())));
Log.i(TAG, "onPictureTaken - wrote bytes: " + data.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
Log.d(TAG, "onPictureTaken - jpg");
try {
stillCount++;
camera.startPreview();
if (stillCount < 10) {
preview.mCamera.takePicture(shutterCallback, rawCallback,
jpegCallback);
if (stillCount == 9) {
frameLayout.setClickable(true);
}
} else {
stillCount = 0;
takePictureButton.setEnabled(true);
frameLayout.setClickable(true);
}
} catch (Exception e) {
Log.d(TAG, "Error starting preview: " + e.toString());
}
}
};
I got the solution.
i was calling mCamera.startPreview(); out of my loop.
preview is must to take shots, and not including mCamera.startPreview(); was blocking my execution.
trying to get my first android widget and let me tell you moving from Visual Studio Pro to eclipse is not a smooth thing!
Anyway i run into the above error which gives me some trouble to solve. Ive searched for all possible duplicates and it seems that this error occurs when the app hits IPC size (ie. big images)
My small widget launches an intentservice to download some info from the web and one image. the original image size of the png is around 100k. However before i update widget I downscale to around 17k. (checked with memory tracker size was 16400bytes[]).
and yet the update fails. if i remove the image the update is successful.
this is the listener in widgetprovider which get executed:
public void onHttpLoaded(Bitmap image, String titlestring, String subtitlestring)
{
Context context = getApplicationContext();
if (image == null) //no data?
{
Toast.makeText(context, context.getResources().getString(R.string.null_data_toast), Toast.LENGTH_LONG);
return;// exit!
}
try
{
RemoteViews widgetView = new RemoteViews(this.getPackageName(), R.layout.main);
widgetView.setTextViewText(R.id.title, titlestring);
//all other comment out
//The problem!
widgetView.setImageViewBitmap(R.id.main_icon, image);
//Get global appwidgetmanager
AppWidgetManager manager = AppWidgetManager.getInstance(this);
//update widget
manager.updateAppWidget(Constants.THIS_APPWIDGET, widgetView);
}
catch (Exception e)
{
Log.d("onHttpLoaded", e.toString());
}
}
and the onHandleIntent from my service that the above code gets called:
protected void onHandleIntent(Intent intent)
{
resources = getApplicationContext().getResources();
int iAppWidgetId;
try
{
iAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}
catch (Exception e)
{
Log.d("Service", "WidgetID not found");
}
//comment out stuff...
//launch new httpTask
httpTask myhttpTask = new httpTask(tittlestring, subtitlestring, (myHttpListener) MyUpdateService.this, MyUpdateService.this);
//limit size of bitmap
myhttpTask.setSampleSize(Constants.BITMAP_SAMPLE_SIZE); //size = 4
myhttpTask.execute();
}
all tests are done in emulator.
one detail that may be important is that in logcat i get 20-30 fail messages "!!! failed binder transaction !!!"
any ideas on how i messed this up?
thnks!
solved. it was a memory problem of the emulator.
I have some code I have been experimenting with to see what I can do with the camera device. This following code works, but I have some issues with it that I cannot seem to solve.
The first call never works. The first time running the code the onPictureTaken callback is never called, so the file is never written. However the camera goes through all the other steps, including making the fake shutter noise.
I can't seem to set the picture size to something other than whatever it defaults to. If I try to set it to something else, the code stops working. Does the same as above, where the camera goes through all the motions, but the onPictureTaken callback is never called.
When the pictures are saved to the DCIM folder, the taken pictures do not show up in the default 'Photos' app on my phone, unless i reboot the phone.
Is there any way through code to disable the shutter noise?
Sorry, the code is a little messy because its an experiment.
Also, this code is executed in a BroadcastReceiver
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if(intent.getAction().equals(TAKE_PICTURE_INTENT))
{
Toast.makeText(context, "Test", Toast.LENGTH_LONG).show();
System.out.println("GOT THE INTENT");
try
{
Camera camera = Camera.open();
System.out.println("CAMERA OPENED");
Parameters params = camera.getParameters();
params.set("flash-mode", "off");
params.set("focus-mode", "infinity");
params.set("jpeg-quality", "100");
//params.setPictureSize(2592, 1952);
String str = params.get("picture-size" + "-values");
System.out.println(str);
String size = str.split(",")[0];
System.out.println(size);
//params.set("picture-size", size);
camera.setParameters(params);
System.out.println("CAMERA PARAMETERS SET");
camera.startPreview();
System.out.println("CAMERA PREVIEW STARTED");
camera.autoFocus(new AutoFocusCallBackImpl());
}
catch(Exception ex)
{
System.out.println("CAMERA FAIL, SKIP");
return ;
}
}//if
}//onreceive
private void TakePicture(Camera camera)
{
camera.takePicture(new Camera.ShutterCallback() {
#Override
public void onShutter() {
// TODO Auto-generated method stub
System.out.println("CAMERA SHUTTER CALLBACK");
}
}
, null,
new Camera.PictureCallback() {
public void onPictureTaken(byte[] imageData, Camera c) {
//c.release();
System.out.println("CAMERA CALLBACK");
FileOutputStream outStream = null;
try {
System.out.println("Start Callback");
File esd = Environment.getExternalStorageDirectory();
outStream = new FileOutputStream(esd.getAbsolutePath() + String.format(
"/DCIM/%d.jpg", System.currentTimeMillis()));
outStream.write(imageData);
outStream.close();
System.out.println( "onPictureTaken - wrote bytes: " + imageData.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("File not found exception");
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO exception");
} finally {
System.out.println("Finally");
c.release();
}
}
}
);
//camera.release();
}//TAKE PICTURE
private class AutoFocusCallBackImpl implements Camera.AutoFocusCallback {
#Override
public void onAutoFocus(boolean success, Camera camera) {
//bIsAutoFocused = success; //update the flag used in onKeyDown()
System.out.println("Inside autofocus callback. autofocused="+success);
//play the autofocus sound
//MediaPlayer.create(CameraActivity.this, R.raw.auto_focus).start();
if(success)
{
System.out.println("AUTO FOCUS SUCCEDED");
}
else
{
System.out.println("AUTO FOCUS FAILED");
}
TakePicture(camera);
System.out.println("CALLED TAKE PICTURE");
}
}//AUTOFOCUSCALLBACK
1.First of all put all camera logic out of BroadCast receiver & put it into seprate Activity.
2.
When the pictures are saved to the DCIM folder, the taken pictures do not show up in the default 'Photos' app on my phone, unless i reboot the phone.
because MediaScanner needs to be called to rescan images/changes once you take photo. When u reboot phone mediascanner scans media & finds new images. for this isuue you should check out MediaScanner.
3.Follow Android Camera Tutorial & Camera API
-Thanks