Uses Camera 2 API on Android
For real time image processing I have a listener set up to do some image processing that gives a boolean output on whether to capture an image or no. Currently I am using camera2Raw example that has a takePicture() when a button is clicked. How can I ensure that the same frame that I processed is captured and no additional ones are captured. Please do help me out. Thanks
Link to camera2Raw
When you made a capture in your captureSession, the current frame will be capture and go through onCapturePictureComplete() method from the current callback associate to your capture:
mCaptureSession.capture(mPhotoRequestBuilder.build(), YourCallback, null);
private CameraCaptureSession.CaptureCallback YourCallback = new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureCompleted(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull TotalCaptureResult result) {
//get the iso and time exposure from the picture
Integer iso = result.get(CaptureResult.SENSOR_SENSITIVITY);
long timeExposure = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
Log.i(TAG, "[mHdrCaptureCallback][HDR] Photo: " + mHdrIndex + " Exposure: " + timeExposure);
Log.i(TAG, "[mHdrCaptureCallback][HDR] Photo: " + mHdrIndex + " ISO " + iso);
}
};
In the example above I made a capture and when it was complete, the Capture callback is call. There I just print the values of Exposure and ISO from the image result. But, when you take a picture, the onImageAvailable Listener from your current ImageReader will be called too, and there is where you will have the current frame and the image to save it.
Look at your example in Google:
/**
* This a callback object for the {#link ImageReader}. "onImageAvailable" will be called when a
* JPEG image is ready to be saved.
*/
private final ImageReader.OnImageAvailableListener mOnJpegImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
dequeueAndSaveImage(mJpegResultQueue, mJpegImageReader);
}
};
/**
* This a callback object for the {#link ImageReader}. "onImageAvailable" will be called when a
* RAW image is ready to be saved.
*/
private final ImageReader.OnImageAvailableListener mOnRawImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
dequeueAndSaveImage(mRawResultQueue, mRawImageReader);
}
};
Hope that it will help you and now you know a bit better how the save image process work's with camera2, let me know if I can help you in something else!
Related
I am using camera2 API to capture images with manual exposure and ISO. But sometimes the image captured has different values for ISO and exposure then the one I have specified.
Is there any way I could pass the information of the values that I set in the capture request to the image reader listener where callback comes when image is captured to see if the image is actually having the values that I specified.
I am capturing a lot of images(say a loop) with different values of ISO and exposure for every image.
This is my code to capture images:
imageReader = ImageReader.newInstance(imageWidth, imageHeight, ImageFormat.JPEG, 1);
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader imageReader) {
/// How to check the image is taken with correct values
}
}, backgroundHandler);
captureRequest = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureRequest.addTarget(preview);
captureRequest.addTarget(imageReader.getSurface());
captureRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
captureRequest.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
captureRequest.set(CaptureRequest.SENSOR_SENSITIVITY, <MANUAL_ISO>);
captureRequest.set(CaptureRequest.SENSOR_EXPOSURE_TIME, <MANUAL_EXPOSURE>);
mSession.capture(captureRequest.build(), null, backgroundHandler);
This works most of the times, like if I am taking 100 photos then around 70 are taken with the values I specify and rest 30 will have different values.
What I tried:
I have tried below approach where, when I capture image, I check the values in onCaptureCompleted and create a queue indicating whether the image is taken with correct values or not. But when I get image in imageReader, I have no idea if the value in queue is for current image or some other image. This happens because I don't know when will imageReader listener be invoked for a image: It can be invoked just after onCaptureCompleted finishes or before it, or in worst case after onCaptureCompleted is invoked 2-3 times for 2-3 images as I am capturing images in loop.
Basically I need a tag to identify images in this approach but I don't know how to do that.
Here is the code for the same:
class CapturedPicture {
static Queue<Boolean> iso = new LinkedList<>();
}
mSession.capture(captureRequest.build(), new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureCompleted(#NonNull CameraCaptureSession session, #NonNull CaptureRequest request, #NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
int capturedISO = result.get(CaptureResult.SENSOR_SENSITIVITY);
CapturedPicture.iso.add(<MANUAL_ISO> == capturedISO);
}
}, backgroundHandler);
So I need a way to pass information to imageReader listener to indicate if the current image conforms to the settings I specified.
Any help is appreciated.
PS: I also tried saving TotalCaptureResult's SENSOR_TIMESTAMP and image.getTimestamp and comparing them but I can confirm sometimes the image with some timestamp doesn't have same parameters as captured from totalCaptureResult.
Ideally the captureResult data should match with the corresponding images based on timestamp.
If it is not, could you check if your device supports postRawSensitivityBoost or not, you may need to consider setting this as well.
I am developing an android camera application, and I wanted to pass in the capture size to configure the camera before taking a picture.
This is my code:
try {
mCaptureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCaptureRequestBuilder.addTarget(previewSurface);
InputConfiguration inputConfiguration = new InputConfiguration(1920, 1080, ImageFormat.JPEG); //error here.
cameraDevice.createReprocessableCaptureSession(inputConfiguration, Arrays.asList(previewSurface), new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
try {
cameraCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(), null, handler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession cameraCaptureSession) {
Toast.makeText(getApplicationContext(), "Camera Preview Failed!!", Toast.LENGTH_SHORT).show();
}
}, null);
}
So, I am trying to pass an input configuration to the camera here.
My problem is I'm getting an error on the InputConfiguration line.
This is my error:
java.lang.IllegalArgumentException: input format 256 is not valid
I tried this with a lot of different ImageFormats like JPEG, UNKNOWN, NV21 and others. It's not working.
Help me resolve this error and also if my approach is wrong in interacting with the camera do tell me.
You are working with TEMPLATE_PREVIEW, which does not support ImageFormat.JPEG.
Camera2 mandates that preview supports YUV 420, like this:
InputConfiguration inputConfiguration = new InputConfiguration(1920, 1080, ImageFormat.YUV_420_888);
Input configurations are only used reprocessing use cases, where you have an application-level circular buffer of captured partially processed frames.
When the user hits the shutter button, you send one of the captured frames back to the camera device for final processing. The input configuration is for selecting the size and format of this path back into the camera.
For simple capture applications, you only care about the output configurations.
Another sad case is described here: https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#REPROCESS_MAX_CAPTURE_STALL
Check that your camera supports reprocessing or you would not pass through "input format is not valid" at all as no input would be allowed for reprocessing.
Also the absence of this key value can signal that Yuv reprocessing is not available: https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING
I'm having trouble with ML Kit Barcode Scanner. When I try to decode a sample QR code,
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.qr_code_sample);
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
FirebaseVisionBarcodeDetector detector = FirebaseVision.getInstance().getVisionBarcodeDetector();
Task<List<FirebaseVisionBarcode>> result = detector.detectInImage(image)
.addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionBarcode>>() {
#Override
public void onSuccess(List<FirebaseVisionBarcode> barcodes) {
for (FirebaseVisionBarcode barcode:barcodes) {
Log.e("Log", "QR Code: "+barcode.getUrl().getUrl());
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.e("Log", "Decode QR Code failed");
}
});
The output is like this:
QR Code: ""
How to solve this problem?
According to the API Reference, the getUrl() is:
set iff getValueType() is TYPE_URL
So your barcode is probably not an URL/Bookmark, or ML Kit does not recognize it as such.
I recommend printing these 3 values:
#Override
public void onSuccess(List<FirebaseVisionBarcode> barcodes) {
for (FirebaseVisionBarcode barcode:barcodes) {
Log.e("Log", "QR Code: "+barcode.getDisplayValue()); //Returns barcode value in a user-friendly format.
Log.e("Log", "Raw Value: "+barcode.getRawValue());//Returns barcode value as it was encoded in the barcode.
Log.e("Log", "Code Type: "+barcode.getValueType()); //This will tell you the type of your barcode
}
}
You'll probably find your desired output in one of the first 2 lines. The third line tells you what type is the barcode you've scanned.
To extract title and url from barcode, you need to have Url Bookmark inside barcode, not just Url.
Raw Data of barcode that contains url bookmark would look something like this: MEBKM:TITLE:MyBookmark;URL:www.google.com;;
When you use ML KIT to scan barcode that consists of url only you get Raw Data like this: www.google.com
So to be able to extract title and url data from object of type FirebaseVisionBarcode.UrlBookmark you need to have those data inside that object.
Try to generate QR code here: https://www.montreallisting.ca/article/qr-code-quick-response-scan-mobile-android-iphone-blackberry/
and then use that picture to extract data you want and you will see as difference.
I'm working on an Android project where I have to repeatedly take photos and process them with a facial recognition API. I realize that I have to use camera2's methods (either setRepeatingRequest or setRepeatingBurst), but these methods only take a photo once every 5 seconds or so. I was wondering how I would change this to take a picture at least twice every second without lag. Is this even possible? If not, what would you recommend I use to do this?
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
try (Image image = reader.acquireNextImage()) {
Image.Plane[] planes = image.getPlanes();
if (planes.length > 0) {
ByteBuffer buffer = planes[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
mCallback.onPictureTaken(data);
}
}
}
};
This may help you,here is the use of Camera2.
https://github.com/google/cameraview
https://github.com/google/cameraview/blob/master/library/src/main/api21/com/google/android/cameraview/Camera2.java
I am using firebase storage in my android app to store images uploaded by users. all images uploaded are square in shape.
I discovered that downloading this images consumes a lot of user bandwidth and i would like to reduce this consumption by reducing the size of image downloaded into a square imageview
I am using Glide to download this images and i have tried to download images of custom size but the image is not appearing on the imageview.
interface
public interface MyDataModel {
public String buildUrl(int width, int height);
}
my class which extends BaseGlideUrlLoader
public class MyUrlLoader extends BaseGlideUrlLoader<MyDataModel> {
public MyUrlLoader(Context context) {
super(context);
}
#Override
protected String getUrl(MyDataModel model, int width, int height) {
// Construct the url for the correct size here.
return model.buildUrl(width, height);
}
}
class which implements MyDataModel interface
public class CustomImageSize implements MyDataModel {
private String uri;
public CustomImageSize(String uri){
this.uri = uri;
}
#Override
public String buildUrl(int width, int height) {
return uri + "?w=" + width + "&h=" + height;
}
}
Finally
CustomImageSize size = new CustomImageSize(uri);
Glide.with(context)
.using(new MyUrlLoader(context))
.load(size)
.centerCrop()
.priority(Priority.HIGH)
.into(imageView);
RESULTS OF SOLUTION ABOVE
Image is not appearing in my square imageview
SOLUTION 2: use firebase image loader
// Reference to an image file in Firebase Storage
StorageReference storageReference = ...;
ImageView imageView = ...;
// Load the image using Glide
Glide.with(this /* context */)
.using(new FirebaseImageLoader())
.load(storageReference)
.into(imageView);
RESULT OF SOLUTION 2 ABOVE
working! image is appearing BUT it's like entire image is been downloaded which consume a lot of bandwidth. I just want a custom size image e.g 200px by 200px to be downloaded.
How can I do or change in my solution 1 above to download images of custom size from firebase storage?
EDIT
I have tried to access one of my images https://firebasestorage.googleapis.com/....m.png from the browser and it was loaded successfully to the webpage. but when i try to put to size specific parameters to my image url link https://firebasestorage.googleapis.com/....m.png?w=100&h=100 an error appeared on the webpage
{
"error": {
"code": 403,
"message": "Permission denied. Could not perform this operation"
}
}
I was finally able to download images from firebase storage using MyUrlLoader class
You see, firebase storage urls look like this
firebasestorage.googleapis.com/XXXX.appspot.com/Folder%2Image.png?&alt=media&token=XXX
As you can see above, the link already have this special question mark character ? which stands for the start of querying string so when i use CustomImageSize class, another ? was being added so the link was ending up with two ? which made downloading to fail
firebasestorage.googleapis.com/XXXX.appspot.com/Folder%2Image.png?&alt=media&token=XXX?w=200&h=200
Solution was to remove the ? in my CustomImageSize class. so it ended up like this
public class CustomImageSize implements MyDataModel {
private String uri;
public CustomImageSize(String uri){
this.uri = uri;
}
#Override
public String buildUrl(int width, int height) {
return uri + "w=" + width + "&h=" + height;
}
}
Although it downloaded, am not sure whether entire image was being downloaded or just the custom size one. This is because, i tried to access the image in my browser after correcting the error that was making viewing to fail, but still i was receiving an entire image. not a resized image (w=200&h=200)
Try downloading your image using the following commands:-
StorageReference islandRef = storageRef.child("yourImage.jpg");
// defines the specific size of your image
final long ONE_MEGABYTE = 1024 * 1024;
islandRef.getBytes(ONE_MEGABYTE).addOnSuccessListener(new OnSuccessListener<byte[]>() {
#Override
public void onSuccess(byte[] bytes) {
// Data for "yourImage.jpg" is returns, use this as needed
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Handle any errors
}
});