By following the steps described in this document, you can experience the complete end-to-end process from ingesting image data into the lake, processing image datasets, to text-based image search and image-based image search. Through this process, you will acquire the following skills:
This practice uses the following 1,000 animal images to test the full process of image data ingestion, processing, and value-added processing. You can click to download this sample data or prepare your own test images.
warning
You need to enable Volcano Engine Object Storage TOS and create a TOS bucket to store the test image data during this experiment.
Preparations | Key points | Reference documentation | Configuration example |
|---|---|---|---|
Create Bucket |
| Bucket name: las-doctest |
You need to log in to the Access Control (IAM) console and prepare an AK and SK for subsequent API access to TOS and LAS.
Preparation | Key points | Reference documentation | Configuration example |
|---|---|---|---|
Obtain AK and SK | To ensure the security of access to TOS/LAS services, you must provide valid access keys (AK/SK) for subsequent authentication.
|
You need to enable the LAS service and create the queue resources and development machine environment required for this experiment.
Preparation | Key points | Reference documentation | Configuration example |
|---|---|---|---|
Queue resources |
| See key points | |
LAS API Key | Before calling LAS online operators, you need to generate an API Key for authentication when invoking operators.
warning LAS provides you with two types of data processing operators: offline and online. Only online operators require authentication using the LAS API Key.
| See key points | |
Development machine | Before calling LAS offline operators, you need to prepare the development environment for invoking offline operators.
|
warning
The main purpose of this step is to upload images to a TOS Bucket and create an image dataset in LAS. Afterwards, you can directly preview and process images in the LAS image dataset.
After logging in to the TOS product console, upload the images to be processed to the TOS bucket.
Procedure | Key points | Reference documentation | Configuration example |
|---|---|---|---|
Upload images | Upload the images to be processed to the prepared TOS Bucket. You can create folders as needed and upload images to the folders. | The TOS path is: |
Log in to the LAS console, click Datasets > Common dataset in the left navigation bar, then click "Create dataset" on the dataset page. Refer to the following configuration points to set the dataset parameters. After completing the configuration, click "Create" to finish creating the image dataset.
Procedure | Key points | Reference documentation | Configuration example |
|---|---|---|---|
Create an image dataset |
|
|
After the dataset is created, you can view the basic information of the dataset on the dataset details page. For image datasets, previewing the first ten images is supported.
warning
The main purpose of this practice step is to convert the image dataset into a Lance dataset, enabling image data ingestion into the data lake, which facilitates subsequent operations such as image content understanding, vectorization, and other data processing tasks.
You need to log in to the TOS console and prepare a TOS path for storing the converted Lance dataset data. The TOS path for the Lance dataset in this example is: tos://las-doctest/image2lance/.
This step is mainly performed on the development machine using VeDaft to call the SDK for operations such as reading and writing the dataset. Since accessing the TOS Bucket is involved, it is recommended to configure some authentication parameters and fixed path parameters as environment variables in the development environment, making it easier to reference them in subsequent code execution.
You can refer to the following content to prepare the environment variable configuration.
# Region settings, in this example, the North China 2 - Beijing region is used. export REGION="cn-beijing" # Environment variables related to AK authentication # Authentication sk and ak for accessing TOS and LAS export LAS_TOS_SECRET_KEY="<your_sk>" export LAS_TOS_ACCESS_KEY="<your_ak>" # Environment variables related to TOS access # TOS access endpoint export LAS_TOS_ENDPOINT="https://tos-cn-beijing.ivolces.com" # TOS endpoint (for TOSConfig) export TOS_ENDPOINT="https://tos-cn-beijing.ivolces.com"
On the development machine, run the source env.sh command to activate the environment variables.
# Please run the following code on the LAS development machine import os """ Convert to Lance Dataset """ import daft from daft.io import CreateLasDatasetOptions, IOConfig, LanceWriteOptions from daft.las.io import TOSConfig # You need to set the image dataset name and configure the TOS path for the new Lance dataset dataset_name = "las_dataset_image" lance_tos_dir = f"tos://las-doctest/image2lance/las_dataset_image_lance.lance" LANCE_DATASET_FORMAT = "lance" # Create the relevant configuration io_config = IOConfig(s3=TOSConfig.from_env().to_s3_config()) write_options = LanceWriteOptions(io_config=io_config, uri=lance_tos_dir) create_ds_options = CreateLasDatasetOptions( nick_name="daft_test_lance_write", privacy="public", description="This is my dataset", ) # Read the original data df = daft.read_las_dataset(name=dataset_name) Create here: The naming rule for the new Lance dataset is to use the image dataset name with the _lance suffix appended new_dataset_name = dataset_name + "_lance" df.write_las_dataset( name=new_dataset_name, format=LANCE_DATASET_FORMAT, write_options=write_options, create_ds_options=create_ds_options, ) # Read the converted new dataset df = daft.read_las_dataset(name=new_dataset_name) print("\n\nnew dataset:") df.show()
You need to modify the following parameters.
las_dataset_imageOther parameters can remain as the sample values.
new dataset: ERROR:daft_local_execution.runtime_stats:RuntimeStatsManager finished with active nodes {0} ╭────────────────────────────────┬────────┬──────────╮ │ image ┆ size ┆ num_rows │ │ --- ┆ --- ┆ --- │ │ String ┆ Int64 ┆ Int64 │ ╞════════════════════════════════╪════════╪══════════╡ │ s3://las-doctest/images/anima… ┆ 15390 ┆ None │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 240478 ┆ None │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 15008 ┆ None │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 11645 ┆ None │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 9909 ┆ None │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 14252 ┆ None │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 3226 ┆ None │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 878304 ┆ None │ ╰────────────────────────────────┴────────┴──────────╯ (Showing first 8 rows)
The converted Lance dataset contains the fields "image", "size", and "num_rows". Specifically:
If an ERROR prompt appears in the output but the dataset data is returned normally, the ERROR prompt can be ignored.
After completing the conversion to a Lance dataset, you can log in to the LAS console and view the newly created Lance dataset on the dataset list page.
warning
The main purpose of this practice step is to perform simple processing on the images, writing the binary data of the images into the Lance dataset after ingestion, so that the image content can be previewed directly in the Lance dataset and to prepare for subsequent operations such as image content understanding and vectorization.
Region settings This example uses the North China 2 (Beijing) region. export REGION="cn-beijing" # Authentication AK environment variables Access authentication keys (SK and AK) for TOS and LAS export LAS_TOS_SECRET_KEY="<your_sk>" export LAS_TOS_ACCESS_KEY="<your_ak>" # TOS access-related environment variables # Access endpoints of TOS export LAS_TOS_ENDPOINT="https://tos-cn-beijing.ivolces.com" # TOS endpoints (for TOSConfig) export TOS_ENDPOINT="https://tos-cn-beijing.ivolces.com" # Fixed parameters of the dataset can also be set as environment variables export DATASET_NAME="las_dataset_image_lance"
This step adds:
source env.sh command to activate the environment variables.Run the following script on the development machine to convert the images to binary format and write them into the Lance dataset.
import os # Import the values of the following parameters from environment variables LAS_TOS_ACCESS_KEY = os.environ["LAS_TOS_ACCESS_KEY"] LAS_TOS_SECRET_KEY = os.environ["LAS_TOS_SECRET_KEY"] TOS_ENDPOINT = os.environ["TOS_ENDPOINT"] REGION = os.environ["REGION"] dataset_name = os.environ["DATASET_NAME"] import pyarrow as pa import tos from daft.io.lance import merge_columns from daft.io import IOConfig from daft.las.io import TOSConfig # Read image data from tos path def read_tos_path(path: str, tos_client: tos.TosClientV2): if (not path) or path.strip() == "": return None if path.startswith("tos://"): path = path[len("tos://") :] if path.startswith("s3://"): path = path[len("s3://") :] bucket, path = path.split("/", 1) obj = tos_client.get_object(bucket=bucket, key=path) return obj.read() def get_create_assign_raw_image_func(input_col: str, output_col: str): def create_assign_raw_image(batch: pa.RecordBatch) -> pa.RecordBatch: paths = batch.column(input_col).to_pylist() tos_client = tos.TosClientV2(LAS_TOS_ACCESS_KEY, LAS_TOS_SECRET_KEY, TOS_ENDPOINT, REGION) image_data_list = [] for path in paths: data = read_tos_path(path, tos_client) image_data_list.append(data) image_data_array = pa.array(image_data_list, type=pa.binary()) new_batch = pa.RecordBatch.from_arrays([image_data_array], names=[output_col]) Only return new columns return new_batch return create_assign_raw_image print("正在将图片原始数据存入 lance 数据集中 ...") Modify lance_tos_dir and the transform parameter in merge_columns in the following code io_config = IOConfig(s3=TOSConfig.from_env().to_s3_config()) lance_tos_dir = f"tos://las-doctest/image2lance/{dataset_name}.lance" merge_columns( uri=lance_tos_dir.replace("tos://", "s3://"), io_config=io_config, transform=get_create_assign_raw_image_func("image", "raw_image"), ) print("图片原始数据已存入 lance 数据集")
You need to modify the following parameters in the sample code according to your actual situation:
To delete unnecessary fields or data from a dataset caused by accidental operations, refer to the following content: Reference: Common dataset operation examples.
Read the dataset:
""" read data """ import os import daft dataset_name = os.environ["DATASET_NAME"] df = daft.read_las_dataset(name=dataset_name) df.show()
Code output:
ERROR:daft_local_execution.runtime_stats:RuntimeStatsManager finished with active nodes {0} ╭────────────────────────────────┬────────┬──────────┬────────────────────────────────╮ │ image ┆ size ┆ num_rows ┆ raw_image │ │ --- ┆ --- ┆ --- ┆ --- │ │ String ┆ Int64 ┆ Int64 ┆ Binary │ ╞════════════════════════════════╪════════╪══════════╪════════════════════════════════╡ │ s3://las-doctest/images/anima… ┆ 15390 ┆ None ┆ b"ÿØÿà�JFI… │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 240478 ┆ None ┆ b"ÿØÿà�JFI… │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 15008 ┆ None ┆ b"ÿØÿà�JFI… │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 11645 ┆ None ┆ b"ÿØÿà�JFI… │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 9909 ┆ None ┆ b"ÿØÿà�JFI… │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 14252 ┆ None ┆ b"ÿØÿà�JFI… │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 3226 ┆ None ┆ b"ÿØÿà�JFI… │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/images/anima… ┆ 878304 ┆ None ┆ b"ÿØÿà�JFI… │ ╰────────────────────────────────┴────────┴──────────┴────────────────────────────────╯ (Showing first 8 rows)
warning
The main purpose of this practice step is to invoke LAS data processing operators to enhance image data:
# Region settings This example uses the North China 2 (Beijing) region. export REGION="cn-beijing" # AK authentication-related environment variables # Access the authentication SK and AK of TOS and LAS export LAS_TOS_SECRET_KEY="<your_sk>" export LAS_TOS_ACCESS_KEY="<your_ak>" # LAS API Key required for invoking LAS operators using the online method (API) export LAS_API_KEY="464812df-2dfb-41f6-********" # TOS access-related environment variables TOS's access endpoints export LAS_TOS_ENDPOINT="https://tos-cn-beijing.ivolces.com" TOS endpoint (for TOSConfig) export TOS_ENDPOINT="https://tos-cn-beijing.ivolces.com" # Fixed parameters of the dataset can also be set as environment variables export DATASET_NAME="las_dataset_image_lance" export TOS_LANCE_DIR="tos://las-doctest/image2lance/las_dataset_image_lance.lance" Test configuration # Sample image URL for testing export TEST_IMAGE_URL="https://ark-project.tos-cn-beijing.volces.com/images/view.jpeg"
This step adds:
source env.sh command to activate the environment variables.You can use a test image URL to quickly experience the result of LAS's multimodal deep thinking operator for image content understanding. The following demonstrates invoking the 多模态深度思考(Doubao-seed-1.8) operator for a quick experience. Execute the following curl command on the development machine to quickly view an example result of LAS operator's image understanding processing.
curl --location "https://operator.las.cn-beijing.volces.com/api/v1/chat/completions" --header "Content-Type: application/json" --header "Authorization: Bearer $LAS_API_KEY" --data '{ "messages": [ { "role": "user", "content": [ { "type": "image_url", "image_url": { "url": "https://ark-project.tos-cn-beijing.volces.com/images/view.jpeg" } }, { "type": "text", "text": "图片里面有什么" } ] } ], "model": "doubao-seed-1-8-251228" }'
Return example:
{"model":"doubao-seed-1-8-251228","usage":{"prompt_tokens":270,"completion_tokens":521,"total_tokens":791,"prompt_tokens_details":{"cached_tokens":0,"provisioned_tokens":null},"completion_tokens_details":{"reasoning_tokens":354,"provisioned_tokens":null}},"created":1773200390,"id":"0217732003734693dfd182f******","choices":[{"finish_reason":"stop","index":0,"logprobs":null,"message":{"content":"这是一幅充满自然静谧感的户外场景: - **前景**:开阔的深蓝色湖面水波轻漾,一艘亮橙色的单人皮划艇浮在水面,艇上坐着一人,正抬起黑色船桨,似乎正在划行,水面清晰倒映出皮划艇与人的身影。 - **中景**:湖对岸是一片茂密的深绿色针叶林,林间萦绕着淡淡的薄雾,给树林添了几分朦胧感。 - **背景**:树林后方是连绵巍峨的雪山,山体被冰雪覆盖,山峦硬朗的轮廓在光影下清晰可见,尽显雄伟壮阔。 - **天空**:上方是澄澈的淡蓝色天空,点缀着几缕轻薄的云丝,整体氛围开阔宁静,传递出户外自然的闲适与苍茫感。","reasoning_content":"用户现在需要描述这张图片里的内容,首先看整体:首先是前景,一片平静的深蓝色湖泊,水面上有一个橙色的皮划艇,艇上有一个人,正举着黑色的船桨,似乎在划行,水面有他和皮划艇的倒影。然后中间部分,湖泊对岸是茂密的深绿色针叶林,树林上方有淡淡的雾气,显得很静谧。背景是巍峨的雪山,山体覆盖着积雪,轮廓硬朗,在蓝天之下显得很壮阔。天空是浅蓝色,飘着几缕轻薄的白云,整体氛围宁静又开阔,有种户外自然的闲适感。现在把这些元素整理清楚,分层次描述。 首先看主体: 1. 水面与皮划艇:前景是开阔平静的深蓝色湖面,水面有轻微波纹,一艘亮橙色的单人皮划艇在湖面,艇上坐着一个人,正抬起黑色船桨,似乎在进行划行动作,水面倒映出皮划艇和人的身影。 2. 中景:湖的对岸是一片茂密的深色针叶林(看起来像冷杉、松树这类),林间有淡淡的薄雾,给树林蒙上了一层朦胧感。 3. 背景:树林后方是连绵巍峨的雪山,山体被冰雪覆盖,山峦的轮廓清晰,在光影下显现出岩石与积雪的质感,显得很雄伟。 4. 天空:上方是澄澈的淡蓝色天空,点缀着几缕轻薄的白色云丝,整体天气晴朗,让整个画面的氛围宁静又开阔,充满自然的静谧与壮阔感。 现在把这些组织成通顺的描述,让内容清晰有条理。","role":"assistant"}}],"object":"chat.completion"}
The following uses Image-text embedding (Doubao series models) as an example to demonstrate how to vectorize image and text content in batches. During processing, a new column will be added to the Lance dataset: image_embedding, which is used to store the vectorization results. After image vectorization, subsequent image search scenarios are supported.
Run the following script on the development machine.
#This code segment requires you to execute "source env.sh" first. The env.sh file is located in the "Configure environment variables" section. # Import necessary libraries import os import daft import lance import pandas as pd import pyarrow as pa from daft import col from daft.io import IOConfig from daft.io.object_store_options import io_config_to_storage_options from daft.las.functions.ark_llm.doubao_embedding_vision import DoubaoEmbeddingVision from daft.las.functions.udf import las_udf from daft.las.io import TOSConfig from daft.las.infra.las_dataset import LasDatasetClient, LasDatasetConfig # Get configuration from environment variables TOS_LANCE_DIR = os.getenv("TOS_LANCE_DIR") # Lance dataset path DATASET_NAME = os.getenv("DATASET_NAME") # Dataset namename # Batch processing parameters BATCH_SIZE = int(os.getenv("BATCH_SIZE", "10")) # Maximum number of rows per operation MAX_CYCLES = int(os.getenv("MAX_CYCLES", "5")) # Maximum batch count # Column name configuration IMAGE_EMBEDDING_NEW_COLUMN = "image_embedding" # New embedding column name DOUBAO_LLM_COLUMN = "doubao_llm_result" # Doubao LLM result column name # Set environment variables related to TOS object storage os.environ["LAS_TOS_ACCESS_KEY"] = os.getenv("LAS_TOS_ACCESS_KEY") os.environ["LAS_TOS_SECRET_KEY"] = os.getenv("LAS_TOS_SECRET_KEY") os.environ["TOS_ENDPOINT"] = os.getenv("LAS_TOS_ENDPOINT") Configure IO settings for accessing TOS object storage io_config = IOConfig(s3=TOSConfig.from_env().to_s3_config()) def update_image_embedding(): # Convert TOS path to S3-compatible path lance_path = TOS_LANCE_DIR.replace("tos://", "s3://") Check whether the embedding column exists; if not, create it storage_options = io_config_to_storage_options(io_config, lance_path) lance_ds = lance.dataset(uri=lance_path, storage_options=storage_options) existing_columns = [field.name for field in lance_ds.schema] column_exists = IMAGE_EMBEDDING_NEW_COLUMN in existing_columns print(f"{IMAGE_EMBEDDING_NEW_COLUMN} 列存在: {column_exists}") if not column_exists: print(f"创建 {IMAGE_EMBEDDING_NEW_COLUMN} 列...") vec_type = pa.list_(pa.float64()) @lance.batch_udf() def add_empty_column(batch): null_array = pa.array([None] * batch.num_rows, type=vec_type) return pa.RecordBatch.from_arrays([null_array], names=[IMAGE_EMBEDDING_NEW_COLUMN]) lance_ds.add_columns(add_empty_column) print(f"成功创建 {IMAGE_EMBEDDING_NEW_COLUMN} 列") # Read the complete dataset and filter for rows to be processed df = daft.read_lance(lance_path, io_config=io_config) all_data = df.collect().to_pandas() if IMAGE_EMBEDDING_NEW_COLUMN in all_data.columns: pending_data = all_data[all_data[IMAGE_EMBEDDING_NEW_COLUMN].isna()].copy() pending_data = pending_data.reset_index(drop=True) print(f"跳过已有 embedding 的 {len(all_data) - len(pending_data)} 行") else: pending_data = all_data.copy() print(f"待处理图片总数: {len(pending_data)}") process_limit = MAX_CYCLES * BATCH_SIZE pending_data = pending_data.iloc[:process_limit].copy() print(f"本次实际处理: {len(pending_data)} 张图片") if pending_data.empty: print("所有图片已处理完成,无需继续") return pending_data[DOUBAO_LLM_COLUMN] = ( pending_data[DOUBAO_LLM_COLUMN] .fillna("图片内容") .replace("", "图片内容") ) # 2. Build Daft DataFrame and invoke operators to batch compute embedding pending_daft_df = daft.from_pandas(pending_data) pending_daft_df = pending_daft_df.with_column( IMAGE_EMBEDDING_NEW_COLUMN, las_udf( DoubaoEmbeddingVision, construct_args={ "image_format": "jpeg", # Adjust image format as needed "source_type": "url", The # image column stores URL }, )(col("image"), col(DOUBAO_LLM_COLUMN)), # image column + text column → multimodal joint vector ) result_df = pending_daft_df.collect().to_pandas() # ------------------------------------------------------------------ # Filter out rows where the operator call failed (returned None) valid_mask = result_df[IMAGE_EMBEDDING_NEW_COLUMN].notna() valid_df = result_df[valid_mask].copy() print(f"成功生成 embedding: {len(valid_df)} 行,失败: {(~valid_mask).sum()} 行") if valid_df.empty: print("没有成功生成的 embedding,跳过更新") return # Update the Lance dataset (consistent with the original logic) try: lance_ds = lance.dataset(uri=lance_path, storage_options=storage_options) update_table = pa.Table.from_pandas(valid_df, preserve_index=False) update_table = update_table.cast(lance_ds.schema) lance_ds.merge_insert("image").when_matched_update_all().execute(update_table) print("数据集更新成功") except Exception as e: print(f"更新数据集失败: {e}") print("尝试备用方案(内存更新,不写回)...") try: current_df = daft.read_lance(lance_path, io_config=io_config).collect().to_pandas() for _, row in valid_df.iterrows(): mask = current_df["image"] == row["image"] idx = current_df[mask].index if len(idx) > 0: current_df.at[idx[0], IMAGE_EMBEDDING_NEW_COLUMN] = row[IMAGE_EMBEDDING_NEW_COLUMN] print("备用方案:内存已更新,但未写回数据集") except Exception as e2: print(f"备用方案也失败: {e2}") print(f"\n处理完成!共处理了 {len(valid_df)} 张图片") # Program entry point if __name__ == "__main__": update_image_embedding()
You need to modify the following parameters in the sample code as needed:
After processing the images, you can view the Lance dataset to check the processing results.
Read the dataset:
""" 读取数据 """ import os import daft dataset_name = os.environ["DATASET_NAME"] df = daft.read_las_dataset(name=dataset_name) df.show()
Return example:
ERROR:daft_local_execution.runtime_stats:RuntimeStatsManager finished with active nodes {0} ╭────────────────────┬────────┬────────────┬───────────────────┬───────────────────╮ │ image ┆ size ┆ … ┆ llm_result03 ┆ image_embedding │ │ --- ┆ --- ┆ ┆ --- ┆ --- │ │ String ┆ Int64 ┆ (3 hidden) ┆ String ┆ List[Float64] │ ╞════════════════════╪════════╪════════════╪═══════════════════╪═══════════════════╡ │ s3://las-doctest/i ┆ 15390 ┆ … ┆ {"description": ┆ [0.06982421875, │ │ mages/anima… ┆ ┆ ┆ "一群袋鼠站在绿色 ┆ 0.00019168853… │ │ ┆ ┆ ┆ 的草地上… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/i ┆ 240478 ┆ … ┆ {"description":" ┆ [0.02880859375, │ │ mages/anima… ┆ ┆ ┆ 一只棕黄色带斑纹 ┆ -0.0268554687… │ │ ┆ ┆ ┆ 的蜥蜴,尾… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/i ┆ 15008 ┆ … ┆ {"content": "一只 ┆ [0.040771484375, │ │ mages/anima… ┆ ┆ ┆ 黑色羽毛、颈部有 ┆ -0.040039062… │ │ ┆ ┆ ┆ 橙红色裸露皮… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/i ┆ 11645 ┆ … ┆ {"description": ┆ [-0.0003032684326 │ │ mages/anima… ┆ ┆ ┆ "一只蝙蝠展开翅膀 ┆ 171875, -0.0… │ │ ┆ ┆ ┆ 停在红苹… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/i ┆ 9909 ┆ … ┆ {"description": ┆ [0.00689697265625 │ │ mages/anima… ┆ ┆ ┆ "一只小考拉被灰色 ┆ , -0.0222167… │ │ ┆ ┆ ┆ 布料包裹… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/i ┆ 14252 ┆ … ┆ {"description": ┆ [-0.015380859375, │ │ mages/anima… ┆ ┆ ┆ "一个穿粉色上衣的 ┆ -0.02966308… │ │ ┆ ┆ ┆ 小女孩,… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/i ┆ 3226 ┆ … ┆ {"content": "一只 ┆ [0.0279541015625, │ │ mages/anima… ┆ ┆ ┆ 白色的鹅,喙和脚 ┆ -0.03710937… │ │ ┆ ┆ ┆ 呈橙色,站立… ┆ │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ s3://las-doctest/i ┆ 878304 ┆ … ┆ {"description": ┆ [0.036865234375, │ │ mages/anima… ┆ ┆ ┆ "图片展示了一只毛 ┆ 0.0063171386… │ │ ┆ ┆ ┆ 发乌黑的… ┆ │ ╰────────────────────┴────────┴────────────┴───────────────────┴───────────────────╯ (Showing first 8 rows)
At this point, we have completed the creation of the Lance-format image dataset, image labeling, image vectorization, and other related operations. Based on this information, it is possible to implement text-to-image search, image-to-image search, model training, and more.
To meet additional business requirements or to include more image label data, refer to the previous steps to add more information, such as image recognition information columns from GPT, Claude, and so on.
The following examples demonstrate image data consumption via text-to-image and image-to-image search. Since there is a need to display image search results, Jupyter notebook is used to run the sample code. You must install Jupyter notebook in advance.
import daft import os from PIL import Image import io import matplotlib.pyplot as plt # ✅ Use UDF to implement string containment check @daft.udf(return_dtype=daft.DataType.bool()) def contains_keyword(col, keyword): return [keyword in (s or "") for s in col.to_pylist()] def query_pic_with_word(dataset_name, query_word): df = daft.read_las_dataset(name=dataset_name) keyword = "袋鼠" # ✅ Filter using UDF result = df.where(contains_keyword(df["doubao_llm_result"], keyword)) result_data = result.collect() for i, row in enumerate(result_data.to_pylist()): print(f"URL: {row['image']}") if 'raw_image' in row and row['raw_image'] is not None: image_data = row['raw_image'] raw_image = Image.open(io.BytesIO(image_data)) plt.figure(figsize=(8, 6)) plt.imshow(raw_image) plt.axis('off') plt.show() if 'doubao_llm_result' in row and row['doubao_llm_result']: print(f"描述: {row['doubao_llm_result']}") print("-" * 50) def main(): dataset_name = "las_dataset_image_lance" query_word = "帮我查询袋鼠图片" query_pic_with_word(dataset_name, query_word) if __name__ == "__main__": main()
The sample code for image search in this document is used to search for images related to "kangaroo" in the Lance dataset.
In the main function:
import numpy as np from daft.las.functions.ark_llm.doubao_embedding_vision import DoubaoEmbeddingVision from daft.las.functions.udf import las_udf from daft import col import daft import os from PIL import Image import io import matplotlib.pyplot as plt # ✅ Use UDF to implement string containment check @daft.udf(return_dtype=daft.DataType.bool()) def contains_keyword(col, keyword): return [keyword in (s or "") for s in col.to_pylist()] def query_pic_with_word(dataset_name, query_word): df = daft.read_las_dataset(name=dataset_name) keyword = "袋鼠" # ✅ Filter using UDF result = df.where(contains_keyword(df["llm_result03"], keyword)) result_data = result.collect() for i, row in enumerate(result_data.to_pylist()): print(f"URL: {row['image']}") if 'raw_image' in row and row['raw_image'] is not None: image_data = row['raw_image'] raw_image = Image.open(io.BytesIO(image_data)) plt.figure(figsize=(8, 6)) plt.imshow(raw_image) plt.axis('off') plt.show() if 'llm_result03' in row and row['llm_result03']: print(f"描述: {row['llm_result03']}") print("-" * 50) def main(): dataset_name = "las_dataset_image_lance" query_word = "帮我查询袋鼠图片" query_pic_with_word(dataset_name, query_word) def cosine_similarity(a, b): """计算余弦相似度""" return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) def generate_image_embedding(image_url): """使用算子生成图片embedding(直接传URL,无需下载转base64)""" df = daft.from_pydict({"image_path": [image_url]}) df = df.with_column( "embedding", las_udf( DoubaoEmbeddingVision, construct_args={ "image_format": "jpeg", "source_type": "url", }, )(col("image_path")), ) result = df.collect().to_pydict() return result["embedding"][0] def query_pic_with_pic(dataset_name, query_pic_url): """ 以图片向量的方式查询相似图片并显示 参数: dataset_name: LAS数据集名称 query_pic_url: 查询的图片URL """ # Download the query image (for display only) from urllib.parse import urlparse import tos parsed = urlparse(query_pic_url) bucket = parsed.netloc key = parsed.path.lstrip('/') client = tos.TosClientV2(os.environ["LAS_TOS_ACCESS_KEY"], os.environ["LAS_TOS_SECRET_KEY"], "tos-cn-beijing.volces.com", "cn-beijing") resp = client.get_object(bucket=bucket, key=key) query_image_data = resp.read() query_image = Image.open(io.BytesIO(query_image_data)) print("正在生成查询图片的embedding...") query_embedding = generate_image_embedding(query_pic_url) if query_embedding is None: print("无法生成查询图片的embedding") return print(f"成功生成embedding,维度: {len(query_embedding)}") # Read the LAS dataset df = daft.read_las_dataset(name=dataset_name) # Retrieve all data all_data = df.collect() all_rows = all_data.to_pylist() # Display the query image print("查询图片:") plt.figure(figsize=(8, 6)) plt.imshow(query_image) plt.title("查询图片") plt.axis('off') plt.show() print("-" * 50) # Calculate the similarity between all images and the query image similarities = [] for i, row in enumerate(all_rows): if row['image_embedding'] is not None: similarity = cosine_similarity(query_embedding, row['image_embedding']) similarities.append((similarity, i, row)) # Sort by similarity, select the top 3 with similarity greater than 40% similarities.sort(key=lambda x: x[0], reverse=True) top_3 = [s for s in similarities[:3] if s[0] > 0.40] if not top_3: print("未找到相似度大于40%的图片") return # Display the results for similarity, idx, row in top_3: print(f"URL: {row['image']}") print(f"相似度: {similarity:.4f}") if 'raw_image' in row and row['raw_image'] is not None: image_data = row['raw_image'] raw_image = Image.open(io.BytesIO(image_data)) plt.figure(figsize=(8, 6)) plt.imshow(raw_image) plt.axis('off') plt.show() if 'llm_result03' in row and row['llm_result03']: print(f"描述: {row['llm_result03']}") print("-" * 50) if __name__ == "__main__": dataset_name = "las_dataset_image_lance" query_pic_url = "tos://las-doctest/images/animals/pic_search_1000_images/0009fc27d9.jpg" query_pic_with_pic(dataset_name, query_pic_url)
The sample code for image search in this document is used to search for images in the Lance dataset based on a given input image, calculate the cosine similarity, and return the top 3 images with the highest similarity.
In the main function: