摘要

春节期间,走在街头巷尾,家家户户门上贴的春联其实挺有意思——红纸金字,浓缩了一家人对新年的期许。但大多数人看春联都是走马观花,不知道平仄、不懂典故,更别说自己动手写一副了。这个项目想做的事情很朴素:用Rokid AI眼镜扫一眼门口的春联,系统就能在AR视野里告诉你这副联的出处、意境和赏析,还能帮你根据家庭情况生成一副专属的新年联。整个系统基于Rokid灵珠AI平台和CXR-M SDK开发,核心能力包括眼镜端图像采集、云端OCR+大模型分析、AR结果展示和语音交互。从除夕前贴联,到正月里走亲戚看联,都能用上。


一、从春节贴春联说起

我家每年贴春联这件事,基本是我爸负责的。他有个习惯,每次去买春联都要在摊位前站很久,把上联、下联一副一副念出来比较。问他为什么这么挑,他说字写得好不好是一方面,主要是看联意贴不贴。但具体什么叫贴意,他也说不太清楚,大概就是语感。

后来我意识到这个场景里其实有很多信息是丢失的。市面上卖的春联大部分没有署名、没有注释,买的人也不知道这副联有没有典故、平仄对不对、意境好不好。更不要说自己写一副——除非你有书法功底和文学积累,否则真不知道从哪下手。

在这里插入图片描述

这个小小的观察,加上之前做AR作文批改系统积累的一些经验,让我想试试用Rokid AI眼镜做一个春联相关的应用。目标很具体:让普通人戴上眼镜扫一下春联,就能立刻知道这副联好在哪、来自哪里,或者在除夕前帮全家生成一副属于自己的专属春联。

1.1 应用场景梳理

在动手之前,我把可能的使用场景想了一遍。

最直接的是街头识联。走亲戚或者逛庙会时,见到一副写得漂亮的春联,对着它眨一下眼(触发拍照),眼镜的AR视野里就叠加出这副联的文字识别结果、平仄分析、典故解读和意境评分。

其次是家庭定制联。除夕前打开应用,输入家庭关键词(比如家里有小孩考大学、老人今年七十大寿、做生意的等等),系统用大模型生成几副专属春联备选,还会给出平仄标注和写法建议。

还有一个衍生场景是寒假里帮孩子做语文作业——很多小学会布置手写春联、了解春联文化的作业,这个系统顺手就能支持。

1.2 为什么选Rokid平台

主要是两个原因。第一,我在上个项目里用过CXR-M SDK,对设备连接、拍照接口、AR自定义界面这套流程已经比较熟了,上手成本低。第二,春联这个场景需要"扫一眼就出结果"的即时感,AR眼镜的第一视角和信息叠加能力,天然比拿手机拍更顺手——你总不能走亲戚时一直端着手机对着人家门拍吧。

灵珠AI平台提供的多模态AI能力也正好能用上:图像识别走云端OCR,联意赏析和创作用大模型,TTS把结果念出来,整个链路在平台里基本都有现成接口。


二、系统架构

2.1 整体思路

系统分三层:眼镜端负责触发拍照和展示AR结果,手机端作为中间枢纽处理图像和管理会话,云端负责OCR识别和大模型分析。这个分法和上个项目基本一致,实践证明比较合理——眼镜端算力有限,重活交给手机和云端做。

考虑到春节走亲访友的场景,网络不一定稳定(尤其是在老家),我专门做了本地OCR缓存和离线词库降级方案:识别结果缓存在本地,联意赏析万一联不上云端,会退回本地的春联词库给出简化反馈。

在这里插入图片描述

2.2 核心技术栈

模块 技术选型 说明
设备通信 CXR-M SDK(蓝牙+Wi-Fi P2P) 蓝牙控制指令,Wi-Fi传图像
文字识别 云端OCR + 本地MiniOCR降级 春联竖排文字专项优化
内容分析 灵珠AI大模型(Qwen) 平仄检测、典故解析、意境评分
春联创作 提示词工程 + 格律约束生成 支持自定义家庭关键词
AR展示 CXR-M CustomView JSON配置 竖排文字渲染、典故气泡
语音交互 CXR-M TTS + ASR 听结果、语音下指令
降级方案 本地SQLite春联词库(5000条) 离线环境保证基础体验

