class CameraXActivity : AppCompatActivity() {
// Çok fazla istek olursa, isteklerin karışmasını engellemek için kullanılır
private const val REQUEST_CODE_PERMISSIONS = 10
// Kamera için gereken izinler
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
override fun onCreate(savedInstanceState: Bundle?) {
// ...
// Kamera izinleri alındysa işlemleri yapma
if (allPermissionsGranted()) {
// Layouta kamerayı ekleme
pvCameraX.post { startCamera() }
} else {
ActivityCompat.requestPermissions(
this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
}
/**
* İzin alındıysa aktiviteyi açma ve preview'i başlatma
* İzin alınmadıysa bildirim gösterip, activityi kapatma
*/
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
viewFinder.post { startCamera() }
} else {
Toast.makeText(this,
"Permissions not granted by the user.",
Toast.LENGTH_SHORT).show()
finish()
}
}
}
/**
* Kamera için gereken tüm izinleri kontrol etme
*/
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(
baseContext, it) == PackageManager.PERMISSION_GRANTED
}
private fun startCamera() {
// TODO: CameraX işlemleri eklenecek
}
}
class CameraXActivity : AppCompatActivity() {
private lateinit var cameraProviderFuture:
ListenableFuture<ProcessCameraProvider>
// ...
/**
* Initialize CameraX provider
*/
private fun startCamera() {
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
// Kamera sağlayıcı ile kameranın aktif olduğundan emin oluyoruz
val cameraProvider = cameraProviderFuture.get()
// Daha kullanışlı ama daha çok memory harcar
// https://stackoverflow.com/a/28620918
pvCameraX.implementationMode = PreviewView.ImplementationMode.TEXTURE_VIEW
// Kamera ön izlemesini tanımlama
val cameraPreview = Preview.Builder().apply {
setTargetRotation(pvCameraX.display.rotation)
setTargetAspectRatio(AspectRatio.RATIO_16_9)
setTargetName("Preview")
}.build().apply { setSurfaceProvider(pvCameraX.previewSurfaceProvider) }
// Ön-arka kamera seçimini yapıyoruz
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
// Kamera kullanım durumlarını kameranın yaşam döngüsüne dahil ediyoruz
val camera = cameraProvider.bindToLifecycle(
this as LifecycleOwner, cameraSelector, cameraPreview
)
}, ContextCompat.getMainExecutor(this))
}
}
class CameraXActivity : AppCompatActivity() {
companion object {
private const val TAG = "MlkitActivity"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private const val PHOTO_EXTENSION = ".jpg"
fun getOutputDirectory(context: Context): File {
val appContext = context.applicationContext
val mediaDir = context.externalMediaDirs.firstOrNull()?.let {
File(it, appContext.resources.getString(R.string.app_name)).apply {
mkdirs()
}
}
return if (mediaDir != null && mediaDir.exists()) mediaDir
else appContext.filesDir
}
fun createFile(baseFolder: File, format: String, extension: String) =
File(
baseFolder, SimpleDateFormat(format, Locale.US)
.format(System.currentTimeMillis()) + extension
)
}
/**
* Thread Pool ~ Lib - YEmreAk, alanına bakınız
*/
private val executor = Executors.newSingleThreadExecutor()
private lateinit var cameraProviderFuture:
ListenableFuture<ProcessCameraProvider>
private lateinit var imageCapture: ImageCapture
private lateinit var outputDirectory: File
override fun onCreate(savedInstanceState: Bundle?) {
// ...
// Çekilen fotoğraların kaydedileceği yeri tanımlama
outputDirectory = getOutputDirectory(this)
// Kamera izinleri alındysa işlemleri yapma
if (allPermissionsGranted()) {
// Layouta kamerayı ekleme
pvCameraX.post { startCamera() }
ibTakePicture.setOnClickListener {
takePicture()
}
} else {
ActivityCompat.requestPermissions(
this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
}
private fun startCamera() {
// ...
cameraProviderFuture.addListener(Runnable {
// ...
imageCapture = ImageCapture.Builder().apply {
setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
}.build()
// Attach use cases to camera with the same lifecycle owner
val camera = cameraProvider.bindToLifecycle(
this as LifecycleOwner, cameraSelector, cameraPreview, imageCapture
)
// ...
}, ContextCompat.getMainExecutor(this))
}
/**
* Save image that is shown in camera preview to [outputDirectory]
*/
private fun takePicture() {
val file = createFile(
outputDirectory,
FILENAME_FORMAT,
PHOTO_EXTENSION
)
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
imageCapture.takePicture(
outputFileOptions,
executor,
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults:
ImageCapture.OutputFileResults) {
val msg = "Photo capture succeeded: ${file.absolutePath}"
pvCameraX.post {
Toast.makeText(this@MlkitActivity, msg, Toast.LENGTH_SHORT).show()
}
}
override fun onError(exception: ImageCaptureException) {
val msg = "Photo capture failed: ${file.absolutePath}"
pvCameraX.post {
Toast.makeText(this@MlkitActivity, msg, Toast.LENGTH_SHORT).show()
}
}
})
}
}
private fun startCamera() {
// ...
imageAnalysis.setAnalyzer(
executor,
ImageAnalysis.Analyzer { imageProxy ->
// Process image if exists
imageProxy.image?.let { image ->
val fvImage =
image.toFvImage(imageProxy.imageInfo.rotationDegrees, isDegree = true)
fvImage.detectFaces {
Log.i(TAG, "startCamera: Face count: ${it.size}")
}
}
// val rotationDegree = image.imageInfo.rotationDegrees
// Log.i("TEMP", "startCamera: Image received ${System.currentTimeMillis()}")
// Once the image being analyzed is closed by calling ImageProxy.close(),
// the next latest image will be delivered.
// Important: The Analyzer method implementation must call image.close()
// on received images when finished using them.
// Otherwise, new images may not be received or the camera may stall,
// depending on back pressure setting.
imageProxy.close()
})
// ...
}
/**
* Resim içerisinde bulunan yüzleri algılar, algılama tamamlandığında [onDetected] metodu
* çalışır
*/
fun FirebaseVisionImage.detectFaces(onDetected: (List<FirebaseVisionFace>) -> Unit): Task<MutableList<FirebaseVisionFace>> {
val options = FirebaseVisionFaceDetectorOptions.Builder()
.setClassificationMode(FirebaseVisionFaceDetectorOptions.ACCURATE)
.setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS)
.setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS)
.setMinFaceSize(0.15f)
.enableTracking()
.build()
val detector = FirebaseVision.getInstance().getVisionFaceDetector(options)
return detector.detectInImage(this)
.addOnSuccessListener(onDetected)
.addOnFailureListener(Throwable::printStackTrace)
}
fun Image.toFvImage(rotation: Int, isDegree: Boolean = false): FirebaseVisionImage {
return when (isDegree) {
false -> FirebaseVisionImage.fromMediaImage(this, rotation)
true -> FirebaseVisionImage.fromMediaImage(
this,
degreesToFirebaseRotation(rotation)
)
}
}
fun degreesToFirebaseRotation(degrees: Int): Int {
return when (degrees) {
0 -> FirebaseVisionImageMetadata.ROTATION_0
90 -> FirebaseVisionImageMetadata.ROTATION_90
180 -> FirebaseVisionImageMetadata.ROTATION_180
270 -> FirebaseVisionImageMetadata.ROTATION_270
else -> throw IllegalArgumentException("Rotation must be 0, 90, 180, or 270.")
}
}