$npx -y skills add opengeos/geoai-skills --skill detect-objectsRun pre-trained AI models on geospatial imagery. Detect buildings, cars, ships, solar panels, agriculture fields, or use text-prompted segmentation with GroundedSAM. Requires GPU for best performance.
| 1 | You are helping the user run AI object detection on geospatial imagery using geoai. |
| 2 | |
| 3 | Input: `$@` |
| 4 | |
| 5 | Follow these steps in order. |
| 6 | |
| 7 | ## Step 1 -- Parse arguments |
| 8 | |
| 9 | Extract: |
| 10 | - `$0` as the model name: `buildings`, `cars`, `ships`, `solar-panels`, `parking-lots`, `agriculture`, or `grounded-sam` |
| 11 | - `$1` as the input raster path |
| 12 | - `--text PROMPT` for GroundedSAM text-prompted segmentation (required when model is `grounded-sam`) |
| 13 | - `--output FILE` for the output vector file (default: `./<model>_detections.gpkg`) |
| 14 | |
| 15 | If the model name is not recognized, list the available models and ask the user to pick one. |
| 16 | |
| 17 | Model mapping: |
| 18 | |
| 19 | | Argument | GeoAI Class | |
| 20 | |---|---| |
| 21 | | `buildings` | `geoai.BuildingFootprintExtractor` | |
| 22 | | `cars` | `geoai.CarDetector` | |
| 23 | | `ships` | `geoai.ShipDetector` | |
| 24 | | `solar-panels` | `geoai.SolarPanelDetector` | |
| 25 | | `parking-lots` | `geoai.ParkingSplotDetector` | |
| 26 | | `agriculture` | `geoai.AgricultureFieldDelineator` | |
| 27 | | `grounded-sam` | `geoai.GroundedSAM` | |
| 28 | |
| 29 | ## Step 2 -- Check GPU availability |
| 30 | |
| 31 | ```bash |
| 32 | python3 -c " |
| 33 | import torch |
| 34 | if torch.cuda.is_available(): |
| 35 | print(f'GPU: {torch.cuda.get_device_name(0)}') |
| 36 | print(f'CUDA: {torch.version.cuda}') |
| 37 | print(f'Memory: {torch.cuda.get_device_properties(0).total_mem / 1e9:.1f} GB') |
| 38 | else: |
| 39 | print('GPU: not available (CPU mode)') |
| 40 | print('Warning: inference will be significantly slower without a GPU') |
| 41 | " |
| 42 | ``` |
| 43 | |
| 44 | If no GPU is available, warn the user but continue. |
| 45 | |
| 46 | ## Step 3 -- Resolve the input file |
| 47 | |
| 48 | If `$1` looks like an absolute path, use it directly. Otherwise: |
| 49 | |
| 50 | ```bash |
| 51 | find "$PWD" -name "$1" -not -path '*/.git/*' 2>/dev/null |
| 52 | ``` |
| 53 | |
| 54 | If no file specified and state exists, check for recently inspected/downloaded files: |
| 55 | |
| 56 | ```bash |
| 57 | STATE_DIR="" |
| 58 | test -f .geoai-skills/state.json && STATE_DIR=".geoai-skills" |
| 59 | PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")" |
| 60 | PROJECT_ID="$(echo "$PROJECT_ROOT" | tr '/' '-')" |
| 61 | test -f "$HOME/.geoai-skills/$PROJECT_ID/state.json" && STATE_DIR="$HOME/.geoai-skills/$PROJECT_ID" |
| 62 | ``` |
| 63 | |
| 64 | ## Step 4 -- Run the detector |
| 65 | |
| 66 | ### Pre-trained detectors (buildings, cars, ships, solar-panels, parking-lots, agriculture) |
| 67 | |
| 68 | ```bash |
| 69 | python3 -c " |
| 70 | import geoai |
| 71 | |
| 72 | detector = geoai.DETECTOR_CLASS() |
| 73 | gdf = detector.predict( |
| 74 | 'INPUT_PATH', |
| 75 | output_path='OUTPUT_PATH', |
| 76 | ) |
| 77 | print(f'Detections: {len(gdf)}') |
| 78 | print(f'Output: OUTPUT_PATH') |
| 79 | print(f'Columns: {list(gdf.columns)}') |
| 80 | if len(gdf) > 0: |
| 81 | print('---') |
| 82 | print('Sample (first 5):') |
| 83 | print(gdf.head().to_string()) |
| 84 | " |
| 85 | ``` |
| 86 | |
| 87 | Replace `DETECTOR_CLASS` with the appropriate class from the mapping table (e.g. `BuildingFootprintExtractor`). |
| 88 | |
| 89 | ### GroundedSAM (text-prompted segmentation) |
| 90 | |
| 91 | ```bash |
| 92 | python3 -c " |
| 93 | import geoai |
| 94 | |
| 95 | sam = geoai.GroundedSAM() |
| 96 | gdf = sam.predict( |
| 97 | 'INPUT_PATH', |
| 98 | text_prompt='TEXT_PROMPT', |
| 99 | output_path='OUTPUT_PATH', |
| 100 | ) |
| 101 | print(f'Segments: {len(gdf)}') |
| 102 | print(f'Output: OUTPUT_PATH') |
| 103 | print(f'Columns: {list(gdf.columns)}') |
| 104 | if len(gdf) > 0: |
| 105 | print('---') |
| 106 | print('Sample (first 5):') |
| 107 | print(gdf.head().to_string()) |
| 108 | " |
| 109 | ``` |
| 110 | |
| 111 | Replace `TEXT_PROMPT` with the user's text prompt. |
| 112 | |
| 113 | Replace `INPUT_PATH` and `OUTPUT_PATH` with actual values before running. |
| 114 | |
| 115 | ## Step 5 -- Report results |
| 116 | |
| 117 | Summarize: |
| 118 | - Model used |
| 119 | - Number of detections/segments |
| 120 | - Output file path |
| 121 | - Sample of results |
| 122 | |
| 123 | Then suggest: *"Use `/geoai-skills:inspect-geo` to examine the detection output."* |
| 124 | |
| 125 | ## Error handling |
| 126 | |
| 127 | - **`import geoai` fails** -> delegate to `/geoai-skills:install-geoai`. |
| 128 | - **`import torch` fails** -> suggest installing PyTorch: `pip install torch torchvision`. |
| 129 | - **CUDA out of memory** -> suggest reducing the tile size or processing a smaller area. If the detector accepts a `tile_size` parameter, recommend a smaller value. |
| 130 | - **Model download fails** -> check network connectivity. Models are downloaded from Hugging Face on first use. |
| 131 | - **Input is not a raster** -> suggest using a GeoTIFF file. If the user has a vector file, suggest `/geoai-skills:process-raster vector-to-raster` first. |
| 132 | - **GroundedSAM without --text** -> ask the user for a text prompt describing what to detect. |