三、核心功能实现

3.1 设备连接与初始化

这部分和上个项目差不多,但加了一个针对春节场景的优化:支持快速重连。走亲戚时眼镜可能会摘了放一会儿再戴,每次重启都要重新扫描配对体验很差,所以把上次连接的设备信息缓存下来,再次启动时优先尝试直连。

class ChuanlianManager(private val context: Context) {

    private lateinit var bluetoothHelper: BluetoothHelper
    private var lastConnectedDevice: BluetoothDevice? = null

    fun initialize() {
        requestRequiredPermissions()
        bluetoothHelper = BluetoothHelper(
            context as AppCompatActivity,
            { status -> handleBluetoothStatus(status) },
            { tryQuickReconnectOrScan() }
        )
        bluetoothHelper.checkPermissions()
    }

    // 优先尝试上次连接的设备,避免每次都重新扫描
    private fun tryQuickReconnectOrScan() {
        val savedMac = PreferenceManager.getSavedDeviceMac(context)
        if (savedMac != null) {
            val adapter = BluetoothAdapter.getDefaultAdapter()
            val device = adapter.getRemoteDevice(savedMac)
            if (device != null) {
                Log.d("Chuanlian", "找到上次配对设备,尝试直连: $savedMac")
                connectToDevice(device)
                return
            }
        }
        // 没有缓存记录,走正常扫描流程
        bluetoothHelper.startScan()
    }

    private fun connectToDevice(device: BluetoothDevice) {
        CxrApi.getInstance().initBluetooth(context, device, object : BluetoothStatusCallback {
            override fun onConnected() {
                lastConnectedDevice = device
                PreferenceManager.saveDeviceMac(context, device.address)
                Log.d("Chuanlian", "眼镜连接成功: ${device.name}")
                initWifiForImageTransfer()
            }
            override fun onDisconnected() {
                Log.w("Chuanlian", "眼镜断开,尝试重连")
                Handler(Looper.getMainLooper()).postDelayed({
                    lastConnectedDevice?.let { connectToDevice(it) }
                }, 2000)
            }
            override fun onFailed(errorCode: ValueUtil.CxrBluetoothErrorCode?) {
                Log.e("Chuanlian", "连接失败: ${errorCode?.name}")
                // 快连失败,退回扫描流程
                bluetoothHelper.startScan()
            }
            override fun onConnectionInfo(uuid: String?, mac: String?, account: String?, type: Int) {}
        })
    }

    private fun initWifiForImageTransfer() {
        if (!CxrApi.getInstance().isWifiP2PConnected) {
            CxrApi.getInstance().initWifiP2P(object : WifiP2PStatusCallback {
                override fun onConnected() { Log.d("Chuanlian", "Wi-Fi P2P就绪") }
                override fun onDisconnected() {}
                override fun onFailed(e: ValueUtil.CxrWifiErrorCode?) {}
            })
        }
    }
}

3.2 春联拍摄与竖排文字识别

春联有个特殊性——竖排书写,而且字体多样(隶书、行书、篆书都有),通用OCR在这上面表现一般。我做了几个针对性处理:图像旋转检测(有些联贴歪了)、竖排分列切割(先识别左右两栏,再各自竖向识别),以及笔画增强预处理(提升古字体识别率)。

class ChuanlianCaptureManager {

    private val photoCallback = object : PhotoResultCallback {
        override fun onPhotoResult(status: ValueUtil.CxrStatus?, photo: ByteArray?) {
            when (status) {
                ValueUtil.CxrStatus.RESPONSE_SUCCEED -> {
                    photo?.let { processChuanlianImage(it) }
                }
                ValueUtil.CxrStatus.RESPONSE_TIMEOUT -> {
                    showHint("拍照超时,请保持镜头稳定后重试")
                }
                else -> showHint("拍照异常,错误: ${status?.name}")
            }
        }
    }

    fun capture() {
        val status = CxrApi.getInstance().openGlassCamera(1920, 1080, 90)
        if (status != ValueUtil.CxrStatus.REQUEST_SUCCEED) return
        Handler(Looper.getMainLooper()).postDelayed({
            CxrApi.getInstance().takeGlassPhoto(1920, 1080, 90, photoCallback)
        }, 150)
    }

    private fun processChuanlianImage(data: ByteArray) {
        val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)

