!10521 posenet add front camera and styletransfer save
From: @sishuikang Reviewed-by: @zhanghaibo5,@ivss Signed-off-by: @zhanghaibo5
|
@ -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 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean checkPermissionAllGranted(String[] permissions) {
|
||||
for (String permission : permissions) {
|
||||
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authority application result callback
|
||||
*/
|
||||
@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 {
|
||||
addCameraFragment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (REQUEST_PERMISSION_AGAIN == requestCode) {
|
||||
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;
|
||||
}
|
||||
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);
|
||||
public void onClickSwitch(View view) {
|
||||
poseNetFragment.switchCamera();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -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);
|
||||
public void onClickSwitch(View view) {
|
||||
poseNetFragment.switchCamera();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -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);
|
||||
imgPreview = findViewById(R.id.img_origin);
|
||||
textOriginImage = findViewById(R.id.tv_image);
|
||||
progressBar = findViewById(R.id.progress);
|
||||
recyclerView = findViewById(R.id.recyclerview);
|
||||
|
||||
imgOrigin.setOnClickListener(this);
|
||||
imgStyle.setOnClickListener(this);
|
||||
imgResult.setOnClickListener(this);
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (view.getId() == R.id.img_origin) {
|
||||
if (isHasPermssion) {
|
||||
openGallay();
|
||||
} else {
|
||||
requestPermissions();
|
||||
for (int grant : grantResults) {
|
||||
if (grant != PackageManager.PERMISSION_GRANTED) {
|
||||
isAllGranted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (view.getId() == R.id.img_style) {
|
||||
if (!isRunningModel) {
|
||||
styleFragment.show(getSupportFragmentManager(), TAG);
|
||||
if (!isAllGranted) {
|
||||
openAppDetails();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void openGallay() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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(imgOrigin);
|
||||
Glide.with(this).load(originBitmap).into(imgPreview);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onListFragmentInteraction(String item) {
|
||||
this.selectedStyle = item;
|
||||
styleFragment.dismiss();
|
||||
startRunningModel();
|
||||
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(imgPreview);
|
||||
}
|
||||
}
|
||||
|
||||
private void startRunningModel() {
|
||||
if (!isRunningModel && !TextUtils.isEmpty(selectedStyle)) {
|
||||
styleBitmap = ImageUtils.loadBitmapFromResources(this, getUriFromAssetThumb(selectedStyle));
|
||||
Glide.with(this)
|
||||
.load(styleBitmap)
|
||||
.into(imgStyle);
|
||||
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);
|
||||
}
|
||||
|
||||
if (originBitmap == null) {
|
||||
Toast.makeText(this, "Please select an original picture first", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
progressResult.setVisibility(View.VISIBLE);
|
||||
@Override
|
||||
public void onBackImageSelected(int position) {
|
||||
styleBitmap = BitmapFactory.decodeResource(getResources(), IMAGES[position]);
|
||||
startRunningModel(styleBitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
|
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 116 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 107 KiB |
|
@ -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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<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" />
|
||||
|
||||
<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>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="256dp"
|
||||
android:layout_height="256dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="20dp">
|
||||
|
||||
<ProgressBar
|
||||
android:visibility="invisible"
|
||||
android:id="@+id/progress_circular"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp">
|
||||
|
||||
<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" />
|
||||
android:id="@+id/img_origin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="10dp"
|
||||
android:scaleType="fitXY" />
|
||||
|
||||
<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" />
|
||||
|
||||
<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>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<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" />
|
||||
|
||||
<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>
|