Home | History | Annotate | Download | only in panorama
      1 /*
      2  * Copyright (C) 2011 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.camera.panorama;
     18 
     19 import android.util.Log;
     20 
     21 /**
     22  * Class to handle the processing of each frame by Mosaicer.
     23  */
     24 public class MosaicFrameProcessor {
     25     private static final boolean LOGV = true;
     26     private static final String TAG = "MosaicFrameProcessor";
     27     private static final int NUM_FRAMES_IN_BUFFER = 2;
     28     private static final int MAX_NUMBER_OF_FRAMES = 100;
     29     private static final int MOSAIC_RET_CODE_INDEX = 10;
     30     private static final int FRAME_COUNT_INDEX = 9;
     31     private static final int X_COORD_INDEX = 2;
     32     private static final int Y_COORD_INDEX = 5;
     33     private static final int HR_TO_LR_DOWNSAMPLE_FACTOR = 4;
     34     private static final int WINDOW_SIZE = 3;
     35 
     36     private Mosaic mMosaicer;
     37     private boolean mIsMosaicMemoryAllocated = false;
     38     private final long [] mFrameTimestamp = new long[NUM_FRAMES_IN_BUFFER];
     39     private float mTranslationLastX;
     40     private float mTranslationLastY;
     41 
     42     private int mFillIn = 0;
     43     private int mTotalFrameCount = 0;
     44     private long mLastProcessedFrameTimestamp = 0;
     45     private int mLastProcessFrameIdx = -1;
     46     private int mCurrProcessFrameIdx = -1;
     47 
     48     // Panning rate is in unit of percentage of image content translation / second.
     49     // Use the moving average to calculate the panning rate.
     50     private float mPanningRateX;
     51     private float mPanningRateY;
     52 
     53     private float[] mDeltaX = new float[WINDOW_SIZE];
     54     private float[] mDeltaY = new float[WINDOW_SIZE];
     55     private float[] mDeltaTime = new float[WINDOW_SIZE];
     56     private int mOldestIdx = 0;
     57     private float mTotalTranslationX = 0f;
     58     private float mTotalTranslationY = 0f;
     59     private float mTotalDeltaTime = 0f;
     60 
     61     private ProgressListener mProgressListener;
     62 
     63     private int mPreviewWidth;
     64     private int mPreviewHeight;
     65     private int mPreviewBufferSize;
     66 
     67     public interface ProgressListener {
     68         public void onProgress(boolean isFinished, float panningRateX, float panningRateY,
     69                 float progressX, float progressY);
     70     }
     71 
     72     public MosaicFrameProcessor(int previewWidth, int previewHeight, int bufSize) {
     73         mMosaicer = new Mosaic();
     74         mPreviewWidth = previewWidth;
     75         mPreviewHeight = previewHeight;
     76         mPreviewBufferSize = bufSize;
     77     }
     78 
     79     public void setProgressListener(ProgressListener listener) {
     80         mProgressListener = listener;
     81     }
     82 
     83     public int reportProgress(boolean hires, boolean cancel) {
     84         return mMosaicer.reportProgress(hires, cancel);
     85     }
     86 
     87     public void initialize() {
     88         setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize);
     89         setStripType(Mosaic.STRIPTYPE_WIDE);
     90         reset();
     91     }
     92 
     93     public void clear() {
     94         if (mIsMosaicMemoryAllocated) {
     95             mIsMosaicMemoryAllocated = false;
     96             mMosaicer.freeMosaicMemory();
     97         }
     98     }
     99 
    100     public void setStripType(int type) {
    101         mMosaicer.setStripType(type);
    102     }
    103 
    104     private void setupMosaicer(int previewWidth, int previewHeight, int bufSize) {
    105         Log.v(TAG, "setupMosaicer w, h=" + previewWidth + ',' + previewHeight + ',' + bufSize);
    106         mMosaicer.allocateMosaicMemory(previewWidth, previewHeight);
    107         mIsMosaicMemoryAllocated = true;
    108 
    109         mFillIn = 0;
    110         if  (mMosaicer != null) {
    111             mMosaicer.reset();
    112         }
    113     }
    114 
    115     public void reset() {
    116         // reset() can be called even if MosaicFrameProcessor is not initialized.
    117         // Only counters will be changed.
    118         mTotalFrameCount = 0;
    119         mFillIn = 0;
    120         mLastProcessedFrameTimestamp = 0;
    121         mTotalTranslationX = 0;
    122         mTranslationLastX = 0;
    123         mTotalTranslationY = 0;
    124         mTranslationLastY = 0;
    125         mTotalDeltaTime = 0;
    126         mPanningRateX = 0;
    127         mPanningRateY = 0;
    128         mLastProcessFrameIdx = -1;
    129         mCurrProcessFrameIdx = -1;
    130         for (int i = 0; i < WINDOW_SIZE; ++i) {
    131             mDeltaX[i] = 0f;
    132             mDeltaY[i] = 0f;
    133             mDeltaTime[i] = 0f;
    134         }
    135         mMosaicer.reset();
    136     }
    137 
    138     public int createMosaic(boolean highRes) {
    139         return mMosaicer.createMosaic(highRes);
    140     }
    141 
    142     public byte[] getFinalMosaicNV21() {
    143         return mMosaicer.getFinalMosaicNV21();
    144     }
    145 
    146     // Processes the last filled image frame through the mosaicer and
    147     // updates the UI to show progress.
    148     // When done, processes and displays the final mosaic.
    149     public void processFrame() {
    150         if (!mIsMosaicMemoryAllocated) {
    151             // clear() is called and buffers are cleared, stop computation.
    152             // This can happen when the onPause() is called in the activity, but still some frames
    153             // are not processed yet and thus the callback may be invoked.
    154             return;
    155         }
    156         long t1 = System.currentTimeMillis();
    157         mFrameTimestamp[mFillIn] = t1;
    158 
    159         mCurrProcessFrameIdx = mFillIn;
    160         mFillIn = ((mFillIn + 1) % NUM_FRAMES_IN_BUFFER);
    161 
    162         // Check that we are trying to process a frame different from the
    163         // last one processed (useful if this class was running asynchronously)
    164         if (mCurrProcessFrameIdx != mLastProcessFrameIdx) {
    165             mLastProcessFrameIdx = mCurrProcessFrameIdx;
    166 
    167             // Access the timestamp associated with it...
    168             long timestamp = mFrameTimestamp[mCurrProcessFrameIdx];
    169 
    170             // TODO: make the termination condition regarding reaching
    171             // MAX_NUMBER_OF_FRAMES solely determined in the library.
    172             if (mTotalFrameCount < MAX_NUMBER_OF_FRAMES) {
    173                 // If we are still collecting new frames for the current mosaic,
    174                 // process the new frame.
    175                 calculateTranslationRate(timestamp);
    176 
    177                 // Publish progress of the ongoing processing
    178                 if (mProgressListener != null) {
    179                     mProgressListener.onProgress(false, mPanningRateX, mPanningRateY,
    180                             mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth,
    181                             mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight);
    182                 }
    183             } else {
    184                 if (mProgressListener != null) {
    185                     mProgressListener.onProgress(true, mPanningRateX, mPanningRateY,
    186                             mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth,
    187                             mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight);
    188                 }
    189             }
    190         }
    191     }
    192 
    193     public void calculateTranslationRate(long now) {
    194         float[] frameData = mMosaicer.setSourceImageFromGPU();
    195         int ret_code = (int) frameData[MOSAIC_RET_CODE_INDEX];
    196         mTotalFrameCount  = (int) frameData[FRAME_COUNT_INDEX];
    197         float translationCurrX = frameData[X_COORD_INDEX];
    198         float translationCurrY = frameData[Y_COORD_INDEX];
    199 
    200         if (mLastProcessedFrameTimestamp == 0f) {
    201             // First time: no need to update delta values.
    202             mTranslationLastX = translationCurrX;
    203             mTranslationLastY = translationCurrY;
    204             mLastProcessedFrameTimestamp = now;
    205             return;
    206         }
    207 
    208         // Moving average: remove the oldest translation/deltaTime and
    209         // add the newest translation/deltaTime in
    210         int idx = mOldestIdx;
    211         mTotalTranslationX -= mDeltaX[idx];
    212         mTotalTranslationY -= mDeltaY[idx];
    213         mTotalDeltaTime -= mDeltaTime[idx];
    214         mDeltaX[idx] = Math.abs(translationCurrX - mTranslationLastX);
    215         mDeltaY[idx] = Math.abs(translationCurrY - mTranslationLastY);
    216         mDeltaTime[idx] = (now - mLastProcessedFrameTimestamp) / 1000.0f;
    217         mTotalTranslationX += mDeltaX[idx];
    218         mTotalTranslationY += mDeltaY[idx];
    219         mTotalDeltaTime += mDeltaTime[idx];
    220 
    221         // The panning rate is measured as the rate of the translation percentage in
    222         // image width/height. Take the horizontal panning rate for example, the image width
    223         // used in finding the translation is (PreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR).
    224         // To get the horizontal translation percentage, the horizontal translation,
    225         // (translationCurrX - mTranslationLastX), is divided by the
    226         // image width. We then get the rate by dividing the translation percentage with deltaTime.
    227         mPanningRateX = mTotalTranslationX /
    228                 (mPreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR) / mTotalDeltaTime;
    229         mPanningRateY = mTotalTranslationY /
    230                 (mPreviewHeight / HR_TO_LR_DOWNSAMPLE_FACTOR) / mTotalDeltaTime;
    231 
    232         mTranslationLastX = translationCurrX;
    233         mTranslationLastY = translationCurrY;
    234         mLastProcessedFrameTimestamp = now;
    235         mOldestIdx = (mOldestIdx + 1) % WINDOW_SIZE;
    236     }
    237 }
    238