运动图像检测系统:
准备:
移植Linux2.6.29运行于s3c2440板子上,
按键驱动,
USB host controller驱动
声卡驱动
动态链接文件系统制作SDK-MOTION/src/fs/rootfs-motion.tar.gz
madplay播放器移植
图像运动检测程序设计
报警主程序设计
1.当移动物体进入监控范围,系统报警
2.系统报警后保存移动物体图像
3.报警时播放一段指定音乐,mp3,avi格式
4.三分钟内检测到连续变化的次数超过20次,认为是在下雨,检测系统暂停2小时,2小时后重启。
有两种方式触发报警:
1.外部按键中断
2.图像有变化触发报警
ls
app driver fs kernel shell
tar zxvf linux2.6.29.tar.gz
cd linux2.6.29
make clean
cp config-motion .config
该.config文件采用NFS起根文件系统,且已经选择了声卡驱动,网眼V2000摄像头驱动。修
改.config的CMDLINE行的IP配置,NFS起根文件系统。
make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-配置内核
编译内核,解压根文件系统。
按键驱动移植:
SDK-MOTION/src/driver/button目录,采用上升沿触发方式。
cd motion/src/driver
make clean;make
生成mini2440_buttons_rise.ko
参考madplay移植到mini2440.doc
移植完后cp madplay /nfsroot/rootfs-motion
检测程序设计:
运动图像检测最常用图像检测方法是将当前帧和前一帧(背景帧)比较,如果不同像素的点数超过阀
值,则认为图像有变化。
本项目图像检测程序采用开源软件motion:
http://www.lavrsen.dk/foswiki/bin/view/Motion/WebHome
参考motion-3.2.11.1.tar.gz,motion的使用.doc
将motion和motion.conf拷贝到文件系统/nfsroot/rootfs-motion/motion
拷贝脚本文件:
报警文件11.mp3,22.mp3
count.txt用于存放计算/root/motion中图像数
key_pic_motion总的程序运行脚本
appon(motion.conf中on_event_start本)
appoff(motion.conf中on_event_end)
mapplay(播放器)
pic.txt(记录是否有图像运动被检测到)
cd /home/motion/src/shell
cp -a * /nfsroot/rootfs-motion/motion
报警主程序设计:
程序中使用定时器,每三分钟比较图像连续变化次数是否超过20次,如果超过则添加一个2小时定时
器,在这2小时如有图像变化不处理,如果没有超过则添加下一个3分钟定时器。
报警主程序为app-motion.c
cd /home/motion/src/app
make clean;make
cp app-motion /nfsroot/rootfs-motion/motion
项目测试一:
NFS方式起根文件系统,要求文件系统使用动态链接的文件系统。
cd /motion
./key_pic_motion
该脚本包括按键驱动加载,检测程序运行,主程序运行。
测试按键中断:
当按键被按下时,[type1]button detect, button_value:4
[key]Now detect **key** have change
表明主程序检测到键值4被按下,开始报警。
[key]No,have not mp3 play 表明目前系统中没有mp3播放,
[key]Start add 3 minute alarm! 第一次检测时,添加一个3分钟定时器。
THIS SONG IS 11.mp3 报警铃声是11.mp3
Now con_cnt=0表明当前连续变化的次数为0
测试项目二:测试运动图像
[key2]pic motion detect!表明检测到图像运动。
Now con_cnt=2表明目前检测到图像连续变化的次数是2。
测试项目三:
3分钟到达20次以上时
[pic]3alarm! con_cnt=23
Now maybe it is raining! system will sleep 7200 seconds!
测试项目四:
系统处于睡眠状态时,有外部中断或者图像运动时会提示:
It is raining! system is sleeping!
summary time:7200 seconds, sleep:157 seconds,nees sleep:7043 seconds
代码分析:
主要程序是app-motion.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
1. 检测到图像变化,报警
2. 检测到外部中断,报警
3. 3分钟能连续检测图像变化20次,则暂停2小时,2小时后在开启
*/
/*define globe variable*/
/*play_pid:当前播放的MP3子进程ID*/
unsigned int play_pid = 0;
/*gradchild:当前播放的MP3孙子进程ID*/
unsigned int gradchild = 0;
unsigned int play_flag;
/*共享内存描述标记
sharemem:
byte1:孙子进程ID号
byte2:是否有MP3播放标识play_flag_2
//byte3:MP3播放次数
*/
int shmid;
char *p_addr;
#define PERM S_IRUSR|S_IWUSR
/*报警铃声歌曲名,song1图像变化报警铃声
song2外部中断报警铃声*/
char *song1="11.mp3";
char *song2="22.mp3";
//char *song="234.mp3";
/*定时器时间为3分钟*/
#define THREE_ALARM 3*60
/*睡眠时间为2小时*/
#define SLEEP_TIME 2*60*60
#define CPM_CNT 20
int threemin_alarm = 1;
static int con_cnt=0;
int sleep_flag = 0;
unsigned int time_tmp;
#define max(flag) (flag) >1 ? "pic":"key"
//#define DEBUG
int alarm_flag;
static int pic_cnt;
int cnt_fd;
/*计算图像变化次数,超过2次则认为有图像变化*/
#define COMPARE_CNT 5
/*************************************************
Function name: count_pic
Called by : 函数main
Parameter : void
Description : 计算图片变化数
Return : int
Autor & date : ykz 10.4.25
**************************************************/
int count_pic(void)
{
int fd,ret;
char *buf;
buf = (char *)malloc(10);
system("ls /root/motion | wc -l > count.txt");
lseek(cnt_fd, 0 ,SEEK_SET);
ret = read(cnt_fd , buf , 10);
if(ret)
ret = atoi(buf);
free (buf);
return ret;
}
/*************************************************
Function name: my_func_sleepalarm
Called by : 函数my_func_3alarm
Parameter : sing_no
Description : 3分钟连续变化20次后,延时2小时后的唤醒函数
Return : void
Autor & date : ykz 10.4.25
**************************************************/
void my_func_sleepalarm(int sign_no)
{
if( sign_no == SIGALRM){
printf("Now sleep time finished!start a new work!\n");
/*则睡眠表示sleep_flag为0*/
sleep_flag = 0;
}//end sing_no
}
/*************************************************
Function name: my_func_3alarm
Called by : 函数restart_caculate_play
Parameter : sing_no
Description : 每隔3分钟检测图像连续变化是否超过20次
Return : void
Autor & date : ykz 10.4.25
**************************************************/
void my_func_3alarm(int sign_no)
{
if( sign_no == SIGALRM){
struct tm *p;
time_t timep;
unsigned int second;
printf("\n------------------warning------------------\n");
printf("[%s]3alarm!con_cnt=%d\n",max(alarm_flag),con_cnt);
/*判断图像运动次数是否超过20次
如果是:则系统睡眠2小时;
不是,则系统继续添加下一个3分钟的定时器*/
if( con_cnt >= CPM_CNT){
if(play_flag){
kill(play_pid,SIGKILL);
kill(gradchild,SIGKILL);
wait(NULL);
}
//sleep (1);
time(&timep);
p = localtime(&timep);
second = (p->tm_hour)*60*60 + (p->tm_min) * 60 + p->tm_sec;
time_tmp=second;
printf("Now maybe it is rainning!,system will sleep %d seconds!\n",SLEEP_TIME);
con_cnt = 0;
/*置睡眠标识sleep_flag为1*/
sleep_flag = 1;
/*系统睡眠2小时*/
if(signal(SIGALRM,my_func_sleepalarm)<0)
perror("signal");
alarm(SLEEP_TIME);
}
else{
printf("Nornal test!not rainning!\n");
con_cnt = 0;
/*添加下一个3分钟的定时器*/
if(signal(SIGALRM,my_func_3alarm)<0)
perror("signal");
alarm(THREE_ALARM);
}
printf("\n------------------warning------------------\n");
} //end sing_no
}
/*************************************************
Function name: play
Called by : 函数startplaymp3
Parameter : void
Description : 子进程创建孙子进程播放MP3
Return : void
Autor & date : ykz 10.4.2
**************************************************/
void play(void)
{
pid_t fd;
char *c_addr;
char *song_name;
int play_flag_gradchild1=1;
int play_flag_gradchild2=2;
int i=0;
/*创建孙子进程*/
fd = fork();
if(fd == -1)
{
perror("fork");
exit(1);
}
else if(fd == 0) /*孙子进程,播放MP3*/
{
printf("\n--------------play mp3----------------\n");
if(alarm_flag==1)
song_name=song1;
else song_name=song1;
printf("THIS SONG IS %s\n",song_name);
/*使用madplay播放MP3*/
execl("/motion/madplay","madplay",song_name,NULL);
printf("\n\n\n");
}
else /*子进程*/
{
/*把孙子进程的id传入共享内存*/
memcpy(c_addr,&fd,sizeof(pid_t));
/*目前在播放MP3,将播放标记传入共享内存*/
memcpy(c_addr+sizeof(int),&play_flag_gradchild1,4);
/*等待孙子进程结束,只要结束:
传回play_flag_gradchild2=2,表示现在MP3没有播放*/
if(fd == wait(NULL))
{
printf("\n------------------warning------------------\n");
/*通过共享内存传回play_flag_gradchild2=2,表明后面的一定不是连续的MP3播放*/
memcpy(c_addr+sizeof(int),&play_flag_gradchild2,4);
printf("Gradchild normal finish!\n");
printf("------------------warning------------------\n");
}//end if(fd == wait(NULL))
}
}
/*************************************************
Function name: startplaymp3
Called by : 函数caculate_play,restart_caculate_play
Parameter : pid_t *childpid
Description : 主进程创建子进程
Return : void
Autor & date : ykz 10.4.2
**************************************************/
void startplaymp3(pid_t *childpid,int flag)
{
int ret = 0;
pid_t cun_pid;
/*创建子进程*/
cun_pid = fork();
if(cun_pid == -1)
{
perror("son fork");
exit(1);
}
if(cun_pid == 0) /*子进程*/
play();
if(cun_pid > 0) /*父进程*/
{
*childpid = cun_pid;
sleep(1); /*让孙子进程先执行*/
/*如果是图像运动变化,将全局变量con_cnt加1*/
if( flag == 2 )
con_cnt++;
printf("\nNow con_cnt=%d\n",con_cnt);
/*把孙子进程的pid传给父进程*/
memcpy(&gradchild,p_addr,sizeof(pid_t));
}
}
/*************************************************
Function name: caculate_play
Called by : 函数key_pic_mp3
Parameter : int flag
Description : 连续播放时,计算连续播放时间,调用函数startplaymp3开始新的播放
Return : int
Autor & date : ykz 10.4.2
**************************************************/
int caculate_play(int flag)
{
int ret;
/*kill掉当前播放MP3的子进程,孙子进程*/
kill(play_pid,SIGKILL);
kill(gradchild,SIGKILL);
wait(NULL);
/*将共享内存清空*/
memset(p_addr,'\0',1024);
startplaymp3(&play_pid , flag);
return 1;
}
/*************************************************
Function name: restart_caculate_play
Called by : 函数key_pic_mp3
Parameter : int flag
Description : 未超过连续播放时间时,有新的图像或者外部中断检测到时调用函数
Return : void
Autor & date : ykz 10.4.2
**************************************************/
void restart_caculate_play(int flag)
{
int ret;
/*add 3 minute alarm*/
if(threemin_alarm){
if(signal(SIGALRM,my_func_3alarm)<0)
perror("signal");
ret = alarm(THREE_ALARM);
printf("[%s]Start add 3 minute alarm!\n",max(flag));
threemin_alarm = 0;
}
#if 1
/*判断是否有子进程,或者孙子进程,如果有则KILL掉*/
if(play_flag){
kill(play_pid,SIGKILL);
kill(gradchild,SIGKILL);
wait(NULL);
//sleep(1);
}
#endif
play_pid = 0;
gradchild = 0;
memset(p_addr,'\0',1024);
/*开始播放MP3*/
startplaymp3(&play_pid,flag);
}
/*************************************************
Function name: key_pic_mp3
Called by : 函数main
Parameter : int flag
Description : 当检测到有外部中断,或者图像变化时处理函数
Return : int
Autor & date : ykz 10.4.2
**************************************************/
int key_pic_mp3(int flag)
{
printf("------------------------- KEY_PIC_MP3 ----------------------------\n");
printf("[%s]Now detect ** %s ** have change\n",max(flag),max(flag));
int ret = 0;
int over_flag_2;
int play_flag_2;
/*sleep_flag 用于判断,检测系统是否处于2小时的睡眠状态*/
if(sleep_flag){
printf("It is rainning!system is sleeping!\n");
struct tm *p;
time_t timep;
unsigned int second;
time(&timep);
p = localtime(&timep);
second = (p->tm_hour)*60*60 + (p->tm_min) * 60 + p->tm_sec;
printf("summary time:%d seconds,sleep:%d seconds,need sleep:%d seconds\n",
SLEEP_TIME,second-time_tmp,SLEEP_TIME-(second-time_tmp));
return 0;
}
alarm_flag = flag;
/*从sharemem中读出是否有MP3处在播放状态标识*/
memcpy(&play_flag_2,p_addr + sizeof(int),sizeof(int));
// printf("===>key_pic_mp3,play_flag_2=%d\n",play_flag_2);
play_flag = play_flag_2;
/*play_flag_2:当前是否有MP3在播放
0:当前没有MP3播放
1:子进程当前处在MP3播放状态
2:孙子当前播放MP3正常结束,且当前没有MP3播放*/
/*当前有MP3在播放*/
if(play_flag_2 == 1){
printf("[%s]Yes,have mp3 play\n",max(flag));
/*调用*/
ret = caculate_play(flag);
}
/*当前无MP3在播放*/
else{
printf("[%s]No, have not mp3 play\n",max(flag));
restart_caculate_play(flag);
}
return ret;
}
/*************************************************
Function name: main
Called by :
Parameter : void
Description : 主函数,检测按键是否有按下,通过pic.txt检测图像是否有变化
Return : int
Autor & date : ykz 10.4.2
**************************************************/
main(void)
{
int buttons_fd,pic_fd;
char pic_buf[1];
int key_value;
int flag;
int ret;
int tmp_cnt;
/*设备文件的打开*/
buttons_fd = open("/dev/buttons", 0);
if(buttons_fd < 0) {
perror("open device buttons");
exit(1);
}
printf("open buttons sucess!\n");
/*文件pic.txt记录是否有图像变化。
1:有图像变化
0:没有图像变化*/
pic_fd = open("pic.txt",O_RDWR | O_CREAT,0666);
if(pic_fd < 0) {
perror("open pic.txt");
exit(1);
}
printf("open pic.txt success!\n");
/*文件count.txt用于图像连续变化时,记录/root/motion中图片张数*/
cnt_fd = open("count.txt",O_RDWR | O_CREAT,0666);
if(cnt_fd < 0){
perror("open count.txt");
exit(1);
}
printf("open count.txt success!\n");
system("ls > count.txt");
/*共享内存申请*/
if((shmid = shmget(IPC_PRIVATE,20,PERM))== -1)
exit(1);
p_addr = shmat(shmid,0,0);
memset(p_addr,'\0',1024);
/*主循环,首先判断是外部中断还是图像变化*/
while(1){
/*外部中断检测,监听获取键值*/
ret = read(buttons_fd, &key_value, sizeof key_value);
if (ret != sizeof key_value)
perror("read buttons\n");
else {
if(key_value){
printf("====================================================================\n");
printf("\n\n\n====================================================================\n");
printf("[type1]button detect,buttons_value: %d\n", key_value);
/*外部中断处理*/
key_pic_mp3(1);
}
} //end else
/*图形变化检测,当有图像变化时motion会产生一个事件,
事件处理为脚本/motion/appon,该脚本先点亮LED灯;
然后向文件/motion/pic.txt写入字符"1"表明现在有图像变化被检测到;
读取文件/motion/pic.txt第一个字符
0:没有图像变化
1:有图像变化*/
lseek(pic_fd, 0 ,SEEK_SET);
ret = read(pic_fd, pic_buf, 1);
if(ret==1)
{
if(pic_buf[0] == '1'){ /*有图像变化被检测到*/
printf("====================================================================\n");
printf("\n\n\n====================================================================\n");
printf("[type2]pic motion detect!\n");
lseek(pic_fd, 0 ,SEEK_SET);
if((ret = write(pic_fd, "0", 1)))
lseek(pic_fd, 0 ,SEEK_SET);
// system("rm /root/motion/* -rf");
pic_cnt = count_pic();
//printf("first pic_cnt=%d\n",pic_cnt);
/*图像运动变化处理*/
key_pic_mp3(2);
}
else if(pic_buf[0] == '0'){
tmp_cnt = count_pic();
if( (tmp_cnt-pic_cnt) > COMPARE_CNT){
printf("====================================================================\n");
printf("\n\n\n====================================================================\n");
printf("[type3]pic motion detect!\n");
// system("rm /root/motion/* -rf");
pic_cnt = count_pic();
/*连续图像运动变化处理*/
key_pic_mp3(2);
}
} //end else if
}
sleep (2);
}//end while
close (cnt_fd);
close (pic_fd);
close (buttons_fd);
exit(0);
}