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 { onProgress(boolean isFinished, float panningRateX, float panningRateY, float progressX, float progressY)66 public void onProgress(boolean isFinished, float panningRateX, float panningRateY, 67 float progressX, float progressY); 68 } 69 getInstance()70 public static MosaicFrameProcessor getInstance() { 71 if (sMosaicFrameProcessor == null) { 72 sMosaicFrameProcessor = new MosaicFrameProcessor(); 73 } 74 return sMosaicFrameProcessor; 75 } 76 MosaicFrameProcessor()77 private MosaicFrameProcessor() { 78 mMosaicer = new Mosaic(); 79 } 80 setProgressListener(ProgressListener listener)81 public void setProgressListener(ProgressListener listener) { 82 mProgressListener = listener; 83 } 84 reportProgress(boolean hires, boolean cancel)85 public int reportProgress(boolean hires, boolean cancel) { 86 return mMosaicer.reportProgress(hires, cancel); 87 } 88 initialize(int previewWidth, int previewHeight, int bufSize)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 clear()98 public void clear() { 99 if (mIsMosaicMemoryAllocated) { 100 mMosaicer.freeMosaicMemory(); 101 mIsMosaicMemoryAllocated = false; 102 } 103 synchronized (this) { 104 notify(); 105 } 106 } 107 isMosaicMemoryAllocated()108 public boolean isMosaicMemoryAllocated() { 109 return mIsMosaicMemoryAllocated; 110 } 111 setStripType(int type)112 public void setStripType(int type) { 113 mMosaicer.setStripType(type); 114 } 115 setupMosaicer(int previewWidth, int previewHeight, int bufSize)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 reset()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 createMosaic(boolean highRes)145 public int createMosaic(boolean highRes) { 146 return mMosaicer.createMosaic(highRes); 147 } 148 getFinalMosaicNV21()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. processFrame()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 calculateTranslationRate()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