با ML Kit در Android، اطلاعات فیس مش را شناسایی کنید

می‌توانید از کیت ML برای تشخیص چهره‌ها در تصاویر و ویدیوهای سلفی مانند استفاده کنید.

API تشخیص مش چهره
نام SDK face-mesh-detection
پیاده سازی کد و دارایی ها به صورت ایستا به برنامه شما در زمان ساخت مرتبط هستند.
تاثیر اندازه برنامه ~ 6.4 مگابایت
عملکرد زمان واقعی در اکثر دستگاه ها.

آن را امتحان کنید

قبل از شروع

  1. در فایل build.gradle در سطح پروژه خود، مطمئن شوید که مخزن Maven Google را در هر دو بخش buildscript و allprojects خود قرار دهید.

  2. وابستگی کتابخانه تشخیص مش چهره کیت ML را به فایل gradle سطح برنامه ماژول خود اضافه کنید، که معمولا app/build.gradle است:

    dependencies {
     // ...
    
     implementation 'com.google.mlkit:face-mesh-detection:16.0.0-beta1'
    }
    

دستورالعمل های تصویر ورودی

  1. تصاویر باید در فاصله ~2 متری (~7 فوت) از دوربین دستگاه گرفته شوند، به طوری که چهره ها به اندازه کافی بزرگ باشند تا بتوان بهینه مش چهره را تشخیص داد. به طور کلی، هرچه صورت بزرگتر باشد، تشخیص مش چهره بهتر است.

  2. صورت باید رو به دوربین باشد و حداقل نیمی از صورت قابل مشاهده باشد. هر جسم بزرگی بین صورت و دوربین ممکن است باعث کاهش دقت شود.

اگر می‌خواهید چهره‌ها را در یک برنامه بلادرنگ شناسایی کنید، باید ابعاد کلی تصویر ورودی را نیز در نظر بگیرید. تصاویر کوچک‌تر را می‌توان سریع‌تر پردازش کرد، بنابراین ثبت تصاویر با وضوح پایین‌تر، تأخیر را کاهش می‌دهد. با این حال، الزامات دقت بالا را در نظر داشته باشید و اطمینان حاصل کنید که صورت سوژه تا حد امکان تصویر را اشغال می کند.

آشکارساز مش چهره را پیکربندی کنید

اگر می‌خواهید هر یک از تنظیمات پیش‌فرض آشکارساز چهره مش را تغییر دهید، آن تنظیمات را با یک شی FaceMeshDetectorOptions مشخص کنید. می توانید تنظیمات زیر را تغییر دهید:

  1. setUseCase

    • BOUNDING_BOX_ONLY : فقط یک جعبه محدود کننده برای مش چهره شناسایی شده ارائه می دهد. این سریعترین آشکارساز چهره است، اما دارای محدودیت برد است (چهره ها باید در فاصله 2 متری یا ~ 7 فوتی دوربین باشند).

    • FACE_MESH (گزینه پیش‌فرض): یک کادر محدود و اطلاعات مش اضافی (468 نقطه سه بعدی و اطلاعات مثلث) را ارائه می‌کند. در مقایسه با موارد استفاده BOUNDING_BOX_ONLY ، تأخیر تا 15٪ افزایش می‌یابد که در Pixel 3 اندازه‌گیری می‌شود.

به عنوان مثال:

کاتلین

val defaultDetector = FaceMeshDetection.getClient(
  FaceMeshDetectorOptions.DEFAULT_OPTIONS)

val boundingBoxDetector = FaceMeshDetection.getClient(
  FaceMeshDetectorOptions.Builder()
    .setUseCase(UseCase.BOUNDING_BOX_ONLY)
    .build()
)

جاوا

FaceMeshDetector defaultDetector =
        FaceMeshDetection.getClient(
                FaceMeshDetectorOptions.DEFAULT_OPTIONS);

FaceMeshDetector boundingBoxDetector = FaceMeshDetection.getClient(
        new FaceMeshDetectorOptions.Builder()
                .setUseCase(UseCase.BOUNDING_BOX_ONLY)
                .build()
        );

تصویر ورودی را آماده کنید

