- 從相簿選取一張圖片
- 通過人臉識別找到人臉,並在圖片上畫一個方塊
Face Recognizer
這一個臉部識別的流程是
- 從 Album 中取出一張照片
- 放到 FaceView 的 Canvas 上
- 通過 FaceDetector 偵測人臉
- 偵測到人臉以後,在人臉上畫一個黃色的方塊
FaceView
初始化我們的 Canvas 以及 Paint
注意:官方文件有標註 bitmap must be in 565 format.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class FaceView(context: Context, attrs: AttributeSet): View(context, attrs) { private var paint: Paint private var bitmap: Bitmap private var canvas: Canvas private val maxFaceNumbers = 25 private var numberOfFaceDetected = 0 private var faces = arrayOfNulls<FaceDetector.Face>(25) init { // bitmap val width = Resources.getSystem().displayMetrics.widthPixels bitmap = Bitmap.createBitmap(width, 800, Bitmap.Config.RGB_565) // Canvas canvas = Canvas(bitmap) canvas.drawColor(Color.GRAY) // Paint paint = Paint() paint.color = Color.YELLOW paint.style = Paint.Style.STROKE paint.strokeWidth = 4f paint.textSize = 50f } } |
setupWithImage 方法
提供一個來放入圖片的 Uri 的方法,將圖片的大小轉成和 FaceView 相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
fun setupWithImage(uri: Uri) { Log.e("FaceView","Setup With Uri $uri") try { val resolver = context.contentResolver // must be RGB_565 val options = BitmapFactory.Options() options.inPreferredConfig = Bitmap.Config.RGB_565 val image = BitmapFactory.decodeStream(resolver.openInputStream(uri), null, options) // scale the bitmap bitmap = ThumbnailUtils.extractThumbnail(image, width, height) } catch (e: Exception) { Log.e("setup with image","failed") } } |
DetectFace 方法
通過 FaceDetector 來進行人臉偵測,偵測結束後通過 invalidate() 來呼叫 onDraw 方法
1 2 3 4 5 6 7 8 9 10 11 12 |
fun detectFace() { // init FaceDetector val faceDetector = FaceDetector(bitmap.width, bitmap.height, maxFaceNumbers) faces = arrayOfNulls<FaceDetector.Face>(maxFaceNumbers) numberOfFaceDetected = faceDetector.findFaces(bitmap, faces) Toast.makeText(context,"Detected $numberOfFaceDetected faces",Toast.LENGTH_SHORT).show() // call onDraw invalidate() } |
override onDraw 方法
當觸發 onDraw 方法的時候,會去看有幾張臉被識別出來,
根據雙眼的距離來計算整張臉的位置,然後在臉上畫黃色的方塊。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.drawBitmap(bitmap, 0f, 0f , null) for(i in 0..numberOfFaceDetected){ val face = faces[i] if (face != null){ val point = PointF() face.getMidPoint(point) val eyesDistance = face.eyesDistance() canvas.drawRect( (point.x - eyesDistance).toFloat(), (point.y - eyesDistance / 2).toFloat(), (point.x + eyesDistance).toFloat(), (point.y + eyesDistance * 3 / 2).toFloat(), paint) } } } |
MainActivity
通過 Intent 讓使用者選取圖片
1 2 3 4 5 6 7 |
private fun takeImageFromAlbum() { val intent = Intent() intent.type = "image/*" intent.action = Intent.ACTION_GET_CONTENT startActivityForResult(intent, OPEN_PHOTO_FOLDER_REQUEST_CODE) } |
onActivityResult
通過 intent 來接收圖片的 Uri 然後丟給 FaceView 呈現在畫面上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) println("onActivityResult requestCode is $requestCode") when(requestCode){ OPEN_PHOTO_FOLDER_REQUEST_CODE -> { val imageUri = data?.data println("imageUri is $imageUri") if(imageUri != null){ faceView.setupWithImage(imageUri) } else { Toast.makeText(this,"get image Uri failed", Toast.LENGTH_SHORT).show() } } else -> { println("no handler on ActivityResult, resultcode is $resultCode") } } } |
detectFace 方法
在通過剛才 FaceView 提供的 detectFace 方法進行臉部識別和畫方塊的功能。
1 |
faceView.detectFace() |
筆記
- 研究:原本無法識別的臉部圖片,我通過工具轉成 jpg 就可以識別了,這是為什麼?
參考
- 官方文件 – FaceDetector
- 可以到 Github 上看對應的 Source Code