1. מטרות
סקירה כללית
ה-Codelab הזה יתמקד ביצירת אפליקציה של Vertex AI Vision מקצה לקצה כדי לעקוב אחרי גודל התור באמצעות קטעי וידאו מתחום הקמעונאות. נשתמש בתכונות המובנות של המודל המיוחד ניתוח תפוסה שעבר הכשרה מראש כדי לתעד את הנתונים הבאים:
- ספירת מספר האנשים בתור.
- ספקו את מספר האנשים שמקבלים שירות בדלפק.
מה תלמדו
- איך ליצור אפליקציה ב-Vertex AI Vision ולפרוס אותה
- איך מגדירים סטרימינג של RTSP באמצעות קובץ וידאו ומטמיעים את הסטרימינג ב-Vertex AI Vision באמצעות vaictl מ-Jupyter Notebook.
- איך משתמשים במודל 'ניתוח תפוסה' ובתכונות השונות שלו.
- איך מחפשים סרטונים במחסן המדיה של Vertex AI Vision.
- איך מחברים את הפלט ל-BigQuery, כותבים שאילתת SQL כדי לחלץ תובנות מהפלט של ה-JSON של המודל ומשתמשים בפלט כדי לתייג את הסרטון המקורי ולהוסיף לו הערות.
עלות:
העלות הכוללת של הפעלת שיעור ה-Lab הזה ב-Google Cloud היא כ-2$.
2. לפני שתתחיל
יוצרים פרויקט ומפעילים ממשקי API:
- במסוף Google Cloud, בדף בורר הפרויקטים, בוחרים או יוצרים פרויקט ב-Google Cloud. הערה: אם אתם לא מתכננים לשמור את המשאבים שאתם יוצרים בתהליך, צריך ליצור פרויקט במקום לבחור פרויקט קיים. בסיום התהליך תוכלו למחוק את הפרויקט ולהסיר את כל המשאבים הקשורים אליו. כניסה לדף לבחירת הפרויקט
- הקפידו לוודא שהחיוב מופעל בפרויקט שלכם ב-Cloud. כך בודקים אם החיוב מופעל בפרויקט
- מפעילים את Compute Engine, Vertex API, Notebook API ו-Vision AI API. הפעלת ממשקי ה-API
יוצרים חשבון שירות:
- נכנסים לדף Create service account במסוף Google Cloud. כניסה לדף Create service account
- בוחרים את הפרויקט הרצוי.
- כותבים שם בשדה Service account name. השדה Service account ID במסוף Google Cloud יאוכלס בהתאם לשם הזה. כותבים תיאור בשדה Service account description. לדוגמה, 'חשבון שירות' במדריך למתחילים.
- לוחצים על יצירה והמשך.
- כדי לתת גישה לפרויקט, מקצים לחשבון השירות את התפקידים הבאים:
- Vision AI > Vision AI Editor
- Compute Engine > Compute Instance Admin (בטא)
- BigQuery > אדמין של BigQuery
בוחרים תפקיד מהרשימה Select a role. כדי להוסיף עוד תפקידים, לוחצים על Add another role ומוסיפים אותם אחד אחרי השני.
- לוחצים על המשך.
- לוחצים על Done כדי לסיים ליצור את חשבון השירות. חשוב לא לסגור את חלון הדפדפן. תשתמשו בו בשלב הבא.
3. הגדרת Jupyter Notebook
לפני שיוצרים אפליקציה ב'ניתוח תפוסה', צריך לרשום מקור נתונים שאפשר להשתמש בו מאוחר יותר באפליקציה.
במדריך הזה תלמדו ליצור מכונה של Jupyter Notebook שמארחת סרטון, ולשלוח את נתוני הסטרימינג של הסרטון מהמכונה. אנחנו משתמשים ב-Jupyter Notebook כי הוא מספק לנו גמישות להריץ פקודות מעטפת וגם להריץ קוד מותאם אישית של עיבוד מקדים או עיבוד לאחרי, במקום אחד שמתאים מאוד לניסויים מהירים. אנחנו נשתמש ב-notebook הזה כדי:
- הפעלת שרת rtsp בתור תהליך ברקע
- הרצת הפקודה vaictl כתהליך ברקע
- הרצת שאילתות וקוד עיבוד כדי לנתח את הפלט של ניתוח התפוסה
יצירת notebook של Jupyter
השלב הראשון בשליחת וידאו ממכונת Jupyter Notebook הוא יצירת המסמך באמצעות חשבון השירות שיצרנו בשלב הקודם.
- נכנסים לדף Vertex AI במסוף. כניסה ל-Vertex AI Workbench
- לוחצים על 'notebooks בניהול המשתמשים'.
- לוחצים על New Notebook (יומן חדש) > Tensorflow Enterprise 2.6 (with LTS) (TensorFlow Enterprise 2.6 עם LTS) > Without GPUs (ללא מעבדי GPU).
- נותנים שם ל-Jupyter Notebook. מידע נוסף זמין במאמר מוסכמה למתן שמות למשאבים.
- לוחצים על אפשרויות מתקדמות.
- גוללים למטה לקטע קטעי הרשאות.
- מבטלים את הסימון של האפשרות שימוש בחשבון השירות שמוגדר כברירת מחדל ב-Compute Engine.
- מוסיפים את כתובת האימייל של חשבון השירות שנוצר בשלב הקודם. ולוחצים על Create.
- אחרי יצירת המכונה, לוחצים על OPEN JUPYTERLAB.
4. איך מגדירים ב-Notebook סטרימינג של סרטונים
לפני שיוצרים אפליקציה ב'ניתוח תפוסה', צריך לרשום מקור נתונים שאפשר להשתמש בו מאוחר יותר באפליקציה.
במדריך הזה נשתמש במכונה של Jupyter Notebook כדי לארח סרטון, ותשלחו את נתוני הסטרימינג של הסרטון ממסוף Notebook.
הורדת כלי שורת הפקודה vaictl
- במכונה הפתוחה של JupyterLab, פותחים Notebook מהמרכז למשימות.
- מורידים את הכלי של שורת הפקודה של Vertex AI Vision (vaictl), את הכלי של שורת הפקודה של שרת ה-RTSP ואת הכלי של open-cv באמצעות הפקודה הבאה בתא של ה-notebook:
!wget -q https://github.com/aler9/rtsp-simple-server/releases/download/v0.20.4/rtsp-simple-server_v0.20.4_linux_amd64.tar.gz
!wget -q https://github.com/google/visionai/releases/download/v0.0.4/visionai_0.0-4_amd64.deb
!tar -xf rtsp-simple-server_v0.20.4_linux_amd64.tar.gz
!pip install opencv-python --quiet
!sudo apt-get -qq remove -y visionai
!sudo apt-get -qq install -y ./visionai_0.0-4_amd64.deb
!sudo apt-get -qq install -y ffmpeg
5. הטמעת קובץ וידאו לשידור בסטרימינג
אחרי שמגדירים את סביבת היומן באמצעות הכלים הנדרשים בשורת הפקודה, אפשר להעתיק קובץ וידאו לדוגמה ולהשתמש ב-vaictl כדי להעביר את נתוני הסרטון בסטרימינג לאפליקציית ניתוח נתוני התפוסה.
רישום של שידור חדש
- לוחצים על הכרטיסייה 'מקורות נתונים' בחלונית הימנית של Vertex AI Vision.
- לוחצים על לחצן ההרשמה בחלק העליון
- בשדה 'שם מקור הנתונים', מזינים 'queue-stream'.
- בשדה region, בוחרים את אותו אזור שנבחר במהלך יצירת המחברות בשלב הקודם.
- לוחצים על Register.
העתקת סרטון לדוגמה ל-VM
- ביומן, מעתיקים סרטון לדוגמה באמצעות הפקודה הבאה של wget.
!wget -q https://github.com/vagrantism/interesting-datasets/raw/main/video/collective_activity/seq25_h264.mp4
שידור וידאו ממכונה וירטואלית והטמעת נתונים בשידור
- כדי לשלוח את קובץ הסרטון המקומי הזה לזרם הקלט של האפליקציה, משתמשים בפקודה הבאה בתא של המחברות. צריך לבצע את החלפות המשתנים הבאות:
- PROJECT_ID: מזהה הפרויקט שלכם ב-Google Cloud.
- LOCATION: מזהה המיקום שלך. לדוגמה, us-central1. מידע נוסף זמין במאמר מיקומים ב-Cloud.
- LOCAL_FILE: שם הקובץ של קובץ וידאו מקומי. לדוגמה,
seq25_h264
.mp4.
PROJECT_ID='<Your Google Cloud project ID>'
LOCATION='<Your stream location>'
LOCAL_FILE='seq25_h264.mp4'
STREAM_NAME='queue-stream'
- מפעילים שרת rtsp-simple-server שבו אנחנו משדרים את קובץ הווידאו באמצעות פרוטוקול rtsp
import os
import time
import subprocess
subprocess.Popen(["nohup", "./rtsp-simple-server"], stdout=open('rtsp_out.log', 'a'), stderr=open('rtsp_err.log', 'a'), preexec_fn=os.setpgrp)
time.sleep(5)
- שימוש בכלי שורת הפקודה ffmpeg כדי להפעיל את הסרטון בלופ בשידור ה-RTSP
subprocess.Popen(["nohup", "ffmpeg", "-re", "-stream_loop", "-1", "-i", LOCAL_FILE, "-c", "copy", "-f", "rtsp", f"rtsp://localhost:8554/{LOCAL_FILE.split('.')[0]}"], stdout=open('ffmpeg_out.log', 'a'), stderr=open('ffmpeg_err.log', 'a'), preexec_fn=os.setpgrp)
time.sleep(5)
- משתמשים בכלי שורת הפקודה vaictl כדי להעביר את הסרטון בשידור חי מ-URI של שרת RTSP אל 'queue-stream', מקור הנתונים של Vertex AI Vision שנוצר בשלב הקודם.
subprocess.Popen(["nohup", "vaictl", "-p", PROJECT_ID, "-l", LOCATION, "-c", "application-cluster-0", "--service-endpoint", "visionai.googleapis.com", "send", "rtsp", "to", "streams", "queue-stream", "--rtsp-uri", f"rtsp://localhost:8554/{LOCAL_FILE.split('.')[0]}"], stdout=open('vaictl_out.log', 'a'), stderr=open('vaictl_err.log', 'a'), preexec_fn=os.setpgrp)
יכול להיות שיחלפו כ-100 שניות בין תחילת פעולת הטמעת הנתונים ב-vaictl לבין הופעת הסרטון במרכז הבקרה.
אחרי שהטמעת הנתונים מהסטרימינג תהיה זמינה, תוכלו לראות את פיד הסרטונים בכרטיסייה Streams בלוח הבקרה של Vertex AI Vision, על ידי בחירה בסטרימינג queue-stream.
6. יצירת אפליקציה
השלב הראשון הוא ליצור אפליקציה שתعالج את הנתונים שלכם. אפשר לחשוב על אפליקציה כצינור אוטומטי לעיבוד נתונים שמחבר בין הגורמים הבאים:
- הטמעת נתונים: הטמעת פיד וידאו בסטרימינג.
- ניתוח נתונים: אפשר להוסיף מודל AI(ראיית מכונה) אחרי הטמעת הנתונים.
- אחסון נתונים: אפשר לאחסן את שתי הגרסאות של פיד הווידאו (השידור המקורי והשידור שעבר עיבוד על ידי מודל ה-AI) במחסן מדיה.
במסוף Google Cloud, אפליקציה מיוצגת כתרשים.
יצירת אפליקציה ריקה
כדי לאכלס את תרשים האפליקציות, קודם צריך ליצור אפליקציה ריקה.
יוצרים אפליקציה במסוף Google Cloud.
- נכנסים למסוף של Google Cloud.
- פותחים את הכרטיסייה Applications בלוח הבקרה של Vertex AI Vision. עוברים לכרטיסייה 'אפליקציות'.
- לוחצים על הלחצן Create.
- מזינים 'queue-app' בתור שם האפליקציה ובוחרים את האזור.
- לוחצים על יצירה.
הוספת צמתים של רכיבי אפליקציה
אחרי שיוצרים את האפליקציה הריקה, אפשר להוסיף את שלושת הצמתים לתרשים האפליקציה:
- צומת הטמעה: משאב הסטרימינג שמטמיע נתונים שנשלחים משרת וידאו מסוג RTSP שיצרתם ביומן.
- צומת של עיבוד: מודל ניתוח התפוסה שפועל על נתונים שהוטמעו.
- צומת אחסון: מחסן המדיה שבו מאוחסנים סרטונים שעברו עיבוד, ומשמש גם כמאגר מטא-נתונים. מאגרי המטא-נתונים כוללים מידע אנליטי על נתוני הסרטונים שצורפו, ומידע שנגזר על ידי מודלים של AI.
מוסיפים צמתים של רכיבים לאפליקציה במסוף.
- פותחים את הכרטיסייה Applications במרכז הבקרה של Vertex AI Vision. כניסה לכרטיסייה 'אפליקציות'
פעולה זו תעביר אתכם לתצוגה חזותית של צינור עיבוד הנתונים.
הוספת צומת להטמעת נתונים
- כדי להוסיף צומת של מקור נתונים, בוחרים באפשרות Streams בקטע Connectors בתפריט הצדדי.
- בקטע Source בתפריט Stream שנפתח, בוחרים באפשרות Add streams.
- בתפריט Add streams בוחרים באפשרות queue-stream.
- כדי להוסיף את הסטרימינג לתרשים האפליקציה, לוחצים על הוספת סטרימינג.
הוספת צומת לעיבוד נתונים
- כדי להוסיף את הצומת של מודל ספירת התושבים, בוחרים באפשרות ניתוח נתוני תפוסה בקטע מודלים מיוחדים בתפריט הצדדי.
- משאירים את האפשרויות שמוגדרות כברירת מחדל אנשים. מבטלים את הסימון של האפשרות Vehicles (כלי רכב) אם היא כבר מסומנת.
- בקטע 'אפשרויות מתקדמות', לוחצים על יצירת תחומים/קווים פעילים
- כדי לספור אנשים באזור הזה, מציירים את האזורים הפעילים באמצעות הכלי 'פוליגון'. מסמנים את האזור בהתאם.
- לוחצים על 'חץ חזרה' למעלה.
- מוסיפים הגדרות לזמן שהייה כדי לזהות גודש בלחיצה על תיבת הסימון.
הוספת צומת לאחסון נתונים
- כדי להוסיף את צומת היעד של הפלט (אחסון), בוחרים באפשרות Vision AI Warehouse בקטע Connectors בתפריט הצדדי.
- לוחצים על המחבר Vertex AI Warehouse כדי לפתוח את התפריט שלו, ואז לוחצים על Connect warehouse.
- בתפריט קישור מחסן, בוחרים באפשרות יצירת מחסן חדש. נותנים למחסן את השם queue-warehouse,ומשאירים את משך ה-TTL ל-14 ימים.
- לוחצים על הלחצן Create (יצירה) כדי להוסיף את המחסן.
7. חיבור הפלט לטבלה ב-BigQuery
כשמוסיפים מחבר BigQuery לאפליקציית Vertex AI Vision, כל הפלט של מודל האפליקציה המקושר ייכלל בטבלת היעד.
אתם יכולים ליצור טבלה משלכם ב-BigQuery ולציין את הטבלה הזו כשמוסיפים מחבר BigQuery לאפליקציה, או לאפשר לפלטפורמת האפליקציה של Vertex AI Vision ליצור את הטבלה באופן אוטומטי.
יצירת טבלה אוטומטית
אם מאפשרים לפלטפורמת האפליקציה Vertex AI Vision ליצור את הטבלה באופן אוטומטי, אפשר לציין את האפשרות הזו כשמוסיפים את הצומת של מחבר BigQuery.
התנאים הבאים לגבי מערך נתונים וטבלה חלים אם רוצים להשתמש ביצירת טבלה אוטומטית:
- מערך נתונים: שם מערך הנתונים שנוצר באופן אוטומטי הוא visionai_dataset.
- טבלה: שם הטבלה שנוצר באופן אוטומטי הוא visionai_dataset.APPLICATION_ID.
- טיפול בשגיאות:
- אם הטבלה עם אותו שם קיימת באותו מערך נתונים, לא מתבצע יצירה אוטומטית.
- פותחים את הכרטיסייה Applications בלוח הבקרה של Vertex AI Vision. כניסה לכרטיסייה 'אפליקציות'
- בוחרים באפשרות הצגת האפליקציה לצד שם האפליקציה מהרשימה.
- בדף של יוצר האפליקציות, בוחרים באפשרות BigQuery בקטע Connectors (מחברים).
- משאירים את השדה BigQuery path (נתיב BigQuery).
- בקטע store metadata from: (אחסון המטא-נתונים מ:), בוחרים רק באפשרות occupancy Analytics' (ניתוח נתוני תפוסה) ומבטלים את הסימון של מקורות הנתונים.
תרשים האפליקציה הסופי אמור להיראות כך:
8. פריסה של האפליקציה לשימוש
אחרי שיוצרים את האפליקציה מקצה לקצה עם כל הרכיבים הנדרשים, השלב האחרון בשימוש באפליקציה הוא לפרוס אותה.
- פותחים את הכרטיסייה Applications בלוח הבקרה של Vertex AI Vision. כניסה לכרטיסייה 'אפליקציות'
- בוחרים באפשרות הצגת האפליקציה לצד האפליקציה אפליקציה לתור ברשימה.
- בדף Studio, לוחצים על הלחצן Deploy.
- בתיבת הדו-שיח הבאה לאישור, לוחצים על Deploy (פריסה). פעולת הפריסה עשויה להימשך מספר דקות. בסיום הפריסה יופיעו סימני וי ירוקים ליד הצמתים.
9. חיפוש תוכן וידאו במחסן האחסון
אחרי שמטמיעים נתוני וידאו באפליקציית העיבוד, אפשר לראות את נתוני הסרטונים שנותחו ולחפש בנתונים על סמך ניתוח נתוני תפוסה.
- פותחים את הכרטיסייה Warehouses במרכז הבקרה של Vertex AI Vision. כניסה לכרטיסייה Warehouses
- מחפשים את המחסן queue-warehouse ברשימה ולוחצים על View assets (הצגת הנכסים).
- בקטע ספירת אנשים, מגדירים את הערך מינימום כ-1 ואת הערך מקסימום כ-5.
- כדי לסנן נתוני סרטונים מעובדים שמאוחסנים ב-Media warehouse של Vertex AI Vision, לוחצים על חיפוש.
תצוגה של נתוני וידאו שמאוחסנים ועומדים בקריטריונים לחיפוש במסוף Google Cloud.
10. הוספת הערות וניתוח פלט באמצעות טבלת BigQuery
- ב-Notebook, מאתחלים את המשתנים הבאים בתא.
DATASET_ID='vision_ai_dataset'
bq_table=f'{PROJECT_ID}.{DATASET_ID}.queue-app'
frame_buffer_size=10000
frame_buffer_error_milliseconds=5
dashboard_update_delay_seconds=3
rtsp_url='rtsp://localhost:8554/seq25_h264'
- עכשיו נצלם את המסגרות מהשידור ב-RTSP באמצעות הקוד הבא:
import cv2
import threading
from collections import OrderedDict
from datetime import datetime, timezone
frame_buffer = OrderedDict()
frame_buffer_lock = threading.Lock()
stream = cv2.VideoCapture(rtsp_url)
def read_frames(stream):
global frames
while True:
ret, frame = stream.read()
frame_ts = datetime.now(timezone.utc).timestamp() * 1000
if ret:
with frame_buffer_lock:
while len(frame_buffer) >= frame_buffer_size:
_ = frame_buffer.popitem(last=False)
frame_buffer[frame_ts] = frame
frame_buffer_thread = threading.Thread(target=read_frames, args=(stream,))
frame_buffer_thread.start()
print('Waiting for stream initialization')
while not list(frame_buffer.keys()): pass
print('Stream Initialized')
- מחלצים את חותמת הזמן של הנתונים ואת פרטי ההערה מהטבלה ב-BigQuery ויוצרים ספרייה לאחסון תמונות המסגרות שצולמו:
from google.cloud import bigquery
import pandas as pd
client = bigquery.Client(project=PROJECT_ID)
query = f"""
SELECT MAX(ingestion_time) AS ts
FROM `{bq_table}`
"""
bq_max_ingest_ts_df = client.query(query).to_dataframe()
bq_max_ingest_epoch = str(int(bq_max_ingest_ts_df['ts'][0].timestamp()*1000000))
bq_max_ingest_ts = bq_max_ingest_ts_df['ts'][0]
print('Preparing to pull records with ingestion time >', bq_max_ingest_ts)
if not os.path.exists(bq_max_ingest_epoch):
os.makedirs(bq_max_ingest_epoch)
print('Saving output frames to', bq_max_ingest_epoch)
- מוסיפים הערות לפריימים באמצעות הקוד הבא:
import json
import base64
import numpy as np
from IPython.display import Image, display, HTML, clear_output
im_width = stream.get(cv2.CAP_PROP_FRAME_WIDTH)
im_height = stream.get(cv2.CAP_PROP_FRAME_HEIGHT)
dashdelta = datetime.now()
framedata = {}
cntext = lambda x: {y['entity']['labelString']: y['count'] for y in x}
try:
while True:
try:
annotations_df = client.query(f'''
SELECT ingestion_time, annotation
FROM `{bq_table}`
WHERE ingestion_time > TIMESTAMP("{bq_max_ingest_ts}")
''').to_dataframe()
except ValueError as e:
continue
bq_max_ingest_ts = annotations_df['ingestion_time'].max()
for _, row in annotations_df.iterrows():
with frame_buffer_lock:
frame_ts = np.asarray(list(frame_buffer.keys()))
delta_ts = np.abs(frame_ts - (row['ingestion_time'].timestamp() * 1000))
delta_tx_idx = delta_ts.argmin()
closest_ts_delta = delta_ts[delta_tx_idx]
closest_ts = frame_ts[delta_tx_idx]
if closest_ts_delta > frame_buffer_error_milliseconds: continue
image = frame_buffer[closest_ts]
annotations = json.loads(row['annotation'])
for box in annotations['identifiedBoxes']:
image = cv2.rectangle(
image,
(
int(box['normalizedBoundingBox']['xmin']*im_width),
int(box['normalizedBoundingBox']['ymin']*im_height)
),
(
int((box['normalizedBoundingBox']['xmin'] + box['normalizedBoundingBox']['width'])*im_width),
int((box['normalizedBoundingBox']['ymin'] + box['normalizedBoundingBox']['height'])*im_height)
),
(255, 0, 0), 2
)
img_filename = f"{bq_max_ingest_epoch}/{row['ingestion_time'].timestamp() * 1000}.png"
cv2.imwrite(img_filename, image)
binimg = base64.b64encode(cv2.imencode('.jpg', image)[1]).decode()
curr_framedata = {
'path': img_filename,
'timestamp_error': closest_ts_delta,
'counts': {
**{
k['annotation']['displayName'] : cntext(k['counts'])
for k in annotations['stats']["activeZoneCounts"]
},
'full-frame': cntext(annotations['stats']["fullFrameCount"])
}
}
framedata[img_filename] = curr_framedata
if (datetime.now() - dashdelta).total_seconds() > dashboard_update_delay_seconds:
dashdelta = datetime.now()
clear_output()
display(HTML(f'''
<h1>Queue Monitoring Application</h1>
<p>Live Feed of the queue camera:</p>
<p><img alt="" src="{img_filename}" style="float: left;"/></a></p>
<table border="1" cellpadding="1" cellspacing="1" style="width: 500px;">
<caption>Current Model Outputs</caption>
<thead>
<tr><th scope="row">Metric</th><th scope="col">Value</th></tr>
</thead>
<tbody>
<tr><th scope="row">Serving Area People Count</th><td>{curr_framedata['counts']['serving-zone']['Person']}</td></tr>
<tr><th scope="row">Queueing Area People Count</th><td>{curr_framedata['counts']['queue-zone']['Person']}</td></tr>
<tr><th scope="row">Total Area People Count</th><td>{curr_framedata['counts']['full-frame']['Person']}</td></tr>
<tr><th scope="row">Timestamp Error</th><td>{curr_framedata['timestamp_error']}</td></tr>
</tbody>
</table>
<p> </p>
'''))
except KeyboardInterrupt:
print('Stopping Live Monitoring')
- מפסיקים את המשימה של הוספת ההערות באמצעות הלחצן Stop בסרגל התפריטים של המחברות.
- ניתן לחזור למסגרות ספציפיות באמצעות הקוד הבא:
from IPython.html.widgets import Layout, interact, IntSlider
imgs = sorted(list(framedata.keys()))
def loadimg(frame):
display(framedata[imgs[frame]])
display(Image(open(framedata[imgs[frame]]['path'],'rb').read()))
interact(loadimg, frame=IntSlider(
description='Frame #:',
value=0,
min=0, max=len(imgs)-1, step=1,
layout=Layout(width='100%')))
11. מזל טוב
כל הכבוד, סיימת את שיעור ה-Lab!
ניקוי
כדי להימנע מחיובים בחשבון Google Cloud בגלל השימוש במשאבים שנעשה במסגרת המדריך הזה, אפשר למחוק את הפרויקט שמכיל את המשאבים, או להשאיר את הפרויקט ולמחוק את המשאבים הנפרדים.
מחיקת הפרויקט
מחיקת משאבים ספציפיים
מקורות מידע
https://cloud.google.com/vision-ai/docs/overview
https://cloud.google.com/vision-ai/docs/occupancy-count-tutorial