嵌入式多功能浏览器系统设计详解
在学习了伟东山的完整嵌入式相机流程之后,将文本阅读器和人脸识别内容添加进去。这是完成后的整体框架,代码会开源在gitee。https://gitee.com/huayi1234/complete-browser
1. 系统整体架构
本项目是一个基于Linux系统的嵌入式多功能浏览器,主要用于图像浏览、文本阅读和人脸识别等功能。系统采用严格的分层模块化设计,使各功能模块间解耦合,便于扩展和维护。程序运行前要export TSLIB_TSDEVICE=/dev/input/event1(对于伟东山Imx6ull)。
1.1 核心架构层次
系统采用三层架构设计:
1.1.1 底层硬件驱动层
- 显示驱动:基于Linux FrameBuffer(/dev/fb0),实现像素级操作
- 输入设备驱动:支持触摸屏(/dev/input/eventX)和标准输入设备
- 文件系统驱动:提供对文件系统的统一访问接口
1.1.2 中间层管理模块
- 显示管理模块:管理显示缓冲区和渲染操作
- 输入管理模块:统一处理多种输入事件
- 编码管理模块:处理不同字符编码格式
- 字体管理模块:基于FreeType引擎,处理字体渲染
- 图片格式管理模块:解析并处理多种图片格式
- 调试管理模块:提供日志和调试支持
1.1.3 应用层功能模块
- 页面管理模块:实现多页面切换和交互
- 渲染模块:处理图像渲染和变换
- 文本阅读器模块:解析和显示文本内容
- 人脸识别模块:基于OpenCV实现人脸检测功能
1.2 系统数据流
输入设备 → 输入管理器 → 页面管理器 → 渲染引擎 → 显示管理器 → 显示设备↑↓文件系统 ← 格式解析器(图片/文本)
2. 详细设计模式与实现方法
2.1 注册-管理器模式
所有功能模块都采用了统一的注册机制,实现了模块的可插拔性。
2.1.1 注册机制代码示例
// 显示设备注册
int RegisterDispOpr(PT_DispOpr ptDispOpr)
{PT_DispOpr ptTmp;if (!g_ptDispOprHead){g_ptDispOprHead = ptDispOpr;ptDispOpr->ptNext = NULL;}else{ptTmp = g_ptDispOprHead;while (ptTmp->ptNext){ptTmp = ptTmp->ptNext;}ptTmp->ptNext = ptDispOpr;ptDispOpr->ptNext = NULL;}return 0;
}
2.2 工厂模式
通过工厂模式创建不同类型的对象,如显示设备、输入设备等。
2.2.1 工厂模式代码示例
PT_VideoMem GetVideoMem(int iID, int bCur)
{PT_VideoMem ptRetVideoMem = NULL;/* 1. 在链表中查找是否有iID对应的VideoMem */ptRetVideoMem = GetVideoMemDevID(iID);if (ptRetVideoMem){return ptRetVideoMem;}/* 2. 如果没有找到, 则取一个空闲的VideoMem */ptRetVideoMem = GetVideoMemFreq();if (ptRetVideoMem){ptRetVideoMem->iID = iID;return ptRetVideoMem;}/* 3. 如果前面都失败了, 则取一个VideoMem(不管它是否空闲) */if (bCur){ptRetVideoMem = g_ptVideoMemHead;ptRetVideoMem->iID = iID;return ptRetVideoMem;}return NULL;
}
2.3 链表管理结构
使用链表管理同类型对象,便于动态添加和移除。
2.3.1 链表管理代码结构示例
typedef struct VideoMem {int iID; /* ID值,用于标识不同的页面 */int bDevFrameBuffer; /* 是否是显示设备的显存 */E_VideoMemState eVideoMemState; /* VideoMem的状态 */E_PicState ePicState; /* 图片状态 */T_PixelDatas tPixelDatas; /* 图像数据 */struct VideoMem *ptNext; /* 链表指针 */
} T_VideoMem, *PT_VideoMem;
2.4 缓冲区管理机制
实现了高效的显示缓冲区管理,通过三种状态(空闲/预备/当前使用)提高显示性能。
2.4.1 缓冲区管理核心函数
int AllocVideoMem(int iNum)
{int i;int iXres, iYres, iBpp;int iVMSize;int iLineBytes;PT_VideoMem ptNew;GetDispResolution(&iXres, &iYres, &iBpp);iLineBytes = iXres * iBpp / 8;iVMSize = iLineBytes * iYres;/* 先把设备本身的framebuffer缓冲区用于VideoMem */ptNew = malloc(sizeof(T_VideoMem));if (ptNew == NULL){return -1;}/* 注册显示设备的显存 */ptNew->tPixelDatas.aucPixelDatas = g_ptDispOprDefault->pucDispMem;ptNew->iID = 0;ptNew->bDevFrameBuffer = 1;ptNew->eVideoMemState = VMS_FREE;ptNew->ePicState = PS_BLANK;ptNew->tPixelDatas.iWidth = iXres;ptNew->tPixelDatas.iHeight = iYres;ptNew->tPixelDatas.iBpp = iBpp;ptNew->tPixelDatas.iLineBytes = iLineBytes;ptNew->tPixelDatas.iTotalBytes = iVMSize;/* 添加到链表 */ptNew->ptNext = g_ptVideoMemHead;g_ptVideoMemHead = ptNew;/* 分配用于缓存的VideoMem */for (i = 0; i < iNum; i++){ptNew = malloc(sizeof(T_VideoMem));if (ptNew == NULL){return -1;}ptNew->tPixelDatas.aucPixelDatas = malloc(iVMSize);if (ptNew->tPixelDatas.aucPixelDatas == NULL){free(ptNew);return -1;}ptNew->iID = 0;ptNew->bDevFrameBuffer = 0;ptNew->eVideoMemState = VMS_FREE;ptNew->ePicState = PS_BLANK;ptNew->tPixelDatas.iWidth = iXres;ptNew->tPixelDatas.iHeight = iYres;ptNew->tPixelDatas.iBpp = iBpp;ptNew->tPixelDatas.iLineBytes = iLineBytes;ptNew->tPixelDatas.iTotalBytes = iVMSize;ptNew->ptNext = g_ptVideoMemHead;g_ptVideoMemHead = ptNew;}return 0;
}
2.5 事件驱动模型
通过输入设备获取用户操作,通过事件驱动系统响应。
2.5.1 事件处理核心代码
static void InputEventCallback(PT_InputEvent ptInputEvent)
{/* 触摸屏事件 */if (ptInputEvent->iType == INPUT_TYPE_TOUCHSCREEN){/* 处理触摸屏事件 */gptCurPage->OnInputEvent(ptInputEvent);}else if (ptInputEvent->iType == INPUT_TYPE_KEY){/* 处理按键事件 */gptCurPage->OnInputEvent(ptInputEvent);}
}
3. 系统初始化与运行流程
3.1 详细初始化流程
系统初始化分为九个主要步骤,每个步骤负责特定模块的初始化:
int main(int argc, char **argv)
{ /* 1. 初始化调试系统 */DebugInit();InitDebugChanel();SetDbgLevel("dbglevel=1");SetDbgChanel("netprint=0");/* 2. 初始化显示系统 */DisplayInit();SelectAndInitDefaultDispDev("fb");AllocVideoMem(5);/* 3. 初始化输入系统 */InputInit();AllInputDevicesInit();/* 4. 初始化编码系统 */EncodingInit();/* 5. 初始化字体系统 */FontsInit();SetFontsDetail("freetype", argv[1], 16);/* 6. 初始化图片处理系统 */PicFmtsInit();/* 7. 初始化文本阅读器 */TextReaderInit();/* 8. 初始化页面系统 */PagesInit();/* 9. 运行主页面 */Page("main")->Run(NULL);return 0;
}
3.2 页面切换与运行机制
系统采用页面管理器统一管理所有页面,通过页面对象的Run方法启动页面,每个页面包含自己的初始化、运行和退出逻辑。
int MainPageRun(PT_PageParams ptPageParams)
{int iIndex;T_InputEvent tInputEvent;int bPressed = 0;int iRet;MainPageLayout();/* 注册触摸屏按下/松开时的处理函数 */iRet = RegisterInputEventHook(&g_tMainPageInputEventHook);while (1){iRet = GetInputEvent(&tInputEvent);if (iRet){/* 获取到输入事件 */if (tInputEvent.iType == INPUT_TYPE_TOUCHSCREEN){/* 处理触摸屏事件 */if (tInputEvent.iPressure){/* 按下 */bPressed = 1;iIndex = GetMainPageLayoutIndex(tInputEvent.iX, tInputEvent.iY);}else{/* 松开 */if (bPressed){bPressed = 0;iIndex = GetMainPageLayoutIndex(tInputEvent.iX, tInputEvent.iY);if (iIndex != -1){MainPageDispSpecialIcon(iIndex);MainPageRunCommand(iIndex);}}}}else if (tInputEvent.iType == INPUT_TYPE_KEY){/* 处理按键事件 */if (tInputEvent.iVal == INPUT_KEY_EXIT){break;}}}}UnregisterInputEventHook(&g_tMainPageInputEventHook);return 0;
}
4. 功能模块详细说明
4.1 显示管理模块
显示管理模块负责管理显示设备和显示缓冲区,提供像素级操作接口。
4.1.1 关键数据结构
typedef struct DispOpr {char *name; /* 显示模块名字 */int iXres; /* X分辨率 */int iYres; /* Y分辨率 */int iBpp; /* 每像素位数 */int iLineWidth; /* 一行数据字节数 */unsigned char *pucDispMem; /* 显存地址 */int (*DeviceInit)(void); /* 设备初始化函数 */int (*ShowPixel)(int iPenX, int iPenY, unsigned int dwColor);int (*CleanScreen)(unsigned int dwBackColor);int (*ShowPage)(PT_VideoMem ptVideoMem);struct DispOpr *ptNext;
} T_DispOpr, *PT_DispOpr;
4.1.2 核心功能函数
- DisplayInit(): 初始化显示系统
- SelectAndInitDefaultDispDev(): 选择并初始化默认显示设备
- GetVideoMem(): 获取显示缓冲区
- PutVideoMem(): 释放显示缓冲区
- ClearVideoMem(): 清空显示缓冲区
- FlushVideoMemToDev(): 将缓冲区内容刷新到设备
4.2 输入管理模块
输入管理模块统一管理触摸屏和按键等输入设备,提供事件驱动机制。
4.2.1 关键数据结构
typedef struct InputEvent {unsigned int time; /* 触发事件的时间 */int iType; /* 事件类型 */int iX; /* X坐标 */int iY; /* Y坐标 */int iPressure; /* 压力值 */int iKey; /* 按键值 */
} T_InputEvent, *PT_InputEvent;
4.2.2 核心功能函数
- InputInit(): 初始化输入系统
- RegisterInputOpr(): 注册输入设备
- AllInputDevicesInit(): 初始化所有输入设备
- GetInputEvent(): 获取输入事件
- RegisterInputEventHook(): 注册输入事件钩子
4.3 编码管理模块
编码管理模块处理不同字符编码格式,支持ASCII、UTF-8、UTF-16BE、UTF-16LE等。
4.3.1 核心功能函数
- EncodingInit(): 初始化编码系统
- RegisterEncodingOpr(): 注册编码处理模块
- GetCodeFrmBuf(): 从缓冲区获取字符编码
- GetStrParseEncoder(): 获取字符串解析编码器
4.4 字体管理模块
字体管理模块处理字体渲染,支持ASCII、GBK等字符集,集成FreeType引擎。
4.4.1 核心功能函数
- FontsInit(): 初始化字体系统
- RegisterFontOpr(): 注册字体操作
- SetFontsDetail(): 设置字体细节
- GetFontBitmap(): 获取字体位图
4.5 页面管理模块
页面管理模块实现多页面切换和交互,包括主页面、浏览页面、设置页面等。
4.5.1 关键数据结构
typedef struct PageOpr {char *name; /* 页面名称 */void (*Run)(PT_PageParams ptPageParams); /* 运行函数 */struct PageOpr *ptNext; /* 链表指针 */
} T_PageOpr, *PT_PageOpr;
4.5.2 核心功能函数
- PagesInit(): 初始化页面系统
- RegisterPage(): 注册页面
- Page(): 获取页面对象
- MainPageRun(): 主页面运行函数
- BrowsePageRun(): 浏览页面运行函数
- TextPageRun(): 文本页面运行函数
4.6 渲染模块
渲染模块处理图像渲染和变换,支持BMP、JPG等图片格式。
4.6.1 核心功能函数
- PicZoom(): 图片缩放
- PicMerge(): 图片合并
- PicRotate(): 图片旋转
- GetPixelDatasForBmp(): 获取BMP图片数据
- GetPixelDatasForJpg(): 获取JPG图片数据
4.7 文本阅读器模块
文本阅读器模块解析和显示文本内容,支持多种文本文件格式。
4.7.1 核心功能函数
- TextReaderInit(): 初始化文本阅读器
- ParseTextFile(): 解析文本文件
- ShowNextPage(): 显示下一页
- ShowPrePage(): 显示上一页
4.8 人脸识别模块
基于OpenCV实现的人脸检测功能,支持实时人脸识别并标记。
4.8.1 关键功能实现
# 人脸检测核心代码
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
faces = face_cascade.detectMultiScale(gray_frame,scaleFactor=1.1,minNeighbors=3,minSize=(15, 15),flags=cv2.CASCADE_SCALE_IMAGE
)# 标记识别到的人脸
for (x, y, w, h) in faces:cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2)cv2.rectangle(frame, (x, y+h-label_h), (x+w, y+h), (0, 0, 255), cv2.FILLED)font = cv2.FONT_HERSHEY_DUPLEXcv2.putText(frame, "Face", (x+6, y+h-6), font, font_scale, (255, 255, 255), 1)
5. 系统扩展性设计
5.1 模块扩展机制
系统采用注册机制,使所有功能模块都可以方便地进行扩展:
/* 注册新的显示设备 */
int NewDisplayDeviceInit(void)
{PT_DispOpr ptNewOpr;ptNewOpr = malloc(sizeof(T_DispOpr));if (!ptNewOpr)return -1;ptNewOpr->name = "new_display";ptNewOpr->DeviceInit = NewDisplayInit;ptNewOpr->ShowPixel = NewDisplayShowPixel;ptNewOpr->CleanScreen = NewDisplayCleanScreen;ptNewOpr->ShowPage = NewDisplayShowPage;return RegisterDispOpr(ptNewOpr);
}
5.2 多格式支持扩展
编码管理器和图片格式管理器可以轻松扩展支持更多格式:
/* 注册新的图片格式 */
int NewPicFormatInit(void)
{PT_PicFileParser ptNewParser;ptNewParser = malloc(sizeof(T_PicFileParser));if (!ptNewParser)return -1;ptNewParser->name = "new_format";ptNewParser->isSupport = NewFormatIsSupport;ptNewParser->GetPixelDatas = NewFormatGetPixelDatas;ptNewParser->FreePixelDatas = NewFormatFreePixelDatas;return RegisterPicFileParser(ptNewParser);
}
5.3 多页面支持扩展
页面系统设计灵活,可以方便地增加新页面:
int NewPageInit(void)
{PT_PageOpr ptNewPageOpr;ptNewPageOpr = malloc(sizeof(T_PageOpr));if (!ptNewPageOpr)return -1;ptNewPageOpr->name = "new_page";ptNewPageOpr->Run = NewPageRun;return RegisterPage(ptNewPageOpr);
}
6. 系统性能与优化
6.1 显示性能优化
- 使用VideoMem多级缓冲机制提高显示性能
- 按需加载和渲染图像内容
- 实现图像缩放和预处理以适应不同分辨率
6.2 内存管理优化
- 使用内存池技术减少内存碎片
- 资源使用完毕立即释放
- 大型数据使用引用计数管理
6.3 图像处理优化
- 图像处理使用缩放后处理提高速度
- 图像处理采用分块渲染减少内存占用
- 实现渐进式图像加载
7. 设计难点与解决方案
7.1 显示性能瓶颈
难点:在嵌入式系统上,显示性能是一个主要瓶颈。
解决方案:
- 实现了VideoMem多级缓冲机制
- 预先构建显示页面数据
- 按需渲染可见区域
7.2 多格式兼容问题
难点:支持多种文本编码和图片格式。
解决方案:
- 设计通用的编码管理器
- 实现插件式的格式解析器
- 自动检测和转换编码格式
7.3 资源受限环境优化
难点:在资源有限的嵌入式系统上高效运行。
解决方案:
- 内存池和资源缓存管理
- 延迟加载和提前释放资源
- 根据系统配置动态调整性能参数
7.4 人脸识别性能与准确性平衡
难点:实现高效且准确的人脸识别功能。
解决方案:
- 多级缩放处理提高性能
- 使用直方图均衡化提高准确率
- 实现帧间隔处理减少CPU占用
8. 结论与应用场景
本项目是一个功能完备的嵌入式浏览器系统,采用模块化设计和分层架构,具有良好的扩展性和可维护性。系统不仅支持基本的图像浏览和文本阅读功能,还集成了人脸识别等高级特性,可应用于多种嵌入式设备,如信息亭、工业控制面板、智能家居控制器等场景。
通过抽象硬件接口,系统实现了对底层硬件的良好封装,使上层应用可以专注于业务逻辑实现,同时保持了系统的可扩展性和可移植性。