はじめに
ケアプラン作成は、いまだにエクセル使用している施設ケアマネジャーです。
画像中に人物が一人であるときに、その人物の服の色を判別できるところまでできたので、今回はカメラで映像を取得します。
課題
カメラ映像からビットマップを取得したい。ただし、保存はしない。
方法
Camerax を用いて、Bitmap を取得する。
結果
カメラからビットマップを取得できた。
今後の課題
Camerax の画像解析ユースケースを用いて、一人歩きの利用者を発見すること。
コード等
build.gradle.kts(.app)
build.gradle.kts(.app)
android {
...
buildFeatures {
viewBinding = true
}
}
dependencies {
...
val camerax_version = "1.5.0-alpha03"
implementation("androidx.camera:camera-core:${camerax_version}")
implementation("androidx.camera:camera-camera2:${camerax_version}")
implementation("androidx.camera:camera-lifecycle:${camerax_version}")
...
}
AndroidManifest.xml
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA"/>
<application
...
activity_main.xml
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_marginStart="40dp"
android:layout_marginTop="44dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="128dp"
android:layout_marginTop="88dp"
android:text="Button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
MainActivity.kt
package yourpackagename
import android.Manifest
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Matrix
import android.os.Build
import android.os.Bundle
import android.util.Size
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888
import androidx.camera.core.ImageProxy
import androidx.camera.core.resolutionselector.ResolutionSelector
import androidx.camera.core.resolutionselector.ResolutionStrategy
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import kotlin.coroutines.resume
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resumeWithException
import com.android.example.yourappname.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var viewBinding: ActivityMainBinding
private var cameraProvider: ProcessCameraProvider? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
// Request camera permissions
if (allPermissionsGranted()) {
// startCamera()
} else {
ActivityCompat.requestPermissions(
this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
viewBinding.button.setOnClickListener{ doJudge() }
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults:
IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
// startCamera()
} else {
Toast.makeText(this,
"Permissions not granted by the user.",
Toast.LENGTH_SHORT).show()
finish()
}
}
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
baseContext, it) == PackageManager.PERMISSION_GRANTED
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
companion object {
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS =
mutableListOf(
Manifest.permission.CAMERA,
).apply {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
// add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}.toTypedArray()
}
private val cameraExecutor by lazy {
Executors.newSingleThreadExecutor()
}
private val imageAnalyzer by lazy {
ImageAnalysis.Builder()
//.setOutputImageFormat(OUTPUT_IMAGE_FORMAT_YUV_420_888)//https://developer.android.com/reference/kotlin/androidx/camera/core/ImageAnalysis#OUTPUT_IMAGE_FORMAT_RGBA_8888()
.setOutputImageFormat(OUTPUT_IMAGE_FORMAT_RGBA_8888)//https://developer.android.com/reference/kotlin/androidx/camera/core/ImageAnalysis#OUTPUT_IMAGE_FORMAT_RGBA_8888()
//.setTargetResolution(Size(640, 480))//'setTargetResolution(Size): ImageAnalysis.Builder' is deprecated. Deprecated in Java
.setResolutionSelector(
ResolutionSelector.Builder()
.setResolutionStrategy(ResolutionStrategy(Size(640, 480), ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER))
.build()
)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build()
}
private suspend fun setAnalyzer(executor: Executor): ImageProxy {
return suspendCancellableCoroutine { continuation ->
try {
if (continuation.isActive) {
imageAnalyzer.setAnalyzer(executor) { image ->
continuation.resume(image)
}
}
} catch (exception: Exception) {
if (continuation.isActive) {
continuation.resumeWithException(exception)
}
}
}
}
private fun doJudge() {
var resultImage: ImageProxy? = null
lifecycleScope.launch {
try {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this@MainActivity)
cameraProvider = cameraProviderFuture.get()
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_FRONT)
.build()
cameraProvider?.bindToLifecycle(this@MainActivity, cameraSelector, imageAnalyzer)
// ここで取れた resultImage の中に画像データが入っている
resultImage = setAnalyzer(cameraExecutor)
val bitmap = imageProxyToBitmap(resultImage)
val rotation = resultImage!!.imageInfo.rotationDegrees
val rotateBitmap = rotateImage(bitmap, rotation)//上下を整える
val mirroredBitmap = mirrorImageBitmap(rotateBitmap)//鏡像にする
viewBinding.imageView.setImageBitmap(mirroredBitmap)
resultImage!!.close()
resultImage = null
cameraProvider?.unbindAll()
cameraProvider = null
} catch (e: Exception) {
//
} finally {
cameraProvider?.unbindAll()
cameraProvider = null
resultImage?.close()
}
}
}
private fun rotateImage(bitmap: Bitmap, degree: Int): Bitmap {
val matrix = Matrix()
matrix.postRotate(degree.toFloat())
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
private fun mirrorImageBitmap(bitmap: Bitmap) : Bitmap{
val matrix = Matrix()
matrix.preScale(-1f, 1f)
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
private fun imageProxyToBitmap(image: ImageProxy?): Bitmap {
val planes: Array<ImageProxy.PlaneProxy> = image!!.planes
val buffer = planes[0].buffer
val bitmap = Bitmap.createBitmap(image.width, image.height, Bitmap.Config.ARGB_8888)
bitmap.copyPixelsFromBuffer(buffer)
return bitmap
}
}
参考