Skip to content

Commit

Permalink
Merge pull request #65 from Yoonit-Labs/feature/computer-vision
Browse files Browse the repository at this point in the history
Computer Vision models usage
  • Loading branch information
TeruyaHaroldo authored Sep 19, 2021
2 parents 03857af + 13abcf8 commit e85473b
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 83 deletions.
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# android-yoonit-camera

![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)
![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 release (latest by date)](https://img.shields.io/github/v/release/Yoonit-Labs/android-yoonit-facefy?color=lightgrey&label=Facefy&style=for-the-badge) ![GitHub](https://img.shields.io/github/license/Yoonit-Labs/android-yoonit-camera?color=lightgrey&style=for-the-badge) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/Yoonit-Labs/android-yoonit-facefy?color=lightgrey&label=Facefy&style=for-the-badge)

A Android plugin to provide:
* Modern Android Camera API [Camera X](https://developer.android.com/training/camerax)
Expand All @@ -16,6 +16,8 @@ A Android plugin to provide:
* Capture timed images
* QR Code scanning

<img src="https://raw.githubusercontent.com/Yoonit-Labs/android-yoonit-camera/development/facefy.gif" width="300" />

## Table of Contents

* [Installation](#installation)
Expand Down Expand Up @@ -152,12 +154,15 @@ fun buildCameraEventListener(): CameraEventListener = object : CameraEventListen

### Variables

| Variable | Type | Default Value | Description
| - | - | - | -
| detectionTopSize | Float | '0.0f' | Represents the percentage. Positive value enlarges and negative value reduce the top side of the detection. Use the `setDetectionBox` to have a visual result.
| detectionRightSize | Float | '0.0f' | Represents the percentage. Positive value enlarges and negative value reduce the right side of the detection. Use the `setDetectionBox` to have a visual result.
| detectionBottomSize | Float | '0.0f' | Represents the percentage. Positive value enlarges and negative value reduce the bottom side of the detection. Use the `setDetectionBox` to have a visual result.
| detectionLeftSize | Float | '0.0f' | Represents the percentage. Positive value enlarges and negative value reduce the left side of the detection. Use the `setDetectionBox` to have a visual result.
| Variable | Type | Default Value | Description
| - | - | - | -
| detectionTopSize | Float | '0.0f' | Represents the percentage. Positive value enlarges and negative value reduce the top side of the detection. Use the `setDetectionBox` to have a visual result.
| detectionRightSize | Float | '0.0f' | Represents the percentage. Positive value enlarges and negative value reduce the right side of the detection. Use the `setDetectionBox` to have a visual result.
| detectionBottomSize | Float | '0.0f' | Represents the percentage. Positive value enlarges and negative value reduce the bottom side of the detection. Use the `setDetectionBox` to have a visual result.
| detectionLeftSize | Float | '0.0f' | Represents the percentage. Positive value enlarges and negative value reduce the left side of the detection. Use the `setDetectionBox` to have a visual result.
| ComputerVision.enable | Boolean | `false | Enable/disable computer vision usage.
| ComputerVision.modelPaths | ArrayList<String> | `[]` | The computer vision model paths.
| ComputerVision.inputSize | Size | `(0, 0)` | Image input size to use the loaded model paths.

### Methods

Expand Down Expand Up @@ -190,10 +195,8 @@ fun buildCameraEventListener(): CameraEventListener = object : CameraEventListen
| setROIAreaOffsetColor | `alpha: Int, red: Int, green: Int, blue: Int` | Any positive integer between 0 and 255. | void | Set face region of interest area offset color. Default value is `(100, 255, 255, 255)`.
| setBlurFaceDetectionBox | `enable: Boolean` | `true` or `false`. | void | Enable/disable blur in face detection box.
| setColorEncodingCapture | `colorEncoding: String` | <ul><li>`"RGB"`</li><li>`"YUV"`</li> | void | Set the color encoding for the saved images.
| setComputerVision | `enable: Boolean` | `true` or `false`. | void | Enable/disable computer vision usage.
| setComputerVisionLoadModels | `modelPaths: ArrayList<String>` | Must exist all model files paths. | void | Set the computer vision model files paths to load.
| computerVisionClearModels | - | - | void | Clear loaded computer vision models.
| setTorch | `enable: Boolean` | `true` or `false`. | void | Set to enable/disable the device torch. Available only to camera lens `"back"`.
| ComputerVision.clear | - | - | void | Clear computer vision model paths.

### Events

Expand Down Expand Up @@ -289,7 +292,6 @@ Pre-define message constants used by the `onMessage` event.
## To contribute and make it better

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!
Expand Down
12 changes: 7 additions & 5 deletions app/src/main/java/ai/cyberlabs/yoonit/camerademo/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.os.Bundle
import android.util.Log
import android.util.Size
import android.view.View
import android.widget.RadioButton
import android.widget.Toast
Expand Down Expand Up @@ -77,14 +78,15 @@ class MainActivity : AppCompatActivity() {
this.cameraView.setROITopOffset(0.1f)
this.cameraView.setROIBottomOffset(0.1f)
this.cameraView.setSaveImageCaptured(true)
this.cameraView.setComputerVision(true)
this.cameraView.ComputerVision.enable = true
this.cameraView.ComputerVision.inputSize = Size(28, 28)
this.cameraView.setDetectionBox(true)
this.cameraView.startPreview()
this.captureType = "face"

this.cameraView.setComputerVisionLoadModels(arrayListOf(
this.cameraView.ComputerVision.modelPaths = arrayListOf(
this.getModelPath("mask_custom_model.pt")
))
)

return
}
Expand All @@ -99,7 +101,7 @@ class MainActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()

this.cameraView.computerVisionClearModels()
this.cameraView.ComputerVision.clear()
}

private fun getModelPath(assetName: String): String {
Expand Down Expand Up @@ -287,7 +289,7 @@ class MainActivity : AppCompatActivity() {
fun onComputerSwitchSwitchClick(view: View) {
if (view is SwitchCompat) {
val checked = view.isChecked
this.cameraView.setComputerVision(checked)
this.cameraView.ComputerVision.enable = checked
}
}

Expand Down
Binary file added facefy.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ class CameraGraphicView constructor(
!CaptureOptions.detectionBox &&
!CaptureOptions.blurFaceDetectionBox &&
!CaptureOptions.faceContours &&
!CaptureOptions.roi.enable &&
!CaptureOptions.roi.areaOffsetEnable
!CaptureOptions.ROI.enable &&
!CaptureOptions.ROI.areaOffsetEnable
) {
return
}
Expand All @@ -63,8 +63,8 @@ class CameraGraphicView constructor(

// Draw face/qrcode region of interest area offset bitmap.
if (
CaptureOptions.roi.enable &&
CaptureOptions.roi.areaOffsetEnable
CaptureOptions.ROI.enable &&
CaptureOptions.ROI.areaOffsetEnable
) {
this.drawROIAreaOffset(canvas)
}
Expand Down Expand Up @@ -155,10 +155,10 @@ class CameraGraphicView constructor(
}

val roi = Rect(
(width * CaptureOptions.roi.leftOffset).toInt(),
(height * CaptureOptions.roi.topOffset).toInt(),
(width - (width * CaptureOptions.roi.rightOffset)).toInt(),
(height - (height * CaptureOptions.roi.bottomOffset)).toInt()
(width * CaptureOptions.ROI.leftOffset).toInt(),
(height * CaptureOptions.ROI.topOffset).toInt(),
(width - (width * CaptureOptions.ROI.rightOffset)).toInt(),
(height - (height * CaptureOptions.ROI.bottomOffset)).toInt()
)

val roiAreaOffsetBitmap: Bitmap = Bitmap.createBitmap(
Expand All @@ -170,7 +170,7 @@ class CameraGraphicView constructor(
val areaCanvas = Canvas(roiAreaOffsetBitmap)

val offsetAreaPaint = Paint()
offsetAreaPaint.color = CaptureOptions.roi.areaOffsetColor
offsetAreaPaint.color = CaptureOptions.ROI.areaOffsetColor
areaCanvas.drawRect(
Rect(
0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@ import ai.cyberlabs.yoonit.camera.interfaces.CameraEventListener
import ai.cyberlabs.yoonit.camera.models.CaptureOptions
import android.content.Context
import android.graphics.Color
import android.graphics.RectF
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import androidx.camera.core.CameraSelector
import kotlinx.android.synthetic.main.cameraview_layout.view.*
import java.io.File

/**
* This class represents the camera layout.
Expand All @@ -40,6 +38,8 @@ open class CameraView @JvmOverloads constructor(
// Camera interface event listeners object.
private var cameraEventListener: CameraEventListener? = null

var ComputerVision = ComputerVision()

/**
* Inflate CameraView layout and instantiate [CameraController].
*/
Expand Down Expand Up @@ -406,7 +406,7 @@ open class CameraView @JvmOverloads constructor(
* Default value is `false`.
*/
fun setROI(enable: Boolean) {
CaptureOptions.roi.enable = enable
CaptureOptions.ROI.enable = enable
}

/**
Expand All @@ -420,7 +420,7 @@ open class CameraView @JvmOverloads constructor(
throw IllegalArgumentException(KeyError.INVALID_ROI_TOP_OFFSET)
}

CaptureOptions.roi.topOffset = topOffset
CaptureOptions.ROI.topOffset = topOffset
}

/**
Expand All @@ -434,7 +434,7 @@ open class CameraView @JvmOverloads constructor(
throw IllegalArgumentException(KeyError.INVALID_ROI_RIGHT_OFFSET)
}

CaptureOptions.roi.rightOffset = rightOffset
CaptureOptions.ROI.rightOffset = rightOffset
}

/**
Expand All @@ -448,7 +448,7 @@ open class CameraView @JvmOverloads constructor(
throw IllegalArgumentException(KeyError.INVALID_ROI_BOTTOM_OFFSET)
}

CaptureOptions.roi.bottomOffset = bottomOffset
CaptureOptions.ROI.bottomOffset = bottomOffset
}

/**
Expand All @@ -462,7 +462,7 @@ open class CameraView @JvmOverloads constructor(
throw IllegalArgumentException(KeyError.INVALID_ROI_LEFT_OFFSET)
}

CaptureOptions.roi.leftOffset = leftOffset
CaptureOptions.ROI.leftOffset = leftOffset
}

/**
Expand All @@ -472,7 +472,7 @@ open class CameraView @JvmOverloads constructor(
* Default value is `false`.
*/
fun setROIAreaOffset(enable: Boolean) {
CaptureOptions.roi.areaOffsetEnable = enable
CaptureOptions.ROI.areaOffsetEnable = enable
this.graphicView.postInvalidate()
}

Expand Down Expand Up @@ -500,7 +500,7 @@ open class CameraView @JvmOverloads constructor(
throw java.lang.IllegalArgumentException(KeyError.INVALID_ROI_COLOR)
}

CaptureOptions.roi.areaOffsetColor = Color.argb(alpha, red, green, blue)
CaptureOptions.ROI.areaOffsetColor = Color.argb(alpha, red, green, blue)
this.graphicView.postInvalidate()
}

Expand Down Expand Up @@ -528,40 +528,6 @@ open class CameraView @JvmOverloads constructor(
CaptureOptions.colorEncoding = colorEncoding
}

/**
* Enable/disable computer vision usage.
*
* @param enable The indicator to enable/disable computer vision usage.
* Default value is `false`.
*/
fun setComputerVision(enable: Boolean) {
CaptureOptions.computerVision.enable = enable
}

/**
* Set the computer vision model paths to load.
*
* @param modelPaths The computer vision absolute model file path array list.
* Default value is an empty array.
*/
fun setComputerVisionLoadModels(modelPaths: ArrayList<String>) {
modelPaths.forEach {
modelPath ->
if (!File(modelPath).exists()) {
throw IllegalArgumentException("${KeyError.INVALID_COMPUTER_VISION_MODEL_PATHS}: $modelPath")
}
}

CaptureOptions.computerVision.paths = modelPaths
}

/**
* Clear loaded computer vision models.
*/
fun computerVisionClearModels() {
CaptureOptions.computerVision.clear()
}

companion object {
private const val TAG = "CameraView"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ai.cyberlabs.yoonit.camera

import ai.cyberlabs.yoonit.camera.models.CaptureOptions
import android.util.Size
import java.io.File

class ComputerVision {

/**
* Enable/disable computer vision usage.
* Default value is `false`.
*/
var enable: Boolean = CaptureOptions.ComputerVision.enable
set(value) {
CaptureOptions.ComputerVision.enable = value
field = value
}

/**
* The computer vision model paths.
* Default value is an empty array.
*/
var modelPaths: ArrayList<String> = CaptureOptions.ComputerVision.paths
set(value) {
value.forEach {
modelPath ->
if (!File(modelPath).exists()) {
throw IllegalArgumentException("${KeyError.INVALID_COMPUTER_VISION_MODEL_PATHS}: $modelPath")
}
}

CaptureOptions.ComputerVision.paths = value
field = value
}

/**
* Image input size to use the loaded model paths.
* Default value is (0, 0).
*/
var inputSize: Size = CaptureOptions.ComputerVision.inputSize
set(value) {
CaptureOptions.ComputerVision.inputSize = value
field = value
}

/**
* Clear computer vision path models.
*/
fun clear() {
CaptureOptions.ComputerVision.clear()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,15 @@ class CoordinatesController(
return ""
}

if (CaptureOptions.roi.enable) {
if (CaptureOptions.ROI.enable) {

// Detection box offsets.
val topOffset: Float = detectionBox.top / screenHeight
val rightOffset: Float = (screenWidth - detectionBox.right) / screenWidth
val bottomOffset: Float = (screenHeight - detectionBox.bottom) / screenHeight
val leftOffset: Float = detectionBox.left / screenWidth

if (CaptureOptions.roi.isOutOf(
if (CaptureOptions.ROI.isOutOf(
topOffset,
rightOffset,
bottomOffset,
Expand All @@ -181,13 +181,13 @@ class CoordinatesController(
return Message.INVALID_OUT_OF_ROI
}

if (CaptureOptions.roi.hasChanges) {
if (CaptureOptions.ROI.hasChanges) {

// Face is inside the region of interest and faceROI is setted.
// Face is smaller than the defined "minimumSize".
val roiWidth: Float =
screenWidth -
((CaptureOptions.roi.rightOffset + CaptureOptions.roi.leftOffset) * screenWidth)
((CaptureOptions.ROI.rightOffset + CaptureOptions.ROI.leftOffset) * screenWidth)
val faceRelatedWithROI: Float = detectionBox.width() / roiWidth

if (CaptureOptions.minimumSize > faceRelatedWithROI) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ class FaceAnalyzer(

// Computer Vision Inference.
val inferences: ArrayList<android.util.Pair<String, FloatArray>> =
if (CaptureOptions.computerVision.enable)
if (CaptureOptions.ComputerVision.enable)
ComputerVisionController.getInferences(
CaptureOptions.computerVision.modelMap,
CaptureOptions.ComputerVision.modelMap,
faceBitmap
)
else arrayListOf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ class FrameAnalyzer(

// Computer Vision Inference.
var inferences: ArrayList<android.util.Pair<String, FloatArray>> = arrayListOf()
if (CaptureOptions.computerVision.enable) {
if (CaptureOptions.ComputerVision.enable) {
inferences = ComputerVisionController.getInferences(
CaptureOptions.computerVision.modelMap,
CaptureOptions.ComputerVision.modelMap,
frameBitmap
)
}
Expand Down Expand Up @@ -111,7 +111,7 @@ class FrameAnalyzer(

if (
!CaptureOptions.saveImageCaptured &&
!CaptureOptions.computerVision.enable
!CaptureOptions.ComputerVision.enable
) {
return false
}
Expand Down
Loading

0 comments on commit e85473b

Please sign in to comment.