📸 CameraX KullanımıAndroid üzerinde güncel beta sürmü olan CameraX kullanımı (👨🔬 Beta)
📦 Bağımlılıkları Dahil Etme
➕ Projenizin build.gradle
dosyasındaki dependencies
alanına alttaki implementation
bilgilerini ekleyin
📢 CameraX, java 8 kütüphanelerini de kullandığı için compileOptions
da eklenmelidir
Copy
compileOptions {
sourceCompatibility JavaVersion. VERSION_1_8
targetCompatibility JavaVersion. VERSION_1_8
}
dependencies {
// ...
implementation "androidx.camera:camera-camera2:1.0.0-beta01"
implementation "androidx.camera:camera-core:1.0.0-beta01"
implementation "androidx.camera:camera-extensions:1.0.0-alpha08"
implementation "androidx.camera:camera-lifecycle:1.0.0-beta01"
implementation "androidx.camera:camera-view:1.0.0-alpha08"
}
📃 CameraX XML Kodları
👮♂️ Buradaki XML kodları, Activity java sınıfının temsil ettiği layout dosyasına yazılmalıdır
😥 Android layout editörü PreviewView
'i henüz desteklememektedir, IDE görsel çıktı sunmaz
⭐ Alttaki fotoğrafta XML'in temsil ettiği çıktı gösterilmiştir
Copy <?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=".CameraXActivity">
<androidx.camera.view.PreviewView
android:id="@+id/pvCameraX"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/ibTakePicture"
android:layout_width="72dp"
android:layout_height="72dp"
android:layout_margin="24dp"
android:contentDescription="Take picture"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@android:drawable/ic_menu_camera" />
</androidx.constraintlayout.widget.ConstraintLayout>
👮♂️ Gerekli İzinlerin Alınması
📜 Manifest izinlerini alma
😅 Kamera ile çalışacağımızdan, haliyle kamera iznine ihtiyacımız olacaktır
📜 Android manifest dosyanıza alttaki izin satırını ekleyin
Copy <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yemreak.example">
<uses-permission android:name="android.permission.CAMERA" />
<!-- application alanı -->
</manifest>
👮♂️ Uygulama içinden izin isteme
Copy 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
}
}
👀 CameraX Ön İzlemesi
📸 Alttaki kod ile kameraya gelen görüntüyü ekrana basacağız
👮♂️ cameraProviderFuture.get()
ile kameranın olduğundan emin oluyoruz
🎳 PreviewView.ImplementationMode.TEXTURE_VIEW
animasyonları ve dönüşümleri destekler, daha fazla memory kullanır
🕊️ PreviewView.ImplementationMode.SURFACE_VIEW
daha hızlı ve basit çalışan bir yapıdır
Copy 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 ))
}
}
🧙♂ Detaylı bilgi için
alanlarına bakabilirsin.
📸 Resim Çekme Özelliği Ekleme
✨ ImageCapture
objesi oluşturup, onu kameramıza dahil edeceğiz
💠 Resmin alındığı metodu takePicture
olarak tanımlayacağız
💫 Daha önceden XML üzerinde tanımladığımız ImageButton
'a tıklandığında takePicture
metodu çalışacak
💎 Alınan resimleri kayıt edileceği yeri ayarlamak için companion object
tanımlayacağız
Copy 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 ()
}
}
})
}
}
🔥 ML Kit ile Resmi Analiz Etme
👮♂️ Yüz yanıma işlemleri için resim boyutunun en az 480x360 olması gerekmektedir
✨ Resmin analiz işlemleri için ilk olarak imageAnalyser
objesi tanımlanır
😅 Firebase hakimiyetin olduğunu varsayarak devam ediyorum
👨💼 Oluşturulan imageAnalyser
objesi içerisinde resim Firebase resmine dönüştürülüp işlenir
👨🎨 Preview üzerine çıktıları göstermek için canvas
işlemlerini araştırınız
⭐ Analiz örneği istersen MLKit Demo ~ AsmaaMirkhan projesindeki MLKitFaceAnalyser java sınıfını inceleyebilirsin.
imageAnalysis.setAnalyzer(executor,MLKitFaceAnalyser())
şeklinde kullanılır.
Copy 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." )
}
}
🧙♂ Detaylı bilgi için
alanlarına bakabilirsin.
⭐ Uygulamanın Son Çıktısı
🖊️ Kamera Çıktısına Çizim Yapma
👮♂️ İlk olarak PreviewView
üzerine çizim yapamazsın, çünkü kamera ile kitlenmiş durumdadır
🐣 Yeni bir SurfaceView
tanımlayıp, onun üzerine çizim yapmalısın
🖼️ FrameLayout
ile her ikisini üst üste koymalı ve çizim yaptığını daha önde göstermelisin
🔗 Faydalı Kaynaklar
Last updated 9 months ago