基于ARM9的摄像头采集分析颜色的系统设计
一、项目简介
1.1实验目标重述
ARM架构的微处理器在追求低成本、低功耗和高性能的嵌入式系统领域占有主导地位,学习ARM微处理器的嵌入式系统设计对于未来的学习与实践都有重要的意义。本次实验,主要是基于KDLAB-I 嵌入式系统实验平台(核心为ARM9架构的S3C2440微处理器),在Linux系统下进行设计与编程,利用OV9650摄像头实现摄像、采集图片、分析图片、识别颜色的功能。
九层之台,起于垒土,整个实验跨度达两周,具体言之,又可以细分为以下几个目标;
1、 熟悉Linux虚拟机的安装和Linux系统的使用,掌握常见的命令,熟悉Linux系统下的操作逻辑;
2、 了解ARM嵌入式Linux系统的开发过程,掌握修改、编写、烧 写应用程序和驱动程序的方法;
3、 掌握嵌入式Linux系统开发交叉编译环境的建立和使用;
4、 熟悉实验台上常用外设的使用方法和连接方式;
5、 熟练掌握摄像头的应用程序的编写和调试;
6、 熟悉RGB-565、RGB-888和HSI等常见颜色空间的转换以及编程的实现;
7、 掌握位图的保存格式、数据格式和保存方法,并在Linux下编程实现;
8、 掌握八邻域生长算法的算法核心和编程实现,并能够在嵌入式 Linux系统中实现对拍摄图片的正确连通;
9、 熟悉嵌入式系统的编程和调试方法,学会利用输出来进行调试;
1.2作品功能分析
1、 摄像头能够实现实时监视功能,将拍摄到的图像实时显示在 LCD屏幕上;
2、 具备截图功能,能够定时或根据输入保存当时的实时画面并另存为图片;
3、 具备分析功能,能够判别图中是否存在面积达到一定大小的红色色块,并判定其形心和半径;
4、 具备结果可视化功能,可以将图片处理为除原图红色区域外均为黑色的图形,更加直观;
二、功能流程图
实验的功能可以用流程图表示如下:
图 1. 系统流程图
三、实验过程
3.1准备:Linux平台下ARM嵌入式编程基本操作
主要包括基本文件操作、定制与修改内核、给开发板烧写定制的嵌入式Linux系统以及虚拟机与ARM9开发板的通讯。
其中,烧写系统或许是最重要的一项技能了,因为在实验的过程中几乎每天都会遇到莫名其妙的故障,甚至连重启都解决不了(比如无限自动重启),这时候我就采取了最笨的办法——重新安装系统。
3.2例程学习——camtest.cpp
3.2.1 Trect类
例程将TRect用作屏幕显示的基类,定义了图像存储的起始地址Addr, 图像的尺寸Size、宽度Width、高度 Height、每行图像存储时占 用的字节数LineLen以及图像的位数BPP,并声明和定义了将缓存数据显示到屏幕上的DrawRect函数和清屏函数Clear。在这次实验中,完成bmp图像的存储和图像分析功能的SavePic函数都是通过在DrawRect基础上加以修改来实现的。
3.2.2 TFrameBuffer类
TFrameBuffer作为TRect类的派生类,主要作为缓存,将 TVideo存储的图片信息通过映射放到内存中,便于以后的直接读 写,也可以少用read()和write()这类读写函数,加快读写速度,其他部分没有太大的改动。
3.2.3 TVideo类
TVideo同样是TRect类的派生类,这个派生类主要负责将摄像头拍摄到的图像存储在缓存区中,以待TFrameBuffer转存和显示。
3.2.4 主函数
Camtest的主函数比较简单,短短的几行却完成了很多事情,定义了一个TFrameBuffer对象和一个TVideo对象,调用FetchPicture函数存储图像之后,再调用DrawRect将图像显示到屏幕上。使用for结构无限循环此动作,并在最外层使用try-catch的结构来捕捉意外动作。
3.3 双线程
3.3.1 需求分析
最初并没有打算使用双线程,但是在仔细分析之后,如果需要加入按键来决定是否采集分析图像,那么久不能使用单线程,否则将会出现用户不按键,系统就一直等待的现象,这显然不是一个合格的系统所应有的样子。所以还是硬着头皮使用了双线程。一个线程负责键盘的检测,另一个线程负责实时拍摄和显示功能,并且,没有设置互斥量进行交互以保证完全的实时运行,设置了一个信息量以负责两个线程之间的信息传递。
3.3.2 双线程例程pthread学习
在双线程的例程pthread中,首先声明了互斥量pthread_tmutex,之后声明了主线程writer_function和子线程reader_function,在主函数中创建了子线程的进程,而主函数则运行主线程,两者通过互斥量进行交互,分别作为生产者和消费者增加和减少虚拟的缓存buffer_has_item,并在PC终端上对剩余的缓存产品进行显示。
3.3.3 双线程设计
基于之前的分析,将双线程例程中的互斥量去掉以保证两个线程能够完全独立地工作。当然在Makefile中还要在编译语句的最后加上-lpthread,把多线程的库链接上。在两个独立的线程中进一步添加功能。
3.4 颜色识别
3.4.1 什么是颜色空间
颜色通常用三个相对独立的属性来描述,三个独立变量综合作用,自然就构成一个空间坐标,这就是颜色空间。而颜色可以由不同的角度,用三个一组的不同属性加以描述,就产生了不同的颜色空间。虽然被描述的对象颜色是客观存在的,但是很明显这是多指标非线性的,由于不同场合的需求不同,因此不同的颜色空间仍有存在的必要。颜色空间按照基本结构可以分两大类:基色颜色空间和色、亮分离颜色空间。前者的典型是RGB;后者包括 YCC/YUV、Lab、以及一批"色相类颜色空间"。
3.4.2 RGB颜色空间
RGB颜色空间是一种大的分类,具体而言RGB空间还包含多种空间,其中sRGB是HP和Microsoft联合制定的标准RGB空间,除此之外还有Adobe RGB,Apple RGB,ColorMatch RGB等等,他们通过不同的方式表示RGB三种颜色,使得它们具有不同的色彩宽度,GAMMA值也是不一样的,但是在此处并不需要多加讨论。
本实验平台上摄像头OV9650回传的数据是RGB565格式的,相对RGB888格式(俗称24位真彩色)牺牲了一定的信息量,由于在想HSI空间转换的时候需要全部的RGB信息,因此还需要转化到RGB888格式,这其中更还涉及到补偿,会在后面叙述。
3.4.3 HIS颜色空间
HSI模型属于第二类色亮分离的颜色空间,其建立基于两个重要的事实:
- I分量与图像的彩色信息无关;
- H和S分量与人感受颜色的方式是紧密相联的。这些特点使得HSI模型非常适合彩色特性检测与分析。
HSI模型是美国色彩学家孟塞尔(H.A.Munseu)于1915年提出的,它反映了人的视觉系统感知彩色的方式,用色调(Hue)、色饱和度(Saturation或Chroma)和亮度 (Intensity或Brightness)来描述色彩其中,色调与光波的波长有关,它表示人的感官对不同颜色的感受,如红色、绿色、蓝色等,它也可表示一定范围的颜色,如暖色、冷色等。饱和度 表示颜色的纯度,纯光谱色是完全饱和的,加入白光会稀释饱和度。饱和度越大,颜色看起来就会越鲜艳,反之亦然。亮度 对应成像亮度和图像灰度,是颜色的明亮程度。HSI色彩空间可以用一个圆锥空间模型来描述。虽然这种圆锥模型相当复杂,但确能把色调、亮度和色饱和度的变化情形表现得很清楚。为了便于色彩处理和识别,人的视觉系统经常采用HSI色彩空间, 它比RGB色彩空间更符合人的视觉特性。在图像处理和计算机视觉中大量算法都可在HSI色彩空间中方便地使用,它们可以分开处理而且是相互独立的。因此,在HSI色彩空间可以大大简化图像分析和处理的工作量。HSI色彩空间和RGB色彩空间只是同一物理量的不同表示法,因而它们之间存在着转换关系。
3.4.4 RGB565转换到RGB888
将RGB565格式转换为RGB888以进行到HIS颜色空间的转换的必要性之前已经讲过。我们知道RGB565格式分别保存了色彩的RGB分量的高5位、6位、5位,那么,亟需做的一件事情就是决定如何添加低位信息,最直接的想法是低位补零,但是实践表明效果并不好,现在被广泛采用是是一种称为量化补偿的方法,有三个步骤:
1. 将原数据填充至高位
2. 对于低位,用原始数据的低位进行补偿
3. 如果仍然有未填充的位,继续使用原始数据的低位进行循环补偿
解释一下循环补偿的概念:
3.4.5 RGB颜色空间到HSI颜色空间的转换
RGB 向HSI 模型的转换是由一个基于笛卡尔直角坐标系的单位立方体向基于圆柱极坐标的双锥体的转换。基本要求是将RGB 中的亮度因素分离,将色度分解为色调和饱和度,并用角向量表示色调,如图 4所示。
图 4. RGB与HSI
3.5 图像识别——区域增长算法
3.5.1 区域增长算法
为了识别所采集的图像中是否存在红色区域,在老师的指导下,查阅相关资料,得知需要使用区域生长算法。
区域生长方法是根据同一物体区域内象素的相似性质来聚集象素点的方法,从初始区域(如小邻域或甚至于每个象素)开始,将相邻的具有同样性质的象素或其它区域归并到目前的区域中从而逐步增长区域,直至没有可以归并的点或其它小区域为止。区域内象素的相似性度量可以包括平均灰度值、纹理、颜色等信息。
区域生长方法是一种比较普遍的方法,算法思想简单、普适性较好,在没有先验知识可以利用时,可以取得最佳的性能,可以用来分割比较复杂的图象,如自然景物。同时,其生长过程中生长准则可以自由指定。但是,区域增长方法是一种迭代的方法,空间和时间开销都比较大。
区域生长的基本思想是将具有相似性质的像素集合起来构成区域。具体先对每个需要分割的区域找一个种子像素作为生长起点,然后将种子像素和周围邻域中与种子像素有相同或相似性质的像素(根据某种事先确定的生长或相似准则来判定)合并到种子像素所在的区域中。将这些新像素当作新的种子继续上面的过程,直到没有满足条件的像素可被包括进来。这样一个区域就生长成了。(算法流程如图 5所示
图 5. 八邻域算法流程
四、系统功能测试
经测试,系统各项功能均可正常执行。(缺图若干)
五、实验收获与心得
5.1 存储对齐方式
通常情况下,存储器中字节对齐有两种方式:小端对齐(Little Endian,将低序字节存储在起始地址)和大端对齐(Big Endian,将低序字节存储在起始地址)。在本实验平台上采用的是小端对齐,因此在存储和操作的数据的时候要,Data[2i+1]是高位而Data[2i]是低位,如果不注意这个问题,就会导致颜色错位,例如下面这个(本意是将图片红色之外的部分全部做成黑色的)(缺图):
5.2 调试小结
5.2.1 编译C++报错
最开始编译.cpp文件的时候,有时候会出现类似"undefined reference to `std::ios_base::Init::~Init()"之类的错误,经搜索可能是由于没有添加C++标准库的原因。修改命令如下即可编译:
$ gcc -Wall hello.cpp -o hello -lstdc++
5.2.2 Segmentation Fault
一般是内存错误,一开始自己并没有检查出来,后来经老师帮忙找出了报错的原因是没有对指针赋初值。修改一下即可。
5.3 优化程序
5.3.1 使用宏
使用宏之后,在编译时编译器会直接将相应代码嵌入相应位置,这样比调用函数来讲要更加节省资源。例如在取RGB三原色中最大值的时候就使用了宏。
#define MAX(a,b,c) ((a>b)?((a>c)?a:c):((b>c)?b:c))
这里使用了三目运算符的嵌套,使得程序的逻辑和效率都有了提高。
5.4 收获
最需要感谢的是让我们可以重新选择做ARM实验的徐老师和一直指导我们的肖老师,没有肖老师的帮助,我是不可能这么快完成任务的。
其次,最大的收获就是知道了做事要循序渐进,困难总能克服。
六、代码
#include <iostream>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <linux/types.h>
#include <linux/fb.h>
#include <time.h>
#include <getopt.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include <stddef.h>
#define MAX(a,b,c) ((a>b)?((a>c)?a:c):((b>c)?b:c))
#define MIN(a,b,c) ((a<b)?((a<c)?a:c):((b<c)?b:c))
static char path[50];
static int picno = 0;//Used to count the pics
static int red = 0;
static bool RED[480*640];
static bool table[480*640];
static unsigned char *Start=NULL;
static int buffer_has_item=0;
static int SIZE=0;
static double xcenter=0;
static double ycenter=0;
static double xcentermax=0;
static double ycentermax=0;
static double countc=0;
static double countrec=0;
static double deep=0;
static double deepth=0;
static unsigned char * BMPdata=NULL;
void RegionGrow(bool * pUnRegion, bool * hmatrix, int x, int y)
{
static int nDx[]={-1,0,1,1,1,0,-1,-1,-1};
static int nDy[]={1,1,1,0,-1,-1,-1,0,1};
// 图象的长宽大小
int nWidth = 640;
int nHeight = 480;
// 定义堆栈,存储坐标
int *pnGrowQueX ;
int *pnGrowQueY ;
// 分配空间
pnGrowQueX = (int*) malloc(nWidth*nHeight*sizeof(int));
pnGrowQueY = (int*) malloc(nWidth*nHeight*sizeof(int));
// 定义堆栈的起点和终点
// 当nStart=nEnd, 表示堆栈中只有一个点
int nStart ;
int nEnd ;
//初始化
nStart = 0 ;
nEnd = 0 ;
deep=0;
// 把种子点的坐标压入栈
pnGrowQueX[nEnd] = x;
pnGrowQueY[nEnd] = y;
// 当前正在处理的象素
int nCurrX ;
int nCurrY ;
// 循环控制变量
int k ;
// 图象的横纵坐标,用来对当前象素的8邻域进行遍历
int xx;
int yy;
while (nStart<=nEnd)
{
// 当前种子点的坐标
nCurrX = pnGrowQueX[nStart];
nCurrY = pnGrowQueY[nStart];
// 对当前点的8邻域进行遍历
for (k=0; k<8; k++)
{
// 4邻域象素的坐标
xx = nCurrX+nDx[k];
yy = nCurrY+nDy[k];
// 判断象素(xx,yy) 是否在图像内部
// 判断象素(xx,yy) 是否已经处理过
if ( (xx < nWidth) && (xx>=0) && (yy<nHeight) && (yy>=0)
&& (pUnRegion[yy*nWidth+xx]==0) && hmatrix[yy*nWidth+xx]==1)
{
// 堆栈的尾部指针后移一位
nEnd++;
deep++;
xcenter+=nCurrX;
ycenter+=nCurrY;
countc++;
// 象素(xx,yy) 压入栈
pnGrowQueX[nEnd] = xx;
pnGrowQueY[nEnd] = yy;
// 把象素(xx,yy)设置成逻辑1(255)
// 同时也表明该象素处理过
pUnRegion[yy*nWidth+xx] =1;
}
}
nStart++;
}
// 释放内存
free(pnGrowQueX);
free(pnGrowQueY);
pnGrowQueX = NULL ;
pnGrowQueY = NULL ;
if(deep>deepth) deepth=deep;
}
void rgb2hsi(unsigned char* Data, int row)
{
unsigned char r,g,b;
int h;
for(int i=0;i<1280;i+=2)
{
r=(Data[i+1]&0xf8);
g=(Data[i+1]<<5)|((Data[i]&0xe0)>>3);
b=(Data[i]<<3);
if((MAX(r,g,b)==r)&&(MAX(r,g,b)!=MIN(r,g,b)))
{
h=60*(g-b)/(MAX(r,g,b)-MIN(r,g,b));
}
else if ((MAX(r,g,b)==g)&&(MAX(r,g,b)!=MIN(r,g,b)))
{
h=60*(b-r)/(MAX(r,g,b)-MIN(r,g,b))+120;
}
else if ((MAX(r,g,b)==b)&&(MAX(r,g,b)!=MIN(r,g,b)))
{
h=60*(r-g)/(MAX(r,g,b)-MIN(r,g,b))+240;
}
else
{
h=0;
}
if(h<0)
{
h=h+360;
}
if(((h>=0)&&(h<=1))||((h>=359)&&(h<=360)))
{
RED[row*640+i/2]=1;
red++;
}
else
{
RED[row*640+i/2]=0;
}
table[row*640+i/2]=0;
}
}
typedef struct bmp_header
{
short twobyte; //Two byte to ensure
char bfType[2]; //Type of file, must be 0x4D42
unsigned int bfSize; //
unsigned int bfReserved1;
unsigned int bfOffBits;
}BMPHEADER;
typedef struct bmp_info
{
unsigned int biSize;
int biWidth;
int biHeight;
unsigned short biPlanes;
unsigned short biBItCount;
unsigned int biCompression;
#define BI_RGB 0L
#define BI_RLE8 1L
#define BI_RLE4 2L
#define BI_BITFIELDS 3L
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
}BMPINFO;
typedef struct tagRGBQUAD{
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
}RGBQUAD;
typedef struct tagBITMAPINFO{
BMPINFO bmiHeader;
unsigned int rgb[3];
}BITMAPINFO;
static int get_rgb565_header(int w, int h, BMPHEADER * head, BITMAPINFO * info)
{
int size = 0;
if (head && info)
{
size = w * h * 2;
memset(head, 0, sizeof(* head));
memset(info, 0, sizeof(* info));
head->bfType[0] = 'B';
head->bfType[1] = 'M';
head->bfOffBits = 14 + sizeof(* info);
head->bfSize = head->bfOffBits + size;
size = head->bfSize - head->bfOffBits;
info->bmiHeader.biSize = sizeof(info->bmiHeader);
info->bmiHeader.biWidth = w;
info->bmiHeader.biHeight = h;
info->bmiHeader.biPlanes = 1;
info->bmiHeader.biBItCount = 16;
info->bmiHeader.biCompression = BI_BITFIELDS;
info->bmiHeader.biSizeImage = size;
info->rgb[0]=0xf800;
info->rgb[1]=0x07e0;
info->rgb[2]=0x001f;
printf("rgb565:%dbit,%d*%d,%d\n",info->bmiHeader.biBItCount, w, h, head->bfSize);
}
return size;
};
class TError {
public:
TError(const char *msg) {
this->msg = msg;
}
TError(const TError bitand e) {
msg = e.msg;
}
void Output() {
std::cerr << msg << std::endl;
}
virtual ~TError() {}
protected:
TError bitand operator=(const TError bitand);
private:
const char *msg;
};
// Linear memory based image
class TRect {
public:
TRect(): Addr(0), Size(0), Width(0), Height(0), LineLen(0), BPP(16) {
}
virtual ~TRect() {
}
bool DrawRect(const TRect bitand SrcRect, int x, int y) const {
if (BPP not_eq 16 or SrcRect.BPP not_eq 16) {
// don't support that yet
throw TError("does not support other than 16 BPP yet");
}
// clip
int x0, y0, x1, y1;
x0 = x;
y0 = y;
x1 = x0 + SrcRect.Width - 1;
y1 = y0 + SrcRect.Height - 1;
if (x0 < 0) {
x0 = 0;
}
if (x0 > Width - 1) {
return true;
}
if( x1 < 0) {
return true;
}
if (x1 > Width - 1) {
x1 = Width - 1;
}
if (y0 < 0) {
y0 = 0;
}
if (y0 > Height - 1) {
return true;
}
if (y1 < 0) {
return true;
}
if (y1 > Height - 1) {
y1 = Height -1;
}
//copy
int copyLineLen = (x1 + 1 - x0) * BPP / 8;
unsigned char *DstPtr = Addr + LineLen * y0 + x0 * BPP / 8;
const unsigned char *SrcPtr = SrcRect.Addr + SrcRect.LineLen *(y0 - y) + (x0 - x) * SrcRect.BPP / 8;
for (int i = y0; i <= y1; i++) {
memcpy(DstPtr, SrcPtr, copyLineLen);
DstPtr += LineLen;
SrcPtr += SrcRect.LineLen;
}
return true;
}
bool DrawRect(const TRect bitand rect) const { // default is Center
return DrawRect(rect, (Width - rect.Width) / 2, (Height - rect.Height) / 2);
}
bool Clear() const {
int i;
unsigned char *ptr;
for (i = 0, ptr = Addr; i < Height; i++, ptr += LineLen) {
memset(ptr, 0, Width * BPP / 8);
}
return true;
}
bool SavePic(const TRect bitand SrcRect, int x, int y) const {
if (BPP not_eq 16 or SrcRect.BPP not_eq 16) {
// don't support that yet
throw TError("does not support other than 16 BPP yet");
}
// clip
int x0, y0, x1, y1;
x0 = x;
y0 = y;
x1 = x0 + SrcRect.Width - 1;
y1 = y0 + SrcRect.Height - 1;
printf("x0=%d;y0=%d;x1=%d;y1=%d\n",x0,y0,x1,y1);
if (x0 < 0) {
x0 = 0;
printf("x0<0\n");
}
if (x0 > Width - 1) {
printf("x0 > Width - 1\n");
return true;
}
if( x1 < 0) {
printf("x1 < 0\n");
return true;
}
if (x1 > Width - 1) {
printf("x1 > Width - 1\n");
x1 = Width - 1;
}
if (y0 < 0) {
printf("y0 < 0\n");
y0 = 0;
}
if (y0 > Height - 1) {
printf("y0 > Height - 1");
return true;
}
if (y1 < 0) {
printf("y1 < 0");
return true;
}
if (y1 > Height - 1) {
printf("y1 > Height - 1");
y1 = Height -1;
}
//copy
int copyLineLen = (x1 + 1 - x0) * BPP / 8;
unsigned char *DstPtr = Addr + LineLen * y0 + x0 * BPP / 8;
const unsigned char *SrcPtr = SrcRect.Addr + SrcRect.LineLen *(y0 - y) + (x0 - x) * SrcRect.BPP / 8;
FILE * hfile = NULL;
sprintf(path,"/root/pic/%d.bmp",picno);
hfile = fopen(path,"wb");
if (hfile == NULL)
{
printf("open(%s) failed!\n",path);
}
int size = 0;
BMPHEADER head;
BITMAPINFO info;//BMP File Preparation
printf("open OK");
size = get_rgb565_header(640,480, &head, &info);
if (size>0)
{
fwrite(head.bfType, 1,14,hfile);
fwrite(&info,1,sizeof(info),hfile);
}
for (int i = y0; i <= y1; i++) {
memcpy(DstPtr, SrcPtr, copyLineLen);
rgb2hsi(DstPtr, i);
BMPdata = (unsigned char *)malloc(640*480*sizeof(unsigned char));
for(int fm=0;fm<1280;fm+=2)
{
BMPdata[fm]=DstPtr[fm];
BMPdata[fm+1]=DstPtr[fm+1];
if(RED[i*640+fm/2]==1)
{ DstPtr[fm+1]=0xf8;
DstPtr[fm]=0x00;
}
}
printf("1:%d||2:%d",BMPdata[1],BMPdata[2]);
fwrite(DstPtr,copyLineLen,sizeof(unsigned char),hfile);
DstPtr += LineLen;
SrcPtr += SrcRect.LineLen;
}
if (hfile !=NULL)
fclose(hfile);
//ANALYSE PICTURE
for (int i=0;i<480;i+=5)
{
// printf("%d\n",i);
for (int j=0;j<640;j+=5)
{
RegionGrow(table, RED, j, i);
ycenter=ycenter/countc;
xcenter=xcenter/countc;
if(countc>countrec)
{
countrec=countc;
ycentermax=ycenter;
xcentermax=xcenter;
}
countc=0;
ycenter=0;
xcenter=0;
}
if(i==475)
{
printf("redpoint is %d\n",red);
printf("countrec=%le\n",countrec);
printf("deepth=%lf\n",deepth);
printf("RED\n");
if(countrec>1e+1/*1e+4*/)
{
printf("the size is %le\n",countrec);
printf("the coodinate is (%le,%le)\n",ycentermax, xcentermax);
}
else
{
printf("red colour not recognized\n");
}
}
countrec=0;
deepth=0;
}
return true;
}
bool SavePic(const TRect bitand rect) const { // default is Center
return SavePic(rect, (Width - rect.Width) / 2, (Height - rect.Height) / 2);
}
/**/
protected:
TRect(const TRect bitand);
TRect bitand operator=( const TRect bitand);
protected:
unsigned char *Addr;
int Size;
int Width, Height, LineLen;
unsigned BPP;
};
class TFrameBuffer: public TRect {
public:
TFrameBuffer(const char *DeviceName = "/dev/fb0"): TRect(), fd(-1) {
Addr = (unsigned char *)MAP_FAILED;
fd = open(DeviceName, O_RDWR);
if (fd < 0) {
throw TError("cannot open frame buffer");
}
struct fb_fix_screeninfo Fix;
struct fb_var_screeninfo Var;
if (ioctl(fd, FBIOGET_FSCREENINFO, bitand Fix) < 0 or ioctl(fd, FBIOGET_VSCREENINFO, bitand Var) < 0) {
throw TError("cannot get frame buffer information");
}
BPP = Var.bits_per_pixel;
if (BPP not_eq 16) {
throw TError("support 16BPP frame buffer only");
}
Width = Var.xres;
Height = Var.yres;
LineLen = Fix.line_length;
Size = LineLen * Height;
int PageSize = getpagesize();
Size = (Size + PageSize - 1) / PageSize * PageSize ;
Addr = (unsigned char *)mmap(NULL, Size, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);
if (Addr == (unsigned char *)MAP_FAILED) {
throw TError("map frame buffer failed");
return;
}
::close(fd);
fd = -1;
Clear();
}
virtual ~TFrameBuffer() {
::munmap(Addr, Size);
Addr = (unsigned char *)MAP_FAILED;
::close(fd);
fd = -1;
}
protected:
TFrameBuffer(const TFrameBuffer bitand);
TFrameBuffer bitand operator=( const TFrameBuffer bitand);
private:
int fd;
};
class TVideo : public TRect {
public:
TVideo(const char *DeviceName = "/dev/camera"): TRect(), fd(-1) {
Addr = 0;
fd = ::open(DeviceName, O_RDONLY);
if (fd < 0) {
throw TError("cannot open video device");
}
Width = 640;
Height = 512;
BPP = 16;
LineLen = Width * BPP / 8;
Size = LineLen * Height;
Addr = new unsigned char<:Size:>;
Clear();
}
bool FetchPicture() const {
int count = ::read(fd, Addr, Size);
if (count not_eq Size) {
throw TError("error in fetching picture from video");
}
return true;
}
virtual ~TVideo() {
::close(fd);
fd = -1;
delete<::> Addr;
Addr = 0;
}
protected:
TVideo(const TVideo bitand);
TVideo bitand operator=(const TVideo bitand);
private:
int fd;
};
int main(int argc, char **argv)
{
try {
TFrameBuffer FrameBuffer;
TVideo Video;
for (int ii=1;ii<=100;ii++) {
if (ii==10)
{ Video.FetchPicture();
FrameBuffer.SavePic(Video);
}
else
{
Video.FetchPicture();
FrameBuffer.DrawRect(Video);
};
printf("this is %d\n",ii);
}
} catch (TError bitand e) {
e.Output();
return 1;
}
return 0;
}
转载于:https://www.cnblogs.com/sandersjaylaw/articles/3932170.html