I've followed this article and implemented a Customer Renderer that's used to display a preview video stream from the device's camera.
I intend to capture a frame from the live stream and feed it into Tesseract OCR in order to read a number printed on a ticket.
My problem is that the image from camera preview is almost always not focused. So it is useless for its intended purpose.
An identical question has been asked years ago here in SO, but the accepted answer is not at all helpful for me.
This is the code of OnElementChanged
event of CameraPreviewRenderer
:
void OnElementChanged(ElementChangedEventArgs<CameraPreview> e)
{
CameraFragment newFragment = null;
if (e.OldElement != null)
{
e.OldElement.PropertyChanged -= OnElementPropertyChanged;
cameraFragment.Dispose();
}
if (e.NewElement != null)
{
this.EnsureId();
e.NewElement.PropertyChanged += OnElementPropertyChanged;
ElevationHelper.SetElevation(this, e.NewElement);
newFragment = new CameraFragment { Element = element };
}
FragmentManager.BeginTransaction()
.Replace(Id, cameraFragment = newFragment, "camera")
.Commit();
ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement));
}
This is a piece from class CameraFragment
. Method RetrieveCameraDevice
is the one that opens the camera (I think).
class CameraFragment : Fragment, TextureView.ISurfaceTextureListener
{
CameraDevice device;
CaptureRequest.Builder sessionBuilder;
CameraCaptureSession session;
CameraTemplate cameraTemplate;
CameraManager manager;
....
public async Task RetrieveCameraDevice(bool force = false)
{
if (Context == null || (!force && initTaskSource != null))
{
return;
}
if (device != null)
{
CloseDevice();
}
await RequestCameraPermissions();
if (!cameraPermissionsGranted)
{
return;
}
if (!captureSessionOpenCloseLock.TryAcquire(2500, TimeUnit.Milliseconds))
{
throw new RuntimeException("Timeout waiting to lock camera opening.");
}
IsBusy = true;
cameraId = GetCameraId();
if (string.IsNullOrEmpty(cameraId))
{
IsBusy = false;
captureSessionOpenCloseLock.Release();
Console.WriteLine("No camera found");
}
else
{
try
{
CameraCharacteristics characteristics = Manager.GetCameraCharacteristics(cameraId);
StreamConfigurationMap map = (StreamConfigurationMap)characteristics.Get(CameraCharacteristics.ScalerStreamConfigurationMap);
previewSize = ChooseOptimalSize(map.GetOutputSizes(Class.FromType(typeof(SurfaceTexture))),
texture.Width, texture.Height, GetMaxSize(map.GetOutputSizes((int)ImageFormatType.Jpeg)));
sensorOrientation = (int)characteristics.Get(CameraCharacteristics.SensorOrientation);
cameraType = (LensFacing)(int)characteristics.Get(CameraCharacteristics.LensFacing);
if (Resources.Configuration.Orientation == Android.Content.Res.Orientation.Landscape)
{
texture.SetAspectRatio(previewSize.Width, previewSize.Height);
}
else
{
texture.SetAspectRatio(previewSize.Height, previewSize.Width);
}
initTaskSource = new TaskCompletionSource<CameraDevice>();
Manager.OpenCamera(cameraId, new CameraStateListener
{
OnOpenedAction = device => initTaskSource?.TrySetResult(device),
OnDisconnectedAction = device =>
{
initTaskSource?.TrySetResult(null);
CloseDevice(device);
},
OnErrorAction = (device, error) =>
{
initTaskSource?.TrySetResult(device);
Console.WriteLine($"Camera device error: {error}");
CloseDevice(device);
},
OnClosedAction = device =>
{
initTaskSource?.TrySetResult(null);
CloseDevice(device);
}
}, backgroundHandler);
captureSessionOpenCloseLock.Release();
device = await initTaskSource.Task;
initTaskSource = null;
if (device != null)
{
await PrepareSession();
}
}
catch (Java.Lang.Exception ex)
{
Console.WriteLine("Failed to open camera.", ex);
Available = false;
}
finally
{
IsBusy = false;
}
}
}
I'm supposed to setup the "Focus Mode" in the Camera properties somewhere in this code. But I can't figure out a way to do this.