        // 步骤1: 自动纠正倾斜(部分联贴歪)
        val corrected = SkewCorrector.correctSkew(bitmap)

        // 步骤2: 竖排分列——把上联/下联区域分开识别
        val (leftCol, rightCol) = VerticalLayoutSplitter.split(corrected)

        // 步骤3: 笔画增强,改善古字体识别效果
        val enhancedLeft = StrokeEnhancer.enhance(leftCol)
        val enhancedRight = StrokeEnhancer.enhance(rightCol)

        // 步骤4: 两栏分别送OCR
        CoroutineScope(Dispatchers.IO).launch {
            val ocrResult = recognizeVerticalText(enhancedLeft, enhancedRight)
            val analysisResult = ChuanlianAIEngine.analyze(ocrResult)
            withContext(Dispatchers.Main) {
                ARChuanlianDisplay.show(ocrResult, analysisResult)
            }
        }
    }

    private suspend fun recognizeVerticalText(
        leftBitmap: Bitmap,
        rightBitmap: Bitmap
    ): ChuanlianOCRResult {
        return try {
            val localLeft = LocalVerticalOCR.recognize(leftBitmap)
            val localRight = LocalVerticalOCR.recognize(rightBitmap)
            if (localLeft.confidence > 0.8 && localRight.confidence > 0.8) {
                ChuanlianOCRResult(localLeft.text, localRight.text, source = "local")
            } else {
                CloudVerticalOCR.recognize(leftBitmap, rightBitmap)
            }
        } catch (e: Exception) {
            Log.w("Capture", "本地OCR失败,走云端", e)
            CloudVerticalOCR.recognize(leftBitmap, rightBitmap)
        }
    }
}

竖排文字识别这块踩过一个坑:直接把整张图送OCR,模型会把上联下联混在一起乱序输出。分列处理后准确率从约60%提升到85%以上,古字体场景笔画增强再能拉5个点左右。

在这里插入图片描述

3.3 AI分析引擎:从识别到赏析

OCR拿到文字之后,才是这个系统真正有意思的部分——让大模型做平仄检测、典故查找和意境分析。这里我用了灵珠AI平台提供的大模型接口,提示词工程花了不少时间调。

class ChuanlianAIEngine {

    suspend fun analyze(ocrResult: ChuanlianOCRResult): ChuanlianAnalysis {
        return withContext(Dispatchers.IO) {
            try {
                val prompt = buildAnalysisPrompt(ocrResult.upper, ocrResult.lower)
                val response = RizonAIClient.chat(prompt)
                parseAnalysisResponse(response)
            } catch (e: Exception) {
                Log.e("AIEngine", "云端分析失败,走本地词库降级", e)
                fallbackToLocalDatabase(ocrResult)
            }
        }
    }

    private fun buildAnalysisPrompt(upper: String, lower: String): String {
        // 提示词结构经过多次迭代,明确要求输出JSON避免解析麻烦
        return """
你是一位精通中国传统文化的春联鉴赏专家。请分析以下这副春联:

上联:$upper
下联:$lower

请从以下维度分析,严格以JSON格式返回,不要有多余文字:
{
  "pinze": {
    "upper_marks": "每字标注平(P)仄(Z),例如:平平仄仄平平仄",
    "lower_marks": "同上",
    "is_correct": true/false,
    "issues": "如有平仄问题,指出具体位置"
  },
  "source": "来源或出处,无法确定则填'民间传统联'",
  "allusions": [
    {"word": "涉及典故的词", "explanation": "典故说明"}
  ],
  "mood": "意境分析,100字以内",
  "score": 0-100,
  "suitable_for": ["适合张贴的场合,如:商家、书香门第、农家"]
}
        """.trimIndent()
    }

    private fun parseAnalysisResponse(raw: String): ChuanlianAnalysis {
        // 去掉可能的markdown代码块包裹
        val json = raw.replace(Regex("```json|```"), "").trim()
        return Gson().fromJson(json, ChuanlianAnalysis::class.java)
    }

