1. מטרות
סקירה כללית
ב-Codelab הזה נתמקד ביצירה של אפליקציית Vertex AI Vision מקצה לקצה כדי לעקוב אחרי גודל התור באמצעות צילומי וידאו של חנות קמעונאית. נשתמש בתכונות המובנות של המודל המיוחד שאומן מראש Occupancy analytics כדי לתעד את הדברים הבאים:
- ספרו את מספר האנשים שעומדים בתור.
- ספירת מספר האנשים שמקבלים שירות בדלפק.
מה תלמדו
- איך יוצרים ומפעילים אפליקציה ב-Vertex AI Vision
- איך מגדירים שידור RTSP באמצעות קובץ וידאו ומטמיעים את השידור ב-Vertex AI Vision באמצעות vaictl מ-Jupyter Notebook.
- איך משתמשים במודל Occupancy Analytics ובפיצ'רים השונים שלו.
- איך מחפשים סרטונים במאגר המדיה של Vertex AI Vision.
- איך מחברים את הפלט ל-BigQuery, כותבים שאילתת SQL כדי לחלץ תובנות מפלט ה-JSON של המודל ומשתמשים בפלט כדי להוסיף תוויות והערות לסרטון המקורי.
עלות:
העלות הכוללת להרצת ה-Lab הזה ב-Google Cloud היא כ-2$.
2. לפני שתתחיל
יוצרים פרויקט ומפעילים ממשקי API:
- במסוף Google Cloud, בדף לבחירת הפרויקט בוחרים פרויקט או לוחצים על create a Google Cloud project. הערה: אם אתם לא מתכננים לשמור את המשאבים שתיצרו בתהליך הזה, תוכלו ליצור פרויקט חדש במקום לבחור באחד מהפרויקטים הקיימים. בסיום התהליך תוכלו למחוק את הפרויקט ולהסיר את כל המשאבים שמשויכים אליו. כניסה לדף לבחירת הפרויקט
- הקפידו לוודא שהחיוב מופעל בפרויקט שלכם ב-Cloud. כך בודקים אם החיוב מופעל בפרויקט
- מפעילים את Compute Engine, Vertex API, Notebook API ו-Vision AI API. הפעלת ממשקי ה-API
יצירת חשבון שירות:
- במסוף Google Cloud, נכנסים לדף יצירת חשבון שירות. כניסה לדף יצירת חשבון שירות
- בוחרים את הפרויקט הרצוי.
- כותבים שם בשדה Service account name. השדה מזהה חשבון שירות במסוף Google Cloud יאוכלס בהתאם לשם הזה. בשדה תיאור חשבון שירות, מזינים תיאור. לדוגמה, חשבון שירות להפעלה מהירה.
- לוחצים על יצירה והמשך.
- כדי לתת גישה לפרויקט, מקצים לחשבון השירות את התפקידים הבאים:
- Vision AI > Vision AI Editor
- Compute Engine > אדמין מכונות של Compute (בטא)
- BigQuery > BigQuery Admin (BigQuery > ניהול BigQuery).
בוחרים תפקיד מהרשימה Select a role. כדי להוסיף עוד תפקידים, לוחצים על Add another role ומוסיפים אותם אחד אחרי השני.
- לוחצים על המשך.
- לוחצים על Done כדי לסיים ליצור את חשבון השירות. חשוב לא לסגור את חלון הדפדפן. תשתמשו בו בשלב הבא.
3. הגדרת Jupyter Notebook
לפני שיוצרים אפליקציה ב-Occupancy Analytics, צריך לרשום מקור נתונים שאפשר להשתמש בו בהמשך באפליקציה.
במדריך הזה יוצרים מופע של Jupyter Notebook שמארח סרטון, ושולחים את נתוני הסרטון בסטרימינג מה-notebook. אנחנו משתמשים ב-jupyter notebook כי הוא מאפשר לנו לבצע פקודות shell וגם להריץ קוד מותאם אישית לעיבוד מקדים או לעיבוד שאחרי העיבוד במקום אחד, וזה מאוד נוח לניסויים מהירים. נשתמש ב-notebook הזה כדי:
- הפעלת שרת rtsp כתהליך ברקע
- הפעלת הפקודה vaictl כתהליך ברקע
- הרצת שאילתות וקוד עיבוד כדי לנתח את הפלט של ניתוח התפוסה
יצירת Jupyter Notebook
השלב הראשון בשליחת סרטון ממופע של Jupyter Notebook הוא יצירת ה-notebook עם חשבון השירות שיצרנו בשלב הקודם.
- במסוף, עוברים לדף Vertex AI. מעבר אל Vertex AI Workbench
- לוחצים על 'מחברות בניהול המשתמשים'.

