Android 无法检测面,返回矩形大小始终为1x0
我有以下Android代码:Android 无法检测面,返回矩形大小始终为1x0,android,opencv,opencv4android,opencv4,android-opencv,Android,Opencv,Opencv4android,Opencv4,Android Opencv,我有以下Android代码: class MainActivity : AppCompatActivity(), TtsSpeaker.Listener, PocketSphinx.Listener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)
class MainActivity : AppCompatActivity(), TtsSpeaker.Listener, PocketSphinx.Listener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val ocvLoaded = OpenCVLoader.initDebug()
if (ocvLoaded) {
loadModel(this) // to be sure model in sot alled before initiation openCV
} else {
Log.d("openCV", "loader: ${OpenCVLoader.initDebug()}")
}
}
}
并使用以下人脸检测类别:
package hasan.tts_mobile
import android.app.Activity
import android.graphics.Bitmap
import org.opencv.core.Mat
import org.opencv.core.MatOfRect
import org.opencv.core.Size
import org.opencv.imgproc.Imgproc
import org.opencv.objdetect.CascadeClassifier
import java.io.File
import org.opencv.core.CvType
import android.opengl.ETC1.getWidth
import android.opengl.ETC1.getHeight
object FaceDetection {
private val faceModel = "haarcascade_frontalface_default.xml" //lateinit var faceModel: String // = "haarcascade_frontalface_default.xml"
private lateinit var faceCascade: CascadeClassifier
fun loadModel(activity: Activity) {
println("started loading the model")
faceCascade = CascadeClassifier(File(activity.filesDir, "das").apply {
writeBytes(activity.assets.open(faceModel).readBytes())
}.path)
println("completed loading the model")
tts!!.say("I'm 100% ready!")
}
fun detectFaces(activity: Activity, image: Bitmap?): Long {
// bitmap
val matImage = Mat(image!!.height, image.width, CvType.CV_8UC1)
val bmpImage = image.copy(Bitmap.Config.ARGB_8888, true)
Utils.bitmapToMat(bmpImage, matImage)
val rectangles = MatOfRect() //RectVector() //MatOfRect()
val grayScaled = matImage .prepare()
// loadModel(activity)
faceCascade.detectMultiScale(
grayScaled, rectangles, 1.2, 10, 0,
Size(40.0, 40.0),
null)
return rectangles.size() as Long
}
private fun Mat.toGrayScale(): Mat =
if (channels() >= 3) Mat().apply {
Imgproc.cvtColor(
this@toGrayScale,
this,
Imgproc.COLOR_BGR2GRAY
)
}
else this
private fun Mat.prepare(): Mat {
val mat = toGrayScale()
Imgproc.equalizeHist(mat, mat)
return mat
}
}
调用人脸检测功能是在使用以下代码成功从相机获取pic后发生的:
private fun startCamera() {
val fileName = System.currentTimeMillis().toString() + ".jpeg"
output = File(
this.getExternalFilesDir(Environment.DIRECTORY_PICTURES),
fileName
)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
outPutFileUri = this?.let { it1 ->
FileProvider.getUriForFile(
it1,
BuildConfig.APPLICATION_ID,
output!!
)
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, outPutFileUri)
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE)
}
@SuppressLint("MissingSuperCall")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
val bitmap = outPutFileUri?.let { getCapturedImage(it) }
imageView.setImageBitmap(bitmap)
FaceDetection.detectFaces(this, bitmap) // Calling facedetection
}
}
private fun getCapturedImage(selectedPhotoUri: Uri): Bitmap {
val bitmap = when {
Build.VERSION.SDK_INT < 28 -> MediaStore.Images.Media.getBitmap(
this.contentResolver,
selectedPhotoUri
)
else -> {
val source = ImageDecoder.createSource(this.contentResolver, selectedPhotoUri)
ImageDecoder.decodeBitmap(source)
}
}
return when (ExifInterface(contentResolver.run { openInputStream(selectedPhotoUri) }).getAttributeInt(
ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
ExifInterface.ORIENTATION_ROTATE_90 -> Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply { postRotate(90F) }, true)
ExifInterface.ORIENTATION_ROTATE_180 -> Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply { postRotate(180F) }, true)
ExifInterface.ORIENTATION_ROTATE_270 -> Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply { postRotate(270F) }, true)
else -> bitmap
}
}
我补充说
以前
faceCascade.detectMultiScale(
grayScaled, rectangles, 1.2, 10, 0,
Size(40.0, 40.0),
null)
并获得以下信息:
I/System.out:pic大小:960x1280
faceCascade:24x24
D/AndroidRuntime:关闭虚拟机
E/AndroidRuntime:致命异常:主
进程:hasan.tts_mobile,PID:15177
更新
看起来问题在于我将maxSize
定义为null
,它应该是Size()
或全尺寸为:Size(40.0,40.0)
现在我将其更改为:
faceCascade.detectMultiScale(
grayScaled, rectangles, 1.1, 3, 0,
Size(30.0, 30.0), Size()
)
println("rectangles ${rectangles.size()}")
我不是挂起或崩溃,而是将矩形.size()
返回为:
I/System.out:pic大小:3264x2448
faceCascade:24x24
I/System.out:矩形1x0
这是否意味着它没有检测到任何人脸,如果是,如何修复它?我发现,人脸检测过程的结果是在过程本身完成之前读取的,因此解决方案是使用
async
调用,在Kotlin中,这可以通过使用coroutine
来完成
在Kotlin中有coroutine core
和coroutine android
,因此我应该小心选择正确的一个,即:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.1'
在Kotlin合作项目中,有:
suspend fun mySuspendMethodThatWaitsForChildCoroutinesToFinish() {
coroutineScope {
launch { mySyspendMethod() }
}
}
// or
suspend fun mySuspendMethodThatWaitsForChildCoroutinesToFinish() = coroutineScope {
launch { mySyspendMethod() }
}
但这将继续要求在所有层次结构中传播暂停,并且在到达拍照后触发的onActivityResult
时将被卡住,因此解决方案是使用MainScope()协同程序
,即允许从非暂停
函数使用协同程序
,您可以:
1-将coroutine作用域声明为val scope=MainScope()
2-执行coroutine
作为scope.launch{}
全文如下:
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
private val scope = MainScope()
override fun onCreate(savedInstanceState: Bundle?) { }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
detectFace(this, image, textView)
}
}
private fun detectFace(context: Context, image: Bitmap, facesValue: TextView) {
val frame = AndroidFrameConverter().convert(image)
val mat = OpenCVFrameConverter.ToMat().convert(frame)
scope.launch {
val numberOfFaces = FaceDetection.detectFaces(mat).toString()
(context as Activity).runOnUiThread {
facesValue.text = numberOfFaces
}
}
}
因此,我的main活动也不是:
class MainActivity : AppCompatActivity() {
private val scope = MainScope()
val REQUEST_IMAGE_CAPTURE = 1
private var output: File? = null
var outPutFileUri: Uri? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (!isPermissionGranted(permission.RECORD_AUDIO)) {
requestAudioPermission(this)
} else {
Toast.makeText(
this@MainActivity, "Audio permission is granted",
Toast.LENGTH_SHORT
).show()
}
val ocvLoaded = OpenCVLoader.initDebug()
if (ocvLoaded) {
loadModel(this)
Toast.makeText(
this@MainActivity, "OpenCV loaded",
Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
this@MainActivity, "Unable to load OpenCV",
Toast.LENGTH_SHORT
).show()
Log.d("openCV", "loader: ${OpenCVLoader.initDebug()}")
}
btnCamera.setOnClickListener {
if(isPermissionGranted(permission.CAMERA)) startCamera()
else requestCameraPermission(this)
}
gryImage.setOnClickListener {
val bitmap = outPutFileUri?.let { getCapturedImage(it) }
val matImage = Mat(bitmap!!.height, bitmap.width, CvType.CV_8UC1)
val bmpImage = bitmap.copy(Bitmap.Config.ARGB_8888, true)
Utils.bitmapToMat(bmpImage, matImage)
val bmp = Bitmap.createBitmap(matImage.cols(), matImage.rows(), Bitmap.Config.ARGB_8888)
Imgproc.cvtColor(matImage, matImage, Imgproc.COLOR_RGB2GRAY)
Utils.matToBitmap(matImage, bmp)
imageView.setImageBitmap(bmp)
}
}
private fun startCamera() {
val fileName = System.currentTimeMillis().toString() + ".jpeg"
output = File(
this.getExternalFilesDir(Environment.DIRECTORY_PICTURES),
fileName
)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
outPutFileUri = this.let { it ->
FileProvider.getUriForFile(
it,
BuildConfig.APPLICATION_ID,
output!!
)
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, outPutFileUri)
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val activity = this
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
val bitmap = outPutFileUri?.let { getCapturedImage(it) }
imageView.setImageBitmap(bitmap)
outPutFileUri?.let {
scope.launch {
val detectedFaces = FaceDetection.detectFaces(bitmap)
println("Detected Faces = $detectedFaces")
Toast.makeText(
this@MainActivity, "Detected Faces = $detectedFaces",
Toast.LENGTH_SHORT
).show()
}
}
}
}
private fun getCapturedImage(selectedPhotoUri: Uri): Bitmap {
return when {
Build.VERSION.SDK_INT < 28 -> MediaStore.Images.Media.getBitmap(
contentResolver,
selectedPhotoUri
)
else -> {
val source = ImageDecoder.createSource(contentResolver, selectedPhotoUri)
ImageDecoder.decodeBitmap(source)
}
}
// If the image is rotated, fix it
/* return when (ExifInterface(contentResolver.run { openInputStream(selectedPhotoUri) }).getAttributeInt(
ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
ExifInterface.ORIENTATION_ROTATE_90 ->
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
postRotate(90F) }, true)
ExifInterface.ORIENTATION_ROTATE_180 ->
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
postRotate(180F) }, true)
ExifInterface.ORIENTATION_ROTATE_270 ->
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
postRotate(270F) }, true)
else -> bitmap
} */
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>,grantResults: IntArray) =
onPermissionsRequestResult(this@MainActivity,
requestCode, permissions, grantResults)
}
faceModel
不是字符串
。它是一个XmlResourceParser
,如下所示。
class MainActivity : AppCompatActivity() {
private val scope = MainScope()
val REQUEST_IMAGE_CAPTURE = 1
private var output: File? = null
var outPutFileUri: Uri? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (!isPermissionGranted(permission.RECORD_AUDIO)) {
requestAudioPermission(this)
} else {
Toast.makeText(
this@MainActivity, "Audio permission is granted",
Toast.LENGTH_SHORT
).show()
}
val ocvLoaded = OpenCVLoader.initDebug()
if (ocvLoaded) {
loadModel(this)
Toast.makeText(
this@MainActivity, "OpenCV loaded",
Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
this@MainActivity, "Unable to load OpenCV",
Toast.LENGTH_SHORT
).show()
Log.d("openCV", "loader: ${OpenCVLoader.initDebug()}")
}
btnCamera.setOnClickListener {
if(isPermissionGranted(permission.CAMERA)) startCamera()
else requestCameraPermission(this)
}
gryImage.setOnClickListener {
val bitmap = outPutFileUri?.let { getCapturedImage(it) }
val matImage = Mat(bitmap!!.height, bitmap.width, CvType.CV_8UC1)
val bmpImage = bitmap.copy(Bitmap.Config.ARGB_8888, true)
Utils.bitmapToMat(bmpImage, matImage)
val bmp = Bitmap.createBitmap(matImage.cols(), matImage.rows(), Bitmap.Config.ARGB_8888)
Imgproc.cvtColor(matImage, matImage, Imgproc.COLOR_RGB2GRAY)
Utils.matToBitmap(matImage, bmp)
imageView.setImageBitmap(bmp)
}
}
private fun startCamera() {
val fileName = System.currentTimeMillis().toString() + ".jpeg"
output = File(
this.getExternalFilesDir(Environment.DIRECTORY_PICTURES),
fileName
)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
outPutFileUri = this.let { it ->
FileProvider.getUriForFile(
it,
BuildConfig.APPLICATION_ID,
output!!
)
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, outPutFileUri)
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val activity = this
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
val bitmap = outPutFileUri?.let { getCapturedImage(it) }
imageView.setImageBitmap(bitmap)
outPutFileUri?.let {
scope.launch {
val detectedFaces = FaceDetection.detectFaces(bitmap)
println("Detected Faces = $detectedFaces")
Toast.makeText(
this@MainActivity, "Detected Faces = $detectedFaces",
Toast.LENGTH_SHORT
).show()
}
}
}
}
private fun getCapturedImage(selectedPhotoUri: Uri): Bitmap {
return when {
Build.VERSION.SDK_INT < 28 -> MediaStore.Images.Media.getBitmap(
contentResolver,
selectedPhotoUri
)
else -> {
val source = ImageDecoder.createSource(contentResolver, selectedPhotoUri)
ImageDecoder.decodeBitmap(source)
}
}
// If the image is rotated, fix it
/* return when (ExifInterface(contentResolver.run { openInputStream(selectedPhotoUri) }).getAttributeInt(
ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) {
ExifInterface.ORIENTATION_ROTATE_90 ->
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
postRotate(90F) }, true)
ExifInterface.ORIENTATION_ROTATE_180 ->
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
postRotate(180F) }, true)
ExifInterface.ORIENTATION_ROTATE_270 ->
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix().apply {
postRotate(270F) }, true)
else -> bitmap
} */
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>,grantResults: IntArray) =
onPermissionsRequestResult(this@MainActivity,
requestCode, permissions, grantResults)
}
object FaceDetection {
private const val faceModel = "haarcascades/haarcascade_frontalface_default.xml" // main/assets/haarcascades
private lateinit var faceCascade: CascadeClassifier
fun loadModel(activity: Activity) {
faceCascade = CascadeClassifier(File(activity.filesDir, "das").apply {
writeBytes(activity.assets.open(faceModel).readBytes())
}.path))
}
fun detectFaces(image: Bitmap?): Int {
val matImage = Mat(image!!.height, image.width, CvType.CV_8UC1) // CV_8UC1 is gray scale image
val bmpImage = image.copy(Bitmap.Config.ARGB_8888, true)
Utils.bitmapToMat(bmpImage, matImage)
val faceDetections = MatOfRect()
val grayScaled = matImage.prepare()
faceCascade.detectMultiScale(matImage, faceDetections, 1.1, 7, 0,
Size(250.0, 40.0), Size())
// process faces found
for (rect in faceDetections.toArray()) {
println("face found")
// Imgproc.rectangle(
// image,
// Point(rect.x, rect.y),
// Point(rect.x + rect.width, rect.y + rect.height),
// Scalar(0.0, 255.0, 0.0)
// )
}
return faceDetections.toArray().size
}
private fun Mat.toGrayScale(): Mat =
if (channels() >= 3) Mat().apply {
Imgproc.cvtColor(
this@toGrayScale,
this,
Imgproc.COLOR_BGR2GRAY
)
}
else this
private fun Mat.prepare(): Mat {
val mat = toGrayScale()
Imgproc.equalizeHist(mat, mat)
return mat
}
}