Kamera IP menghasilkan aliran video terus-menerus, sehingga tinjauan manual terhadap rekaman menjadi tidak praktis dalam skala besar. Tutorial ini menjelaskan cara menggunakan fitur pengindeksan data OSS untuk membangun sistem pencarian semantik yang memungkinkan Anda mengkueri pustaka video menggunakan deskripsi bahasa alami—misalnya, "halaman dengan mobil yang diparkir."
Ikhtisar solusi
Solusi ini mencakup dua langkah:
Create a bucket and upload videos: Simpan file video mentah dari kamera IP Anda di bucket OSS.
Enable the AISearch feature: Aktifkan AISearch untuk bucket agar OSS mengindeks konten video dan menjadikannya dapat dicari menggunakan bahasa alami.
Kemampuan utama
| Capability | Description |
|---|---|
| Semantic search | Kueri video menggunakan deskripsi bahasa alami dan kondisi gabungan. Temukan frame spesifik dengan cepat tanpa tinjauan manual. |
| Multimodal search | Cari di seluruh video, gambar, dan teks melalui satu antarmuka. Mengurangi hambatan teknis serta biaya operasi dan maintenance (O&M). |
| Horizontal scaling | OSS menyediakan kapasitas tak terbatas dengan skalabilitas elastis untuk menangani set data video yang besar dan terus bertambah. |
Prasyarat
Sebelum memulai, pastikan Anda memiliki:
Akun Alibaba Cloud dengan akses OSS
Akses ke OSS console
File video yang siap diunggah (file sampel disediakan pada Langkah 1)
Langkah 1: Buat bucket dan unggah video
Login ke OSS console.
Buka halaman Buckets dan klik Create Bucket.
Pada halaman Create Bucket, masukkan nama bucket yang relevan dengan bisnis Anda, misalnya
ipc-videos-oss-metaquery-demo. Pertahankan konfigurasi default untuk semua parameter lainnya.Klik Create. Setelah bucket dibuat, klik View Bucket.
Pada halaman File List, klik Upload File > Scan File. Pilih file video yang akan diunggah, seperti Video A.mp4, Video B.mp4, dan Video C.mp4. Pertahankan konfigurasi default dan klik Upload File.
(Opsional) Tambahkan tag ke file video yang telah diunggah. Tag memungkinkan Anda memfilter video berdasarkan atribut seperti ID kamera atau lokasi selama proses pengindeksan maupun pencarian. Pada kolom Actions untuk file target, pilih
> More > Tagging. Di kotak dialog, tambahkan pasangan kunci-nilai tag. Contohnya: Klik OK.Tetapkan kunci ke
need-seekdan nilai ketrueuntuk digunakan sebagai kondisi filter saat membuat indeks.Tetapkan kunci ke
cameradan nilai kecamera-auntuk memfilter hasil berdasarkan kamera selama pencarian.
Langkah 2: Aktifkan fitur AISearch
Pada panel navigasi kiri, pilih File Management > Data Indexing.
Pada halaman Data Indexing, jika ini pertama kali Anda menggunakan pengindeksan data, ikuti instruksi di layar untuk memberikan izin kepada peran AliyunMetaQueryDefaultRole. Hal ini memungkinkan OSS mengelola data di bucket Anda. Setelah memberikan izin, klik Enable Data Indexing dan pilih Search.
(Opsional) AI Content Awareness: Pilih Image Content Awareness atau Video Content Awareness sesuai kebutuhan Anda.
(Opsional) File filtering rules: Konfigurasikan opsi ini untuk menjalankan analisis AI hanya pada file yang memenuhi kondisi tertentu. Maksimal lima aturan didukung. Kriteria filter mencakup awalan (prefix), ukuran file, tag, dan LastModifiedTime. Misalnya, tambahkan aturan filter tag dengan kunci
need-seekdan nilaitrue— sistem kemudian hanya mengindeks file yang memiliki tag tersebut.Jika filter file diaktifkan, Anda dikenai biaya pengindeksan data, AISearch, dan content awareness hanya berdasarkan jumlah file yang difilter.
Klik Enable.
Pembuatan indeks metadata memerlukan waktu. Durasi tepatnya bergantung pada jumlah objek di bucket. Muat ulang halaman untuk memeriksa status.
![]() | ![]() |
|---|
Verifikasi hasil
Cukup masukkan teks deskriptif, seperti a yard with a parked car, dan sistem akan mengembalikan video relevan yang sesuai dengan deskripsi tersebut.
Pada halaman Buckets, klik nama bucket Anda.
Pada halaman Files, pastikan video telah diunggah.
Pada panel navigasi kiri, pilih File Management > Data Indexing.
Pada halaman Data Indexing, masukkan
a yard with a parked cardi kotak pencarian. Untuk Media Type, pilihVideo.(Opsional) Untuk Object Tagging, tetapkan kunci ke
cameradan nilai kecamera-a. Sistem hanya akan mengembalikan video yang ditandai dengancamera=camera-a.Klik Search.
Salin path file dari hasil pencarian. Kembali ke halaman Objects, tempel path tersebut ke kotak pencarian, lalu cari untuk melihat video yang sesuai.
![]() |
|---|
Mulai Beroperasi
Langkah-langkah konsol di atas bertujuan untuk validasi fitur. Untuk lingkungan produksi, gunakan kit pengembangan perangkat lunak (SDK) OSS untuk mengotomatiskan unggah video, manajemen tag, dan kueri pencarian.
Unggah video ke OSS
Kamera IP menghasilkan aliran video terus-menerus. Gunakan SDK Python OSS untuk mengunggah segmen video ke bucket Anda secara otomatis.
Contoh berikut menggunakan file upload manager untuk mengunggah video:
import argparse
import alibabacloud_oss_v2 as oss
from alibabacloud_oss_v2.models import ListObjectsRequest, PutObjectTaggingRequest, Tagging, TagSet, Tag, PutObjectRequest
import os
def upload_video(client, bucket, video_config):
"""Upload a video"""
try:
# Check if the file exists
if not os.path.exists(video_config['path']):
print(f'File does not exist: {video_config["path"]}')
return False
# Create an object to upload the file
uploader = client.uploader()
# Execute the upload request - pass the filepath parameter correctly
result = uploader.upload_file(
filepath=video_config['path'], # Add the filepath parameter
request=PutObjectRequest(
bucket=bucket,
key=video_config['key']
)
)
print(f'Upload {video_config["key"]} successful:')
print(f'status code: {result.status_code},'
f' request id: {result.request_id}')
return True
except Exception as e:
print(f'Upload {video_config["key"]} failed: {str(e)}')
return False
def main():
# OSS configuration
args = argparse.Namespace(
region='cn-beijing',
bucket='ipc-videos-oss-metaquery-demo',
endpoint='https://oss-cn-beijing.aliyuncs.com'
)
# Configure the OSS client
credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials_provider
cfg.region = args.region
cfg.endpoint = args.endpoint
client = oss.Client(cfg)
# Video file configuration, including different tags
videos = [
{
'path': '<Your-Path>/VideoA.mp4',
'key': 'VideoA.mp4'
},
{
'path': '<Your-Path>/VideoB.mp4',
'key': 'VideoB.mp4'
},
{
'path': '<Your-Path>/VideoC.mp4',
'key': 'VideoC.mp4'
}
]
# Upload all videos
for video in videos:
print(f"\nStart uploading video: {video['key']}")
upload_video(
client=client,
bucket=args.bucket,
video_config=video
)
if __name__ == "__main__":
main()Ganti nilai placeholder sebelum menjalankan:
| Placeholder | Description | Example |
|---|---|---|
<Your-Path> | Direktori lokal yang berisi file video | /home/user/videos |
Jalankan pencarian semantik
Integrasikan OSS MetaQuery ke layanan backend Anda untuk mengkueri indeks video secara terprogram. Semua permintaan pencarian menggunakan API DoMetaQuery dengan mode=semantic.
Contoh berikut membangun permintaan XML yang sesuai dengan spesifikasi OSS MetaQuery dan mengembalikan hasil yang cocok beserta URL yang ditandatangani:
# -*- coding: utf-8 -*-
import argparse
import alibabacloud_oss_v2 as oss
# Parse the XML response
import xml.etree.ElementTree as ET
import json
from datetime import datetime
def get_search_conditions():
"""Get multi-conditional input from the user"""
print("Enter a semantic description (example: a yard with a parked car)")
query = input("> Semantic keywords: ").strip()
while not query:
print("Semantic keywords cannot be empty!")
query = input("> Semantic keywords: ").strip()
return query
def build_metaquery_xml(query):
"""Build a MetaQuery XML that complies with OSS specifications"""
xml_parts = [f'<Query>{query}</Query>']
# Add the MediaTypes tag, hard-coded to video here
xml_parts.append('<MediaTypes><MediaType>video</MediaType></MediaTypes>')
meta_query_xml = f'''<MetaQuery>
{"".join(xml_parts)}
</MetaQuery>'''
return meta_query_xml # Encode as a UTF-8 byte stream
def format_result(key, pre_url):
"""Format a single search result"""
return f""" File path: {key}
File address: {pre_url}
-----------------------"""
def semantic_search():
# Assign values directly to command line arguments
args = argparse.Namespace(
region = 'cn-beijing', # Replace with your region
bucket = 'ipc-videos-oss-metaquery-demo', # Replace with your bucket name
endpoint = 'https://oss-cn-beijing.aliyuncs.com', # Replace with your endpoint. You can leave this empty or delete it if not needed.
)
# Initialize the OSS client
credentials = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials
cfg.region = args.region
if args.endpoint:
cfg.endpoint = args.endpoint
client = oss.Client(cfg)
# Get user input
query = get_search_conditions() # Get only the query
# Build the request
try:
# Construct the request body (XML data)
data_str = build_metaquery_xml(query)
# Define the operation input
req = oss.OperationInput(
op_name='DoMetaQuery', # Custom action name
method='POST', # HTTP method
parameters={ # Query parameters
'metaQuery': '',
'mode': 'semantic',
'comp': 'query',
},
headers=None, # Custom request headers (optional)
body=data_str.encode("utf-8"), # Request body (encoded as a UTF-8 byte stream)
bucket=args.bucket, # Target bucket name
)
# Call the generic interface to execute the operation
resp = client.invoke_operation(req)
except oss.exceptions.ServiceError as e:
print(f" Server error: {e.message}")
return
root = ET.fromstring(resp.http_response.content.decode('utf-8'))
# Find all File elements
files = root.findall('.//File')
print(f"\n Found {len(files)} matching results:")
for i, file in enumerate(files, 1):
print(f"\nFile {i}:")
# Fetch and print various properties
uri_element = file.find('URI')
uri = uri_element.text if uri_element is not None else 'N/A'
print(f" URI: {uri}")
key_element = file.find('Filename')
key = key_element.text if key_element is not None else 'N/A'
print(f" File name: {key}")
size_element = file.find('Size')
size = size_element.text if size_element is not None else 'N/A'
print(f" Size: {size}")
modified_time_element = file.find('FileModifiedTime')
modified_time = modified_time_element.text if modified_time_element is not None else 'N/A'
print(f" Modified time: {modified_time}")
content_type_element = file.find('ContentType')
content_type = content_type_element.text if content_type_element is not None else 'N/A'
print(f" ContentType: {content_type}")
media_type_element = file.find('MediaType')
media_type = media_type_element.text if media_type_element is not None else 'N/A'
print(f" MediaType: {media_type}")
# You can add fetching and printing for more properties as needed, such as ImageHeight, ImageWidth, OSSStorageClass, etc.
# image_height_element = file.find('ImageHeight')
# image_height = image_height_element.text if image_height_element is not None else 'N/A'
# print(f" ImageHeight: {image_height}")
# Generate a signed URL (if needed)
if key != 'N/A': # Generate a URL only if the file name exists
try:
pre_url = client.presign(
oss.GetObjectRequest(
bucket=args.bucket, # Specify the bucket name
key=key, # Specify the object key
)
)
print(f" File address (signed URL): {pre_url.url}")
except Exception as e:
print(f" Failed to generate signed URL: {e}")
print("-" * 20) # Separator
if __name__ == "__main__":
semantic_search()Setelah menjalankan program, masukkan deskripsi seperti a yard with a parked car. Sistem akan mengkueri indeks data dan mengembalikan hasil yang cocok. Gunakan URL yang ditandatangani pada setiap hasil untuk mengakses video secara langsung.
Contoh output:
Found 1 matching result:
File 1:
URI: oss://ipc-videos-oss-metaquery-demo/VideoA.mp4
File name: VideoA.mp4
Size: 2311252
Modified time: 2025-05-23T17:38:10+08:00
ContentType: video/mp4
MediaType: video
File address (signed URL): https://ipc-videos-oss-metaquery-demo.oss-cn-beijing.aliyuncs.com/VideoA.mp4?x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-date=20250523T094511Z&x-oss-expires=900&x-oss-credential=LTAI********************%2F20250523%2Fcn-beijing%2Foss%2Faliyun_v4_request&x-oss-signature=0bf38092c42a179ff0e8334c8bea3fd92f5a78599038e816e2ed3e02755542af
--------------------Atur tag video
Saat bekerja dengan set data video yang besar, path file saja sering kali tidak cukup untuk pemfilteran dan kategorisasi. Penandaan objek OSS memungkinkan Anda menyambungkan metadata pasangan kunci-nilai ke file—misalnya, untuk mencatat kamera mana yang merekam video atau zona geografis mana yang dimilikinya. Tag memungkinkan Anda:
Mengkategorikan video secara dinamis menggunakan pasangan kunci-nilai
Memfilter hasil pencarian dengan cepat ke subset tertentu (misalnya, berdasarkan ID kamera atau lokasi)
Menetapkan aturan pengindeksan yang hanya berlaku untuk file yang ditandai
Tiga video contoh yang digunakan dalam tutorial ini ditandai sebagai berikut:
| File | Content | Tag |
|---|---|---|
| VideoA.mp4 | Rekaman halaman belakang | camera=camera-a |
| VideoB.mp4 | Rekaman area penjual | camera=camera-b |
| VideoC.mp4 | Rekaman halaman belakang (mirip VideoA) | camera=camera-c |
| VideoA.mp4 | VideoB.mp4 | VideoC.mp4 |
|---|---|---|
![]() | ![]() | ![]() |
| Video halaman belakang, ditandai direkam oleh camera-a | Video vending, ditandai sebagai rekaman oleh camera-b | Video halaman belakang, mirip kontennya dengan Video A, ditandai direkam oleh camera-c |
Tag dapat ditetapkan saat unggah atau ditambahkan dan diperbarui setelah unggah.
Pencarian dengan filter tag
Pencarian semantik mengembalikan semua video yang sesuai dengan deskripsi. Untuk mempersempit hasil lebih lanjut—misalnya, hanya mengambil rekaman halaman belakang dari kamera tertentu—terapkan filter tag setelah hasil pencarian awal dikembalikan.
Contoh berikut menggabungkan pencarian semantik dengan pemfilteran tag di sisi klien:
# -*- coding: utf-8 -*-
import argparse
import alibabacloud_oss_v2 as oss
# Parse the XML response
import xml.etree.ElementTree as ET
import json
from datetime import datetime
# --- New import ---
from alibabacloud_oss_v2.models import GetObjectTaggingRequest
# --- End of new import ---
def get_inputs():
"""Get the user's semantic query (required) and filter conditions (optional)"""
# 1. Get the required semantic query
print(" Enter a semantic description (example: a yard with a parked car)")
query = input("> Semantic keywords: ").strip()
while not query:
print(" Semantic keywords cannot be empty!")
query = input("> Semantic keywords: ").strip()
conditions = [] # Initialize the list of filter conditions
target_tag = None # --- Used to store the target tag ---
# 2. Get the optional tag filter
print("\n (Optional) Enter tag filter conditions (used for client-side filtering, example: camera=camera-a, press Enter to skip)")
tag_input = input("> Tag (format key=value): ").strip()
if tag_input:
if '=' in tag_input:
# conditions.append({
# 'field': 'Tags',
# 'value': tag_input,
# 'op': 'eq'
# })
target_tag = tag_input
print(f" Info: The tag '{target_tag}' will be used for client-side filtering after getting the results.")
else:
print(" Incorrect tag format, ignored. Use the 'key=value' format.")
# --- Return target_tag ---
return query, conditions, target_tag
def build_metaquery_xml(query, conditions):
"""Build a MetaQuery XML that complies with OSS specifications (includes semantic query and optional filters)"""
# Always include the semantic query part
xml_parts = [f'<Query>{query}</Query>']
# Add the media type restriction
xml_parts.append('<MediaTypes><MediaType>video</MediaType></MediaTypes>')
# Add optional filter conditions - in semantic mode, only some fields are supported (such as Filename here)
for cond in conditions:
# In semantic mode, skip building SimpleQuery for the Tags field (although we no longer add Tags to conditions, this is kept just in case)
if cond['field'] == 'Tags':
continue
json_query = json.dumps({
"Field": cond['field'],
"Value": cond['value'],
"Operation": cond['op']
}, ensure_ascii=False)
xml_parts.append(f'<SimpleQuery>{json_query}</SimpleQuery>')
# Combine into the complete MetaQuery XML
meta_query_xml = f'''<MetaQuery>
{"".join(xml_parts)}
</MetaQuery>'''
return meta_query_xml
def format_result(key, pre_url):
"""Format a single search result"""
return f""" File address: {pre_url}
File path: {key}
-----------------------"""
def perform_search():
# Assign values directly to command line arguments
args = argparse.Namespace(
region='cn-beijing', # Replace with your region
bucket='ipc-videos-oss-metaquery-demo', # Replace with your bucket name
endpoint='https://oss-cn-beijing.aliyuncs.com', # Replace with your endpoint
)
# Initialize the OSS client
credentials = oss.credentials.EnvironmentVariableCredentialsProvider()
cfg = oss.config.load_default()
cfg.credentials_provider = credentials
cfg.region = args.region
if args.endpoint:
cfg.endpoint = args.endpoint
client = oss.Client(cfg)
# Get user input (semantic query + optional conditions + target tag)
query, conditions, target_tag = get_inputs()
# Build the request
try:
# Construct the request body (XML data)
# --- Pass only conditions (without Tags) ---
data_str = build_metaquery_xml(query, conditions)
# Define the operation input
req = oss.OperationInput(
op_name='DoMetaQuery',
method='POST',
parameters={
'metaQuery': '',
'mode': 'semantic', # <-- Re-add semantic mode
'comp': 'query',
},
headers=None,
body=data_str.encode("utf-8"),
bucket=args.bucket,
)
# Call the generic interface to execute the operation
print("\n Sending DoMetaQuery request...")
resp = client.invoke_operation(req)
print(f"\n Request successful, HTTP status code: {resp.http_response.status_code}")
except oss.exceptions.ServiceError as e:
print(f" Server error (ServiceError): {e.message}")
print(f" - HTTP Status Code: {e.status_code}")
print(f" - Error Code: {e.error_code}")
print(f" - Request ID: {e.request_id}")
return
except oss.exceptions.ClientError as e: # Add catch for client errors
print(f" Client or network error (ClientError): {e.message}")
return
except Exception as e: # Catch other possible exceptions
print(f" Unknown error: {e}")
import traceback
traceback.print_exc() # Print the full traceback
return
# Parse and process the results...
final_results_count = 0 # --- New: counter to tally the final matching results ---
try:
root = ET.fromstring(resp.http_response.content.decode('utf-8'))
# Find all File elements
files = root.findall('.//File')
print(f"\n Got {len(files)} initial matching results from OSS, starting client-side tag filtering...")
if not files:
# Check for NextContinuationToken, paging may be needed
next_token_elem = root.find('.//NextContinuationToken')
if next_token_elem is not None and next_token_elem.text:
print(" Note: There may be more results. The current implementation does not handle paging.")
print("\n No initial matching results.")
return # Return early if there are no files
for i, file in enumerate(files, 1):
# Get the file name
key_element = file.find('Filename')
if key_element is None:
print(f" Warning: The {i}-th initial result is missing the Filename field, skipped.")
continue
key = key_element.text
# --- Client-side tag filtering ---
if target_tag:
try:
tagging_req = GetObjectTaggingRequest(bucket=args.bucket, key=key)
tagging_resp = client.get_object_tagging(tagging_req)
# Check if the returned tag set contains the target tag
tag_found = False
target_k, target_v = target_tag.split('=', 1)
if tagging_resp.tag_set and tagging_resp.tag_set.tags: # Use .tags
for tag in tagging_resp.tag_set.tags: # Use .tags
if tag.key == target_k and tag.value == target_v:
tag_found = True
break
if not tag_found:
continue # Tag does not match, skip this file
except oss.exceptions.ServiceError as tag_err:
if tag_err.status_code == 404 and tag_err.error_code == 'NoSuchTagSet':
continue
else:
print(f" Warning: Error getting tags for file '{key}': {tag_err.error_code} - {tag_err.message}, skipped.")
continue
except Exception as tag_e:
print(f" Warning: Unknown error while getting or processing tags for file '{key}': {tag_e}, skipped.")
# --- Add traceback for debugging ---
import traceback
traceback.print_exc()
# --- End of addition ---
continue
# --- End of client-side tag filtering ---
# --- If it passed the tag filter (or no tag filter was set), process and print the result ---
final_results_count += 1 # Increment the final result counter
print(f"\n[{final_results_count}] File '{key}' matches all conditions:") # Print the final result number
# Generate a signed URL
try:
pre_url = client.presign(
oss.GetObjectRequest(
bucket=args.bucket,
key=key,
)
)
print(format_result(key, pre_url.url))
except Exception as presign_e:
print(f" Warning: Error generating signed URL for file '{key}': {presign_e}")
print(format_result(key, "[Could not generate URL]"))
# --- Print final statistics after the loop ---
print(f"\n Client-side filtering complete, found {final_results_count} final matching results.")
except ET.ParseError as xml_e:
print(f" Error: Error parsing OSS response XML - {xml_e}")
except Exception as parse_e:
print(f" Error: Unexpected error while processing results - {parse_e}")
if __name__ == "__main__":
perform_search()Untuk memfilter video halaman belakang dari kamera tertentu, jalankan program dan masukkan hal berikut:
Deskripsi semantik:
a yard with a parked carFilter tag:
camera=camera-a
Dalam contoh ini, baik VideoA maupun VideoC berisi adegan yang sesuai dengan deskripsi tersebut. Filter tag hanya menyimpan VideoA (ditandai camera-a), sehingga hasil akhir hanya berisi satu file.
Contoh output:
Sending DoMetaQuery request...
Request successful, HTTP status code: 200
Got 2 initial matching results from OSS, starting client-side tag filtering...
[1] File 'VideoA.mp4' matches all conditions:
File address: https://ipc-videos-oss-metaquery-demo.oss-cn-beijing.aliyuncs.com/VideoA.mp4?x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-date=20250526T054908Z&x-oss-expires=900&x-oss-credential=LTAI********************%2Fcn-beijing%2Foss%2Faliyun_v4_request&x-oss-signature=01bbf29790763d8e0f177d4cb0469cb00ae1c69d565219edb3866f75110b37ab
File path: VideoA.mp4
-----------------------
Client-side filtering complete, found 1 final matching result.



