Skip to content

Commit

Permalink
Merge pull request #46 from Yoonit-Labs/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
Goulartvic authored Mar 8, 2021
2 parents ee67ff5 + cf2d503 commit 5e6536c
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 303 deletions.
147 changes: 108 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,57 @@
![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/Yoonit-Labs/android-yoonit-camera?color=lightgrey&label=version&style=for-the-badge) ![GitHub](https://img.shields.io/github/license/Yoonit-Labs/android-yoonit-camera?color=lightgrey&style=for-the-badge)

A Android plugin to provide:
- Modern Android Camera API (Camera X)
- [Standart Google ML Kit](https://developers.google.com/ml-kit)
- Modern Android Camera API [Camera X](https://developer.android.com/training/camerax)
- Camera preview (Front & Back)
- Face detection (With Min & Max size)
- Landmark detection
- Face crop
- Face capture
- [Google MLKit](https://developers.google.com/ml-kit) integration
- [PyTorch](https://pytorch.org/mobile/home/) integration
- Computer vision pipeline
- Face detection, capture and image crop
- Understanding of the human face
- Frame capture
- Face ROI
- Capture timed images
- QR Code scanning

## Install

Add the JitPack repository to your root `build.gradle` at the end of repositories
## Table of Contents

* [Installation](#installation)
* [Usage](#usage)
* [Camera Preview](#camera-preview)
* [Start capturing face images](#start-capturing-face-images)
* [Start scanning QR Codes](#start-capturing-face-images)
* [API](#api)
* [Methods](#methods)
* [Events](#events)
* [Face Analysis](#face-analysis)
* [Head Movements](#head-movements)
* [KeyError](#keyerror)
* [Message](#message)
* [To contribute and make it better](#to-contribute-and-make-it-better)

## Installation

Add the JitPack repository to your root `build.gradle` at the end of repositories

```groovy
```groovy
allprojects {
repositories {
...
repositories {
...
maven { url 'https://jitpack.io' }
}
}
```
}
}
```

Add the dependency
Add the dependency

```groovy
```groovy
dependencies {
implementation 'com.github.Yoonit-Labs:android-yoonit-camera:master-SNAPSHOT'
}
```
```


## Usage

## Usage

All the functionalities that the `android-yoonit-camera` provides is accessed through the `CameraView`, that includes the camera preview. Below we have the basic usage code, for more details, see the [**Methods**](#methods).


Expand All @@ -48,9 +64,9 @@ All the functionalities that the `android-yoonit-camera` provides is accessed th
Do not forget request camera permission. Use like this in the your layout XML:

```kotlin
<ai.cyberlabs.yoonit.camera.CameraView
android:id="@+id/camera_view"
android:layout_width="match_parent"
<ai.cyberlabs.yoonit.camera.CameraView
android:id="@+id/camera_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
```

Expand All @@ -77,9 +93,30 @@ this.cameraView.setCameraEventListener(this.buildCameraEventListener())
...
fun buildCameraEventListener(): CameraEventListener = object : CameraEventListener {
...
override fun onImageCaptured(type: String, count: Int, total: Int, imagePath: String) {
override fun onImageCaptured(
type: String,
count: Int,
total: Int,
imagePath: String,
inferences: ArrayList<Pair<String, FloatArray>>
) {
// YOUR CODE
}

override fun onFaceDetected(
x: Int,
y: Int,
width: Int,
height: Int,
leftEyeOpenProbability: Float?,
rightEyeOpenProbability: Float?,
smilingProbability: Float?,
headEulerAngleX: Float,
headEulerAngleY: Float,
headEulerAngleZ: Float
) {
// YOU CODE
}
...
}
```
Expand All @@ -99,14 +136,13 @@ this.cameraView.setCameraEventListener(this.buildCameraEventListener())
...
fun buildCameraEventListener(): CameraEventListener = object : CameraEventListener {
...
override fun onQRCodeScanned(content: String) {
override fun onQRCodeScanned(content: String) {
// YOUR CODE
}
...
}
```


```

## API

### Methods
Expand Down Expand Up @@ -147,16 +183,43 @@ fun buildCameraEventListener(): CameraEventListener = object : CameraEventListen

### Events

| Event | Parameters | Description
| - | - | -
| onImageCaptured | `type: String, count: Int, total: Int, imagePath: String, inferences: ArrayList<Pair<String, FloatArray>>` | Must have started capture type of face/frame (see `startCaptureType`). Emitted when the image file is created: <ul><li>type: 'face' | 'frame'</li><li>count: current index</li><li>total: total to create</li><li>imagePath: the image path</li><li>inferences: each array element is the image inference result.</li><ul>
| onFaceDetected | `x: Int, y: Int, width: Int, height: Int` | Must have started capture type of face. Emit the detected face bounding box.
| onFaceUndetected | - | Must have started capture type of face. Emitted after `onFaceDetected`, when there is no more face detecting.
| onEndCapture | - | Must have started capture type of face/frame. Emitted when the number of image files created is equal of the number of images set (see the method `setNumberOfImages`).
| onQRCodeScanned | `content: String` | Must have started capture type of qrcode (see `startCaptureType`). Emitted when the camera scan a QR Code.
| onError | `error: String` | Emit message error.
| onMessage | `message: String` | Emit message.
| onPermissionDenied | - | Emit when try to `startPreview` but there is not camera permission.
| Event | Parameters | Description
| - | - | -
| onImageCaptured | `type: String, count: Int, total: Int, imagePath: String, inferences: ArrayList<Pair<String, FloatArray>>` | Must have started capture type of face/frame (see `startCaptureType`). Emitted when the image file is created: <ul><li>type: 'face' | 'frame'</li><li>count: current index</li><li>total: total to create</li><li>imagePath: the image path</li><li>inferences: each array element is the image inference result.</li><ul>
| onFaceDetected | `x: Int, y: Int, width: Int, height: Int, leftEyeOpenProbability: Float?, rightEyeOpenProbability: Float?, smilingProbability: Float?, headEulerAngleX: Float, headEulerAngleY: Float, headEulerAngleZ: Float` | Must have started capture type of face. Emit the [face analysis](#face-analysis).
| onFaceUndetected | - | Must have started capture type of face. Emitted after `onFaceDetected`, when there is no more face detecting.
| onEndCapture | - | Must have started capture type of face/frame. Emitted when the number of image files created is equal of the number of images set (see the method `setNumberOfImages`).
| onQRCodeScanned | `content: String` | Must have started capture type of qrcode (see `startCaptureType`). Emitted when the camera scan a QR Code.
| onError | `error: String` | Emit message error.
| onMessage | `message: String` | Emit message.
| onPermissionDenied | - | Emit when try to `startPreview` but there is not camera permission.

#### Face Analysis

The face analysis is the response send by the `onFaceDetected`. Here we specify all the parameters.

| Attribute | Type | Description |
| - | - | - |
| x | `Int` | The `x` position of the face in the screen. |
| y | `Int` | The `y` position of the face in the screen. |
| width | `Int` | The `width` position of the face in the screen. |
| height | `Int` | The `height` position of the face in the screen. |
| leftEyeOpenProbability | `Float?` | The left eye open probability. |
| rightEyeOpenProbability | `Float?` | The right eye open probability. |
| smilingProbability | `Float?` | The smiling probability. |
| headEulerAngleX | `Float` | The angle in degrees that indicate the vertical head direction. See [Head Movements](#headmovements) |
| headEulerAngleY | `Float` | The angle in degrees that indicate the horizontal head direction. See [Head Movements](#headmovements) |
| headEulerAngleZ | `Float` | The angle in degrees that indicate the tilt head direction. See [Head Movements](#headmovements) |

#### Head Movements

Here we explaining the above gif and how reached the "results". Each "movement" (vertical, horizontal and tilt) is a state, based in the angle in degrees that indicate head direction;

| Head Direction | Attribute | _v_ < -36° | -36° < _v_ < -12° | -12° < _v_ < 12° | 12° < _v_ < 36° | 36° < _v_ |
| - | - | - | - | - | - | - |
| Vertical | `headEulerAngleX` | Super Down | Down | Frontal | Up | Super Up |
| Horizontal | `headEulerAngleY` | Super Right | Right | Frontal | Left | Super Left |
| Tilt | `headEulerAngleZ` | Super Left | Left | Frontal | Right | Super Right |

### KeyError

Expand Down Expand Up @@ -198,8 +261,14 @@ Pre-define message constants used by the `onMessage` event.

Clone the repo, change what you want and send PR.

For commit messages we use <a href="https://www.conventionalcommits.org/">Conventional Commits</a>.

Contributions are always welcome!

<a href="https://github.com/Yoonit-Labs/android-yoonit-camera/graphs/contributors">
<img src="https://contrib.rocks/image?repo=Yoonit-Labs/android-yoonit-camera" />
</a>

---

Code with ❤ by the [**Cyberlabs AI**](https://cyberlabs.ai/) Front-End Team
27 changes: 23 additions & 4 deletions app/src/main/java/ai/cyberlabs/yoonit/camerademo/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,13 @@ class MainActivity : AppCompatActivity() {
}

"face" -> {

this.cameraView.startCaptureType("face")
this.image_preview.visibility = View.VISIBLE
this.info_textview.visibility = View.VISIBLE
this.qrcode_textview.visibility = View.INVISIBLE
}

"frame" -> {

this.cameraView.startCaptureType("frame")
this.image_preview.visibility = View.VISIBLE
this.info_textview.visibility = View.VISIBLE
Expand Down Expand Up @@ -327,8 +325,29 @@ class MainActivity : AppCompatActivity() {
image_preview.visibility = View.VISIBLE
}

override fun onFaceDetected(x: Int, y: Int, width: Int, height: Int) {
Log.d(TAG, "onFaceDetected $x, $y, $width, $height.")
override fun onFaceDetected(
x: Int,
y: Int,
width: Int,
height: Int,
leftEyeOpenProbability: Float?,
rightEyeOpenProbability: Float?,
smilingProbability: Float?,
headEulerAngleX: Float,
headEulerAngleY: Float,
headEulerAngleZ: Float
) {
Log.d(
TAG,
"onFaceDetected: \n" +
"x: $x, y: $y, w: $width, h: $height. \n" +
"leftEye: $leftEyeOpenProbability \n" +
"rightEye: $leftEyeOpenProbability \n" +
"smilling: $smilingProbability \n" +
"head X angle: $headEulerAngleX \n" +
"head Y angle: $headEulerAngleY \n" +
"head Z angle: $headEulerAngleZ"
)
}

override fun onFaceUndetected() {
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}

Expand Down
3 changes: 3 additions & 0 deletions yoonit-camera/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ dependencies {
implementation "androidx.camera:camera-lifecycle:$camerax_version"
implementation "androidx.camera:camera-view:1.0.0-alpha15"

// Facefy
implementation 'com.github.Yoonit-Labs:android-yoonit-facefy:1.0.2'

// MLKit
implementation "com.google.mlkit:barcode-scanning:16.0.3"
implementation "com.google.mlkit:face-detection:16.0.2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,14 @@ class CameraGraphicView constructor(
private fun drawFaceContours(canvas: Canvas) {
this.faceContours?.let { faceContours ->
faceContours.forEach { contour ->
canvas.drawPoint(contour.x, contour.y, Paint().apply {
this.color = CaptureOptions.faceContoursColor
this.style = Paint.Style.STROKE
this.strokeWidth = 5.0f
})
canvas.drawPoint(
contour.x, contour.y,
Paint().apply {
this.color = CaptureOptions.faceContoursColor
this.style = Paint.Style.STROKE
this.strokeWidth = 5.0f
}
)
}
}
}
Expand Down Expand Up @@ -188,12 +191,15 @@ class CameraGraphicView constructor(

val offsetAreaPaint = Paint()
offsetAreaPaint.color = CaptureOptions.faceROI.areaOffsetColor
areaCanvas.drawRect(Rect(
0,
0,
width,
height
), offsetAreaPaint)
areaCanvas.drawRect(
Rect(
0,
0,
width,
height
),
offsetAreaPaint
)

offsetAreaPaint.color = Color.TRANSPARENT
offsetAreaPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ open class CameraView @JvmOverloads constructor(
if (cameraLens == "front") CameraSelector.LENS_FACING_FRONT
else CameraSelector.LENS_FACING_BACK


if (CaptureOptions.cameraLens != cameraSelector) {
this.cameraController.toggleCameraLens()
}
Expand Down Expand Up @@ -482,14 +481,6 @@ open class CameraView @JvmOverloads constructor(
CaptureOptions.computerVision.clear()
}

fun flipScreen() {
CaptureOptions.isScreenFlipped = !CaptureOptions.isScreenFlipped

this.rotation =
if (CaptureOptions.isScreenFlipped) 180f
else 0f
}

companion object {
private const val TAG = "CameraView"
}
Expand Down
Loading

0 comments on commit 5e6536c

Please sign in to comment.