Home | History | Annotate | Download | only in fov
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.verifier.camera.fov;
     18 
     19 import com.android.cts.verifier.R;
     20 
     21 import android.app.Activity;
     22 import android.content.SharedPreferences;
     23 import android.graphics.Bitmap;
     24 import android.graphics.BitmapFactory;
     25 import android.graphics.Canvas;
     26 import android.graphics.Color;
     27 import android.graphics.Paint;
     28 import android.graphics.RectF;
     29 import android.os.Bundle;
     30 import android.preference.PreferenceManager;
     31 import android.view.SurfaceHolder;
     32 import android.view.SurfaceView;
     33 import android.view.View;
     34 import android.widget.Button;
     35 import android.widget.SeekBar;
     36 import android.widget.SeekBar.OnSeekBarChangeListener;
     37 
     38 import java.io.File;
     39 import java.io.FileInputStream;
     40 import java.io.IOException;
     41 
     42 /**
     43  * Shows the picture taken and lets the user specify the field of view (FOV).
     44  */
     45 public class DetermineFovActivity extends Activity {
     46 
     47     private static final float FOV_ADJUSTMENT_RANGE = 20;
     48     private static final int SEEKBAR_MAX_VALUE = 100;
     49     private static final float TEXT_SIZE = 16;
     50     private static final float TEXT_PADDING = 0.2f;
     51     private static final String DEFAULT_MARKER_DISTANCE = "36.8";
     52     private static final String DEFAULT_TARGET_DISTANCE = "99.7";
     53 
     54     private float mMarkerDistanceCm;
     55     private SurfaceView mSurfaceView;
     56     private SurfaceHolder mSurfaceHolder;
     57     private Bitmap mPhotoBitmap;
     58     private float mFovMinDegrees;
     59     private float mFovMaxDegrees;
     60     private float mFovDegrees;
     61     private float mReportedFovDegrees;
     62     private SeekBar mSeekBar;
     63     private Button mDoneButton;
     64     private float mTargetDistanceCm;
     65     private String mMeasuredText;
     66     private String mReportedText;
     67 
     68     @Override
     69     protected void onCreate(Bundle savedInstanceState) {
     70         super.onCreate(savedInstanceState);
     71         setContentView(R.layout.camera_fov_calibration_determine_fov);
     72         File pictureFile = PhotoCaptureActivity.getPictureFile(this);
     73         try {
     74             mPhotoBitmap =
     75                     BitmapFactory.decodeStream(new FileInputStream(pictureFile));
     76         } catch (IOException e) {
     77             e.printStackTrace();
     78         }
     79 
     80         mSurfaceView = (SurfaceView) findViewById(R.id.camera_fov_photo_surface);
     81         mSurfaceHolder = mSurfaceView.getHolder();
     82         mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
     83             @Override
     84             public void surfaceDestroyed(SurfaceHolder holder) {}
     85 
     86         @Override
     87         public void surfaceCreated(SurfaceHolder holder) {
     88             drawContents();
     89         }
     90 
     91         @Override
     92         public void surfaceChanged(
     93                 SurfaceHolder holder, int format, int width, int height) {
     94             drawContents();
     95         }
     96         });
     97 
     98         mSeekBar = (SeekBar) findViewById(R.id.camera_fov_seekBar);
     99         mSeekBar.setMax(SEEKBAR_MAX_VALUE);
    100         mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    101             @Override
    102             public void onStopTrackingTouch(SeekBar seekBar) {}
    103 
    104             @Override
    105             public void onStartTrackingTouch(SeekBar seekBar) {}
    106 
    107             @Override
    108             public void onProgressChanged(
    109                     SeekBar seekBar, int progress, boolean fromUser) {
    110                 mFovDegrees = seekBarProgressToFovDegrees(progress);
    111                 drawContents();
    112             }
    113         });
    114 
    115         mDoneButton = (Button) findViewById(R.id.camera_fov_fov_done);
    116         mDoneButton.setOnClickListener(new View.OnClickListener() {
    117             @Override
    118             public void onClick(View v) {
    119                 setResult(RESULT_OK);
    120                 CtsTestHelper.storeCtsTestResult(DetermineFovActivity.this,
    121                         mReportedFovDegrees, mFovDegrees);
    122                 finish();
    123             }
    124         });
    125     }
    126 
    127     private int fovToSeekBarProgress(float fovDegrees) {
    128         return Math.round((fovDegrees - mFovMinDegrees)
    129                 / (mFovMaxDegrees - mFovMinDegrees) * SEEKBAR_MAX_VALUE);
    130     }
    131 
    132     private float seekBarProgressToFovDegrees(int progress) {
    133         float degrees = mFovMinDegrees + (float) progress / SEEKBAR_MAX_VALUE
    134                 * (mFovMaxDegrees - mFovMinDegrees);
    135         // keep only 2 decimal places.
    136         return (int) (degrees * 100) / 100.0f;
    137     }
    138 
    139     @Override
    140     protected void onResume() {
    141         super.onResume();
    142 
    143         setResult(RESULT_CANCELED);
    144         mMarkerDistanceCm = getMarkerDistance();
    145         mTargetDistanceCm = getTargetDistance();
    146         mReportedFovDegrees = PhotoCaptureActivity.getReportedFovDegrees();
    147 
    148         mFovDegrees = mReportedFovDegrees > 120 ? 60 : mReportedFovDegrees;
    149         mFovMaxDegrees = mFovDegrees + FOV_ADJUSTMENT_RANGE / 2;
    150         mFovMinDegrees = mFovDegrees - FOV_ADJUSTMENT_RANGE / 2;
    151 
    152         mMeasuredText = getResources().getString(R.string.camera_fov_displayed_fov_label);
    153         mReportedText = getResources().getString(R.string.camera_fov_reported_fov_label);
    154 
    155         mSeekBar.setProgress(fovToSeekBarProgress(mFovDegrees));
    156         drawContents();
    157     }
    158 
    159     private float getMarkerDistance() {
    160         // Get the marker distance from the preferences.
    161         SharedPreferences prefs =
    162                 PreferenceManager.getDefaultSharedPreferences(this);
    163         return Float.parseFloat(prefs.getString(
    164                 CalibrationPreferenceActivity.OPTION_MARKER_DISTANCE,
    165                 DEFAULT_MARKER_DISTANCE));
    166     }
    167 
    168     private float getTargetDistance() {
    169         // Get the marker distance from the preferences.
    170         SharedPreferences prefs =
    171                 PreferenceManager.getDefaultSharedPreferences(this);
    172         return Float.parseFloat(prefs.getString(
    173                 CalibrationPreferenceActivity.OPTION_TARGET_DISTANCE,
    174                 DEFAULT_TARGET_DISTANCE));
    175     }
    176 
    177     private float focalLengthPixels(float fovDegrees, float imageWidth) {
    178         return (float) (imageWidth
    179                 / (2 * Math.tan(fovDegrees / 2 * Math.PI / 180.0f)));
    180     }
    181 
    182     private void drawContents() {
    183         SurfaceHolder holder = mSurfaceView.getHolder();
    184         Canvas canvas = holder.lockCanvas();
    185         if (canvas == null || mPhotoBitmap == null) {
    186             return;
    187         }
    188 
    189         int canvasWidth = canvas.getWidth();
    190         int canvasHeight = canvas.getHeight();
    191         int photoWidth = mPhotoBitmap.getWidth();
    192         int photoHeight = mPhotoBitmap.getHeight();
    193         RectF drawRect = new RectF();
    194 
    195         // Determine if the canvas aspect ratio is larger than that of the photo.
    196         float scale = (float) canvasWidth / photoWidth;
    197         int scaledHeight = (int) (scale * photoHeight);
    198         if (scaledHeight < canvasHeight) {
    199             // If the aspect ratio is smaller, set the destination rectangle to pad
    200             // vertically.
    201             int pad = (canvasHeight - scaledHeight) / 2;
    202             drawRect.set(0, pad, canvasWidth, pad + scaledHeight - 1);
    203         } else {
    204             // Set the destination rectangle to pad horizontally.
    205             scale = (float) canvasHeight / photoHeight;
    206             float scaledWidth = scale * photoWidth;
    207             float pad = (canvasWidth - scaledWidth) / 2;
    208             drawRect.set(pad, 0, pad + scaledWidth - 1, canvasHeight);
    209         }
    210 
    211         // Draw the photo.
    212         canvas.drawColor(Color.BLACK);
    213         canvas.drawBitmap(mPhotoBitmap, null, drawRect, null);
    214 
    215         // Draw the fov indicator text.
    216         Paint paint = new Paint();
    217         paint.setColor(0xffffffff);
    218         float textSize = TEXT_SIZE * DetermineFovActivity.this.getResources()
    219                 .getDisplayMetrics().scaledDensity;
    220         paint.setTextSize(textSize);
    221         canvas.drawText(mMeasuredText + " " + mFovDegrees + " degrees.", textSize,
    222                 2 * textSize * (1.0f + TEXT_PADDING), paint);
    223         canvas.drawText(mReportedText + " " + mReportedFovDegrees + " degrees.",
    224                 textSize, textSize * (1.0f + TEXT_PADDING), paint);
    225 
    226         // Draw the image center circle.
    227         paint.setColor(Color.BLACK);
    228         paint.setStyle(Paint.Style.STROKE);
    229         paint.setStrokeWidth(3);
    230         float dstWidth = drawRect.right - drawRect.left + 1;
    231         float dstHeight = drawRect.bottom - drawRect.top + 1;
    232         float centerX = drawRect.left + dstWidth / 2;
    233         canvas.drawLine(centerX, drawRect.top, centerX, drawRect.bottom, paint);
    234 
    235         // Project the markers into the scaled image with the given field of view.
    236         float markerX = mMarkerDistanceCm / 2;
    237         float markerZ = mTargetDistanceCm;
    238         float focalLength = focalLengthPixels(mFovDegrees, dstWidth);
    239         float dx = markerX / markerZ * focalLength;
    240         float projectedMarkerLeft = dstWidth / 2 - dx;
    241         float projectedMarkerRight = dstWidth / 2 + dx;
    242 
    243         // Draw the marker lines over the image.
    244         paint.setColor(Color.GREEN);
    245         paint.setStrokeWidth(2);
    246         float markerImageLeft = projectedMarkerLeft + drawRect.left;
    247         canvas.drawLine(
    248                 markerImageLeft, drawRect.top, markerImageLeft, drawRect.bottom, paint);
    249         float markerImageRight = projectedMarkerRight + drawRect.left;
    250         canvas.drawLine(markerImageRight, drawRect.top, markerImageRight,
    251                         drawRect.bottom, paint);
    252 
    253         holder.unlockCanvasAndPost(canvas);
    254     }
    255 }
    256