تشخیص چهره با ML Kit در اندروید

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

ویژگی بدون دسته بندی بسته‌بندی‌شده
پیاده‌سازی مدل به صورت پویا از طریق سرویس‌های گوگل پلی دانلود می‌شود. مدل در زمان ساخت به صورت ایستا به برنامه شما متصل است.
اندازه برنامه حدود ۸۰۰ کیلوبایت افزایش حجم. حدود ۶.۹ مگابایت افزایش حجم.
زمان اولیه سازی ممکن است لازم باشد قبل از اولین استفاده، منتظر دانلود مدل باشید. مدل فوراً موجود است

امتحانش کن.

  • برای مشاهده‌ی نحوه‌ی استفاده از این API، با برنامه‌ی نمونه کار کنید.
  • خودتان کد را با codelab امتحان کنید.

قبل از اینکه شروع کنی

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

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

    برای باندل کردن مدل با اپلیکیشن خود:

    dependencies {
      // ...
      // Use this dependency to bundle the model with your app
      implementation 'com.google.mlkit:face-detection:16.1.7'
    }
    

    برای استفاده از مدل در سرویس‌های گوگل پلی:

    dependencies {
      // ...
      // Use this dependency to use the dynamically downloaded model in Google Play Services
      implementation 'com.google.android.gms:play-services-mlkit-face-detection:17.1.0'
    }
    
  3. اگر تصمیم دارید از مدل در سرویس‌های گوگل پلی استفاده کنید ، می‌توانید برنامه خود را طوری پیکربندی کنید که پس از نصب برنامه از فروشگاه پلی استور، مدل را به طور خودکار روی دستگاه دانلود کند. برای انجام این کار، اعلان زیر را به فایل AndroidManifest.xml برنامه خود اضافه کنید:

    <application ...>
          ...
          <meta-data
              android:name="com.google.mlkit.vision.DEPENDENCIES"
              android:value="face" >
          <!-- To use multiple models: android:value="face,model2,model3" -->
    </application>
    

    همچنین می‌توانید به صراحت در دسترس بودن مدل را بررسی کرده و از طریق API ModuleInstallClient سرویس‌های گوگل پلی درخواست دانلود دهید.

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

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

برای تشخیص چهره، باید از تصویری با ابعاد حداقل ۴۸۰x۳۶۰ پیکسل استفاده کنید. برای اینکه کیت یادگیری ماشین بتواند چهره‌ها را به طور دقیق تشخیص دهد، تصاویر ورودی باید حاوی چهره‌هایی باشند که با داده‌های پیکسلی کافی نمایش داده می‌شوند. به طور کلی، هر چهره‌ای که می‌خواهید در یک تصویر تشخیص دهید باید حداقل ۱۰۰x۱۰۰ پیکسل باشد. اگر می‌خواهید خطوط چهره‌ها را تشخیص دهید، کیت یادگیری ماشین به ورودی با وضوح بالاتر نیاز دارد: هر چهره باید حداقل ۲۰۰x۲۰۰ پیکسل باشد.

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

فوکوس ضعیف تصویر نیز می‌تواند بر دقت تأثیر بگذارد. اگر نتایج قابل قبولی به دست نیاوردید، از کاربر بخواهید که دوباره تصویر را ثبت کند.

جهت‌گیری چهره نسبت به دوربین نیز می‌تواند بر ویژگی‌های چهره‌ای که کیت ML تشخیص می‌دهد تأثیر بگذارد. به مفاهیم تشخیص چهره مراجعه کنید.

۱. پیکربندی تشخیص چهره

قبل از اعمال تشخیص چهره به یک تصویر، اگر می‌خواهید هر یک از تنظیمات پیش‌فرض تشخیص چهره را تغییر دهید، آن تنظیمات را با شیء FaceDetectorOptions مشخص کنید. می‌توانید تنظیمات زیر را تغییر دهید:

تنظیمات
setPerformanceMode PERFORMANCE_MODE_FAST (پیش‌فرض) | PERFORMANCE_MODE_ACCURATE

هنگام تشخیص چهره، سرعت یا دقت را ترجیح دهید.

setLandmarkMode LANDMARK_MODE_NONE (پیش‌فرض) | LANDMARK_MODE_ALL

اینکه آیا باید برای شناسایی «نشانه‌های» صورت تلاش کرد یا خیر: چشم‌ها، گوش‌ها، بینی، گونه‌ها، دهان و غیره.

setContourMode CONTOUR_MODE_NONE (پیش‌فرض) | CONTOUR_MODE_ALL

