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