برای شناسایی چهره‌ها در یک تصویر، یک شی InputImage از Bitmap ، media.Image ، ByteBuffer ، آرایه بایت یا یک فایل روی دستگاه ایجاد کنید. سپس، شی InputImage را به روش process FaceDetector منتقل کنید.

برای تشخیص چهره مش، باید از تصویری با ابعاد حداقل 480x360 پیکسل استفاده کنید. اگر چهره‌ها را در زمان واقعی شناسایی می‌کنید، گرفتن فریم‌ها با این حداقل وضوح می‌تواند به کاهش تأخیر کمک کند.

می توانید یک شی InputImage از منابع مختلف ایجاد کنید که هر کدام در زیر توضیح داده شده است.

استفاده از یک media.Image

برای ایجاد یک شیء InputImage از یک شیء media.Image ، مانند زمانی که تصویری را از دوربین دستگاه می‌گیرید، شیء media.Image Image و چرخش تصویر را به InputImage.fromMediaImage() منتقل کنید.

اگر از کتابخانه CameraX استفاده می کنید، کلاس های OnImageCapturedListener و ImageAnalysis.Analyzer مقدار چرخش را برای شما محاسبه می کنند.

کاتلین

private class YourImageAnalyzer : ImageAnalysis.Analyzer {

    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image
        if (mediaImage != null) {
            val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
            // Pass image to an ML Kit Vision API
            // ...
        }
    }
}

جاوا

private class YourAnalyzer implements ImageAnalysis.Analyzer {

    @Override
    public void analyze(ImageProxy imageProxy) {
        Image mediaImage = imageProxy.getImage();
        if (mediaImage != null) {
          InputImage image =
                InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees());
          // Pass image to an ML Kit Vision API
          // ...
        }
    }
}

اگر از کتابخانه دوربینی که درجه چرخش تصویر را به شما می دهد استفاده نمی کنید، می توانید آن را از روی درجه چرخش دستگاه و جهت سنسور دوربین در دستگاه محاسبه کنید:

کاتلین

private val ORIENTATIONS = SparseIntArray()

init {
    ORIENTATIONS.append(Surface.ROTATION_0, 0)
    ORIENTATIONS.append(Surface.ROTATION_90, 90)
    ORIENTATIONS.append(Surface.ROTATION_180, 180)
    ORIENTATIONS.append(Surface.ROTATION_270, 270)
}

/**
 * Get the angle by which an image must be rotated given the device's current
 * orientation.
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Throws(CameraAccessException::class)
private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int {
    // Get the device's current rotation relative to its "native" orientation.
    // Then, from the ORIENTATIONS table, look up the angle the image must be
    // rotated to compensate for the device's rotation.
    val deviceRotation = activity.windowManager.defaultDisplay.rotation
    var rotationCompensation = ORIENTATIONS.get(deviceRotation)

    // Get the device's sensor orientation.
    val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager
    val sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION)!!

    if (isFrontFacing) {
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360
    } else { // back-facing
        rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360
    }
    return rotationCompensation
}

جاوا

private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0, 0);
    ORIENTATIONS.append(Surface.ROTATION_90, 90);
    ORIENTATIONS.append(Surface.ROTATION_180, 180);
    ORIENTATIONS.append(Surface.ROTATION_270, 270);
}

/**
 * Get the angle by which an image must be rotated given the device's current
 * orientation.
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing)
        throws CameraAccessException {
    // Get the device's current rotation relative to its "native" orientation.
    // Then, from the ORIENTATIONS table, look up the angle the image must be
    // rotated to compensate for the device's rotation.
    int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
    int rotationCompensation = ORIENTATIONS.get(deviceRotation);

    // Get the device's sensor orientation.
    CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE);
    int sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION);

    if (isFrontFacing) {
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360;
    } else { // back-facing
        rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360;
    }
    return rotationCompensation;
}

سپس، شی media.Image و مقدار درجه چرخش را به InputImage.fromMediaImage() منتقل کنید:

کاتلین

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

InputImage image = InputImage.fromMediaImage(mediaImage, rotation);

استفاده از URI فایل

برای ایجاد یک شی InputImage از URI فایل، زمینه برنامه و فایل URI را به InputImage.fromFilePath() ارسال کنید. این زمانی مفید است که از یک هدف ACTION_GET_CONTENT استفاده می کنید تا از کاربر بخواهید تصویری را از برنامه گالری خود انتخاب کند.

کاتلین

val image: InputImage
try {
    image = InputImage.fromFilePath(context, uri)
} catch (e: IOException) {
    e.printStackTrace()
}

Java

InputImage image;
try {
    image = InputImage.fromFilePath(context, uri);
} catch (IOException e) {
    e.printStackTrace();
}

استفاده از ByteBuffer یا ByteArray

برای ایجاد یک شی InputImage از ByteBuffer یا ByteArray ، ابتدا درجه چرخش تصویر را همانطور که قبلا برای ورودی media.Image توضیح داده شد محاسبه کنید. سپس، شی InputImage با بافر یا آرایه به همراه ارتفاع، عرض، فرمت کدگذاری رنگ و درجه چرخش تصویر ایجاد کنید:

کاتلین

val image = InputImage.fromByteBuffer(
        byteBuffer,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
)
// Or:
val image = InputImage.fromByteArray(
        byteArray,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
)

جاوا

InputImage image = InputImage.fromByteBuffer(byteBuffer,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
);
// Or:
InputImage image = InputImage.fromByteArray(
        byteArray,
        /* image width */480,
        /* image height */360,
        rotation,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
);

