Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Find duplicate or near-duplicate images in FiftyOne datasets using brain similarity computation. Use when users want to deduplicate datasets, find similar images, cluster visually similar content, or remove redundant samples. Requires FiftyOne MCP server with @voxel51/brain plugin installed.
.claude/skills/aiskillstore-fiftyone-find-duplicates/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 28% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 162% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 72% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 333% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 60% | 0% |
Find and remove duplicate or near-duplicate images using FiftyOne's brain similarity operators. Uses deep learning embeddings to identify visually similar images.
Use this skill when:
@voxel51/brain plugin installed and enabledALWAYS follow these rules:
pythonset_context(dataset_name="my-dataset")
Brain operators are delegated and require the app:
pythonlaunch_app()
Wait 5-10 seconds for initialization.
python# List all brain operators list_operators(builtin_only=False) # Get schema for specific operator get_operator_schema(operator_uri="@voxel51/brain/compute_similarity")
pythonexecute_operator( operator_uri="@voxel51/brain/compute_similarity", params={"brain_key": "img_sim", "model": "mobilenet-v2-imagenet-torch"} )
pythonclose_app()
python# Set context set_context(dataset_name="my-dataset") # Launch app (required for brain operators) launch_app()
python# Check if brain plugin is available list_plugins(enabled=True) # If not installed: download_plugin( url_or_repo="voxel51/fiftyone-plugins", plugin_names=["@voxel51/brain"] ) enable_plugin(plugin_name="@voxel51/brain")
python# List all available operators list_operators(builtin_only=False) # Get schema for compute_similarity get_operator_schema(operator_uri="@voxel51/brain/compute_similarity") # Get schema for find_duplicates get_operator_schema(operator_uri="@voxel51/brain/find_duplicates")
python# Execute operator to compute embeddings execute_operator( operator_uri="@voxel51/brain/compute_similarity", params={ "brain_key": "img_duplicates", "model": "mobilenet-v2-imagenet-torch" } )
pythonexecute_operator( operator_uri="@voxel51/brain/find_near_duplicates", params={ "similarity_index": "img_duplicates", "threshold": 0.3 } )
Threshold guidelines (distance-based, lower = more similar):
0.1 = Very similar (near-exact duplicates)0.3 = Near duplicates (recommended default)0.5 = Similar images0.7 = Loosely similarThis operator creates two saved views automatically:
near duplicates: all samples that are near duplicatesrepresentatives of near duplicates: one representative from each groupAfter finding duplicates, use set_view to display them in the FiftyOne App:
Option A: Filter by near_dup_id field
python# Show all samples that have a near_dup_id (all duplicates) set_view(exists=["near_dup_id"])
Option B: Show specific duplicate group
python# Show samples with a specific duplicate group ID set_view(filters={"near_dup_id": 1})
Option C: Load saved view (if available)
python# Load the automatically created saved view set_view(view_name="near duplicates")
Option D: Clear filter to show all samples
pythonclear_view()
The find_near_duplicates operator adds a near_dup_id field to samples. Samples with the same ID are duplicates of each other.
Option A: Use deduplicate operator (keeps one representative per group)
pythonexecute_operator( operator_uri="@voxel51/brain/deduplicate_near_duplicates", params={} )
Option B: Manual deletion from App UI
set_view(exists=["near_dup_id"]) to show duplicatespythonclose_app()
| Tool | Description | |------|-------------| | set_view(exists=[...]) | Filter samples where field(s) have non-None values | | set_view(filters={...}) | Filter samples by exact field values | | set_view(tags=[...]) | Filter samples by tags | | set_view(sample_ids=[...]) | Select specific sample IDs | | set_view(view_name="...") | Load a saved view by name | | clear_view() | Clear filters, show all samples |
Use list_operators() to discover and get_operator_schema() to see parameters:
| Operator | Description | |----------|-------------| | @voxel51/brain/compute_similarity | Compute embeddings and similarity index | | @voxel51/brain/find_near_duplicates | Find near-duplicate samples | | @voxel51/brain/deduplicate_near_duplicates | Delete duplicates, keep representatives | | @voxel51/brain/find_exact_duplicates | Find exact duplicate media files | | @voxel51/brain/deduplicate_exact_duplicates | Delete exact duplicates | | @voxel51/brain/compute_uniqueness | Compute uniqueness scores |
For accidentally duplicated files (identical bytes):
pythonset_context(dataset_name="my-dataset") launch_app() execute_operator( operator_uri="@voxel51/brain/find_exact_duplicates", params={} ) execute_operator( operator_uri="@voxel51/brain/deduplicate_exact_duplicates", params={} ) close_app()
For visually similar but not identical images:
pythonset_context(dataset_name="my-dataset") launch_app() # Compute embeddings execute_operator( operator_uri="@voxel51/brain/compute_similarity", params={"brain_key": "near_dups", "model": "mobilenet-v2-imagenet-torch"} ) # Find duplicates execute_operator( operator_uri="@voxel51/brain/find_near_duplicates", params={"similarity_index": "near_dups", "threshold": 0.3} ) # View duplicates in the App set_view(exists=["near_dup_id"]) # After review, deduplicate execute_operator( operator_uri="@voxel51/brain/deduplicate_near_duplicates", params={} ) # Clear view and close clear_view() close_app()
Find images similar to a specific sample:
pythonset_context(dataset_name="my-dataset") launch_app() execute_operator( operator_uri="@voxel51/brain/compute_similarity", params={"brain_key": "search"} ) execute_operator( operator_uri="@voxel51/brain/sort_by_similarity", params={ "brain_key": "search", "query_id": "sample_id_here", "k": 20 } ) close_app()
Error: "No executor available"
find_near_duplicates, deduplicate_near_duplicatesError: "Brain key not found"
compute_similarity first with a brain_keyError: "Operator not found"
download_plugin() and enable_plugin()Error: "Missing dependency" (e.g., torch, tensorflow)
missing_package and install_commandjson { "error_type": "missing_dependency", "missing_package": "torch", "install_command": "pip install torch" }
Similarity computation is slow
mobilenet-v2-imagenet-torchlist_operators() and get_operator_schema() to get current operator names and parametersbrain_keyEmbedding computation time:
Memory requirements:
Copyright 2017-2025, Voxel51, Inc. Apache 2.0 License
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 21,867 | 3,168 | -86% | 1 | 1 | 0% | 3,419 | 2,925 | -14% | 0 | 0 | — |
case-02 | fail→pass | 15,024 | 6,114 | -59% | 1 | 1 | 0% | 2,866 | 3,678 | +28% | 0 | 0 | — |
case-03 | pass→fail | 11,424 | 4,499 | -61% | 1 | 1 | 0% | 2,181 | 3,156 | +45% | 0 | 0 | — |
case-04 | fail→pass | 7,049 | 3,954 | -44% | 1 | 1 | 0% | 1,275 | 3,339 | +162% | 0 | 0 | — |
case-05 | fail→pass | 10,787 | 3,793 | -65% | 1 | 1 | 0% | 1,884 | 3,248 | +72% | 0 | 0 | — |
case-11 | fail→pass | 3,603 | 3,109 | -14% | 1 | 1 | 0% | 714 | 3,094 | +333% | 0 | 0 | — |
case-06 | fail→pass | 12,891 | 4,087 | -68% | 1 | 1 | 0% | 2,079 | 3,326 | +60% | 0 | 0 | — |
case-07 | fail→pass | 9,698 | 3,842 | -60% | 1 | 1 | 0% | 1,723 | 3,161 | +83% | 0 | 0 | — |
case-08 | fail→pass | 9,489 | 4,856 | -49% | 1 | 1 | 0% | 1,442 | 3,338 | +131% | 0 | 0 | — |
case-09 | fail→pass | 6,992 | 5,078 | -27% | 1 | 1 | 0% | 1,301 | 3,446 | +165% | 0 | 0 | — |
case-10 | fail→pass | 7,221 | 3,681 | -49% | 1 | 1 | 0% | 1,303 | 3,234 | +148% | 0 | 0 | — |
case-12 | fail→pass | 10,113 | 2,456 | -76% | 1 | 1 | 0% | 1,611 | 2,886 | +79% | 0 | 0 | — |
case-13 | fail→pass | 6,297 | 1,700 | -73% | 1 | 1 | 0% | 1,151 | 2,794 | +143% | 0 | 0 | — |
case-14 | pass→pass | 7,451 | 3,806 | -49% | 1 | 1 | 0% | 1,256 | 3,291 | +162% | 0 | 0 | — |
case-15 | fail→pass | 12,782 | 7,175 | -44% | 1 | 1 | 0% | 2,165 | 3,903 | +80% | 0 | 0 | — |
case-16 | fail→pass | 9,316 | 1,730 | -81% | 1 | 1 | 0% | 1,688 | 2,785 | +65% | 0 | 0 | — |
case-17 | pass→pass | 7,587 | 1,874 | -75% | 1 | 1 | 0% | 1,331 | 2,812 | +111% | 0 | 0 | — |
case-18 | pass→pass | 14,778 | 3,366 | -77% | 1 | 1 | 0% | 2,937 | 3,183 | +8% | 0 | 0 | — |
case-19 | fail→pass | 11,251 | 3,900 | -65% | 1 | 1 | 0% | 2,011 | 3,173 | +58% | 0 | 0 | — |
case-20 | pass→pass | 7,783 | 7,877 | +1% | 1 | 1 | 0% | 1,403 | 3,951 | +182% | 0 | 0 | — |
case-21 | pass→pass | 9,640 | 9,245 | -4% | 1 | 1 | 0% | 1,945 | 4,359 | +124% | 0 | 0 | — |
case-22 | pass→fail | 8,452 | 8,095 | -4% | 1 | 1 | 0% | 1,820 | 3,009 | +65% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +55 percentage points is the difference between those two pass rates over the 21 comparable cases. 3 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.