Skill: jfxia/chinese-calligraphy-recognition (version 1.0.0)

--- AIGC: ContentProducer: Minimax Agent AI ContentPropagator: Minimax Agent AI Label: AIGC ProduceID: [REDACTED_SECRET] PropagateID: [REDACTED_SECRET] ReservedCode1: 3046022100d61658ffc6f5bdbf4ebb1716d5aa7dca050f66263bf32e213943418ffb16c55e0221009c2d3c375e8dec9ad5ed853b5a5a4362d57952f6e99e4e4f90c085f47204f159 ReservedCode2: 3045022010f2180d5d9d279369f3095ce2c44b7239bd3cdd2bcac43073b1d6e2caf25ae8022100c578ab0227680f8486e4611e114f91c031192271e6475de8a9da186806f3179a description: |- 汉字书法字体识别技能。用于识别书法图片中的字体类型，包括楷书、行书、草书、篆书、隶书等。 当用户上传书法图片并要求识别字体时触发此技能。 适用于书法作品鉴赏、古籍研究、书法学习、文物鉴定等场景。 模型地址: https://huggingface.co/spaces/jfxia/shufa name: shufa-recognition --- # 汉字书法字体识别技能 ## 功能概述 本技能调用 HuggingFace Spaces 上的书法识别模型（jfxia/shufa）来识别书法图片中的字体类型。 ## 支持的字体类型 根据书法识别模型，支持以下字体识别： | 字体类型 | 说明 | |----------|------| | 楷书 | 标准楷体，如欧阳询、颜真卿、柳公权字体 | | 行书 | 行云流水，如王羲之、米芾字体 | | 草书 | 狂放不羁，如怀素、张旭字体 | | 篆书 | 大篆、小篆 | | 隶书 | 汉隶风格 | | 魏碑 | 魏碑体 | ## 使用方法 ### 1. 图像输入 支持以下输入方式： - **直接上传图片**：用户上传书法图片文件（PNG、JPG、JPEG、BMP 等格式） - **图片URL**：提供图片的网络链接 ### 2. API 调用 使用 HuggingFace Inference API 进行预测： ```python import requests # 方法一：使用 HuggingFace Inference API API_URL = "https://api-inference.huggingface.co/models/xiajingfeng/shufa" headers = {"Authorization": "Bearer YOUR_API_TOKEN"} def query(filename): with open(filename, "rb") as f: data = f.read() response = requests.post(API_URL, headers=headers, data=data) return response.json() # 方法二：使用 HuggingFace Space (Gradio UI) # 直接通过 HTTP 请求调用 Space 的 API SPACE_URL = "https://xiajingfeng-shufa.hf.space" ``` ### 3. 返回结果 模型返回识别结果，包括： ```json { "label": "行书", "confidence": 0.85, "scores": [ {"label": "行书", "score": 0.85}, {"label": "楷书", "score": 0.10}, {"label": "草书", "score": 0.03}, {"label": "篆书", "score": 0.01}, {"label": "隶书", "score": 0.01} ] } ``` ## 识别流程 ``` 用户上传书法图片 │ ▼ 图片预处理 │ ├── 格式转换 ├── 尺寸调整 └── 质量检查 │ ▼ 调用识别模型 │ ├── API 请求 ├── 模型推理 └── 结果解析 │ ▼ 返回识别结果 │ ├── 字体类型 ├── 置信度 └── 备选字体 ``` ## 使用示例 ### 示例 1：识别上传的图片 **用户输入**： > 请识别这张书法图片是什么字体？ （上传书法图片） **处理流程**： 1. 接收用户上传的图片 2. 进行图片预处理 3. 调用书法识别 API 4. 返回识别结果 **返回结果**： > 识别结果：**行书**（置信度 85%） > > 备选字体：楷书（10%）、草书（3%） ### 示例 2：识别图片URL **用户输入**： > 识别这个链接中的书法字体：https://example.com/calligraphy.jpg **处理流程**： 1. 下载图片 2. 调用识别 API 3. 返回结果 ## 注意事项 1. **图片质量**：识别效果与图片质量密切相关，建议使用清晰、完整的书法图片 2. **图片内容**：图片中应包含完整的汉字，避免只识别单个笔画 3. **混合字体**：如果图片中包含多种字体，模型会返回主要字体 4. **API 限制**：注意 HuggingFace API 的调用频率限制 ## 错误处理 | 错误类型 | 说明 | 处理方式 | |----------|------|----------| | 图片格式不支持 | 上传了不支持的图片格式 | 提示用户使用支持的格式 | | 图片过大 | 图片超过 API 限制 | 自动压缩或提示用户 | | API 调用失败 | 网络问题或 API 不可用 | 重试或提示稍后重试 | | 识别失败 | 模型无法识别 | 返回"无法识别"并说明原因 | ## 相关工具 本技能可能需要使用以下工具： - `images_understand` - 图片理解分析 - `extract_content_from_websites` - 从网页提取图片 - `images_search_and_download` - 搜索并下载图片 - `image_synthesize` - 图片合成（生成书法风格图片） ## 扩展功能 除了基本的字体识别，还可以扩展以下功能： 1. **字体风格分析**：分析书法的具体风格特点 2. **作者推断**：根据风格推断可能的书法家 3. **年代鉴定**：估计书法作品的大致年代 4. **临摹建议**：提供临摹建议和学习路径