استفاده از Bitmap

برای ایجاد یک شی InputImage از یک شی Bitmap ، اعلان زیر را انجام دهید:

کاتلین

val image = InputImage.fromBitmap(bitmap, 0)

Java

InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);

تصویر با یک شی Bitmap همراه با درجه چرخش نمایش داده می شود.

تصویر را پردازش کنید

ارسال تصویر به روش process :

کاتلین

val result = detector.process(image)
        .addOnSuccessListener { result ->
            // Task completed successfully
            // …
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            // …
        }

جاوا

Task<List<FaceMesh>> result = detector.process(image)
        .addOnSuccessListener(
                new OnSuccessListener<List<FaceMesh>>() {
                    @Override
                    public void onSuccess(List<FaceMesh> result) {
                        // Task completed successfully
                        // …
                    }
                })
        .addOnFailureListener(
                new OnFailureListener() {
                    @Override
                    Public void onFailure(Exception e) {
                        // Task failed with an exception
                        // …
                    }
                });

اطلاعاتی در مورد مش چهره شناسایی شده دریافت کنید

اگر چهره‌ای در تصویر شناسایی شود، فهرستی از اشیاء FaceMesh به شنونده موفق ارسال می‌شود. هر FaceMesh نشان دهنده چهره ای است که در تصویر شناسایی شده است. برای هر فیس مش، می توانید مختصات مرزی آن را در تصویر ورودی و همچنین هر اطلاعات دیگری را که آشکارساز مش چهره را برای یافتن آن پیکربندی کرده اید، دریافت کنید.

کاتلین

for (faceMesh in faceMeshs) {
    val bounds: Rect = faceMesh.boundingBox()

    // Gets all points
    val faceMeshpoints = faceMesh.allPoints
    for (faceMeshpoint in faceMeshpoints) {
      val index: Int = faceMeshpoints.index()
      val position = faceMeshpoint.position
    }

    // Gets triangle info
    val triangles: List<Triangle<FaceMeshPoint>> = faceMesh.allTriangles
    for (triangle in triangles) {
      // 3 Points connecting to each other and representing a triangle area.
      val connectedPoints = triangle.allPoints()
    }
}

جاوا

for (FaceMesh faceMesh : faceMeshs) {
    Rect bounds = faceMesh.getBoundingBox();

    // Gets all points
    List<FaceMeshPoint> faceMeshpoints = faceMesh.getAllPoints();
    for (FaceMeshPoint faceMeshpoint : faceMeshpoints) {
        int index = faceMeshpoints.getIndex();
        PointF3D position = faceMeshpoint.getPosition();
    }

    // Gets triangle info
    List<Triangle<FaceMeshPoint>> triangles = faceMesh.getAllTriangles();
    for (Triangle<FaceMeshPoint> triangle : triangles) {
        // 3 Points connecting to each other and representing a triangle area.
        List<FaceMeshPoint> connectedPoints = triangle.getAllPoints();
    }
}