- לוחצים על New Notebook > Tensorflow Enterprise 2.6 (with LTS) > Without GPUs (מחברת חדשה > Tensorflow Enterprise 2.6 (עם LTS) > ללא מעבדי GPU).

- מזינים את השם של מחברת Jupyter. מידע נוסף זמין במאמר מוסכמות למתן שמות למשאבים.

- לוחצים על אפשרויות מתקדמות.
- גוללים למטה אל קטעי הרשאות.
- מבטלים את הסימון של האפשרות שימוש בחשבון השירות שמוגדר כברירת מחדל ב-Compute Engine.
- מוסיפים את כתובת האימייל של חשבון השירות שנוצר בשלב הקודם. לוחצים על יצירה.

- אחרי שיוצרים את המופע, לוחצים על OPEN JUPYTERLAB (פתיחת JupyterLab).
4. הגדרת מחברת לסטרימינג של סרטון
לפני שיוצרים אפליקציה ב-Occupancy Analytics, צריך לרשום מקור נתונים שאפשר להשתמש בו בהמשך באפליקציה.
במדריך הזה נשתמש במופע 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. העלאת קובץ וידאו לשידור
אחרי שמגדירים את סביבת ה-notebook עם כלי שורת הפקודה הנדרשים, אפשר להעתיק סרטון לאימון המודל ואז להשתמש ב-vaictl כדי להזרים את נתוני הווידאו לאפליקציה לניתוח נתוני תפוסה.
רישום של שידור חדש
- בחלונית הימנית של Vertex AI Vision, לוחצים על הכרטיסייה 'סטרימינג'.
- לוחצים על לחצן ההרשמה בחלק העליון

- בשדה Stream name (שם השידור), מזינים queue-stream.
- באזור, בוחרים את אותו אזור שנבחר במהלך יצירת ה-Notebook בשלב הקודם.
- לוחצים על הרשמה.
העתקת סרטון לאימון המודל למכונה הווירטואלית
- במחברת, מעתיקים סרטון לאימון המודל באמצעות פקודת ה-wget הבאה.
!wget -q https://github.com/vagrantism/interesting-datasets/raw/main/video/collective_activity/seq25_h264.mp4
שידור וידאו ממכונה וירטואלית והטמעת נתונים בשידור
- כדי לשלוח את קובץ הווידאו המקומי הזה לזרם הקלט של האפליקציה, משתמשים בפקודה הבאה בתא של המחברת. צריך להחליף את המשתנים הבאים:
- PROJECT_ID: מזהה הפרויקט שלכם ב-Google Cloud.
- מיקום: מזהה המיקום שלכם. לדוגמה, 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. מעבר לכרטיסייה 'אפליקציות'
- לוחצים על הלחצן יצירה.

