基于Rokid AI眼镜的春节春联智能赏析与创作系统
春节期间,走在街头巷尾,家家户户门上贴的春联其实挺有意思——红纸金字,浓缩了一家人对新年的期许。但大多数人看春联都是走马观花,不知道平仄、不懂典故,更别说自己动手写一副了。这个项目想做的事情很朴素:用Rokid AI眼镜扫一眼门口的春联,系统就能在AR视野里告诉你这副联的出处、意境和赏析,还能帮你根据家庭情况生成一副专属的新年联。整个系统基于Rokid灵珠AI平台和CXR-M SDK开发,核心能力
文章目录
摘要
春节期间,走在街头巷尾,家家户户门上贴的春联其实挺有意思——红纸金字,浓缩了一家人对新年的期许。但大多数人看春联都是走马观花,不知道平仄、不懂典故,更别说自己动手写一副了。这个项目想做的事情很朴素:用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结果展示和语音交互四个核心模块,同时针对春节户外使用场景设计了离线降级方案和快速重连机制。系统目前已在实际春节场景中使用验证,整体功能可用,在印刷体和规范楷书的识别上效果较好。
更多推荐


所有评论(0)