diff options
author | Camera Software Integration <camswint@localhost> | 2017-04-28 12:14:17 +0530 |
---|---|---|
committer | Camera Software Integration <camswint@localhost> | 2017-04-28 12:30:27 +0530 |
commit | d35d20015170337d5b6092e852b2f54fde487ce3 (patch) | |
tree | 03782f3cae12ac14e3e4a7100cff82f827e5333e | |
parent | d4ab65586d0fd74497e942c66186c9a35584bc6c (diff) | |
parent | d72b5762c1a4be9d8818d88b138182721bad3eca (diff) | |
download | android_packages_apps_Snap-d35d20015170337d5b6092e852b2f54fde487ce3.tar.gz android_packages_apps_Snap-d35d20015170337d5b6092e852b2f54fde487ce3.tar.bz2 android_packages_apps_Snap-d35d20015170337d5b6092e852b2f54fde487ce3.zip |
Merge tag 'camera.lnx.1.0-00238' into camera.lnx.2.0-dev
Merge tag 'camera.lnx.1.0-00238' into camera.lnx.2.0-dev
Change-Id: I45706fe72d038eea45cbf63480e28a7dc66d34b6
-rw-r--r-- | res/values/arrays.xml | 8 | ||||
-rw-r--r--[-rwxr-xr-x] | res/values/qcomstrings.xml | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | src/com/android/camera/AndroidCameraManagerImpl.java | 1 | ||||
-rwxr-xr-x | src/com/android/camera/CaptureModule.java | 6 | ||||
-rw-r--r--[-rwxr-xr-x] | src/com/android/camera/MediaSaveService.java | 183 | ||||
-rw-r--r-- | src/com/android/camera/VideoModule.java | 2 | ||||
-rw-r--r-- | src/com/android/camera/imageprocessor/filter/StillmoreFilter.java | 12 | ||||
-rw-r--r--[-rwxr-xr-x] | src/com/android/camera/util/PersistUtil.java | 36 | ||||
-rw-r--r-- | src/com/android/camera/util/XmpUtil.java | 230 | ||||
-rw-r--r--[-rwxr-xr-x] | src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java | 329 | ||||
-rw-r--r-- | src/org/codeaurora/snapcam/filter/ClearSightNativeEngine.java | 4 | ||||
-rw-r--r-- | src/org/codeaurora/snapcam/filter/DDMNativeEngine.java | 396 | ||||
-rw-r--r-- | src/org/codeaurora/snapcam/filter/GDepth.java | 307 | ||||
-rw-r--r-- | src/org/codeaurora/snapcam/filter/GImage.java | 68 |
14 files changed, 1526 insertions, 56 deletions
diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 7f6143e6e..40b55892f 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -361,13 +361,13 @@ <!-- Videocamera Preferences flash mode dialog box entries --> <string-array name="pref_camera_video_flashmode_entries" translatable="false"> - <item>@string/pref_camera_flashmode_entry_on</item> <item>@string/pref_camera_flashmode_entry_off</item> + <item>@string/pref_camera_flashmode_entry_on</item> </string-array> <string-array name="pref_camera_video_flashmode_labels" translatable="false"> - <item>@string/pref_camera_flashmode_label_on</item> <item>@string/pref_camera_flashmode_label_off</item> + <item>@string/pref_camera_flashmode_label_on</item> </string-array> <string-array name="pref_camera_video_flashmode_entryvalues" translatable="false"> @@ -376,13 +376,13 @@ </string-array> <array name="video_flashmode_icons" translatable="false"> - <item>@drawable/ic_flash_on_holo_light</item> <item>@drawable/ic_flash_off_holo_light</item> + <item>@drawable/ic_flash_on_holo_light</item> </array> <array name="video_flashmode_largeicons" translatable="false"> - <item>@drawable/ic_flash_on_holo_light</item> <item>@drawable/ic_flash_off_holo_light</item> + <item>@drawable/ic_flash_on_holo_light</item> </array> <string-array name="pref_camera_recordlocation_entryvalues" translatable="false"> diff --git a/res/values/qcomstrings.xml b/res/values/qcomstrings.xml index 6603701de..6603701de 100755..100644 --- a/res/values/qcomstrings.xml +++ b/res/values/qcomstrings.xml diff --git a/src/com/android/camera/AndroidCameraManagerImpl.java b/src/com/android/camera/AndroidCameraManagerImpl.java index 4decaec6c..987691bb6 100644..100755 --- a/src/com/android/camera/AndroidCameraManagerImpl.java +++ b/src/com/android/camera/AndroidCameraManagerImpl.java @@ -431,7 +431,6 @@ class AndroidCameraManagerImpl implements CameraManager { } return; } - throw e; } } } diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index c225160b7..c4f56f2a6 100755 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -76,6 +76,7 @@ import android.provider.MediaStore; import android.util.Log; import android.util.Range; import android.util.Size; +import android.view.Gravity; import android.view.KeyEvent; import android.view.OrientationEventListener; import android.view.Surface; @@ -4684,6 +4685,7 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onClearSightSuccess(byte[] thumbnailBytes) { Log.d(TAG, "onClearSightSuccess"); + onReleaseShutterLock(); if(thumbnailBytes != null) mActivity.updateThumbnail(thumbnailBytes); mActivity.runOnUiThread(new Runnable() { @Override @@ -4706,8 +4708,7 @@ public class CaptureModule implements CameraModule, PhotoController, } }); - unlockFocus(BAYER_ID); - unlockFocus(MONO_ID); + onReleaseShutterLock(); } /** @@ -4861,6 +4862,7 @@ public class CaptureModule implements CameraModule, PhotoController, private void showToast(String tips) { if (mToast == null) { mToast = Toast.makeText(mActivity, tips, Toast.LENGTH_LONG); + mToast.setGravity(Gravity.CENTER, 0, 0); } mToast.setText(tips); mToast.show(); diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java index 2d7247d83..6ca37b9e2 100755..100644 --- a/src/com/android/camera/MediaSaveService.java +++ b/src/com/android/camera/MediaSaveService.java @@ -16,6 +16,8 @@ package com.android.camera; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.nio.ByteOrder; @@ -23,7 +25,10 @@ import android.app.Service; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Intent; +import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.Rect; import android.location.Location; import android.net.Uri; import android.os.AsyncTask; @@ -36,6 +41,14 @@ import com.android.camera.exif.ExifInterface; import com.android.camera.mpo.MpoData; import com.android.camera.mpo.MpoImageData; import com.android.camera.mpo.MpoInterface; +import com.android.camera.util.XmpUtil; + +import org.codeaurora.snapcam.filter.GDepth; +import org.codeaurora.snapcam.filter.GImage; + +import com.adobe.xmp.XMPException; +import com.adobe.xmp.XMPMeta; + /* * Service for saving images in the background thread. @@ -148,6 +161,25 @@ public class MediaSaveService extends Service { t.execute(); } + public void addClearsightImage(byte[] clearsight, GImage bayer, GDepth.DepthMap depthMap, + String title, long date, Location loc, int width, int height, + int orientation, ExifInterface exif, + OnMediaSavedListener l, ContentResolver resolver, String pictureFormat) { + if (isQueueFull()) { + Log.e(TAG, "Cannot add image when the queue is full"); + return; + } + ClearsightImageSaveTask t = new ClearsightImageSaveTask(clearsight, bayer, depthMap, + title, date, (loc == null) ? null : new Location(loc), + width, height, orientation, exif, resolver, l, pictureFormat); + + mMemoryUse += clearsight.length; + if (isQueueFull()) { + onQueueFull(); + } + t.execute(); + } + public void addImage(final byte[] data, String title, long date, Location loc, int orientation, ExifInterface exif, OnMediaSavedListener l, ContentResolver resolver) { @@ -350,6 +382,157 @@ public class MediaSaveService extends Service { } } + private class ClearsightImageSaveTask extends AsyncTask <Void, Void, Uri> { + private byte[] clearsight; + private byte[] depth; + private GImage bayer; + private GDepth.DepthMap depthMap; + private GDepth gDepth; + private byte[] data; + private String title; + private long date; + private Location loc; + private int width, height; + private int orientation; + private ExifInterface exif; + private ContentResolver resolver; + private OnMediaSavedListener listener; + private String pictureFormat; + + public ClearsightImageSaveTask(byte[] clearsight, GImage bayer,GDepth.DepthMap depthMap, + String title, long date, Location loc, + int width, int height, int orientation, + ExifInterface exif, ContentResolver resolver, + OnMediaSavedListener listener, String pictureFormat) { + this.clearsight = clearsight; + this.bayer = bayer; + this.depthMap = depthMap; + this.title = title; + this.date = date; + this.loc = loc; + this.width = width; + this.height = height; + this.orientation = orientation; + this.exif = exif; + this.resolver = resolver; + this.listener = listener; + this.pictureFormat = pictureFormat; + + gDepth = null; + } + + @Override + protected void onPreExecute() { + // do nothing. + } + + @Override + protected Uri doInBackground(Void... v) { + if ( depthMap != null ) { + depthMap.buffer = converToJpegByte(depthMap.rawDepth, depthMap.width, depthMap.height); + gDepth = GDepth.createGDepth(depthMap); + } + data = embedGDepthAndBayerInClearSight(clearsight); + if ( data == null ) { + data = clearsight; + Log.e(TAG, "embedGDepthAndBayerInClearSight fail"); + } + + if (width == 0 || height == 0) { + // Decode bounds + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeByteArray(data, 0, data.length, options); + width = options.outWidth; + height = options.outHeight; + } + return Storage.addImage( + resolver, title, date, loc, orientation, exif, data, width, height, pictureFormat); + } + + @Override + protected void onPostExecute(Uri uri) { + if (listener != null) listener.onMediaSaved(uri); + boolean previouslyFull = isQueueFull(); + mMemoryUse -= data.length; + if (isQueueFull() != previouslyFull) onQueueAvailable(); + } + + private byte[] converToJpegByte(byte[] depthBuf, int width, int height) { + int[] colors = new int[depthBuf.length]; + for(int i=0; i < colors.length; ++i) { + colors[i] = (256+depthBuf[i])%256; + } + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + for( int y=0; y < height; ++y ) { + for( int x=0; x < width; ++x) { + int c = colors[y*width+x]; + bitmap.setPixel(x, y, Color.rgb(c, c, c)); + } + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); + return baos.toByteArray(); + } + + private byte[] embedGDepthAndBayerInClearSight(byte[] clearSightImageBytes) { + Log.d(TAG, "embedGDepthInClearSight"); + if ( clearSightImageBytes == null || (gDepth ==null && bayer==null) ) { + Log.d(TAG, "clearSightImageBytes is null"); + return null; + } + + XMPMeta xmpMeta = XmpUtil.createXMPMeta(); + try { + if ( gDepth != null ) { + xmpMeta.setProperty(GDepth.NAMESPACE_URL, GDepth.PROPERTY_MIME, gDepth.getMime()); + xmpMeta.setProperty(GDepth.NAMESPACE_URL, GDepth.PROPERTY_NEAR, gDepth.getNear()); + xmpMeta.setProperty(GDepth.NAMESPACE_URL, GDepth.PROPERTY_FAR, gDepth.getFar()); + xmpMeta.setProperty(GDepth.NAMESPACE_URL, GDepth.PROPERTY_FORMAT, gDepth.getFormat()); + //extend for ROI + Rect roi = gDepth.getRoi(); + xmpMeta.setProperty(GDepth.NAMESPACE_URL, GDepth.PROPERTY_ROI_X, roi.left); + xmpMeta.setProperty(GDepth.NAMESPACE_URL, GDepth.PROPERTY_ROI_Y, roi.top); + xmpMeta.setProperty(GDepth.NAMESPACE_URL, GDepth.PROPERTY_ROI_WIDTH, roi.width()); + xmpMeta.setProperty(GDepth.NAMESPACE_URL, GDepth.PROPERTY_ROI_HEIGHT, roi.height()); + } + + if ( bayer != null ) { + xmpMeta.setProperty(GImage.NAMESPACE_URL, GImage.PROPERTY_MIME, bayer.getMime()); + } + + + } catch(XMPException exception) { + Log.d(TAG, "create XMPMeta error", exception); + return null; + } + + XMPMeta extendXmpMeta = XmpUtil.createXMPMeta(); + try{ + if ( gDepth != null) { + extendXmpMeta.setProperty(GDepth.NAMESPACE_URL, GDepth.PROPERTY_DATA, gDepth.getData()); + } + + if ( bayer != null ) { + extendXmpMeta.setProperty(GImage.NAMESPACE_URL, GImage.PROPERTY_DATA, bayer.getData()); + } + }catch(XMPException exception) { + Log.d(TAG, "create extended XMPMeta error", exception); + } + + + ByteArrayInputStream bais = new ByteArrayInputStream(clearSightImageBytes); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + if ( XmpUtil.writeXMPMeta(bais, baos, xmpMeta, extendXmpMeta) ){ + return baos.toByteArray(); + }else{ + Log.e(TAG, "embedGDepthInClearSight failure "); + return null; + } + + } + } + private class VideoSaveTask extends AsyncTask <Void, Void, Uri> { private String path; private long duration; diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index 103529bb8..830c04079 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -1136,7 +1136,6 @@ public class VideoModule implements CameraModule, if(mWasMute != mIsMute) { setMute(mIsMute, false); } - initializeVideoControl(); showVideoSnapshotUI(false); installIntentFilter(); @@ -1153,6 +1152,7 @@ public class VideoModule implements CameraModule, mUI.enableShutter(true); } + initializeVideoControl(); mUI.applySurfaceChange(VideoUI.SURFACE_STATUS.SURFACE_VIEW); mUI.initDisplayChangeListener(); diff --git a/src/com/android/camera/imageprocessor/filter/StillmoreFilter.java b/src/com/android/camera/imageprocessor/filter/StillmoreFilter.java index 2f483bfdc..34784faa0 100644 --- a/src/com/android/camera/imageprocessor/filter/StillmoreFilter.java +++ b/src/com/android/camera/imageprocessor/filter/StillmoreFilter.java @@ -38,13 +38,14 @@ import android.util.Log; import android.util.Range; import com.android.camera.CaptureModule; +import com.android.camera.util.PersistUtil; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; public class StillmoreFilter implements ImageFilter{ - public static final int NUM_REQUIRED_IMAGE = 5; + public static final int NUM_REQUIRED_IMAGE = PersistUtil.getStillmoreNumRequiredImages(); private int mWidth; private int mHeight; private int mStrideY; @@ -99,6 +100,13 @@ public class StillmoreFilter implements ImageFilter{ Log("width: "+mWidth+" height: "+mHeight+" strideY: "+mStrideY+" strideVU: "+mStrideVU); nativeInit(mWidth, mHeight, mStrideY, mStrideVU, 0, 0, mWidth, mHeight, NUM_REQUIRED_IMAGE); + float brColor = PersistUtil.getStillmoreBrColor(); + float brIntensity = PersistUtil.getStillmoreBrIntensity(); + float smoothingintensity = PersistUtil.getStillmoreSmoothingIntensity(); + nativeConfigureStillMore(brColor, brIntensity, smoothingintensity); + Log("ConfigureStillmore brColor: " + brColor + " brIntensity: " + + brIntensity + " smoothingintensity: " + smoothingintensity + + " NUM_REQUIRED_IMAGE: " + NUM_REQUIRED_IMAGE); } @Override @@ -156,6 +164,8 @@ public class StillmoreFilter implements ImageFilter{ return mIsSupported; } + private native int nativeConfigureStillMore(float brColor, float brIntensity, + float smoothingintensity); private native int nativeInit(int width, int height, int yStride, int vuStride, int roiX, int roiY, int roiW, int roiH, int numImages); private native int nativeDeinit(); diff --git a/src/com/android/camera/util/PersistUtil.java b/src/com/android/camera/util/PersistUtil.java index 69860491e..c22df6263 100755..100644 --- a/src/com/android/camera/util/PersistUtil.java +++ b/src/com/android/camera/util/PersistUtil.java @@ -46,8 +46,16 @@ public class PersistUtil { SystemProperties.getBoolean("persist.camera.zsl.disabled", false); private static final int PERSIST_CAMERA_CANCEL_TOUCHFOCUS_DELAY = SystemProperties.getInt("persist.camera.focus_delay", 5000); - private static final int PERSIST_CAMERA2_DEBUG = - SystemProperties.getInt("persist.camera2.debug", 0); + private static final int PERSIST_CAMERA_DEBUG = + SystemProperties.getInt("persist.camera.debug", 0); + private static final String PERSIST_CAMERA_STILLMORE_BRCOLR = + SystemProperties.get("persist.camera.stm_brcolor", "0.5"); + private static final String PERSIST_CAMERA_STILLMORE_BRINTENSITY = + SystemProperties.get("persist.camera.stm_brintensity", "0.6"); + private static final String PERSIST_CAMERA_STILLMORE_SMOOTHINGINTENSITY = + SystemProperties.get("persist.camera.stm_smooth", "0"); + private static final int PERSIST_CAMERA_STILLMORE_NUM_REQUIRED_IMAGE = + SystemProperties.getInt("persist.camera.stm_img_nums", 5); public static final int CAMERA2_DEBUG_DUMP_IMAGE = 1; public static final int CAMERA2_DEBUG_DUMP_LOG = 2; @@ -78,7 +86,29 @@ public class PersistUtil { } public static int getCamera2Debug() { - return PERSIST_CAMERA2_DEBUG; + return PERSIST_CAMERA_DEBUG; + } + + public static float getStillmoreBrColor(){ + float brColor = Float.parseFloat(PERSIST_CAMERA_STILLMORE_BRCOLR); + return brColor = (brColor < 0 || brColor > 1) ? 0.5f : brColor; + } + + public static float getStillmoreBrIntensity(){ + float brIntensity = Float.parseFloat(PERSIST_CAMERA_STILLMORE_BRINTENSITY); + return brIntensity = (brIntensity < 0 || brIntensity > 1) ? 0.6f : brIntensity; + } + + public static float getStillmoreSmoothingIntensity(){ + float smoothingIntensity = Float.parseFloat(PERSIST_CAMERA_STILLMORE_SMOOTHINGINTENSITY); + return smoothingIntensity = (smoothingIntensity < 0 || smoothingIntensity > 1) ? + 0f : smoothingIntensity; + } + + public static int getStillmoreNumRequiredImages() { + return (PERSIST_CAMERA_STILLMORE_NUM_REQUIRED_IMAGE < 3 || + PERSIST_CAMERA_STILLMORE_NUM_REQUIRED_IMAGE > 5) ? + 5 : PERSIST_CAMERA_STILLMORE_NUM_REQUIRED_IMAGE; } public static int getCancelTouchFocusDelay() { diff --git a/src/com/android/camera/util/XmpUtil.java b/src/com/android/camera/util/XmpUtil.java index c985a6bd8..5fcfa1aa5 100644 --- a/src/com/android/camera/util/XmpUtil.java +++ b/src/com/android/camera/util/XmpUtil.java @@ -30,7 +30,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Formatter; import java.util.List; /** @@ -52,6 +56,13 @@ public class XmpUtil { private static final String XMP_HEADER = "http://ns.adobe.com/xap/1.0/\0"; private static final int MAX_XMP_BUFFER_SIZE = 65502; + private static final String EXTENDED_XMP_HEADER_SIGNATURE = "http://ns.adobe.com/xmp/extension/\0"; + private static final String XMP_NOTE_NAMESPACE = "http://ns.adobe.com/xmp/note/"; + private static final String NOTE_PREFIX = "xmpNote"; + + private static final int MAX_EXTENDED_XMP_BUFFER_SIZE = 65000; + private static final int EXTEND_XMP_HEADER_SIZE = 75; + private static final String GOOGLE_PANO_NAMESPACE = "http://ns.google.com/photos/1.0/panorama/"; private static final String PANO_PREFIX = "GPano"; @@ -71,6 +82,9 @@ public class XmpUtil { try { XMPMetaFactory.getSchemaRegistry().registerNamespace( GOOGLE_PANO_NAMESPACE, PANO_PREFIX); + + XMPMetaFactory.getSchemaRegistry().registerNamespace( + XMP_NOTE_NAMESPACE, NOTE_PREFIX); } catch (XMPException e) { e.printStackTrace(); } @@ -401,5 +415,221 @@ public class XmpUtil { } } + private static Section createStandardXMPSection(XMPMeta meta) { + byte[] buffer; + try { + SerializeOptions options = new SerializeOptions(); + options.setUseCompactFormat(true); + // We have to omit packet wrapper here because + // javax.xml.parsers.DocumentBuilder + // fails to parse the packet end <?xpacket end="w"?> in android. + options.setOmitPacketWrapper(true); + buffer = XMPMetaFactory.serializeToBuffer(meta, options); + } catch (XMPException e) { + Log.d(TAG, "Serialize xmp failed", e); + return null; + } + if (buffer.length > MAX_XMP_BUFFER_SIZE) { + Log.e(TAG, "exceed max size"); + return null; + } + // The XMP section starts with XMP_HEADER and then the real xmp data. + byte[] xmpdata = new byte[buffer.length + XMP_HEADER_SIZE]; + System.arraycopy(XMP_HEADER.getBytes(), 0, xmpdata, 0, XMP_HEADER_SIZE); + System.arraycopy(buffer, 0, xmpdata, XMP_HEADER_SIZE, buffer.length); + Section xmpSection = new Section(); + xmpSection.marker = M_APP1; + // Adds the length place (2 bytes) to the section length. + xmpSection.length = xmpdata.length + 2; + xmpSection.data = xmpdata; + + return xmpSection; + } + + private static Section createSection(byte[] portionOfExtendedMeta, byte[] headerBytes) { + + if (portionOfExtendedMeta.length > MAX_EXTENDED_XMP_BUFFER_SIZE) { + // Do not support extended xmp now. + Log.e(TAG, "createSection fail exceed max size"); + return null; + } + + byte[] xmpdata = new byte[portionOfExtendedMeta.length + 75]; + System.arraycopy(headerBytes, 0, xmpdata, 0, headerBytes.length); + + System.arraycopy(portionOfExtendedMeta, 0, xmpdata, headerBytes.length, portionOfExtendedMeta.length); + Section xmpSection = new Section(); + xmpSection.marker = M_APP1; + // Adds the length place (2 bytes) to the section length. + xmpSection.length = xmpdata.length + 2; + xmpSection.data = xmpdata; + ByteBuffer byteBuffer2 = ByteBuffer.wrap(xmpdata); + Log.d(TAG, "fullLength=" + byteBuffer2.getInt(67) + " offset=" + byteBuffer2.getInt(71)); + return xmpSection; + } + + /** + * Split extendXMPMeta to multiple marker segments + * @param extendedXMPMetaBytes serialized extended XMP + * @param guid Is a 128-bit MD5 digest of the full ExtendedXMP serialization, + * stored as a 32-byte ASCII hex string + * @return split result + */ + private static List<Section> splitExtendXMPMeta(byte[] extendedXMPMetaBytes, String guid){ + List<Section> sections = new ArrayList<Section>(); + /* + The extended XMP JPEG marker segment content holds: + - a signature string, "http://ns.adobe.com/xmp/extension/\0" + - a 128 bit GUID stored as a 32 byte ASCII hex string + - a UInt32 full length of the entire extended XMP + - a UInt32 offset for this portion of the extended XMP + - the UTF-8 text for this portion of the extended XMP + */ + int splitNum = extendedXMPMetaBytes.length/MAX_EXTENDED_XMP_BUFFER_SIZE; + byte[] portion = new byte[MAX_EXTENDED_XMP_BUFFER_SIZE]; + ByteBuffer byteBuffer = ByteBuffer.wrap(extendedXMPMetaBytes); + Section extendedXmpSection = null; + + byte[] headerBytes = new byte[EXTEND_XMP_HEADER_SIZE]; + int index = 0; + System.arraycopy(EXTENDED_XMP_HEADER_SIGNATURE.getBytes(), 0, headerBytes, 0, EXTENDED_XMP_HEADER_SIGNATURE.length()); + index += EXTENDED_XMP_HEADER_SIGNATURE.length(); + + System.arraycopy(guid.getBytes(), 0, headerBytes, index, guid.length()); + index += guid.length(); + + Log.d(TAG, "buffer.length=" + extendedXMPMetaBytes.length); + byte[] fullLengthBytes = new byte[4]; + ByteBuffer intBuffer = ByteBuffer.wrap(fullLengthBytes); + intBuffer.putInt(0, extendedXMPMetaBytes.length); + System.arraycopy(fullLengthBytes, 0, headerBytes, index, 4); + index += 4; + + byte[] offsetBytes = new byte[4]; + ByteBuffer offsetBuffer = ByteBuffer.wrap(offsetBytes); + for( int i=0; i < splitNum; ++i ) { + offsetBuffer.putInt(0, i*MAX_EXTENDED_XMP_BUFFER_SIZE); + System.arraycopy(offsetBytes, 0, headerBytes, index, 4); + + byteBuffer.get(portion); + extendedXmpSection = createSection(portion, headerBytes); + sections.add(extendedXmpSection); + } + + int remainSize = extendedXMPMetaBytes.length - splitNum*MAX_EXTENDED_XMP_BUFFER_SIZE; + if ( remainSize > 0 ) { + offsetBuffer.putInt(0, splitNum*MAX_EXTENDED_XMP_BUFFER_SIZE); + System.arraycopy(offsetBytes, 0, headerBytes, index, 4); + + byte[] remain = new byte[remainSize]; + byteBuffer.get(remain); + extendedXmpSection = createSection(remain, headerBytes); + sections.add(extendedXmpSection); + } + + return sections; + } + + /** + * Updates a jpeg file from inputStream with XMPMeta to outputStream. + * @param inputStream Input image data stream + * @param outputStream Output image data stream + * @param standardMeta The main portion of the metadata tree must be serialized and written as + * the standard XMP packet + * @param extendedMeta The extended portion must be serialized without a packet wrapper, + * and written as a series of APP1 marker segments + */ + public static boolean writeXMPMeta(InputStream inputStream, OutputStream outputStream, + XMPMeta standardMeta, XMPMeta extendedMeta) { + byte[] buffer; + try { + SerializeOptions options = new SerializeOptions(); + options.setUseCompactFormat(true); + // We have to omit packet wrapper here because + // javax.xml.parsers.DocumentBuilder + // fails to parse the packet end <?xpacket end="w"?> in android. + options.setOmitPacketWrapper(true); + buffer = XMPMetaFactory.serializeToBuffer(extendedMeta, options); + } catch (XMPException e) { + Log.d(TAG, "Serialize extended xmp failed", e); + return false; + } + + String guid = getGUID(buffer); + try { + standardMeta.setProperty(XMP_NOTE_NAMESPACE, "HasExtendedXMP", guid); + } catch (XMPException exception) { + Log.d(TAG, "set XMPMeta Property", exception); + return false; + } + List<Section> sections = parse(inputStream, false); + List<Section> xmpSections = new ArrayList<Section>(); + Section standardXmpSection = createStandardXMPSection(standardMeta); + if (standardXmpSection == null) { + Log.e(TAG, "create standard meta section error"); + return false; + } + xmpSections.add(standardXmpSection); + + List<Section> extendedSections = splitExtendXMPMeta(buffer, guid); + xmpSections.addAll(extendedSections); + sections = insertXMPSection(sections, xmpSections); + if (sections == null) { + Log.d(TAG, "Insert XMP fialed"); + return false; + } + try { + // Overwrite the image file with the new meta data. + writeJpegFile(outputStream, sections); + } catch (IOException e) { + Log.d(TAG, "Write to stream failed", e); + return false; + } finally { + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + // Ignore. + } + } + } + return true; + } + + private static List<Section> insertXMPSection( + List<Section> sections, List<Section> xmpSections) { + if (sections == null || sections.size() <= 1) { + return null; + } + + // If the first section is Exif, insert XMP data before the second section, + // otherwise, make xmp data the first section. + List<Section> newSections = new ArrayList<Section>(); + int position = (sections.get(0).marker == M_APP1) ? 1 : 0; + newSections.addAll(sections.subList(0, position)); + newSections.addAll(xmpSections); + newSections.addAll(sections.subList(position, sections.size())); + return newSections; + } + + private static String getGUID(byte[] src) { + StringBuilder builder = new StringBuilder(); + try { + MessageDigest digester = MessageDigest.getInstance("MD5"); + digester.update(src); + byte[] digest = digester.digest(); + + Formatter formatter = new Formatter(builder); + for (int i = 0; i < digest.length; ++i) { + formatter.format("%02x", ((256 + digest[i]) % 256)); + } + } catch (NoSuchAlgorithmException exception) { + Log.d(TAG, "get md5 instance failure" + exception); + return null; + } + + return builder.toString().toUpperCase(); + } + private XmpUtil() {} } diff --git a/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java b/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java index bfd58db28..8a6e7667f 100755..100644 --- a/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java +++ b/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -30,6 +30,10 @@ package org.codeaurora.snapcam.filter; import java.io.ByteArrayOutputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.ArrayList; @@ -38,8 +42,11 @@ import java.util.List; import org.codeaurora.snapcam.filter.ClearSightNativeEngine.CamSystemCalibrationData; import org.codeaurora.snapcam.filter.ClearSightNativeEngine.ClearsightImage; +import org.codeaurora.snapcam.filter.DDMNativeEngine; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.ImageFormat; import android.graphics.Rect; import android.graphics.YuvImage; @@ -80,6 +87,7 @@ import com.android.camera.PhotoModule.NamedImages.NamedEntity; import com.android.camera.Storage; import com.android.camera.util.CameraUtil; + public class ClearSightImageProcessor { private static final String TAG = "ClearSightImageProcessor"; private static final String PERSIST_TIMESTAMP_LIMIT_KEY = "persist.camera.cs.threshold"; @@ -87,6 +95,7 @@ public class ClearSightImageProcessor { private static final String PERSIST_DUMP_FRAMES_KEY = "persist.camera.cs.dumpframes"; private static final String PERSIST_DUMP_YUV_KEY = "persist.camera.cs.dumpyuv"; private static final String PERSIST_CS_TIMEOUT_KEY = "persist.camera.cs.timeout"; + private static final String PERSIST_DUMP_DEPTH_KEY = "persist.camera.cs.dumpdepth"; private static final long DEFAULT_TIMESTAMP_THRESHOLD_MS = 10; private static final int DEFAULT_IMAGES_TO_BURST = 4; @@ -106,6 +115,9 @@ public class ClearSightImageProcessor { private static final int MSG_NEW_REPROC_RESULT = 4; private static final int MSG_NEW_REPROC_FAIL = 5; private static final int MSG_END_CAPTURE = 6; + private static final int MSG_CALIBRATION_DATA = 7; + private static final int MSG_NEW_LENS_FOCUS_DISTANCE_BAYER = 8; + private static final int MSG_NEW_DEPTH = 9; private static final int CAM_TYPE_BAYER = 0; private static final int CAM_TYPE_MONO = 1; @@ -115,6 +127,10 @@ public class ClearSightImageProcessor { new CameraCharacteristics.Key<>( "org.codeaurora.qcamera3.dualcam_calib_meta_data.dualcam_calib_meta_data_blob", byte[].class); + private CaptureResult.Key<byte[]> SCALE_CROP_ROTATION_REPROCESS_BLOB = + new CaptureResult.Key<byte[]>( + "org.codeaurora.qcamera3.hal_private_data.reprocess_data_blob", + byte[].class); private NamedImages mNamedImages; private ImageReader[] mImageReader = new ImageReader[NUM_CAM]; @@ -128,10 +144,13 @@ public class ClearSightImageProcessor { private ClearsightRegisterHandler mClearsightRegisterHandler; private ClearsightProcessHandler mClearsightProcessHandler; private ImageEncodeHandler mImageEncodeHandler; + private DepthProcessHandler mDepthProcessHandler; + private HandlerThread mImageProcessThread; private HandlerThread mClearsightRegisterThread; private HandlerThread mClearsightProcessThread; private HandlerThread mImageEncodeThread; + private HandlerThread mDepthProcessThread; private Callback mCallback; private CameraCaptureSession[] mCaptureSessions = new CameraCaptureSession[NUM_CAM]; @@ -144,6 +163,7 @@ public class ClearSightImageProcessor { private int mCsTimeout; private boolean mDumpImages; private boolean mDumpYUV; + private boolean mDumpDepth; private boolean mIsClosing; private int mFinishReprocessNum; @@ -167,8 +187,12 @@ public class ClearSightImageProcessor { mDumpYUV = SystemProperties.getBoolean(PERSIST_DUMP_YUV_KEY, false); Log.d(TAG, "mDumpYUV: " + mDumpYUV); + mDumpDepth = SystemProperties.getBoolean(PERSIST_DUMP_DEPTH_KEY, false); + Log.d(TAG, "mDumpDepth: " + mDumpDepth); + mCsTimeout = SystemProperties.getInt(PERSIST_CS_TIMEOUT_KEY, DEFAULT_CS_TIMEOUT_MS); Log.d(TAG, "mCsTimeout: " + mCsTimeout); + } public static void createInstance() { @@ -197,11 +221,14 @@ public class ClearSightImageProcessor { mClearsightProcessThread.start(); mImageEncodeThread = new HandlerThread("CameraImageEncode"); mImageEncodeThread.start(); + mDepthProcessThread = new HandlerThread("DepthProcess"); + mDepthProcessThread.start(); mImageProcessHandler = new ImageProcessHandler(mImageProcessThread.getLooper()); mClearsightRegisterHandler = new ClearsightRegisterHandler(mClearsightRegisterThread.getLooper()); mClearsightProcessHandler = new ClearsightProcessHandler(mClearsightProcessThread.getLooper()); mImageEncodeHandler = new ImageEncodeHandler(mImageEncodeThread.getLooper()); + mDepthProcessHandler = new DepthProcessHandler(mImageEncodeThread.getLooper()); mFinalPictureSize = new Size(width, height); mFinalPictureRatio = (float)width / (float)height; @@ -219,8 +246,11 @@ public class ClearSightImageProcessor { try { CameraCharacteristics cc = cm.getCameraCharacteristics("0"); byte[] blob = cc.get(OTP_CALIB_BLOB); + CamSystemCalibrationData calibrationData = CamSystemCalibrationData.createFromBytes(blob); ClearSightNativeEngine.getInstance().init(mNumFrameCount*2, - maxWidth, maxHeight, CamSystemCalibrationData.createFromBytes(blob)); + maxWidth, maxHeight, calibrationData); + mDepthProcessHandler.obtainMessage(MSG_CALIBRATION_DATA,0, 0, + calibrationData).sendToTarget(); } catch (CameraAccessException e) { e.printStackTrace(); } @@ -281,6 +311,17 @@ public class ClearSightImageProcessor { } } + if ( mDepthProcessThread != null ) { + mDepthProcessThread.quit(); + try{ + mDepthProcessThread.join(); + mDepthProcessThread = null; + mDepthProcessHandler = null; + }catch (InterruptedException e){ + e.printStackTrace(); + } + } + for(int i=0; i<mImageReader.length; i++) { if (null != mImageReader[i]) { mImageReader[i].close(); @@ -356,9 +397,16 @@ public class ClearSightImageProcessor { Log.d(TAG, "capture - onCaptureCompleted: " + cam); if(isClosing()) Log.d(TAG, "capture - onCaptureCompleted - closing"); - else + else { mImageProcessHandler.obtainMessage(MSG_NEW_CAPTURE_RESULT, cam, 0, result).sendToTarget(); + if (cam == CAM_TYPE_BAYER) { + float lensFocusDistance = result.get(CaptureResult.LENS_FOCUS_DISTANCE); + Log.d(TAG, "lensFocusDistance=" + lensFocusDistance); + mDepthProcessHandler.obtainMessage(MSG_NEW_LENS_FOCUS_DISTANCE_BAYER, + 0, 0, lensFocusDistance).sendToTarget(); + } + } } @Override @@ -492,6 +540,7 @@ public class ClearSightImageProcessor { mNamedEntity = mNamedImages.getNextNameEntity(); mClearsightRegisterHandler.obtainMessage(MSG_START_CAPTURE, 0, 0, mNamedEntity).sendToTarget(); + mDepthProcessHandler.obtainMessage(MSG_START_CAPTURE).sendToTarget(); break; case MSG_END_CAPTURE: // TIMED OUT WAITING FOR FRAME @@ -832,6 +881,8 @@ public class ClearSightImageProcessor { // reference not yet set Log.d(TAG, "reprocess - setReferenceResult: " + msg.obj); ClearSightNativeEngine.getInstance().setReferenceResult(isBayer, result); + mDepthProcessHandler.obtainMessage(MSG_NEW_REPROC_RESULT, msg.arg1, 0, msg.obj) + .sendToTarget(); } mFinishReprocessNum++; checkReprocessDone(); @@ -912,6 +963,7 @@ public class ClearSightImageProcessor { .hasReferenceImage(isBayer)) { // reference not yet set ClearSightNativeEngine.getInstance().setReferenceImage(isBayer, image); + mDepthProcessHandler.obtainMessage(MSG_NEW_IMG, msg.arg1, 0, msg.obj).sendToTarget(); } else { // if ref images set, register this image if(ClearSightNativeEngine.getInstance().registerImage( @@ -960,9 +1012,6 @@ public class ClearSightImageProcessor { ClearSightNativeEngine.getInstance().reset(); if(processInit) { - if(mCallback != null) - mCallback.onReleaseShutterLock(); - Image encodeImage = mImageWriter[CAM_TYPE_BAYER].dequeueInputImage(); ClearSightNativeEngine.ClearsightImage csImage = new ClearsightImage(encodeImage); encodeImage.setTimestamp(csTs); @@ -1086,13 +1135,19 @@ public class ClearSightImageProcessor { private short mEncodeRequest; private short mEncodeResults; - private boolean mReadyToSave; private boolean mHasFailure; private Image mMonoImage; private Image mBayerImage; private Image mClearSightImage; private NamedEntity mNamedEntity; + private GDepth.DepthMap mDepthMap; + private GImage mGImage; + private boolean mDepthMapReady; + private boolean mClearSightReady; + + private long CLEAR_SIGHT_IMAGE_SAVE_DELAY = 1*500; + public ImageEncodeHandler(Looper looper) { super(looper); } @@ -1110,15 +1165,19 @@ public class ClearSightImageProcessor { Log.d(TAG, "ImageEncodeEvent - END_CAPTURE"); mNamedEntity = (NamedEntity) msg.obj; mEncodeRequest = (short)msg.arg1; - mReadyToSave = true; - saveMpoImage(); + mClearSightReady = true; + saveClearSightImage(); break; case MSG_NEW_IMG: case MSG_NEW_CAPTURE_RESULT: case MSG_NEW_CAPTURE_FAIL: processNewEvent(msg); - saveMpoImage(); + saveClearSightImage(); break; + case MSG_NEW_DEPTH: + processNewGDepth(msg); + saveClearSightImage(); + break; } } @@ -1128,12 +1187,20 @@ public class ClearSightImageProcessor { if(msg.arg1 == CAM_TYPE_MONO) { mMonoImage = (Image)msg.obj; mEncodeResults |= MASK_MONO_ENCODE; + if ( mDumpDepth ) { + saveToFile(getJpegData(mMonoImage), "mono", "jpg"); + } } else if(mBayerImage == null){ mBayerImage = (Image)msg.obj; mEncodeResults |= MASK_BAYER_ENCODE; + mGImage = new GImage(getJpegData(mBayerImage), "image/jpeg"); + if ( mDumpDepth ) { + saveToFile(getJpegData(mBayerImage), "bayer", "jpg"); + } } else { mClearSightImage = (Image)msg.obj; mEncodeResults |= MASK_CS_ENCODE; + } } else if (msg.what == MSG_NEW_CAPTURE_RESULT) { Log.d(TAG, "processNewEncodeEvent - newResult: " + msg.arg1); @@ -1152,63 +1219,69 @@ public class ClearSightImageProcessor { } } - private void saveMpoImage() { - if(!mReadyToSave || mEncodeRequest != mEncodeResults) { - Log.d(TAG, "saveMpoImage - not yet ready to save"); + private void processNewGDepth(Message msg) { + mDepthMap = (GDepth.DepthMap)msg.obj; + mDepthMapReady = true; + } + + private void saveClearSightImage() { + if ( !isReadyToSave() || mEncodeRequest != mEncodeResults) { + Log.d(TAG, "saveClearSightImage - not yet ready to save"); return; } - Log.d(TAG, "saveMpoImage"); if(mHasFailure) { // don't save anything and fail - Log.d(TAG, "saveMpoImage has failure - aborting."); + Log.d(TAG, "saveClearSightImage has failure - aborting."); if(mCallback != null) mCallback.onClearSightFailure(null); resetParams(); return; } + Log.d(TAG, "saveClearSightImage"); + byte[] clearSightBytes = getJpegData(mClearSightImage); + String title = (mNamedEntity == null) ? null : mNamedEntity.title; long date = (mNamedEntity == null) ? -1 : mNamedEntity.date; - int width = mBayerImage.getWidth(); - int height = mBayerImage.getHeight(); - if(mClearSightImage != null) { + int width = 0; + int height = 0; + if ( mBayerImage != null ) { + mBayerImage.getWidth(); + mBayerImage.getHeight(); + } + if ( mClearSightImage != null ) { width = mClearSightImage.getWidth(); height = mClearSightImage.getHeight(); } - byte[] clearSightBytes = getJpegData(mClearSightImage); byte[] bayerBytes = getJpegData(mBayerImage); - byte[] monoBytes = getJpegData(mMonoImage); - ExifInterface exif = Exif.getExif(bayerBytes); - int orientation = Exif.getOrientation(exif); - - if(clearSightBytes != null) { - if(mCallback != null) mCallback.onClearSightSuccess(clearSightBytes); - } else if (bayerBytes != null) { - if(mCallback != null) mCallback.onClearSightFailure(bayerBytes); - } else { - if(mCallback != null) mCallback.onClearSightFailure(null); - } + if ( bayerBytes != null ) { + ExifInterface exif = Exif.getExif(bayerBytes); + int orientation = Exif.getOrientation(exif); + + if(clearSightBytes != null) { + if(mCallback != null) mCallback.onClearSightSuccess(clearSightBytes); + } else if (bayerBytes != null) { + if(mCallback != null) mCallback.onClearSightFailure(bayerBytes); + } else { + if(mCallback != null) mCallback.onClearSightFailure(null); + } - if(monoBytes == null) { - mMediaSaveService.addImage( - clearSightBytes!=null?clearSightBytes:bayerBytes, title, date, null, + mMediaSaveService.addClearsightImage( + clearSightBytes != null ? clearSightBytes : bayerBytes, + mGImage, mDepthMap,title, date, null, width, height, orientation, exif, mMediaSavedListener, mMediaSaveService.getContentResolver(), "jpeg"); - } else if (bayerBytes != null) { - mMediaSaveService.addMpoImage( - clearSightBytes, - bayerBytes, - monoBytes, width, height, title, - date, null, orientation, mMediaSavedListener, - mMediaSaveService.getContentResolver(), "jpeg"); } - resetParams(); } + private boolean isReadyToSave() { + return (mDepthMapReady &&mClearSightReady); + } + void resetParams() { if(mBayerImage != null) { mBayerImage.close(); @@ -1223,10 +1296,182 @@ public class ClearSightImageProcessor { mClearSightImage = null; } mNamedEntity = null; - mReadyToSave = false; mHasFailure = false; mEncodeRequest = 0; mEncodeResults = 0; + mGImage = null; + mDepthMapReady = false; + mClearSightReady = false; + } + } + + private class DepthProcessHandler extends Handler{ + private TotalCaptureResult mReprocessCaptureResult; + private DDMNativeEngine mDDMNativeEngine; + public DepthProcessHandler(Looper looper) { + super(looper); + mDDMNativeEngine = new DDMNativeEngine(); + } + + @Override + public void handleMessage(Message msg) { + switch( msg.what ) { + case MSG_CALIBRATION_DATA: + setCalibrationdata(msg); + break; + + case MSG_NEW_LENS_FOCUS_DISTANCE_BAYER: + setBayerLensFocusDistance(msg); + break; + + case MSG_START_CAPTURE: + resetParams(); + break; + + case MSG_NEW_IMG: + registerImage(msg); + break; + + case MSG_NEW_REPROC_RESULT: + registerReprocessResult(msg); + break; + } + } + + private void setCalibrationdata(Message msg) { + mDDMNativeEngine.setCamSystemCalibrationData((CamSystemCalibrationData)msg.obj); + } + private void resetParams(){ + Log.d(TAG, "resetParams"); + mDDMNativeEngine.reset(); + } + + private void setBayerLensFocusDistance(Message msg) { + mDDMNativeEngine.setBayerLensFocusDistance((float)msg.obj); + } + + private void registerImage(Message msg) { + boolean isBayer = (msg.arg1 == CAM_TYPE_BAYER); + Image image = (Image) msg.obj; + if ( isBayer ) { + mDDMNativeEngine.setBayerImage(image); + }else{ + mDDMNativeEngine.setMonoImage(image); + } + + if ( mDDMNativeEngine.isReadyForGenerateDepth() ) { + generateDepthmap(); + } + } + + private void registerReprocessResult(Message msg) { + + boolean isBayer = (msg.arg1 == CAM_TYPE_BAYER); + Log.d(TAG, "registerReprocessResult bayer=" + isBayer); + TotalCaptureResult result = (TotalCaptureResult)msg.obj; + if ( isBayer ) { + mDDMNativeEngine.setBayerReprocessResult(result); + }else{ + mDDMNativeEngine.setMonoReprocessResult(result); + } + + if ( mDDMNativeEngine.isReadyForGenerateDepth() ) { + generateDepthmap(); + } + + } + + private void generateDepthmap() { + GDepth.DepthMap depthMap = null; + int[] size = new int[2]; + if ( mDDMNativeEngine.getDepthMapSize(size) ) { + int width = size[0]; + int height = size[1]; + Bitmap bmp = Bitmap.createBitmap(width, height, + Bitmap.Config.ALPHA_8); + int stride = bmp.getRowBytes(); + byte[] depthBuffer = new byte[stride*height]; + Log.d(TAG, "depthMapWidth=" + width + " depthMapHeight=" + + height + " stride=" + stride); + Rect roiRect = new Rect(); + + if ( mDDMNativeEngine.dualCameraGenerateDDM(depthBuffer, stride, roiRect) ) { + if ( mDumpDepth ) { + saveAsRGB(depthBuffer, width, height); + } + depthMap = new GDepth.DepthMap(width, height); + depthMap.roi = roiRect; + depthMap.rawDepth = depthBuffer; + }else{ + Log.e(TAG, "dualCameraGenerateDDM failure"); + } + }else{ + Log.e(TAG, "getDepthMapSize failure"); + } + if ( mDumpDepth ) { + dumpCameraParam(); + } + mImageEncodeHandler.obtainMessage(MSG_NEW_DEPTH, 0, 0, depthMap).sendToTarget(); + } + + private void dumpCameraParam() { + saveToFile(mDDMNativeEngine.getOTPCalibration().getBytes(), "OTPdata", "txt"); + saveToFile(mDDMNativeEngine.getBayerScaleCrop().getBytes(), "BayerScaleCrop", "txt"); + saveToFile(mDDMNativeEngine.getMonoScaleCrop().getBytes(), "MonoScaleCrop", "txt"); + } + } + + private void saveAsRGB(byte[] depth, int width, int height) { + int[] colors = new int[depth.length]; + for(int i=0; i < colors.length; ++i) { + colors[i] = (256+depth[i])%256; + } + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + for( int y=0; y < height; ++y ) { + for( int x=0; x < width; ++x) { + int c = colors[y*width+x]; + bitmap.setPixel(x, y, Color.rgb(c, c, c)); + } + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); + File file = new File("sdcard/depthmap_rgb.jpg"); + byte[] jpeg = baos.toByteArray(); + Log.d(TAG, "jpeg.size=" + jpeg.length); + OutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + out.write(jpeg, 0, jpeg.length); + }catch(Exception e) { + Log.d(TAG, e.toString()); + }finally { + if (out != null) { + try { + out.close(); + }catch(Exception e){ + Log.d(TAG, e.toString()); + } + } + } + } + + private void saveToFile(byte[] bytes, String name, String format){ + File file = new File("sdcard/"+ name + "." + format); + OutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + out.write(bytes, 0, bytes.length); + }catch(Exception e) { + Log.d(TAG, e.toString()); + }finally { + if (out != null) { + try { + out.close(); + }catch(Exception e){ + Log.d(TAG, e.toString()); + } + } } } diff --git a/src/org/codeaurora/snapcam/filter/ClearSightNativeEngine.java b/src/org/codeaurora/snapcam/filter/ClearSightNativeEngine.java index 402ffce84..8b30ca032 100644 --- a/src/org/codeaurora/snapcam/filter/ClearSightNativeEngine.java +++ b/src/org/codeaurora/snapcam/filter/ClearSightNativeEngine.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, 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 @@ -412,7 +412,7 @@ public class ClearSightNativeEngine { public static class CamSystemCalibrationData { private static final String[] CALIB_FMT_STRINGS = { - "Calibration OTP format version = %x\n", + "Calibration OTP format version = %d\n", "Main Native Sensor Resolution width = %dpx\n", "Main Native Sensor Resolution height = %dpx\n", "Main Calibration Resolution width = %dpx\n", diff --git a/src/org/codeaurora/snapcam/filter/DDMNativeEngine.java b/src/org/codeaurora/snapcam/filter/DDMNativeEngine.java new file mode 100644 index 000000000..b2b0a523d --- /dev/null +++ b/src/org/codeaurora/snapcam/filter/DDMNativeEngine.java @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2017, 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 org.codeaurora.snapcam.filter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import android.graphics.Rect; +import android.media.Image; +import android.media.Image.Plane; +import android.util.Log; + +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.TotalCaptureResult; + +import org.codeaurora.snapcam.filter.ClearSightNativeEngine.CamSystemCalibrationData; + +public class DDMNativeEngine { + private static final String TAG = "DDMNativeEngine"; + static { + try {//load jni_dualcamera + System.loadLibrary("jni_dualcamera"); + mLibLoaded = true; + Log.v(TAG, "successfully loaded jni_dualcamera lib"); + } catch (UnsatisfiedLinkError e) { + Log.e(TAG, "failed to load jni_dualcamera lib"); + Log.e(TAG, e.toString()); + e.printStackTrace(); + mLibLoaded = false; + } + + } + + private CaptureResult.Key<byte[]> SCALE_CROP_ROTATION_REPROCESS_BLOB = + new CaptureResult.Key<byte[]>( + "org.codeaurora.qcamera3.hal_private_data.reprocess_data_blob", + byte[].class); + + private static boolean mLibLoaded; + private Image mBayerImage; + private Image mMonoImage; + private ByteBuffer mPrimaryY; + private ByteBuffer mPrivaryVU; + CamReprocessInfo mBayerCamReprocessInfo; + CamReprocessInfo mMonoCamReprocessInfo; + CamSystemCalibrationData mCamSystemCalibrationData; + private float mLensFocusDistance; + private static final int Y_PLANE = 0; + private static final int VU_PLANE = 2; + + public boolean getDepthMapSize(int[] depthMap){ + return nativeGetDepthMapSize(mBayerImage.getWidth(), mBayerImage.getHeight(), depthMap); + } + + public void setCamSystemCalibrationData(CamSystemCalibrationData otpCalibration){ + mCamSystemCalibrationData = otpCalibration; + } + + public String getOTPCalibration() { + return mCamSystemCalibrationData.toString(); + } + + public void reset() { + mBayerImage = null; + mMonoImage = null; + mBayerCamReprocessInfo = null; + mMonoCamReprocessInfo = null; + mLensFocusDistance = 0; + } + public boolean isReadyForGenerateDepth(){ + return mBayerImage != null && mMonoImage != null + && mBayerCamReprocessInfo != null && mMonoCamReprocessInfo != null; + } + + public void setBayerLensFocusDistance(float lensFocusDistance) { + mLensFocusDistance = lensFocusDistance; + } + public void setBayerImage(Image image){ + mBayerImage = image; + } + + public void setMonoImage(Image image) { + mMonoImage = image; + } + + public void setBayerReprocessResult(CaptureResult result ){ + byte[] bytes = result.get(SCALE_CROP_ROTATION_REPROCESS_BLOB); + mBayerCamReprocessInfo = CamReprocessInfo.createCamReprocessFromBytes(bytes); + } + + public String getBayerScaleCrop() { + return mBayerCamReprocessInfo.toString(); + } + + public void setMonoReprocessResult(CaptureResult result) { + byte[] bytes = result.get(SCALE_CROP_ROTATION_REPROCESS_BLOB); + mMonoCamReprocessInfo = CamReprocessInfo.createCamReprocessFromBytes(bytes); + } + + public String getMonoScaleCrop(){ + return mMonoCamReprocessInfo.toString(); + } + + public boolean dualCameraGenerateDDM(byte[] depthMapBuffer, int depthMapStride, Rect roiRect) { + if ( mLensFocusDistance == 0 ){ + Log.e(TAG, " dualCameraGenerateDDM error: mLensFocusDistance is 0"); + return false; + } + + if (mBayerImage == null || mMonoImage == null ) { + Log.e(TAG, "mBayerImage=" +(mBayerImage == null)+ " mMonoImage=" + (mMonoImage == null)); + return false; + } + + if ( depthMapBuffer == null ) { + Log.e(TAG, "depthMapBuffer can't be null"); + return false; + } + + if ( mMonoCamReprocessInfo== null + || mBayerCamReprocessInfo == null + || mCamSystemCalibrationData == null ) { + Log.e(TAG, "mMonoCamReprocessInfo== null:" +(mMonoCamReprocessInfo== null) + + " mBayerCamReprocessInfo == null:" +(mBayerCamReprocessInfo == null) + + " mCamSystemCalibrationData == null:" +(mCamSystemCalibrationData == null)); + return false; + } + + Plane[] bayerPlanes = mBayerImage.getPlanes(); + Plane[] monoPlanes = mMonoImage.getPlanes(); + int[] goodRoi = new int[4]; + boolean result = nativeDualCameraGenerateDDM( + bayerPlanes[Y_PLANE].getBuffer(), + bayerPlanes[VU_PLANE].getBuffer(), + mBayerImage.getWidth(), + mBayerImage.getHeight(), + bayerPlanes[Y_PLANE].getRowStride(), + bayerPlanes[VU_PLANE].getRowStride(), + + monoPlanes[Y_PLANE].getBuffer(), + monoPlanes[VU_PLANE].getBuffer(), + mMonoImage.getWidth(), + mMonoImage.getHeight(), + monoPlanes[Y_PLANE].getRowStride(), + monoPlanes[VU_PLANE].getRowStride(), + + depthMapBuffer, + depthMapStride, + + goodRoi, + + mBayerCamReprocessInfo.toString(), + mMonoCamReprocessInfo.toString(), + mCamSystemCalibrationData.toString(), + mLensFocusDistance, + true); + roiRect.left = goodRoi[0]; + roiRect.top = goodRoi[1]; + roiRect.right = goodRoi[0] + goodRoi[2]; + roiRect.bottom = goodRoi[1] + goodRoi[3]; + + return result; + } + + + + private native boolean nativeGetDepthMapSize(int primaryWidth, int primaryHeight,int[] size); + + private native boolean nativeDualCameraGenerateDDM( + ByteBuffer primaryY, + ByteBuffer primaryVU, + int primaryWidth, + int primaryHeight, + int primaryStrideY, + int primaryStrideVU, + + ByteBuffer auxiliaryY, + ByteBuffer auxiliaryVU, + int auxiliaryWidth, + int auxiliaryHeight, + int auxiliaryStrideY, + int auxiliaryStrideVU, + + byte[] outDst, + int dstStride, + + int[] roiRect, + + String scaleCropRotationDataPrimaryCamera, + String scaleCropRotationDataAuxiliaryCamera, + String otpCalibration, + float focalLengthPrimaryCamera, + boolean isAuxiliaryMonoSensor); + + public static class DepthMap{ + private int width; + private int height; + private ByteBuffer buffer; + private int stride; + private Rect roi; + } + public static class CamStreamCropInfo{ + int stream_id; + Rect crop; + Rect roi_map; + + private CamStreamCropInfo(){} + + public static CamStreamCropInfo createFromBytes(byte[] bytes) { + ByteBuffer buffer = ByteBuffer.wrap(bytes); + buffer.order(ByteOrder.LITTLE_ENDIAN); + return createFromByteBuffer(buffer); + } + + public static CamStreamCropInfo createFromByteBuffer(ByteBuffer buffer) { + CamStreamCropInfo camStreamCropInfo = new CamStreamCropInfo(); + camStreamCropInfo.stream_id = buffer.getInt(); + Rect crop = new Rect(); + crop.left = buffer.getInt(); + crop.top = buffer.getInt(); + crop.right = crop.left + buffer.getInt(); + crop.bottom = crop.top + buffer.getInt(); + camStreamCropInfo.crop = crop; + + Rect roi_map = new Rect(); + roi_map.left = buffer.getInt(); + roi_map.top = buffer.getInt(); + roi_map.right = roi_map.left + buffer.getInt(); + roi_map.bottom = roi_map.top + buffer.getInt(); + camStreamCropInfo.roi_map = roi_map; + + return camStreamCropInfo; + } + } + + public static class CamRotationInfo { + int jpeg_rotation; + int device_rotation; + int stream_id; + private CamRotationInfo(){} + + public static CamRotationInfo createCamReprocessFromBytes(byte[] bytes) { + ByteBuffer buf = ByteBuffer.wrap(bytes); + buf.order(ByteOrder.LITTLE_ENDIAN); + return createFromByteBuffer(buf); + } + public static CamRotationInfo createFromByteBuffer(ByteBuffer buffer) { + CamRotationInfo rotation_info = new CamRotationInfo(); + rotation_info.jpeg_rotation = buffer.getInt(); + rotation_info.device_rotation = buffer.getInt(); + rotation_info.stream_id = buffer.getInt(); + return rotation_info; + } + } + public static class CamReprocessInfo{ + CamStreamCropInfo sensor_crop_info; + CamStreamCropInfo camif_crop_info; + CamStreamCropInfo isp_crop_info; + CamStreamCropInfo cpp_crop_info; + float af_focal_length_ratio; + int pipeline_flip; + CamRotationInfo rotation_info; + + private final String SCALE_CROP_ROTATION_FORMAT_STRING[] = { + "Sensor Crop left = %d\n", + "Sensor Crop top = %d\n", + "Sensor Crop width = %d\n", + "Sensor Crop height = %d\n", + "Sensor ROI Map left = %d\n", + "Sensor ROI Map top = %d\n", + "Sensor ROI Map width = %d\n", + "Sensor ROI Map height = %d\n", + "CAMIF Crop left = %d\n", + "CAMIF Crop top = %d\n", + "CAMIF Crop width = %d\n", + "CAMIF Crop height = %d\n", + "CAMIF ROI Map left = %d\n", + "CAMIF ROI Map top = %d\n", + "CAMIF ROI Map width = %d\n", + "CAMIF ROI Map height = %d\n", + "ISP Crop left = %d\n", + "ISP Crop top = %d\n", + "ISP Crop width = %d\n", + "ISP Crop height = %d\n", + "ISP ROI Map left = %d\n", + "ISP ROI Map top = %d\n", + "ISP ROI Map width = %d\n", + "ISP ROI Map height = %d\n", + "CPP Crop left = %d\n", + "CPP Crop top = %d\n", + "CPP Crop width = %d\n", + "CPP Crop height = %d\n", + "CPP ROI Map left = %d\n", + "CPP ROI Map top = %d\n", + "CPP ROI Map width = %d\n", + "CPP ROI Map height = %d\n", + "Focal length Ratio = %f\n", + "Current pipeline mirror flip setting = %d\n", + "Current pipeline rotation setting = %d\n" + }; + + public static CamReprocessInfo createCamReprocessFromBytes(byte[] bytes){ + ByteBuffer buf = ByteBuffer.wrap(bytes); + buf.order(ByteOrder.LITTLE_ENDIAN); + return createCamReprocessFromBytes(buf); + } + public static CamReprocessInfo createCamReprocessFromBytes(ByteBuffer buffer){ + CamReprocessInfo scaleCropRotation = new CamReprocessInfo(); + scaleCropRotation.sensor_crop_info = CamStreamCropInfo.createFromByteBuffer(buffer); + scaleCropRotation.camif_crop_info = CamStreamCropInfo.createFromByteBuffer(buffer); + scaleCropRotation.isp_crop_info = CamStreamCropInfo.createFromByteBuffer(buffer); + scaleCropRotation.cpp_crop_info = CamStreamCropInfo.createFromByteBuffer(buffer); + scaleCropRotation.af_focal_length_ratio = buffer.getFloat(); + scaleCropRotation.pipeline_flip = buffer.getInt(); + scaleCropRotation.rotation_info = CamRotationInfo.createFromByteBuffer(buffer); + return scaleCropRotation; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[0], this.sensor_crop_info.crop.left)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[1], this.sensor_crop_info.crop.top)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[2], this.sensor_crop_info.crop.width())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[3], this.sensor_crop_info.crop.height())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[4], this.sensor_crop_info.roi_map.left)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[5], this.sensor_crop_info.roi_map.top)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[6], this.sensor_crop_info.roi_map.width())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[7], this.sensor_crop_info.roi_map.height())); + + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[8], this.camif_crop_info.crop.left)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[9], this.camif_crop_info.crop.top)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[10], this.camif_crop_info.crop.width())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[11], this.camif_crop_info.crop.height())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[12], this.camif_crop_info.roi_map.left)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[13], this.camif_crop_info.roi_map.top)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[14], this.camif_crop_info.roi_map.width())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[15], this.camif_crop_info.roi_map.height())); + + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[16], this.isp_crop_info.crop.left)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[17], this.isp_crop_info.crop.top)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[18], this.isp_crop_info.crop.width())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[19], this.isp_crop_info.crop.height())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[20], this.isp_crop_info.roi_map.left)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[21], this.isp_crop_info.roi_map.top)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[22], this.isp_crop_info.roi_map.width())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[23], this.isp_crop_info.roi_map.height())); + + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[24], this.cpp_crop_info.crop.left)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[25], this.cpp_crop_info.crop.top)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[26], this.cpp_crop_info.crop.width())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[27], this.cpp_crop_info.crop.height())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[28], this.cpp_crop_info.roi_map.left)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[29], this.cpp_crop_info.roi_map.top)); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[30], this.cpp_crop_info.roi_map.width())); + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[31], this.cpp_crop_info.roi_map.height())); + + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[32], this.af_focal_length_ratio)); + + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[33], this.pipeline_flip)); + + sb.append(String.format(SCALE_CROP_ROTATION_FORMAT_STRING[34], this.rotation_info.jpeg_rotation)); + return sb.toString(); + } + + } + + +}
\ No newline at end of file diff --git a/src/org/codeaurora/snapcam/filter/GDepth.java b/src/org/codeaurora/snapcam/filter/GDepth.java new file mode 100644 index 000000000..8f9a935a9 --- /dev/null +++ b/src/org/codeaurora/snapcam/filter/GDepth.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2017, 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 org.codeaurora.snapcam.filter; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Rect; +import android.hardware.Camera.Size; +import android.util.Base64; +import android.util.Log; + + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; + +import java.io.OutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.BufferedOutputStream; + +import com.adobe.xmp.XMPException; +import com.adobe.xmp.XMPMeta; +import com.adobe.xmp.XMPMetaFactory; + +public class GDepth{ + private final static String TAG = "Flow_GDepth"; + public final static String NAMESPACE_URL = "http://ns.google.com/photos/1.0/depthmap/"; + public final static String PREFIX = "GDepth"; + public final static String PROPERTY_FORMAT = "Format"; + public final static String PROPERTY_NEAR = "Near"; + public final static String PROPERTY_FAR = "Far"; + public final static String PROPERTY_MIME = "Mime"; + public final static String PROPERTY_DATA = "Data"; + //extend roi + public final static String PROPERTY_ROI_X = "RoiX"; + public final static String PROPERTY_ROI_Y = "RoiY"; + public final static String PROPERTY_ROI_WIDTH = "RoiWidth"; + public final static String PROPERTY_ROI_HEIGHT = "RoiHeight"; + + public final static String FORMAT_RANGE_INVERSE="RangeInverse"; + public final static String FORMAT_RANGLE_LINEAR = "RangeLinear"; + private final static String MIME = "image/jpeg"; + + private DepthMap mDepthMap; + private String mData; + private int mNear; + private int mFar; + private final String mFormat = "RangeLinear"; + private int[] mMap; + + static { + try { + XMPMetaFactory.getSchemaRegistry().registerNamespace( + NAMESPACE_URL, PREFIX); + } catch (XMPException e) { + e.printStackTrace(); + } + } + + private GDepth(DepthMap depthMap){ + mDepthMap = depthMap; + mMap = new int[depthMap.buffer.length]; + + for( int i=0; i < mMap.length; ++i ) { + mMap[i] = (256+depthMap.buffer[i])%256; + } + mNear = mFar = mMap[0]; + for(int d : mMap ) { + if ( d < mNear) { + mNear = d; + }else if ( d > mFar) { + mFar = d; + } + } + } + + public int getNear() { + return mNear; + } + + public int getFar(){ + return mFar; + } + + public String getFormat(){ + return mFormat; + } + + public String getMime() { + return MIME; + } + + public String getData(){ + return mData; + } + + public Rect getRoi() { + return mDepthMap.roi; + } + public static GDepth createGDepth(DepthMap depthMap){ + GDepth gDepth = new GDepth(depthMap); + if ( gDepth.encoding() ) { + return gDepth; + } + return null; + } + + private boolean encoding(){ + Log.d(TAG, "encoding"); + boolean result = false; + int[] grayscaleImage = convertIntoImage(mMap); + byte[] jpegBytes = compressToJPEG(grayscaleImage ); + if (jpegBytes != null ) { + String base64String = serializeAsBase64Str(jpegBytes); + result = true; + mData = base64String; + }else{ + Log.e(TAG, "compressToJPEG failure"); + } + + return result; + } + + private int[] convertIntoImage(int[] depthMap) { + int[] imageBuffer = new int[depthMap.length]; + float dividend = mFar-mNear; + for ( int i =0; i < imageBuffer.length; ++i) { + if ( depthMap[i] == 0 && mNear == 0 ) { + imageBuffer[i] = 0; + }else{ + imageBuffer[i] = getRangeLinearDepth(depthMap[i], mNear, dividend); + } + } + return imageBuffer; + } + + private int getRangeLinearDepth(int depth, int near, float dividend) { + return (int)(255*(depth-near)/dividend); + } + + private int getRangeLinearDepth(int depth, int near, int far) { + return (int)(255*(depth-near)/(float)(far-near)); + } + + private byte[] compressToJPEG(int[] image) { + Log.d(TAG, "compressToJPEG int[].size=" + image.length); + byte[] bitsBuffer = new byte[image.length]; + for(int i=0; i < image.length; ++i){ + bitsBuffer[i] = (byte)image[i]; + } + return compressToJPEG(bitsBuffer); + } + + private byte[] compressToJPEG(byte[] image) { + Log.d(TAG, "compressToJPEG byte[].size=" + image.length); + Bitmap bmp = BitmapFactory.decodeByteArray (image, 0, image.length); + if ( bmp == null ) { + Log.d(TAG, " buffer can't be decoded "); + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + bmp.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); + return outputStream.toByteArray(); + } + + private String serializeAsBase64Str(byte[] image) { + Log.d(TAG, "serializeAsBase64Str"); + return Base64.encodeToString(image, Base64.DEFAULT); + } + + private void saveAsFile(String str, String name){ + Log.d(TAG, "saveAsFile " + "sdcard/DDM/"+ TAG + name + ".log"); + File file = new File("sdcard/DDM/"+ TAG + name + ".log"); + + OutputStream out = null; + byte[] bytes = str.getBytes(); + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + out.write(bytes, 0, bytes.length); + }catch(Exception e) { + Log.d(TAG, e.toString()); + }finally { + if (out != null) { + try { + out.close(); + }catch(Exception e){ + Log.d(TAG, e.toString()); + } + } + } + } + + private void saveAsJPEG(byte[] bytes){ + Log.d(TAG, "saveAsJPEG"); + File file = new File("sdcard/"+ System.currentTimeMillis() + "_depth.JPEG"); + OutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + out.write(bytes, 0, bytes.length); + }catch(Exception e) { + Log.d(TAG, e.toString()); + }finally { + if (out != null) { + try { + out.close(); + }catch(Exception e){ + Log.d(TAG, e.toString()); + } + } + } + } + + public static class DepthMap{ + public byte[] buffer; + public int width; + public int height; + public Rect roi; + public byte[] rawDepth; + public DepthMap(int width, int height){ + this.width = width; + this.height = height; + } + } + + + private GDepth(int near, int far, String data) { + this.mNear = near; + this.mFar = far; + this.mData = data; + } + public static GDepth createGDepth(XMPMeta xmpMeta){ + try { + int near = Integer.parseInt((String) + xmpMeta.getProperty(GDepth.NAMESPACE_URL, PROPERTY_NEAR).getValue()); + int far = Integer.parseInt((String) + xmpMeta.getProperty(GDepth.NAMESPACE_URL, PROPERTY_FAR).getValue()); + String data = (String)xmpMeta.getProperty( + GDepth.NAMESPACE_URL, PROPERTY_DATA).getValue(); + String format = (String)xmpMeta.getProperty( + GDepth.NAMESPACE_URL, PROPERTY_FORMAT).getValue(); + Log.d(TAG, "new GDepth: nerar=" + near+ " far=" + far + "format=" + format+ " data=" + data); + int x = Integer.parseInt((String) + xmpMeta.getProperty(GDepth.NAMESPACE_URL, PROPERTY_ROI_X).getValue()); + int y = Integer.parseInt((String) + xmpMeta.getProperty(GDepth.NAMESPACE_URL, PROPERTY_ROI_Y).getValue()); + int width = Integer.parseInt((String) + xmpMeta.getProperty(GDepth.NAMESPACE_URL, PROPERTY_ROI_WIDTH).getValue()); + int height = Integer.parseInt((String) + xmpMeta.getProperty(GDepth.NAMESPACE_URL, PROPERTY_ROI_HEIGHT).getValue()); + Log.d(TAG, "x=" + x + " y=" + y + " width=" + width + " height=" + height); + GDepth gDepth = new GDepth(near, far, data); + return gDepth; + }catch(XMPException e){ + Log.e(TAG, e.toString()); + }catch(Exception e) { + Log.e(TAG, e.toString()); + } + return null; + } + + public boolean decode() { + Log.d(TAG, "decode"); + byte[] depthBuffer = Base64.decode(mData, Base64.DEFAULT); + saveAsJPEG(depthBuffer); + //TODO: + //convert JPEG compress bytes to bytes + + int[] intDepthBuffer = new int[depthBuffer.length]; + int[] intDepth = new int[depthBuffer.length]; + + //conver to 0-255; + for( int i=0; i < intDepthBuffer.length; ++i) { + intDepthBuffer[i] = (256+depthBuffer[i])%256; + } + //conver to depth value + + return false; + + } +}
\ No newline at end of file diff --git a/src/org/codeaurora/snapcam/filter/GImage.java b/src/org/codeaurora/snapcam/filter/GImage.java new file mode 100644 index 000000000..bac6fd296 --- /dev/null +++ b/src/org/codeaurora/snapcam/filter/GImage.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017, 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 org.codeaurora.snapcam.filter; + +import com.adobe.xmp.XMPException; +import com.adobe.xmp.XMPMeta; +import com.adobe.xmp.XMPMetaFactory; + +import android.util.Base64; + +public class GImage{ + public final static String NAMESPACE_URL = "http://ns.google.com/photos/1.0/image/"; + public final static String PREFIX = "GImage"; + public final static String PROPERTY_MIME = "Mime"; + public final static String PROPERTY_DATA = "Data"; + + static { + try { + XMPMetaFactory.getSchemaRegistry().registerNamespace( + NAMESPACE_URL, PREFIX); + } catch (XMPException e) { + e.printStackTrace(); + } + } + + private String mMime = "image/jpeg"; + private String mData; + + public GImage(byte[] data, String mime){ + mData = Base64.encodeToString(data, Base64.DEFAULT); + mMime = mime; + } + + public String getMime(){ + return mMime; + } + + public String getData(){ + return mData; + } +}
\ No newline at end of file |