اینکه آیا خطوط منحنی ویژگی‌های صورت تشخیص داده شوند یا خیر. خطوط منحنی فقط برای برجسته‌ترین چهره در تصویر تشخیص داده می‌شوند.

setClassificationMode CLASSIFICATION_MODE_NONE (پیش‌فرض) | CLASSIFICATION_MODE_ALL

اینکه آیا چهره‌ها را به دسته‌هایی مانند «لبخند» و «چشمان باز» طبقه‌بندی کنیم یا نه.

setMinFaceSize float (پیش‌فرض: 0.1f )

کوچکترین اندازه دلخواه چهره را تنظیم می‌کند، که به صورت نسبت عرض سر به عرض تصویر بیان می‌شود.

enableTracking false (پیش‌فرض) | true

اینکه آیا به چهره‌ها شناسه‌ای اختصاص داده شود یا خیر، که بتوان از آن برای ردیابی چهره‌ها در تصاویر استفاده کرد.

توجه داشته باشید که وقتی تشخیص خطوط اطراف فعال است، فقط یک چهره شناسایی می‌شود، بنابراین ردیابی چهره نتایج مفیدی به همراه ندارد. به همین دلیل و برای بهبود سرعت تشخیص، هم تشخیص خطوط اطراف و هم ردیابی چهره را فعال نکنید.

برای مثال:

کاتلین

// High-accuracy landmark detection and face classification
val highAccuracyOpts = FaceDetectorOptions.Builder()
        .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
        .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
        .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
        .build()

// Real-time contour detection
val realTimeOpts = FaceDetectorOptions.Builder()
        .setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL)
        .build()

جاوا

// High-accuracy landmark detection and face classification
FaceDetectorOptions highAccuracyOpts =
        new FaceDetectorOptions.Builder()
                .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
                .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
                .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
                .build();

// Real-time contour detection
FaceDetectorOptions realTimeOpts =
        new FaceDetectorOptions.Builder()
                .setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL)
                .build();

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

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

برای تشخیص چهره، باید از تصویری با ابعاد حداقل ۴۸۰x۳۶۰ پیکسل استفاده کنید. اگر چهره‌ها را به صورت بلادرنگ تشخیص می‌دهید، ثبت فریم‌ها با این حداقل وضوح می‌تواند به کاهش تأخیر کمک کند.

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

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

برای ایجاد یک شیء InputImage از یک شیء media.Image ، مانند زمانی که از دوربین یک دستگاه تصویر می‌گیرید، شیء media.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 به همراه درجه چرخش نمایش داده می‌شود.

۳. یک نمونه از FaceDetector دریافت کنید

کاتلین

val detector = FaceDetection.getClient(options)
// Or, to use the default option:
// val detector = FaceDetection.getClient();

جاوا

FaceDetector detector = FaceDetection.getClient(options);
// Or use the default options:
// FaceDetector detector = FaceDetection.getClient();

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

تصویر را به متد process ارسال کنید:

کاتلین

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

جاوا

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

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

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

کاتلین

for (face in faces) {
    val bounds = face.boundingBox
    val rotY = face.headEulerAngleY // Head is rotated to the right rotY degrees
    val rotZ = face.headEulerAngleZ // Head is tilted sideways rotZ degrees

    // If landmark detection was enabled (mouth, ears, eyes, cheeks, and
    // nose available):
    val leftEar = face.getLandmark(FaceLandmark.LEFT_EAR)
    leftEar?.let {
        val leftEarPos = leftEar.position
    }

    // If contour detection was enabled:
    val leftEyeContour = face.getContour(FaceContour.LEFT_EYE)?.points
    val upperLipBottomContour = face.getContour(FaceContour.UPPER_LIP_BOTTOM)?.points

    // If classification was enabled:
    if (face.smilingProbability != null) {
        val smileProb = face.smilingProbability
    }
    if (face.rightEyeOpenProbability != null) {
        val rightEyeOpenProb = face.rightEyeOpenProbability
    }

    // If face tracking was enabled:
    if (face.trackingId != null) {
        val id = face.trackingId
    }
}

جاوا

