1. قبل از شروع
تایپ کردن کد یک راه عالی برای ایجاد حافظه عضلانی و عمق بخشیدن به درک شما از مطالب است. در حالی که کپی پیست می تواند باعث صرفه جویی در زمان شود، سرمایه گذاری در این عمل می تواند منجر به کارایی بیشتر و مهارت های کدنویسی قوی تر در دراز مدت شود.
در این کد لبه، شما یاد خواهید گرفت که چگونه یک برنامه اندروید بسازید که با استفاده از زمان اجرا جدید Google برای TensorFlow Lite، LiteRT، بخش بندی تصاویر را در زمان واقعی بر روی فید دوربین زنده انجام می دهد. شما یک برنامه اندروید شروع کننده را انتخاب می کنید و قابلیت تقسیم بندی تصویر را به آن اضافه می کنید. همچنین مراحل پیش پردازش، استنتاج و پس پردازش را نیز طی خواهیم کرد. شما:
- یک برنامه اندروید بسازید که تصاویر را در زمان واقعی بخش بندی می کند.
- یک مدل تقسیمبندی تصویر LiteRT از قبل آموزشدیده را ادغام کنید.
- تصویر ورودی مدل را از قبل پردازش کنید.
- از زمان اجرا LiteRT برای شتاب CPU و GPU استفاده کنید.
- نحوه پردازش خروجی مدل برای نمایش ماسک تقسیم بندی را بدانید.
- نحوه تنظیم دوربین جلو را بدانید.
در پایان، چیزی شبیه به تصویر زیر ایجاد خواهید کرد:
پیش نیازها
این کد لبه برای توسعه دهندگان باتجربه موبایل که می خواهند با یادگیری ماشین تجربه کسب کنند طراحی شده است. باید با:
- توسعه اندروید با استفاده از Kotlin و Android Studio
- مفاهیم اولیه پردازش تصویر
چیزی که یاد خواهید گرفت
- نحوه ادغام و استفاده از زمان اجرا LiteRT در یک برنامه اندروید.
- نحوه انجام بخش بندی تصویر با استفاده از یک مدل LiteRT از پیش آموزش دیده.
- نحوه پیش پردازش تصویر ورودی برای مدل
- نحوه اجرای استنتاج برای مدل
- نحوه پردازش خروجی یک مدل تقسیم بندی برای تجسم نتایج.
- نحوه استفاده از CameraX برای پردازش فید دوربین در زمان واقعی.
آنچه شما نیاز دارید
- نسخه اخیر اندروید استودیو (تست شده در نسخه 2025.1.1).
- یک دستگاه اندروید فیزیکی بهترین تست روی دستگاه های Galaxy و Pixel است.
- کد نمونه (از GitHub).
- دانش اولیه توسعه اندروید در Kotlin.
2. تقسیم بندی تصویر
تقسیم بندی تصویر یک کار بینایی کامپیوتری است که شامل تقسیم یک تصویر به بخش ها یا مناطق متعدد است. بر خلاف تشخیص اشیا، که یک کادر محدود را در اطراف یک شی ترسیم می کند، تقسیم بندی تصویر یک کلاس یا برچسب خاص را به هر پیکسل در تصویر اختصاص می دهد. این امر درک بسیار دقیقتری از محتوای تصویر را فراهم میکند و به شما امکان میدهد شکل و مرز دقیق هر جسم را بدانید.
بهعنوان مثال، بهجای اینکه بدانید یک «شخص» در یک جعبه است، میتوانید دقیقاً بدانید که کدام پیکسلها متعلق به آن شخص هستند. این آموزش نشان میدهد که چگونه میتوان با استفاده از یک مدل یادگیری ماشینی از پیش آموزشدیده، بخشبندی تصویر را در یک دستگاه اندرویدی در زمان واقعی انجام داد.
LiteRT: فشار دادن لبه ML روی دستگاه
یک فناوری کلیدی که امکان تقسیمبندی همزمان و با وفاداری بالا را در دستگاههای تلفن همراه فراهم میکند، LiteRT است. LiteRT بهعنوان زمان اجرا با عملکرد بالا و نسل بعدی Google برای TensorFlow Lite، مهندسی شده است تا بهترین عملکرد را از سختافزار زیربنایی دریافت کند.
این امر از طریق استفاده هوشمندانه و بهینه از شتاب دهنده های سخت افزاری مانند GPU (واحد پردازش گرافیکی) و NPU (واحد پردازش عصبی) به دست می آید. با بارگذاری حجم کار محاسباتی شدید مدل تقسیمبندی از CPU همه منظوره به این پردازندههای تخصصی، LiteRT زمان استنتاج را بهطور چشمگیری کاهش میدهد. این شتاب همان چیزی است که اجرای روان مدلهای پیچیده را در فید دوربین زنده امکانپذیر میسازد، و لبه آنچه را که میتوانیم با یادگیری ماشینی مستقیماً در تلفن شما به دست آوریم، افزایش میدهد. بدون این سطح از عملکرد، تقسیمبندی بلادرنگ برای یک تجربه کاربری خوب بسیار کند و متلاطم خواهد بود.
3. راه اندازی شوید
مخزن را شبیه سازی کنید
ابتدا مخزن LiteRT را شبیه سازی کنید:
git clone https://github.com/google-ai-edge/LiteRT.git
LiteRT/litert/samples/image_segmentation
دایرکتوری با تمام منابعی است که شما نیاز دارید. برای این کد لبه، فقط به پروژه kotlin_cpu_gpu/android_starter
نیاز دارید. اگر گیر کردید ممکن است بخواهید پروژه تمام شده را مرور کنید: kotlin_cpu_gpu/android
یادداشتی در مورد مسیرهای فایل
این آموزش مسیرهای فایل را در قالب Linux/macOS مشخص می کند. اگر در ویندوز هستید، باید مسیرها را بر اساس آن تنظیم کنید.
توجه به تمایز بین نمای پروژه Android Studio و نمای سیستم فایل استاندارد نیز مهم است. نمای پروژه Android Studio یک نمایش ساختار یافته از فایل های پروژه شما است که برای توسعه اندروید سازماندهی شده است. مسیرهای فایل در این آموزش به مسیرهای سیستم فایل اشاره دارد، نه مسیرهای موجود در نمای پروژه اندروید استودیو.
برنامه شروع را وارد کنید
بیایید با وارد کردن برنامه شروع به Android Studio شروع کنیم.
- Android Studio را باز کرده و Open را انتخاب کنید.
- به دایرکتوری
kotlin_cpu_gpu/android_starter
بروید و آن را باز کنید.
برای اطمینان از اینکه همه وابستگیها برای برنامه شما در دسترس هستند، باید پروژه خود را با فایلهای gradle همگامسازی کنید، پس از اتمام فرآیند واردات.
- Sync Project with Gradle Files را از نوار ابزار Android Studio انتخاب کنید.
- لطفاً این مرحله را نادیده نگیرید - اگر این کار مؤثر واقع نشد، بقیه آموزش منطقی نخواهد بود.
برنامه استارتر را اجرا کنید
اکنون که پروژه را به Android Studio وارد کرده اید، برای اولین بار آماده اجرای برنامه هستید.
دستگاه اندروید خود را از طریق USB به رایانه خود وصل کنید و روی Run در نوار ابزار Android Studio کلیک کنید.
برنامه باید روی دستگاه شما راه اندازی شود. شما یک فید دوربین زنده خواهید دید، اما هنوز هیچ بخشبندی انجام نمیشود. تمام ویرایشهای فایلی که در این آموزش انجام میدهید تحت LiteRT/litert/samples/image_segmentation/kotlin_cpu_gpu/android_starter/app/src/main/java/com/google/aiedge/examples/image_segmentation
دایرکتوری خواهند بود.
همچنین نظرات TODO
را در فایل های ImageSegmentationHelper.kt
، MainViewModel.kt
و view/SegmentationOverlay.kt
خواهید دید. در مراحل زیر، با پر کردن این TODO
ها، قابلیت تقسیم بندی تصویر را پیاده سازی خواهید کرد.
4. برنامه شروع را درک کنید
برنامه شروع از قبل دارای یک رابط کاربری اولیه و منطق مدیریت دوربین است. در اینجا یک نمای کلی از فایل های کلیدی آورده شده است:
-
app/src/main/java/com/google/aiedge/examples/image_segmentation/MainActivity.kt
: این نقطه ورود اصلی برنامه است. این رابط کاربری را با استفاده از Jetpack Compose تنظیم می کند و مجوزهای دوربین را کنترل می کند. -
app/src/main/java/com/google/aiedge/examples/image_segmentation/MainViewModel.kt
: این ViewModel وضعیت رابط کاربری را مدیریت میکند و فرآیند تقسیمبندی تصویر را هماهنگ میکند. -
app/src/main/java/com/google/aiedge/examples/image_segmentation/ImageSegmentationHelper.kt
: اینجاست که منطق اصلی را برای تقسیم بندی تصویر اضافه می کنیم. بارگذاری مدل، پردازش فریم های دوربین و اجرای استنتاج انجام می شود. -
app/src/main/java/com/google/aiedge/examples/image_segmentation/view/CameraScreen.kt
: این تابع Composable پیشنمایش دوربین و پوشش تقسیمبندی را نمایش میدهد. -
app/src/main/assets/selfie_multiclass.tflite
: این مدل تقسیمبندی تصویر TensorFlow Lite از پیش آموزشدیده است که ما از آن استفاده خواهیم کرد.
5. درک LiteRT و افزودن وابستگی ها
اکنون، بیایید عملکرد تقسیم بندی تصویر را به برنامه شروع اضافه کنیم.
1. وابستگی LiteRT را اضافه کنید
ابتدا باید کتابخانه LiteRT را به پروژه خود اضافه کنید. این اولین گام مهم برای فعال کردن یادگیری ماشینی روی دستگاه با زمان اجرا بهینه گوگل است.
فایل app/build.gradle.kts
را باز کنید و خط زیر را به بلوک dependencies
اضافه کنید:
// LiteRT for on-device ML
implementation(libs.litert)
پس از افزودن وابستگی، با کلیک بر روی دکمه Sync Now که در گوشه سمت راست بالای Android Studio ظاهر می شود، پروژه خود را با فایل های Gradle همگام کنید.
2. API های Key LiteRT را درک کنید
ImageSegmentationHelper.kt
را باز کنید
قبل از نوشتن کد پیاده سازی، مهم است که اجزای اصلی LiteRT API را که از آن استفاده خواهید کرد، درک کنید. مطمئن شوید که از بسته com.google.ai.edge.litert
وارد میکنید، واردات زیر را به بالای ImageSegmentationHelper.kt
اضافه کنید:
import com.google.ai.edge.litert.Accelerator
import com.google.ai.edge.litert.CompiledModel
-
CompiledModel
: این کلاس مرکزی برای تعامل با مدل TFLite شما است. این مدلی را نشان می دهد که برای یک شتاب دهنده سخت افزاری خاص (مانند CPU یا GPU) از قبل کامپایل و بهینه شده است. این پیش کامپایل یکی از ویژگی های کلیدی LiteRT است که منجر به استنتاج سریعتر و کارآمدتر می شود. -
CompiledModel.Options
: شما از این کلاس سازنده برای پیکربندیCompiledModel
استفاده می کنید. مهم ترین تنظیم، تعیین شتاب دهنده سخت افزاری است که می خواهید برای اجرای مدل خود استفاده کنید. -
Accelerator
: این فهرست به شما امکان می دهد سخت افزار را برای استنتاج انتخاب کنید. پروژه شروع از قبل برای مدیریت این گزینه ها پیکربندی شده است:-
Accelerator.CPU
: برای اجرای مدل بر روی CPU دستگاه. این سازگارترین گزینه جهانی است. -
Accelerator.GPU
: برای اجرای مدل بر روی GPU دستگاه. این اغلب به طور قابل توجهی سریعتر از CPU برای مدل های مبتنی بر تصویر است.
-
- بافرهای ورودی و خروجی (
TensorBuffer
) : LiteRT ازTensorBuffer
برای ورودی ها و خروجی های مدل استفاده می کند. این به شما کنترل دقیقی بر حافظه می دهد و از کپی های غیر ضروری داده ها جلوگیری می کند. شما این بافرها را مستقیماً از نمونهCompiledModel
خود با استفاده ازmodel.createInputBuffers()
وmodel.createOutputBuffers()
دریافت می کنید و سپس داده های ورودی خود را روی آنها می نویسید و نتایج را از روی آنها می خوانید. -
model.run()
: این تابعی است که استنتاج را اجرا می کند. شما بافرهای ورودی و خروجی را به آن منتقل می کنید و LiteRT وظیفه پیچیده اجرای مدل را بر روی شتاب دهنده سخت افزاری انتخاب شده انجام می دهد.
6. اجرای Initial ImageSegmentationHelper را تمام کنید
حالا نوبت نوشتن کد است. پیاده سازی اولیه ImageSegmentationHelper.kt
را تکمیل خواهید کرد. این شامل راه اندازی کلاس خصوصی Segmenter
برای نگه داشتن مدل LiteRT و پیاده سازی تابع cleanup()
برای انتشار صحیح آن است.
- کلاس
Segmenter
و تابعcleanup()
را به پایان برسانید : در فایلImageSegmentationHelper.kt
، یک اسکلت برای یک کلاس خصوصی به نامSegmenter
و یک تابع به نامcleanup()
پیدا خواهید کرد. ابتدا کلاسSegmenter
را با تعریف سازنده آن برای نگه داشتن مدل، ایجاد خصوصیات برای بافرهای ورودی/خروجی، و افزودن متدclose()
برای آزادسازی مدل، تکمیل کنید. سپس، تابعcleanup()
را برای فراخوانی متدclose()
جدید پیاده سازی کنید.کلاسSegmenter
و تابعcleanup()
را با موارد زیر جایگزین کنید: (~خط 83)private class Segmenter( // Add this argument private val model: CompiledModel, private val coloredLabels: List<ColoredLabel>, ) { // Add these private vals private val inputBuffers = model.createInputBuffers() private val outputBuffers = model.createOutputBuffers() fun cleanup() { // cleanup buffers inputBuffers.forEach { it.close() } outputBuffers.forEach { it.close() } // cleanup model model.close() } }
- روش to Accelerator را تعریف کنید : این روش فهرستهای شتابدهنده تعریفشده را از منوی شتابدهنده به فهرستهای شتابدهنده مخصوص ماژولهای LiteRT وارداتی نگاشت میکند (~خط 225):
fun toAccelerator(acceleratorEnum: AcceleratorEnum): Accelerator { return when (acceleratorEnum) { AcceleratorEnum.CPU -> Accelerator.CPU AcceleratorEnum.GPU -> Accelerator.GPU } }
- راه اندازی
CompiledModel
: حالا تابعinitSegmenter
پیدا کنید. اینجاست که شما نمونهCompiledModel
ایجاد میکنید و از آن برای نمونهسازی کلاسSegmenter
که اکنون تعریف شدهاید، استفاده میکنید. این کد مدل را با شتاب دهنده مشخص شده (CPU یا GPU) تنظیم می کند و آن را برای استنتاج آماده می کند.TODO
درinitSegmenter
با پیاده سازی زیر جایگزین کنید (Cmd/Ctrl+f 'initSegmenter' یا ~line 62):cleanup() try { withContext(singleThreadDispatcher) { val model = CompiledModel.create( context.assets, "selfie_multiclass.tflite", CompiledModel.Options(toAccelerator(acceleratorEnum)), null, ) segmenter = Segmenter(model, coloredLabels) Log.d(TAG, "Created an image segmenter") } } catch (e: Exception) { Log.i(TAG, "Create LiteRT from selfie_multiclass is failed: ${e.message}") _error.emit(e) }
7. بخش بندی و پیش پردازش را شروع کنید
اکنون که یک مدل داریم، باید فرآیند تقسیم بندی را راه اندازی کنیم و داده های ورودی را برای مدل آماده کنیم.
تقسیم بندی ماشه
فرآیند تقسیم بندی در MainViewModel.kt
شروع می شود که فریم ها را از دوربین دریافت می کند.
MainViewModel.kt
باز کنید
- Trigger Segmentation from Camera Frames : توابع
segment
درMainViewModel
نقطه ورود برای وظیفه تقسیم بندی ما هستند. هر زمان که یک تصویر جدید از دوربین در دسترس باشد یا از گالری انتخاب شود، آنها فراخوانی می شوند. سپس این توابع متدsegment
را درImageSegmentationHelper
فراخوانی میکنند.TODO
ها را در هر دو تابعsegment
با زیر جایگزین کنید (خط ~107):// For ImageProxy (from CameraX) fun segment(imageProxy: ImageProxy) { segmentJob = viewModelScope.launch { imageSegmentationHelper.segment(imageProxy.toBitmap(), imageProxy.imageInfo.rotationDegrees) imageProxy.close() } } // For Bitmaps (from gallery) fun segment(bitmap: Bitmap, rotationDegrees: Int) { segmentJob = viewModelScope.launch { val argbBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true) imageSegmentationHelper.segment(argbBitmap, rotationDegrees) } }
تصویر را از قبل پردازش کنید
حالا بیایید به ImageSegmentationHelper.kt
برگردیم تا پیش پردازش تصویر را انجام دهیم.
ImageSegmentationHelper.kt
را باز کنید
- پیاده سازی تابع
segment
عمومی : این تابع به عنوان یک بسته بندی عمل می کند که تابعsegment
خصوصی را در کلاسSegmenter
فراخوانی می کند.TODO
با (~خط 95) جایگزین کنید:try { withContext(singleThreadDispatcher) { segmenter?.segment(bitmap, rotationDegrees)?.let { if (isActive) _segmentation.emit(it) } } } catch (e: Exception) { Log.i(TAG, "Image segment error occurred: ${e.message}") _error.emit(e) }
- پیاده سازی پیش پردازش : تابع
segment
خصوصی در کلاسSegmenter
جایی است که ما تغییرات لازم را روی تصویر ورودی انجام می دهیم تا آن را برای مدل آماده کنیم. این شامل مقیاس بندی، چرخش و عادی سازی تصویر است. سپس این تابع تابعsegment
خصوصی دیگری را برای انجام استنتاج فراخوانی می کند. تابعTODO
درsegment(bitmap: Bitmap, ...)
با (~خط 121) جایگزین کنید:val totalStartTime = SystemClock.uptimeMillis() val rotation = -rotationDegrees / 90 val (h, w) = Pair(256, 256) // Preprocessing val preprocessStartTime = SystemClock.uptimeMillis() var image = bitmap.scale(w, h, true) image = rot90Clockwise(image, rotation) val inputFloatArray = normalize(image, 127.5f, 127.5f) Log.d(TAG, "Preprocessing time: ${SystemClock.uptimeMillis() - preprocessStartTime} ms") // Inference val inferenceStartTime = SystemClock.uptimeMillis() val segmentResult = segment(inputFloatArray) Log.d(TAG, "Inference time: ${SystemClock.uptimeMillis() - inferenceStartTime} ms") Log.d(TAG, "Total segmentation time: ${SystemClock.uptimeMillis() - totalStartTime} ms") return SegmentationResult(segmentResult, SystemClock.uptimeMillis() - inferenceStartTime)
8. استنتاج اولیه با LiteRT
با داده های ورودی پیش پردازش شده، اکنون می توانیم استنتاج اصلی را با استفاده از LiteRT اجرا کنیم.
ImageSegmentationHelper.kt
را باز کنید
- اجرای مدل پیاده سازی : تابع
segment(inputFloatArray: FloatArray)
جایی است که ما مستقیماً با متدrun()
LiteRT تعامل داریم. داده های از پیش پردازش شده خود را در بافر ورودی می نویسیم، مدل را اجرا می کنیم و نتایج را از بافر خروجی می خوانیم.TODO
در این تابع با (~خط 188) جایگزین کنید:val (h, w, c) = Triple(256, 256, 6) // MODEL EXECUTION PHASE val modelExecStartTime = SystemClock.uptimeMillis() // Write input data - measure time val bufferWriteStartTime = SystemClock.uptimeMillis() inputBuffers[0].writeFloat(inputFloatArray) val bufferWriteTime = SystemClock.uptimeMillis() - bufferWriteStartTime Log.d(TAG, "Buffer write time: $bufferWriteTime ms") // Optional tensor inspection logTensorStats("Input tensor", inputFloatArray) // Run model inference - measure time val modelRunStartTime = SystemClock.uptimeMillis() model.run(inputBuffers, outputBuffers) val modelRunTime = SystemClock.uptimeMillis() - modelRunStartTime Log.d(TAG, "Model.run() time: $modelRunTime ms") // Read output data - measure time val bufferReadStartTime = SystemClock.uptimeMillis() val outputFloatArray = outputBuffers[0].readFloat() val outputBuffer = FloatBuffer.wrap(outputFloatArray) val bufferReadTime = SystemClock.uptimeMillis() - bufferReadStartTime Log.d(TAG, "Buffer read time: $bufferReadTime ms") val modelExecTime = SystemClock.uptimeMillis() - modelExecStartTime Log.d(TAG, "Total model execution time: $modelExecTime ms") // Optional tensor inspection logTensorStats("Output tensor", outputFloatArray) // POSTPROCESSING PHASE val postprocessStartTime = SystemClock.uptimeMillis() // Process mask from model output val inferenceData = InferenceData(width = w, height = h, channels = c, buffer = outputBuffer) val mask = processImage(inferenceData) val postprocessTime = SystemClock.uptimeMillis() - postprocessStartTime Log.d(TAG, "Postprocessing time (mask creation): $postprocessTime ms") return Segmentation( listOf(Mask(mask, inferenceData.width, inferenceData.height)), coloredLabels, )
9. پس پردازش و نمایش پوشش
پس از اجرای استنتاج، یک خروجی خام از مدل دریافت می کنیم. ما باید این خروجی را پردازش کنیم تا یک ماسک تقسیمبندی بصری ایجاد کنیم و سپس آن را روی صفحه نمایش دهیم.
ImageSegmentationHelper.kt
را باز کنید
- پیاده سازی پردازش خروجی : تابع
processImage
خروجی نقطه شناور خام از مدل را به یکByteBuffer
تبدیل می کند که نمایانگر ماسک تقسیم بندی است. این کار را با یافتن کلاسی با بیشترین احتمال برای هر پیکسل انجام می دهد.TODO
آن را با (~خط 238) جایگزین کنید:val mask = ByteBuffer.allocateDirect(inferenceData.width * inferenceData.height) for (i in 0 until inferenceData.height) { for (j in 0 until inferenceData.width) { val offset = inferenceData.channels * (i * inferenceData.width + j) var maxIndex = 0 var maxValue = inferenceData.buffer.get(offset) for (index in 1 until inferenceData.channels) { if (inferenceData.buffer.get(offset + index) > maxValue) { maxValue = inferenceData.buffer.get(offset + index) maxIndex = index } } mask.put(i * inferenceData.width + j, maxIndex.toByte()) } } return mask
MainViewModel.kt
باز کنید
- جمع آوری و پردازش نتایج تقسیم بندی : اکنون به
MainViewModel
برمی گردیم تا نتایج تقسیم بندی را ازImageSegmentationHelper
پردازش کنیم.segmentationUiShareFlow
SegmentationResult
جمع آوری می کند، ماسک را به یکBitmap
رنگارنگ تبدیل می کند و آن را در اختیار UI قرار می دهد.TODO
در ویژگیsegmentationUiShareFlow
با (~خط 63) جایگزین کنید - کد را جایگزین نکنید، فقط بدنه را پر کنید:viewModelScope.launch { imageSegmentationHelper.segmentation .filter { it.segmentation.masks.isNotEmpty() } .map { val segmentation = it.segmentation val mask = segmentation.masks[0] val maskArray = mask.data val width = mask.width val height = mask.height val pixelSize = width * height val pixels = IntArray(pixelSize) val colorLabels = segmentation.coloredLabels.mapIndexed { index, coloredLabel -> ColorLabel(index, coloredLabel.label, coloredLabel.argb) } // Set color for pixels for (i in 0 until pixelSize) { val colorLabel = colorLabels[maskArray[i].toInt()] val color = colorLabel.getColor() pixels[i] = color } // Get image info val overlayInfo = OverlayInfo(pixels = pixels, width = width, height = height) val inferenceTime = it.inferenceTime Pair(overlayInfo, inferenceTime) } .collect { flow.emit(it) } }
view/SegmentationOverlay.kt
باز کنید
قطعه نهایی این است که وقتی کاربر به سمت دوربین جلو می رود، روکش تقسیم بندی را به درستی جهت دهی می کند. فید دوربین به طور طبیعی برای دوربین جلو منعکس می شود، بنابراین باید همان چرخش افقی را روی Bitmap
همپوشانی خود اعمال کنیم تا مطمئن شویم که به درستی با پیش نمایش دوربین همراستا می شود.
- Handle Overlay Orientation :
TODO
در فایلSegmentationOverlay.kt
پیدا کرده و کد زیر را جایگزین آن کنید. این کد بررسی میکند که آیا دوربین جلو فعال است یا نه، قبل از اینکه رویCanvas
کشیده شود، یک چرخش افقی رویBitmap
اعمال میکند. (~خط 42):val orientedBitmap = if (lensFacing == CameraSelector.LENS_FACING_FRONT) { // Create a matrix for horizontal flipping val matrix = Matrix().apply { preScale(-1f, 1f) } Bitmap.createBitmap(image, 0, 0, image.width, image.height, matrix, false).also { image.recycle() } } else { image }
10. برنامه نهایی را اجرا و استفاده کنید
اکنون تمام تغییرات کد لازم را تکمیل کرده اید. وقت آن است که برنامه را اجرا کنید و کار خود را در عمل ببینید!
- برنامه را اجرا کنید : دستگاه Android خود را وصل کنید و روی Run در نوار ابزار Android Studio کلیک کنید.
- ویژگیها را آزمایش کنید : پس از راهاندازی برنامه، باید فید دوربین زنده را با یک پوشش تقسیمبندی رنگارنگ ببینید.
- تغییر دوربین : روی نماد چرخش دوربین در بالا ضربه بزنید تا بین دوربین های جلو و عقب جابجا شوید. توجه کنید که روکش چگونه به درستی جهت گیری می کند.
- تغییر شتاب دهنده : روی دکمه "CPU" یا "GPU" در پایین ضربه بزنید تا شتاب دهنده سخت افزاری تغییر کند. تغییر در زمان استنتاج نمایش داده شده در پایین صفحه را مشاهده کنید. GPU باید به طور قابل توجهی سریعتر باشد.
- استفاده از تصویر گالری : برای انتخاب تصویر از گالری عکس دستگاه خود، روی برگه "گالری" در بالا ضربه بزنید. برنامه تقسیم بندی را روی تصویر ثابت انتخاب شده اجرا می کند.
اکنون یک برنامه کاملاً کاربردی و بیدرنگ تقسیمبندی تصویر دارید که توسط LiteRT طراحی شده است!
11. پیشرفته (اختیاری): استفاده از NPU
این مخزن همچنین حاوی نسخه ای از برنامه است که برای واحدهای پردازش عصبی (NPU) بهینه شده است. نسخه NPU می تواند عملکرد قابل توجهی را در دستگاه هایی که دارای NPU سازگار هستند، افزایش دهد.
برای امتحان نسخه NPU، پروژه kotlin_npu/android
را در Android Studio باز کنید. کد بسیار شبیه به نسخه CPU/GPU است و برای استفاده از نماینده NPU پیکربندی شده است.
برای استفاده از نماینده NPU، باید در برنامه دسترسی زودهنگام ثبت نام کنید.
12. تبریک می گویم!
شما با موفقیت یک برنامه اندرویدی ساخته اید که با استفاده از LiteRT، بخش بندی تصاویر را در زمان واقعی انجام می دهد. شما یاد گرفته اید که چگونه:
- زمان اجرا LiteRT را در یک برنامه اندروید ادغام کنید.
- یک مدل تقسیم بندی تصویر TFLite را بارگیری و اجرا کنید.
- ورودی مدل را از قبل پردازش کنید.
- خروجی مدل را برای ایجاد یک ماسک تقسیم بندی پردازش کنید.
- از CameraX برای یک برنامه دوربین بیدرنگ استفاده کنید.
مراحل بعدی
- یک مدل تقسیم بندی تصویر متفاوت را امتحان کنید.
- با نمایندگان مختلف LiteRT (CPU، GPU، NPU) آزمایش کنید.