No announcement yet.

Camera Zoom

  • Filter
  • Time
  • Show
Clear All
new posts

  • Camera Zoom

    Is there a way to control the zoom-level / field-of-view for Vuforia?

  • #2
    I am interested in that too. Anyone knows? Is there a command like one for flash ?


    • #3
      Short answer is: Yes, you can control zoom using the Vuforia Java API (at least, for a Moto G5 Plus).

      Very long answer:

      First, check out the documentation of the Vuforia Java API here:

      In particular, look at the CameraDevice and CameraField classes.

      Get the unique instance of CameraDevice using CameraDevice.getInstance().

      CameraDevice provides access to a large number of fields that describe and control the behavior of the camera through its getNumFields(), getCameraField(), setField(), and a variety of getField... methods.

      Each CameraField will have an associated key, value, and data type. Some are read-only, others read-write. I put together an op mode that logs all of the fields, with their keys, data types, and initial values, to a log file. For the Moto G5 Plus, there are 237 different fields. The available fields are expected to differ among different phones.

      Three fields of interest (given as key, data type, and initial value) are:

      key="opti-zoom", type=String, initial value="opti-zoom-off"
      key="zoom", type=String, initial value="0"
      key="zoom-ratios", type=String, initial value="100,102,104, etc etc etc all the way up to 400"

      I believe that "zoom-ratios" is a read-only field that just tells you what zoom ratio you'll get for a particular value of "zoom". So for zoom="0", the zoom ratio is 100%. The 20th item in the "zoom-ratios" field is 200, so I think if you set zoom to "19", you'll get a zoom ratio of 200%.

      So I tried this: Initialized Vuforia, then immediately after "waitForStart()" put the following code:

      CameraDevice.getInstance.setField("opti-zoom", "opti-zoom-on");
      CameraDevice.getInstance.setField("zoom", "19");

      It worked: immediately after pressing the "Start" arrowhead, the picture zoomed in significantly (I'm guessing 200%).

      I don't know how this zoom will interact with the Vuforia localization engine. Here's one possible pitfall:

      You can get your camera's long-axis and short-axis field-of-view angles (in radians) as follows:

      float fovRad = CameraDevice.getInstance().getCameraCalibration(). getFieldOfViewRads().getData();

      When I do this before zooming, I get a long axis fov (converted to degrees) of about 60 degrees (which seems to be about right). You'd expect this to decrease by 50% after zooming to 200%, but that doesn't happen. It stays the same after zooming in. So the Vuforia localization engine may or may not realize that it's dealing with a zoomed-in image. One would have to do additional testing to find out.


      • #4
        Correction: in the preceding post fovRad should be declared as a float[], not just a float.


        • #5
          jkenney - thanks for detail answer. I will try these functions and find out more. I assume default there is no zoom so FOV is max by default. Is there a way to zoom out so that cam area can see three minerals?


          • #6
            FTC12676 I think you're correct that the default is maximal fov -- zoom starts at 0 and can only go up. We just look at center and right mineral; if neither is gold, we just go left.


            • #7
              jkenney I am trying to write an op mode to log all of the fields available on my device, but the fields all have keys like "android.flash.mode" or "android.edge.mode" and there are no fields which have the type as a string. Any idea what I'm doing wrong? Here is my code. I'm testing on a Galaxy S6.

              public class VuforiaZoomTest extends OpMode {
                  protected static final String TAG = "VuforiaZoom";
                  private VuforiaLocalizer vuforia;
                  private VuforiaTrackables targetsRoverRuckus;
                  private ElapsedTime timer = new ElapsedTime();
                  private boolean zoomedIn = false;
                  public void init() {
                      //Vuforia initialization
                      int cameraMonitorViewId = hardwareMap.appContext.getResources().getIdentifier("cameraMonitorViewId", "id", hardwareMap.appContext.getPackageName());
                      VuforiaLocalizer.Parameters parametersVuforia = new VuforiaLocalizer.Parameters(cameraMonitorViewId);
                      parametersVuforia.vuforiaLicenseKey = "my-vuforia-license-key";
                      parametersVuforia.cameraDirection = VuforiaLocalizer.CameraDirection.BACK;
                      this.vuforia = ClassFactory.getInstance().createVuforia(parametersVuforia);
                      targetsRoverRuckus = this.vuforia.loadTrackablesFromAsset("RoverRuckus");
                      VuforiaTrackable backSpace = targetsRoverRuckus.get(3);
                      Log.v(TAG, Vuforia.getLibraryVersion());
                  public void start(){
                  public void loop() {
                      if(timer.seconds() > 2 && !zoomedIn){
                          CameraDevice camera = CameraDevice.getInstance();
                          int numFields = camera.getNumFields();
                          int[] valueCounts = new int[]{0,0,0,0,0,0};
                          for(int i = 0; i < numFields; i++){
                              CameraField cameraField = new CameraField();
                              boolean success = camera.getCameraField(i, cameraField);
                              String valueString = "FAIL!";
                              if(cameraField.getType() == CameraField.DataType.TypeString){
                                  valueString = camera.getFieldString(cameraField.getKey());
                              if(cameraField.getType() == CameraField.DataType.TypeInt64){
                                  long[] l = new long[3];
                                  camera.getFieldInt64(cameraField.getKey(), l);
                                  valueString = Arrays.toString(l);
                              if(cameraField.getType() == CameraField.DataType.TypeFloat){
                                  float[] f = new float[3];
                                  camera.getFieldFloat(cameraField.getKey(), f);
                                  valueString = Arrays.toString(f);
                              if(cameraField.getType() == CameraField.DataType.TypeBool){
                                  boolean[] b = new boolean[1];
                                  camera.getFieldBool(cameraField.getKey(), b);
                                  valueString = Arrays.toString(b);
                              if(cameraField.getType() == CameraField.DataType.TypeInt64Range){
                                  long[] l = new long[3];
                                  camera.getFieldInt64Range(cameraField.getKey(), l);
                                  valueString = Arrays.toString(l);
                              if(cameraField.getType() == CameraField.DataType.TypeUnknown){
                                  valueString = "Unknown";
                              Log.v(TAG, cameraField.getKey()+", "+cameraField.getType()+",  "+valueString);
                          Log.v(TAG, Arrays.toString(valueCounts));
                          zoomedIn = true;


              • #8
                Sorry for the delay; I haven't been checking the forum regularly post-season.

                I don't see any problem there. Maybe the Galaxy S6 just has non-String field types. If not String (DataType = 0), what data types are you getting?

                Below is the code I used for testing. In the "for" loop, I tested whether the call to getCameraField was successful. I used arrays of length 1 to hold the TypeInt64, TypeFloat, and TypeBool values, and an array of length 2 to hold the TypeInt64Range values (perhaps moot, as I only saw String types). BetaLog is a utility class to log to a custom log file.

                int numFields = CameraDevice.getInstance().getNumFields();
                BetaLog.dd("CAMERA FIELDS","");
                CameraField cameraField = new CameraField();
                CameraDevice cd = CameraDevice.getInstance();
                String key = null;
                for (int i = 0; i < numFields; i++){
                    if (!cd.getCameraField(i, cameraField)) continue;
                    key = cameraField.getKey();
                    switch (cameraField.getType()){
                        case CameraField.DataType.TypeString:
                            BetaLog.d("%d  key = %s  type = %s  value = \"%s\"", i, key, "String", cd.getFieldString(key));
                        case CameraField.DataType.TypeInt64:
                            long[] longValue = new long[1];
                            if (!cd.getFieldInt64(key, longValue)) break;
                            BetaLog.d("%d  key = %s  type = %s  value = %d", i, key, "long", longValue[0]);
                        case CameraField.DataType.TypeFloat:
                            float[] floatValue = new float[1];
                            if (!cd.getFieldFloat(key, floatValue)) break;
                            BetaLog.d("%d  key = %s  type = %s  value = %g", i, key, "float", floatValue);
                        case CameraField.DataType.TypeBool:
                            boolean[] boolValue = new boolean[1];
                            if (!cd.getFieldBool(key, boolValue)) break;
                            String boolString = boolValue[0]? "true" : "false";
                            BetaLog.d("%d  key = %s  type = %s  value = %s", i, key, "boolean", boolString);
                        case CameraField.DataType.TypeInt64Range:
                            long[] longValues = new long[2];
                            if (!cd.getFieldInt64Range(key, longValues)) break;
                            BetaLog.d("%d  key = %s  type = %s  values = %d  %d", i, key, "range", longValues[0], longValues[1]);
                        case CameraField.DataType.TypeUnknown:
                            BetaLog.d("%d  DataType Unknown", i);
                            BetaLog.d("Unrecognized Data Type Specifier");


                • #9
                  jkenney Thanks for responding. Turns out the problem was that for higher OS versions Vuforia uses Android's Camera2 parameters, whose values aren't strings, instead of Android's Camera parameters, which are all strings. Unfortunately this makes zooming impossible since the value for the zoom parameters is a Rect object, which Vuforia interprets as TypeUnknown and TypeUnknown parameters can't be set.

                  I did a little bit of testing and it looks like using a earlier version of Vuforia results in Vuforia returning the Camera parameters regardless of the OS version, but I wasn't sure how to incorporate an earlier version of Vuforia into the FTC app. Any ideas?


                  • #10
                    Strange thing is, my Moto G5 is on Android v. 8.1.0. I would have thought that Camera2 would be at work for it as well. My only ideas are what you suggested (earlier Vuforia version) or trying to get direct access to the camera through Camera2; both are well above my current abilities.