for (Face face : faces) {
    Rect bounds = face.getBoundingBox();
    float rotY = face.getHeadEulerAngleY();  // Head is rotated to the right rotY degrees
    float rotZ = face.getHeadEulerAngleZ();  // Head is tilted sideways rotZ degrees

    // If landmark detection was enabled (mouth, ears, eyes, cheeks, and
    // nose available):
    FaceLandmark leftEar = face.getLandmark(FaceLandmark.LEFT_EAR);
    if (leftEar != null) {
        PointF leftEarPos = leftEar.getPosition();
    }

    // If contour detection was enabled:
    List<PointF> leftEyeContour =
            face.getContour(FaceContour.LEFT_EYE).getPoints();
    List<PointF> upperLipBottomContour =
            face.getContour(FaceContour.UPPER_LIP_BOTTOM).getPoints();

    // If classification was enabled:
    if (face.getSmilingProbability() != null) {
        float smileProb = face.getSmilingProbability();
    }
    if (face.getRightEyeOpenProbability() != null) {
        float rightEyeOpenProb = face.getRightEyeOpenProbability();
    }

    // If face tracking was enabled:
    if (face.getTrackingId() != null) {
        int id = face.getTrackingId();
    }
}

نمونه‌ای از خطوط چهره

وقتی تشخیص خطوط چهره فعال باشد، فهرستی از نقاط برای هر ویژگی چهره که شناسایی شده است، دریافت خواهید کرد. این نقاط نشان دهنده شکل آن ویژگی هستند. برای جزئیات بیشتر در مورد نحوه نمایش خطوط چهره، به مفاهیم تشخیص چهره مراجعه کنید.

تصویر زیر نحوه‌ی نگاشت این نقاط به یک وجه را نشان می‌دهد، برای بزرگنمایی تصویر، روی آن کلیک کنید:

به عنوان مثال، مش کانتور صورت شناسایی شد

تشخیص چهره در زمان واقعی

اگر می‌خواهید از تشخیص چهره در یک برنامه‌ی بلادرنگ استفاده کنید، برای دستیابی به بهترین نرخ فریم، این دستورالعمل‌ها را دنبال کنید:

  • آشکارساز چهره را طوری پیکربندی کنید که از تشخیص خطوط چهره یا طبقه‌بندی و تشخیص نقاط برجسته استفاده کند، اما نه هر دو:

    تشخیص کانتور
    تشخیص نقاط برجسته
    طبقه‌بندی
    تشخیص و طبقه‌بندی نشانه‌های جغرافیایی
    تشخیص کانتور و تشخیص نقاط برجسته
    تشخیص و طبقه‌بندی کانتور
    تشخیص کانتور، تشخیص نقاط برجسته و طبقه‌بندی

  • حالت FAST را فعال کنید (به طور پیش‌فرض فعال است).

  • ضبط تصاویر با وضوح پایین‌تر را در نظر بگیرید. با این حال، الزامات ابعاد تصویر این API را نیز در نظر داشته باشید.

  • اگر از API Camera یا camera2 استفاده می‌کنید، فراخوانی‌های throttle به آشکارساز انجام می‌شود. اگر در حین اجرای آشکارساز، یک فریم ویدیویی جدید در دسترس قرار گرفت، فریم را حذف کنید. برای مثال، به کلاس VisionProcessorBase در برنامه نمونه شروع سریع مراجعه کنید.
  • اگر از API CameraX استفاده می‌کنید، مطمئن شوید که استراتژی فشار معکوس (backpressure strategy) روی مقدار پیش‌فرض خود ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST تنظیم شده است. این تضمین می‌کند که فقط یک تصویر در هر زمان برای تجزیه و تحلیل تحویل داده می‌شود. اگر تصاویر بیشتری هنگام مشغول بودن تحلیلگر تولید شوند، به طور خودکار حذف می‌شوند و برای تحویل در صف قرار نمی‌گیرند. پس از بسته شدن تصویر در حال تجزیه و تحلیل با فراخوانی ImageProxy.close()، آخرین تصویر بعدی تحویل داده می‌شود.
  • اگر از خروجی آشکارساز برای همپوشانی گرافیک روی تصویر ورودی استفاده می‌کنید، ابتدا نتیجه را از کیت ML دریافت کنید، سپس تصویر و همپوشانی را در یک مرحله رندر کنید. این کار فقط یک بار برای هر فریم ورودی روی سطح نمایشگر رندر می‌شود. برای مثال به کلاس‌های CameraSourcePreview و GraphicOverlay در برنامه نمونه شروع سریع مراجعه کنید.
  • اگر از API دوربین ۲ استفاده می‌کنید، تصاویر را با فرمت ImageFormat.YUV_420_888 ضبط کنید. اگر از API دوربین قدیمی‌تر استفاده می‌کنید، تصاویر را با فرمت ImageFormat.NV21 ضبط کنید.