ใช้ ML Kit เพื่อเพิ่มฟีเจอร์การแบ่งกลุ่มหัวข้อลงในแอปอย่างง่ายดาย
ฟีเจอร์ | รายละเอียด |
---|---|
ชื่อ SDK | play-services-mlkit-subject-segmentation |
การใช้งาน | ไม่ได้จัดกลุ่ม: โมเดลจะดาวน์โหลดแบบไดนามิกโดยใช้บริการ Google Play |
ผลกระทบต่อขนาดแอป | เพิ่มขนาดประมาณ 200 KB |
เวลาในการเริ่มต้น | ผู้ใช้อาจต้องรอให้โมเดลดาวน์โหลดก่อนจึงจะใช้งานได้ |
ลองเลย
- ลองใช้แอปตัวอย่างเพื่อ ดูตัวอย่างการใช้ API นี้
ก่อนเริ่มต้น
- ในไฟล์
build.gradle
ระดับโปรเจ็กต์ อย่าลืมรวมที่เก็บ Maven ของ Google ไว้ทั้งในส่วนbuildscript
และallprojects
- เพิ่มทรัพยากร Dependency สำหรับไลบรารีการแบ่งกลุ่มหัวข้อของ ML Kit ลงในไฟล์ Gradle ระดับแอปของโมดูล ซึ่งปกติคือ
app/build.gradle
dependencies {
implementation 'com.google.android.gms:play-services-mlkit-subject-segmentation:16.0.0-beta1'
}
ตามที่กล่าวไว้ข้างต้น โมเดลมาจากบริการ Google Play
คุณกำหนดค่าแอปให้ดาวน์โหลดโมเดลลงในอุปกรณ์โดยอัตโนมัติได้
หลังจากติดตั้งแอปจาก Play Store แล้ว วิธีการคือ ให้เพิ่มค่าต่อไปนี้
ลงในไฟล์ AndroidManifest.xml
ของแอป
<application ...>
...
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="subject_segment" >
<!-- To use multiple models: android:value="subject_segment,model2,model3" -->
</application>
นอกจากนี้ คุณยังตรวจสอบความพร้อมใช้งานของโมเดลและส่งคำขอดาวน์โหลดผ่านบริการ Google Play อย่างชัดแจ้งได้ด้วย ModuleInstallClient API
หากคุณไม่เปิดใช้การดาวน์โหลดโมเดลเวลาติดตั้งหรือขอการดาวน์โหลดอย่างชัดแจ้ง ระบบจะดาวน์โหลดโมเดลในครั้งแรกที่คุณเรียกใช้ตัวแบ่งกลุ่ม คําขอที่คุณสร้าง ก่อนที่การดาวน์โหลดจะเสร็จสิ้น จะไม่เกิดผลลัพธ์ใดๆ
1. เตรียมรูปภาพอินพุต
หากต้องการแบ่งกลุ่มรูปภาพ ให้สร้างออบเจ็กต์ InputImage
จากอาร์เรย์ Bitmap
, media.Image
, ByteBuffer
, ไบต์ หรือไฟล์ใน
อุปกรณ์
คุณสามารถสร้างInputImage
จากแหล่งที่มาต่างๆ ซึ่งอธิบายไว้ด้านล่าง
กำลังใช้media.Image
วิธีสร้าง InputImage
จากออบเจ็กต์ media.Image
เช่น เมื่อคุณจับภาพจาก
กล้องของอุปกรณ์ ส่งวัตถุ media.Image
และ
การหมุนเวียนเป็น InputImage.fromMediaImage()
หากคุณใช้แท็ก
ไลบรารี CameraX, OnImageCapturedListener
และ
ImageAnalysis.Analyzer
คลาสจะคำนวณค่าการหมุนเวียน
สำหรับคุณ
Kotlin
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 // ... } } }
Java
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 // ... } } }
ถ้าคุณไม่ได้ใช้ไลบรารีกล้องถ่ายรูปที่ให้องศาการหมุนของภาพ คุณ สามารถคำนวณได้จากระดับการหมุนของอุปกรณ์และการวางแนวของกล้อง เซ็นเซอร์ในอุปกรณ์:
Kotlin
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 }
Java
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()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
การใช้ URI ของไฟล์
วิธีสร้าง InputImage
จาก URI ของไฟล์ แล้วส่งบริบทของแอปและ URI ของไฟล์ไปยัง
InputImage.fromFilePath()
วิธีนี้มีประโยชน์เมื่อคุณ
ใช้ Intent ACTION_GET_CONTENT
เพื่อแจ้งให้ผู้ใช้เลือก
รูปภาพจากแอปแกลเลอรี
Kotlin
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
พร้อมบัฟเฟอร์หรืออาร์เรย์ ร่วมกับรูปภาพ
ความสูง ความกว้าง รูปแบบการเข้ารหัสสี และระดับการหมุน:
Kotlin
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 )
Java
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
ให้ทำการประกาศต่อไปนี้
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
รูปภาพจะแสดงเป็นวัตถุ Bitmap
ร่วมกับองศาการหมุน
2. สร้างอินสแตนซ์ SubjectSegmenter
กำหนดตัวเลือกตัวแบ่งกลุ่ม
หากต้องการแบ่งกลุ่มรูปภาพ ให้สร้างอินสแตนซ์ของ SubjectSegmenterOptions
เป็น
ติดตาม:
Kotlin
val options = SubjectSegmenterOptions.Builder() // enable options .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() // enable options .build();
รายละเอียดของแต่ละตัวเลือกมีดังนี้
มาสก์หน้าความมั่นใจ
มาสก์ความมั่นใจในเบื้องหน้าช่วยให้คุณแยกแยะวัตถุในเบื้องหน้าจาก พื้นหลัง
โทรหา enableForegroundConfidenceMask()
ในตัวเลือกเพื่อรับสายในภายหลัง
มาสก์เบื้องหน้าโดยการเรียกใช้ getForegroundMask()
ใน
ออบเจ็กต์ SubjectSegmentationResult
รายการแสดงผลหลังจากประมวลผลรูปภาพ
Kotlin
val options = SubjectSegmenterOptions.Builder() .enableForegroundConfidenceMask() .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableForegroundConfidenceMask() .build();
บิตแมปที่เบื้องหน้า
ในทำนองเดียวกัน คุณจะรับบิตแมปของวัตถุในเบื้องหน้าได้ด้วย
โทรหา enableForegroundBitmap()
ในตัวเลือกเพื่อให้คุณรับสายได้ในภายหลัง
บิตแมปของเบื้องหน้าโดยการเรียกใช้ getForegroundBitmap()
ใน
ออบเจ็กต์ SubjectSegmentationResult
รายการแสดงผลหลังจากประมวลผลรูปภาพ
Kotlin
val options = SubjectSegmenterOptions.Builder() .enableForegroundBitmap() .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableForegroundBitmap() .build();
มาสก์ความมั่นใจแบบหลายวัตถุ
เช่นเดียวกับตัวเลือกเบื้องหน้า คุณจะใช้ SubjectResultOptions
เพื่อเปิดใช้ได้
มาสก์ความเชื่อมั่นสำหรับวัตถุเบื้องหน้าแต่ละแบบมีดังนี้
Kotlin
val subjectResultOptions = SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableConfidenceMask() .build() val options = SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
Java
SubjectResultOptions subjectResultOptions = new SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableConfidenceMask() .build() SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
บิตแมปหลายเรื่อง
และในทำนองเดียวกัน คุณสามารถเปิดใช้บิตแมปสำหรับแต่ละหัวเรื่องได้ ดังนี้
Kotlin
val subjectResultOptions = SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableSubjectBitmap() .build() val options = SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
Java
SubjectResultOptions subjectResultOptions = new SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableSubjectBitmap() .build() SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
สร้างการแบ่งกลุ่มเรื่อง
เมื่อคุณระบุตัวเลือก SubjectSegmenterOptions
แล้ว ให้สร้าง
อินสแตนซ์ SubjectSegmenter
ที่เรียกใช้ getClient()
และส่งตัวเลือกเป็น
พารามิเตอร์:
Kotlin
val segmenter = SubjectSegmentation.getClient(options)
Java
SubjectSegmenter segmenter = SubjectSegmentation.getClient(options);
3. ประมวลผลรูปภาพ
ผ่านInputImage
ที่เตรียมไว้
เป็นเมธอด process
ของ SubjectSegmenter
:
Kotlin
segmenter.process(inputImage) .addOnSuccessListener { result -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
segmenter.process(inputImage) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(SubjectSegmentationResult result) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. รับผลลัพธ์การแบ่งกลุ่มหัวเรื่อง
ดึงข้อมูลมาสก์เบื้องหน้าและบิตแมป
เมื่อประมวลผลแล้ว คุณจะเรียกมาสก์เบื้องหน้าสำหรับการเรียกใช้รูปภาพได้
getForegroundConfidenceMask()
ดังต่อไปนี้
Kotlin
val colors = IntArray(image.width * image.height) val foregroundMask = result.foregroundConfidenceMask for (i in 0 until image.width * image.height) { if (foregroundMask[i] > 0.5f) { colors[i] = Color.argb(128, 255, 0, 255) } } val bitmapMask = Bitmap.createBitmap( colors, image.width, image.height, Bitmap.Config.ARGB_8888 )
Java
int[] colors = new int[image.getWidth() * image.getHeight()]; FloatBuffer foregroundMask = result.getForegroundConfidenceMask(); for (int i = 0; i < image.getWidth() * image.getHeight(); i++) { if (foregroundMask.get() > 0.5f) { colors[i] = Color.argb(128, 255, 0, 255); } } Bitmap bitmapMask = Bitmap.createBitmap( colors, image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888 );
คุณยังเรียกบิตแมปของเบื้องหน้าของรูปภาพที่กำลังเรียก getForegroundBitmap()
ได้ด้วย:
Kotlin
val foregroundBitmap = result.foregroundBitmap
Java
Bitmap foregroundBitmap = result.getForegroundBitmap();
ดึงข้อมูลมาสก์และบิตแมปสำหรับแต่ละหัวข้อ
ในทำนองเดียวกัน คุณเรียกข้อมูลมาสก์สำหรับวัตถุที่แบ่งกลุ่มได้โดยเรียกใช้
getConfidenceMask()
ในแต่ละวิชาดังนี้
Kotlin
val subjects = result.subjects val colors = IntArray(image.width * image.height) for (subject in subjects) { val mask = subject.confidenceMask for (i in 0 until subject.width * subject.height) { val confidence = mask[i] if (confidence > 0.5f) { colors[image.width * (subject.startY - 1) + subject.startX] = Color.argb(128, 255, 0, 255) } } } val bitmapMask = Bitmap.createBitmap( colors, image.width, image.height, Bitmap.Config.ARGB_8888 )
Java
Listsubjects = result.getSubjects(); int[] colors = new int[image.getWidth() * image.getHeight()]; for (Subject subject : subjects) { FloatBuffer mask = subject.getConfidenceMask(); for (int i = 0; i < subject.getWidth() * subject.getHeight(); i++) { float confidence = mask.get(); if (confidence > 0.5f) { colors[width * (subject.getStartY() - 1) + subject.getStartX()] = Color.argb(128, 255, 0, 255); } } } Bitmap bitmapMask = Bitmap.createBitmap( colors, image.width, image.height, Bitmap.Config.ARGB_8888 );
คุณยังสามารถเข้าถึงบิตแมปของแต่ละหัวเรื่องที่แบ่งกลุ่มได้ดังนี้
Kotlin
val bitmaps = mutableListOf() for (subject in subjects) { bitmaps.add(subject.bitmap) }
Java
Listbitmaps = new ArrayList<>(); for (Subject subject : subjects) { bitmaps.add(subject.getBitmap()); }
เคล็ดลับในการปรับปรุงประสิทธิภาพ
สําหรับเซสชันของแอปแต่ละเซสชัน การอนุมานครั้งแรกมักจะช้ากว่า จากการเริ่มต้นโมเดล หากเวลาในการตอบสนองต่ำคือเรื่องร้ายแรง ให้พิจารณา ที่เรียก "หุ่น" การอนุมานได้ล่วงหน้า
คุณภาพของผลลัพธ์จะขึ้นอยู่กับคุณภาพของรูปภาพที่ป้อน ดังนี้
- รูปภาพควรมีขนาดอย่างน้อย 512x512 พิกเซล เพื่อให้ ML Kit ได้ผลลัพธ์การแบ่งกลุ่มลูกค้าที่แม่นยำ
- การโฟกัสของรูปภาพไม่ดีอาจส่งผลต่อความแม่นยำด้วย ถ้าคุณไม่ได้ผลลัพธ์ที่ยอมรับได้ โปรดขอให้ผู้ใช้ถ่ายภาพอีกครั้ง