path: scripts/recognize.py
content: #!/usr/bin/env python3
"""
汉字书法字体识别脚本
调用书法识别API进行书法字体识别
支持多个API源：
1. 镜像站: https://xjf123.dy.takin.cc (推荐，无需Token)
2. HuggingFace Space: https://jfxia-shufa.hf.space
"""

import os
import sys
import argparse
import requests
from typing import Dict, List, Optional
import json
import base64


class CalligraphyRecognizer:
    """书法字体识别器"""
    
    # 支持的字体类型
    FONT_TYPES = [
        "楷书", "行书", "草书", 
        "篆书", "隶书"
    ]
    
    def __init__(self, api_token: Optional[str] = None):
        """
        初始化识别器
        
        Args:
            api_token: HuggingFace API Token (可选)
        """
        self.api_token = api_token or os.environ.get("HF_TOKEN")
        
        # API 地址列表（按优先级排序）
        self.api_endpoints = [
            # 镜像站 (推荐，无速率限制)
            "https://xjf123.dy.takin.cc/upload",
            # HuggingFace Space
            "https://jfxia-shufa.hf.space/run/predict",
        ]
    
    def recognize_from_file(self, image_path: str) -> Dict:
        """
        从图片文件识别书法字体
        
        Args:
            image_path: 图片文件路径
            
        Returns:
            识别结果字典
        """
        if not os.path.exists(image_path):
            return {
                "success": False,
                "error": f"图片文件不存在: {image_path}"
            }
        
        try:
            with open(image_path, "rb") as f:
                image_data = f.read()
            
            return self._call_api(image_data)
            
        except Exception as e:
            return {
                "success": False,
                "error": f"读取图片失败: {str(e)}"
            }
    
    def recognize_from_url(self, image_url: str) -> Dict:
        """
        从 URL 识别书法字体
        
        Args:
            image_url: 图片 URL
            
        Returns:
            识别结果字典
        """
        try:
            # 下载图片
            response = requests.get(image_url, timeout=30)
            if response.status_code != 200:
                return {
                    "success": False,
                    "error": f"下载图片失败: HTTP {response.status_code}"
                }
            
            image_data = response.content
            return self._call_api(image_data)
            
        except Exception as e:
            return {
                "success": False,
                "error": f"下载图片失败: {str(e)}"
            }
    
    def _call_api(self, image_data: bytes) -> Dict:
        """调用 API 进行识别"""
        
        # 尝试镜像站 (推荐)
        try:
            result = self._call_mirror_api(image_data)
            if result.get("success"):
                return result
        except Exception as e:
            print(f"镜像站调用失败: {e}", file=sys.stderr)
        
        # 尝试 HuggingFace Space
        try:
            result = self._call_hf_space_api(image_data)
            if result.get("success"):
                return result
        except Exception as e:
            print(f"HuggingFace Space 调用失败: {e}", file=sys.stderr)
        
        return {
            "success": False,
            "error": "所有 API 调用均失败"
        }
    
    def _call_mirror_api(self, image_data: bytes) -> Dict:
        """调用镜像站 API"""
        
        url = "https://xjf123.dy.takin.cc/upload"
        
        files = {"file": image_data}
        
        response = requests.post(url, files=files, timeout=60)
        
        if response.status_code == 200:
            data = response.json()
            return self._parse_mirror_result(data)
        else:
            raise Exception(f"HTTP {response.status_code}")
    
    def _call_hf_space_api(self, image_data: bytes) -> Dict:
        """调用 HuggingFace Space API"""
        
        # 将图片转换为 base64
        image_base64 = base64.b64encode(image_data).decode('utf-8')
        
        # 构建请求
        url = "https://jfxia-shufa.hf.space/run/predict"
        payload = {
            "data": [f"data:image/png;base64,{image_base64}"]
        }
        
        headers = {"Content-Type": "application/json"}
        if self.api_token:
            headers["Authorization"] = f"Bearer {self.api_token}"
        
        response = requests.post(url, json=payload, headers=headers, timeout=60)
        
        if response.status_code == 200:
            result = response.json()
            return self._parse_hf_result(result)
        else:
            raise Exception(f"HTTP {response.status_code}")
    
    def _parse_mirror_result(self, result: List) -> Dict:
        """解析镜像站返回结果"""
        
        try:
            if not result or len(result) == 0:
                return {
                    "success": False,
                    "error": "返回结果为空"
                }
            
            # 解析结果列表
            all_results = []
            for item in result:
                char = item.get("char", "")
                prob_str = item.get("prob", "0%").replace("%", "")
                try:
                    prob = float(prob_str) / 100
                except:
                    prob = 0.0
                all_results.append({
                    "char": char,
                    "confidence": prob
                })
            
            return {
                "success": True,
                "char": all_results[0]["char"],
                "confidence": all_results[0]["confidence"],
                "all_results": all_results,
                "raw_result": result
            }
            
        except Exception as e:
            return {
                "success": False,
                "error": f"解析结果失败: {str(e)}"
            }
    
    def _parse_hf_result(self, result: Dict) -> Dict:
        """解析 HuggingFace 返回结果"""
        
        try:
            # Gradio 返回格式: {"data": [...]}
            if "data" in result and len(result["data"]) > 0:
                first_result = result["data"][0]
                
                if isinstance(first_result, dict):
                    label = first_result.get("label", "")
                    confidences = first_result.get("confidences", [])
                    
                    confidence = 0.0
                    if confidences and len(confidences) > 0:
                        confidence = confidences[0].get("confidence", confidences[0].get("score", 0))
                    
                    return {
                        "success": True,
                        "font_type": label,
                        "confidence": confidence,
                        "all_results": confidences,
                        "raw_result": result
                    }
            
            return {
                "success": False,
                "error": "无法解析识别结果"
            }
            
        except Exception as e:
            return {
                "success": False,
                "error": f"解析结果失败: {str(e)}"
            }
    
    def print_result(self, result: Dict) -> None:
        """打印识别结果"""
        if result.get("success"):
            print(f"✓ 识别成功")
            print(f"  汉字: {result.get('char', result.get('font_type', 'N/A'))}")
            print(f"  置信度: {result['confidence']:.2%}")
            if "all_results" in result and result["all_results"]:
                print(f"\n  备选结果:")
                for r in result["all_results"][:5]:
                    label = r.get("char", r.get("label", r.get("class", "")))
                    score = r.get("confidence", r.get("score", 0))
                    print(f"    - {label}: {score:.2%}")
        else:
            print(f"✗ 识别失败")
            print(f"  错误信息: {result.get('error', '未知错误')}")
        
        # 打印原始结果（调试用）
        if "--verbose" in sys.argv or "-v" in sys.argv:
            print(f"\n原始结果:")
            print(json.dumps(result.get("raw_result", {}), indent=2, ensure_ascii=False))


def main():
    parser = argparse.ArgumentParser(description="汉字书法字体识别")
    parser.add_argument("image", help="图片文件路径或图片URL")
    parser.add_argument("-t", "--token", help="HuggingFace API Token")
    parser.add_argument("-v", "--verbose", action="store_true", help="显示详细信息")
    
    args = parser.parse_args()
    
    recognizer = CalligraphyRecognizer(args.token)
    
    # 判断是文件还是 URL
    if args.image.startswith("http://") or args.image.startswith("https://"):
        result = recognizer.recognize_from_url(args.image)
    else:
        result = recognizer.recognize_from_file(args.image)
    
    recognizer.print_result(result)
    
    # 返回状态码
    sys.exit(0 if result.get("success") else 1)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)