From ed15d1a140986473bbe7fffd72ec9618c41c5979 Mon Sep 17 00:00:00 2001 From: Angus Kong Date: Mon, 19 Aug 2013 15:06:12 -0700 Subject: Bring back wide angle panorama. bug:10293937 Change-Id: I23a977e87b7416f07ecac20025b6c142ae61be05 --- src/com/android/camera/MosaicFrameProcessor.java | 237 +++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 src/com/android/camera/MosaicFrameProcessor.java (limited to 'src/com/android/camera/MosaicFrameProcessor.java') diff --git a/src/com/android/camera/MosaicFrameProcessor.java b/src/com/android/camera/MosaicFrameProcessor.java new file mode 100644 index 000000000..cb305344d --- /dev/null +++ b/src/com/android/camera/MosaicFrameProcessor.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera; + +import android.util.Log; + +/** + * A singleton to handle the processing of each frame by {@link Mosaic}. + */ +public class MosaicFrameProcessor { + private static final String TAG = "MosaicFrameProcessor"; + private static final int NUM_FRAMES_IN_BUFFER = 2; + private static final int MAX_NUMBER_OF_FRAMES = 100; + private static final int MOSAIC_RET_CODE_INDEX = 10; + private static final int FRAME_COUNT_INDEX = 9; + private static final int X_COORD_INDEX = 2; + private static final int Y_COORD_INDEX = 5; + private static final int HR_TO_LR_DOWNSAMPLE_FACTOR = 4; + private static final int WINDOW_SIZE = 3; + + private Mosaic mMosaicer; + private boolean mIsMosaicMemoryAllocated = false; + private float mTranslationLastX; + private float mTranslationLastY; + + private int mFillIn = 0; + private int mTotalFrameCount = 0; + private int mLastProcessFrameIdx = -1; + private int mCurrProcessFrameIdx = -1; + private boolean mFirstRun; + + // Panning rate is in unit of percentage of image content translation per + // frame. Use moving average to calculate the panning rate. + private float mPanningRateX; + private float mPanningRateY; + + private float[] mDeltaX = new float[WINDOW_SIZE]; + private float[] mDeltaY = new float[WINDOW_SIZE]; + private int mOldestIdx = 0; + private float mTotalTranslationX = 0f; + private float mTotalTranslationY = 0f; + + private ProgressListener mProgressListener; + + private int mPreviewWidth; + private int mPreviewHeight; + private int mPreviewBufferSize; + + private static MosaicFrameProcessor sMosaicFrameProcessor; // singleton + + public interface ProgressListener { + public void onProgress(boolean isFinished, float panningRateX, float panningRateY, + float progressX, float progressY); + } + + public static MosaicFrameProcessor getInstance() { + if (sMosaicFrameProcessor == null) { + sMosaicFrameProcessor = new MosaicFrameProcessor(); + } + return sMosaicFrameProcessor; + } + + private MosaicFrameProcessor() { + mMosaicer = new Mosaic(); + } + + public void setProgressListener(ProgressListener listener) { + mProgressListener = listener; + } + + public int reportProgress(boolean hires, boolean cancel) { + return mMosaicer.reportProgress(hires, cancel); + } + + public void initialize(int previewWidth, int previewHeight, int bufSize) { + mPreviewWidth = previewWidth; + mPreviewHeight = previewHeight; + mPreviewBufferSize = bufSize; + setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize); + setStripType(Mosaic.STRIPTYPE_WIDE); + // no need to call reset() here. reset() should be called by the client + // after this initialization before calling other methods of this object. + } + + public void clear() { + if (mIsMosaicMemoryAllocated) { + mMosaicer.freeMosaicMemory(); + mIsMosaicMemoryAllocated = false; + } + synchronized (this) { + notify(); + } + } + + public boolean isMosaicMemoryAllocated() { + return mIsMosaicMemoryAllocated; + } + + public void setStripType(int type) { + mMosaicer.setStripType(type); + } + + private void setupMosaicer(int previewWidth, int previewHeight, int bufSize) { + Log.v(TAG, "setupMosaicer w, h=" + previewWidth + ',' + previewHeight + ',' + bufSize); + + if (mIsMosaicMemoryAllocated) throw new RuntimeException("MosaicFrameProcessor in use!"); + mIsMosaicMemoryAllocated = true; + mMosaicer.allocateMosaicMemory(previewWidth, previewHeight); + } + + public void reset() { + // reset() can be called even if MosaicFrameProcessor is not initialized. + // Only counters will be changed. + mFirstRun = true; + mTotalFrameCount = 0; + mFillIn = 0; + mTotalTranslationX = 0; + mTranslationLastX = 0; + mTotalTranslationY = 0; + mTranslationLastY = 0; + mPanningRateX = 0; + mPanningRateY = 0; + mLastProcessFrameIdx = -1; + mCurrProcessFrameIdx = -1; + for (int i = 0; i < WINDOW_SIZE; ++i) { + mDeltaX[i] = 0f; + mDeltaY[i] = 0f; + } + mMosaicer.reset(); + } + + public int createMosaic(boolean highRes) { + return mMosaicer.createMosaic(highRes); + } + + public byte[] getFinalMosaicNV21() { + return mMosaicer.getFinalMosaicNV21(); + } + + // Processes the last filled image frame through the mosaicer and + // updates the UI to show progress. + // When done, processes and displays the final mosaic. + public void processFrame() { + if (!mIsMosaicMemoryAllocated) { + // clear() is called and buffers are cleared, stop computation. + // This can happen when the onPause() is called in the activity, but still some frames + // are not processed yet and thus the callback may be invoked. + return; + } + + mCurrProcessFrameIdx = mFillIn; + mFillIn = ((mFillIn + 1) % NUM_FRAMES_IN_BUFFER); + + // Check that we are trying to process a frame different from the + // last one processed (useful if this class was running asynchronously) + if (mCurrProcessFrameIdx != mLastProcessFrameIdx) { + mLastProcessFrameIdx = mCurrProcessFrameIdx; + + // TODO: make the termination condition regarding reaching + // MAX_NUMBER_OF_FRAMES solely determined in the library. + if (mTotalFrameCount < MAX_NUMBER_OF_FRAMES) { + // If we are still collecting new frames for the current mosaic, + // process the new frame. + calculateTranslationRate(); + + // Publish progress of the ongoing processing + if (mProgressListener != null) { + mProgressListener.onProgress(false, mPanningRateX, mPanningRateY, + mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth, + mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight); + } + } else { + if (mProgressListener != null) { + mProgressListener.onProgress(true, mPanningRateX, mPanningRateY, + mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth, + mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight); + } + } + } + } + + public void calculateTranslationRate() { + float[] frameData = mMosaicer.setSourceImageFromGPU(); + int ret_code = (int) frameData[MOSAIC_RET_CODE_INDEX]; + mTotalFrameCount = (int) frameData[FRAME_COUNT_INDEX]; + float translationCurrX = frameData[X_COORD_INDEX]; + float translationCurrY = frameData[Y_COORD_INDEX]; + + if (mFirstRun) { + // First time: no need to update delta values. + mTranslationLastX = translationCurrX; + mTranslationLastY = translationCurrY; + mFirstRun = false; + return; + } + + // Moving average: remove the oldest translation/deltaTime and + // add the newest translation/deltaTime in + int idx = mOldestIdx; + mTotalTranslationX -= mDeltaX[idx]; + mTotalTranslationY -= mDeltaY[idx]; + mDeltaX[idx] = Math.abs(translationCurrX - mTranslationLastX); + mDeltaY[idx] = Math.abs(translationCurrY - mTranslationLastY); + mTotalTranslationX += mDeltaX[idx]; + mTotalTranslationY += mDeltaY[idx]; + + // The panning rate is measured as the rate of the translation percentage in + // image width/height. Take the horizontal panning rate for example, the image width + // used in finding the translation is (PreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR). + // To get the horizontal translation percentage, the horizontal translation, + // (translationCurrX - mTranslationLastX), is divided by the + // image width. We then get the rate by dividing the translation percentage with the + // number of frames. + mPanningRateX = mTotalTranslationX / + (mPreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE; + mPanningRateY = mTotalTranslationY / + (mPreviewHeight / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE; + + mTranslationLastX = translationCurrX; + mTranslationLastY = translationCurrY; + mOldestIdx = (mOldestIdx + 1) % WINDOW_SIZE; + } +} -- cgit v1.2.3