کاربران انتظار دارند برنامههای شخص ثالث عکسها و ویدیوها را با همان کیفیت بالای دوربین اصلی دستگاهشان ثبت کنند، صرفنظر از شرایط نور یا سطح سختافزار. ارائه این ثبات در سراسر بومسازگان درحال گسترش Android با ملاحظات معماری کلیدی همراه است. این جلسه نشان میدهد که چگونه خطوط لوله ضبط قوی و حرفهای بسازید که قابلیتهای دستگاه را به حداکثر میرساند و درعینحال در همه ابعاد قابلاطمینان باقی میماند.
جدیدترین پیشرفتها در CameraX را بررسی خواهیم کرد و به شما نشان خواهیم داد که چگونه قابلیتهای پیشرفتهای مانند «عکسهای حرکتی» و بهبودهای هوش مصنوعی دروندستگاهی را ازطریق خدمات Google Play، درکنار تجربههای فضایی بااستفاده از Jetpack XR SDK، ادغام کنید. همچنین به مهارت CameraX خود نگاهی خواهیم انداخت که به عامل کدنویسی انتخابی شما اجازه میدهد مفاهیم این گفتگو و موارد دیگر را بهطور خودکار پیادهسازی کند!
CameraXViewfinder پیشنمایشهای دوربین را در ضریبهای شکل مختلف مانند دستگاههای تاشو تطبیق میدهد. در CameraX 1.7، کنترلهای اشارهای و پشتیبانی از «چشمکزنی صفحه» اضافه شده است.ProjectedDeviceContext، CameraX میتواند مستقیماً از عینک تصویربرداری کند.آوانویسی
سلام به همه.
به جلسه ما خوش آمدید.
من امان هستم و اسکات هم با من است.
امروز واقعاً هیجانزدهایم که درباره اینکه چگونه میتوانید تجربههای دوربین خود را در دستگاههای مختلف با CameraX و هوش مصنوعی دروندستگاهی ارتقا دهید صحبت کنیم.
پس بیایید شروع کنیم.
به نحوه استفاده از تلفنهایتان فکر کنید.
ما اغلب از دوربین تلفن برای ثبت لحظات و همرسانی با عزیزانمان استفاده میکنیم.
دوربین به بخش اصلی تجربه تلفن هوشمند تبدیل شده است.
درواقع، دوربین خوب اغلب عامل کلیدی هنگام خرید تلفن هوشمند است.
امروز، دو موضوع کلی را برای ساختن تجربههای دوربین ممتاز پوشش خواهیم داد.
ابتدا، برخیاز ویژگیهای CameraX را کاوش میکنیم، و سپس، با برخیاز تکنیکهای یادگیری ماشین دروندستگاهی آشنا میشویم.
بیایید با CameraX شروع کنیم.
Jetpack CameraX تجربه دوربین را در Android تقویت میکند.
علاوهبر CameraX، ما Camera1 و Camera2 را نیز داریم که بخشی از چارچوب پلاتفرم Android هستند.
بنابراین، از کدام کتابخانه باید استفاده کنید؟
Camera1 میانای برنامهسازی کاربردی دوربین اصلی در Android بود.
این میانای برنامهسازی کاربردی اکنون منسوخ شده است، بنابراین برنامههای جدید باید از Camera2 یا CameraX استفاده کنند.
Camera2 جایگزین Camera1 شد.
بنابراین، اگر برای موارد استفاده پیچیده به کنترل دوربین سطح پایین نیاز دارید، Camera2 گزینه خوبی است.
بااینحال، این روش مستلزم آن است که پیکربندیهای مختص دستگاه را خودتان انجام دهید.
و درنهایت، Jetpack CameraX برپایه Camera2 ساخته شده است.
این میانای برنامهسازی کاربردی سازگار را ارائه میدهد و پیکربندیهای خاص دستگاه را بهطور خودکار مدیریت میکند.
این گزینه برای اکثر توسعهدهندگان توصیه میشود.
باید نکته بسیار مهمی را اعلام کنیم.
قصد داریم Camera1 را غیرفعال کنیم و این کار با اجرای خطمشی Play در سال آینده در Android 18 آغاز خواهد شد.
اگر هنوز از Camera1 API قدیمی استفاده میکنید، باید به CameraX یا Camera2 مهاجرت کنید.
بنابراین، چرا باید از CameraX استفاده کنید؟
اول اینکه از Android نسخه ۷ و بالاتر پشتیبانی میکند، بنابراین با تقریباً ۹۹٪ دستگاههای Android فعال سازگار است.
دوم اینکه استفاده از آن آسان است و میاناهای برنامهسازی کاربردی سادهای را برای موارد استفاده رایج مانند پیشنمایش، ضبط، و ضبط با کد کمتر ارائه میدهد.
سوم، با کاهش مشکلات مختص دستگاه، مانند کشیدگی پیشنما که در دستگاههای تاشو و رایانههای لوحی بسیار رایج است، تجربهای یکنواخت در دستگاههای مختلف ارائه میدهد.
و درنهایت، پیادهسازی قابلیتهای پیشرفته OEM مانند حالت شب و چهره را ساده میکند.
اکنون میخواهم از اسکات دعوت کنم تا درباره برخیاز ویژگیهای CameraX صحبت کند.
نوبت شما است، اسکات.
ممنون ممنون، امان.
سلام به همه.
نام من اسکات است.
و امروز، چندین ویژگی CameraX را پوشش خواهیم داد تا بتوانید تجربه دوربین حرفهای ایجاد کنید.
و اولین مورد «نمایاب تطبیقی» است.
این ابزار طراحی شده است تا مشکل پیشنمایش دسته را در اندازههای مختلف صفحهنمایش و ضریب شکل بهطور کامل برطرف کند.
کاربران اکنون دستگاههای مختلفی دارند، ازجمله دستگاه تاشو، تلفن، رایانه کیفی، و دستگاههای XR.
بنابراین، ساختن تجربه دوربین سازگار با ابعاد جدید از همیشه مهمتر است.
و پیشنمایش دوربین بهطور بدنامی برای درست شدن دشوار است، بهویژه در دستگاه تاشو با صفحهنمایش عریض.
در این دستگاهها، وقتی باز میشوند، دوربین داخلی بهصورت افقی و دوربین پشتی بهصورت عمودی نصب میشود.
بنابراین، اگر فرض کنید همه حسگرهای دوربین بهصورت عمودی نصب شدهاند، این امر منجر به عدم تطابق فضایی و ایجاد اعوجاج یا کج شدن تصویر میشود.
و CameraXViewfinder، بهگونهای طراحی شده است که پیچیدگی را کاملاً برای شما مدیریت کند، بنابراین اطمینان حاصل میکند که پیشنمایش دوربین شما بهطور یکپارچه در همه ضرایب شکل کار میکند.
و بیایید ببینیم چگونه از ترکیبپذیر CameraXViewfinder استفاده کنیم.
var surfaceRequest by remember {
mutableStateOf<SurfaceRequest?>(null)
}
LaunchedEffect(...) {
preview.setSurfaceProvider { surfaceRequest = it }
cameraProvider.bindToLifecycle(..., preview)
}
surfaceRequest?.let { request ->
CameraXViewfinder(
surfaceRequest = request,
contentScale = ContentScale.Fit
)
}ابتدا، متغیر محتوای surfaceRequest را برای نگهداری درخواست سطح اعلام میکنید.
متأسفیم.
و دوم، دوم، در LaunchedEffect، ارائهدهنده سطح را برای گرفتن شیء درخواست سطح تنظیم میکنید.
و سپس، به چرخه حیات متصل میشوید و سپس دوربین را راهاندازی میکنید.
و درنهایت، هرگاه وضعیت با درخواست سطح معتبر بهروز شود و بازترکیب را راهاندازی کند، درخواست را مستقیماً روی CameraXViewfinder تنظیم میکنید.
و در اینجا میتوانید contentScale را بسته به اولویتتان روی «متناسب کردن» یا «برش دادن» تنظیم کنید.
همین، همه چیز آماده است.
در CameraX 1.7، چندین ویژگی جدید به CameraXViewfinder اضافه کردهایم.
این شامل اشارههای جدیدی مثل تکضرب زدن برای فوکوس کردن یا نیشگون لمسی برای بزرگنمایی میشود.
همچنین از «فلاش صفحهنمایش» پشتیبانی میکند که به شما امکان میدهد از صفحهنمایش برای روشن کردن سلفی استفاده کنید.
پیادهسازی این ویژگی آسان است.
CameraXViewfinder(
surfaceRequest = viewModel.surfaceRequest,
contentScale = ContentScale.Fit,
isTapToFocusEnabled = true,
isPinchToZoomEnabled = true,
onScreenFlashReady =
{
imageCapture.screenFlash = it
imageCapture.flashMode = FLASH_MODE_SCREEN
}
)فقط باید «تکضرب زدن برای تمرکز» و «نزدیک یا دور کردن انگشتان برای بزرگنمایی» را روی «درست» تنظیم کنید.
و برای «چشمکزنی صفحه»، نمونه «چشمکزنی صفحه» را از بازخوان onScreenFlashReady دریافت میکنید و سپس به ImageCapture ارسال میکنید و حالت چشمکزنی را روی FLASH_MODE_SCREEN تنظیم میکنید.
و همین.
دیدیم که CameraX چگونه «پیشنمایش» دوربین را فعال میکند که بهطور یکپارچه در همه ابعاد کار میکند.
اکنون بیایید با ضبط دوربین در Android XR یک گام فراتر برویم.
بنابراین سه دسته دستگاه واقعیت گسترده وجود دارد: اول، هدستهای واقعیت گسترده.
این هدستها کاملاً بسته و فراگیر هستند و از دوربین برای ضبط دنیای فیزیکی و نمایش آن در داخل هدست استفاده میکنند.
و دوم، عینک واقعیت گسترده سیمی.
امروز در سخنرانی اصلی در این دسته، XREAL Aura را معرفی کردیم.
و همانطور که در سخنرانیهای کلیدی میبینید، این عینکهای شفاف به دیسک محاسباتی متصل هستند و به شما امکان میدهند رونهاد محتوای مجازی را روی دنیای واقعی مشاهده کنید.
و سومی، «عینک نمایشگردار و صوتی».
این عینک سبک است.
این دستگاهها میتوانند صدا، دوربین، و نمایشگر را برای تجربههای دستآزاد و قابلمشاهده با یک نگاه ترکیب کنند.
And the first two category, XR headset and Wired XR Glasses, they also support Camera2 and CameraX, just like a regular phone.
بنابراین امروز فقط روی صدا و «عینک نمایشگردار» تمرکز خواهیم کرد.
در این عینک، برنامهها بهجای عینک در تلفن Android، تلفن Android میزبان، اجرا میشوند، اما در اینجا به دوربین تلفن و دوربین عینک دسترسی دارند.
سؤال کلیدی این است که چطور میدانید با کدام دوربین صحبت میکنید؟
پس ساده است.
با Context استاندارد، CameraX به دوربین تلفن دسترسی پیدا میکند.
و با ProjectedDeviceContext، CameraX به دوربین عینک دسترسی پیدا میکند.
بنابراین «بافت» متفاوت به دوربین هدف متفاوت منجر خواهد شد.
و بهیاد داشته باشید که اجازه دوربین و عینک جدا هستند.
اجازه تلفن و عینک جدا هستند، بنابراین باید اجازه عینک را جداگانه درخواست کنید.
گزینه دیگر این است که میتوانید از Projected Activity نیز استفاده کنید که آن هم در دستگاههای Android میزبان اجرا میشود، اما میانای کاربر شما روی عینک نمایش داده میشود.
و اینجا قانون برعکس میشود.
اگر از «زمینه» استاندارد استفاده میکنید، به دوربین عینک دسترسی خواهید داشت.
اگر میخواهید به دوربین تلفن دسترسی داشته باشید، باید از HostContext استفاده کنید.
و بیایید به کد نگاه کنیم، ببینیم چگونه میتوانیم بااستفاده از دوربین عینک ضبط کنیم.
// In a coroutine
val imageCapture = ImageCapture.Builder().build()
ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext)
.collectLatest { isConnected ->
if (isConnected) {
val projectedContext = ProjectedContext.createProjectedDeviceContext(activity)
val cameraConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
.setRepeatingStreamForced(false)
.build()
val cameraProvider = LifecycleCameraProvider.createInstance(projectedContext, cameraConfig)
cameraProvider.bindToLifecycle(activity, DEFAULT_BACK_CAMERA, imageCapture)
}
}
// Shutter button clicked
imageCapture.takePicture(...)ابتدا، isProjectedDeviceConnected را فراخوانی میکنید تا ببینید عینک متصل است یا نه.
و سپس با createProjectedDeviceContext تماس میگیرید تا projectedContext دریافت کنید.
و دوم، در اینجا از LifecycleCameraProvider برای مقداردهی اولیه CameraX استفاده میکنید، نه ProcessCameraProvider.
بنابراین ProcessCameraProvider یک سینگلتون است که نمیتوانید Context را پساز مقداردهی اولیه تغییر دهید.
بنابراین استفاده از LifecycleCameraProvider به شما امکان میدهد هر زمان که خواستید Context را تغییر دهید.
و سپس، باید جاریسازی تکراری اضافی را در CameraX غیرفعال کنید.
این کار لازم است چون عینکهای امروزی فقط از یک جاریسازی پشتیبانی میکنند.
و میانای برنامهسازی کاربردی درحالحاضر داخلی است، اما بهزودی آن را عمومی خواهیم کرد.
و همین.
از اینجا CameraX استاندارد است، فقط ImageCapture را متصل میکنید و اکنون ازطریق عینک عکس میگیرید.
بسیارخوب.
سپس، بیایید ببینیم چگونه CameraX به شما کمک میکند ترکیب ویژگیهایی را که باهم کار میکنند پیدا کنید و بهترین نتیجه را از دستگاه بگیرید.
برنامههای دوربین مدرن با ویژگیهای قدرتمند تعریف میشوند و کاربر انتظار دارد که دوربین از ویژگیهایی مانند HDR، ۶۰ فریم در ثانیه، و «لرزشگیر پیشدید» بهطور همزمان پشتیبانی کند.
اما بهعنوان توسعهدهنده میدانیم که ترکیب این ویژگیها با یکدیگر همیشه ساده نیست.
بررسی پشتیبانی از ویژگیهای پشتیبانی تکی تضمین نمیکند که آنها باهم کار کنند.
و این عدم قطعیت توسعهدهنده را مجبور کرده است که محافظهکار باشد و آنچه را که کاربر میتواند در دستگاههای توانمند تجربه کند محدود کند.
برای حل این مشکل، Feature Group API را معرفی کردیم که از چارچوب پُرسمان ترکیب ویژگی Camera2 بهره میبرد.
و دو راه برای استفاده از آن وجود دارد: اول، از requiredFeatureGroup برای بررسی و فعال کردن ترکیب ویژگی درصورت پشتیبانی استفاده کنید.
و دوم، میتوانید اولویت ویژگیتان را با preferredFeatureGroup ارائه دهید و سپس CameraX بهترین ترکیب پشتیبانیشده را برای شما انتخاب میکند.
و این هم مثال.
در سمت راست، در سمت چپ، ببخشید، بدون فعال کردن چیزی، فقط یک تلفن معمولی، ضبط معمولی.
در سمت راست، HDR، ۶۰ فریم در ثانیه، و «لرزشگیر پیشنمایش» را باهم فعال میکنیم.
و میتوانید ببینید که کیفیت ویدیو با روشنتر، روانتر، و بدون لرزش شدن بهطور قابلتوجهی بهبود یافته است.
و بیایید به کد نگاه کنیم.
cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
SessionConfig(
useCases = listOf(preview, videoCapture),
// HDR_HLG10 > FPS_60 > Preview Stabilization.
preferredFeatureGroup =
listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),
)
)بنابراین در اینجا از preferredFeatureGroup برای ارائه فهرست اولویت ویژگیهایمان استفاده میکنیم که عبارت است از HDR، سپس ۶۰ فریم در ثانیه، و سپس «لرزشگیر پیشنمایش».
و CameraX ترکیب را بررسی میکند و بهترین ترکیبهای پشتیبانیشده توسط دستگاه را انتخاب میکند.
با CameraX 1.6 به بالا، «ضبط 4K» و «لرزشگیر ویدیو» را مستقیماً به Feature Group API اضافه میکنیم.
// CameraX 1.6+ supports:
// 4K, video stabilization & Effect
val sessionConfig = SessionConfig(
useCases = listOf(preview, videoCapture),
requiredFeatureGroup =
setOf(UHD_RECORDING, VIDEO_STABILIZATION),
effects = listOf(overlayEffect)
)بسیاری از دستگاههای ممتاز از ترکیب 4K و «لرزشگیر ویدیو» پشتیبانی میکنند.
اکنون با Feature Group API میتوانید آنها را با اطمینان باهم فعال کنید.
علاوهبراین، اکنون این انعطافپذیری را دارید که جلوه همزمان را با Feature Group API اعمال کنید، که قبلاً امکانپذیر نبود.
اکنون بیایید درباره نحوه دریافت ضبط تصویر با کیفیت بالا در CameraX صحبت کنیم.
CameraX اکنون از ضبط DNG RAW پشتیبانی میکند.
این ویژگی به شما امکان میدهد برای ویرایش حرفهای، مستقیماً از حسگر دوربین به تصویر RAW پردازشنشده و فشردهنشده دسترسی داشته باشید.
و در ادامه، افزونه «حالت شب» را بهبود بخشیدهایم.
این افزونه برای گرفتن عکسهای روشن و واضح در نور کم بدون استفاده از فلاش فعال شده است.
و میانای برنامهسازی کاربردی نشانگر حالت شب جدیدی را معرفی کردهایم.
این میانای برنامهسازی کاربردی تاریک بودن محیط فعلی و مناسب بودن آن برای «حالت شب» را پایش میکند و میتوانید از آن برای نشان دادن نشانگر «حالت شب» استفاده کنید.
و Ultra HDR اکنون در افزونههای CameraX پشتیبانی میشود.
این کار باعث میشود «حالت شب» دامنه پویای گستردهتر و رنگهای زندهتری داشته باشد و «حالت شب» با «وضوح فوقالعاده بالا» درحالحاضر در بسیاری از دستگاههای ممتاز اخیر پشتیبانی میشود.
در اینجا میتوانید تفاوت بین حالت عادی، شب، و «حالت شب» با Ultra HDR را ببینید.
بنابراین، بیایید ببینیم چگونه این را پیادهسازی کنیم.
val cameraInfo = cameraProvider.getCameraInfo(cameraSelector,
ExtensionSessionConfig(ExtensionMode.NIGHT, extensionsManager))
val imageCapabilities = ImageCapture.getImageCaptureCapabilities(cameraInfo)
val imageCapture = ImageCapture.Builder().apply {
if (imageCapabilities.supportedOutputFormats
.contains(OUTPUT_FORMAT_JPEG_ULTRA_HDR)) {
setOutputFormat(OUTPUT_FORMAT_JPEG_ULTRA_HDR)
}
}.build()
val extensionSessionConfig = ExtensionSessionConfig(
ExtensionMode.NIGHT, extensionsManager, preview, imageCapture
)
cameraProvider.bindToLifecycle(..., extensionSessionConfig)ابتدا یک ExtensionSessionConfig برای «حالت شب» ایجاد میکنید و از آن برای دریافت نمونه CameraInfo استفاده میکنید.
سپس، از CameraInfo برای دریافت قابلیت ضبط تصویر استفاده میکنید و سپس پرسوجو میکنید که آیا Ultra HDR پشتیبانی میشود یا نه.
و سپس قالب خروجی را روی Ultra HDR تنظیم میکنید.
و درنهایت، ExtensionSessionConfig را دوباره با موارد استفاده ایجاد میکنید و به چرخه حیات متصل میکنید.
اکنون آمادهاید تا با «حالت شب» عکسهای Ultra HDR بگیرید.
همچنین پشتیبانی از «عکسهای حرکتی» را به CameraX اضافه میکنیم.
«عکسهای حرکتی» صدا و حرکت اطراف عکس را ضبط میکند و لحظه را حفظ میکند، نه فقط یک تصویر ثابت.
ما این را در تلفنها و دستگاههای Android مختلف استانداردسازی میکنیم و پشتیبانی از «عکسهای حرکتی» را آسانتر میکنیم.
«عکسهای حرکتی» در سهماهه اول ۲۰۲۷ به CameraX اضافه میشود.
وقتی دردسترس قرار گرفت، کافی است حالت «عکسهای حرکتی» را در ImageCapture.Builder تنظیم کنید و اکنون میتوانید شروع به ضبط «عکسهای حرکتی» کنید.
val imageCapture = ImageCapture.Builder()
.setMotionPhotoMode(
ImageCapture.MOTION_PHOTO_MODE_ON,
customConfig
)
.build()
cameraProvider.bindToLifecycle(.. imageCapture)
imageCapture.takePicture(...)سپس، بیایید بررسی کنیم که CameraX چگونه ضبط ویدیو با سرعت بالا را فعال میکند.
ضبط واضح کنشهای سریع یکی از ویژگیهای ضروری برنامههای دوربین مدرن است.
این حالت بهویژه برای سناریوهای سریع مانند ورزش مفید است.
این کار ازطریق ضبط با سرعت بالا در ۱۲۰ یا ۲۴۰ فریم در ثانیه انجام میشود.
دو روش برای استفاده از آن وجود دارد.
ابتدا، کدگذاری با همان نرخ فریم، سپس یک ویدیو با نرخ فریم بالا واقعی ایجاد خواهید کرد.
گزینه دیگر این است که آن را با نرخ ۳۰ فریم در ثانیه کدبندی کنید تا یک ویدیو حرکت آهسته آماده پخش ایجاد شود.
برای مثال، یک ثانیه ضبطشده با سرعت ۱۲۰ فریم در ثانیه، چهار ثانیه طول میکشد تا با سرعت ۳۰ فریم در ثانیه پخش شود.
بنابراین، این کشش زمانی جلوه حرکت آهسته ایجاد میکند.
و برای پیادهسازی ضبط با سرعت بالا، HighSpeedVideoSessionConfig را با videoCapture ایجاد میکنید.
val recorder = Recorder.Builder().build()
val videoCapture = VideoCapture.withOutput(recorder)
val frameRateRange = cameraInfo.getSupportedFrameRateRanges(
HighSpeedVideoSessionConfig(videoCapture, preview)).first()
val sessionConfig = HighSpeedVideoSessionConfig(
videoCapture,
preview,
frameRateRange = frameRateRange,
isSlowMotionEnabled = true
)
cameraProvider.bindToLifecycle(
lifecycleOwner, cameraSelector, sessionConfig)
// Start recording slow motion videos.
val recording = recorder.prepareRecording(context, outputOption)
.start(executor, {})And then you set isSlowMotionEnabled to true to encode it at 30 fps to create a slow-motion video, or set it to false to create a high speed frame rate video.
و وقتی حرکت آهسته فعال باشد، Google Photos بهطور خودکار آن را شناسایی میکند و نشان حرکت آهسته را به تصویر کوچک اضافه میکند.
و بیایید ببینیم این ویژگی در Google Photos چگونه کار میکند.
در اینجا حرکت آهسته جلوهای دراماتیک به آن گربه میدهد.
گربهام هیچوقت نمیخواهد به او دست بزنم.
بسیار متأسفیم.
درنهایت، بیایید برخیاز بهبودهای Concurrent API، Concurrent Camera API را ببینیم.
Concurrent API به برنامه شما اجازه میدهد بهطور همزمان از دوربین جلو و عقب جاریسازی کند.
امسال، بهبودهایی در Concurrent Camera API ایجاد کردیم.
میتوانید ترکیببندی دوربین را پساز اتصال به چرخه حیات و باز کردن دوربین بهطور پویا بهروز کنید.
بنابراین، این شامل تغییر موقعیت، اندازه، و ترتیب پرداز میشود.
همچنین میتوانید سبکهایی مثل گوشه گرد، یا عرض حاشیه، و رنگ حاشیه اضافه کنید.
و نحوه انجام آن به این صورت است: فقط از CompositionSettings برای پیکربندی سبکبندی مانند گرد کردن گوشه، عرض حاشیه، و رنگ استفاده کنید، و از setZOrder برای تنظیم ترتیب پردازش استفاده کنید.
val concurrentCamera =
cameraProvider.bindToLifecycle(list(config1, config2))
// CameraX 1.7 features: rounded corner, border width & color
val pipSettings = CompositionSettings.Builder()
.setOffset(0.2f, 0.2f)
.setScale(0.35f, 0.35f)
.setRoundedCornerRatio(0.15f)
.setBorderWidthRatio(0.02f)
.setBorderColor(Color.WHITE)
.setZOrder(1)
.build()
// CameraX 1.7 features: dynamical composition update
concurrentCamera.setCompositionSettings(
listOf(mainSettings, pipSettings))و در مرحله بعد، فقط setCompositionSettings را در دوربین همزمان فعال فراخوانی میکنید تا ترکیببندی را بهطور پویا بهروز کنید.
و همین.
میتوانید ترکیببندی را بهطور پویا بهروز کنید.
بسیارخوب.
اکنون همه ویژگیها را بررسی کردهایم.
در اینجا نکته مهمی داریم، بنابراین اکیداً توصیه میکنیم به CameraX 1.6 و نسخههای بالاتر ارتقا دهید.
این کار میتواند از خراب شدن دستگاههای آینده که از قالب جدید Eclipsa Video پشتیبانی میکنند جلوگیری کند و همچنین تضمین میکند که افزونه CameraX شما در بهروزرسانی آینده Pixel برای این ویژگی همچنان کار کند.
با این کار، اولین جلسهمان را به پایان میرسانیم.
ما ویژگیهای CameraX را که به شما کمک میکند تجربه دوربین در سطح حرفهای بسازید پوشش دادهایم، و اکنون میخواهم از امان دعوت کنم تا جلسه بعدی ما را درباره هوش مصنوعی دروندستگاهی پوشش دهد.
از شما، اسکات، برای راهنمایی ما در ویژگیهای CameraX سپاسگزاریم.
اکنون، بیایید نگاهی بیندازیم به اینکه چگونه میتوانید از یادگیری ماشین دروندستگاهی برای ساختن تجربههای دوربین هوشمند استفاده کنید.
یادگیری ماشین دروندستگاهی طیف گستردهای از موارد استفاده از دوربین را در دو حوزه اصلی امکانپذیر میکند.
اولین مورد پیشضبط است.
در اینجا میتوانید پیشنمایش دوربینتان را بهطور همزمان برای انجام کارهایی مثل تشخیص اشیا تجزیهوتحلیل کنید یا میتوانید خود پیشنمایش را بهبود دهید.
دومی پساز عکس گرفتن است.
پساز گرفتن تصویر، میتوانید کیفیت آن را بهبود دهید یا کارهایی مثل تولید شرح برای تصویر را انجام دهید.
اکنون، بیایید به برخی از راهحلهای یادگیری ماشین دروندستگاهی که برای برنامههای دوربین شما داریم بپردازیم.
اولین کتابخانه یادگیری ماشین دروندستگاهی که درباره آن صحبت خواهیم کرد ML Kit است.
ML Kit در درجه اول برای حل مشکلات رایج در حوزههای بینایی و زبان طبیعی مانند تشخیص نوشتار، تشخیص چهره، و ترجمه زبان ساخته شده است.
سال گذشته، ML Kit را معرفی کردیم.
در اینجا چند نمونه از میاناهای برنامهسازی کاربردی ML Kit Vision آورده شده است، برای مثال تشخیص مش چهره، بخشبندی سوژه، و تشخیص همزمان اشیا.
بله.
سال گذشته، GenAI API را معرفی کردیم.
این میاناهای برنامهسازی کاربردی از قدرت Gemini Nano برای فعال کردن قابلیتهایی مانند خلاصه کردن نوشتار، شرح تصویر، و موارد دیگر استفاده میکنند.
در اینجا نمونهای از شرح تصویر بااستفاده از Prompt API ارائه شده است.
ما نقشه بیتی تصویر را با پیامواره نوشتاری به مدل ارسال میکنیم و نتیجه بهصورت یک ربات کوچک سبز Android با طراحی شبیه کاکتوس که روی سطحی سیاه نشسته است خوانده میشود.
و همه اینها در دستگاه انجام میشود.
خیلی جالب است، نه؟
Adobe Photoshop از Prompt API برای بهبود تجربه کاربری در برنامهاش استفاده میکند.
هنگام ویرایش داراییها، کاربران اغلب باید چندین لایه را در برنامه Photoshop مدیریت کنند.
بااستفاده از Prompt API، اکنون برنامه میتواند بهطور خودکار نام لایههای نامنظم را تغییر دهد.
این برنامه همچنین از Prompt API برای تولید زیرنویس و هشتگ برای تصاویر استفاده میکند که برای همرسانی در رسانههای اجتماعی بسیار مفید است.
بیایید به کد شرح تصویر بااستفاده از Prompt API نگاهی بیندازیم.
// 1. Initialize the on-device GenerativeModel client
val generativeModel = Generation.getClient()
// 2. Check whether Gemini Nano is available on the device
val status = generativeModel.checkStatus()
when (status) {
FeatureStatus.UNAVAILABLE -> {}
FeatureStatus.DOWNLOADABLE -> {}
FeatureStatus.DOWNLOADING -> {}
FeatureStatus.AVAILABLE -> {}
}ابتدا به شیء کارخواه generativeModel نیاز دارید، سپس بررسی کنید که Gemini Nano در دستگاهتان دردسترس یا بارگیریشدنی است یا نه.
سپس، با ترکیب تصویر و پیامواره نوشتاری، درخواست محتوا تولید کنید و درنهایت این درخواست را به فراخوانی generateContent ارائه دهید تا استنتاج را در Gemini Nano اجرا کنید و شرح تصویرتان را دریافت کنید.
// 3. Build a multimodal request with the image and text prompt
val request = generateContentRequest(
ImagePart(bitmap),
TextPart("Describe this image in detail.")
) {}
// 4. Run inference on-device and get the description
val response = generativeModel.generateContent(request)
val description = response.candidates.first().textبله، کتابخانه یادگیری ماشین دروندستگاهی بعدی که داریم MediaPipe است.
MediaPipe طیف گستردهای از راهحلهای یادگیری ماشین را برای برنامههای شما ارائه میدهد.
میتوانید بهسرعت به وبسایت MediaPipe بروید تا نمونههای مختلفی مثل تشخیص چهره، تشخیص شیء، بخشبندی تصویر، و موارد دیگر را امتحان کنید.
همه این موارد استفاده بسیار شبیه به ML Kit هستند، بنابراین از کدام کتابخانه باید استفاده کنید؟
اگرچه MediaPipe و ML Kit مشکلات مشابهی را حل میکنند، اما چند تفاوت کلیدی وجود دارد.
اولین مورد پشتیبانی پلاتفرم است.
MediaPipe در Android، iOS، وب، و Python کار میکند، درحالیکه ML Kit فقط روی Android و iOS تمرکز دارد.
علاوهبراین، MediaPipe سفارشیسازی مدل را ارائه میدهد، درحالیکه ML Kit میاناهای برنامهسازی کاربردی آماده برای استفاده را ارائه میدهد.
و درنهایت، درحالیکه MediaPipe متنباز است، ML Kit یک کیت توسعه نرمافزار Google است.
در اینجا نمونهای از تشخیص اشاره بااستفاده از MediaPipe Vision API آورده شده است.
این API اشارههای دست را بهطور همزمان از ورودی دوربین تشخیص میدهد.
برای مثال، میتوان از این ویژگی برای شروع یا توقف ضبط دوربین بااستفاده از اشارههای ساده دست درحالیکه در فاصلهای از تلفن ایستادهاید استفاده کرد.
بیایید به کد نگاه کنیم.
بنابراین ابتدا باید شیء GestureRecognizerOptions را با ارائه مسیر مدل، حالت جاریسازی زنده، و شنونده نتیجه بسازید.
// 1. Build GestureRecognizerOptions
val options = GestureRecognizerOptions.builder().setBaseOptions(
BaseOptions.builder().setModelAssetPath(MP_RECOGNIZER_TASK).build())
.setRunningMode(RunningMode.LIVE_STREAM)
.setResultListener { result, image -> /* Handle detected gesture */ }
.build()
// 2. Create the GestureRecognizer
val gestureRecognizer = GestureRecognizer.createFromOptions(context, options)
// 3. Convert CameraX ImageProxy to MediaPipe MPImage
val bitmap = rotateBitmap(imageProxy.toBitmap(),
imageProxy.imageInfo.rotationDegrees)
val mpImage = BitmapImageBuilder(bitmap).build()
// 4. Run gesture recognition on the image
gestureRecognizer.recognizeAsync(mpImage, frameTime)سپس یک شیء تشخیص اشاره از گزینهها ایجاد کنید.
سپس، CameraX ImageProxy را دریافت کنید و آن را به نقشه بیتی تبدیل کنید، و از نقشه بیتی برای تولید شیء MediaPipe mpImage استفاده کنید.
درنهایت، باید شیء MediaPipe mpImage را به فراخوانی recognizeAsync ارائه دهید تا اشارهها را در زمان واقعی تشخیص دهد.
در مرحله بعد، «بهبود تصویر و ویدیو دروندستگاهی» را داریم که ازطریق میانای برنامهسازی کاربردی «خدمات Google Play» دردسترس است.
این مدل قابلیتهایی مثل نگاشت رنگ، رفع تاری، و «اَبَر وضوح» را در اکثر دستگاههای پولی ارائه میدهد.
در اینجا نمونهای از تصویر نگاشتشده و ارتقایافته بااستفاده از این مدل آورده شده است.
میتوانید رنگهای غنیتر در آسمان و دریا و وضوح بهبودیافته را بهوضوح در تصویر بهبودیافته ببینید.
بیایید به کد «بهبود تصویر» نگاه کنیم.
// Step 1: Verify device support & install module
val enhancementClient = Enhancement.getClient(context)
suspend fun prepare(): Boolean {
if (!enhancementClient.isDeviceSupported().await()) return false
if (!enhancementClient.isModuleInstalled().await()) {
check(enhancementClient.installModule().await()) {
"Enhancement module install failed"
}
}
return true
}ابتدا باید پرسوجو کنید که آیا دستگاه اصلی از آن پشتیبانی میکند یا نه، و درصورت لزوم، باید پیمانه بهبود را در دستگاهتان نصب کنید.
سپس، شیء EnhancementOptions را با مشخص کردن نقشه بیتی و بهبودهایی که میخواهید انتخاب کنید ایجاد کنید.
// Step 2: Select Enhancement options & create session
val options = EnhancementOptions(
width = bitmap.width,
height = bitmap.height,
enhancementMode = EnhancementMode.BITMAP,
isTonemappingEnabled = true,
isDeblurAndDenoisePhotoEnabled = true,
isDeblurAndDenoiseVideoEnabled = false,
isUpscalePhotoEnabled = false,
isUpscaleVideoEnabled = false
)
val enhancementSession = enhancementClient
.createSessionAsync(options, enhancementExecutor)سپس، یک جلسه کارخواه با میانای برنامهسازی کاربردی «خدمات Google Play» ایجاد کنید، سپس یک callback برای مدیریت بیتمپ حاصل تعریف کنید، و درنهایت، تابع پردازش را با بیتمپ و گزینههای بهبود تصویر فراخوانی کنید.
// Step 3: Process Enhancement
val enhancedBitmap = suspendCancellableCoroutine { continuation ->
val enhancementCallback = object : EnhancementCallback {
override fun onBitmapProcessed(result: Bitmap) {
if (continuation.isActive) continuation.resume(result)
}
override fun onError(statusCode: Int) {
if (continuation.isActive) {
continuation.resumeWithException(
Exception("Bitmap processing failed: $statusCode")
)
}
}
override fun onSurfaceProcessed(timestamp: Long) {}
}
enhancementSession.process(bitmap, options, enhancementCallback)
}اخیراً میاناهای برنامهسازی کاربردی «بهبود ویدیو» را در Jetpack Media3 ادغام کردهایم.
برای کسب اطلاعات بیشتر درباره Image and Video Enhancement API، میتوانید به گفتگوی رسانهای ما مراجعه کنید.
آخرین ویژگی «یادگیری ماشین در دستگاه» که میخواهیم پوشش دهیم «تقویت نور کم» است.
عکاسی در نور کم یکی از مشکلات رایج کاربران تلفن هوشمند است.
کاربرانی را درنظر بگیرید که در محیطهای کمنور عکس میگیرند یا تماس ویدیویی برقرار میکنند.
«تقویت نور کم» این مشکل را با روشنتر کردن پیشنمایش دوربین بهطور همزمان حل میکند.
دو نوع «تقویت نور کم» وجود دارد: سختافزاری و نرمافزاری.
نسخه سختافزاری از سختافزار OEM برای بهبود قابهای کمنور بدون استفاده از یادگیری ماشین استفاده میکند.
درحالیکه، در مقابل، نسخه نرمافزاری از مدل یادگیری ماشین دروندستگاهی برای بهبود فریمها استفاده میکند.
بیایید بهسرعت نگاهی بیندازیم به اینکه چگونه میتوانیم CameraX را با «تقویت نور کم نرمافزاری» که با نام «تقویت نور کم Google» نیز شناخته میشود ادغام کنیم.
«بهینهسازی نور کم Google» به دو سطح کلیدی نیاز دارد: اول، به یک سطح ورودی برای قابهای خام دوربین و یک سطح هدف خروجی برای قابهای خروجی بهبودیافته نیاز دارید.
برای ادغام با CameraX، کافی است خط لوله «بهبود نور کم Google» را در CameraEffect قرار دهید.
CameraX بهطور خودکار چرخه حیات سطح و مسیریابی را بهطور یکپارچه برای شما مدیریت میکند.
درنهایت، «جلوه تقویت نور کم» را در سهماهه چهارم ۲۰۲۶ در CameraX راهاندازی میکنیم.
// Integrates SW Low Light Boost
val lowLightBoostEffect =
LowLightBoostEffect(
CameraEffect.PREVIEW or
CameraEffect.VIDEO_CAPTURE or
CameraEffect.IMAGE_CAPTURE
)
val sessionConfig = SessionConfig(
useCases = listOf(preview, imageCapture),
effects = lowLightBoostEffect
)
cameraProvider.bindToLifecycle(.. sessionConfig)با این کار، میتوانید یک شیء «تقویت نور کم» ایجاد کنید و آن را به پیکربندی جلسه خود اضافه کنید، سپس پیکربندی جلسه را به چرخه حیات دوربین خود متصل کنید و این ویژگی بهراحتی در دستگاههای پشتیبانیشده کار میکند.
به پایان هر دو بخش رسیدیم.
بیایید بهسرعت آنچه را که امروز پوشش دادیم خلاصه کنیم.
امروز درباره اینکه چگونه میتوانید تجربههای دوربین خود را در دستگاههای مختلف با CameraX و یادگیری ماشین دروندستگاهی ارتقا دهید صحبت کردیم.
ابتدا، با CameraXViewfinder، میتوانید پیشنمایشهای دوربین خود را فعال کنید تا با ابعاد مختلف سازگار شوند.
سپس دیدیم که CameraX به شما امکان میدهد در دستگاههای XR نیز ضبط کنید.
سپس Feature Group API به شما کمک میکند ترکیب بهینه ویژگیهای سختافزاری را پیدا کنید.
همچنین ارتقاهای «ضبط تصویر» را بررسی کردیم و با «ضبط ویدیو با سرعت بالا» برای ویدیوهای حرکت آهسته آشنا شدیم.
درنهایت، یاد گرفتیم که چگونه پیشنمای «دوربین همزمان» را بهصورت پویا بهبود دهیم.
سپس، برخیاز راهحلهای یادگیری ماشین دروندستگاهی مانند ML Kit و MediaPipe را برای ساختن تجربههای بینایی هوشمند بررسی کردیم.
درنهایت، «بهبود ویدیو و تصویر» در دستگاه را بههمراه «تقویت نور کم» برای روشن کردن پیشنمای دوربین بهصورت همزمان دیدیم.
همانطور که قبلاً ذکر شد، قصد داریم Camera1 را غیرفعال کنیم، بنابراین اکیداً توصیه میکنیم به CameraX مهاجرت کنید.
برای پشتیبانی از این انتقال، اخیراً مهارت CameraX را راهاندازی کردهایم.
همچنین راهنمای انتقال و مخزن نمونههای دوربین را برای راهنمایی شما داریم.
برای دسترسی به منابع بیشتر درباره ویژگیهایی که امروز پوشش دادیم، میتوانید از این پیوند بازدید کنید.
این بخش شامل پیوند به اسناد، وبلاگ، و منابع انتقال است که همه در یک جا جمع شدهاند.
یک ثانیه صبر میکنم، مکث.
بسیارخوب.
از همه شما سپاسگزاریم.
به پایان صحبتهایمان رسیدیم.
var surfaceRequest by remember {
mutableStateOf<SurfaceRequest?>(null)
}
LaunchedEffect(...) {
preview.setSurfaceProvider { surfaceRequest = it }
cameraProvider.bindToLifecycle(..., preview)
}
surfaceRequest?.let { request ->
CameraXViewfinder(
surfaceRequest = request,
contentScale = ContentScale.Fit
)
}CameraXViewfinder(
surfaceRequest = viewModel.surfaceRequest,
contentScale = ContentScale.Fit,
isTapToFocusEnabled = true,
isPinchToZoomEnabled = true,
onScreenFlashReady =
{
imageCapture.screenFlash = it
imageCapture.flashMode = FLASH_MODE_SCREEN
}
)// In a coroutine
val imageCapture = ImageCapture.Builder().build()
ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext)
.collectLatest { isConnected ->
if (isConnected) {
val projectedContext = ProjectedContext.createProjectedDeviceContext(activity)
val cameraConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
.setRepeatingStreamForced(false)
.build()
val cameraProvider = LifecycleCameraProvider.createInstance(projectedContext, cameraConfig)
cameraProvider.bindToLifecycle(activity, DEFAULT_BACK_CAMERA, imageCapture)
}
}
// Shutter button clicked
imageCapture.takePicture(...)cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
SessionConfig(
useCases = listOf(preview, videoCapture),
// HDR_HLG10 > FPS_60 > Preview Stabilization.
preferredFeatureGroup =
listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),
)
)// CameraX 1.6+ supports:
// 4K, video stabilization & Effect
val sessionConfig = SessionConfig(
useCases = listOf(preview, videoCapture),
requiredFeatureGroup =
setOf(UHD_RECORDING, VIDEO_STABILIZATION),
effects = listOf(overlayEffect)
)val cameraInfo = cameraProvider.getCameraInfo(cameraSelector,
ExtensionSessionConfig(ExtensionMode.NIGHT, extensionsManager))
val imageCapabilities = ImageCapture.getImageCaptureCapabilities(cameraInfo)
val imageCapture = ImageCapture.Builder().apply {
if (imageCapabilities.supportedOutputFormats
.contains(OUTPUT_FORMAT_JPEG_ULTRA_HDR)) {
setOutputFormat(OUTPUT_FORMAT_JPEG_ULTRA_HDR)
}
}.build()
val extensionSessionConfig = ExtensionSessionConfig(
ExtensionMode.NIGHT, extensionsManager, preview, imageCapture
)
cameraProvider.bindToLifecycle(..., extensionSessionConfig)val imageCapture = ImageCapture.Builder()
.setMotionPhotoMode(
ImageCapture.MOTION_PHOTO_MODE_ON,
customConfig
)
.build()
cameraProvider.bindToLifecycle(.. imageCapture)
imageCapture.takePicture(...)val recorder = Recorder.Builder().build()
val videoCapture = VideoCapture.withOutput(recorder)
val frameRateRange = cameraInfo.getSupportedFrameRateRanges(
HighSpeedVideoSessionConfig(videoCapture, preview)).first()
val sessionConfig = HighSpeedVideoSessionConfig(
videoCapture,
preview,
frameRateRange = frameRateRange,
isSlowMotionEnabled = true
)
cameraProvider.bindToLifecycle(
lifecycleOwner, cameraSelector, sessionConfig)
// Start recording slow motion videos.
val recording = recorder.prepareRecording(context, outputOption)
.start(executor, {})val concurrentCamera =
cameraProvider.bindToLifecycle(list(config1, config2))
// CameraX 1.7 features: rounded corner, border width & color
val pipSettings = CompositionSettings.Builder()
.setOffset(0.2f, 0.2f)
.setScale(0.35f, 0.35f)
.setRoundedCornerRatio(0.15f)
.setBorderWidthRatio(0.02f)
.setBorderColor(Color.WHITE)
.setZOrder(1)
.build()
// CameraX 1.7 features: dynamical composition update
concurrentCamera.setCompositionSettings(
listOf(mainSettings, pipSettings))// 1. Initialize the on-device GenerativeModel client
val generativeModel = Generation.getClient()
// 2. Check whether Gemini Nano is available on the device
val status = generativeModel.checkStatus()
when (status) {
FeatureStatus.UNAVAILABLE -> {}
FeatureStatus.DOWNLOADABLE -> {}
FeatureStatus.DOWNLOADING -> {}
FeatureStatus.AVAILABLE -> {}
}// 3. Build a multimodal request with the image and text prompt
val request = generateContentRequest(
ImagePart(bitmap),
TextPart("Describe this image in detail.")
) {}
// 4. Run inference on-device and get the description
val response = generativeModel.generateContent(request)
val description = response.candidates.first().text// 1. Build GestureRecognizerOptions
val options = GestureRecognizerOptions.builder().setBaseOptions(
BaseOptions.builder().setModelAssetPath(MP_RECOGNIZER_TASK).build())
.setRunningMode(RunningMode.LIVE_STREAM)
.setResultListener { result, image -> /* Handle detected gesture */ }
.build()
// 2. Create the GestureRecognizer
val gestureRecognizer = GestureRecognizer.createFromOptions(context, options)
// 3. Convert CameraX ImageProxy to MediaPipe MPImage
val bitmap = rotateBitmap(imageProxy.toBitmap(),
imageProxy.imageInfo.rotationDegrees)
val mpImage = BitmapImageBuilder(bitmap).build()
// 4. Run gesture recognition on the image
gestureRecognizer.recognizeAsync(mpImage, frameTime)// Step 1: Verify device support & install module
val enhancementClient = Enhancement.getClient(context)
suspend fun prepare(): Boolean {
if (!enhancementClient.isDeviceSupported().await()) return false
if (!enhancementClient.isModuleInstalled().await()) {
check(enhancementClient.installModule().await()) {
"Enhancement module install failed"
}
}
return true
}// Step 2: Select Enhancement options & create session
val options = EnhancementOptions(
width = bitmap.width,
height = bitmap.height,
enhancementMode = EnhancementMode.BITMAP,
isTonemappingEnabled = true,
isDeblurAndDenoisePhotoEnabled = true,
isDeblurAndDenoiseVideoEnabled = false,
isUpscalePhotoEnabled = false,
isUpscaleVideoEnabled = false
)
val enhancementSession = enhancementClient
.createSessionAsync(options, enhancementExecutor)// Step 3: Process Enhancement
val enhancedBitmap = suspendCancellableCoroutine { continuation ->
val enhancementCallback = object : EnhancementCallback {
override fun onBitmapProcessed(result: Bitmap) {
if (continuation.isActive) continuation.resume(result)
}
override fun onError(statusCode: Int) {
if (continuation.isActive) {
continuation.resumeWithException(
Exception("Bitmap processing failed: $statusCode")
)
}
}
override fun onSurfaceProcessed(timestamp: Long) {}
}
enhancementSession.process(bitmap, options, enhancementCallback)
}// Integrates SW Low Light Boost
val lowLightBoostEffect =
LowLightBoostEffect(
CameraEffect.PREVIEW or
CameraEffect.VIDEO_CAPTURE or
CameraEffect.IMAGE_CAPTURE
)
val sessionConfig = SessionConfig(
useCases = listOf(preview, imageCapture),
effects = lowLightBoostEffect
)
cameraProvider.bindToLifecycle(.. sessionConfig)