    // 离线场景降级:从本地5000条春联词库里匹配最相似的联
    private fun fallbackToLocalDatabase(ocrResult: ChuanlianOCRResult): ChuanlianAnalysis {
        val match = LocalChuanlianDB.findSimilar(ocrResult.upper, ocrResult.lower)
        return if (match != null) {
            ChuanlianAnalysis(
                pinze = match.pinze,
                source = match.source,
                allusions = match.allusions,
                mood = match.mood,
                score = match.score,
                suitableFor = match.suitableFor,
                isOfflineFallback = true
            )
        } else {
            ChuanlianAnalysis(
                mood = "当前网络不可用,无法进行深度分析。春联识别完成,请手动确认文字是否正确。",
                isOfflineFallback = true
            )
        }
    }
}

3.4 专属春联创作功能

识联之外,系统还支持"定制春联"功能——把家里今年的关键词告诉AI,让它生成几副专属的春联。这个功能在除夕前用最合适,我自己试了一下,给的关键词是"儿子高考、老人健康、做餐饮生意",生成的几副里有一副确实挺贴切。

class ChuanlianCreator {

    suspend fun createChuanlian(input: CreationInput): List<ChuanlianDraft> {
        return withContext(Dispatchers.IO) {
            val prompt = buildCreationPrompt(input)
            val response = RizonAIClient.chat(prompt)
            parseCreationResponse(response)
        }
    }

    private fun buildCreationPrompt(input: CreationInput): String {
        val keywordsStr = input.keywords.joinToString("、")
        val styleHint = when (input.style) {
            ChuanlianStyle.TRADITIONAL -> "传统典雅,可引用古语"
            ChuanlianStyle.MODERN      -> "现代活泼,贴近生活"
            ChuanlianStyle.AUSPICIOUS  -> "喜庆吉祥,多用美好词汇"
        }
        return """
你是一位精通格律的春联创作大师。请根据以下信息创作3副春联:

家庭关键词:$keywordsStr
风格要求:$styleHint
字数要求:每联5-7字,上下联字数相同

格律要求:
1. 上联末字必须是仄声,下联末字必须是平声
2. 上下联字数相同,词性对仗
3. 横批2-4字,点题

以JSON数组返回,每个元素包含:
[{
  "upper": "上联",
  "lower": "下联",
  "hengpi": "横批",
  "upper_pinze": "平仄标注",
  "lower_pinze": "平仄标注",
  "explanation": "创作思路,说明如何融入关键词"
}]
        """.trimIndent()
    }

    private fun parseCreationResponse(raw: String): List<ChuanlianDraft> {
        val json = raw.replace(Regex("```json|```"), "").trim()
        val type = object : TypeToken<List<ChuanlianDraft>>() {}.type
        return Gson().fromJson(json, type)
    }
}

3.5 AR展示界面

展示这块是我花时间最多的地方。春联本身是竖排的,AR界面如果用普通横排文字展示显得很割裂。我做了一个竖排文字渲染方案——通过在JSON配置里把每个字拆成独立的TextView纵向排列,模拟竖排效果。这个方案有点笨,但在CXR-M SDK的自定义界面体系里目前是我能找到的最直接的办法。

class ARChuanlianDisplay {

    fun show(ocrResult: ChuanlianOCRResult, analysis: ChuanlianAnalysis) {
        sendCustomIcons()
        val config = buildARConfig(ocrResult, analysis)
        val status = CxrApi.getInstance().openCustomView(config)
        if (status != ValueUtil.CxrStatus.REQUEST_SUCCEED) {
            Log.e("ARDisplay", "界面打开失败: ${status?.name}")
            return
        }
        setupListener()
        speakSummary(analysis)
    }

