1. לפני שמתחילים
ראית את ההדגמה של Google Lens, שבה אפשר לכוון את מצלמת הטלפון לאובייקט ולמצוא איפה אפשר לקנות אותו באינטרנט? אם אתם רוצים לדעת איך להוסיף את אותה תכונה לאפליקציה שלכם, אתם מוזמנים לנסות את ה-codelab הזה. הוא חלק מתוכנית לימודים שמלמדת איך לשלב תכונה של חיפוש תמונות מוצרים באפליקציה לנייד.
ב-codelab הזה תלמדו איך לבצע קריאה ל-backend שנבנה באמצעות Vision API Product Search מאפליקציה לנייד. ה-backend הזה יכול לקבל תמונה של שאילתה ולחפש מוצרים דומים מבחינה חזותית מקטלוג מוצרים.
בתוכנית הלימודים תוכלו לקרוא על השלבים הבאים ליצירת תכונה של חיפוש מוצרים חזותי, כולל איך להשתמש ב-ML Kit Object Detection and Tracking כדי לזהות אובייקטים בתמונת השאילתה ולאפשר למשתמשים לבחור את המוצר שהם רוצים לחפש.
מה תפַתחו
|
מה תלמדו
- איך שולחים קריאה ל-API ומנתחים את התגובה של ממשקי Vision API Product Search מאפליקציית Android
מה צריך להכין
- גרסה עדכנית של Android Studio (גרסה 4.1.2 ואילך)
- Android Studio Emulator או מכשיר Android פיזי
- קוד לדוגמה
- ידע בסיסי בפיתוח ל-Android ב-Kotlin
ה-codelab הזה מתמקד בחיפוש מוצרים באמצעות Vision API. הוא לא מתעמק במושגים ובקטעי קוד לא רלוונטיים, אלא מספק אותם כדי שתוכלו פשוט להעתיק ולהדביק אותם.
2. מידע על Vision API Product Search
Vision API Product Search היא תכונה ב-Google Cloud שמאפשרת למשתמשים לחפש מוצרים שדומים מבחינה ויזואלית מתוך קטלוג מוצרים. קמעונאים יכולים ליצור מוצרים, שכל אחד מהם מכיל תמונות להמחשה שמתארות את המוצר מבחינה ויזואלית מנקודות מבט שונות. לאחר מכן תוכלו להוסיף את המוצרים האלה לקבוצות מוצרים (כלומר לקטלוג מוצרים). נכון לעכשיו, מערכת Vision API Product Search תומכת בקטגוריות המוצרים הבאות: מוצרים לבית, ביגוד, צעצועים, מוצרים ארוזים ומוצרים כלליים.
כשמשתמשים מזינים שאילתה לגבי קבוצת המוצרים עם תמונות משלהם, התכונה'חיפוש מוצרים' ב-Vision API משתמשת בלמידת מכונה כדי להשוות בין המוצר בתמונה של השאילתה לבין התמונות בקבוצת המוצרים של הקמעונאי, ואז מחזירה רשימה מדורגת של תוצאות דומות מבחינה ויזואלית וסמנטית.
3. הורדה והרצה של אפליקציית המפעיל
הורדת הקוד
כדי להוריד את כל הקוד של ה-Codelab הזה, לוחצים על הקישור הבא:
מחלצים את קובץ ה-ZIP שהורד. הפעולה הזו תגרום לפריקה של תיקיית שורש (odml-pathways-main
) עם כל המשאבים שתצטרכו. ב-Codelab הזה תצטרכו רק את המקורות בספריית המשנה product-search/codelab2/android
.
ספריית המשנה codelab2
במאגר odml-pathways
מכילה שתי ספריות:
-
starter – קוד התחלתי שעליו מתבססים ב-Codelab הזה.
-
final – קוד מלא של אפליקציית הדוגמה המוגמרת.
אפליקציית המתחילים כאן היא האפליקציה שיצרתם ב-codelab זיהוי אובייקטים בתמונות כדי ליצור חיפוש חזותי של מוצרים: Android. היא משתמשת בזיהוי ומעקב אובייקטים ב-ML Kit כדי לזהות אובייקטים בתמונה ולהציג אותם על המסך.
ייבוא האפליקציה ל-Android Studio
מתחילים בייבוא אפליקציית המתחילים אל Android Studio.
עוברים אל Android Studio, בוחרים באפשרות Import Project (Gradle, Eclipse ADT, etc.) (ייבוא פרויקט (Gradle, Eclipse ADT וכו')) ובוחרים את התיקייה starter
מקוד המקור שהורדתם קודם.
הפעלת אפליקציית המתחילים
אחרי שיובא הפרויקט ל-Android Studio, אפשר להריץ את האפליקציה בפעם הראשונה. מחברים את מכשיר Android באמצעות USB למארח או מפעילים את האמולטור של Android Studio ולוחצים על Run (הפעלה) ( ) בסרגל הכלים של Android Studio.
(אם הלחצן הזה מושבת, מוודאים שמייבאים רק את starter/app/build.gradle ולא את כל המאגר.)
עכשיו האפליקציה אמורה להיפתח במכשיר Android. הוא כבר כולל את היכולת לזיהוי אובייקטים: זיהוי פריטי אופנה בתמונה והצגת המיקום שלהם. כדאי לנסות עם התמונות המוגדרות מראש כדי לאשר.
צילום מסך של אפליקציית המתחילים שיכולה לזהות אובייקטים בתמונה
בשלב הבא, תרחיבו את האפליקציה כדי לשלוח את האובייקטים שזוהו אל העורף של חיפוש המוצרים ב-Vision API ולהציג את תוצאות החיפוש במסך.
4. טיפול בבחירת אובייקט
משתמשים יכולים להקיש על אוביקט שזוהה כדי לבחור אותו
עכשיו מוסיפים קוד כדי לאפשר למשתמשים לבחור אובייקט מהתמונה ולהתחיל את חיפוש המוצר. לאפליקציית המתחילים כבר יש את היכולת לזהות אובייקטים בתמונה. יכול להיות שיש כמה אובייקטים בתמונה, או שהאובייקט שזוהה תופס רק חלק קטן מהתמונה. לכן, המשתמש צריך להקיש על אחד מהאובייקטים שזוהו כדי לציין באיזה אובייקט הוא רוצה להשתמש לחיפוש מוצרים.
צילום מסך של פריטי האופנה שזוהו בתמונה
כדי לשמור על פשטות ה-codelab ולהתמקד בלמידת מכונה, הטמענו באפליקציית המתחילים קוד Android סטנדרטי שיעזור לכם לזהות את האובייקט שהמשתמש הקיש עליו. התצוגה שבה מוצגת התמונה בפעילות הראשית (ObjectDetectorActivity
) היא למעשה תצוגה בהתאמה אישית (ImageClickableView
) שמרחיבה את ברירת המחדל ImageView
של Android OS. הוא כולל כמה שיטות נוחות לשימוש, כולל:
fun setOnObjectClickListener(listener: ((objectImage: Bitmap) -> Unit))
זוהי קריאה חוזרת לקבלת התמונה החתוכה שמכילה רק את האובייקט שהמשתמש הקיש עליו. התמונה החתוכה הזו תישלח אל העורף של חיפוש המוצרים.
מוסיפים קוד לטיפול במשתמש שמקיש על האובייקטים שזוהו.
עוברים לשיטה initViews
בכיתה ObjectDetectorActivity
ומוסיפים את השורות הבאות בסוף השיטה: (Android Studio יודיע לכם שהוא לא יכול למצוא את השיטה startProductImageSearch
. אל דאגה, תטמיעו אותו בהמשך.)
// Callback received when the user taps on any of the detected objects.
ivPreview.setOnObjectClickListener { objectImage ->
startProductImageSearch(objectImage)
}
הפונקציה onObjectClickListener
מופעלת בכל פעם שהמשתמש מקיש על אחד מהאובייקטים שזוהו במסך. הוא מקבל את התמונה החתוכה שמכילה רק את האובייקט שנבחר. לדוגמה, אם המשתמש מקיש על האדם שלובש את השמלה בצד ימין, מאזין האירועים יופעל עם הערך objectImage
כמו בדוגמה שלמטה.
דוגמה לתמונה החתוכה שמועברת אל onObjectClickListener
שליחת התמונה החתוכה לפעילות חיפוש המוצרים
עכשיו מטמיעים את הלוגיקה של שליחת תמונת השאילתה אל העורף של Vision API Product Search בפעילות נפרדת (ProductSearchActivity
).
כל רכיבי ממשק המשתמש הוטמעו מראש, כך שתוכלו להתמקד בכתיבת הקוד לתקשורת עם העורף של חיפוש המוצרים.
צילום מסך של רכיבי ממשק המשתמש ב-ProductSearchActivity
מוסיפים קוד לשליחת תמונת האובייקט שהמשתמש בחר אל ProductSearchActivity
.
חוזרים אל Android Studio ומוסיפים את ה-method startProductImageSearch
הזה אל המחלקה ObjectDetectorActivity
:
private fun startProductImageSearch(objectImage: Bitmap) {
try {
// Create file based Bitmap. We use PNG to preserve the image quality
val savedFile = createImageFile(ProductSearchActivity.CROPPED_IMAGE_FILE_NAME)
objectImage.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream(savedFile))
// Start the product search activity (using Vision Product Search API.).
startActivity(
Intent(
this,
ProductSearchActivity::class.java
).apply {
// As the size limit of a bundle is 1MB, we need to save the bitmap to a file
// and reload it in the other activity to support large query images.
putExtra(
ProductSearchActivity.REQUEST_TARGET_IMAGE_PATH,
savedFile.absolutePath
)
})
} catch (e: Exception) {
// IO Exception, Out Of memory ....
Toast.makeText(this, e.message, Toast.LENGTH_SHORT).show()
Log.e(TAG, "Error starting the product image search activity.", e)
}
}
קטע הקוד מבצע 3 פעולות:
- התמונה החתוכה נשמרת כקובץ PNG.
- מתחילים את
ProductSearchActivity
כדי להפעיל את רצף החיפוש של המוצר. - כולל את ה-URI של התמונה החתוכה ב-intent של הפעלת הפעילות, כדי שאפליקציית
ProductSearchActivity
תוכל לאחזר אותו מאוחר יותר ולהשתמש בו כתמונה של השאילתה.
חשוב לזכור:
- הלוגיקה לזיהוי אובייקטים ולשאילתות בשרת העורפי פוצלה ל-2 פעילויות רק כדי להקל על ההבנה של ה-codelab. אתם מחליטים איך להטמיע אותם באפליקציה.
- צריך לכתוב את תמונת השאילתה לקובץ ולהעביר את ה-URI של התמונה בין הפעילויות, כי תמונת השאילתה יכולה להיות גדולה יותר ממגבלת הגודל של 1MB של intent ב-Android.
- אפשר לשמור את תמונת השאילתה בפורמט PNG כי זהו פורמט ללא אובדן נתונים.
אחזור תמונת השאילתה בפעילות חיפוש המוצרים
באפליקציית המתחילים ProductSearchActivity
, הקוד לאחזור תמונת השאילתה ולהצגתה במסך כבר הוטמע.
עוברים לשיטה onCreate
ומוודאים שהקוד הזה כבר מופיע שם:
// Receive the query image and show it on the screen
intent.getStringExtra(REQUEST_TARGET_IMAGE_PATH)?.let { absolutePath ->
viewBinding.ivQueryImage.setImageBitmap(BitmapFactory.decodeFile(absolutePath))
}
הפעלת האפליקציה
עכשיו לוחצים על Run (הפעלה) ( ) בסרגל הכלים של Android Studio.
אחרי שהאפליקציה נטענת, מקישים על אחת מהתמונות המוגדרות מראש ובוחרים באחד מהאובייקטים שזוהו.
מוודאים שהתמונה שהקשתם עליה מופיעה בProductSearchActivity
. כפתור החיפוש עדיין לא עושה כלום, אבל אנחנו נטמיע אותו בהמשך.
אחרי שתקישו על אחד מהאובייקטים שזוהו, יופיע מסך דומה.
5. הקצה העורפי של חיפוש המוצרים
איך יוצרים את העורף של חיפוש תמונות מוצרים
ב-codelab הזה נדרש קצה עורפי של חיפוש מוצרים שנבנה באמצעות Vision API Product Search. יש שתי אפשרויות לעשות את זה:
אפשרות 1: שימוש בעורף הקצה של ההדגמה שנפרס עבורכם
אתם יכולים להמשיך עם ה-codelab הזה באמצעות העורף של חיפוש המוצרים ש-Google כבר פרסה בשבילכם. אפשר לשכפל את קצה העורף של ההדגמה באמצעות המדריך לתחילת העבודה עם Vision API Product Search.
אפשרות 2: יצירת קצה עורפי משלכם באמצעות המדריך לתחילת העבודה עם Vision API Product Search
האפשרות הזו מומלצת למי שרוצה ללמוד לעומק איך לבנות קצה עורפי לחיפוש מוצרים, כדי שיוכל לבנות אחד כזה לקטלוג המוצרים שלו בהמשך. אתם צריכים:
- חשבון Google Cloud שהחיוב בו מופעל. (יכול להיות שזה חשבון לתקופת ניסיון בחינם).
- ידע מסוים במושגים של Google Cloud, כולל פרויקטים, חשבונות שירות וכו'.
בהמשך תוכנית הלימודים מוסבר איך עושים זאת.
הסבר על מושגים חשובים
במהלך האינטראקציה עם העורף של חיפוש המוצרים, תיתקלו במושגים הבאים:
- קבוצת מוצרים: קבוצת מוצרים היא מאגר פשוט לקבוצת מוצרים. אפשר לייצג קטלוג מוצרים כקבוצת מוצרים והמוצרים שכלולים בה.
- מוצר: אחרי שיוצרים קבוצת מוצרים, אפשר ליצור מוצרים ולהוסיף אותם לקבוצת המוצרים.
- תמונות ייחוס של המוצר: אלה תמונות שמציגות את המוצרים שלכם מזוויות שונות. תמונות לדוגמה משמשות לחיפוש מוצרים דומים מבחינה חזותית.
- חיפוש מוצרים: אחרי שיוצרים את קבוצת המוצרים ומבצעים אינדוקס של קבוצת המוצרים, אפשר לשלוח שאילתה לקבוצת המוצרים באמצעות Cloud Vision API.
הסבר על קטלוג מוצרים מוגדר מראש
הקצה העורפי של הדמו של חיפוש מוצרים שבו נעשה שימוש ב-codelab הזה נוצר באמצעות Vision API Product Search וקטלוג מוצרים של כ-100 תמונות של נעליים ושמלות. הנה כמה תמונות מהקטלוג:
דוגמאות מקטלוג המוצרים המוגדר מראש
התקשרות עם ה-backend של הדמו של חיפוש המוצרים
אפשר לקרוא ל-Vision API Product Search ישירות מאפליקציה לנייד על ידי הגדרת מפתח Google Cloud API והגבלת הגישה למפתח ה-API רק לאפליקציה שלכם.
כדי שה-codelab הזה יהיה פשוט, הוגדרה נקודת קצה של שרת proxy שמאפשרת לכם לגשת לעורף של ההדגמה בלי לדאוג לגבי מפתח ה-API והאימות. הוא מקבל את בקשת ה-HTTP מהאפליקציה לנייד, מוסיף את מפתח ה-API ומעביר את הבקשה אל העורף של Vision API Product Search. לאחר מכן, ה-proxy מקבל את התגובה מהקצה העורפי ומחזיר אותה לאפליקציה לנייד.
- נקודת קצה של שרת proxy:
https://us-central1-odml-codelabs.cloudfunctions.net/productSearch
- התנהגות ה-proxy: הוספת כותרת אימות מתאימה והעברת בקשות ה-API אל העורף של Vision API Product Search. לדוגמה, קריאה ל-API אל
https://us-central1-odml-codelabs.cloudfunctions.net/productSearch/images:annotate
תועבר אלhttps://vision.googleapis.com/v1/images:annotate
ב-codelab הזה תשתמשו בשני ממשקי API של חיפוש מוצרים באמצעות Vision API:
- projects.locations.images.annotate: שליחת תמונת השאילתה לשרת וקבלת רשימה של מוצרים מקטלוג מוצרים מוגדר מראש שדומים מבחינה חזותית לתמונת השאילתה.
- projects.locations.products.referenceImages.get: אחזור של כתובות ה-URI של תמונות המוצרים שמוחזרות בקריאת ה-API שלמעלה, כדי להציג אותן למשתמשים.
6. הטמעה של לקוח ה-API
הסבר על תהליך העבודה של חיפוש מוצרים
כדי לבצע חיפוש מוצרים באמצעות הקצה העורפי, פועלים לפי תהליך העבודה הזה:
- קידוד תמונת השאילתה כמחרוזת base64
- מבצעים קריאה לנקודת הקצה projects.locations.images.annotate עם תמונת השאילתה
- מקבלים את מזהי תמונות המוצרים מהקריאה הקודמת ל-API ושולחים אותם לנקודות הקצה projects.locations.products.referenceImages.get כדי לקבל את כתובות ה-URI של תמונות המוצרים בתוצאת החיפוש.
הטמעה של מחלקת לקוח ה-API
עכשיו מטמיעים קוד כדי להתקשר ל-backend של חיפוש המוצרים במחלקה ייעודית שנקראת ProductSearchAPIClient
. הטמענו בשבילכם קוד boilerplate באפליקציית המתחילים:
-
class ProductSearchAPIClient
: המחלקה הזו ריקה ברובה כרגע, אבל יש בה כמה שיטות שתטמיעו בהמשך ב-codelab הזה. -
fun convertBitmapToBase64(bitmap: Bitmap)
: המרה של מופע Bitmap לייצוג base64 שלו כדי לשלוח אותו לשרת העורפי של חיפוש המוצרים -
fun annotateImage(image: Bitmap): Task<List<ProductSearchResult>>
: מפעילים את API projects.locations.images.annotate ומנתחים את התגובה. -
fun fetchReferenceImage(searchResult: ProductSearchResult): Task<ProductSearchResult>
: מפעילים את API projects.locations.products.referenceImages.get ומנתחים את התגובה. -
SearchResult.kt
: הקובץ הזה מכיל כמה מחלקות נתונים שמייצגות את הסוגים שמוחזרים על ידי העורף של Vision API Product Search.
ציון הגדרות ה-API
עוברים לכיתה ProductSearchAPIClient
ורואים כמה הגדרות של העורף של חיפוש המוצרים שכבר הוגדרו:
// Define the product search backend
// Option 1: Use the demo project that we have already deployed for you
const val VISION_API_URL =
"https://us-central1-odml-codelabs.cloudfunctions.net/productSearch"
const val VISION_API_KEY = ""
const val VISION_API_PROJECT_ID = "odml-codelabs"
const val VISION_API_LOCATION_ID = "us-east1"
const val VISION_API_PRODUCT_SET_ID = "product_set0"
- VISION_API_URL היא נקודת הקצה של Cloud Vision API. כשממשיכים עם קצה העורף של ההדגמה, מגדירים את זה לנקודת הקצה של ה-Proxy. עם זאת, אם אתם פורסים קצה עורפי משלכם, תצטרכו לשנות אותו לנקודת הקצה של Cloud Vision API.
https://vision.googleapis.com/v1
. - VISION_API_KEY הוא מפתח ה-API של פרויקט Cloud. האימות כבר מתבצע על ידי השרת הפרוקסי, ולכן אפשר להשאיר את השדה הזה ריק.
- VISION_API_PROJECT_ID הוא מזהה הפרויקט ב-Cloud.
odml-codelabs
הוא פרויקט ה-Cloud שבו נפרס ה-Backend של ההדגמה. - VISION_API_LOCATION_ID הוא המיקום בענן שבו פועל ה-backend של חיפוש המוצרים.
us-east1
הוא המקום שבו פרסנו את ה-backend של ההדגמה. - VISION_API_PRODUCT_SET_ID הוא המזהה של קטלוג המוצרים (שנקרא גם 'קבוצת מוצרים' במונחים של Vision API) שבו רוצים לחפש מוצרים דומים מבחינה ויזואלית. יכולים להיות לכם כמה קטלוגים בפרויקט Cloud אחד.
product_set0
הוא קטלוג המוצרים המוגדר מראש של העורף של ההדגמה.
7. קריאה ל-API של חיפוש מוצרים
עיון בפורמט של בקשות ותגובות ב-API
כדי למצוא מוצרים דומים לתמונה מסוימת, מעבירים את מזהה ה-URI של התמונה ב-Google Cloud Storage, את כתובת ה-URL שלה באינטרנט או את המחרוזת המקודדת שלה ב-Base64 אל Vision API Product Search. ב-codelab הזה, נשתמש באפשרות של מחרוזת מקודדת ב-Base64, כי תמונת השאילתה שלנו קיימת רק במכשיר של המשתמש.
צריך לשלוח בקשת POST לנקודת הקצה projects.locations.images.annotate עם תוכן בקשת ה-JSON הזה:
{
"requests": [
{
"image": {
"content": {base64-encoded-image}
},
"features": [
{
"type": "PRODUCT_SEARCH",
"maxResults": 5
}
],
"imageContext": {
"productSearchParams": {
"productSet": "projects/{project-id}/locations/{location-id}/productSets/{product-set-id}",
"productCategories": [
"apparel-v2"
],
}
}
}
]
}
יש כמה פרמטרים שצריך לציין::
- base64-encoded-image: ייצוג base64 (מחרוזת ASCII) של הנתונים הבינאריים של תמונת השאילתה.
- project-id: מזהה הפרויקט ב-GCP.
- location-id: מזהה מיקום תקין.
- product-set-id: המזהה של קבוצת המוצרים שרוצים להפעיל עליה את הפעולה.
מכיוון שקטלוג המוצרים שלכם מכיל רק נעליים ותמונות של שמלות, צריך לציין את productCategories כ-apparel-v2
. v2 כאן מציין שאנחנו משתמשים בגרסה 2 של מודל למידת המכונה לחיפוש מוצרי הלבשה.
אם הבקשה תתבצע בהצלחה, השרת יחזיר קוד סטטוס HTTP 200 OK ואת התגובה בפורמט JSON. תגובת ה-JSON כוללת את שני סוגי התוצאות הבאים:
- productSearchResults – מכיל רשימה של מוצרים תואמים לכל התמונה.
- productGroupedResults – מכיל קואורדינטות של תיבות תוחמות ופריטים תואמים לכל מוצר שזוהה בתמונה.
המוצר כבר נחתך מהתמונה המקורית, ולכן תנתחו את התוצאות ברשימה productSearchResults.
אלה כמה שדות חשובים באובייקט של תוצאת חיפוש מוצרים:
- product.name: המזהה הייחודי של מוצר בפורמט
projects/{project-id}/locations/{location-id}/products/{product_id}
- product.score: ערך שמציין עד כמה תוצאת החיפוש דומה לתמונה בשאילתה. ערכים גבוהים יותר מצביעים על דמיון רב יותר.
- product.image: המזהה הייחודי של תמונת ההפניה של מוצר בפורמט
projects/{project-id}/locations/{location-id}/products/{product_id}/referenceImages/{image_id}
. כדי לקבל את כתובת ה-URL של תמונת ההפניה הזו ולהציג אותה על המסך, צריך לשלוח עוד בקשת API אל projects.locations.products.referenceImages.get. - product.labels: רשימה של תגים מוגדרים מראש של המוצר. האפשרות הזו שימושית אם רוצים לסנן את תוצאות החיפוש כך שיוצגו רק פריטי לבוש מקטגוריה אחת, כמו שמלות.
המרת תמונת השאילתה ל-Base64
צריך להמיר את תמונת השאילתה לייצוג שלה כמחרוזת base64 ולצרף את המחרוזת לאובייקט JSON בגוף הבקשה.
עוברים אל המחלקה ProductSearchAPIClient
, מוצאים את השיטה הריקה convertBitmapToBase64
ומחליפים אותה בהטמעה הזו:
private fun convertBitmapToBase64(bitmap: Bitmap): String {
val byteArrayOutputStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
val byteArray: ByteArray = byteArrayOutputStream.toByteArray()
return Base64.encodeToString(byteArray, Base64.DEFAULT)
}
הטמעה של הקריאה ל-API
לאחר מכן, יוצרים בקשה ל-API של חיפוש מוצרים ושולחים אותה אל ה-Backend. תשתמשו ב-Volley כדי לשלוח את בקשת ה-API, ותחזירו את התוצאה באמצעות Task API.
חוזרים למחלקה ProductSearchAPIClient
, מוצאים את השיטה הריקה annotateImage
ומחליפים אותה בהטמעה הזו:
fun annotateImage(image: Bitmap): Task<List<ProductSearchResult>> {
// Initialization to use the Task API
val apiSource = TaskCompletionSource<List<ProductSearchResult>>()
val apiTask = apiSource.task
// Convert the query image to its Base64 representation to call the Product Search API.
val base64: String = convertBitmapToBase64(image)
// Craft the request body JSON.
val requestJson = """
{
"requests": [
{
"image": {
"content": """".trimIndent() + base64 + """"
},
"features": [
{
"type": "PRODUCT_SEARCH",
"maxResults": $VISION_API_PRODUCT_MAX_RESULT
}
],
"imageContext": {
"productSearchParams": {
"productSet": "projects/${VISION_API_PROJECT_ID}/locations/${VISION_API_LOCATION_ID}/productSets/${VISION_API_PRODUCT_SET_ID}",
"productCategories": [
"apparel-v2"
]
}
}
}
]
}
""".trimIndent()
// Add a new request to the queue
requestQueue.add(object :
JsonObjectRequest(
Method.POST,
"$VISION_API_URL/images:annotate?key=$VISION_API_KEY",
JSONObject(requestJson),
{ response ->
// Parse the API JSON response to a list of ProductSearchResult object/
val productList = apiResponseToObject(response)
// Return the list.
apiSource.setResult(productList)
},
// Return the error
{ error -> apiSource.setException(error) }
) {
override fun getBodyContentType() = "application/json"
}.apply {
setShouldCache(false)
})
return apiTask
}
הצגת תוצאת החיפוש בממשק המשתמש
עכשיו קוד ה-API ב-ProductSearchAPIClient מוכן. חוזרים לפעילות ProductSearchActivity
כדי להטמיע את קוד ממשק המשתמש.
לפעילות כבר יש קוד boilerplate שמפעיל את השיטה searchByImage(queryImage: Bitmap)
. מוסיפים קוד לקריאה ל-backend ולהצגת התוצאות בממשק המשתמש לשיטה הזו, שכרגע ריקה.
apiClient.annotateImage(queryImage)
.addOnSuccessListener { showSearchResult(it) }
.addOnFailureListener { error ->
Log.e(TAG, "Error calling Vision API Product Search.", error)
showErrorResponse(error.localizedMessage)
}
השיטה showSearchResult
מכילה קוד boilerplate שמנתח את תגובת ה-API ומציג אותה על המסך.
הפעלת התוסף
עכשיו לוחצים על Run (הפעלה) ( ) בסרגל הכלים של Android Studio. אחרי שהאפליקציה נטענת, מקישים על אחת מהתמונות המוגדרות מראש, בוחרים אובייקט שזוהה, מקישים על הלחצן חיפוש ורואים את תוצאות החיפוש שמוחזרות מהקצה העורפי. יוצג לכם משהו כזה:
צילום מסך של תוצאות חיפוש המוצרים
הקצה העורפי כבר מחזיר רשימה של מוצרים דומים מבחינה ויזואלית מקטלוג המוצרים שהוגדר מראש. אבל אפשר לראות שתמונת המוצר עדיין ריקה. הסיבה לכך היא שנקודת הקצה projects.locations.images.annotate מחזירה רק מזהים של תמונות מוצרים, כמו projects/odml-codelabs/locations/us-east1/products/product_id77/referenceImages/image77
. תצטרכו לשלוח עוד קריאה ל-API לנקודת הקצה projects.locations.products.referenceImages.get ולקבל את כתובת ה-URL של תמונת ההפניה הזו כדי להציג אותה במסך.
8. קבלת תמונות לדוגמה של המוצר
עיון בפורמט של בקשות ותגובות ב-API
כדי לקבל את כתובות ה-URI של תמונות המוצרים שמוחזרות מנקודת הקצה של חיפוש המוצרים, שולחים בקשת HTTP GET עם גוף בקשה ריק לנקודת הקצה projects.locations.products.referenceImages.get.
בקשת ה-HTTP נראית כך:
GET $VISION_API_URL/projects/odml-codelabs/locations/us-east1/products/product_id77/referenceImages/image77?key=$VISION_API_KEY
אם הבקשה תתבצע בהצלחה, השרת יחזיר קוד סטטוס HTTP 200 OK ואת התשובה בפורמט JSON כמו בדוגמה הבאה:
{
"name":"projects/odml-codelabs/locations/us-east1/products/product_id77/referenceImages/image77",
"uri":"gs://cloud-ai-vision-data/product-search-tutorial/images/46991e7370ba11e8a1bbd20059124800.jpg"
}
- name: המזהה של התמונה לדוגמה
- uri: ה-URI של התמונה ב-Google Cloud Storage (GCS).
הגדרנו את תמונות ההפניה של העורף של חיפוש המוצרים בהדגמה עם הרשאת קריאה ציבורית. לכן, אתם יכולים להמיר בקלות את ה-URI של GCS לכתובת HTTP ולהציג אותו בממשק המשתמש של האפליקציה. צריך רק להחליף את הקידומת gs://
ב-https://storage.googleapis.com/
.
הטמעה של הקריאה ל-API
לאחר מכן, יוצרים בקשה ל-API של חיפוש מוצרים ושולחים אותה אל ה-Backend. השימוש ב-Volley וב-Task API דומה לשימוש בקריאה ל-API של חיפוש מוצרים.
חוזרים למחלקה ProductSearchAPIClient
, מוצאים את השיטה הריקה fetchReferenceImage
ומחליפים אותה בהטמעה הזו:
private fun fetchReferenceImage(searchResult: ProductSearchResult): Task<ProductSearchResult> {
// Initialization to use the Task API
val apiSource = TaskCompletionSource<ProductSearchResult>()
val apiTask = apiSource.task
// Craft the API request to get details about the reference image of the product
val stringRequest = object : StringRequest(
Method.GET,
"$VISION_API_URL/${searchResult.imageId}?key=$VISION_API_KEY",
{ response ->
val responseJson = JSONObject(response)
val gcsUri = responseJson.getString("uri")
// Convert the GCS URL to its HTTPS representation
val httpUri = gcsUri.replace("gs://", "https://storage.googleapis.com/")
// Save the HTTPS URL to the search result object
searchResult.imageUri = httpUri
// Invoke the listener to continue with processing the API response (eg. show on UI)
apiSource.setResult(searchResult)
},
{ error -> apiSource.setException(error) }
) {
override fun getBodyContentType(): String {
return "application/json; charset=utf-8"
}
}
Log.d(ProductSearchActivity.TAG, "Sending API request.")
// Add the request to the RequestQueue.
requestQueue.add(stringRequest)
return apiTask
}
השיטה הזו מקבלת אובייקט searchResult: ProductSearchResult
שהוחזר מנקודת הקצה של חיפוש המוצרים, ואז פועלת לפי השלבים הבאים:
- קוראת לנקודת הקצה של התמונה לדוגמה כדי לקבל את ה-URI של התמונה לדוגמה ב-GCS.
- הפונקציה ממירה את ה-URI של GCS לכתובת URL מסוג HTTP.
- מעדכן את הנכס
httpUri
של האובייקטsearchResult
עם כתובת ה-URL הזו של HTTP.
חיבור שתי בקשות ה-API
חוזרים אל annotateImage
ומשנים אותו כדי לקבל את כל כתובות ה-URL של תמונות ההפניה ב-HTTP לפני שמחזירים את רשימת ProductSearchResult
למתקשר.
מחפשים את השורה הזו:
// Return the list.
apiSource.setResult(productList)
לאחר מכן מחליפים אותו בהטמעה הזו:
// Loop through the product list and create tasks to load reference images.
// We will call the projects.locations.products.referenceImages.get endpoint
// for each product.
val fetchReferenceImageTasks = productList.map { fetchReferenceImage(it) }
// When all reference image fetches have completed,
// return the ProductSearchResult list
Tasks.whenAllComplete(fetchReferenceImageTasks)
// Return the list of ProductSearchResult with product images' HTTP URLs.
.addOnSuccessListener { apiSource.setResult(productList) }
// An error occurred so returns it to the caller.
.addOnFailureListener { apiSource.setException(it) }
קוד ה-boilerplate להצגת תמונות ההפניה במסך כבר הוטמע בשבילכם במחלקה ProductSearchAdapter
, כך שתוכלו להמשיך להפעיל מחדש את האפליקציה.
הפעלת התוסף
עכשיו לוחצים על Run (הפעלה) ( ) בסרגל הכלים של Android Studio. אחרי שהאפליקציה נטענת, מקישים על אחת מהתמונות המוגדרות מראש, בוחרים אובייקט שזוהה ומקישים על לחצן החיפוש כדי לראות את תוצאות החיפוש, הפעם עם תמונות של המוצר.
האם תוצאות החיפוש של המוצרים נראות לך הגיוניות?
9. מעולה!
למדתם איך לקרוא ל-backend של Vision API Product Search כדי להוסיף לאפליקציית Android שלכם את היכולת לחפש תמונות של מוצרים. זה כל מה שצריך כדי להתחיל להשתמש ב-API.
במהלך ההגדרה, תוכלו ליצור קצה עורפי משלכם באמצעות קטלוג המוצרים. כדי ללמוד איך ליצור קצה עורפי משלכם ולהגדיר את מפתח ה-API כדי לקרוא לו מאפליקציה לנייד, כדאי לעיין במדריך Codelab הבא בתוכנית הלימודים בנושא חיפוש תמונות של מוצרים.
מה למדנו
- איך שולחים קריאה ל-Vision API Product Search backend מאפליקציית Android
השלבים הבאים
- כדי ללמוד איך לבנות קצה עורפי משלכם, אפשר לעיין ב-codelab בנושא בניית קצה עורפי לחיפוש תמונות מוצרים באמצעות Vision API Product Search.
- מומלץ לעיין בתוכניות לימודים נוספות באתר On-device Machine Learning
- איך משלבים תכונה של חיפוש מוצרים באפליקציית Android