π RefSpatial-Expand-Bench is officially released!
20
151 commits
1 linked in READMEs
updated Jul 27, 2026
RefSpatial-Bench: A Benchmark for Multi-step Spatial Referring
Welcome to RefSpatial-Bench, a challenging benchmark based on real-world cluttered scenes to evaluate more complex multi-step spatial referring with reasoning.
Location Task: This task contains 100 samples, which requires model to predicts a 2D point indicating the unique target object.
Placement Task: This task contains 100 samples, which requires model to predicts a 2D point within the desired free space.
Unseen Set: This set comprises 77 samples from the Location/Placement task, specifically designed to evaluate model generalization after SFT/RFT training on RefSpatial, as it includes novel spatial relation combinations not present in RefSpatial.
step) for each benchmark sample as the number of anchor objects and their spatial relations that help constrain the search space.step value reflects greater reasoning complexity and a stronger need for spatial understanding and reasoning.We provide two formats:
data/ folder contains HF-compatible splits:
locationplacementunseenEach sample includes:
| Field | Description |
|---|---|
id | Unique integer ID |
object | Natural language description of target (object or free area), which is extracted from the prompt |
prompt | Full Referring expressions |
suffix | Instruction for answer formatting (different models may use different suffixes or none; we provide the format used by RoboRefer) |
image | RGB image (datasets.Image) |
mask | Binary mask image (datasets.Image) |
step | Reasoning complexity (number of anchor objects / spatial relations) |
For full reproducibility and visualization, we also include the original files under:
Location/Placement/Unseen/Each folder contains:
Location/
βββ image/ # RGB images (e.g., 0.png, 1.png, ...)
βββ mask/ # Ground truth binary masks
βββ question.json # List of referring prompts and metadata
Each entry in question.json has the following format:
{
"id": 40,
"object": "the second object from the left to the right on the nearest platform",
"prompt": "Please point out the second object from the left to the right on the nearest platform.",
"suffix": "Your answer should be formatted as a list of tuples, i.e. [(x1, y1)], ...",
"rgb_path": "image/40.png",
"mask_path": "mask/40.png",
"category": "location",
"step": 2
}
The official evaluation code is available at https://github.com/Zhoues/RoboRefer. The following provides a quick guide on how to load and use the RefSpatial-Bench.
You can load the dataset easily using the datasets library:
from datasets import load_dataset
# Load the entire dataset (all splits: location, placement, unseen)
# This returns a DatasetDict
dataset_dict = load_dataset("BAAI/RefSpatial-Bench")
# Access a specific split, for example 'location'
location_split_hf = dataset_dict["location"]
# Or load only a specific split directly (returns a Dataset object)
# location_split_direct = load_dataset("BAAI/RefSpatial-Bench", name="location")
# Access a sample from the location split
sample = location_split_hf[0]
# sample is a dictionary where 'rgb' and 'mask' are PIL Image objects
# To display (if in a suitable environment like a Jupyter notebook):
# sample["image"].show()
# sample["mask"].show()
print(f"Prompt (from HF Dataset): {sample['prompt']}")
print(f"Suffix (from HF Dataset): {sample['suffix']}")
print(f"Reasoning Steps (from HF Dataset): {sample['step']}")
If you are working with the raw data format (e.g., after cloning the repository or downloading the raw files), you can load the questions from the question.json file for each split and then load the images and masks using a library like Pillow (PIL).
This example assumes you have the location, placement, and unseen folders (each containing image/, mask/, and question.json) in a known base_data_path.
import json
import os
from PIL import Image
# Set the dataset split name and base directory path
split_name = "Location"
base_data_path = "." # Or set to your actual dataset path
# Load question.json file
question_file = os.path.join(base_data_path, split_name, "question.json")
try:
with open(question_file, 'r', encoding='utf-8') as f:
samples = json.load(f)
except FileNotFoundError:
print(f"File not found: {question_file}")
samples = []
# Process the first sample if available
if samples:
sample = samples[0]
print(f"\n--- Sample Info ---")
print(f"ID: {sample['id']}")
print(f"Prompt: {sample['prompt']}")
# Construct absolute paths to RGB image and mask
rgb_path = os.path.join(base_data_path, split_name, sample["rgb_path"])
mask_path = os.path.join(base_data_path, split_name, sample["mask_path"])
# Load images using Pillow
try:
rgb_image = Image.open(rgb_path)
mask_image = Image.open(mask_path)
sample["image"] = rgb_image
sample["mask"] = mask_image
print(f"RGB image size: {rgb_image.size}")
print(f"Mask image size: {mask_image.size}, mode: {mask_image.mode}")
except FileNotFoundError:
print(f"Image file not found:\n{rgb_path}\n{mask_path}")
except Exception as e:
print(f"Error loading images: {e}")
else:
print("No samples loaded.")
To evaluate RoboRefer on RefSpatial-Bench:
Prepare Input Prompt:
Concatenate sample["prompt"] and sample["suffix"] to form the complete instruction.
# Example for constructing the full input for a sample
full_input_instruction = sample["prompt"] + " " + sample["suffix"]
Model Prediction & JSON Parsing & Coordinate Scaling:
Model Prediction: After providingthe image (sample["image"]) and full_input_instruction to the RoboRefer, it outputs normalized coordinate in a JSON format like[(x, y),...], where each x and y` value is normalized to a range of 0-1.
JSON Parsing: Parse this JSON string to extract the coordinate attributes (e.g., x, y).
Coordinate Scaling:
sample["image"].size to get (width, height) and scale to the original image dimensions (height for y, width for x).# Example: model_output_robo is [(0.234, 0.567)] from Roborefer/RoboPoint
# sample["image"] is a PIL Image object loaded by the datasets library or loaded from the raw data
def text2pts(text, width, height):
pattern = r"\(([-+]?\d+\.?\d*(?:,\s*[-+]?\d+\.?\d*)*?)\)"
matches = re.findall(pattern, text)
points = []
for match in matches:
vector = [
float(num) if '.' in num else int(num) for num in match.split(',')
]
if len(vector) == 2:
x, y = vector
if isinstance(x, float) or isinstance(y, float):
x = int(x * width)
y = int(y * height)
points.append((x, y))
width, height = sample["image"].size
scaled_roborefer_points = text2pts(model_output_robo, width, height)
# These scaled_roborefer_points are then used for evaluation against the mask.
Evaluation: Compare scaled_roborefer_points against sample["mask"]. The main metric is average success rate β the percentage of predictions falling within the mask.
To evaluate Gemini Series on RefSpatial-Bench:
Prepare Input Prompt:
Concatenate the string "Locate the points of" and sample["object"] to form the complete instruction.
# Example for constructing the full input for a sample
full_input_instruction = "Locate the points of " + sample["object"] + "."
Model Prediction & JSON Parsing & Coordinate Scaling:
Model Prediction: After providing the image (sample["image"]) and full_input_instruction to the Gemini model series, it outputs normalized coordinates in an JSON format like "```json\n[\n {\"point\": [y, x], \"label\": \"free space\"}, ...\n]\n```", where each y and x value is normalized to a range of 0-1000.
JSON Parsing: Parse this JSON string to extract the coordinate attributes (e.g., x1, y1, x2, y2, etc.).
Coordinate Conversion: To use these coordinates for evaluation against the mask, they must be:
# Example: model_output_gemini is "```json\n[\n {\"point\": [438, 330], \"label\": \"free space\"}\n]\n```" from Gemini
# and sample["image"] is a PIL Image object loaded by the datasets library or loaded from the raw data
def json2pts(text, width, height):
match = re.search(r"```(?:\w+)?\n(.*?)```", text, re.DOTALL)
if not match:
print("No valid code block found.")
return np.empty((0, 2), dtype=int)
json_cleaned = match.group(1).strip()
try:
data = json.loads(json_cleaned)
except json.JSONDecodeError as e:
print(f"JSON decode error: {e}")
return np.empty((0, 2), dtype=int)
points = []
for item in data:
if "point" in item and isinstance(item["point"], list) and len(item["point"]) == 2:
y_norm, x_norm = item["point"]
x = int(x_norm / 1000 * width)
y = int(y_norm / 1000 * height)
points.append((x, y))
return np.array(points)
width, height = sample["image"].size
scaled_gemini_points = json2pts(model_output_gemini, width, height)
# These scaled_gemini_points are then used for evaluation against the mask.
Evaluation: Compare scaled_gemini_points against sample["mask"]. The main metric is average success rate β the percentage of predictions falling within the mask.
To evaluate a Molmo model on this benchmark:
Prepare Input Prompt:
Concatenate "Locate several points of" and sample["object"] to form the complete instruction.
# Example for constructing the full input for a sample
full_input_instruction = "Locate several points of " + sample["object"] + "."
Model Prediction, XML Parsing, & Coordinate Scaling:
Model Prediction: After providing the image (sample["image"]) and full_input_instruction to the Molmo, it outputs normalized coordinates in an XML format like <points x1="61.5" y1="40.4" x2="76.8" y2="21.8" ... />, where each x and y value is normalized to a range of 0-100.
XML Parsing: Parse this XML string to extract the coordinate attributes (e.g., x1, y1, x2, y2, etc.).
Coordinate Conversion:
# Example: model_output_molmo is '<points x1="61.5" y1="40.4" x2="76.8" y2="21.8"/>' from Molmo
# and sample["image"] is a PIL Image object loaded by the datasets library or loaded from the raw data
def xml2pts(xml_text, width, height):
import re
pattern = re.compile(r'(x\d+)="(-?\d+\.?\d*)"\s+(y\d+)="(-?\d+\.?\d*)"')
matches = pattern.findall(xml_text)
points = [(int(float(x_val) / 100.0 * width), int(float(y_val) / 100.0 * height) ) for _, x_val, _, y_val in matches]
return np.array(points)
width, height = sample["image"].size
scaled_molmo_points = xml2pts(model_output_molmo, width, height)
# These scaled_molmo_points are then used for evaluation.
Evaluation: Compare scaled_molmo_points against sample["mask"]. The main metric is average success rate β the percentage of predictions falling within the mask.
Detailed statistics on step distributions and instruction lengths are provided in the table below.
| RefSpatial-Bench | Step / Statistic | Samples | Avg. Prompt Length |
|---|---|---|---|
| Location | Step 1 | 30 | 11.13 |
| Step 2 | 38 | 11.97 | |
| Step 3 | 32 | 15.28 | |
| Avg. (All) | 100 | 12.78 | |
| Placement | Step 2 | 43 | 15.47 |
| Step 3 | 28 | 16.07 | |
| Step 4 | 22 | 22.68 | |
| Step 5 | 7 | 22.71 | |
| Avg. (All) | 100 | 17.68 | |
| Unseen | Step 2 | 29 | 17.41 |
| Step 3 | 26 | 17.46 | |
| Step 4 | 17 | 24.71 | |
| Step 5 | 5 | 23.8 | |
| Avg. (All) | 77 | 19.45 |
As our research shows, RefSpatial-Bench presents a significant challenge to current models. In the table below, bold text indicates Top-1 accuracy, and underline text indicates Top-2 accuracy.
| Benchmark | Gemini-2.5-Pro | SpaceLLaVA | RoboPoint | Molmo-7B | Molmo-72B | RoboRefer 2B-SFT | RoboRefer 8B-SFT | RoboRefer 2B-RFT |
|---|---|---|---|---|---|---|---|---|
| RefSpatial-Bench-L | 46.96 | 5.82 | 22.87 | 21.91 | 45.77 | 47.00 | 52.00 | 52.00 |
| RefSpatial-Bench-P | 24.21 | 4.31 | 9.27 | 12.85 | 14.74 | 48.00 | 53.00 | 54.00 |
| RefSpatial-Bench-U | 27.14 | 4.02 | 8.40 | 12.23 | 21.24 | 33.77 | 37.66 | 41.56 |
If you have any questions about the benchmark, feel free to email Jingkun (anjingkun02@gmail.com) and Enshen (zhouenshen@buaa.edu.cn).
Please consider citing our work if this benchmark is useful for your research.
@article{zhou2026roborefer,
title={Roborefer: Towards spatial referring with reasoning in vision-language models for robotics},
author={Zhou, Enshen and An, Jingkun and Chi, Cheng and Han, Yi and Rong, Shanyu and Zhang, Chi and Wang, Pengwei and Wang, Zhongyuan and Huang, Tiejun and Sheng, Lu and others},
journal={Advances in Neural Information Processing Systems},
volume={38},
pages={28404--28481},
year={2026}
}
π RefSpatial-Expand-Bench is officially released!
20
151 commits
1 linked in READMEs
updated Jul 27, 2026
RefSpatial-Bench: A Benchmark for Multi-step Spatial Referring
Welcome to RefSpatial-Bench, a challenging benchmark based on real-world cluttered scenes to evaluate more complex multi-step spatial referring with reasoning.
Location Task: This task contains 100 samples, which requires model to predicts a 2D point indicating the unique target object.
Placement Task: This task contains 100 samples, which requires model to predicts a 2D point within the desired free space.
Unseen Set: This set comprises 77 samples from the Location/Placement task, specifically designed to evaluate model generalization after SFT/RFT training on RefSpatial, as it includes novel spatial relation combinations not present in RefSpatial.
step) for each benchmark sample as the number of anchor objects and their spatial relations that help constrain the search space.step value reflects greater reasoning complexity and a stronger need for spatial understanding and reasoning.We provide two formats:
data/ folder contains HF-compatible splits:
locationplacementunseenEach sample includes:
| Field | Description |
|---|---|
id | Unique integer ID |
object | Natural language description of target (object or free area), which is extracted from the prompt |
prompt | Full Referring expressions |
suffix | Instruction for answer formatting (different models may use different suffixes or none; we provide the format used by RoboRefer) |
image | RGB image (datasets.Image) |
mask | Binary mask image (datasets.Image) |
step | Reasoning complexity (number of anchor objects / spatial relations) |
For full reproducibility and visualization, we also include the original files under:
Location/Placement/Unseen/Each folder contains:
Location/
βββ image/ # RGB images (e.g., 0.png, 1.png, ...)
βββ mask/ # Ground truth binary masks
βββ question.json # List of referring prompts and metadata
Each entry in question.json has the following format:
{
"id": 40,
"object": "the second object from the left to the right on the nearest platform",
"prompt": "Please point out the second object from the left to the right on the nearest platform.",
"suffix": "Your answer should be formatted as a list of tuples, i.e. [(x1, y1)], ...",
"rgb_path": "image/40.png",
"mask_path": "mask/40.png",
"category": "location",
"step": 2
}
The official evaluation code is available at https://github.com/Zhoues/RoboRefer. The following provides a quick guide on how to load and use the RefSpatial-Bench.
You can load the dataset easily using the datasets library:
from datasets import load_dataset
# Load the entire dataset (all splits: location, placement, unseen)
# This returns a DatasetDict
dataset_dict = load_dataset("BAAI/RefSpatial-Bench")
# Access a specific split, for example 'location'
location_split_hf = dataset_dict["location"]
# Or load only a specific split directly (returns a Dataset object)
# location_split_direct = load_dataset("BAAI/RefSpatial-Bench", name="location")
# Access a sample from the location split
sample = location_split_hf[0]
# sample is a dictionary where 'rgb' and 'mask' are PIL Image objects
# To display (if in a suitable environment like a Jupyter notebook):
# sample["image"].show()
# sample["mask"].show()
print(f"Prompt (from HF Dataset): {sample['prompt']}")
print(f"Suffix (from HF Dataset): {sample['suffix']}")
print(f"Reasoning Steps (from HF Dataset): {sample['step']}")
If you are working with the raw data format (e.g., after cloning the repository or downloading the raw files), you can load the questions from the question.json file for each split and then load the images and masks using a library like Pillow (PIL).
This example assumes you have the location, placement, and unseen folders (each containing image/, mask/, and question.json) in a known base_data_path.
import json
import os
from PIL import Image
# Set the dataset split name and base directory path
split_name = "Location"
base_data_path = "." # Or set to your actual dataset path
# Load question.json file
question_file = os.path.join(base_data_path, split_name, "question.json")
try:
with open(question_file, 'r', encoding='utf-8') as f:
samples = json.load(f)
except FileNotFoundError:
print(f"File not found: {question_file}")
samples = []
# Process the first sample if available
if samples:
sample = samples[0]
print(f"\n--- Sample Info ---")
print(f"ID: {sample['id']}")
print(f"Prompt: {sample['prompt']}")
# Construct absolute paths to RGB image and mask
rgb_path = os.path.join(base_data_path, split_name, sample["rgb_path"])
mask_path = os.path.join(base_data_path, split_name, sample["mask_path"])
# Load images using Pillow
try:
rgb_image = Image.open(rgb_path)
mask_image = Image.open(mask_path)
sample["image"] = rgb_image
sample["mask"] = mask_image
print(f"RGB image size: {rgb_image.size}")
print(f"Mask image size: {mask_image.size}, mode: {mask_image.mode}")
except FileNotFoundError:
print(f"Image file not found:\n{rgb_path}\n{mask_path}")
except Exception as e:
print(f"Error loading images: {e}")
else:
print("No samples loaded.")
To evaluate RoboRefer on RefSpatial-Bench:
Prepare Input Prompt:
Concatenate sample["prompt"] and sample["suffix"] to form the complete instruction.
# Example for constructing the full input for a sample
full_input_instruction = sample["prompt"] + " " + sample["suffix"]
Model Prediction & JSON Parsing & Coordinate Scaling:
Model Prediction: After providingthe image (sample["image"]) and full_input_instruction to the RoboRefer, it outputs normalized coordinate in a JSON format like[(x, y),...], where each x and y` value is normalized to a range of 0-1.
JSON Parsing: Parse this JSON string to extract the coordinate attributes (e.g., x, y).
Coordinate Scaling:
sample["image"].size to get (width, height) and scale to the original image dimensions (height for y, width for x).# Example: model_output_robo is [(0.234, 0.567)] from Roborefer/RoboPoint
# sample["image"] is a PIL Image object loaded by the datasets library or loaded from the raw data
def text2pts(text, width, height):
pattern = r"\(([-+]?\d+\.?\d*(?:,\s*[-+]?\d+\.?\d*)*?)\)"
matches = re.findall(pattern, text)
points = []
for match in matches:
vector = [
float(num) if '.' in num else int(num) for num in match.split(',')
]
if len(vector) == 2:
x, y = vector
if isinstance(x, float) or isinstance(y, float):
x = int(x * width)
y = int(y * height)
points.append((x, y))
width, height = sample["image"].size
scaled_roborefer_points = text2pts(model_output_robo, width, height)
# These scaled_roborefer_points are then used for evaluation against the mask.
Evaluation: Compare scaled_roborefer_points against sample["mask"]. The main metric is average success rate β the percentage of predictions falling within the mask.
To evaluate Gemini Series on RefSpatial-Bench:
Prepare Input Prompt:
Concatenate the string "Locate the points of" and sample["object"] to form the complete instruction.
# Example for constructing the full input for a sample
full_input_instruction = "Locate the points of " + sample["object"] + "."
Model Prediction & JSON Parsing & Coordinate Scaling:
Model Prediction: After providing the image (sample["image"]) and full_input_instruction to the Gemini model series, it outputs normalized coordinates in an JSON format like "```json\n[\n {\"point\": [y, x], \"label\": \"free space\"}, ...\n]\n```", where each y and x value is normalized to a range of 0-1000.
JSON Parsing: Parse this JSON string to extract the coordinate attributes (e.g., x1, y1, x2, y2, etc.).
Coordinate Conversion: To use these coordinates for evaluation against the mask, they must be:
# Example: model_output_gemini is "```json\n[\n {\"point\": [438, 330], \"label\": \"free space\"}\n]\n```" from Gemini
# and sample["image"] is a PIL Image object loaded by the datasets library or loaded from the raw data
def json2pts(text, width, height):
match = re.search(r"```(?:\w+)?\n(.*?)```", text, re.DOTALL)
if not match:
print("No valid code block found.")
return np.empty((0, 2), dtype=int)
json_cleaned = match.group(1).strip()
try:
data = json.loads(json_cleaned)
except json.JSONDecodeError as e:
print(f"JSON decode error: {e}")
return np.empty((0, 2), dtype=int)
points = []
for item in data:
if "point" in item and isinstance(item["point"], list) and len(item["point"]) == 2:
y_norm, x_norm = item["point"]
x = int(x_norm / 1000 * width)
y = int(y_norm / 1000 * height)
points.append((x, y))
return np.array(points)
width, height = sample["image"].size
scaled_gemini_points = json2pts(model_output_gemini, width, height)
# These scaled_gemini_points are then used for evaluation against the mask.
Evaluation: Compare scaled_gemini_points against sample["mask"]. The main metric is average success rate β the percentage of predictions falling within the mask.
To evaluate a Molmo model on this benchmark:
Prepare Input Prompt:
Concatenate "Locate several points of" and sample["object"] to form the complete instruction.
# Example for constructing the full input for a sample
full_input_instruction = "Locate several points of " + sample["object"] + "."
Model Prediction, XML Parsing, & Coordinate Scaling:
Model Prediction: After providing the image (sample["image"]) and full_input_instruction to the Molmo, it outputs normalized coordinates in an XML format like <points x1="61.5" y1="40.4" x2="76.8" y2="21.8" ... />, where each x and y value is normalized to a range of 0-100.
XML Parsing: Parse this XML string to extract the coordinate attributes (e.g., x1, y1, x2, y2, etc.).
Coordinate Conversion:
# Example: model_output_molmo is '<points x1="61.5" y1="40.4" x2="76.8" y2="21.8"/>' from Molmo
# and sample["image"] is a PIL Image object loaded by the datasets library or loaded from the raw data
def xml2pts(xml_text, width, height):
import re
pattern = re.compile(r'(x\d+)="(-?\d+\.?\d*)"\s+(y\d+)="(-?\d+\.?\d*)"')
matches = pattern.findall(xml_text)
points = [(int(float(x_val) / 100.0 * width), int(float(y_val) / 100.0 * height) ) for _, x_val, _, y_val in matches]
return np.array(points)
width, height = sample["image"].size
scaled_molmo_points = xml2pts(model_output_molmo, width, height)
# These scaled_molmo_points are then used for evaluation.
Evaluation: Compare scaled_molmo_points against sample["mask"]. The main metric is average success rate β the percentage of predictions falling within the mask.
Detailed statistics on step distributions and instruction lengths are provided in the table below.
| RefSpatial-Bench | Step / Statistic | Samples | Avg. Prompt Length |
|---|---|---|---|
| Location | Step 1 | 30 | 11.13 |
| Step 2 | 38 | 11.97 | |
| Step 3 | 32 | 15.28 | |
| Avg. (All) | 100 | 12.78 | |
| Placement | Step 2 | 43 | 15.47 |
| Step 3 | 28 | 16.07 | |
| Step 4 | 22 | 22.68 | |
| Step 5 | 7 | 22.71 | |
| Avg. (All) | 100 | 17.68 | |
| Unseen | Step 2 | 29 | 17.41 |
| Step 3 | 26 | 17.46 | |
| Step 4 | 17 | 24.71 | |
| Step 5 | 5 | 23.8 | |
| Avg. (All) | 77 | 19.45 |
As our research shows, RefSpatial-Bench presents a significant challenge to current models. In the table below, bold text indicates Top-1 accuracy, and underline text indicates Top-2 accuracy.
| Benchmark | Gemini-2.5-Pro | SpaceLLaVA | RoboPoint | Molmo-7B | Molmo-72B | RoboRefer 2B-SFT | RoboRefer 8B-SFT | RoboRefer 2B-RFT |
|---|---|---|---|---|---|---|---|---|
| RefSpatial-Bench-L | 46.96 | 5.82 | 22.87 | 21.91 | 45.77 | 47.00 | 52.00 | 52.00 |
| RefSpatial-Bench-P | 24.21 | 4.31 | 9.27 | 12.85 | 14.74 | 48.00 | 53.00 | 54.00 |
| RefSpatial-Bench-U | 27.14 | 4.02 | 8.40 | 12.23 | 21.24 | 33.77 | 37.66 | 41.56 |
If you have any questions about the benchmark, feel free to email Jingkun (anjingkun02@gmail.com) and Enshen (zhouenshen@buaa.edu.cn).
Please consider citing our work if this benchmark is useful for your research.
@article{zhou2026roborefer,
title={Roborefer: Towards spatial referring with reasoning in vision-language models for robotics},
author={Zhou, Enshen and An, Jingkun and Chi, Cheng and Han, Yi and Rong, Shanyu and Zhang, Chi and Wang, Pengwei and Wang, Zhongyuan and Huang, Tiejun and Sheng, Lu and others},
journal={Advances in Neural Information Processing Systems},
volume={38},
pages={28404--28481},
year={2026}
}