- מזינים queue-app' כשם האפליקציה ובוחרים את האזור.
- לוחצים על יצירה.
הוספת צמתים של רכיבי אפליקציה
אחרי שיוצרים את האפליקציה הריקה, אפשר להוסיף את שלושת הצמתים לגרף האפליקציה:
- צומת קליטה: משאב הסטרימינג שקולט נתונים שנשלחים משרת וידאו rtsp שיצרתם ב-notebook.
- צומת עיבוד: מודל ניתוח הנתונים של תפוסת המקום שפועל על נתונים שהועברו.
- צומת אחסון: מחסן המדיה שבו מאוחסנים סרטונים שעברו עיבוד, ומשמש גם לאחסון מטא-נתונים. מאגרי המטא-נתונים כוללים מידע אנליטי על נתוני וידאו שהועברו, ומידע שהוסק על ידי מודלים של AI.
מוסיפים צמתי רכיבים לאפליקציה במסוף.
- פותחים את הכרטיסייה Applications (אפליקציות) בלוח הבקרה של Vertex AI Vision. מעבר לכרטיסייה 'אפליקציות'
הפעולה הזו תעביר אתכם לתרשים להמחשה של פייפליין העיבוד.
הוספה של צומת להעברת נתונים
- כדי להוסיף צומת של זרם קלט, בוחרים באפשרות Streams (זרמים) בקטע Connectors (מחברים) בתפריט הצד.
- בתפריט Stream שנפתח, בקטע Source, לוחצים על Add streams.
- בתפריט הוספת סטרימינג, בוחרים באפשרות queue-stream.
- כדי להוסיף את הזרם לתרשים האפליקציה, לוחצים על הוספת זרמים.
הוספה של צומת לעיבוד נתונים
- כדי להוסיף את הצומת של מודל ספירת התפוסה, בוחרים באפשרות ניתוח נתוני תפוסה בקטע מודלים ייעודיים בתפריט הצד.
- משאירים את האפשרות אנשים שמוגדרת כברירת מחדל. מבטלים את הסימון של כלי רכב אם היא כבר מסומנת.

- בקטע Advanced Options (אפשרויות מתקדמות), לוחצים על Create Active Zones/Lines (יצירת אזורים או קווים פעילים)
. - מציירים את אזורי הפעילות באמצעות הכלי 'מצולע' כדי לספור את האנשים באזור הזה. נותנים לאזור תווית מתאימה

- לוחצים על החץ לחזרה לאחור בחלק העליון.

- כדי להוסיף הגדרות לזמן השהייה כדי לזהות עומס, לוחצים על תיבת הסימון.

הוספה של צומת לאחסון נתונים
- כדי להוסיף את צומת יעד הפלט (אחסון), בוחרים באפשרות VIsion AI Warehouse בקטע Connectors בתפריט הצד.
- לוחצים על מחבר Vertex AI Warehouse כדי לפתוח את התפריט שלו, ואז לוחצים על Connect warehouse (חיבור למחסן נתונים).
- בתפריט Connect warehouse, בוחרים באפשרות Create new warehouse. נותנים למחסן את השם queue-warehouse ומשאירים את משך ה-TTL על 14 ימים.
- לוחצים על הלחצן יצירה כדי להוסיף את המחסן.
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 ריק.

- בקטע מטא-נתונים של החנות מ: בוחרים רק באפשרות 'ניתוח נתוני תפוסה' ומבטלים את הסימון של מקורות נתונים.
גרף האפליקציה הסופי אמור להיראות כך:

8. פריסת האפליקציה לשימוש
אחרי שיוצרים את האפליקציה מקצה לקצה עם כל הרכיבים הנדרשים, השלב האחרון בשימוש באפליקציה הוא פריסה שלה.
- פותחים את הכרטיסייה Applications (אפליקציות) בלוח הבקרה של Vertex AI Vision. מעבר לכרטיסייה 'אפליקציות'
- ברשימה, לוחצים על View app (הצגת האפליקציה) לצד האפליקציה queue-app.
- בדף Studio, לוחצים על הלחצן Deploy (פריסה).
- בתיבת הדו-שיח לאישור שמופיעה, לוחצים על פריסה. יכול להיות שייקח כמה דקות עד שהפריסה תושלם. אחרי שהפריסה מסתיימת, מופיעים סימני וי ירוקים ליד הצמתים.

9. חיפוש תוכן וידאו במחסן הנתונים
אחרי שמטמיעים נתוני וידאו באפליקציית העיבוד, אפשר לראות את נתוני הווידאו המנותחים ולחפש בנתונים על סמך מידע מניתוח נתוני התפוסה.
- פותחים את הכרטיסייה Warehouses (מחסני נתונים) בלוח הבקרה של Vertex AI Vision. כניסה לכרטיסייה Warehouses
- ברשימה, מחפשים את מחסן התורים ולוחצים על הצגת נכסים.
- בקטע People count (מספר האנשים), מגדירים את הערך Min (מינימום) ל-1 ואת הערך Max (מקסימום) ל-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')

- מפסיקים את משימת האנוטציה באמצעות הלחצן הפסקה בסרגל התפריטים של ה-notebook

- אפשר לחזור למסגרות ספציפיות באמצעות הקוד הבא:
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