Install any skill in seconds. Free to start, no credit card required.
Get Started Free →图片理解与数据提取 skill。当图片文件(.png/.jpg/.jpeg/.gif/.webp/.bmp)是主要输入且用户需要理解、提取数据或分析图片内容时使用。提供预配置的 caption 脚本(scripts/caption.py),通过 vision 模型将图片转为文本描述,无需额外配置 API Key。覆盖:(1) 通过 scripts/caption.py 对图表/表格/截图/流程图进行 caption,(2) 将 caption 文本解析为结构化 DataFrame,(3) 基于提取数据重新生成可视化图表,(4) 导出为 Excel/CSV。**遇到以下任一情况就主动使用本 skill,不要自行猜测图片内容**:①用户出现触发词:图片分析 / 图表提取 / 表格识别 / OCR / 图片描述 / 截图分析 / 图表数据 / 提取图片中的数据 / 图片转表格 / 识别图片 / image caption / extract data from image / chart analysis / table OCR;②用户上传或指定了图片文件(.png / .jpg / .jp
.claude/skills/opensensenova-sn-da-image-caption/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 33% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 55% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 160% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 313% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 122% | 0% |
Analyze, extract data from, or understand image files (.png, .jpg, .jpeg, .gif, .webp, .bmp). The core workflow:
scripts/caption.py to get a text description of the imageThe script converts images to text descriptions via a vision model. Configure via SN_API_KEY (minimum required), or use SN_VISION_API_KEY / SN_VISION_BASE_URL / SN_VISION_MODEL for fine-grained control. See the project environment variable spec for the full fallback chain.
bash# Basic — get text description python3 scripts/caption.py /mnt/data/image.png # Custom prompt — guide what to extract python3 scripts/caption.py /mnt/data/chart.png --prompt "提取所有数值,Markdown 表格格式" # JSON output — includes detected type, usage stats, cache info python3 scripts/caption.py /mnt/data/image.png --json # Batch — process all images in a directory python3 scripts/caption.py /mnt/data/images/ --batch --output /mnt/data/captions.json # Override model (optional) python3 scripts/caption.py /mnt/data/image.png --model gemini-3.1-flash-lite-preview
| Option | Description | |--------|------------| | --prompt, -p | Custom prompt (overrides auto-detection) | | --model, -m | Vision model (default: sensenova-6.8-flash-lite) | | --json | Output structured JSON instead of plain text | | --batch | Process all images in a directory | | --output, -o | Output file for batch results | | --no-cache | Skip MD5 cache |
json{ "file": "/mnt/data/image.png", "type": "chart", "description": "这是一张柱状图...", "usage": {"prompt_tokens": 1100, "completion_tokens": 400}, "cached": false }
pythonimport subprocess, json CAPTION = "/path/to/skills/sn-da-image-caption/scripts/caption.py" # Single image result = subprocess.run( ["python3", CAPTION, "/mnt/data/chart.png", "--json", "--prompt", "提取图表数据,Markdown 表格输出"], capture_output=True, text=True, timeout=60 ) data = json.loads(result.stdout) description = data["description"] # Batch result = subprocess.run( ["python3", CAPTION, "/mnt/data/images/", "--batch", "--output", "/mnt/data/captions.json"], capture_output=True, text=True, timeout=300 ) with open("/mnt/data/captions.json") as f: all_captions = json.load(f)
Different image types need different prompts. The script auto-detects, but specifying --prompt gives better results.
| Image Type | When | Recommended --prompt | |-----------|------|---------------------| | Data chart | 柱状图/折线图/饼图 | "提取图表标题、坐标轴、每个数据点数值、图例。Markdown 表格输出。" | | Table screenshot | 表格截图 | "提取表格所有内容,Markdown 表格格式,保持行列结构,数值不四舍五入。" | | UI screenshot | 界面截图 | "以前端开发者视角描述:布局、组件、文字、颜色。" | | Diagram | 流程图/架构图 | "描述所有节点、连接关系(A→B)、分支条件。" | | General | 照片、其他 | 不传 --prompt,用默认 |
Caption 通常返回 Markdown 表格,解析为 DataFrame:
pythonimport pandas as pd def parse_markdown_table(text): lines = text.strip().split('\n') table_lines = [] in_table = False for line in lines: stripped = line.strip() if '|' in stripped: in_table = True table_lines.append(stripped) elif in_table: break data_lines = [] for l in table_lines: cells = [c.strip() for c in l.split('|') if c.strip()] if cells and not all(set(c) <= set('-: ') for c in cells): data_lines.append(cells) if len(data_lines) < 2: return None header = data_lines[0] rows = [r for r in data_lines[1:] if len(r) == len(header)] df = pd.DataFrame(rows, columns=header) # Auto numeric conversion for col in df.columns: try: cleaned = df[col].str.replace(',', '').str.strip() if cleaned.str.endswith('%').any(): df[col] = pd.to_numeric(cleaned.str.rstrip('%'), errors='coerce') else: converted = pd.to_numeric(cleaned, errors='coerce') if converted.notna().sum() > len(df) * 0.5: df[col] = converted except Exception: pass return df
pythonimport matplotlib.pyplot as plt import matplotlib import os font_path = '/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc' if os.path.exists(font_path): matplotlib.rcParams['font.family'] = 'WenQuanYi Zen Hei' matplotlib.rcParams['axes.unicode_minus'] = False
pythonCOLORS = ['#4C72B0', '#55A868', '#C44E52', '#8172B2', '#CCB974', '#64B5CD']
pythonplt.savefig('/mnt/data/chart.png', dpi=150, bbox_inches='tight') plt.show() print("")
pythonfrom openpyxl.styles import Font, PatternFill, Alignment output_path = "/mnt/data/result.xlsx" with pd.ExcelWriter(output_path, engine='openpyxl') as writer: df.to_excel(writer, index=False, sheet_name='提取数据') ws = writer.sheets['提取数据'] fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid') for cell in ws[1]: cell.font = Font(bold=True, color='FFFFFF') cell.fill = fill cell.alignment = Alignment(horizontal='center') for i, col in enumerate(df.columns, 1): w = max(df[col].astype(str).str.len().max(), len(str(col))) + 2 ws.column_dimensions[chr(64 + i)].width = min(w * 1.2, 40) print(f"[下载](sandbox:{output_path})")
pythonimport glob image_files = sorted(glob.glob("/mnt/data/*.png")) all_dfs = [] for img in image_files: r = subprocess.run( ["python3", CAPTION, img, "--json", "--prompt", "提取数据,Markdown 表格"], capture_output=True, text=True, timeout=60 ) desc = json.loads(r.stdout)["description"] df = parse_markdown_table(desc) if df is not None: all_dfs.append(df) combined = pd.concat(all_dfs, ignore_index=True) if all_dfs else None
Or batch mode:
bashpython3 scripts/caption.py /mnt/data/images/ --batch --output /mnt/data/captions.json
"提取前半部分" + "提取后半部分"| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 21,084 | 17,267 | -18% | 1 | 1 | 0% | 615 | 2,642 | +330% | 0 | 0 | — |
case-02 | fail→fail | 34,456 | 19,983 | -42% | 1 | 1 | 0% | 3,829 | 2,861 | -25% | 0 | 0 | — |
case-08 | fail→pass | 20,079 | 11,862 | -41% | 1 | 1 | 0% | 2,676 | 3,549 | +33% | 0 | 0 | — |
case-03 | fail→fail | 18,655 | 18,505 | -1% | 1 | 1 | 0% | 401 | 2,813 | +601% | 0 | 0 | — |
case-04 | fail→pass | 18,255 | 12,060 | -34% | 1 | 1 | 0% | 2,201 | 3,417 | +55% | 0 | 0 | — |
case-05 | fail→pass | 13,600 | 11,895 | -13% | 1 | 1 | 0% | 1,266 | 3,286 | +160% | 0 | 0 | — |
case-06 | fail→pass | 9,619 | 10,411 | +8% | 1 | 1 | 0% | 758 | 3,134 | +313% | 0 | 0 | — |
case-07 | fail→pass | 18,564 | 20,825 | +12% | 1 | 1 | 0% | 2,390 | 5,302 | +122% | 0 | 0 | — |
case-09 | pass→pass | 18,718 | 19,879 | +6% | 1 | 1 | 0% | 2,530 | 5,111 | +102% | 0 | 0 | — |
case-10 | fail→fail | 17,460 | 18,843 | +8% | 1 | 1 | 0% | 2,371 | 4,601 | +94% | 0 | 0 | — |
case-11 | pass→pass | 11,063 | 8,058 | -27% | 1 | 1 | 0% | 1,016 | 2,753 | +171% | 0 | 0 | — |
case-12 | fail→pass | 15,449 | 8,989 | -42% | 1 | 1 | 0% | 1,789 | 2,954 | +65% | 0 | 0 | — |
case-13 | pass→pass | 10,399 | 9,205 | -11% | 1 | 1 | 0% | 1,008 | 2,976 | +195% | 0 | 0 | — |
case-14 | fail→pass | 16,246 | 7,409 | -54% | 1 | 1 | 0% | 1,815 | 2,631 | +45% | 0 | 0 | — |
case-15 | fail→pass | 17,519 | 10,875 | -38% | 1 | 1 | 0% | 2,095 | 3,172 | +51% | 0 | 0 | — |
case-16 | fail→pass | 21,096 | 10,411 | -51% | 1 | 1 | 0% | 2,808 | 3,332 | +19% | 0 | 0 | — |
case-17 | pass→pass | 21,801 | 38,881 | +78% | 1 | 1 | 0% | 2,779 | 5,641 | +103% | 0 | 0 | — |
case-18 | pass→pass | 24,007 | 19,876 | -17% | 1 | 1 | 0% | 3,313 | 5,652 | +71% | 0 | 0 | — |
case-19 | fail→pass | 20,609 | 15,552 | -25% | 1 | 1 | 0% | 2,691 | 4,186 | +56% | 0 | 0 | — |
case-20 | pass→pass | 16,842 | 19,282 | +14% | 1 | 1 | 0% | 2,140 | 4,560 | +113% | 0 | 0 | — |
case-21 | pass→pass | 11,175 | 10,025 | -10% | 1 | 1 | 0% | 1,126 | 3,125 | +178% | 0 | 0 | — |
case-22 | pass→pass | 13,005 | 12,758 | -2% | 1 | 1 | 0% | 1,347 | 3,657 | +171% | 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 19 counted toward the lift figure. The other 3 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 +45 percentage points is the difference between those two pass rates over the 19 comparable cases.
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.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.6-flash | verified | 8/4/2026 | +27% |
Other measured skills in the registry, with their headline benchmark lift.