From c8513bf1f30d0cf0257feaf4956e5179cfefe52b Mon Sep 17 00:00:00 2001 From: Jack Yoo Date: Wed, 15 Jun 2016 16:34:57 -0700 Subject: SnapdragonCamera: Adding ubifocus post proc filter Introducing ubifocus to camera2. Change-Id: I611bea207b9cbbde56a23720b22a5a5e3daa215f CRs-Fixed: 1035245 --- res/values/camera2arrays.xml | 2 +- src/com/android/camera/CameraActivity.java | 15 ++ src/com/android/camera/CaptureModule.java | 107 +++++--- src/com/android/camera/CaptureUI.java | 16 +- src/com/android/camera/RefocusActivity.java | 56 ++++- src/com/android/camera/SettingsManager.java | 19 +- .../camera/imageprocessor/PostProcessor.java | 38 ++- .../filter/BeautificationFilter.java | 17 ++ .../camera/imageprocessor/filter/ImageFilter.java | 12 + .../imageprocessor/filter/OptizoomFilter.java | 12 + .../imageprocessor/filter/SharpshooterFilter.java | 13 + .../filter/TrackingFocusFrameListener.java | 17 ++ .../imageprocessor/filter/UbifocusFilter.java | 269 +++++++++++++++++++++ 13 files changed, 533 insertions(+), 60 deletions(-) create mode 100644 src/com/android/camera/imageprocessor/filter/UbifocusFilter.java diff --git a/res/values/camera2arrays.xml b/res/values/camera2arrays.xml index c991dbf92..25105ba65 100644 --- a/res/values/camera2arrays.xml +++ b/res/values/camera2arrays.xml @@ -109,7 +109,7 @@ 0 100 18 - -1 + 102 101 3 4 diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index e3b4573d1..68d9c7c3a 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -249,6 +249,7 @@ public class CameraActivity extends Activity private Cursor mCursor; private WakeLock mWakeLock; + private static final int REFOCUS_ACTIVITY_CODE = 1; private class MyOrientationEventListener extends OrientationEventListener { @@ -576,6 +577,16 @@ public class CameraActivity extends Activity return; } } + if (mCurrentModule instanceof CaptureModule) { + if (((CaptureModule) mCurrentModule).isRefocus()) { + Intent intent = new Intent(); + intent.setClass(this, RefocusActivity.class); + intent.setData(uri); + intent.setFlags(RefocusActivity.MAP_ROTATED); + startActivityForResult(intent, REFOCUS_ACTIVITY_CODE); + return; + } + } try { Intent intent = IntentHelper.getGalleryIntent(this); intent.setAction(Intent.ACTION_VIEW); @@ -1624,6 +1635,10 @@ public class CameraActivity extends Activity if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) { mResetToPreviewOnResume = false; mIsEditActivityInProgress = false; + } else if (requestCode == REFOCUS_ACTIVITY_CODE) { + if(resultCode == RESULT_OK) { + mCaptureModule.setRefocusLastTaken(false); + } } else { super.onActivityResult(requestCode, resultCode, data); } diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 078707a7f..cc9de40c2 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -201,6 +201,7 @@ public class CaptureModule implements CameraModule, PhotoController, private CameraCharacteristics mMainCameraCharacteristics; private int mDisplayRotation; private int mDisplayOrientation; + private boolean mIsRefocus = false; /** * A {@link CameraCaptureSession } for camera preview. @@ -398,13 +399,14 @@ public class CaptureModule implements CameraModule, PhotoController, }); mFirstPreviewLoaded = true; } - - Face[] faces = result.get(CaptureResult.STATISTICS_FACES); - mPreviewFaces = faces; - if(faces != null && faces.length != 0) { - mStickyFaces = faces; + if (id == getMainCameraId()) { + Face[] faces = result.get(CaptureResult.STATISTICS_FACES); + mPreviewFaces = faces; + if (faces != null && faces.length != 0) { + mStickyFaces = faces; + } + mPreviewCaptureResult = result; } - mPreviewCaptureResult = result; updateCaptureStateMachine(id, result); } @@ -763,11 +765,23 @@ public class CaptureModule implements CameraModule, PhotoController, } } - private void setAFModeToPreview(int id, int afMode) { + public void setAFModeToPreview(int id, int afMode) { Log.d(TAG, "setAFModeToPreview " + afMode); mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AF_MODE, afMode); applyAFRegions(mPreviewRequestBuilder[id], id); applyAERegions(mPreviewRequestBuilder[id], id); + mPreviewRequestBuilder[id].setTag(id); + try { + mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id] + .build(), mCaptureCallback, mCameraHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + public void setFocusDistanceToPreview(int id, float fd) { + mPreviewRequestBuilder[id].set(CaptureRequest.LENS_FOCUS_DISTANCE, fd); + mPreviewRequestBuilder[id].setTag(id); try { mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id] .build(), mCaptureCallback, mCameraHandler); @@ -781,6 +795,10 @@ public class CaptureModule implements CameraModule, PhotoController, mSettingsManager.reinit(getMainCameraId()); } + public boolean isRefocus() { + return mIsRefocus; + } + public boolean getRecordLocation() { String value = mSettingsManager.getValue(SettingsManager.KEY_RECORD_LOCATION); if (value == null) value = RecordLocationPreference.VALUE_NONE; @@ -921,9 +939,32 @@ public class CaptureModule implements CameraModule, PhotoController, mPreviewRequestBuilder[id].set(BayerMonoLinkEnableKey, (byte) 0); } } - private void captureStillPicture(final int id) { Log.d(TAG, "captureStillPicture " + id); + mIsRefocus = false; + CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { + + @Override + public void onCaptureCompleted(CameraCaptureSession session, + CaptureRequest request, + TotalCaptureResult result) { + Log.d(TAG, "captureStillPicture onCaptureCompleted: " + id); + } + + @Override + public void onCaptureFailed(CameraCaptureSession session, + CaptureRequest request, + CaptureFailure result) { + Log.d(TAG, "captureStillPicture onCaptureFailed: " + id); + } + + @Override + public void onCaptureSequenceCompleted(CameraCaptureSession session, int + sequenceId, long frameNumber) { + Log.d(TAG, "captureStillPicture onCaptureSequenceCompleted: " + id); + unlockFocus(id); + } + }; try { if (null == mActivity || null == mCameraDevice[id]) { warningToast("Camera is not ready yet to take a picture."); @@ -951,31 +992,14 @@ public class CaptureModule implements CameraModule, PhotoController, ClearSightImageProcessor.getInstance().capture( id==BAYER_ID, mCaptureSession[id], captureBuilder, mCaptureCallbackHandler); } else if(id == getMainCameraId() && mPostProcessor.isFilterOn()) { + mCaptureSession[id].stopRepeating(); captureBuilder.addTarget(mImageReader[id].getSurface()); - List captureList = mPostProcessor.setRequiredImages(captureBuilder); - mCaptureSession[id].captureBurst(captureList, new CameraCaptureSession.CaptureCallback() { - - @Override - public void onCaptureCompleted(CameraCaptureSession session, - CaptureRequest request, - TotalCaptureResult result) { - Log.d(TAG, "captureStillPicture onCaptureCompleted: " + id); - } - - @Override - public void onCaptureFailed(CameraCaptureSession session, - CaptureRequest request, - CaptureFailure result) { - Log.d(TAG, "captureStillPicture onCaptureFailed: " + id); - } - - @Override - public void onCaptureSequenceCompleted(CameraCaptureSession session, int - sequenceId, long frameNumber) { - Log.d(TAG, "captureStillPicture onCaptureSequenceCompleted: " + id); - unlockFocus(id); - } - }, mCaptureCallbackHandler); + if(mPostProcessor.isManualMode()) { + mPostProcessor.manualCapture(captureBuilder, mCaptureSession[id], captureCallback, mCaptureCallbackHandler); + } else { + List captureList = mPostProcessor.setRequiredImages(captureBuilder); + mCaptureSession[id].captureBurst(captureList, captureCallback, mCaptureCallbackHandler); + } } else { captureBuilder.addTarget(mImageReader[id].getSurface()); mCaptureSession[id].stopRepeating(); @@ -1168,7 +1192,8 @@ public class CaptureModule implements CameraModule, PhotoController, // No Clearsight mImageReader[i] = ImageReader.newInstance(mPictureSize.getWidth(), mPictureSize.getHeight(), imageFormat, MAX_IMAGE_NUM); - if(mPostProcessor.isFilterOn() && i == getMainCameraId()) { + if((mPostProcessor.isFilterOn() || getFrameFilters().size() != 0) + && i == getMainCameraId()) { mImageReader[i].setOnImageAvailableListener(mPostProcessor, mImageAvailableHandler); } else { mImageReader[i].setOnImageAvailableListener(new ImageAvailableListener(i) { @@ -1523,11 +1548,22 @@ public class CaptureModule implements CameraModule, PhotoController, return filters; } + public void setRefocusLastTaken(final boolean value) { + mIsRefocus = value; + mActivity.runOnUiThread(new Runnable() { + public void run() { + mUI.showRefocusToast(value); + } + }); + } + private int getPostProcFilterId(int mode) { if (mode == SettingsManager.SCENE_MODE_OPTIZOOM_INT) { return PostProcessor.FILTER_OPTIZOOM; } else if (mode == SettingsManager.SCENE_MODE_NIGHT_INT && SharpshooterFilter.isSupportedStatic()) { return PostProcessor.FILTER_SHARPSHOOTER; + } else if (mode == SettingsManager.SCENE_MODE_UBIFOCUS_INT) { + return PostProcessor.FILTER_UBIFOCUS; } return PostProcessor.FILTER_NONE; } @@ -1563,7 +1599,7 @@ public class CaptureModule implements CameraModule, PhotoController, mFrameProcessor.onOpen(getFrameProcFilterId()); } - if(mPostProcessor.isFilterOn()) { + if(mPostProcessor.isFilterOn() || getFrameFilters().size() != 0) { setUpCameraOutputs(ImageFormat.YUV_420_888); } else { setUpCameraOutputs(ImageFormat.JPEG); @@ -2386,7 +2422,8 @@ public class CaptureModule implements CameraModule, PhotoController, if (seconds > 0) { mUI.startCountDown(seconds, true); } else { - if (mPostProcessor.isFilterOn() && mPostProcessor.isItBusy()) { + if((mPostProcessor.isFilterOn() || getFrameFilters().size() != 0) + && mPostProcessor.isItBusy()) { warningToast("It's still busy processing previous scene mode request."); return; } diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index cefc77651..30a9425b4 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -42,6 +42,8 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.camera.imageprocessor.filter.BeautificationFilter; +import com.android.camera.imageprocessor.filter.TrackingFocusFrameListener; import com.android.camera.ui.AutoFitSurfaceView; import com.android.camera.ui.Camera2FaceView; import com.android.camera.ui.CameraControls; @@ -61,6 +63,7 @@ import com.android.camera.util.CameraUtil; import org.codeaurora.snapcam.R; +import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -97,13 +100,13 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, SettingsManager.KEY_EXPOSURE, SettingsManager.KEY_WHITE_BALANCE, SettingsManager.KEY_CAMERA2, - SettingsManager.KEY_MAKEUP, SettingsManager.KEY_FACE_DETECTION, SettingsManager.KEY_VIDEO_FLASH_MODE, SettingsManager.KEY_VIDEO_DURATION, SettingsManager.KEY_VIDEO_QUALITY, - SettingsManager.KEY_TRACKINGFOCUS - }; + SettingsManager.KEY_TRACKINGFOCUS, + SettingsManager.KEY_MAKEUP + }; String[] mDeveloperKeys = new String[]{ SettingsManager.KEY_REDEYE_REDUCTION, SettingsManager.KEY_MONO_ONLY, @@ -388,7 +391,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, initializeSettingMenu(); initSceneModeButton(); initFilterModeButton(); - if(mTrackingFocusRenderer != null) { + if (mTrackingFocusRenderer != null) { mTrackingFocusRenderer.setVisible(true); } } @@ -1033,7 +1036,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, String[] keys = mSettingKeys; if (mActivity.isDeveloperMenuEnabled()) { - keys = mDeveloperKeys; String[] combined = new String[mSettingKeys.length + mDeveloperKeys.length]; int idx = 0; for (String key: mSettingKeys) { @@ -1358,6 +1360,10 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, return ret; } + public void showRefocusToast(boolean show) { + mCameraControls.showRefocusToast(show); + } + private FocusIndicator getFocusIndicator() { String trackingFocus = mSettingsManager.getValue(SettingsManager.KEY_TRACKINGFOCUS); if (trackingFocus != null && trackingFocus.equalsIgnoreCase("on")) { diff --git a/src/com/android/camera/RefocusActivity.java b/src/com/android/camera/RefocusActivity.java index ae13b484b..599d46656 100644 --- a/src/com/android/camera/RefocusActivity.java +++ b/src/com/android/camera/RefocusActivity.java @@ -30,6 +30,7 @@ package com.android.camera; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.io.OutputStream; import android.animation.Animator; @@ -43,6 +44,7 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.net.Uri; @@ -50,12 +52,15 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Build; import android.util.AttributeSet; +import android.util.Log; import android.view.Display; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import android.widget.FrameLayout; +import com.android.camera.exif.ExifInterface; + import org.codeaurora.snapcam.R; public class RefocusActivity extends Activity { @@ -77,19 +82,27 @@ public class RefocusActivity extends Activity { private int mCurrentImage = -1; private int mRequestedImage = -1; private LoadImageTask mLoadImageTask; + private boolean mMapRotated = false; + private int mOrientation = 0; + public static final int MAP_ROTATED = 1; + private String mFilesPath; @Override public void onCreate(Bundle state) { super.onCreate(state); + mFilesPath = getFilesDir()+""; + if(getIntent().getFlags() == MAP_ROTATED) { + mMapRotated = true; + mFilesPath = getFilesDir()+"/Ubifocus"; + } new Thread(new Runnable() { public void run() { - mDepthMap = new DepthMap(getFilesDir() + "/DepthMapImage.y"); + mDepthMap = new DepthMap(mFilesPath + "/DepthMapImage.y"); } }).start(); mUri = getIntent().getData(); - setResult(RESULT_CANCELED, new Intent()); setContentView(R.layout.refocus_editor); mIndicator = (Indicator) findViewById(R.id.refocus_indicator); @@ -129,6 +142,7 @@ public class RefocusActivity extends Activity { findViewById(R.id.refocus_cancel).setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { + setResult(RESULT_CANCELED, new Intent()); finish(); } }); @@ -137,11 +151,12 @@ public class RefocusActivity extends Activity { @Override public void onClick(final View v) { if (mRequestedImage != NAMES.length - 1) { - new SaveImageTask().execute(getFilesDir() + "/" + NAMES[mRequestedImage] + new SaveImageTask().execute(mFilesPath + "/" + NAMES[mRequestedImage] + ".jpg"); } else { finish(); } + setResult(RESULT_OK, new Intent()); } }); @@ -163,7 +178,7 @@ public class RefocusActivity extends Activity { if (depth != mCurrentImage) { mCurrentImage = depth; mLoadImageTask = new LoadImageTask(); - mLoadImageTask.execute(getFilesDir() + "/" + NAMES[depth] + ".jpg"); + mLoadImageTask.execute(mFilesPath + "/" + NAMES[depth] + ".jpg"); } } } @@ -202,11 +217,16 @@ public class RefocusActivity extends Activity { final BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeFile(path[0], o); - + ExifInterface exif = new ExifInterface(); + mOrientation = 0; + try { + exif.readExif(path[0]); + mOrientation = Exif.getOrientation(exif); + } catch (IOException e) { + } int h = o.outHeight; int w = o.outWidth; int sample = 1; - if (h > mHeight || w > mWidth) { while (h / sample / 2 > mHeight && w / sample / 2 > mWidth) { sample *= 2; @@ -215,7 +235,14 @@ public class RefocusActivity extends Activity { o.inJustDecodeBounds = false; o.inSampleSize = sample; - return BitmapFactory.decodeFile(path[0], o); + Bitmap bitmap = BitmapFactory.decodeFile(path[0], o); + if (mOrientation != 0) { + Matrix matrix = new Matrix(); + matrix.setRotate(mOrientation); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, + bitmap.getWidth(), bitmap.getHeight(), matrix, false); + } + return bitmap; } protected void onPostExecute(Bitmap result) { @@ -258,6 +285,21 @@ public class RefocusActivity extends Activity { int newX = (int) (x * mWidth); int newY = (int) (y * mHeight); + if(mMapRotated) { + if(mOrientation == 0) { + newX = (int) (x * mWidth); + newY = (int) (y * mHeight); + } if(mOrientation == 90) { + newX = (int) ((y) * mWidth); + newY = (int) ((1 - x) * mHeight); + } else if (mOrientation == 180) { + newX = (int) ((1-x) * mWidth); + newY = (int) ((1-y) * mHeight); + } else if (mOrientation == 270) { + newX = (int) ((1-y) * mWidth); + newY = (int) ((x) * mHeight); + } + } int[] hist = new int[256]; for (int i = 0; i < 256; i++) { diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java index b5ac8b3ee..7ab45fbdc 100644 --- a/src/com/android/camera/SettingsManager.java +++ b/src/com/android/camera/SettingsManager.java @@ -44,7 +44,10 @@ import android.util.Range; import android.util.Rational; import android.util.Size; +import com.android.camera.imageprocessor.filter.BeautificationFilter; import com.android.camera.imageprocessor.filter.OptizoomFilter; +import com.android.camera.imageprocessor.filter.TrackingFocusFrameListener; +import com.android.camera.imageprocessor.filter.UbifocusFilter; import com.android.camera.ui.ListMenu; import com.android.camera.util.SettingTranslation; @@ -71,6 +74,7 @@ public class SettingsManager implements ListMenu.SettingsListener { // Custom-Scenemodes start from 100 public static final int SCENE_MODE_DUAL_INT = 100; public static final int SCENE_MODE_OPTIZOOM_INT = 101; + public static final int SCENE_MODE_UBIFOCUS_INT = 102; public static final String SCENE_MODE_DUAL_STRING = "100"; public static final String KEY_CAMERA_SAVEPATH = "pref_camera2_savepath_key"; public static final String KEY_RECORD_LOCATION = "pref_camera2_recordlocation_key"; @@ -493,6 +497,8 @@ public class SettingsManager implements ListMenu.SettingsListener { ListPreference noiseReduction = mPreferenceGroup.findPreference(KEY_NOISE_REDUCTION); ListPreference videoFlash = mPreferenceGroup.findPreference(KEY_VIDEO_FLASH_MODE); ListPreference faceDetection = mPreferenceGroup.findPreference(KEY_FACE_DETECTION); + ListPreference makeup = mPreferenceGroup.findPreference(KEY_MAKEUP); + ListPreference trackingfocus = mPreferenceGroup.findPreference(KEY_TRACKINGFOCUS); if (whiteBalance != null) { CameraSettings.filterUnsupportedOptions(mPreferenceGroup, @@ -568,6 +574,16 @@ public class SettingsManager implements ListMenu.SettingsListener { if (!isFaceDetectionSupported(cameraId)) removePreference(mPreferenceGroup, KEY_FACE_DETECTION); } + + if (makeup != null) { + if (!BeautificationFilter.isSupportedStatic()) + removePreference(mPreferenceGroup, KEY_MAKEUP); + } + + if (trackingfocus != null) { + if (!TrackingFocusFrameListener.isSupportedStatic()) + removePreference(mPreferenceGroup, KEY_TRACKINGFOCUS); + } } private void buildExposureCompensation(int cameraId) { @@ -823,7 +839,8 @@ public class SettingsManager implements ListMenu.SettingsListener { List modes = new ArrayList<>(); modes.add("0"); // need special case handle for auto scene mode if (mIsMonoCameraPresent) modes.add(SCENE_MODE_DUAL_STRING); // need special case handle for dual mode - if (OptizoomFilter.isSupportedStatic()) modes.add(SCENE_MODE_OPTIZOOM_INT + ""); // need special case handle for dual mode + if (OptizoomFilter.isSupportedStatic()) modes.add(SCENE_MODE_OPTIZOOM_INT + ""); + if (UbifocusFilter.isSupportedStatic() && cameraId == CaptureModule.BAYER_ID) modes.add(SCENE_MODE_UBIFOCUS_INT + ""); for (int mode : sceneModes) { modes.add("" + mode); } diff --git a/src/com/android/camera/imageprocessor/PostProcessor.java b/src/com/android/camera/imageprocessor/PostProcessor.java index f39845346..8c0d2ff88 100644 --- a/src/com/android/camera/imageprocessor/PostProcessor.java +++ b/src/com/android/camera/imageprocessor/PostProcessor.java @@ -29,9 +29,12 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.android.camera.imageprocessor; import android.content.ContentResolver; +import android.content.Context; import android.graphics.ImageFormat; import android.graphics.Rect; import android.graphics.YuvImage; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CaptureRequest; import android.media.Image; import android.media.ImageReader; @@ -50,9 +53,11 @@ import com.android.camera.SettingsManager; import com.android.camera.exif.ExifInterface; import com.android.camera.imageprocessor.filter.OptizoomFilter; import com.android.camera.imageprocessor.filter.SharpshooterFilter; +import com.android.camera.imageprocessor.filter.UbifocusFilter; import com.android.camera.ui.RotateTextToast; import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -70,7 +75,8 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ public static final int FILTER_NONE = 0; public static final int FILTER_OPTIZOOM = 1; public static final int FILTER_SHARPSHOOTER = 2; - public static final int FILTER_MAX = 3; + public static final int FILTER_UBIFOCUS = 3; + public static final int FILTER_MAX = 4; private int mCurrentNumImage = 0; private ImageFilter mFilter; @@ -86,6 +92,7 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ private Image[] mImages; private PhotoModule.NamedImages mNamedImages; private WatchdogThread mWatchdog; + private int mOrientation = 0; //This is for the debug feature. private static boolean DEBUG_FILTER = false; @@ -139,11 +146,17 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ } } + public boolean isManualMode() { + return mFilter.isManualMode(); + } + + public void manualCapture(CaptureRequest.Builder builder, CameraCaptureSession captureSession, + CameraCaptureSession.CaptureCallback callback, Handler handler) throws CameraAccessException { + mFilter.manualCapture(builder, captureSession, callback, handler); + } + public boolean isFilterOn() { - if(mFilter != null) { - return true; - } - if(mController.getFrameFilters().size() != 0) { + if (mFilter != null) { return true; } return false; @@ -269,6 +282,9 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ case FILTER_SHARPSHOOTER: mFilter = new SharpshooterFilter(mController); break; + case FILTER_UBIFOCUS: + mFilter = new UbifocusFilter(mController, mActivity); + break; } } @@ -333,6 +349,7 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ if(mWatchdog != null) { mWatchdog.startMonitor(); } + mOrientation = CameraUtil.getJpegRotation(mController.getMainCameraId(), mController.getDisplayOrientation()); } if(mFilter != null && mCurrentNumImage >= mFilter.getNumRequiredImage()) { return; @@ -374,7 +391,7 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ }); } - private byte[] addExifTags(byte[] jpeg, int orientationInDegree) { + public static byte[] addExifTags(byte[] jpeg, int orientationInDegree) { ExifInterface exif = new ExifInterface(); exif.addOrientationTag(orientationInDegree); exif.addDateTimeStampTag(ExifInterface.TAG_DATE_TIME, System.currentTimeMillis(), @@ -434,17 +451,16 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ ) { Log.e(TAG, "Processed outRoi is not within picture range"); } else { - int orientation = CameraUtil.getJpegRotation(mController.getMainCameraId(), mController.getDisplayOrientation()); if(mFilter != null && DEBUG_FILTER) { - bytes = nv21ToJpeg(mDebugResultImage, orientation); + bytes = nv21ToJpeg(mDebugResultImage, mOrientation); mActivity.getMediaSaveService().addImage( bytes, title + "_beforeApplyingFilter", date, null, mDebugResultImage.outRoi.width(), mDebugResultImage.outRoi.height(), - orientation, null, mediaSavedListener, contentResolver, "jpeg"); + mOrientation, null, mediaSavedListener, contentResolver, "jpeg"); } - bytes = nv21ToJpeg(resultImage, orientation); + bytes = nv21ToJpeg(resultImage, mOrientation); mActivity.getMediaSaveService().addImage( bytes, title, date, null, resultImage.outRoi.width(), resultImage.outRoi.height(), - orientation, null, mediaSavedListener, contentResolver, "jpeg"); + mOrientation, null, mediaSavedListener, contentResolver, "jpeg"); mController.updateThumbnailJpegData(bytes); } } diff --git a/src/com/android/camera/imageprocessor/filter/BeautificationFilter.java b/src/com/android/camera/imageprocessor/filter/BeautificationFilter.java index 7a13895f4..77e6ead6e 100644 --- a/src/com/android/camera/imageprocessor/filter/BeautificationFilter.java +++ b/src/com/android/camera/imageprocessor/filter/BeautificationFilter.java @@ -30,8 +30,10 @@ package com.android.camera.imageprocessor.filter; import android.graphics.Rect; import android.hardware.Camera; +import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.Face; +import android.os.Handler; import android.util.Log; import android.util.Size; @@ -120,11 +122,26 @@ public class BeautificationFilter implements ImageFilter { return mIsSupported; } + public static boolean isSupportedStatic() { + return mIsSupported; + } + @Override public boolean isFrameListener() { return false; } + @Override + public boolean isManualMode() { + return false; + } + + @Override + public void manualCapture(CaptureRequest.Builder builder, CameraCaptureSession captureSession, + CameraCaptureSession.CaptureCallback callback, Handler handler) { + + } + private native int nativeBeautificationProcess(ByteBuffer yB, ByteBuffer vuB, int width, int height, int stride, int fleft, int ftop, int fright, int fbottom); diff --git a/src/com/android/camera/imageprocessor/filter/ImageFilter.java b/src/com/android/camera/imageprocessor/filter/ImageFilter.java index b778dee03..bb581c965 100644 --- a/src/com/android/camera/imageprocessor/filter/ImageFilter.java +++ b/src/com/android/camera/imageprocessor/filter/ImageFilter.java @@ -29,7 +29,10 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.android.camera.imageprocessor.filter; import android.graphics.Rect; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CaptureRequest; +import android.os.Handler; import java.nio.ByteBuffer; import java.util.List; @@ -41,6 +44,7 @@ public interface ImageFilter { String getStringName(); + /* This is used for auto mode burst picture */ int getNumRequiredImage(); void init(int width, int height, int strideY, int strideVU); @@ -72,5 +76,13 @@ public interface ImageFilter { } } + /* Whether it is post proc filter or frame proc filter */ boolean isFrameListener(); + + /* Whether it will use burst capture or manual capture */ + boolean isManualMode(); + + /* if it's manual mode, this function has to be implemented */ + void manualCapture(CaptureRequest.Builder builder, CameraCaptureSession captureSession, + CameraCaptureSession.CaptureCallback callback, Handler handler) throws CameraAccessException; } diff --git a/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java b/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java index 16b8ae163..486ea7a0c 100644 --- a/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java +++ b/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java @@ -29,7 +29,9 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.android.camera.imageprocessor.filter; import android.graphics.Rect; +import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CaptureRequest; +import android.os.Handler; import android.util.Log; import com.android.camera.CaptureModule; @@ -132,6 +134,16 @@ public class OptizoomFilter implements ImageFilter{ return false; } + @Override + public boolean isManualMode() { + return false; + } + + @Override + public void manualCapture(CaptureRequest.Builder builder, CameraCaptureSession captureSession, + CameraCaptureSession.CaptureCallback callback, Handler handler) { + } + public static boolean isSupportedStatic() { return mIsSupported; } diff --git a/src/com/android/camera/imageprocessor/filter/SharpshooterFilter.java b/src/com/android/camera/imageprocessor/filter/SharpshooterFilter.java index e123f3439..c51e13c59 100644 --- a/src/com/android/camera/imageprocessor/filter/SharpshooterFilter.java +++ b/src/com/android/camera/imageprocessor/filter/SharpshooterFilter.java @@ -29,9 +29,11 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package com.android.camera.imageprocessor.filter; import android.graphics.Rect; +import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.os.Handler; import android.util.Log; import android.util.Range; import android.util.Rational; @@ -155,6 +157,17 @@ public class SharpshooterFilter implements ImageFilter{ return false; } + @Override + public boolean isManualMode() { + return false; + } + + @Override + public void manualCapture(CaptureRequest.Builder builder, CameraCaptureSession captureSession, + CameraCaptureSession.CaptureCallback callback, Handler handler) { + + } + public static boolean isSupportedStatic() { return mIsSupported; } diff --git a/src/com/android/camera/imageprocessor/filter/TrackingFocusFrameListener.java b/src/com/android/camera/imageprocessor/filter/TrackingFocusFrameListener.java index a12eef91a..8d9a7fc1a 100644 --- a/src/com/android/camera/imageprocessor/filter/TrackingFocusFrameListener.java +++ b/src/com/android/camera/imageprocessor/filter/TrackingFocusFrameListener.java @@ -32,8 +32,10 @@ package com.android.camera.imageprocessor.filter; import android.graphics.Point; import android.graphics.Rect; import android.hardware.Camera; +import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.Face; +import android.os.Handler; import android.util.Log; import android.util.Size; @@ -256,11 +258,26 @@ public class TrackingFocusFrameListener implements ImageFilter { return mIsSupported; } + public static boolean isSupportedStatic() { + return mIsSupported; + } + @Override public boolean isFrameListener() { return true; } + @Override + public boolean isManualMode() { + return false; + } + + @Override + public void manualCapture(CaptureRequest.Builder builder, CameraCaptureSession captureSession, + CameraCaptureSession.CaptureCallback callback, Handler handler) { + + } + private native int nInit(int operationMode, int precision, int width, int height, int stride); private native void nRelease(); private native int nGetMinRoiDimension(); diff --git a/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java b/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java new file mode 100644 index 000000000..b410e01b4 --- /dev/null +++ b/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java @@ -0,0 +1,269 @@ +/* +Copyright (c) 2016, The Linux Foundation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.android.camera.imageprocessor.filter; + +import android.app.ProgressDialog; +import android.content.Context; +import android.graphics.Camera; +import android.graphics.ImageFormat; +import android.graphics.Rect; +import android.graphics.YuvImage; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.os.Handler; +import android.util.Log; +import android.util.Range; + +import com.android.camera.CameraActivity; +import com.android.camera.CaptureModule; +import com.android.camera.imageprocessor.PostProcessor; +import com.android.camera.util.CameraUtil; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class UbifocusFilter implements ImageFilter { + public static final int NUM_REQUIRED_IMAGE = 5; + private int mWidth; + private int mHeight; + private int mStrideY; + private int mStrideVU; + private static String TAG = "UbifocusFilter"; + private static final boolean DEBUG = true; + private static final int FOCUS_ADJUST_TIME_OUT = 200; + private static final int META_BYTES_SIZE = 25; + private int temp; + private static boolean mIsSupported = true; + private ByteBuffer mOutBuf; + private CaptureModule mModule; + private CameraActivity mActivity; + private int mOrientation = 0; + private float mMinFocusDistance = -1f; + final String[] NAMES = {"00.jpg", "01.jpg", "02.jpg", "03.jpg", + "04.jpg", "DepthMapImage.y", "AllFocusImage.jpg"}; + + private static void Log(String msg) { + if (DEBUG) { + Log.d(TAG, msg); + } + } + + public UbifocusFilter(CaptureModule module, CameraActivity activity) { + mModule = module; + mActivity = activity; + } + + @Override + public List setRequiredImages(CaptureRequest.Builder builder) { + return null; + } + + @Override + public String getStringName() { + return "UbifocusFilter"; + } + + @Override + public int getNumRequiredImage() { + return NUM_REQUIRED_IMAGE; + } + + @Override + public void init(int width, int height, int strideY, int strideVU) { + Log("init"); + mWidth = width/2*2; + mHeight = height/2*2; + mStrideY = strideY/2*2; + mStrideVU = strideVU/2*2; + mOutBuf = ByteBuffer.allocate(mStrideY * mHeight * 3 / 2); + Log("width: "+mWidth+" height: "+mHeight+" strideY: "+mStrideY+" strideVU: "+mStrideVU); + nativeInit(mWidth, mHeight, mStrideY, mStrideVU, NUM_REQUIRED_IMAGE); + } + + @Override + public void deinit() { + Log("deinit"); + mOutBuf = null; + nativeDeinit(); + } + + @Override + public void addImage(ByteBuffer bY, ByteBuffer bVU, int imageNum, Object param) { + Log("addImage"); + if(imageNum == 0) { + mModule.setRefocusLastTaken(false); + mOrientation = CameraUtil.getJpegRotation(mModule.getMainCameraId(), mModule.getDisplayOrientation()); + } + saveToPrivateFile(imageNum, nv21ToJpeg(bY, bVU, new Rect(0, 0, mWidth, mHeight), mOrientation)); + int yActualSize = bY.remaining(); + int vuActualSize = bVU.remaining(); + if(nativeAddImage(bY, bVU, yActualSize, vuActualSize, imageNum) < 0) { + Log.e(TAG, "Fail to add image"); + } + } + + @Override + public ResultImage processImage() { + Log("processImage "); + int[] roi = new int[4]; + int[] depthMapSize = new int[2]; + int status = nativeProcessImage(mOutBuf.array(), roi, depthMapSize); + if(status < 0) { //In failure case, library will return the first image as it is. + Log.w(TAG, "Fail to process the "+getStringName()); + } else { + byte[] depthMapBuf = new byte[depthMapSize[0] * depthMapSize[1] + META_BYTES_SIZE]; + nativeGetDepthMap(depthMapBuf, depthMapSize[0], depthMapSize[1]); + saveToPrivateFile(NAMES.length - 2, depthMapBuf); + saveToPrivateFile(NAMES.length - 1, nv21ToJpeg(mOutBuf, null, new Rect(roi[0], roi[1], roi[0] + roi[2], roi[1] + roi[3]), mOrientation)); + mModule.setRefocusLastTaken(true); + } + + ResultImage result = new ResultImage(mOutBuf, new Rect(roi[0], roi[1], roi[0]+roi[2], roi[1] + roi[3]), mWidth, mHeight, mStrideY); + Log("processImage done"); + return result; + } + + @Override + public boolean isSupported() { + return mIsSupported; + } + + @Override + public boolean isFrameListener() { + return false; + } + + @Override + public boolean isManualMode() { + return true; + } + + @Override + public void manualCapture(CaptureRequest.Builder builder, CameraCaptureSession captureSession, + CameraCaptureSession.CaptureCallback callback, Handler handler) throws CameraAccessException { + if (mMinFocusDistance == -1f) { + mMinFocusDistance = mModule.getMainCameraCharacteristics().get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); + } + float step = mMinFocusDistance / NUM_REQUIRED_IMAGE; + for(int i=0; i < NUM_REQUIRED_IMAGE; i++) { + float value = (i * step); + mModule.setAFModeToPreview(mModule.getMainCameraId(), CaptureRequest.CONTROL_AF_MODE_OFF); + mModule.setFocusDistanceToPreview(mModule.getMainCameraId(), value); + try { + int count = FOCUS_ADJUST_TIME_OUT; + do { + Thread.sleep(5); + count -= 5; + if(count <= 0) { + break; + } + } while(Math.abs(mModule.getPreviewCaptureResult().get(CaptureResult.LENS_FOCUS_DISTANCE) + - value) >= 0.5f); + } catch (InterruptedException e) { + } + builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF); + builder.set(CaptureRequest.LENS_FOCUS_DISTANCE, value); + captureSession.capture(builder.build(), callback, handler); + Log.d(TAG, "Request: " + value); + } + } + + public static boolean isSupportedStatic() { + return mIsSupported; + } + + private byte[] nv21ToJpeg(ByteBuffer bY, ByteBuffer bVU, Rect roi, int orientation) { + ByteBuffer buf = ByteBuffer.allocate(mStrideY*mHeight*3/2); + buf.put(bY); + bY.rewind(); + if(bVU != null) { + buf.put(bVU); + bVU.rewind(); + } + BitmapOutputStream bos = new BitmapOutputStream(1024); + YuvImage im = new YuvImage(buf.array(), ImageFormat.NV21, + mWidth, mHeight, new int[]{mStrideY, mStrideVU}); + im.compressToJpeg(roi, 50, bos); + byte[] bytes = bos.getArray(); + bytes = PostProcessor.addExifTags(bytes, orientation); + return bytes; + } + + private class BitmapOutputStream extends ByteArrayOutputStream { + public BitmapOutputStream(int size) { + super(size); + } + + public byte[] getArray() { + return buf; + } + } + + private void saveToPrivateFile(final int index, final byte[] bytes) { + new Thread() { + public void run() { + String filesPath = mActivity.getFilesDir()+"/Ubifocus"; + File file = new File(filesPath); + if(!file.exists()) { + file.mkdir(); + } + file = new File(filesPath+"/"+NAMES[index]); + try { + FileOutputStream out = new FileOutputStream(file); + out.write(bytes, 0, bytes.length); + out.close(); + } catch (Exception e) { + } + } + }.start(); + } + + private native int nativeInit(int width, int height, int yStride, int vuStride, int numImages); + private native int nativeDeinit(); + private native int nativeAddImage(ByteBuffer yB, ByteBuffer vuB, int ySize, int vuSize, int imageNum); + private native int nativeGetDepthMap(byte[] depthMapBuf, int depthMapWidth, int depthMapHeight); + private native int nativeProcessImage(byte[] buffer, int[] roi, int[] depthMapSize); + + static { + try { + System.loadLibrary("jni_ubifocus"); + mIsSupported = true; + }catch(UnsatisfiedLinkError e) { + mIsSupported = false; + } + } +} -- cgit v1.2.3