    private fun buildARConfig(ocrResult: ChuanlianOCRResult, analysis: ChuanlianAnalysis): String {
        val upperChars = buildVerticalText(ocrResult.upper, "#FFCC0000")
        val lowerChars = buildVerticalText(ocrResult.lower, "#FFCC0000")
        val scoreColor = when {
            analysis.score >= 90 -> "#FFFF6600"
            analysis.score >= 75 -> "#FF00AA00"
            else -> "#FF888888"
        }
        return """
{
  "type": "LinearLayout",
  "props": {
    "layout_width": "match_parent",
    "layout_height": "match_parent",
    "orientation": "vertical",
    "backgroundColor": "#DD000000",
    "padding": "30dp"
  },
  "children": [
    {
      "type": "TextView",
      "props": {
        "text": "春联赏析",
        "textSize": "18sp",
        "textColor": "#FFCC0000",
        "textStyle": "bold",
        "marginBottom": "20dp"
      }
    },
    {
      "type": "LinearLayout",
      "props": {
        "orientation": "horizontal",
        "layout_width": "match_parent",
        "marginBottom": "20dp"
      },
      "children": [
        $upperChars,
        {"type": "View", "props": {"layout_width": "20dp", "layout_height": "match_parent"}},
        $lowerChars
      ]
    },
    {
      "type": "TextView",
      "props": {
        "id": "tv_score",
        "text": "评分: ${analysis.score} 来源: ${analysis.source}",
        "textSize": "14sp",
        "textColor": "$scoreColor",
        "marginBottom": "12dp"
      }
    },
    {
      "type": "TextView",
      "props": {
        "id": "tv_mood",
        "text": "${analysis.mood}",
        "textSize": "13sp",
        "textColor": "#FFDDDDDD",
        "padding": "12dp",
        "backgroundColor": "#44FFFFFF",
        "marginBottom": "12dp"
      }
    },
    {
      "type": "TextView",
      "props": {
        "id": "tv_pinze",
        "text": "平仄: ${analysis.pinze?.upper_marks} | ${analysis.pinze?.lower_marks}",
        "textSize": "12sp",
        "textColor": "#FFAAAAAA"
      }
    }
  ]
}
        """.trimIndent()
    }

    // 把一段文字拆成竖排LinearLayout(每字一个TextView纵向堆叠)
    private fun buildVerticalText(text: String, color: String): String {
        val textViews = text.map { char ->
            """
            {
              "type": "TextView",
              "props": {
                "text": "$char",
                "textSize": "22sp",
                "textColor": "$color",
                "textStyle": "bold",
                "gravity": "center"
              }
            }
            """
        }.joinToString(",")

        return """
        {
          "type": "LinearLayout",
          "props": {"orientation": "vertical", "gravity": "center_horizontal"},
          "children": [$textViews]
        }
        """
    }

    private fun speakSummary(analysis: ChuanlianAnalysis) {
        val text = if (analysis.isOfflineFallback) {
            "识别完成,当前离线模式,展示基础信息。"
        } else {
            "这副春联评分${analysis.score}分,${analysis.mood.take(30)}。" +
            if (analysis.allusions.isNotEmpty()) "典故详见屏幕说明。" else ""
        }
        CxrApi.getInstance().sendTtsContent(text)
    }

    private fun setupListener() {
        CxrApi.getInstance().setCustomViewListener(object : CustomViewListener {
            override fun onOpened()             { Log.d("ARDisplay", "春联赏析界面已打开") }
            override fun onClosed()             { Log.d("ARDisplay", "界面关闭") }
            override fun onUpdated()            {}
            override fun onOpenFailed(code: Int){ Log.e("ARDisplay", "打开失败: $code") }
            override fun onIconsSent()          {}
        })
    }
}

3.6 交互设计

交互这块尽量设计得简单,因为戴眼镜看春联这个场景本身就是在室外走动,不适合复杂操作。主要就三个动作:

AI键短按:触发拍照识联。看到想识别的春联,轻按一下就开始识别,大概2-4秒出结果。

AI键长按:激活语音命令模式。可以说"创作春联"切换到定制功能,说"读出来"让系统把完整赏析念一遍,说"关闭"退出界面。

双击触摸板:在识联结果和创作功能之间切换。

class ChuanlianInteractionManager {

    private var lastKeyDownTime = 0L

    fun setup() {
        CxrApi.getInstance().setAiEventListener(object : AiEventListener {
            override fun onAiKeyDown() {
                lastKeyDownTime = System.currentTimeMillis()
            }
            override fun onAiKeyUp() {
                val pressDuration = System.currentTimeMillis() - lastKeyDownTime
                if (pressDuration < 600) {
                    // 短按:拍照识联
                    ChuanlianCaptureManager().capture()
                } else {
                    // 长按:语音命令
                    activateVoiceCommand()
                }
            }
            override fun onAiExit() { exitSession() }
        }, true)
    }

    private fun activateVoiceCommand() {
        CxrApi.getInstance().sendTtsContent("请说:识别春联、创作春联、读出来、或关闭")
        startVoiceRecognition { command ->
            when {
                command.contains("创作") -> enterCreationMode()
                command.contains("识别") -> ChuanlianCaptureManager().capture()
                command.contains("读")   -> readCurrentAnalysis()
                command.contains("关闭") -> exitSession()
                else -> CxrApi.getInstance().sendTtsContent("未识别,请重试")
            }
        }
    }

