From: @sishuikang Reviewed-by: @zhanghaibo5,@ivss Signed-off-by: @zhanghaibo5tags/v1.2.0-rc1
| @@ -8,8 +8,8 @@ android { | |||
| applicationId "com.mindspore.himindspore" | |||
| minSdkVersion 21 | |||
| targetSdkVersion 30 | |||
| versionCode 4 | |||
| versionName "1.1.2" | |||
| versionCode 5 | |||
| versionName "1.1.3" | |||
| testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | |||
| javaCompileOptions { | |||
| @@ -292,7 +292,7 @@ public class SplashActivity extends BaseActivity<MainPresenter> implements MainC | |||
| public void showUpdate(final UpdateInfoBean updateInfo) { | |||
| 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); | |||
| } else { | |||
| 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; | |||
| import android.Manifest; | |||
| import android.annotation.SuppressLint; | |||
| import android.app.Activity; | |||
| 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.Matrix; | |||
| import android.graphics.Paint; | |||
| import android.graphics.PorterDuff; | |||
| import android.graphics.Rect; | |||
| import android.hardware.camera2.CameraAccessException; | |||
| import android.hardware.camera2.CameraCaptureSession; | |||
| import android.hardware.camera2.CameraCharacteristics; | |||
| @@ -41,33 +46,54 @@ import android.view.Surface; | |||
| import android.view.SurfaceView; | |||
| import android.view.View; | |||
| import android.view.ViewGroup; | |||
| import android.widget.Toast; | |||
| import androidx.annotation.NonNull; | |||
| import androidx.annotation.Nullable; | |||
| import androidx.core.app.ActivityCompat; | |||
| import androidx.core.util.Pair; | |||
| import androidx.fragment.app.Fragment; | |||
| import java.nio.ByteBuffer; | |||
| import java.util.Arrays; | |||
| import java.util.List; | |||
| import java.util.concurrent.Semaphore; | |||
| 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. | |||
| * create an instance of this 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 CameraCaptureSession captureSession; | |||
| 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 Handler backgroundHandler; | |||
| private ImageReader imageReader; | |||
| @@ -76,8 +102,18 @@ public class PoseNetFragment extends Fragment { | |||
| private Semaphore cameraOpenCloseLock = new Semaphore(1);//使用信号量 Semaphore 进行多线程任务调度 | |||
| 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() { | |||
| @@ -93,12 +129,14 @@ public class PoseNetFragment extends Fragment { | |||
| public void onDisconnected(@NonNull CameraDevice cameraDevice) { | |||
| cameraOpenCloseLock.release(); | |||
| cameraDevice.close(); | |||
| closeCamera(); | |||
| PoseNetFragment.this.cameraDevice = null; | |||
| } | |||
| @Override | |||
| public void onError(@NonNull CameraDevice cameraDevice, int error) { | |||
| onDisconnected(cameraDevice); | |||
| closeCamera(); | |||
| Activity activity = getActivity(); | |||
| if (activity != null) { | |||
| activity.finish(); | |||
| @@ -106,7 +144,6 @@ public class PoseNetFragment extends Fragment { | |||
| } | |||
| }; | |||
| private CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() { | |||
| @Override | |||
| 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() { | |||
| PoseNetFragment fragment = new PoseNetFragment(); | |||
| return fragment; | |||
| @@ -155,86 +186,24 @@ public class PoseNetFragment extends Fragment { | |||
| openCamera(); | |||
| } | |||
| @Override | |||
| public void onActivityCreated(@Nullable Bundle savedInstanceState) { | |||
| super.onActivityCreated(savedInstanceState); | |||
| posenet = new Posenet(getActivity()); | |||
| } | |||
| public void onPause() { | |||
| this.closeCamera(); | |||
| this.stopBackgroundThread(); | |||
| 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") | |||
| private void openCamera() { | |||
| if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { | |||
| this.requestCameraPermission(); | |||
| } | |||
| setUpCameraOutputs(); | |||
| CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); | |||
| 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 | |||
| if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { | |||
| 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) { | |||
| e.printStackTrace(); | |||
| } catch (InterruptedException e) { | |||
| @@ -305,31 +280,48 @@ public class PoseNetFragment extends Fragment { | |||
| } | |||
| } | |||
| private final ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() { | |||
| @Override | |||
| public void onImageAvailable(ImageReader imageReader) { | |||
| if (previewWidth != 0 && previewHeight != 0 && imageReader != null) { | |||
| if (imageReader != null) { | |||
| 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. | |||
| */ | |||
| private void createCameraPreviewSession() { | |||
| 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. | |||
| 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; | |||
| import android.Manifest; | |||
| import android.content.DialogInterface; | |||
| import android.content.Intent; | |||
| import android.content.pm.PackageManager; | |||
| import android.graphics.Bitmap; | |||
| import android.graphics.Canvas; | |||
| import android.graphics.Color; | |||
| @@ -24,10 +28,19 @@ import android.graphics.PorterDuff; | |||
| import android.graphics.Rect; | |||
| import android.hardware.camera2.CameraCharacteristics; | |||
| import android.media.Image; | |||
| import android.net.Uri; | |||
| import android.os.Build; | |||
| import android.os.Bundle; | |||
| import android.provider.Settings; | |||
| 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.core.app.ActivityCompat; | |||
| import androidx.core.content.ContextCompat; | |||
| import androidx.core.util.Pair; | |||
| 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; | |||
| @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; | |||
| @Override | |||
| protected void onCreate(Bundle savedInstanceState) { | |||
| super.onCreate(savedInstanceState); | |||
| 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 { | |||
| 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:textColor="#ffffff" | |||
| 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> | |||
| </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.Intent; | |||
| 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.os.Build; | |||
| import android.os.Bundle; | |||
| import android.provider.Settings; | |||
| import android.view.SurfaceView; | |||
| import android.view.View; | |||
| import androidx.annotation.NonNull; | |||
| import androidx.annotation.Nullable; | |||
| @@ -40,34 +31,8 @@ import androidx.appcompat.app.AlertDialog; | |||
| import androidx.appcompat.app.AppCompatActivity; | |||
| import androidx.core.app.ActivityCompat; | |||
| 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, | |||
| 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 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; | |||
| @Override | |||
| @@ -182,173 +130,16 @@ public class MainActivity extends AppCompatActivity implements CameraDataDealLis | |||
| } | |||
| 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; | |||
| } | |||
| 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.app.Activity; | |||
| import android.content.Context; | |||
| import android.graphics.Bitmap; | |||
| import android.graphics.Canvas; | |||
| import android.graphics.Color; | |||
| 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.CameraCaptureSession; | |||
| import android.hardware.camera2.CameraCharacteristics; | |||
| @@ -42,29 +49,51 @@ import android.view.ViewGroup; | |||
| import androidx.annotation.NonNull; | |||
| import androidx.annotation.Nullable; | |||
| import androidx.core.util.Pair; | |||
| import androidx.fragment.app.Fragment; | |||
| import java.nio.ByteBuffer; | |||
| import java.util.Arrays; | |||
| import java.util.List; | |||
| import java.util.concurrent.Semaphore; | |||
| 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. | |||
| * create an instance of this 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 String cameraId = "1"; | |||
| private int mCameraId = CameraCharacteristics.LENS_FACING_FRONT; // 要打开的摄像头ID | |||
| private SurfaceView surfaceView; | |||
| private CameraCaptureSession captureSession; | |||
| 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 Handler backgroundHandler; | |||
| private ImageReader imageReader; | |||
| @@ -73,6 +102,19 @@ public class PoseNetFragment extends Fragment { | |||
| private Semaphore cameraOpenCloseLock = new Semaphore(1);//使用信号量 Semaphore 进行多线程任务调度 | |||
| 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() { | |||
| @Override | |||
| @@ -87,12 +129,14 @@ public class PoseNetFragment extends Fragment { | |||
| public void onDisconnected(@NonNull CameraDevice cameraDevice) { | |||
| cameraOpenCloseLock.release(); | |||
| cameraDevice.close(); | |||
| closeCamera(); | |||
| PoseNetFragment.this.cameraDevice = null; | |||
| } | |||
| @Override | |||
| public void onError(@NonNull CameraDevice cameraDevice, int error) { | |||
| onDisconnected(cameraDevice); | |||
| closeCamera(); | |||
| Activity activity = getActivity(); | |||
| if (activity != null) { | |||
| 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() { | |||
| PoseNetFragment fragment = new PoseNetFragment(); | |||
| return fragment; | |||
| @@ -148,57 +186,24 @@ public class PoseNetFragment extends Fragment { | |||
| openCamera(); | |||
| } | |||
| @Override | |||
| public void onActivityCreated(@Nullable Bundle savedInstanceState) { | |||
| super.onActivityCreated(savedInstanceState); | |||
| posenet = new Posenet(getActivity()); | |||
| } | |||
| public void onPause() { | |||
| this.closeCamera(); | |||
| this.stopBackgroundThread(); | |||
| 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") | |||
| private void openCamera() { | |||
| setUpCameraOutputs(); | |||
| CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); | |||
| 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 | |||
| if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { | |||
| 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) { | |||
| e.printStackTrace(); | |||
| } catch (InterruptedException e) { | |||
| @@ -266,31 +280,48 @@ public class PoseNetFragment extends Fragment { | |||
| } | |||
| } | |||
| private final ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() { | |||
| @Override | |||
| public void onImageAvailable(ImageReader imageReader) { | |||
| if (previewWidth != 0 && previewHeight != 0 && imageReader != null) { | |||
| if (imageReader != null) { | |||
| 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. | |||
| */ | |||
| private void createCameraPreviewSession() { | |||
| 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. | |||
| 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"> | |||
| <TextView | |||
| android:layout_width="match_parent" | |||
| android:layout_width="wrap_content" | |||
| android:layout_height="wrap_content" | |||
| android:drawableStart="@drawable/logo2" | |||
| android:drawablePadding="5dp" | |||
| @@ -27,5 +27,14 @@ | |||
| android:text="MindSpore PoseNet" | |||
| android:textColor="#ffffff" | |||
| 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> | |||
| </RelativeLayout> | |||
| @@ -1,5 +1,6 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||
| xmlns:tools="http://schemas.android.com/tools" | |||
| package="com.mindspore.styletransferdemo"> | |||
| <uses-permission android:name="android.permission.CAMERA" /> | |||
| @@ -34,6 +35,18 @@ | |||
| <category android:name="android.intent.category.LAUNCHER" /> | |||
| </intent-filter> | |||
| </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> | |||
| </manifest> | |||
| @@ -89,7 +89,7 @@ public class BitmapUtils { | |||
| } | |||
| // 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 = | |||
| Math.max( | |||
| (float) imageBitmap.getWidth() / (float) targetWidth, | |||
| @@ -104,6 +104,26 @@ public class BitmapUtils { | |||
| 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. | |||
| * | |||
| @@ -16,16 +16,24 @@ | |||
| package com.mindspore.styletransferdemo; | |||
| import android.content.Context; | |||
| import android.content.Intent; | |||
| import android.graphics.Bitmap; | |||
| import android.graphics.BitmapFactory; | |||
| import android.graphics.Color; | |||
| import android.graphics.Matrix; | |||
| 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.exifinterface.media.ExifInterface; | |||
| import java.io.File; | |||
| import java.io.FileNotFoundException; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.nio.ByteBuffer; | |||
| @@ -33,6 +41,8 @@ import java.nio.ByteOrder; | |||
| public class ImageUtils { | |||
| private static final String TAG = "ImageUtils"; | |||
| private static Matrix decodeExifOrientation(int orientation) { | |||
| Matrix matrix = new Matrix(); | |||
| @@ -198,4 +208,59 @@ public class ImageUtils { | |||
| } | |||
| 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; | |||
| import android.Manifest; | |||
| import android.content.DialogInterface; | |||
| import android.content.Intent; | |||
| import android.content.pm.PackageManager; | |||
| import android.content.res.Configuration; | |||
| import android.graphics.Bitmap; | |||
| import android.graphics.BitmapFactory; | |||
| import android.net.Uri; | |||
| import android.os.Build; | |||
| import android.os.Bundle; | |||
| import android.os.Environment; | |||
| import android.provider.MediaStore; | |||
| import android.text.TextUtils; | |||
| import android.provider.Settings; | |||
| import android.util.Log; | |||
| import android.util.Pair; | |||
| import android.view.View; | |||
| import android.widget.ImageView; | |||
| import android.widget.ProgressBar; | |||
| import android.widget.TextView; | |||
| import android.widget.Toast; | |||
| import androidx.annotation.NonNull; | |||
| import androidx.annotation.Nullable; | |||
| import androidx.appcompat.app.AlertDialog; | |||
| import androidx.appcompat.app.AppCompatActivity; | |||
| 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; | |||
| 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 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 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 boolean isHasPermssion; | |||
| private boolean isRunningModel; | |||
| private ImageView imgOrigin, imgStyle, imgResult; | |||
| private ProgressBar progressResult; | |||
| private ImageView imgPreview; | |||
| private Uri imageUri; | |||
| private TextView textOriginImage; | |||
| private ProgressBar progressBar; | |||
| private RecyclerView recyclerView; | |||
| private Integer maxWidthOfImage; | |||
| private Integer maxHeightOfImage; | |||
| private boolean isLandScape; | |||
| private Bitmap originBitmap, styleBitmap; | |||
| private StyleFragment styleFragment; | |||
| private String selectedStyle; | |||
| private Bitmap originBitmap, styleBitmap, resultBitmap; | |||
| @Override | |||
| protected void onCreate(Bundle savedInstanceState) { | |||
| @@ -71,23 +95,35 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||
| } | |||
| 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); | |||
| } | |||
| 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 | |||
| public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | |||
| 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); | |||
| 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 | |||
| protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent 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(); | |||
| int targetWidth = targetedSize.first; | |||
| 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. | |||
| 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) { | |||
| 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 | |||
| 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; | |||
| progressBar.setVisibility(View.VISIBLE); | |||
| 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; | |||
| progressBar.setVisibility(View.INVISIBLE); | |||
| } else { | |||
| Toast.makeText(this, "Previous Model still running", Toast.LENGTH_SHORT).show(); | |||
| } | |||
| } | |||
| private String getUriFromAssetThumb(String thumb) { | |||
| return "thumbnails/" + thumb; | |||
| } | |||
| // Returns max width of image. | |||
| private Integer getMaxWidthOfImage() { | |||
| if (this.maxWidthOfImage == null) { | |||
| if (this.isLandScape) { | |||
| this.maxWidthOfImage = ((View) this.imgOrigin.getParent()).getHeight(); | |||
| this.maxWidthOfImage = ((View) this.imgPreview.getParent()).getHeight(); | |||
| } else { | |||
| this.maxWidthOfImage = ((View) this.imgOrigin.getParent()).getWidth(); | |||
| this.maxWidthOfImage = ((View) this.imgPreview.getParent()).getWidth(); | |||
| } | |||
| } | |||
| return this.maxWidthOfImage; | |||
| @@ -199,9 +332,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||
| private Integer getMaxHeightOfImage() { | |||
| if (this.maxHeightOfImage == null) { | |||
| if (this.isLandScape) { | |||
| this.maxHeightOfImage = ((View) this.imgOrigin.getParent()).getWidth(); | |||
| this.maxHeightOfImage = ((View) this.imgPreview.getParent()).getWidth(); | |||
| } else { | |||
| this.maxHeightOfImage = ((View) this.imgOrigin.getParent()).getHeight(); | |||
| this.maxHeightOfImage = ((View) this.imgPreview.getParent()).getHeight(); | |||
| } | |||
| } | |||
| return this.maxHeightOfImage; | |||
| @@ -219,5 +352,4 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe | |||
| 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; | |||
| import android.content.Context; | |||
| import android.net.Uri; | |||
| import android.view.LayoutInflater; | |||
| import android.view.View; | |||
| import android.view.ViewGroup; | |||
| @@ -27,35 +26,16 @@ import androidx.recyclerview.widget.RecyclerView; | |||
| import com.bumptech.glide.Glide; | |||
| import java.util.List; | |||
| 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.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 | |||
| @@ -68,21 +48,27 @@ public class StyleRecyclerViewAdapter extends RecyclerView.Adapter<StyleRecycler | |||
| @Override | |||
| public void onBindViewHolder(@NonNull StyleItemViewHolder holder, int position) { | |||
| String imagePath = stylesList.get(position); | |||
| Glide.with(context). | |||
| load(Uri.parse("file:///android_asset/thumbnails/" + imagePath)). | |||
| centerInside(). | |||
| load(IMAGES[position]). | |||
| into(holder.getImageView()); | |||
| 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 | |||
| 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"?> | |||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |||
| xmlns:tools="http://schemas.android.com/tools" | |||
| android:layout_width="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 | |||
| android:id="@+id/toolbar" | |||
| @@ -20,57 +18,125 @@ | |||
| android:drawableStart="@drawable/logo2" | |||
| android:drawablePadding="5dp" | |||
| android:gravity="center_vertical" | |||
| android:maxLines="1" | |||
| android:text="MindSpore StyleTransfer" | |||
| android:textColor="#ffffff" | |||
| android:textSize="20sp" /> | |||
| </androidx.appcompat.widget.Toolbar> | |||
| <HorizontalScrollView | |||
| <FrameLayout | |||
| 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: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> | |||
| @@ -1,6 +1,6 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <resources> | |||
| <color name="colorPrimary">#6200EE</color> | |||
| <color name="colorPrimary">#303030</color> | |||
| <color name="colorPrimaryDark">#3700B3</color> | |||
| <color name="colorAccent">#03DAC5</color> | |||
| @@ -13,4 +13,5 @@ | |||
| <color name="text_yellow">#F8E71C</color> | |||
| <color name="text_orange">#FF844D</color> | |||
| <color name="text_green">#66B50A</color> | |||
| </resources> | |||
| @@ -2,4 +2,6 @@ | |||
| <string name="app_name">StyleTransfer</string> | |||
| <!-- TODO: Remove or change this placeholder text --> | |||
| <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> | |||
| @@ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <paths> | |||
| <external-path | |||
| name="external_files" | |||
| path="." /> | |||
| </paths> | |||