I am trying to capture images with Camera2 API on Android with captureBurst()
method. I start the burst capture in my button's OnClickListener
:
button?.setOnClickListener(View.OnClickListener {
imageRequestBuilder?.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
imageRequest = imageRequestBuilder?.build()
val requests = Collections.nCopies(10, imageRequest)
cameraCaptureSession?.captureBurst(requests, captureCallback, backgroundHandler)
})
In my OnImageAvailableListener
listener, I am then reading the captured image, saving it and closing. At this point I would like to display something on my screen and I would like to it in between every captured image in the burst sequence. So I have written this code:
override fun onImageAvailable(reader: ImageReader?) {
val image = reader?.acquireNextImage()
if(image!=null) {
synchronized(uiUpdate) {
runOnUiThread(uiUpdate)
(uiUpdate as java.lang.Object).wait()
}
val buffer = image?.planes?.get(0)?.buffer
val bytes = ByteArray(buffer?.capacity()!!)
buffer.get(bytes)
SaveImageTask().execute(bytes)
}
image?.close()
}
private val uiUpdate = object : Runnable {
override fun run() {
synchronized(this) {
textView?.text = "" + ++imageNum
(this as java.lang.Object).notify()
}
}
}
To test it and to be sure that this is realy happening, I am using my front camera, and holding a mirror above my device so that camera actually captures the app's display. Unfortunately, the display and captures are not synchronized. I have triend to put 10 same ImageRequests
to captureBurst
method, but when I overview the obtained images, first three are the same (display is still displaying initial number 0), but the rest are fine (display is changing synchronosly). Why can't all be synchronized, i.e. why are first few images captured almost at the same time (at the start of the burst), and others are fine?
I have created my ImageReader
with maximum of 1 Image in order to not to capture more than one image at the time:
imageReader = ImageReader.newInstance(/*some width*/, /*some height*/, /*some format*/, 1)
And the RequestBuilder
is created with TEMPLATE_PREVIEW
mode in order to maximize capturing screen.
I am aware that I could call capture
method every time I capture new image in my OnImageAvailableListener
listener, and this is working (tested!). But I need to get my application to capture images in fastest time possible.
Any help?
EDITED: This is my log as @alex-cohn has suggested:
D/onImageAvailable: display #0 Timestamp #0: 111788230978655
D/onImageAvailable: display #1 Timestamp #1: 111788264308655
D/onImageAvailable: display #2 Timestamp #2: 111788297633655
D/onImageAvailable: display #3 Timestamp #3: 111788730892655
D/onImageAvailable: display #4 Timestamp #4: 111788930856655
D/onImageAvailable: display #5 Timestamp #5: 111789030840655
D/onImageAvailable: display #6 Timestamp #6: 111789097494655
D/onImageAvailable: display #7 Timestamp #7: 111789264133655
D/onImageAvailable: display #8 Timestamp #8: 111789364112655
D/onImageAvailable: display #9 Timestamp #9: 111789464097655
EDITED2:
I have managed to get one image per one displayed number, but I am dropping 2 images every time after I had captured one, i.e.:
private val onImageAvailableListener = ImageReader.OnImageAvailableListener {reader ->
val image = reader?.acquireLatestImage()
if(image!=null) {
capturedImagesCount++
if(capturedImagesCount % frameDrop == 1) {
Log.i("IMAGE_TIME", "Image available at: "+ SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault()).format(Date()))
synchronized(uiUpdate) {
runOnUiThread(uiUpdate)
(uiUpdate as java.lang.Object).wait()
}
val buffer = image.planes?.get(0)?.buffer
val bytes = ByteArray(buffer?.capacity()!!)
buffer.rewind()
buffer.get(bytes)
SaveImageTask().execute(bytes)
}
}
image?.close()
}
here, the frameDrop
is set to 3. Also, maxImages
in creating the imageReader is also set to 3. I had tested it on the one other device which is taking 2 images at the time, so in that device's case I had to set frameDrop
and maxImages
to 2. The downside of this is that it is still a bit slow, after all it is taking every third(second) image taken in burst mode which is not exctly the thing you want when using captureBurst
method.
I still do not quite understand why this is working this way and why is camera taking images in something like pairs or triplets.