    private fun enterCreationMode() {
        CxrApi.getInstance().sendTtsContent("请说出您的家庭关键词,例如:孩子升学、老人健康")
        startVoiceRecognition { keywords ->
            CoroutineScope(Dispatchers.IO).launch {
                val input = CreationInput(keywords.split("、"), ChuanlianStyle.TRADITIONAL)
                val drafts = ChuanlianCreator().createChuanlian(input)
                withContext(Dispatchers.Main) {
                    ARCreationDisplay.showDrafts(drafts)
                }
            }
        }
    }
}

四、实际测试与效果

4.1 识别准确率

在测试阶段,我收集了80张春联照片,覆盖印刷体、楷书、行书、隶书四种字体,以及室内(灯光充足)、室外自然光、夜晚灯笼旁三种光线条件。结果如下:

字体类型 室内识别率 室外自然光 夜间弱光
印刷体春联 97% 94% 89%
规范楷书 91% 87% 81%
行书/草书 76% 71% 65%
隶书/篆书 68% 63% 58%

行书和篆书的识别率偏低是意料之中的,这类字体变体太多,OCR训练数据也少。目前的处理方式是:置信度低于70%时,在AR界面提示用户"识别置信度较低,请确认文字是否正确",并提供手动输入修正的入口。

在这里插入图片描述

4.2 典型使用场景回顾

实际拿去用了几次,说几个印象深的场景。

有一次在邻居门口看到一副"春风送暖千家乐,瑞雪兆丰万里春",系统识别出来之后,AI指出这是一副典型的民间通用联,平仄基本工整,但"千家乐"和"万里春"的对仗稍有瑕疵("千家"对"万里"为数词对,但"乐"与"春"词性不完全对称)。这种细节人工很难注意到,系统点出来之后确实涨了知识。

另一次是帮堂弟家定制春联——他们家今年刚开了家小餐馆,关键词输了"开业大吉、美食、家常",系统生成了三副,其中一副"灶台飘香迎四海,菜香留客共团圆",横批"味满人间",堂弟说挺满意,最后就用这副了。

当然也有翻车的时候。一副写得比较草的行书,系统把"福"字识别成了"祝",导致整副联的分析都跑偏了。这类问题目前靠手动修正兜底,后续准备在AR界面加一个"纠正识别"的快捷入口。


五、一些想法和后续计划

做完这个项目,我对"AR眼镜在传统文化场景里能做什么"这个问题有了一些新的理解。春联这个东西其实非常适合AR:它是贴在固定位置的,你扫一眼是完全自然的动作,不需要额外举起手机;信息的叠加也不打扰你本来的体验,看完联意分析,抬眼还是那副红纸黑字。

后续我想做几件事:第一是扩展到其他春节场景,比如庙会里的灯谜(识别灯谜→AI给提示→语音念谜底),或者年画的文化背景解读;第二是支持书法辅助模式,把手写春联也拍进来,不只是评联意,还能评笔画结构;第三是做一个春联数据库的建设,把各地特色联收录进去,让离线体验也足够丰富。

现在这个版本还比较粗糙,尤其是竖排文字在AR里的渲染方案,拆成单字堆叠的方式在字数多的时候有点难看。后续考虑用Canvas自绘竖排文字流,绕过SDK自定义界面的限制。代码我也在整理,准备把竖排OCR处理和春联分析提示词这两块单独出去,希望对做类似场景的开发者有参考。

在这里插入图片描述

最后说一句——我爸今年春节用这个系统在街上扫了一路,回家跟人显摆"这副联平仄有问题",说得头头是道。我觉得这大概就是这个应用存在的意义。


六、总结

本文介绍了基于Rokid CXR-M SDK和灵珠AI平台开发的春联识别与赏析系统,覆盖了竖排文字拍摄与识别、大模型平仄/典故分析、AR结果展示和语音交互四个核心模块,同时针对春节户外使用场景设计了离线降级方案和快速重连机制。系统目前已在实际春节场景中使用验证,整体功能可用,在印刷体和规范楷书的识别上效果较好。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