From: @sishuikang Reviewed-by: @zhanghaibo5,@ivss Signed-off-by: @zhanghaibo5tags/v1.2.0-rc1
| @@ -8,8 +8,8 @@ android { | |||||
| applicationId "com.mindspore.himindspore" | applicationId "com.mindspore.himindspore" | ||||
| minSdkVersion 21 | minSdkVersion 21 | ||||
| targetSdkVersion 30 | targetSdkVersion 30 | ||||
| versionCode 4 | |||||
| versionName "1.1.2" | |||||
| versionCode 5 | |||||
| versionName "1.1.3" | |||||
| testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | ||||
| javaCompileOptions { | javaCompileOptions { | ||||
| @@ -292,7 +292,7 @@ public class SplashActivity extends BaseActivity<MainPresenter> implements MainC | |||||
| public void showUpdate(final UpdateInfoBean updateInfo) { | public void showUpdate(final UpdateInfoBean updateInfo) { | ||||
| if (now_version == updateInfo.getVersionCode()) { | if (now_version == updateInfo.getVersionCode()) { | ||||
| Toast.makeText(this, "已经是最新版本", Toast.LENGTH_SHORT).show(); | |||||
| // Toast.makeText(this, "已经是最新版本", Toast.LENGTH_SHORT).show(); | |||||
| Log.d(TAG + "版本号是", "onResponse: " + now_version); | Log.d(TAG + "版本号是", "onResponse: " + now_version); | ||||
| } else { | } else { | ||||
| AlertDialog.Builder builder = new AlertDialog.Builder(this); | AlertDialog.Builder builder = new AlertDialog.Builder(this); | ||||
| @@ -1,23 +0,0 @@ | |||||
| /** | |||||
| * Copyright 2020 Huawei Technologies Co., Ltd | |||||
| * <p> | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * <p> | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * <p> | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.mindspore.posenet; | |||||
| import android.media.Image; | |||||
| import android.view.SurfaceView; | |||||
| public interface CameraDataDealListener { | |||||
| void dataDeal(Image image, SurfaceView surfaceView); | |||||
| } | |||||
| @@ -15,12 +15,17 @@ | |||||
| */ | */ | ||||
| package com.mindspore.posenet; | package com.mindspore.posenet; | ||||
| import android.Manifest; | |||||
| import android.annotation.SuppressLint; | import android.annotation.SuppressLint; | ||||
| import android.app.Activity; | import android.app.Activity; | ||||
| import android.content.Context; | import android.content.Context; | ||||
| import android.content.pm.PackageManager; | |||||
| import android.graphics.Bitmap; | |||||
| import android.graphics.Canvas; | |||||
| import android.graphics.Color; | |||||
| import android.graphics.ImageFormat; | import android.graphics.ImageFormat; | ||||
| import android.graphics.Matrix; | |||||
| import android.graphics.Paint; | |||||
| import android.graphics.PorterDuff; | |||||
| import android.graphics.Rect; | |||||
| import android.hardware.camera2.CameraAccessException; | import android.hardware.camera2.CameraAccessException; | ||||
| import android.hardware.camera2.CameraCaptureSession; | import android.hardware.camera2.CameraCaptureSession; | ||||
| import android.hardware.camera2.CameraCharacteristics; | import android.hardware.camera2.CameraCharacteristics; | ||||
| @@ -41,33 +46,54 @@ import android.view.Surface; | |||||
| import android.view.SurfaceView; | import android.view.SurfaceView; | ||||
| import android.view.View; | import android.view.View; | ||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||
| import android.widget.Toast; | |||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||
| import androidx.core.app.ActivityCompat; | |||||
| import androidx.core.util.Pair; | |||||
| import androidx.fragment.app.Fragment; | import androidx.fragment.app.Fragment; | ||||
| import java.nio.ByteBuffer; | |||||
| import java.util.Arrays; | import java.util.Arrays; | ||||
| import java.util.List; | |||||
| import java.util.concurrent.Semaphore; | import java.util.concurrent.Semaphore; | ||||
| import java.util.concurrent.TimeUnit; | import java.util.concurrent.TimeUnit; | ||||
| import static com.mindspore.posenet.Posenet.BodyPart.LEFT_ANKLE; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.LEFT_ELBOW; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.LEFT_HIP; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.LEFT_KNEE; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.LEFT_SHOULDER; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.LEFT_WRIST; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.RIGHT_ANKLE; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.RIGHT_ELBOW; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.RIGHT_HIP; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.RIGHT_KNEE; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.RIGHT_SHOULDER; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.RIGHT_WRIST; | |||||
| /** | /** | ||||
| * A simple {@link Fragment} subclass. | * A simple {@link Fragment} subclass. | ||||
| * create an instance of this fragment. | * create an instance of this fragment. | ||||
| */ | */ | ||||
| public class PoseNetFragment extends Fragment { | public class PoseNetFragment extends Fragment { | ||||
| private final static int REQUEST_CAMERA_PERMISSION = 1; | |||||
| private String cameraId = "1"; | |||||
| private final List bodyJoints = Arrays.asList( | |||||
| new Pair(LEFT_WRIST, LEFT_ELBOW), new Pair(LEFT_ELBOW, LEFT_SHOULDER), | |||||
| new Pair(LEFT_SHOULDER, RIGHT_SHOULDER), new Pair(RIGHT_SHOULDER, RIGHT_ELBOW), | |||||
| new Pair(RIGHT_ELBOW, RIGHT_WRIST), new Pair(LEFT_SHOULDER, LEFT_HIP), | |||||
| new Pair(LEFT_HIP, RIGHT_HIP), new Pair(RIGHT_HIP, RIGHT_SHOULDER), | |||||
| new Pair(LEFT_HIP, LEFT_KNEE), new Pair(LEFT_KNEE, LEFT_ANKLE), | |||||
| new Pair(RIGHT_HIP, RIGHT_KNEE), new Pair(RIGHT_KNEE, RIGHT_ANKLE)); | |||||
| private static final String TAG = "PoseNetFragment"; | |||||
| private int mCameraId = CameraCharacteristics.LENS_FACING_FRONT; // 要打开的摄像头ID | |||||
| private SurfaceView surfaceView; | private SurfaceView surfaceView; | ||||
| private CameraCaptureSession captureSession; | private CameraCaptureSession captureSession; | ||||
| private CameraDevice cameraDevice; | private CameraDevice cameraDevice; | ||||
| private Size previewSize; | |||||
| private int previewWidth; | |||||
| private int previewHeight; | |||||
| private final int PREVIEW_WIDTH = 640; | |||||
| private final int PREVIEW_HEIGHT = 480; | |||||
| private final static int PREVIEW_WIDTH = 640; | |||||
| private final static int PREVIEW_HEIGHT = 480; | |||||
| private Size previewSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT); | |||||
| private HandlerThread backgroundThread; | private HandlerThread backgroundThread; | ||||
| private Handler backgroundHandler; | private Handler backgroundHandler; | ||||
| private ImageReader imageReader; | private ImageReader imageReader; | ||||
| @@ -76,8 +102,18 @@ public class PoseNetFragment extends Fragment { | |||||
| private Semaphore cameraOpenCloseLock = new Semaphore(1);//使用信号量 Semaphore 进行多线程任务调度 | private Semaphore cameraOpenCloseLock = new Semaphore(1);//使用信号量 Semaphore 进行多线程任务调度 | ||||
| private boolean flashSupported; | private boolean flashSupported; | ||||
| /** | |||||
| * Model input shape for images. | |||||
| */ | |||||
| private final static int MODEL_WIDTH = 257; | |||||
| private final static int MODEL_HEIGHT = 257; | |||||
| private static final String TAG = "PoseNetFragment"; | |||||
| private final double minConfidence = 0.5; | |||||
| private final float circleRadius = 8.0f; | |||||
| private Paint paint = new Paint(); | |||||
| private Posenet posenet; | |||||
| private int[] rgbBytes = new int[PREVIEW_WIDTH * PREVIEW_HEIGHT]; | |||||
| private byte[][] yuvBytes = new byte[3][]; | |||||
| private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { | private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { | ||||
| @@ -93,12 +129,14 @@ public class PoseNetFragment extends Fragment { | |||||
| public void onDisconnected(@NonNull CameraDevice cameraDevice) { | public void onDisconnected(@NonNull CameraDevice cameraDevice) { | ||||
| cameraOpenCloseLock.release(); | cameraOpenCloseLock.release(); | ||||
| cameraDevice.close(); | cameraDevice.close(); | ||||
| closeCamera(); | |||||
| PoseNetFragment.this.cameraDevice = null; | PoseNetFragment.this.cameraDevice = null; | ||||
| } | } | ||||
| @Override | @Override | ||||
| public void onError(@NonNull CameraDevice cameraDevice, int error) { | public void onError(@NonNull CameraDevice cameraDevice, int error) { | ||||
| onDisconnected(cameraDevice); | onDisconnected(cameraDevice); | ||||
| closeCamera(); | |||||
| Activity activity = getActivity(); | Activity activity = getActivity(); | ||||
| if (activity != null) { | if (activity != null) { | ||||
| activity.finish(); | activity.finish(); | ||||
| @@ -106,7 +144,6 @@ public class PoseNetFragment extends Fragment { | |||||
| } | } | ||||
| }; | }; | ||||
| private CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { | private CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { | ||||
| @Override | @Override | ||||
| public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { | public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { | ||||
| @@ -119,12 +156,6 @@ public class PoseNetFragment extends Fragment { | |||||
| } | } | ||||
| }; | }; | ||||
| private CameraDataDealListener cameraDataDealListener; | |||||
| public void setCameraDataDealListener(CameraDataDealListener cameraDataDealListener) { | |||||
| this.cameraDataDealListener = cameraDataDealListener; | |||||
| } | |||||
| public static PoseNetFragment newInstance() { | public static PoseNetFragment newInstance() { | ||||
| PoseNetFragment fragment = new PoseNetFragment(); | PoseNetFragment fragment = new PoseNetFragment(); | ||||
| return fragment; | return fragment; | ||||
| @@ -155,86 +186,24 @@ public class PoseNetFragment extends Fragment { | |||||
| openCamera(); | openCamera(); | ||||
| } | } | ||||
| @Override | |||||
| public void onActivityCreated(@Nullable Bundle savedInstanceState) { | |||||
| super.onActivityCreated(savedInstanceState); | |||||
| posenet = new Posenet(getActivity()); | |||||
| } | |||||
| public void onPause() { | public void onPause() { | ||||
| this.closeCamera(); | this.closeCamera(); | ||||
| this.stopBackgroundThread(); | this.stopBackgroundThread(); | ||||
| super.onPause(); | super.onPause(); | ||||
| } | } | ||||
| public void onDestroy() { | |||||
| super.onDestroy(); | |||||
| } | |||||
| private void requestCameraPermission() { | |||||
| if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) { | |||||
| Toast.makeText(getContext(), "This app needs camera permission.", Toast.LENGTH_LONG).show(); | |||||
| } else { | |||||
| requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, | |||||
| Manifest.permission.READ_PHONE_STATE, Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); | |||||
| } | |||||
| } | |||||
| @Override | |||||
| public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | |||||
| super.onRequestPermissionsResult(requestCode, permissions, grantResults); | |||||
| if (requestCode == REQUEST_CAMERA_PERMISSION) { | |||||
| if (allPermissionsGranted(grantResults)) { | |||||
| Toast.makeText(getContext(), "This app needs camera permission.", Toast.LENGTH_LONG).show(); | |||||
| } | |||||
| } else { | |||||
| super.onRequestPermissionsResult(requestCode, permissions, grantResults); | |||||
| } | |||||
| } | |||||
| private boolean allPermissionsGranted(int[] grantResults) { | |||||
| for (int grantResult : grantResults) { | |||||
| if (grantResult == PackageManager.PERMISSION_DENIED) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /** | |||||
| * Sets up member variables related to camera. | |||||
| */ | |||||
| private void setUpCameraOutputs() { | |||||
| CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); | |||||
| try { | |||||
| for (String cameraId : manager.getCameraIdList()) { | |||||
| CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); | |||||
| // We don't use a front facing camera in this sample. | |||||
| Integer cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING); | |||||
| if (cameraDirection != null && cameraDirection == CameraCharacteristics.LENS_FACING_FRONT) { | |||||
| continue; | |||||
| } | |||||
| previewSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT); | |||||
| imageReader = ImageReader.newInstance( | |||||
| PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| ImageFormat.YUV_420_888, /*maxImages*/ 2 | |||||
| ); | |||||
| previewHeight = previewSize.getHeight(); | |||||
| previewWidth = previewSize.getWidth(); | |||||
| // Initialize the storage bitmaps once when the resolution is known. | |||||
| // Check if the flash is supported. | |||||
| flashSupported = | |||||
| characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true; | |||||
| this.cameraId = cameraId; | |||||
| // We've found a viable camera and finished setting up member variables, | |||||
| // so we don't need to iterate through other available cameras. | |||||
| return; | |||||
| } | |||||
| } catch (CameraAccessException e) { | |||||
| e.printStackTrace(); | |||||
| } catch (NullPointerException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| public void switchCamera() { | |||||
| mCameraId ^= 1; | |||||
| Log.d(TAG, "switchCamera: mCameraId: " + mCameraId); | |||||
| closeCamera(); | |||||
| openCamera(); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -242,17 +211,23 @@ public class PoseNetFragment extends Fragment { | |||||
| */ | */ | ||||
| @SuppressLint("MissingPermission") | @SuppressLint("MissingPermission") | ||||
| private void openCamera() { | private void openCamera() { | ||||
| if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { | |||||
| this.requestCameraPermission(); | |||||
| } | |||||
| setUpCameraOutputs(); | |||||
| CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); | CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); | ||||
| try { | try { | ||||
| CameraCharacteristics characteristics = manager.getCameraCharacteristics(Integer.toString(mCameraId)); | |||||
| previewSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT); | |||||
| imageReader = ImageReader.newInstance( | |||||
| PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| ImageFormat.YUV_420_888, /*maxImages*/ 2 | |||||
| ); | |||||
| imageReader.setOnImageAvailableListener(imageAvailableListener, backgroundHandler); | |||||
| // Check if the flash is supported. | |||||
| flashSupported = | |||||
| characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true; | |||||
| // Wait for camera to open - 2.5 seconds is sufficient | // Wait for camera to open - 2.5 seconds is sufficient | ||||
| if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { | if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { | ||||
| throw new RuntimeException("Time out waiting to lock camera opening."); | throw new RuntimeException("Time out waiting to lock camera opening."); | ||||
| } | } | ||||
| manager.openCamera(cameraId, mStateCallback, backgroundHandler); | |||||
| manager.openCamera(Integer.toString(mCameraId), mStateCallback, backgroundHandler); | |||||
| } catch (CameraAccessException e) { | } catch (CameraAccessException e) { | ||||
| e.printStackTrace(); | e.printStackTrace(); | ||||
| } catch (InterruptedException e) { | } catch (InterruptedException e) { | ||||
| @@ -305,31 +280,48 @@ public class PoseNetFragment extends Fragment { | |||||
| } | } | ||||
| } | } | ||||
| private final ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() { | private final ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() { | ||||
| @Override | @Override | ||||
| public void onImageAvailable(ImageReader imageReader) { | public void onImageAvailable(ImageReader imageReader) { | ||||
| if (previewWidth != 0 && previewHeight != 0 && imageReader != null) { | |||||
| if (imageReader != null) { | |||||
| Image image = imageReader.acquireLatestImage(); | Image image = imageReader.acquireLatestImage(); | ||||
| if (cameraDataDealListener != null) { | |||||
| cameraDataDealListener.dataDeal(image, surfaceView); | |||||
| if (image == null || image.getPlanes() == null) { | |||||
| return; | |||||
| } | |||||
| fillBytes(image.getPlanes(), yuvBytes); | |||||
| ImageUtils.convertYUV420ToARGB8888(yuvBytes[0], yuvBytes[1], yuvBytes[2], | |||||
| PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| image.getPlanes()[0].getRowStride(), | |||||
| image.getPlanes()[1].getRowStride(), | |||||
| image.getPlanes()[1].getPixelStride(), | |||||
| rgbBytes); | |||||
| Bitmap imageBitmap = Bitmap.createBitmap( | |||||
| rgbBytes, PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| Bitmap.Config.ARGB_8888); | |||||
| Matrix rotateMatrix = new Matrix(); | |||||
| if (mCameraId == CameraCharacteristics.LENS_FACING_FRONT) { | |||||
| rotateMatrix.postRotate(90.0f); | |||||
| } else if (mCameraId == CameraCharacteristics.LENS_FACING_BACK) { | |||||
| rotateMatrix.postRotate(270.0f); | |||||
| rotateMatrix.postScale(-1.0f, 1.0f); | |||||
| } | } | ||||
| Bitmap rotatedBitmap = Bitmap.createBitmap( | |||||
| imageBitmap, 0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| rotateMatrix, true | |||||
| ); | |||||
| processImage(rotatedBitmap); | |||||
| image.close(); | |||||
| } | } | ||||
| } | } | ||||
| }; | }; | ||||
| /** | /** | ||||
| * Creates a new [CameraCaptureSession] for camera preview. | * Creates a new [CameraCaptureSession] for camera preview. | ||||
| */ | */ | ||||
| private void createCameraPreviewSession() { | private void createCameraPreviewSession() { | ||||
| try { | try { | ||||
| // We capture images from preview in YUV format. | |||||
| imageReader = ImageReader.newInstance( | |||||
| previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2); | |||||
| imageReader.setOnImageAvailableListener(imageAvailableListener, backgroundHandler); | |||||
| // This is the surface we need to record images for processing. | // This is the surface we need to record images for processing. | ||||
| Surface recordingSurface = imageReader.getSurface(); | Surface recordingSurface = imageReader.getSurface(); | ||||
| @@ -387,4 +379,136 @@ public class PoseNetFragment extends Fragment { | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Fill the yuvBytes with data from image planes. | |||||
| */ | |||||
| private void fillBytes(Image.Plane[] planes, byte[][] yuvBytes) { | |||||
| // Row stride is the total number of bytes occupied in memory by a row of an image. | |||||
| // Because of the variable row stride it's not possible to know in | |||||
| // advance the actual necessary dimensions of the yuv planes | |||||
| for (int i = 0; i < planes.length; ++i) { | |||||
| ByteBuffer buffer = planes[i].getBuffer(); | |||||
| if (yuvBytes[i] == null) { | |||||
| yuvBytes[i] = new byte[buffer.capacity()]; | |||||
| } | |||||
| buffer.get(yuvBytes[i]); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Crop Bitmap to maintain aspect ratio of model input. | |||||
| */ | |||||
| private Bitmap cropBitmap(Bitmap bitmap) { | |||||
| float bitmapRatio = bitmap.getHeight() / bitmap.getWidth(); | |||||
| float modelInputRatio = MODEL_HEIGHT / MODEL_WIDTH; | |||||
| double maxDifference = 1.0E-5D; | |||||
| float cropHeight = modelInputRatio - bitmapRatio; | |||||
| if (Math.abs(cropHeight) < maxDifference) { | |||||
| return bitmap; | |||||
| } else { | |||||
| Bitmap croppedBitmap; | |||||
| if (modelInputRatio < bitmapRatio) { | |||||
| cropHeight = (float) bitmap.getHeight() - (float) bitmap.getWidth() / modelInputRatio; | |||||
| croppedBitmap = Bitmap.createBitmap(bitmap, | |||||
| 0, (int) (cropHeight / 2), bitmap.getWidth(), (int) (bitmap.getHeight() - cropHeight)); | |||||
| } else { | |||||
| cropHeight = (float) bitmap.getWidth() - (float) bitmap.getHeight() * modelInputRatio; | |||||
| croppedBitmap = Bitmap.createBitmap(bitmap, | |||||
| (int) (cropHeight / 2), 0, (int) (bitmap.getWidth() - cropHeight), bitmap.getHeight()); | |||||
| } | |||||
| return croppedBitmap; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Set the paint color and size. | |||||
| */ | |||||
| private void setPaint() { | |||||
| paint.setColor(getResources().getColor(R.color.posenet_text_blue)); | |||||
| paint.setTextSize(80.0f); | |||||
| paint.setStrokeWidth(8.0f); | |||||
| } | |||||
| /** | |||||
| * Draw bitmap on Canvas. | |||||
| */ | |||||
| private void draw(Canvas canvas, Posenet.Person person, Bitmap bitmap) { | |||||
| canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); | |||||
| // Draw `bitmap` and `person` in square canvas. | |||||
| int screenWidth, screenHeight; | |||||
| int left, right, top, bottom; | |||||
| if (canvas.getHeight() > canvas.getWidth()) { | |||||
| screenWidth = canvas.getWidth(); | |||||
| screenHeight = canvas.getWidth(); | |||||
| left = 0; | |||||
| top = (canvas.getHeight() - canvas.getWidth()) / 2; | |||||
| } else { | |||||
| screenWidth = canvas.getHeight(); | |||||
| screenHeight = canvas.getHeight(); | |||||
| left = (canvas.getWidth() - canvas.getHeight()) / 2; | |||||
| top = 0; | |||||
| } | |||||
| right = left + screenWidth; | |||||
| bottom = top + screenHeight; | |||||
| setPaint(); | |||||
| canvas.drawBitmap( | |||||
| bitmap, | |||||
| new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), | |||||
| new Rect(left, top, right, bottom), paint); | |||||
| float widthRatio = (float) screenWidth / MODEL_WIDTH; | |||||
| float heightRatio = (float) screenHeight / MODEL_HEIGHT; | |||||
| for (Posenet.KeyPoint keyPoint : person.keyPoints) { | |||||
| if (keyPoint.score > minConfidence) { | |||||
| Posenet.Position position = keyPoint.position; | |||||
| float adjustedX = position.x * widthRatio + left; | |||||
| float adjustedY = position.y * heightRatio + top; | |||||
| canvas.drawCircle(adjustedX, adjustedY, circleRadius, paint); | |||||
| } | |||||
| } | |||||
| for (int i = 0; i < bodyJoints.size(); i++) { | |||||
| Pair line = (Pair) bodyJoints.get(i); | |||||
| Posenet.BodyPart first = (Posenet.BodyPart) line.first; | |||||
| Posenet.BodyPart second = (Posenet.BodyPart) line.second; | |||||
| if (person.keyPoints.get(first.ordinal()).score > minConfidence & | |||||
| person.keyPoints.get(second.ordinal()).score > minConfidence) { | |||||
| canvas.drawLine( | |||||
| person.keyPoints.get(first.ordinal()).position.x * widthRatio + left, | |||||
| person.keyPoints.get(first.ordinal()).position.y * heightRatio + top, | |||||
| person.keyPoints.get(second.ordinal()).position.x * widthRatio + left, | |||||
| person.keyPoints.get(second.ordinal()).position.y * heightRatio + top, paint); | |||||
| } | |||||
| } | |||||
| canvas.drawText(String.format("Score: %.2f", person.score), | |||||
| (15.0f * widthRatio), (30.0f * heightRatio + bottom), paint); | |||||
| canvas.drawText(String.format("Time: %.2f ms", posenet.lastInferenceTimeNanos * 1.0f / 1_000_000), | |||||
| (15.0f * widthRatio), (50.0f * heightRatio + bottom), paint | |||||
| ); | |||||
| // Draw! | |||||
| surfaceView.getHolder().unlockCanvasAndPost(canvas); | |||||
| } | |||||
| /** | |||||
| * Process image using Posenet library. | |||||
| */ | |||||
| private void processImage(Bitmap bitmap) { | |||||
| // Crop bitmap. | |||||
| Bitmap croppedBitmap = cropBitmap(bitmap); | |||||
| // Created scaled version of bitmap for model input. | |||||
| Bitmap scaledBitmap = Bitmap.createScaledBitmap(croppedBitmap, MODEL_WIDTH, MODEL_HEIGHT, true); | |||||
| // Perform inference. | |||||
| Posenet.Person person = posenet.estimateSinglePose(scaledBitmap); | |||||
| Canvas canvas = surfaceView.getHolder().lockCanvas(); | |||||
| draw(canvas, person, scaledBitmap); | |||||
| } | |||||
| } | } | ||||
| @@ -15,6 +15,10 @@ | |||||
| */ | */ | ||||
| package com.mindspore.posenet; | package com.mindspore.posenet; | ||||
| import android.Manifest; | |||||
| import android.content.DialogInterface; | |||||
| import android.content.Intent; | |||||
| import android.content.pm.PackageManager; | |||||
| import android.graphics.Bitmap; | import android.graphics.Bitmap; | ||||
| import android.graphics.Canvas; | import android.graphics.Canvas; | ||||
| import android.graphics.Color; | import android.graphics.Color; | ||||
| @@ -24,10 +28,19 @@ import android.graphics.PorterDuff; | |||||
| import android.graphics.Rect; | import android.graphics.Rect; | ||||
| import android.hardware.camera2.CameraCharacteristics; | import android.hardware.camera2.CameraCharacteristics; | ||||
| import android.media.Image; | import android.media.Image; | ||||
| import android.net.Uri; | |||||
| import android.os.Build; | |||||
| import android.os.Bundle; | import android.os.Bundle; | ||||
| import android.provider.Settings; | |||||
| import android.view.SurfaceView; | import android.view.SurfaceView; | ||||
| import android.view.View; | |||||
| import androidx.annotation.NonNull; | |||||
| import androidx.annotation.Nullable; | |||||
| import androidx.appcompat.app.AlertDialog; | |||||
| import androidx.appcompat.app.AppCompatActivity; | import androidx.appcompat.app.AppCompatActivity; | ||||
| import androidx.core.app.ActivityCompat; | |||||
| import androidx.core.content.ContextCompat; | |||||
| import androidx.core.util.Pair; | import androidx.core.util.Pair; | ||||
| import com.alibaba.android.arouter.facade.annotation.Route; | import com.alibaba.android.arouter.facade.annotation.Route; | ||||
| @@ -50,213 +63,114 @@ import static com.mindspore.posenet.Posenet.BodyPart.RIGHT_SHOULDER; | |||||
| import static com.mindspore.posenet.Posenet.BodyPart.RIGHT_WRIST; | import static com.mindspore.posenet.Posenet.BodyPart.RIGHT_WRIST; | ||||
| @Route(path = "/posenet/PosenetMainActivity") | @Route(path = "/posenet/PosenetMainActivity") | ||||
| public class PosenetMainActivity extends AppCompatActivity implements CameraDataDealListener { | |||||
| public class PosenetMainActivity extends AppCompatActivity { | |||||
| private final List bodyJoints = Arrays.asList( | |||||
| new Pair(LEFT_WRIST, LEFT_ELBOW), new Pair(LEFT_ELBOW, LEFT_SHOULDER), | |||||
| new Pair(LEFT_SHOULDER, RIGHT_SHOULDER), new Pair(RIGHT_SHOULDER, RIGHT_ELBOW), | |||||
| new Pair(RIGHT_ELBOW, RIGHT_WRIST), new Pair(LEFT_SHOULDER, LEFT_HIP), | |||||
| new Pair(LEFT_HIP, RIGHT_HIP), new Pair(RIGHT_HIP, RIGHT_SHOULDER), | |||||
| new Pair(LEFT_HIP, LEFT_KNEE), new Pair(LEFT_KNEE, LEFT_ANKLE), | |||||
| new Pair(RIGHT_HIP, RIGHT_KNEE), new Pair(RIGHT_KNEE, RIGHT_ANKLE)); | |||||
| ; | |||||
| private static final String[] PERMISSIONS = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, | |||||
| Manifest.permission.READ_PHONE_STATE, Manifest.permission.CAMERA}; | |||||
| private static final int REQUEST_PERMISSION = 1; | |||||
| private static final int REQUEST_PERMISSION_AGAIN = 2; | |||||
| private boolean isAllGranted; | |||||
| /** | |||||
| * Model input shape for images. | |||||
| */ | |||||
| private final static int MODEL_WIDTH = 257; | |||||
| private final static int MODEL_HEIGHT = 257; | |||||
| private final double minConfidence = 0.5; | |||||
| private final float circleRadius = 8.0f; | |||||
| private Paint paint = new Paint(); | |||||
| private final int PREVIEW_WIDTH = 640; | |||||
| private final int PREVIEW_HEIGHT = 480; | |||||
| private Posenet posenet; | |||||
| private int[] rgbBytes = new int[PREVIEW_WIDTH * PREVIEW_HEIGHT]; | |||||
| private byte[][] yuvBytes = new byte[3][]; | |||||
| private SurfaceView surfaceView; | |||||
| private int lensFacing = CameraCharacteristics.LENS_FACING_BACK; | |||||
| private PoseNetFragment poseNetFragment; | private PoseNetFragment poseNetFragment; | ||||
| @Override | @Override | ||||
| protected void onCreate(Bundle savedInstanceState) { | protected void onCreate(Bundle savedInstanceState) { | ||||
| super.onCreate(savedInstanceState); | super.onCreate(savedInstanceState); | ||||
| setContentView(R.layout.posenet_activity_main); | setContentView(R.layout.posenet_activity_main); | ||||
| addCameraFragment(); | |||||
| requestPermissions(); | |||||
| } | } | ||||
| private void addCameraFragment() { | |||||
| posenet = new Posenet(this); | |||||
| poseNetFragment = PoseNetFragment.newInstance(); | |||||
| poseNetFragment.setCameraDataDealListener(this); | |||||
| // poseNetFragment.setFacingCamera(lensFacing); | |||||
| getSupportFragmentManager().popBackStack(); | |||||
| getSupportFragmentManager().beginTransaction() | |||||
| .replace(R.id.container, poseNetFragment) | |||||
| .commitAllowingStateLoss(); | |||||
| } | |||||
| @Override | |||||
| public void dataDeal(Image image, SurfaceView surfaceView) { | |||||
| if (image == null || image.getPlanes() == null) { | |||||
| return; | |||||
| private void requestPermissions() { | |||||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | |||||
| isAllGranted = checkPermissionAllGranted(PERMISSIONS); | |||||
| if (!isAllGranted) { | |||||
| ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_PERMISSION); | |||||
| } else { | |||||
| addCameraFragment(); | |||||
| } | |||||
| } else { | |||||
| isAllGranted = true; | |||||
| addCameraFragment(); | |||||
| } | } | ||||
| this.surfaceView = surfaceView; | |||||
| fillBytes(image.getPlanes(), yuvBytes); | |||||
| ImageUtils.convertYUV420ToARGB8888(yuvBytes[0], yuvBytes[1], yuvBytes[2], | |||||
| PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| image.getPlanes()[0].getRowStride(), | |||||
| image.getPlanes()[1].getRowStride(), | |||||
| image.getPlanes()[1].getPixelStride(), | |||||
| rgbBytes); | |||||
| Bitmap imageBitmap = Bitmap.createBitmap( | |||||
| rgbBytes, PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| Bitmap.Config.ARGB_8888); | |||||
| Matrix rotateMatrix = new Matrix(); | |||||
| rotateMatrix.postRotate(90.0f); | |||||
| Bitmap rotatedBitmap = Bitmap.createBitmap( | |||||
| imageBitmap, 0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| rotateMatrix, true | |||||
| ); | |||||
| image.close(); | |||||
| processImage(rotatedBitmap); | |||||
| } | } | ||||
| /** | |||||
| * Fill the yuvBytes with data from image planes. | |||||
| */ | |||||
| private void fillBytes(Image.Plane[] planes, byte[][] yuvBytes) { | |||||
| // Row stride is the total number of bytes occupied in memory by a row of an image. | |||||
| // Because of the variable row stride it's not possible to know in | |||||
| // advance the actual necessary dimensions of the yuv planes | |||||
| for (int i = 0; i < planes.length; ++i) { | |||||
| ByteBuffer buffer = planes[i].getBuffer(); | |||||
| if (yuvBytes[i] == null) { | |||||
| yuvBytes[i] = new byte[buffer.capacity()]; | |||||
| private boolean checkPermissionAllGranted(String[] permissions) { | |||||
| for (String permission : permissions) { | |||||
| if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { | |||||
| return false; | |||||
| } | } | ||||
| buffer.get(yuvBytes[i]); | |||||
| } | } | ||||
| return true; | |||||
| } | } | ||||
| /** | /** | ||||
| * Crop Bitmap to maintain aspect ratio of model input. | |||||
| * Authority application result callback | |||||
| */ | */ | ||||
| private Bitmap cropBitmap(Bitmap bitmap) { | |||||
| float bitmapRatio = bitmap.getHeight() / bitmap.getWidth(); | |||||
| float modelInputRatio = MODEL_HEIGHT / MODEL_WIDTH; | |||||
| double maxDifference = 1.0E-5D; | |||||
| float cropHeight = modelInputRatio - bitmapRatio; | |||||
| if (Math.abs(cropHeight) < maxDifference) { | |||||
| return bitmap; | |||||
| } else { | |||||
| Bitmap croppedBitmap; | |||||
| if (modelInputRatio < bitmapRatio) { | |||||
| cropHeight = (float) bitmap.getHeight() - (float) bitmap.getWidth() / modelInputRatio; | |||||
| croppedBitmap = Bitmap.createBitmap(bitmap, | |||||
| 0, (int) (cropHeight / 2), bitmap.getWidth(), (int) (bitmap.getHeight() - cropHeight)); | |||||
| @Override | |||||
| public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | |||||
| if (REQUEST_PERMISSION == requestCode) { | |||||
| isAllGranted = true; | |||||
| for (int grant : grantResults) { | |||||
| if (grant != PackageManager.PERMISSION_GRANTED) { | |||||
| isAllGranted = false; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (!isAllGranted) { | |||||
| openAppDetails(); | |||||
| } else { | } else { | ||||
| cropHeight = (float) bitmap.getWidth() - (float) bitmap.getHeight() * modelInputRatio; | |||||
| croppedBitmap = Bitmap.createBitmap(bitmap, | |||||
| (int) (cropHeight / 2), 0, (int) (bitmap.getWidth() - cropHeight), bitmap.getHeight()); | |||||
| addCameraFragment(); | |||||
| } | } | ||||
| return croppedBitmap; | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Set the paint color and size. | |||||
| */ | |||||
| private void setPaint() { | |||||
| paint.setColor(getResources().getColor(R.color.posenet_text_blue)); | |||||
| paint.setTextSize(80.0f); | |||||
| paint.setStrokeWidth(8.0f); | |||||
| } | |||||
| /** | |||||
| * Draw bitmap on Canvas. | |||||
| */ | |||||
| private void draw(Canvas canvas, Posenet.Person person, Bitmap bitmap) { | |||||
| canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); | |||||
| // Draw `bitmap` and `person` in square canvas. | |||||
| int screenWidth, screenHeight; | |||||
| int left, right, top, bottom; | |||||
| if (canvas.getHeight() > canvas.getWidth()) { | |||||
| screenWidth = canvas.getWidth(); | |||||
| screenHeight = canvas.getWidth(); | |||||
| left = 0; | |||||
| top = (canvas.getHeight() - canvas.getWidth()) / 2; | |||||
| } else { | |||||
| screenWidth = canvas.getHeight(); | |||||
| screenHeight = canvas.getHeight(); | |||||
| left = (canvas.getWidth() - canvas.getHeight()) / 2; | |||||
| top = 0; | |||||
| } | |||||
| right = left + screenWidth; | |||||
| bottom = top + screenHeight; | |||||
| setPaint(); | |||||
| canvas.drawBitmap( | |||||
| bitmap, | |||||
| new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), | |||||
| new Rect(left, top, right, bottom), paint); | |||||
| float widthRatio = (float) screenWidth / MODEL_WIDTH; | |||||
| float heightRatio = (float) screenHeight / MODEL_HEIGHT; | |||||
| for (Posenet.KeyPoint keyPoint : person.keyPoints) { | |||||
| if (keyPoint.score > minConfidence) { | |||||
| Posenet.Position position = keyPoint.position; | |||||
| float adjustedX = position.x * widthRatio + left; | |||||
| float adjustedY = position.y * heightRatio + top; | |||||
| canvas.drawCircle(adjustedX, adjustedY, circleRadius, paint); | |||||
| private void openAppDetails() { | |||||
| AlertDialog.Builder builder = new AlertDialog.Builder(this); | |||||
| builder.setMessage("PoseNet 需要访问 “相机” 和 “外部存储器”,请到 “应用信息 -> 权限” 中授予!"); | |||||
| builder.setPositiveButton("去手动授权", new DialogInterface.OnClickListener() { | |||||
| @Override | |||||
| public void onClick(DialogInterface dialog, int which) { | |||||
| Intent intent = new Intent(); | |||||
| intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); | |||||
| intent.addCategory(Intent.CATEGORY_DEFAULT); | |||||
| intent.setData(Uri.parse("package:" + getPackageName())); | |||||
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | |||||
| intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); | |||||
| intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); | |||||
| startActivityForResult(intent, REQUEST_PERMISSION_AGAIN); | |||||
| } | } | ||||
| } | |||||
| for (int i = 0; i < bodyJoints.size(); i++) { | |||||
| Pair line = (Pair) bodyJoints.get(i); | |||||
| Posenet.BodyPart first = (Posenet.BodyPart) line.first; | |||||
| Posenet.BodyPart second = (Posenet.BodyPart) line.second; | |||||
| if (person.keyPoints.get(first.ordinal()).score > minConfidence & | |||||
| person.keyPoints.get(second.ordinal()).score > minConfidence) { | |||||
| canvas.drawLine( | |||||
| person.keyPoints.get(first.ordinal()).position.x * widthRatio + left, | |||||
| person.keyPoints.get(first.ordinal()).position.y * heightRatio + top, | |||||
| person.keyPoints.get(second.ordinal()).position.x * widthRatio + left, | |||||
| person.keyPoints.get(second.ordinal()).position.y * heightRatio + top, paint); | |||||
| }); | |||||
| builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { | |||||
| @Override | |||||
| public void onClick(DialogInterface dialog, int which) { | |||||
| finish(); | |||||
| } | } | ||||
| } | |||||
| }); | |||||
| builder.show(); | |||||
| } | |||||
| canvas.drawText(String.format("Score: %.2f", person.score), | |||||
| (15.0f * widthRatio), (30.0f * heightRatio + bottom), paint); | |||||
| canvas.drawText(String.format("Time: %.2f ms", posenet.lastInferenceTimeNanos * 1.0f / 1_000_000), | |||||
| (15.0f * widthRatio), (50.0f * heightRatio + bottom), paint | |||||
| ); | |||||
| @Override | |||||
| protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { | |||||
| super.onActivityResult(requestCode, resultCode, data); | |||||
| if (REQUEST_PERMISSION_AGAIN == requestCode) { | |||||
| requestPermissions(); | |||||
| } | |||||
| } | |||||
| // Draw! | |||||
| surfaceView.getHolder().unlockCanvasAndPost(canvas); | |||||
| private void addCameraFragment() { | |||||
| poseNetFragment = PoseNetFragment.newInstance(); | |||||
| getSupportFragmentManager().popBackStack(); | |||||
| getSupportFragmentManager().beginTransaction() | |||||
| .replace(R.id.container, poseNetFragment) | |||||
| .commitAllowingStateLoss(); | |||||
| } | } | ||||
| /** | |||||
| * Process image using Posenet library. | |||||
| */ | |||||
| private void processImage(Bitmap bitmap) { | |||||
| // Crop bitmap. | |||||
| Bitmap croppedBitmap = cropBitmap(bitmap); | |||||
| // Created scaled version of bitmap for model input. | |||||
| Bitmap scaledBitmap = Bitmap.createScaledBitmap(croppedBitmap, MODEL_WIDTH, MODEL_HEIGHT, true); | |||||
| // Perform inference. | |||||
| Posenet.Person person = posenet.estimateSinglePose(scaledBitmap); | |||||
| Canvas canvas = surfaceView.getHolder().lockCanvas(); | |||||
| draw(canvas, person, scaledBitmap); | |||||
| public void onClickSwitch(View view) { | |||||
| poseNetFragment.switchCamera(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -27,5 +27,14 @@ | |||||
| android:text="MindSpore PoseNet" | android:text="MindSpore PoseNet" | ||||
| android:textColor="#ffffff" | android:textColor="#ffffff" | ||||
| android:textSize="20sp" /> | android:textSize="20sp" /> | ||||
| <ImageView | |||||
| android:onClick="onClickSwitch" | |||||
| android:clickable="true" | |||||
| android:layout_marginEnd="10dp" | |||||
| android:layout_gravity="center_vertical|right" | |||||
| android:layout_width="45dp" | |||||
| android:layout_height="45dp" | |||||
| android:src="@drawable/ic_camera_switch"/> | |||||
| </androidx.appcompat.widget.Toolbar> | </androidx.appcompat.widget.Toolbar> | ||||
| </RelativeLayout> | </RelativeLayout> | ||||
| @@ -1,23 +0,0 @@ | |||||
| /** | |||||
| * Copyright 2020 Huawei Technologies Co., Ltd | |||||
| * <p> | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * <p> | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * <p> | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.mindspore.posenetdemo; | |||||
| import android.media.Image; | |||||
| import android.view.SurfaceView; | |||||
| public interface CameraDataDealListener { | |||||
| void dataDeal(Image image, SurfaceView surfaceView); | |||||
| } | |||||
| @@ -19,20 +19,11 @@ import android.Manifest; | |||||
| import android.content.DialogInterface; | import android.content.DialogInterface; | ||||
| import android.content.Intent; | import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | import android.content.pm.PackageManager; | ||||
| import android.graphics.Bitmap; | |||||
| import android.graphics.Canvas; | |||||
| import android.graphics.Color; | |||||
| import android.graphics.Matrix; | |||||
| import android.graphics.Paint; | |||||
| import android.graphics.PorterDuff; | |||||
| import android.graphics.Rect; | |||||
| import android.hardware.camera2.CameraCharacteristics; | |||||
| import android.media.Image; | |||||
| import android.net.Uri; | import android.net.Uri; | ||||
| import android.os.Build; | import android.os.Build; | ||||
| import android.os.Bundle; | import android.os.Bundle; | ||||
| import android.provider.Settings; | import android.provider.Settings; | ||||
| import android.view.SurfaceView; | |||||
| import android.view.View; | |||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||
| @@ -40,34 +31,8 @@ import androidx.appcompat.app.AlertDialog; | |||||
| import androidx.appcompat.app.AppCompatActivity; | import androidx.appcompat.app.AppCompatActivity; | ||||
| import androidx.core.app.ActivityCompat; | import androidx.core.app.ActivityCompat; | ||||
| import androidx.core.content.ContextCompat; | import androidx.core.content.ContextCompat; | ||||
| import androidx.core.util.Pair; | |||||
| import java.nio.ByteBuffer; | |||||
| import java.util.Arrays; | |||||
| import java.util.List; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_ANKLE; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_ELBOW; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_HIP; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_KNEE; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_SHOULDER; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_WRIST; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_ANKLE; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_ELBOW; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_HIP; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_KNEE; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_SHOULDER; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_WRIST; | |||||
| public class MainActivity extends AppCompatActivity implements CameraDataDealListener { | |||||
| private final List bodyJoints = Arrays.asList( | |||||
| new Pair(LEFT_WRIST, LEFT_ELBOW), new Pair(LEFT_ELBOW, LEFT_SHOULDER), | |||||
| new Pair(LEFT_SHOULDER, RIGHT_SHOULDER), new Pair(RIGHT_SHOULDER, RIGHT_ELBOW), | |||||
| new Pair(RIGHT_ELBOW, RIGHT_WRIST), new Pair(LEFT_SHOULDER, LEFT_HIP), | |||||
| new Pair(LEFT_HIP, RIGHT_HIP), new Pair(RIGHT_HIP, RIGHT_SHOULDER), | |||||
| new Pair(LEFT_HIP, LEFT_KNEE), new Pair(LEFT_KNEE, LEFT_ANKLE), | |||||
| new Pair(RIGHT_HIP, RIGHT_KNEE), new Pair(RIGHT_KNEE, RIGHT_ANKLE)); | |||||
| public class MainActivity extends AppCompatActivity { | |||||
| private static final String[] PERMISSIONS = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, | private static final String[] PERMISSIONS = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, | ||||
| Manifest.permission.READ_PHONE_STATE, Manifest.permission.CAMERA}; | Manifest.permission.READ_PHONE_STATE, Manifest.permission.CAMERA}; | ||||
| @@ -75,23 +40,6 @@ public class MainActivity extends AppCompatActivity implements CameraDataDealLis | |||||
| private static final int REQUEST_PERMISSION_AGAIN = 2; | private static final int REQUEST_PERMISSION_AGAIN = 2; | ||||
| private boolean isAllGranted; | private boolean isAllGranted; | ||||
| /** | |||||
| * Model input shape for images. | |||||
| */ | |||||
| private final static int MODEL_WIDTH = 257; | |||||
| private final static int MODEL_HEIGHT = 257; | |||||
| private final double minConfidence = 0.5; | |||||
| private final float circleRadius = 8.0f; | |||||
| private Paint paint = new Paint(); | |||||
| private final int PREVIEW_WIDTH = 640; | |||||
| private final int PREVIEW_HEIGHT = 480; | |||||
| private Posenet posenet; | |||||
| private int[] rgbBytes = new int[PREVIEW_WIDTH * PREVIEW_HEIGHT]; | |||||
| private byte[][] yuvBytes = new byte[3][]; | |||||
| private SurfaceView surfaceView; | |||||
| private int lensFacing = CameraCharacteristics.LENS_FACING_BACK; | |||||
| private PoseNetFragment poseNetFragment; | private PoseNetFragment poseNetFragment; | ||||
| @Override | @Override | ||||
| @@ -182,173 +130,16 @@ public class MainActivity extends AppCompatActivity implements CameraDataDealLis | |||||
| } | } | ||||
| private void addCameraFragment() { | private void addCameraFragment() { | ||||
| posenet = new Posenet(this); | |||||
| poseNetFragment = PoseNetFragment.newInstance(); | poseNetFragment = PoseNetFragment.newInstance(); | ||||
| poseNetFragment.setCameraDataDealListener(this); | |||||
| // poseNetFragment.setFacingCamera(lensFacing); | |||||
| getSupportFragmentManager().popBackStack(); | getSupportFragmentManager().popBackStack(); | ||||
| getSupportFragmentManager().beginTransaction() | getSupportFragmentManager().beginTransaction() | ||||
| .replace(R.id.container, poseNetFragment) | .replace(R.id.container, poseNetFragment) | ||||
| .commitAllowingStateLoss(); | .commitAllowingStateLoss(); | ||||
| } | } | ||||
| @Override | |||||
| public void dataDeal(Image image, SurfaceView surfaceView) { | |||||
| if (image == null || image.getPlanes() == null) { | |||||
| return; | |||||
| } | |||||
| this.surfaceView = surfaceView; | |||||
| fillBytes(image.getPlanes(), yuvBytes); | |||||
| ImageUtils.convertYUV420ToARGB8888(yuvBytes[0], yuvBytes[1], yuvBytes[2], | |||||
| PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| image.getPlanes()[0].getRowStride(), | |||||
| image.getPlanes()[1].getRowStride(), | |||||
| image.getPlanes()[1].getPixelStride(), | |||||
| rgbBytes); | |||||
| Bitmap imageBitmap = Bitmap.createBitmap( | |||||
| rgbBytes, PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| Bitmap.Config.ARGB_8888); | |||||
| Matrix rotateMatrix = new Matrix(); | |||||
| rotateMatrix.postRotate(90.0f); | |||||
| Bitmap rotatedBitmap = Bitmap.createBitmap( | |||||
| imageBitmap, 0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| rotateMatrix, true | |||||
| ); | |||||
| image.close(); | |||||
| processImage(rotatedBitmap); | |||||
| } | |||||
| /** | |||||
| * Fill the yuvBytes with data from image planes. | |||||
| */ | |||||
| private void fillBytes(Image.Plane[] planes, byte[][] yuvBytes) { | |||||
| // Row stride is the total number of bytes occupied in memory by a row of an image. | |||||
| // Because of the variable row stride it's not possible to know in | |||||
| // advance the actual necessary dimensions of the yuv planes | |||||
| for (int i = 0; i < planes.length; ++i) { | |||||
| ByteBuffer buffer = planes[i].getBuffer(); | |||||
| if (yuvBytes[i] == null) { | |||||
| yuvBytes[i] = new byte[buffer.capacity()]; | |||||
| } | |||||
| buffer.get(yuvBytes[i]); | |||||
| } | |||||
| public void onClickSwitch(View view) { | |||||
| poseNetFragment.switchCamera(); | |||||
| } | } | ||||
| /** | |||||
| * Crop Bitmap to maintain aspect ratio of model input. | |||||
| */ | |||||
| private Bitmap cropBitmap(Bitmap bitmap) { | |||||
| float bitmapRatio = bitmap.getHeight() / bitmap.getWidth(); | |||||
| float modelInputRatio = MODEL_HEIGHT / MODEL_WIDTH; | |||||
| double maxDifference = 1.0E-5D; | |||||
| float cropHeight = modelInputRatio - bitmapRatio; | |||||
| if (Math.abs(cropHeight) < maxDifference) { | |||||
| return bitmap; | |||||
| } else { | |||||
| Bitmap croppedBitmap; | |||||
| if (modelInputRatio < bitmapRatio) { | |||||
| cropHeight = (float) bitmap.getHeight() - (float) bitmap.getWidth() / modelInputRatio; | |||||
| croppedBitmap = Bitmap.createBitmap(bitmap, | |||||
| 0, (int) (cropHeight / 2), bitmap.getWidth(), (int) (bitmap.getHeight() - cropHeight)); | |||||
| } else { | |||||
| cropHeight = (float) bitmap.getWidth() - (float) bitmap.getHeight() * modelInputRatio; | |||||
| croppedBitmap = Bitmap.createBitmap(bitmap, | |||||
| (int) (cropHeight / 2), 0, (int) (bitmap.getWidth() - cropHeight), bitmap.getHeight()); | |||||
| } | |||||
| return croppedBitmap; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Set the paint color and size. | |||||
| */ | |||||
| private void setPaint() { | |||||
| paint.setColor(getResources().getColor(R.color.text_blue)); | |||||
| paint.setTextSize(80.0f); | |||||
| paint.setStrokeWidth(8.0f); | |||||
| } | |||||
| /** | |||||
| * Draw bitmap on Canvas. | |||||
| */ | |||||
| private void draw(Canvas canvas, Posenet.Person person, Bitmap bitmap) { | |||||
| canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); | |||||
| // Draw `bitmap` and `person` in square canvas. | |||||
| int screenWidth, screenHeight; | |||||
| int left, right, top, bottom; | |||||
| if (canvas.getHeight() > canvas.getWidth()) { | |||||
| screenWidth = canvas.getWidth(); | |||||
| screenHeight = canvas.getWidth(); | |||||
| left = 0; | |||||
| top = (canvas.getHeight() - canvas.getWidth()) / 2; | |||||
| } else { | |||||
| screenWidth = canvas.getHeight(); | |||||
| screenHeight = canvas.getHeight(); | |||||
| left = (canvas.getWidth() - canvas.getHeight()) / 2; | |||||
| top = 0; | |||||
| } | |||||
| right = left + screenWidth; | |||||
| bottom = top + screenHeight; | |||||
| setPaint(); | |||||
| canvas.drawBitmap( | |||||
| bitmap, | |||||
| new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), | |||||
| new Rect(left, top, right, bottom), paint); | |||||
| float widthRatio = (float) screenWidth / MODEL_WIDTH; | |||||
| float heightRatio = (float) screenHeight / MODEL_HEIGHT; | |||||
| for (Posenet.KeyPoint keyPoint : person.keyPoints) { | |||||
| if (keyPoint.score > minConfidence) { | |||||
| Posenet.Position position = keyPoint.position; | |||||
| float adjustedX = position.x * widthRatio + left; | |||||
| float adjustedY = position.y * heightRatio + top; | |||||
| canvas.drawCircle(adjustedX, adjustedY, circleRadius, paint); | |||||
| } | |||||
| } | |||||
| for (int i = 0; i < bodyJoints.size(); i++) { | |||||
| Pair line = (Pair) bodyJoints.get(i); | |||||
| Posenet.BodyPart first = (Posenet.BodyPart) line.first; | |||||
| Posenet.BodyPart second = (Posenet.BodyPart) line.second; | |||||
| if (person.keyPoints.get(first.ordinal()).score > minConfidence & | |||||
| person.keyPoints.get(second.ordinal()).score > minConfidence) { | |||||
| canvas.drawLine( | |||||
| person.keyPoints.get(first.ordinal()).position.x * widthRatio + left, | |||||
| person.keyPoints.get(first.ordinal()).position.y * heightRatio + top, | |||||
| person.keyPoints.get(second.ordinal()).position.x * widthRatio + left, | |||||
| person.keyPoints.get(second.ordinal()).position.y * heightRatio + top, paint); | |||||
| } | |||||
| } | |||||
| canvas.drawText(String.format("Score: %.2f", person.score), | |||||
| (15.0f * widthRatio), (30.0f * heightRatio + bottom), paint); | |||||
| canvas.drawText(String.format("Time: %.2f ms", posenet.lastInferenceTimeNanos * 1.0f / 1_000_000), | |||||
| (15.0f * widthRatio), (50.0f * heightRatio + bottom), paint | |||||
| ); | |||||
| // Draw! | |||||
| surfaceView.getHolder().unlockCanvasAndPost(canvas); | |||||
| } | |||||
| /** | |||||
| * Process image using Posenet library. | |||||
| */ | |||||
| private void processImage(Bitmap bitmap) { | |||||
| // Crop bitmap. | |||||
| Bitmap croppedBitmap = cropBitmap(bitmap); | |||||
| // Created scaled version of bitmap for model input. | |||||
| Bitmap scaledBitmap = Bitmap.createScaledBitmap(croppedBitmap, MODEL_WIDTH, MODEL_HEIGHT, true); | |||||
| // Perform inference. | |||||
| Posenet.Person person = posenet.estimateSinglePose(scaledBitmap); | |||||
| Canvas canvas = surfaceView.getHolder().lockCanvas(); | |||||
| draw(canvas, person, scaledBitmap); | |||||
| } | |||||
| } | } | ||||
| @@ -18,7 +18,14 @@ package com.mindspore.posenetdemo; | |||||
| import android.annotation.SuppressLint; | import android.annotation.SuppressLint; | ||||
| import android.app.Activity; | import android.app.Activity; | ||||
| import android.content.Context; | import android.content.Context; | ||||
| import android.graphics.Bitmap; | |||||
| import android.graphics.Canvas; | |||||
| import android.graphics.Color; | |||||
| import android.graphics.ImageFormat; | import android.graphics.ImageFormat; | ||||
| import android.graphics.Matrix; | |||||
| import android.graphics.Paint; | |||||
| import android.graphics.PorterDuff; | |||||
| import android.graphics.Rect; | |||||
| import android.hardware.camera2.CameraAccessException; | import android.hardware.camera2.CameraAccessException; | ||||
| import android.hardware.camera2.CameraCaptureSession; | import android.hardware.camera2.CameraCaptureSession; | ||||
| import android.hardware.camera2.CameraCharacteristics; | import android.hardware.camera2.CameraCharacteristics; | ||||
| @@ -42,29 +49,51 @@ import android.view.ViewGroup; | |||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||
| import androidx.core.util.Pair; | |||||
| import androidx.fragment.app.Fragment; | import androidx.fragment.app.Fragment; | ||||
| import java.nio.ByteBuffer; | |||||
| import java.util.Arrays; | import java.util.Arrays; | ||||
| import java.util.List; | |||||
| import java.util.concurrent.Semaphore; | import java.util.concurrent.Semaphore; | ||||
| import java.util.concurrent.TimeUnit; | import java.util.concurrent.TimeUnit; | ||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_ANKLE; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_ELBOW; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_HIP; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_KNEE; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_SHOULDER; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.LEFT_WRIST; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_ANKLE; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_ELBOW; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_HIP; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_KNEE; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_SHOULDER; | |||||
| import static com.mindspore.posenetdemo.Posenet.BodyPart.RIGHT_WRIST; | |||||
| /** | /** | ||||
| * A simple {@link Fragment} subclass. | * A simple {@link Fragment} subclass. | ||||
| * create an instance of this fragment. | * create an instance of this fragment. | ||||
| */ | */ | ||||
| public class PoseNetFragment extends Fragment { | public class PoseNetFragment extends Fragment { | ||||
| private final List bodyJoints = Arrays.asList( | |||||
| new Pair(LEFT_WRIST, LEFT_ELBOW), new Pair(LEFT_ELBOW, LEFT_SHOULDER), | |||||
| new Pair(LEFT_SHOULDER, RIGHT_SHOULDER), new Pair(RIGHT_SHOULDER, RIGHT_ELBOW), | |||||
| new Pair(RIGHT_ELBOW, RIGHT_WRIST), new Pair(LEFT_SHOULDER, LEFT_HIP), | |||||
| new Pair(LEFT_HIP, RIGHT_HIP), new Pair(RIGHT_HIP, RIGHT_SHOULDER), | |||||
| new Pair(LEFT_HIP, LEFT_KNEE), new Pair(LEFT_KNEE, LEFT_ANKLE), | |||||
| new Pair(RIGHT_HIP, RIGHT_KNEE), new Pair(RIGHT_KNEE, RIGHT_ANKLE)); | |||||
| private static final String TAG = "PoseNetFragment"; | private static final String TAG = "PoseNetFragment"; | ||||
| private String cameraId = "1"; | |||||
| private int mCameraId = CameraCharacteristics.LENS_FACING_FRONT; // 要打开的摄像头ID | |||||
| private SurfaceView surfaceView; | private SurfaceView surfaceView; | ||||
| private CameraCaptureSession captureSession; | private CameraCaptureSession captureSession; | ||||
| private CameraDevice cameraDevice; | private CameraDevice cameraDevice; | ||||
| private Size previewSize; | |||||
| private int previewWidth; | |||||
| private int previewHeight; | |||||
| private final int PREVIEW_WIDTH = 640; | |||||
| private final int PREVIEW_HEIGHT = 480; | |||||
| private final static int PREVIEW_WIDTH = 640; | |||||
| private final static int PREVIEW_HEIGHT = 480; | |||||
| private Size previewSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT); | |||||
| private HandlerThread backgroundThread; | private HandlerThread backgroundThread; | ||||
| private Handler backgroundHandler; | private Handler backgroundHandler; | ||||
| private ImageReader imageReader; | private ImageReader imageReader; | ||||
| @@ -73,6 +102,19 @@ public class PoseNetFragment extends Fragment { | |||||
| private Semaphore cameraOpenCloseLock = new Semaphore(1);//使用信号量 Semaphore 进行多线程任务调度 | private Semaphore cameraOpenCloseLock = new Semaphore(1);//使用信号量 Semaphore 进行多线程任务调度 | ||||
| private boolean flashSupported; | private boolean flashSupported; | ||||
| /** | |||||
| * Model input shape for images. | |||||
| */ | |||||
| private final static int MODEL_WIDTH = 257; | |||||
| private final static int MODEL_HEIGHT = 257; | |||||
| private final double minConfidence = 0.5; | |||||
| private final float circleRadius = 8.0f; | |||||
| private Paint paint = new Paint(); | |||||
| private Posenet posenet; | |||||
| private int[] rgbBytes = new int[PREVIEW_WIDTH * PREVIEW_HEIGHT]; | |||||
| private byte[][] yuvBytes = new byte[3][]; | |||||
| private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { | private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { | ||||
| @Override | @Override | ||||
| @@ -87,12 +129,14 @@ public class PoseNetFragment extends Fragment { | |||||
| public void onDisconnected(@NonNull CameraDevice cameraDevice) { | public void onDisconnected(@NonNull CameraDevice cameraDevice) { | ||||
| cameraOpenCloseLock.release(); | cameraOpenCloseLock.release(); | ||||
| cameraDevice.close(); | cameraDevice.close(); | ||||
| closeCamera(); | |||||
| PoseNetFragment.this.cameraDevice = null; | PoseNetFragment.this.cameraDevice = null; | ||||
| } | } | ||||
| @Override | @Override | ||||
| public void onError(@NonNull CameraDevice cameraDevice, int error) { | public void onError(@NonNull CameraDevice cameraDevice, int error) { | ||||
| onDisconnected(cameraDevice); | onDisconnected(cameraDevice); | ||||
| closeCamera(); | |||||
| Activity activity = getActivity(); | Activity activity = getActivity(); | ||||
| if (activity != null) { | if (activity != null) { | ||||
| activity.finish(); | activity.finish(); | ||||
| @@ -112,12 +156,6 @@ public class PoseNetFragment extends Fragment { | |||||
| } | } | ||||
| }; | }; | ||||
| private CameraDataDealListener cameraDataDealListener; | |||||
| public void setCameraDataDealListener(CameraDataDealListener cameraDataDealListener) { | |||||
| this.cameraDataDealListener = cameraDataDealListener; | |||||
| } | |||||
| public static PoseNetFragment newInstance() { | public static PoseNetFragment newInstance() { | ||||
| PoseNetFragment fragment = new PoseNetFragment(); | PoseNetFragment fragment = new PoseNetFragment(); | ||||
| return fragment; | return fragment; | ||||
| @@ -148,57 +186,24 @@ public class PoseNetFragment extends Fragment { | |||||
| openCamera(); | openCamera(); | ||||
| } | } | ||||
| @Override | |||||
| public void onActivityCreated(@Nullable Bundle savedInstanceState) { | |||||
| super.onActivityCreated(savedInstanceState); | |||||
| posenet = new Posenet(getActivity()); | |||||
| } | |||||
| public void onPause() { | public void onPause() { | ||||
| this.closeCamera(); | this.closeCamera(); | ||||
| this.stopBackgroundThread(); | this.stopBackgroundThread(); | ||||
| super.onPause(); | super.onPause(); | ||||
| } | } | ||||
| public void onDestroy() { | |||||
| super.onDestroy(); | |||||
| } | |||||
| /** | |||||
| * Sets up member variables related to camera. | |||||
| */ | |||||
| private void setUpCameraOutputs() { | |||||
| CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); | |||||
| try { | |||||
| for (String cameraId : manager.getCameraIdList()) { | |||||
| CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); | |||||
| // We don't use a front facing camera in this sample. | |||||
| Integer cameraDirection = characteristics.get(CameraCharacteristics.LENS_FACING); | |||||
| if (cameraDirection != null && cameraDirection == CameraCharacteristics.LENS_FACING_FRONT) { | |||||
| continue; | |||||
| } | |||||
| previewSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT); | |||||
| imageReader = ImageReader.newInstance( | |||||
| PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| ImageFormat.YUV_420_888, /*maxImages*/ 2 | |||||
| ); | |||||
| previewHeight = previewSize.getHeight(); | |||||
| previewWidth = previewSize.getWidth(); | |||||
| // Initialize the storage bitmaps once when the resolution is known. | |||||
| // Check if the flash is supported. | |||||
| flashSupported = | |||||
| characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true; | |||||
| this.cameraId = cameraId; | |||||
| // We've found a viable camera and finished setting up member variables, | |||||
| // so we don't need to iterate through other available cameras. | |||||
| } | |||||
| } catch (CameraAccessException e) { | |||||
| e.printStackTrace(); | |||||
| } catch (NullPointerException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| public void switchCamera() { | |||||
| mCameraId ^= 1; | |||||
| Log.d(TAG, "switchCamera: mCameraId: " + mCameraId); | |||||
| closeCamera(); | |||||
| openCamera(); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -206,14 +211,23 @@ public class PoseNetFragment extends Fragment { | |||||
| */ | */ | ||||
| @SuppressLint("MissingPermission") | @SuppressLint("MissingPermission") | ||||
| private void openCamera() { | private void openCamera() { | ||||
| setUpCameraOutputs(); | |||||
| CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); | CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); | ||||
| try { | try { | ||||
| CameraCharacteristics characteristics = manager.getCameraCharacteristics(Integer.toString(mCameraId)); | |||||
| previewSize = new Size(PREVIEW_WIDTH, PREVIEW_HEIGHT); | |||||
| imageReader = ImageReader.newInstance( | |||||
| PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| ImageFormat.YUV_420_888, /*maxImages*/ 2 | |||||
| ); | |||||
| imageReader.setOnImageAvailableListener(imageAvailableListener, backgroundHandler); | |||||
| // Check if the flash is supported. | |||||
| flashSupported = | |||||
| characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) == true; | |||||
| // Wait for camera to open - 2.5 seconds is sufficient | // Wait for camera to open - 2.5 seconds is sufficient | ||||
| if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { | if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { | ||||
| throw new RuntimeException("Time out waiting to lock camera opening."); | throw new RuntimeException("Time out waiting to lock camera opening."); | ||||
| } | } | ||||
| manager.openCamera(cameraId, mStateCallback, backgroundHandler); | |||||
| manager.openCamera(Integer.toString(mCameraId), mStateCallback, backgroundHandler); | |||||
| } catch (CameraAccessException e) { | } catch (CameraAccessException e) { | ||||
| e.printStackTrace(); | e.printStackTrace(); | ||||
| } catch (InterruptedException e) { | } catch (InterruptedException e) { | ||||
| @@ -266,31 +280,48 @@ public class PoseNetFragment extends Fragment { | |||||
| } | } | ||||
| } | } | ||||
| private final ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() { | private final ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() { | ||||
| @Override | @Override | ||||
| public void onImageAvailable(ImageReader imageReader) { | public void onImageAvailable(ImageReader imageReader) { | ||||
| if (previewWidth != 0 && previewHeight != 0 && imageReader != null) { | |||||
| if (imageReader != null) { | |||||
| Image image = imageReader.acquireLatestImage(); | Image image = imageReader.acquireLatestImage(); | ||||
| if (cameraDataDealListener != null) { | |||||
| cameraDataDealListener.dataDeal(image, surfaceView); | |||||
| if (image == null || image.getPlanes() == null) { | |||||
| return; | |||||
| } | |||||
| fillBytes(image.getPlanes(), yuvBytes); | |||||
| ImageUtils.convertYUV420ToARGB8888(yuvBytes[0], yuvBytes[1], yuvBytes[2], | |||||
| PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| image.getPlanes()[0].getRowStride(), | |||||
| image.getPlanes()[1].getRowStride(), | |||||
| image.getPlanes()[1].getPixelStride(), | |||||
| rgbBytes); | |||||
| Bitmap imageBitmap = Bitmap.createBitmap( | |||||
| rgbBytes, PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| Bitmap.Config.ARGB_8888); | |||||
| Matrix rotateMatrix = new Matrix(); | |||||
| if (mCameraId == CameraCharacteristics.LENS_FACING_FRONT) { | |||||
| rotateMatrix.postRotate(90.0f); | |||||
| } else if (mCameraId == CameraCharacteristics.LENS_FACING_BACK) { | |||||
| rotateMatrix.postRotate(270.0f); | |||||
| rotateMatrix.postScale(-1.0f, 1.0f); | |||||
| } | } | ||||
| Bitmap rotatedBitmap = Bitmap.createBitmap( | |||||
| imageBitmap, 0, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT, | |||||
| rotateMatrix, true | |||||
| ); | |||||
| processImage(rotatedBitmap); | |||||
| image.close(); | |||||
| } | } | ||||
| } | } | ||||
| }; | }; | ||||
| /** | /** | ||||
| * Creates a new [CameraCaptureSession] for camera preview. | * Creates a new [CameraCaptureSession] for camera preview. | ||||
| */ | */ | ||||
| private void createCameraPreviewSession() { | private void createCameraPreviewSession() { | ||||
| try { | try { | ||||
| // We capture images from preview in YUV format. | |||||
| imageReader = ImageReader.newInstance( | |||||
| previewSize.getWidth(), previewSize.getHeight(), ImageFormat.YUV_420_888, 2); | |||||
| imageReader.setOnImageAvailableListener(imageAvailableListener, backgroundHandler); | |||||
| // This is the surface we need to record images for processing. | // This is the surface we need to record images for processing. | ||||
| Surface recordingSurface = imageReader.getSurface(); | Surface recordingSurface = imageReader.getSurface(); | ||||
| @@ -348,4 +379,136 @@ public class PoseNetFragment extends Fragment { | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Fill the yuvBytes with data from image planes. | |||||
| */ | |||||
| private void fillBytes(Image.Plane[] planes, byte[][] yuvBytes) { | |||||
| // Row stride is the total number of bytes occupied in memory by a row of an image. | |||||
| // Because of the variable row stride it's not possible to know in | |||||
| // advance the actual necessary dimensions of the yuv planes | |||||
| for (int i = 0; i < planes.length; ++i) { | |||||
| ByteBuffer buffer = planes[i].getBuffer(); | |||||
| if (yuvBytes[i] == null) { | |||||
| yuvBytes[i] = new byte[buffer.capacity()]; | |||||
| } | |||||
| buffer.get(yuvBytes[i]); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Crop Bitmap to maintain aspect ratio of model input. | |||||
| */ | |||||
| private Bitmap cropBitmap(Bitmap bitmap) { | |||||
| float bitmapRatio = bitmap.getHeight() / bitmap.getWidth(); | |||||
| float modelInputRatio = MODEL_HEIGHT / MODEL_WIDTH; | |||||
| double maxDifference = 1.0E-5D; | |||||
| float cropHeight = modelInputRatio - bitmapRatio; | |||||
| if (Math.abs(cropHeight) < maxDifference) { | |||||
| return bitmap; | |||||
| } else { | |||||
| Bitmap croppedBitmap; | |||||
| if (modelInputRatio < bitmapRatio) { | |||||
| cropHeight = (float) bitmap.getHeight() - (float) bitmap.getWidth() / modelInputRatio; | |||||
| croppedBitmap = Bitmap.createBitmap(bitmap, | |||||
| 0, (int) (cropHeight / 2), bitmap.getWidth(), (int) (bitmap.getHeight() - cropHeight)); | |||||
| } else { | |||||
| cropHeight = (float) bitmap.getWidth() - (float) bitmap.getHeight() * modelInputRatio; | |||||
| croppedBitmap = Bitmap.createBitmap(bitmap, | |||||
| (int) (cropHeight / 2), 0, (int) (bitmap.getWidth() - cropHeight), bitmap.getHeight()); | |||||
| } | |||||
| return croppedBitmap; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Set the paint color and size. | |||||
| */ | |||||
| private void setPaint() { | |||||
| paint.setColor(getResources().getColor(R.color.text_blue)); | |||||
| paint.setTextSize(80.0f); | |||||
| paint.setStrokeWidth(8.0f); | |||||
| } | |||||
| /** | |||||
| * Draw bitmap on Canvas. | |||||
| */ | |||||
| private void draw(Canvas canvas, Posenet.Person person, Bitmap bitmap) { | |||||
| canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); | |||||
| // Draw `bitmap` and `person` in square canvas. | |||||
| int screenWidth, screenHeight; | |||||
| int left, right, top, bottom; | |||||
| if (canvas.getHeight() > canvas.getWidth()) { | |||||
| screenWidth = canvas.getWidth(); | |||||
| screenHeight = canvas.getWidth(); | |||||
| left = 0; | |||||
| top = (canvas.getHeight() - canvas.getWidth()) / 2; | |||||
| } else { | |||||
| screenWidth = canvas.getHeight(); | |||||
| screenHeight = canvas.getHeight(); | |||||
| left = (canvas.getWidth() - canvas.getHeight()) / 2; | |||||
| top = 0; | |||||
| } | |||||
| right = left + screenWidth; | |||||
| bottom = top + screenHeight; | |||||
| setPaint(); | |||||
| canvas.drawBitmap( | |||||
| bitmap, | |||||
| new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), | |||||
| new Rect(left, top, right, bottom), paint); | |||||
| float widthRatio = (float) screenWidth / MODEL_WIDTH; | |||||
| float heightRatio = (float) screenHeight / MODEL_HEIGHT; | |||||
| for (Posenet.KeyPoint keyPoint : person.keyPoints) { | |||||
| if (keyPoint.score > minConfidence) { | |||||
| Posenet.Position position = keyPoint.position; | |||||
| float adjustedX = position.x * widthRatio + left; | |||||
| float adjustedY = position.y * heightRatio + top; | |||||
| canvas.drawCircle(adjustedX, adjustedY, circleRadius, paint); | |||||
| } | |||||
| } | |||||
| for (int i = 0; i < bodyJoints.size(); i++) { | |||||
| Pair line = (Pair) bodyJoints.get(i); | |||||
| Posenet.BodyPart first = (Posenet.BodyPart) line.first; | |||||
| Posenet.BodyPart second = (Posenet.BodyPart) line.second; | |||||
| if (person.keyPoints.get(first.ordinal()).score > minConfidence & | |||||
| person.keyPoints.get(second.ordinal()).score > minConfidence) { | |||||
| canvas.drawLine( | |||||
| person.keyPoints.get(first.ordinal()).position.x * widthRatio + left, | |||||
| person.keyPoints.get(first.ordinal()).position.y * heightRatio + top, | |||||
| person.keyPoints.get(second.ordinal()).position.x * widthRatio + left, | |||||
| person.keyPoints.get(second.ordinal()).position.y * heightRatio + top, paint); | |||||
| } | |||||
| } | |||||
| canvas.drawText(String.format("Score: %.2f", person.score), | |||||
| (15.0f * widthRatio), (30.0f * heightRatio + bottom), paint); | |||||
| canvas.drawText(String.format("Time: %.2f ms", posenet.lastInferenceTimeNanos * 1.0f / 1_000_000), | |||||
| (15.0f * widthRatio), (50.0f * heightRatio + bottom), paint | |||||
| ); | |||||
| // Draw! | |||||
| surfaceView.getHolder().unlockCanvasAndPost(canvas); | |||||
| } | |||||
| /** | |||||
| * Process image using Posenet library. | |||||
| */ | |||||
| private void processImage(Bitmap bitmap) { | |||||
| // Crop bitmap. | |||||
| Bitmap croppedBitmap = cropBitmap(bitmap); | |||||
| // Created scaled version of bitmap for model input. | |||||
| Bitmap scaledBitmap = Bitmap.createScaledBitmap(croppedBitmap, MODEL_WIDTH, MODEL_HEIGHT, true); | |||||
| // Perform inference. | |||||
| Posenet.Person person = posenet.estimateSinglePose(scaledBitmap); | |||||
| Canvas canvas = surfaceView.getHolder().lockCanvas(); | |||||
| draw(canvas, person, scaledBitmap); | |||||
| } | |||||
| } | } | ||||
| @@ -19,7 +19,7 @@ | |||||
| android:background="#66000000"> | android:background="#66000000"> | ||||
| <TextView | <TextView | ||||
| android:layout_width="match_parent" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | android:layout_height="wrap_content" | ||||
| android:drawableStart="@drawable/logo2" | android:drawableStart="@drawable/logo2" | ||||
| android:drawablePadding="5dp" | android:drawablePadding="5dp" | ||||
| @@ -27,5 +27,14 @@ | |||||
| android:text="MindSpore PoseNet" | android:text="MindSpore PoseNet" | ||||
| android:textColor="#ffffff" | android:textColor="#ffffff" | ||||
| android:textSize="20sp" /> | android:textSize="20sp" /> | ||||
| <ImageView | |||||
| android:onClick="onClickSwitch" | |||||
| android:clickable="true" | |||||
| android:layout_marginEnd="10dp" | |||||
| android:layout_gravity="center_vertical|right" | |||||
| android:layout_width="45dp" | |||||
| android:layout_height="45dp" | |||||
| android:src="@drawable/ic_camera_switch"/> | |||||
| </androidx.appcompat.widget.Toolbar> | </androidx.appcompat.widget.Toolbar> | ||||
| </RelativeLayout> | </RelativeLayout> | ||||
| @@ -1,5 +1,6 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| xmlns:tools="http://schemas.android.com/tools" | |||||
| package="com.mindspore.styletransferdemo"> | package="com.mindspore.styletransferdemo"> | ||||
| <uses-permission android:name="android.permission.CAMERA" /> | <uses-permission android:name="android.permission.CAMERA" /> | ||||
| @@ -34,6 +35,18 @@ | |||||
| <category android:name="android.intent.category.LAUNCHER" /> | <category android:name="android.intent.category.LAUNCHER" /> | ||||
| </intent-filter> | </intent-filter> | ||||
| </activity> | </activity> | ||||
| <provider | |||||
| android:name="androidx.core.content.FileProvider" | |||||
| android:authorities="com.mindspore.styletransferdemo.fileprovider" | |||||
| android:exported="false" | |||||
| tools:replace="android:authorities" | |||||
| android:grantUriPermissions="true"> | |||||
| <meta-data | |||||
| tools:replace="android:resource" | |||||
| android:name="android.support.FILE_PROVIDER_PATHS" | |||||
| android:resource="@xml/style_file_paths" /> | |||||
| </provider> | |||||
| </application> | </application> | ||||
| </manifest> | </manifest> | ||||
| @@ -89,7 +89,7 @@ public class BitmapUtils { | |||||
| } | } | ||||
| // Scale pictures to screen width. | // Scale pictures to screen width. | ||||
| private static Bitmap zoomImage(Bitmap imageBitmap, int targetWidth, int maxHeight) { | |||||
| public static Bitmap zoomImage(Bitmap imageBitmap, int targetWidth, int maxHeight) { | |||||
| float scaleFactor = | float scaleFactor = | ||||
| Math.max( | Math.max( | ||||
| (float) imageBitmap.getWidth() / (float) targetWidth, | (float) imageBitmap.getWidth() / (float) targetWidth, | ||||
| @@ -104,6 +104,26 @@ public class BitmapUtils { | |||||
| return resizedBitmap; | return resizedBitmap; | ||||
| } | } | ||||
| public static Bitmap changeBitmapSize(Bitmap bitmap, int targetWidth, int targetHeight) { | |||||
| int width = bitmap.getWidth(); | |||||
| int height = bitmap.getHeight(); | |||||
| Log.e("width", "width:" + width); | |||||
| Log.e("height", "height:" + height); | |||||
| float scaleWidth = ((float) targetWidth) / width; | |||||
| float scaleHeight = ((float) targetHeight) / height; | |||||
| Matrix matrix = new Matrix(); | |||||
| matrix.postScale(scaleWidth, scaleHeight); | |||||
| bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); | |||||
| bitmap.getWidth(); | |||||
| bitmap.getHeight(); | |||||
| Log.e("newWidth", "newWidth" + bitmap.getWidth()); | |||||
| Log.e("newHeight", "newHeight" + bitmap.getHeight()); | |||||
| return bitmap; | |||||
| } | |||||
| /** | /** | ||||
| * Get the rotation angle of the photo. | * Get the rotation angle of the photo. | ||||
| * | * | ||||
| @@ -16,16 +16,24 @@ | |||||
| package com.mindspore.styletransferdemo; | package com.mindspore.styletransferdemo; | ||||
| import android.content.Context; | import android.content.Context; | ||||
| import android.content.Intent; | |||||
| import android.graphics.Bitmap; | import android.graphics.Bitmap; | ||||
| import android.graphics.BitmapFactory; | import android.graphics.BitmapFactory; | ||||
| import android.graphics.Color; | import android.graphics.Color; | ||||
| import android.graphics.Matrix; | import android.graphics.Matrix; | ||||
| import android.graphics.RectF; | import android.graphics.RectF; | ||||
| import android.media.MediaScannerConnection; | |||||
| import android.net.Uri; | |||||
| import android.os.Build; | |||||
| import android.os.Environment; | |||||
| import android.util.Log; | |||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||
| import androidx.exifinterface.media.ExifInterface; | import androidx.exifinterface.media.ExifInterface; | ||||
| import java.io.File; | import java.io.File; | ||||
| import java.io.FileNotFoundException; | |||||
| import java.io.FileOutputStream; | |||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.io.InputStream; | import java.io.InputStream; | ||||
| import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||
| @@ -33,6 +41,8 @@ import java.nio.ByteOrder; | |||||
| public class ImageUtils { | public class ImageUtils { | ||||
| private static final String TAG = "ImageUtils"; | |||||
| private static Matrix decodeExifOrientation(int orientation) { | private static Matrix decodeExifOrientation(int orientation) { | ||||
| Matrix matrix = new Matrix(); | Matrix matrix = new Matrix(); | ||||
| @@ -198,4 +208,59 @@ public class ImageUtils { | |||||
| } | } | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| // Save the picture to the system album and refresh it. | |||||
| public static void saveToAlbum(final Context context, Bitmap bitmap) { | |||||
| File file = null; | |||||
| String fileName = System.currentTimeMillis() + ".jpg"; | |||||
| File root = new File(Environment.getExternalStorageDirectory().getAbsoluteFile(), context.getPackageName()); | |||||
| File dir = new File(root, "image"); | |||||
| if (dir.mkdirs() || dir.isDirectory()) { | |||||
| file = new File(dir, fileName); | |||||
| } | |||||
| FileOutputStream os = null; | |||||
| try { | |||||
| os = new FileOutputStream(file); | |||||
| bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os); | |||||
| os.flush(); | |||||
| } catch (FileNotFoundException e) { | |||||
| Log.e(TAG, e.getMessage()); | |||||
| } catch (IOException e) { | |||||
| Log.e(TAG, e.getMessage()); | |||||
| } finally { | |||||
| try { | |||||
| if (os != null) { | |||||
| os.close(); | |||||
| } | |||||
| } catch (IOException e) { | |||||
| Log.e(TAG, e.getMessage()); | |||||
| } | |||||
| } | |||||
| if (file == null) { | |||||
| return; | |||||
| } | |||||
| // Gallery refresh. | |||||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { | |||||
| String path = null; | |||||
| try { | |||||
| path = file.getCanonicalPath(); | |||||
| } catch (IOException e) { | |||||
| Log.e(TAG, e.getMessage()); | |||||
| } | |||||
| MediaScannerConnection.scanFile(context, new String[]{path}, null, | |||||
| new MediaScannerConnection.OnScanCompletedListener() { | |||||
| @Override | |||||
| public void onScanCompleted(String path, Uri uri) { | |||||
| Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); | |||||
| mediaScanIntent.setData(uri); | |||||
| context.sendBroadcast(mediaScanIntent); | |||||
| } | |||||
| }); | |||||
| } else { | |||||
| String relationDir = file.getParent(); | |||||
| File file1 = new File(relationDir); | |||||
| context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(file1.getAbsoluteFile()))); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -16,50 +16,74 @@ | |||||
| package com.mindspore.styletransferdemo; | package com.mindspore.styletransferdemo; | ||||
| import android.Manifest; | import android.Manifest; | ||||
| import android.content.DialogInterface; | |||||
| import android.content.Intent; | import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | |||||
| import android.content.res.Configuration; | import android.content.res.Configuration; | ||||
| import android.graphics.Bitmap; | import android.graphics.Bitmap; | ||||
| import android.graphics.BitmapFactory; | |||||
| import android.net.Uri; | import android.net.Uri; | ||||
| import android.os.Build; | |||||
| import android.os.Bundle; | import android.os.Bundle; | ||||
| import android.os.Environment; | |||||
| import android.provider.MediaStore; | import android.provider.MediaStore; | ||||
| import android.text.TextUtils; | |||||
| import android.provider.Settings; | |||||
| import android.util.Log; | import android.util.Log; | ||||
| import android.util.Pair; | import android.util.Pair; | ||||
| import android.view.View; | import android.view.View; | ||||
| import android.widget.ImageView; | import android.widget.ImageView; | ||||
| import android.widget.ProgressBar; | import android.widget.ProgressBar; | ||||
| import android.widget.TextView; | |||||
| import android.widget.Toast; | import android.widget.Toast; | ||||
| import androidx.annotation.NonNull; | import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | import androidx.annotation.Nullable; | ||||
| import androidx.appcompat.app.AlertDialog; | |||||
| import androidx.appcompat.app.AppCompatActivity; | import androidx.appcompat.app.AppCompatActivity; | ||||
| import androidx.core.app.ActivityCompat; | import androidx.core.app.ActivityCompat; | ||||
| import androidx.core.content.ContextCompat; | |||||
| import androidx.core.content.FileProvider; | |||||
| import androidx.recyclerview.widget.GridLayoutManager; | |||||
| import androidx.recyclerview.widget.RecyclerView; | |||||
| import com.bumptech.glide.Glide; | import com.bumptech.glide.Glide; | ||||
| public class MainActivity extends AppCompatActivity implements View.OnClickListener, StyleFragment.OnListFragmentInteractionListener { | |||||
| import java.io.File; | |||||
| import java.io.FileNotFoundException; | |||||
| public class MainActivity extends AppCompatActivity implements OnBackgroundImageListener { | |||||
| private static final String TAG = "MainActivity"; | private static final String TAG = "MainActivity"; | ||||
| private static final int[] IMAGES = {R.drawable.style0, R.drawable.style1, R.drawable.style2, R.drawable.style3, R.drawable.style4, | |||||
| R.drawable.style5, R.drawable.style6, R.drawable.style7, R.drawable.style8, R.drawable.style9, | |||||
| R.drawable.style10, R.drawable.style11, R.drawable.style12, R.drawable.style13, R.drawable.style14, | |||||
| R.drawable.style15, R.drawable.style16, R.drawable.style17, R.drawable.style18, R.drawable.style19, R.drawable.add}; | |||||
| private static final String[] PERMISSIONS = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, | |||||
| Manifest.permission.READ_PHONE_STATE, Manifest.permission.CAMERA}; | |||||
| private static final int REQUEST_PERMISSION = 1; | private static final int REQUEST_PERMISSION = 1; | ||||
| private static final int RC_CHOOSE_PHOTO = 2; | private static final int RC_CHOOSE_PHOTO = 2; | ||||
| private static final int RC_CHOOSE_PHOTO_FOR_BACKGROUND = 3; | |||||
| private static final int RC_CHOOSE_CAMERA = 4; | |||||
| private boolean isAllGranted; | |||||
| private StyleTransferModelExecutor transferModelExecutor; | private StyleTransferModelExecutor transferModelExecutor; | ||||
| private boolean isHasPermssion; | |||||
| private boolean isRunningModel; | private boolean isRunningModel; | ||||
| private ImageView imgOrigin, imgStyle, imgResult; | |||||
| private ProgressBar progressResult; | |||||
| private ImageView imgPreview; | |||||
| private Uri imageUri; | private Uri imageUri; | ||||
| private TextView textOriginImage; | |||||
| private ProgressBar progressBar; | |||||
| private RecyclerView recyclerView; | |||||
| private Integer maxWidthOfImage; | private Integer maxWidthOfImage; | ||||
| private Integer maxHeightOfImage; | private Integer maxHeightOfImage; | ||||
| private boolean isLandScape; | private boolean isLandScape; | ||||
| private Bitmap originBitmap, styleBitmap; | |||||
| private StyleFragment styleFragment; | |||||
| private String selectedStyle; | |||||
| private Bitmap originBitmap, styleBitmap, resultBitmap; | |||||
| @Override | @Override | ||||
| protected void onCreate(Bundle savedInstanceState) { | protected void onCreate(Bundle savedInstanceState) { | ||||
| @@ -71,23 +95,35 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||||
| } | } | ||||
| private void init() { | private void init() { | ||||
| imgOrigin = findViewById(R.id.img_origin); | |||||
| imgStyle = findViewById(R.id.img_style); | |||||
| imgResult = findViewById(R.id.img_result); | |||||
| progressResult = findViewById(R.id.progress_circular); | |||||
| imgOrigin.setOnClickListener(this); | |||||
| imgStyle.setOnClickListener(this); | |||||
| imgResult.setOnClickListener(this); | |||||
| imgPreview = findViewById(R.id.img_origin); | |||||
| textOriginImage = findViewById(R.id.tv_image); | |||||
| progressBar = findViewById(R.id.progress); | |||||
| recyclerView = findViewById(R.id.recyclerview); | |||||
| styleFragment = StyleFragment.newInstance(); | |||||
| recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); | |||||
| recyclerView.setAdapter(new StyleRecyclerViewAdapter(this, IMAGES, this)); | |||||
| transferModelExecutor = new StyleTransferModelExecutor(this, false); | transferModelExecutor = new StyleTransferModelExecutor(this, false); | ||||
| } | } | ||||
| private void requestPermissions() { | private void requestPermissions() { | ||||
| ActivityCompat.requestPermissions(this, | |||||
| new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, | |||||
| Manifest.permission.READ_PHONE_STATE, Manifest.permission.CAMERA}, REQUEST_PERMISSION); | |||||
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | |||||
| isAllGranted = checkPermissionAllGranted(PERMISSIONS); | |||||
| if (!isAllGranted) { | |||||
| ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_PERMISSION); | |||||
| } | |||||
| } else { | |||||
| isAllGranted = true; | |||||
| } | |||||
| } | |||||
| private boolean checkPermissionAllGranted(String[] permissions) { | |||||
| for (String permission : permissions) { | |||||
| if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -96,43 +132,115 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||||
| @Override | @Override | ||||
| public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||||
| if (REQUEST_PERMISSION == requestCode) { | if (REQUEST_PERMISSION == requestCode) { | ||||
| isHasPermssion = true; | |||||
| isAllGranted = true; | |||||
| for (int grant : grantResults) { | |||||
| if (grant != PackageManager.PERMISSION_GRANTED) { | |||||
| isAllGranted = false; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (!isAllGranted) { | |||||
| openAppDetails(); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @Override | |||||
| public void onClick(View view) { | |||||
| if (view.getId() == R.id.img_origin) { | |||||
| if (isHasPermssion) { | |||||
| openGallay(); | |||||
| } else { | |||||
| requestPermissions(); | |||||
| } | |||||
| } else if (view.getId() == R.id.img_style) { | |||||
| if (!isRunningModel) { | |||||
| styleFragment.show(getSupportFragmentManager(), TAG); | |||||
| private void openAppDetails() { | |||||
| AlertDialog.Builder builder = new AlertDialog.Builder(this); | |||||
| builder.setMessage("HiMindSpore需要访问 “相机” 和 “外部存储器”,请到 “应用信息 -> 权限” 中授予!"); | |||||
| builder.setPositiveButton("去手动授权", new DialogInterface.OnClickListener() { | |||||
| @Override | |||||
| public void onClick(DialogInterface dialog, int which) { | |||||
| Intent intent = new Intent(); | |||||
| intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); | |||||
| intent.addCategory(Intent.CATEGORY_DEFAULT); | |||||
| intent.setData(Uri.parse("package:" + getPackageName())); | |||||
| intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | |||||
| intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); | |||||
| intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); | |||||
| startActivity(intent); | |||||
| } | } | ||||
| }); | |||||
| builder.setNegativeButton("取消", null); | |||||
| builder.show(); | |||||
| } | |||||
| public void onClickPhoto(View view) { | |||||
| if (isAllGranted) { | |||||
| openGallay(RC_CHOOSE_PHOTO); | |||||
| textOriginImage.setVisibility(View.GONE); | |||||
| } else { | |||||
| requestPermissions(); | |||||
| } | |||||
| } | |||||
| public void onClickCamera(View view) { | |||||
| if (isAllGranted) { | |||||
| openCamera(); | |||||
| textOriginImage.setVisibility(View.GONE); | |||||
| } else { | |||||
| requestPermissions(); | |||||
| } | |||||
| } | |||||
| public void onClickRecovery(View view) { | |||||
| if (originBitmap != null) { | |||||
| Glide.with(this).load(originBitmap).into(imgPreview); | |||||
| } else { | |||||
| Toast.makeText(this, "Please select an original picture first", Toast.LENGTH_SHORT).show(); | |||||
| } | } | ||||
| } | } | ||||
| private void openGallay() { | |||||
| public void onClickSave(View view) { | |||||
| if (this.resultBitmap == null) { | |||||
| Log.e(TAG, "null processed image"); | |||||
| Toast.makeText(this.getApplicationContext(), R.string.no_pic_neededSave, Toast.LENGTH_SHORT).show(); | |||||
| } else { | |||||
| ImageUtils.saveToAlbum(getApplicationContext(), this.resultBitmap); | |||||
| Toast.makeText(this.getApplicationContext(), R.string.save_success, Toast.LENGTH_SHORT).show(); | |||||
| } | |||||
| } | |||||
| private void openGallay(int request) { | |||||
| Intent intentToPickPic = new Intent(Intent.ACTION_PICK, null); | Intent intentToPickPic = new Intent(Intent.ACTION_PICK, null); | ||||
| intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); | intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); | ||||
| startActivityForResult(intentToPickPic, RC_CHOOSE_PHOTO); | |||||
| startActivityForResult(intentToPickPic, request); | |||||
| } | |||||
| private void openCamera() { | |||||
| if (isAllGranted) { | |||||
| Intent intentToTakePhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); | |||||
| String mTempPhotoPath = Environment.getExternalStorageDirectory() + File.separator + "photo.jpeg"; | |||||
| imageUri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".fileprovider", new File(mTempPhotoPath)); | |||||
| intentToTakePhoto.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); | |||||
| startActivityForResult(intentToTakePhoto, RC_CHOOSE_CAMERA); | |||||
| } else { | |||||
| requestPermissions(); | |||||
| } | |||||
| } | } | ||||
| @Override | @Override | ||||
| protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { | ||||
| super.onActivityResult(requestCode, resultCode, data); | super.onActivityResult(requestCode, resultCode, data); | ||||
| if (RC_CHOOSE_PHOTO == requestCode && null != data && null != data.getData()) { | |||||
| if (data != null) { | |||||
| this.imageUri = data.getData(); | |||||
| showOriginImage(); | |||||
| if (resultCode == RESULT_OK) { | |||||
| if (RC_CHOOSE_PHOTO == requestCode) { | |||||
| if (null != data && null != data.getData()) { | |||||
| this.imageUri = data.getData(); | |||||
| showOriginImage(); | |||||
| } else { | |||||
| finish(); | |||||
| } | |||||
| } else if (RC_CHOOSE_PHOTO_FOR_BACKGROUND == requestCode) { | |||||
| if (null != data && null != data.getData()) { | |||||
| showCustomBack(data.getData()); | |||||
| } else { | |||||
| finish(); | |||||
| } | |||||
| } else if (RC_CHOOSE_CAMERA == requestCode) { | |||||
| showOriginCamera(); | |||||
| } | } | ||||
| } else { | |||||
| finish(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -140,56 +248,81 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||||
| Pair<Integer, Integer> targetedSize = this.getTargetSize(); | Pair<Integer, Integer> targetedSize = this.getTargetSize(); | ||||
| int targetWidth = targetedSize.first; | int targetWidth = targetedSize.first; | ||||
| int maxHeight = targetedSize.second; | int maxHeight = targetedSize.second; | ||||
| originBitmap = BitmapUtils.loadFromPath(MainActivity.this, imageUri, targetWidth, maxHeight); | |||||
| originBitmap = BitmapUtils.loadFromPath(this, imageUri, targetWidth, maxHeight); | |||||
| // Determine how much to scale down the image. | // Determine how much to scale down the image. | ||||
| Log.i(TAG, "resized image size width:" + originBitmap.getWidth() + ",height: " + originBitmap.getHeight()); | |||||
| Log.e(TAG, "resized image size width:" + originBitmap.getWidth() + ",height: " + originBitmap.getHeight()); | |||||
| if (originBitmap != null) { | |||||
| Glide.with(this).load(originBitmap).into(imgPreview); | |||||
| } | |||||
| } | |||||
| private void showOriginCamera() { | |||||
| try { | |||||
| Pair<Integer, Integer> targetedSize = this.getTargetSize(); | |||||
| int targetWidth = targetedSize.first; | |||||
| int maxHeight = targetedSize.second; | |||||
| Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri)); | |||||
| originBitmap = BitmapUtils.zoomImage(bitmap, targetWidth, maxHeight); | |||||
| } catch (FileNotFoundException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| // Determine how much to scale down the image. | |||||
| Log.e(TAG, "resized image size width:" + originBitmap.getWidth() + ",height: " + originBitmap.getHeight()); | |||||
| if (originBitmap != null) { | if (originBitmap != null) { | ||||
| Glide.with(this).load(originBitmap).into(imgOrigin); | |||||
| Glide.with(this).load(originBitmap).into(imgPreview); | |||||
| } | } | ||||
| } | } | ||||
| private void showCustomBack(Uri imageUri) { | |||||
| Pair<Integer, Integer> targetedSize = this.getTargetSize(); | |||||
| int targetWidth = targetedSize.first; | |||||
| int maxHeight = targetedSize.second; | |||||
| styleBitmap = BitmapUtils.loadFromPath(this, imageUri, targetWidth, maxHeight); | |||||
| startRunningModel(styleBitmap); | |||||
| } | |||||
| @Override | |||||
| public void onBackImageSelected(int position) { | |||||
| styleBitmap = BitmapFactory.decodeResource(getResources(), IMAGES[position]); | |||||
| startRunningModel(styleBitmap); | |||||
| } | |||||
| @Override | @Override | ||||
| public void onListFragmentInteraction(String item) { | |||||
| this.selectedStyle = item; | |||||
| styleFragment.dismiss(); | |||||
| startRunningModel(); | |||||
| } | |||||
| private void startRunningModel() { | |||||
| if (!isRunningModel && !TextUtils.isEmpty(selectedStyle)) { | |||||
| styleBitmap = ImageUtils.loadBitmapFromResources(this, getUriFromAssetThumb(selectedStyle)); | |||||
| Glide.with(this) | |||||
| .load(styleBitmap) | |||||
| .into(imgStyle); | |||||
| if (originBitmap == null) { | |||||
| Toast.makeText(this, "Please select an original picture first", Toast.LENGTH_SHORT).show(); | |||||
| return; | |||||
| } | |||||
| progressResult.setVisibility(View.VISIBLE); | |||||
| public void onImageAdd(View view) { | |||||
| openGallay(RC_CHOOSE_PHOTO_FOR_BACKGROUND); | |||||
| } | |||||
| private void startRunningModel(Bitmap styleBitmap) { | |||||
| if (originBitmap == null) { | |||||
| Toast.makeText(this, "Please select an original picture first", Toast.LENGTH_SHORT).show(); | |||||
| return; | |||||
| } | |||||
| if (!isRunningModel) { | |||||
| isRunningModel = true; | isRunningModel = true; | ||||
| progressBar.setVisibility(View.VISIBLE); | |||||
| ModelExecutionResult result = transferModelExecutor.execute(originBitmap, styleBitmap); | ModelExecutionResult result = transferModelExecutor.execute(originBitmap, styleBitmap); | ||||
| Glide.with(this).load(result.getStyledImage()).into(imgResult); | |||||
| progressResult.setVisibility(View.GONE); | |||||
| if (null != result && null != result.getStyledImage()) { | |||||
| resultBitmap = BitmapUtils.changeBitmapSize(result.getStyledImage(), originBitmap.getWidth(), originBitmap.getHeight()); | |||||
| Log.e("AAA", "w>>" + resultBitmap.getWidth() + ">>>h>>" + resultBitmap.getHeight()); | |||||
| Glide.with(this).load(resultBitmap).override(resultBitmap.getWidth(), resultBitmap.getHeight()).into(imgPreview); | |||||
| } else { | |||||
| Toast.makeText(this, "ModelExecute failed", Toast.LENGTH_SHORT).show(); | |||||
| } | |||||
| isRunningModel = false; | isRunningModel = false; | ||||
| progressBar.setVisibility(View.INVISIBLE); | |||||
| } else { | } else { | ||||
| Toast.makeText(this, "Previous Model still running", Toast.LENGTH_SHORT).show(); | Toast.makeText(this, "Previous Model still running", Toast.LENGTH_SHORT).show(); | ||||
| } | } | ||||
| } | } | ||||
| private String getUriFromAssetThumb(String thumb) { | |||||
| return "thumbnails/" + thumb; | |||||
| } | |||||
| // Returns max width of image. | // Returns max width of image. | ||||
| private Integer getMaxWidthOfImage() { | private Integer getMaxWidthOfImage() { | ||||
| if (this.maxWidthOfImage == null) { | if (this.maxWidthOfImage == null) { | ||||
| if (this.isLandScape) { | if (this.isLandScape) { | ||||
| this.maxWidthOfImage = ((View) this.imgOrigin.getParent()).getHeight(); | |||||
| this.maxWidthOfImage = ((View) this.imgPreview.getParent()).getHeight(); | |||||
| } else { | } else { | ||||
| this.maxWidthOfImage = ((View) this.imgOrigin.getParent()).getWidth(); | |||||
| this.maxWidthOfImage = ((View) this.imgPreview.getParent()).getWidth(); | |||||
| } | } | ||||
| } | } | ||||
| return this.maxWidthOfImage; | return this.maxWidthOfImage; | ||||
| @@ -199,9 +332,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||||
| private Integer getMaxHeightOfImage() { | private Integer getMaxHeightOfImage() { | ||||
| if (this.maxHeightOfImage == null) { | if (this.maxHeightOfImage == null) { | ||||
| if (this.isLandScape) { | if (this.isLandScape) { | ||||
| this.maxHeightOfImage = ((View) this.imgOrigin.getParent()).getWidth(); | |||||
| this.maxHeightOfImage = ((View) this.imgPreview.getParent()).getWidth(); | |||||
| } else { | } else { | ||||
| this.maxHeightOfImage = ((View) this.imgOrigin.getParent()).getHeight(); | |||||
| this.maxHeightOfImage = ((View) this.imgPreview.getParent()).getHeight(); | |||||
| } | } | ||||
| } | } | ||||
| return this.maxHeightOfImage; | return this.maxHeightOfImage; | ||||
| @@ -219,5 +352,4 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||||
| return new Pair<>(targetWidth, targetHeight); | return new Pair<>(targetWidth, targetHeight); | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,9 @@ | |||||
| package com.mindspore.styletransferdemo; | |||||
| import android.view.View; | |||||
| public interface OnBackgroundImageListener { | |||||
| void onBackImageSelected(int position); | |||||
| void onImageAdd(View view); | |||||
| } | |||||
| @@ -1,92 +0,0 @@ | |||||
| /** | |||||
| * Copyright 2020 Huawei Technologies Co., Ltd | |||||
| * <p> | |||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| * you may not use this file except in compliance with the License. | |||||
| * You may obtain a copy of the License at | |||||
| * <p> | |||||
| * http://www.apache.org/licenses/LICENSE-2.0 | |||||
| * <p> | |||||
| * Unless required by applicable law or agreed to in writing, software | |||||
| * distributed under the License is distributed on an "AS IS" BASIS, | |||||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| * See the License for the specific language governing permissions and | |||||
| * limitations under the License. | |||||
| */ | |||||
| package com.mindspore.styletransferdemo; | |||||
| import android.content.Context; | |||||
| import android.os.Bundle; | |||||
| import android.view.LayoutInflater; | |||||
| import android.view.View; | |||||
| import android.view.ViewGroup; | |||||
| import androidx.annotation.NonNull; | |||||
| import androidx.fragment.app.DialogFragment; | |||||
| import androidx.fragment.app.Fragment; | |||||
| import androidx.recyclerview.widget.GridLayoutManager; | |||||
| import androidx.recyclerview.widget.RecyclerView; | |||||
| import java.io.IOException; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Arrays; | |||||
| import java.util.List; | |||||
| /** | |||||
| * A simple {@link Fragment} subclass. | |||||
| * Use the {@link StyleFragment#newInstance} factory method to | |||||
| * create an instance of this fragment. | |||||
| */ | |||||
| public class StyleFragment extends DialogFragment { | |||||
| private OnListFragmentInteractionListener listener; | |||||
| public StyleFragment() { | |||||
| // Required empty public constructor | |||||
| } | |||||
| public static StyleFragment newInstance() { | |||||
| StyleFragment fragment = new StyleFragment(); | |||||
| return fragment; | |||||
| } | |||||
| @Override | |||||
| public View onCreateView(LayoutInflater inflater, ViewGroup container, | |||||
| Bundle savedInstanceState) { | |||||
| View view = inflater.inflate(R.layout.fragment_style, container, false); | |||||
| List<String> styles = new ArrayList<>(); | |||||
| try { | |||||
| styles.addAll(Arrays.asList(getActivity().getAssets().list("thumbnails"))); | |||||
| } catch (IOException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| if (view instanceof RecyclerView) { | |||||
| GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), 3); | |||||
| ((RecyclerView) view).setLayoutManager(gridLayoutManager); | |||||
| ((RecyclerView) view).setAdapter(new StyleRecyclerViewAdapter(getActivity(), styles, listener)); | |||||
| } | |||||
| return view; | |||||
| } | |||||
| @Override | |||||
| public void onAttach(@NonNull Context context) { | |||||
| super.onAttach(context); | |||||
| if (context instanceof OnListFragmentInteractionListener) { | |||||
| this.listener = (StyleFragment.OnListFragmentInteractionListener) context; | |||||
| } | |||||
| } | |||||
| public void onDetach() { | |||||
| super.onDetach(); | |||||
| this.listener = null; | |||||
| } | |||||
| public interface OnListFragmentInteractionListener { | |||||
| void onListFragmentInteraction(String item); | |||||
| } | |||||
| } | |||||
| @@ -16,7 +16,6 @@ | |||||
| package com.mindspore.styletransferdemo; | package com.mindspore.styletransferdemo; | ||||
| import android.content.Context; | import android.content.Context; | ||||
| import android.net.Uri; | |||||
| import android.view.LayoutInflater; | import android.view.LayoutInflater; | ||||
| import android.view.View; | import android.view.View; | ||||
| import android.view.ViewGroup; | import android.view.ViewGroup; | ||||
| @@ -27,35 +26,16 @@ import androidx.recyclerview.widget.RecyclerView; | |||||
| import com.bumptech.glide.Glide; | import com.bumptech.glide.Glide; | ||||
| import java.util.List; | |||||
| public class StyleRecyclerViewAdapter extends RecyclerView.Adapter<StyleRecyclerViewAdapter.StyleItemViewHolder> { | public class StyleRecyclerViewAdapter extends RecyclerView.Adapter<StyleRecyclerViewAdapter.StyleItemViewHolder> { | ||||
| private View.OnClickListener mOnClickListener; | |||||
| private List<String> stylesList; | |||||
| private Context context; | |||||
| private StyleFragment.OnListFragmentInteractionListener mListener; | |||||
| private final int[] IMAGES; | |||||
| private final Context context; | |||||
| private final OnBackgroundImageListener mListener; | |||||
| public StyleRecyclerViewAdapter(Context context, List<String> stylesList, StyleFragment.OnListFragmentInteractionListener mListener) { | |||||
| this.stylesList = stylesList; | |||||
| public StyleRecyclerViewAdapter(Context context, int[] IMAGES, OnBackgroundImageListener mListener) { | |||||
| this.IMAGES = IMAGES; | |||||
| this.context = context; | this.context = context; | ||||
| this.mListener = mListener; | this.mListener = mListener; | ||||
| this.mOnClickListener = new View.OnClickListener() { | |||||
| @Override | |||||
| public void onClick(View view) { | |||||
| } | |||||
| }; | |||||
| this.mOnClickListener = (View.OnClickListener) (new View.OnClickListener() { | |||||
| public final void onClick(View v) { | |||||
| if (v.getTag() != null && v.getTag() instanceof String) { | |||||
| mListener.onListFragmentInteraction(String.valueOf(v.getTag())); | |||||
| } | |||||
| } | |||||
| }); | |||||
| } | } | ||||
| @NonNull | @NonNull | ||||
| @@ -68,21 +48,27 @@ public class StyleRecyclerViewAdapter extends RecyclerView.Adapter<StyleRecycler | |||||
| @Override | @Override | ||||
| public void onBindViewHolder(@NonNull StyleItemViewHolder holder, int position) { | public void onBindViewHolder(@NonNull StyleItemViewHolder holder, int position) { | ||||
| String imagePath = stylesList.get(position); | |||||
| Glide.with(context). | Glide.with(context). | ||||
| load(Uri.parse("file:///android_asset/thumbnails/" + imagePath)). | |||||
| centerInside(). | |||||
| load(IMAGES[position]). | |||||
| into(holder.getImageView()); | into(holder.getImageView()); | ||||
| View view = holder.getMView(); | View view = holder.getMView(); | ||||
| view.setTag(imagePath); | |||||
| view.setOnClickListener(this.mOnClickListener); | |||||
| view.setTag(IMAGES[position]); | |||||
| view.setOnClickListener(view1 -> { | |||||
| if (mListener != null) { | |||||
| if (IMAGES.length - 1 == position) { | |||||
| mListener.onImageAdd(holder.getImageView()); | |||||
| } else { | |||||
| mListener.onBackImageSelected(position); | |||||
| } | |||||
| } | |||||
| }); | |||||
| } | } | ||||
| @Override | @Override | ||||
| public int getItemCount() { | public int getItemCount() { | ||||
| return stylesList == null ? 0 : stylesList.size(); | |||||
| return IMAGES == null ? 0 : IMAGES.length; | |||||
| } | } | ||||
| @@ -0,0 +1,20 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <animated-rotate xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| android:fromDegrees="0" | |||||
| android:pivotX="50%" | |||||
| android:pivotY="50%" | |||||
| android:toDegrees="360"> | |||||
| <shape | |||||
| android:innerRadiusRatio="3" | |||||
| android:shape="ring" | |||||
| android:thicknessRatio="8" | |||||
| android:useLevel="false"> | |||||
| <gradient | |||||
| android:centerColor="#62AEEC" | |||||
| android:centerY="0.50" | |||||
| android:endColor="#1063A5" | |||||
| android:startColor="#61C2EC" | |||||
| android:type="sweep" | |||||
| android:useLevel="false" /> | |||||
| </shape> | |||||
| </animated-rotate> | |||||
| @@ -1,11 +1,9 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| xmlns:tools="http://schemas.android.com/tools" | |||||
| android:layout_width="match_parent" | android:layout_width="match_parent" | ||||
| android:layout_height="match_parent" | android:layout_height="match_parent" | ||||
| android:background="@color/white" | |||||
| android:orientation="vertical" | |||||
| tools:context=".TestActivity"> | |||||
| android:background="@color/colorPrimary" | |||||
| android:orientation="vertical"> | |||||
| <androidx.appcompat.widget.Toolbar | <androidx.appcompat.widget.Toolbar | ||||
| android:id="@+id/toolbar" | android:id="@+id/toolbar" | ||||
| @@ -20,57 +18,125 @@ | |||||
| android:drawableStart="@drawable/logo2" | android:drawableStart="@drawable/logo2" | ||||
| android:drawablePadding="5dp" | android:drawablePadding="5dp" | ||||
| android:gravity="center_vertical" | android:gravity="center_vertical" | ||||
| android:maxLines="1" | |||||
| android:text="MindSpore StyleTransfer" | android:text="MindSpore StyleTransfer" | ||||
| android:textColor="#ffffff" | android:textColor="#ffffff" | ||||
| android:textSize="20sp" /> | android:textSize="20sp" /> | ||||
| </androidx.appcompat.widget.Toolbar> | </androidx.appcompat.widget.Toolbar> | ||||
| <HorizontalScrollView | |||||
| <FrameLayout | |||||
| android:layout_width="match_parent" | android:layout_width="match_parent" | ||||
| android:layout_height="wrap_content"> | |||||
| android:layout_height="300dp"> | |||||
| <LinearLayout | |||||
| android:layout_width="match_parent" | |||||
| <ImageView | |||||
| android:id="@+id/img_origin" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | android:layout_height="wrap_content" | ||||
| android:orientation="horizontal"> | |||||
| android:layout_gravity="center" | |||||
| android:layout_margin="10dp" | |||||
| android:scaleType="fitXY" /> | |||||
| <ImageView | |||||
| android:id="@+id/img_origin" | |||||
| android:layout_width="256dp" | |||||
| android:layout_height="256dp" | |||||
| android:layout_margin="10dp" | |||||
| android:scaleType="fitXY" | |||||
| android:src="@android:drawable/ic_menu_gallery" /> | |||||
| <TextView | |||||
| android:id="@+id/tv_image" | |||||
| android:layout_width="206dp" | |||||
| android:layout_height="48dp" | |||||
| android:layout_gravity="center" | |||||
| android:gravity="center" | |||||
| android:paddingLeft="4dp" | |||||
| android:text="Choose a Image" | |||||
| android:textAllCaps="false" | |||||
| android:textColor="@color/white" | |||||
| android:textSize="20sp" /> | |||||
| <ImageView | |||||
| android:id="@+id/img_style" | |||||
| android:layout_width="256dp" | |||||
| android:layout_height="256dp" | |||||
| android:layout_margin="10dp" | |||||
| android:scaleType="fitXY" | |||||
| android:src="@drawable/style_chose" /> | |||||
| </LinearLayout> | |||||
| <ProgressBar | |||||
| android:id="@+id/progress" | |||||
| android:layout_width="80dp" | |||||
| android:layout_height="80dp" | |||||
| android:layout_gravity="center" | |||||
| android:indeterminateDrawable="@drawable/progressbar" | |||||
| android:visibility="invisible" /> | |||||
| </FrameLayout> | |||||
| </HorizontalScrollView> | |||||
| <FrameLayout | |||||
| android:layout_width="256dp" | |||||
| android:layout_height="256dp" | |||||
| android:layout_gravity="center" | |||||
| android:layout_margin="20dp"> | |||||
| <LinearLayout | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="wrap_content" | |||||
| android:orientation="horizontal"> | |||||
| <ProgressBar | |||||
| android:visibility="invisible" | |||||
| android:id="@+id/progress_circular" | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_gravity="center" /> | |||||
| <Button | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="48dp" | |||||
| android:layout_marginLeft="20dp" | |||||
| android:layout_marginRight="5dp" | |||||
| android:layout_weight="1" | |||||
| android:background="@color/gray_btn" | |||||
| android:gravity="center" | |||||
| android:onClick="onClickPhoto" | |||||
| android:text="PHOTO" | |||||
| android:textAllCaps="false" | |||||
| android:textColor="@color/white" | |||||
| android:textSize="12sp" /> | |||||
| <ImageView | |||||
| android:id="@+id/img_result" | |||||
| android:layout_width="256dp" | |||||
| android:layout_height="256dp" | |||||
| android:padding="4dp" | |||||
| android:src="@android:drawable/ic_menu_report_image" /> | |||||
| </FrameLayout> | |||||
| <Button | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="48dp" | |||||
| android:layout_marginLeft="5dp" | |||||
| android:layout_marginRight="5dp" | |||||
| android:layout_weight="1" | |||||
| android:background="@color/gray_btn" | |||||
| android:gravity="center" | |||||
| android:onClick="onClickCamera" | |||||
| android:text="CAMERA" | |||||
| android:textAllCaps="false" | |||||
| android:textColor="@color/white" | |||||
| android:textSize="12sp" /> | |||||
| <Button | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="48dp" | |||||
| android:layout_marginLeft="5dp" | |||||
| android:layout_marginRight="5dp" | |||||
| android:layout_weight="1" | |||||
| android:background="@color/gray_btn" | |||||
| android:gravity="center" | |||||
| android:onClick="onClickRecovery" | |||||
| android:text="RECOVERY" | |||||
| android:textAllCaps="false" | |||||
| android:textColor="@color/white" | |||||
| android:textSize="12sp" /> | |||||
| <Button | |||||
| android:layout_width="0dp" | |||||
| android:layout_height="48dp" | |||||
| android:layout_marginLeft="5dp" | |||||
| android:layout_marginRight="20dp" | |||||
| android:layout_weight="1" | |||||
| android:background="@color/gray_btn" | |||||
| android:gravity="center" | |||||
| android:onClick="onClickSave" | |||||
| android:text="SAVE" | |||||
| android:textAllCaps="false" | |||||
| android:textColor="@color/white" | |||||
| android:textSize="12sp" /> | |||||
| </LinearLayout> | |||||
| <TextView | |||||
| android:layout_width="wrap_content" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_marginLeft="20dp" | |||||
| android:layout_marginTop="20dp" | |||||
| android:text="Choose a Style" | |||||
| android:textColor="@color/white" | |||||
| android:textSize="20sp" /> | |||||
| <androidx.recyclerview.widget.RecyclerView | |||||
| android:id="@+id/recyclerview" | |||||
| android:layout_width="match_parent" | |||||
| android:layout_height="wrap_content" | |||||
| android:layout_margin="20dp" | |||||
| android:fadeScrollbars="false" | |||||
| android:scrollbarSize="6dp" | |||||
| android:scrollbarStyle="outsideInset" | |||||
| android:scrollbarThumbVertical="@color/gray" | |||||
| android:scrollbars="vertical" /> | |||||
| </LinearLayout> | </LinearLayout> | ||||
| @@ -1,6 +1,6 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | <resources> | ||||
| <color name="colorPrimary">#6200EE</color> | |||||
| <color name="colorPrimary">#303030</color> | |||||
| <color name="colorPrimaryDark">#3700B3</color> | <color name="colorPrimaryDark">#3700B3</color> | ||||
| <color name="colorAccent">#03DAC5</color> | <color name="colorAccent">#03DAC5</color> | ||||
| @@ -13,4 +13,5 @@ | |||||
| <color name="text_yellow">#F8E71C</color> | <color name="text_yellow">#F8E71C</color> | ||||
| <color name="text_orange">#FF844D</color> | <color name="text_orange">#FF844D</color> | ||||
| <color name="text_green">#66B50A</color> | <color name="text_green">#66B50A</color> | ||||
| </resources> | </resources> | ||||
| @@ -2,4 +2,6 @@ | |||||
| <string name="app_name">StyleTransfer</string> | <string name="app_name">StyleTransfer</string> | ||||
| <!-- TODO: Remove or change this placeholder text --> | <!-- TODO: Remove or change this placeholder text --> | ||||
| <string name="hello_blank_fragment">Hello blank fragment</string> | <string name="hello_blank_fragment">Hello blank fragment</string> | ||||
| <string name="no_pic_neededSave">Null Image needed to save</string> | |||||
| <string name="save_success">Save success</string> | |||||
| </resources> | </resources> | ||||
| @@ -0,0 +1,6 @@ | |||||
| <?xml version="1.0" encoding="utf-8"?> | |||||
| <paths> | |||||
| <external-path | |||||
| name="external_files" | |||||
| path="." /> | |||||
| </paths> | |||||