视频移动侦测VMD的实现

视频移动侦测区域报警的原理、实现与应用

  简介
  ======

此项目用于前端摄像头的视频移动侦测报警。

之前已经发布了一个版本,功能比较简单,现在在以前版本的基础上增加了区域报警的功能。

所谓区域报警,就是在摄像头的视角范围定义出一些区域,当此区域的出现非法入侵的时候就产生报警。
通过这种方式可以有效的对重点区域进行监控而忽略其它的一些非重点区域。

  功能要求
  ============
- 以矩形区域来表征目标


- 支持多区域报警


- 支持多级别报警




  原理与实现
  ===============
        移动侦测
        ============

通过分析目标区域的图片灰度值的变化来判断。
继承第一版本的算法(详情参考相关文档),采用二次差分的方式来对目标区域进行检测。

首先,在不考虑分区域报警的情形下,分析一下其算法实现。具体步骤如下:

1. 采集第一帧图片,计算出灰度值,并保存该灰度值。

2. 采集第二帧图片,计算出灰度值,并与前一次保存的灰度值进行比较,计算出灰度变化方向,并保存该变化方向,其包含三种情形:

 - 灰度不变

 - 灰度变大

 - 灰度变小

用本次计算出来的灰度值覆盖上一次计算出的灰度值。

3. 采集第三帧图片, 计算出灰度值,并与前一次保存的灰度值进行比较,计算出灰度变化方向,并结合前一次的灰度变化方向来做判断,
  若本次的灰度变化方向与前一次的灰度变化方向相异,则表明目标区域出现了变化,应该产生报警。即:

 - 本次灰度变小,前一次灰度变大,产生报警。

 - 本次灰度变大,前一次灰度变小,产生报警。

 - 本次灰度不变,不动作。

 - 前一次灰度不变,不动作。

用本次计算出来的灰度值覆盖上一次计算出的灰度值。

用本次计算出来的灰度变化方向覆盖上一次计算出来的灰度变化方向。

4. 继续采集图片,并以上一步的算法来分析。此时进入稳定的循环检测期。

        区域报警
        ============

使用区域报警,其原理和上述小节描述的是完全一样的,
只是为了支持区域报警,程序中使用了特定的数据结构来描述这个增强功能。

- 灰度数组

使用数组 last_y[] 来存储上一帧jpeg图片的各个block的灰度值。
数组长度为 (IMG_WIDTH_MAX / JPEG_BLOCK_DIM) * (IMG_HEIGHT_MAX / JPEG_BLOCK_DIM)

当前帧的灰度值存储在 crt_y[]数组中。

- 灰度变化方向数组

使用二维数组 last_y_direction[][] 存储上一帧jpeg图片的灰度变化方向。

定义了三个宏来标示:

 - #define BLK_Y_NO_CHANGE 0
 - #define BLK_Y_INCREASED 1
 - #define BLK_Y_DECREASED 2

数组的第一维长度是 ALARM_SENSITY_LEVEL_MAX, 由于需要支持多级别报警,所以需要记录每一个级别的灰度变化方向。

数组的第二维长度是 (IMG_WIDTH_MAX / JPEG_BLOCK_DIM) * (IMG_HEIGHT_MAX / JPEG_BLOCK_DIM) / 4,
为了节约内存资源,每个字节存储了4组变化方向。

- 报警数组

使用数组 alarm_tbl[] 存储目前的报警状态,为了节约内存资源,每一位代表一个block。

- 已注册报警区域

使用数组 alarm_zone_tbl[] 来存储已注册的报警区域,最大报警区域数可配置。

- 程序流程

程序流程和之前的描述是一致的,总体比较简单,看代码即可,不在赘述。

        灰度计算
        ============
使用第三方代码,具体原理超出本文档的范围。

  应用步骤
  ============

1. 初始化系统。如果在主程序的启动代码里有对全局变量进行清零操作,则这一步可以省略。

  reset_motion_detect();

2. 设置视频分辨率。参数不要超过范围,否则会返回错误。每次改变摄像头的分辨率之后都需要调用此函数重新初始化系统。

  set_img_dim(640, 480);

3. 注册报警区域。

  参数1:矩形区域的坐标采用比例数来设置,因此当摄像头的分辨率改变之后不会影响系统的工作。

  矩形结构体的各个元素的取值范围是[0 ~ 999] ,代表[0.000 ~ 0.999] , 注意是闭区间。

  参数2:ID号不能重复。
  参数3:报警级别使用头文件定义的宏来表示,不要采用数字。

  Rect alarm_zone = {400, 600, 400, 600};  /* 中间区域 */
  
  if (!add_alarm_zone(&alarm_zone, 1, ALARM_SENSITY_LV3)) {
      /* 添加报警区域成功 */
  } else {
      /* 出错了! */
  }

  可以添加多个报警区域,区域坐标可以交叉重叠。

4. 开始移动侦测。

  Image img;
  AlarmInfoArray alarm_info;
  int alarm_count;
  int i;
  img.pbuf = jpeg_buf;
  img.len = jpeg_len;
  
  if (!motion_detect(&img, &alarm_info, &alarm_count)) {
      for (i = 0; i < alarm_count; i++) {
          /* 
          业务逻辑在这里处理 
          printf("Alarm->  id:%d, level:%d, zone%d, %d) (%d, %d) \n",
              (int)alarm_info.id, (int)alarm_info.alarm_level,
              (int)alarm_info.zone.left, (int)alarm_info.zone.top,
              (int)alarm_info.zone.right, (int)alarm_info.zone.bottom);
          */
      }
  } else {
      /* 出错或者未就绪 */
  }

  以上代码需要以一定的间隔(比如0.5秒)循环调用,图片帧存在jpeg_buf里,帧长度存在jpeg_len里。

5. 移除报警区域。

  移除以上注册的ID号为1的报警区域。

  if (!rm_alarm_zone(1)) {
      /* 成功移除 */;
  } else {
      /* 出错 */
  }

6. 更改配置信息

- ALARM_ZONE_MAX 

用于定义总的报警区域数,可以根据需要配置。

/* How many zones which are being monitored */
#define ALARM_ZONE_MAX 20

- ALARM_SENSITY_LEVEL_MAX 

用于定义报警级别数,最大限制在5。

/* Don't be great than 5 */
#define ALARM_SENSITY_LEVEL_MAX 5

- ALARM_SENSITY_START_VAL 

用于定义报警的门槛值,小于该值系统认为没有变化。

/* The start threshold */
#define ALARM_SENSITY_START_VAL 5

- ALARM_SENSITY_STEP_VAL 

用于定义报警级别的步进值。

/* The stepping value of each sensity level */ 
#define ALARM_SENSITY_STEP_VAL 10

/*
 * Motion detecting 
 *
 * File: motion_detect.h
 *
 * Author: Hugui   
 * E-Mail: y_y_z_l @ 163.com
 * Date:   2012.7.6
 *
 * Changing Log:
 *
 */


/*
 * Coordinate system of jpeg blocks.
 * 
 *   +--------------->  x
 *   |
 *   |
 *   |
 *   |
 *   V
 *   y
 *
       /---> JPEG BLOCK                                                          
      /                                                                         
     /                                                                          
    /                                                                           
   /                                                                            
  /                                                                             
 /     |       |       |       |       |       |       |       |       |       |         
       |       |       |       |       |       |       |       |       |       |         
       |       |       |       |       |       |       |       |       |       |         
   0   |   1   |   2   |       |       |       |       |       |       |   7   |   ...   
       |       |       |       |       |       |       |       |       |       |         
       |       |       |       |       |       |       |       |       |       |         
       |       |       |       |       |       |       |       |       |       |         
-----------------------------------------------------------------------------------------
       |       |       |       |       |       |       |       |       |       |         
       |       |       |       |       |       |       |       |       |       |         
       |       |   #######################################################     |         
       |       |   #   |       |       |       |       |       |       | #     |         
       |       |   #   |       |       |       |       |       |       | #     |         
       |       |   #   |       |       |       |       |       |       | #     |         
       |       |   #   |       |       |       |       |       |       | #     |         
-------------------#-----------------------------------------------------#---------------
       |       |   #   |       |       |       |       |       |       | #     |         
       |       |   #   |       |       |       |       |       |       | #     |         
       |       |   #   |       |       |       |       |       |       | #     |     /--> Selected zone 1#
       |       |   #   |       |       |       |       |       |       | #     |    /    
       |       |   #   |       |       |       |       |       |    ---------------/     
       |       |   #   | ###########################################   | #     |         
       |       |   #   | #     |       |       |       |       |   #   | #     |         
-------------------#-----#-----------------------------------------#-----#---------------
       |       |   #   | #     |       |       |       |       |   #   | #     |         
       |       |   #   | #     |       |       |       |       |   #   | #     |         
       |       |   #######################################################     |         
       |       |       | #     |       |       |       |       |   #   |       |         
       |       |       | #     |       |       |       |       |   #   |       |         
       |       |       | #     |       |       |    #########################  |         
       |       |       | #     |       |       |    #  |       |   #   |    #  |         
-------------------------#--------------------------#--------------#--------#------------
       |       |       | #     |       |       |    #  |       |   #   |    #  |         
       |       |       | #     |       |       |    #  |       |   #   |    #  |         
       | ##########################    |       |    #  |       |   #   |    #  |    /--> Selected zone 2#
       | #     |       | #     |  #    |       |    #  |       |   #   |    #  |   /     
       | #     |       | #     |  #    |       |    #  |       |   #   | ---------/      
       | #     |       | #     |  #    |   |   |    #  |       |   #   |    #  |         
       | #     |       | #     |  #    |   |   |    #  |       |   #   |    #  |         
---------#---------------#--------#--------|--------#--------------#--------#------------
       | #     |       | #     |  #    |   |   |    #  |       |   #   |    #  |         
       | #     |       | ##################|########################   |    #  |         
   .   | #     |       |       |  #    |   |   |    #  |       |       |    #  |         
   .   | #     |   |   |       |  #    |   |   |    #########################  |         
   .   | #     |   |   |       |  #    |   |   |       |       |       |       |         
       | ##########|###############    |   |   |       |       |       |       |         
       |       |   |   |       |       |   |   |       |       |       |       |         
                   |                       \.                                                             
                   \                        \---> Selected zone 4#                                            
                    \--> Selected zone 3#                                                                     
*/


/* 
 * Coordinate system of alarm zone.
 *
 *  +--------------------------> x
 *  |  (left, top)
 *  |   +---------------------+
 *  |   |                     |
 *  |   |    .p1(x1,y1)       |
 *  |   |        .p2(x2,y2)   |
 *  |   |                     |
 *  |   +---------------------+ (right, bottom)
 *  |
 *  V
 *  y
 *  
 *  p1(250,300)  -->  (0.250 * img_width, 0.3 * img_height)
 *  p2(350,600)  -->  (0.350 * img_width, 0.6 * img_height)
 *
 * Coordinate by thousandths. 
 * Example:
 *   value |  actual coordinate 
 *   ------+---------------------------------
 *   100   |  0.1   *  img_width(img_height)
 *   50    |  0.05  *  img_width(img_height)
 *   500   |  0.5   *  img_width(img_height)
 *   1     |  0.001 *  img_width(img_height)
 */

#ifndef _H_MOTION_DETECT_H_
 #define _H_MOTION_DETECT_H_
#endif

#ifdef __cplusplus
extern "C" {
#endif

/* The maximum length of image */
#define IMG_WIDTH_MAX 640
#define IMG_HEIGHT_MAX 480

/* The minimum length of image */
#define IMG_WIDTH_MIN 160
#define IMG_HEIGHT_MIN 120

/* JPEG image block dimention: 8pixels * 8pixels */
#define JPEG_BLOCK_DIM 8

/* Indicating whether the pointer parameter is INPUT or OUTPUT */
#define __INPUT__
#define __OUTPUT__

/* How many zones which are being monitored */
#define ALARM_ZONE_MAX 20

/* Don't be great than 5 */
#define ALARM_SENSITY_LEVEL_MAX 5


/* 
 *   -----------------------     level 5     The least sensitive 
 * 
 *   -----------------------     level 4
 * 
 *   -----------------------     level 3
 *         | ALARM_SENSITY_STEP_VAL 
 *   -----------------------     level 2
 * 
 *   -----------------------     ALARM_SENSITY_START_VAL, start threshold, 
 *                               level 1, The most sensitive
 *
 *   -----------------------     zero, means no changing.
 */

/* The start threshold */
#define ALARM_SENSITY_START_VAL 5
/* The stepping value of each sensity level */ 
#define ALARM_SENSITY_STEP_VAL 10

/* Each level */
#if ALARM_SENSITY_LEVEL_MAX > 5
  #error "ALARM_SENSITY_LEVEL_MAX is not allowed to be great than 5"
#elif ALARM_SENSITY_LEVEL_MAX < 1
  #error "ALARM_SENSITY_LEVEL_MAX is not allowed to be less than 1"
#else
  #if ALARM_SENSITY_LEVEL_MAX >= 1
    #define ALARM_SENSITY_LV1 1 
    #define ALARM_SENSITY_VAL_LV1 ALARM_SENSITY_START_VAL
    #define USING_ALARM_LV1
  #endif
  #if ALARM_SENSITY_LEVEL_MAX >= 2
    #define ALARM_SENSITY_LV2 2 
    #define ALARM_SENSITY_VAL_LV2 (ALARM_SENSITY_START_VAL + \
            ALARM_SENSITY_STEP_VAL * (ALARM_SENSITY_LV2 - 1))
    #define USING_ALARM_LV2
  #endif
  #if ALARM_SENSITY_LEVEL_MAX >= 3
    #define ALARM_SENSITY_LV3 3 
    #define ALARM_SENSITY_VAL_LV3 (ALARM_SENSITY_START_VAL + \
            ALARM_SENSITY_STEP_VAL * (ALARM_SENSITY_LV3 - 1))
    #define USING_ALARM_LV3
  #endif
  #if ALARM_SENSITY_LEVEL_MAX >= 4
    #define ALARM_SENSITY_LV4 4 
    #define ALARM_SENSITY_VAL_LV4 (ALARM_SENSITY_START_VAL + \
            ALARM_SENSITY_STEP_VAL * (ALARM_SENSITY_LV4 - 1))
    #define USING_ALARM_LV4
  #endif
  #if ALARM_SENSITY_LEVEL_MAX >= 5
    #define ALARM_SENSITY_LV5 5 
    #define ALARM_SENSITY_VAL_LV5 (ALARM_SENSITY_START_VAL + \
            ALARM_SENSITY_STEP_VAL * (ALARM_SENSITY_LV5 - 1))
    #define USING_ALARM_LV5
  #endif
#endif

/* Error code */
#define EC_MEM        1     /* No memery */
#define EC_ID         2     /* ID error */
#define EC_NO_SLOT    3     /* Alarm table full */
#define EC_FRAM1      4     /* Not ready */
#define EC_FRAM2      5     /* Not ready */
#define EC_STATE      6     /* Error state */
#define EC_DIM        7     /* Dimention Error */
#define EC_DIM_CHANG  8     /* Dimention changed but no notify */

/* 
 * Bits operation 
 *
 * dat: the target byte.
 * pos: the position you want to operate.
 * len: the width of bits.
 * val: the value you want to set or test.
 *
 * Example:
 *  dat      |  operation                    |   result
 *  ---------+-------------------------------+---------- 
 *  11010001 |  SET_BYTE_BITS(dat, 2, 2, 3)  |   11011101
 *  11010001 |  SET_BYTE_BITS(dat, 4, 2, 3)  |   11110001
 *  10110111 |  TEST_BYTE_BITS(dat, 3, 2, 1) |   flase
 *  10110111 |  TEST_BYTE_BITS(dat, 3, 2, 2) |   true
 */
#define SET_BYTE_BITS(dat, pos, len, val) do {                         \
        (dat) &= ~( (~(0xFF<<(len))) << (pos) ); /* Set to zero */     \
        (dat) |= ( ((val)&(~(0xFF<<(len)))) << (pos) ); /* Set val */  \
    } while (0)
#define TEST_BYTE_BITS(dat, pos, len, val)                             \
    ( (((val)&(~(0xFF<<(len)))) << (pos)) ==                           \
        ( (dat) & ( (~(0xFF<<(len))) << (pos) ) ) ) 

/* Rectangle define */
typedef struct _tag_rect {
    unsigned short left;
    unsigned short right;
    unsigned short top;
    unsigned short bottom;
} Rect, *PRect;

/* Image define */
typedef struct _tag_image {
    unsigned char * pbuf;
    int  len;
} Image, *PImage;

/* Define the alarm zone */
typedef struct _tag_alarm_zone {
    int id;
    Rect zone;
    unsigned char alarm_level;
} AlarmZone, *PAlarmZone;

/* 
 * Return the alarm info into an array whose type is AlarmInfoArray when 
 *  invoke motion_detect()
 */
typedef AlarmZone AlarmInfoArray[ALARM_ZONE_MAX];


/* 
 *  ------   Interface  -------
 */

/* Initial or reset the motion detecting system. */
int reset_motion_detect(void);

/* 
 * Set dimention of image 
 * When the dimention of image has changed, you should call this function.
 * Default setting: 320 * 240
 */
int set_img_dim(unsigned short width, unsigned short height);

/* Add & remove alarm zone. Zone Cross or Zone Overlapping is OK */
int add_alarm_zone(Rect* zone, int id, unsigned char alarm_level);
int rm_alarm_zone(int id);
int rm_all_alarm(void);

/* Invoke this at every image frame */
int motion_detect(Image* __INPUT__ img, 
        AlarmInfoArray* __OUTPUT__ alarm_info, int* __OUTPUT__ alarm_count);


/* 
 *  ------  Internal function --------
 */

/* Caculate the alarm level by y. */
unsigned char whichlevel(int delta_y);

/* Update the changed direction of y. */
void update_y_direction(int block_index, int delta_y);

/*  Update the alarm table. */
void update_alarm_tbl(int block_index, int delta_y);

/* Scan the alarm table */
void generate_alarm(AlarmInfoArray* __OUTPUT__ alarm_info, int* __OUTPUT__ alarm_count);

/* Check whether the selected block zone contains valid alarm. */
bool is_alarm(Rect* blk_zone, unsigned char alarm_level);

/* 
 * Transfer the rectangle from image view coordinate system 
 * to jpeg block coordinate system.
 */
void coordinate_transfer(Rect* alarm_zone, Rect* __OUTPUT__ blk_zone);


#ifdef __cplusplus
}
#endif



/*
 * Motion detecting 
 *
 * File: motion_detect.c
 *
 * Author: Hugui   
 * E-Mail: y_y_z_l @ 163.com
 * Date:   2012.7.6
 *
 * Changing Log:
 *
 */


#include <string.h>
#include <malloc.h>
#include "motion_detect.h"

extern int decode_y(unsigned char * jpeg, int jpeglen, int * y);

/*
 * The last changing direction of Y.
 * 00:  grayscale no changed.
 * 01:  grayscale increased.
 * 10:  grayscale decreased.
 *
 * last_y_direction[0][]
 *  byte0          byte1          byte2
 *  01 00 10 01    10 10 00 01    10 10 00 01 ...
 *            |
 *            |
 *            V
 *       Start from here,This is the first block.
 * last_y_direction[1][]
 * ...
 *
 */
#define BLK_Y_NO_CHANGE 0
#define BLK_Y_INCREASED 1
#define BLK_Y_DECREASED 2
unsigned char last_y_direction[ALARM_SENSITY_LEVEL_MAX][       \
                    (IMG_WIDTH_MAX / JPEG_BLOCK_DIM) *         \
                        (IMG_HEIGHT_MAX / JPEG_BLOCK_DIM) / 4];

/* Last grayscale */
int last_y[(IMG_WIDTH_MAX / JPEG_BLOCK_DIM) * (IMG_HEIGHT_MAX / JPEG_BLOCK_DIM)];
int last_y_len;

/* Current graysacle, this variable is for temperary using. */
int crt_y[(IMG_WIDTH_MAX / JPEG_BLOCK_DIM) * (IMG_HEIGHT_MAX / JPEG_BLOCK_DIM)];

/*
 * Alarm table.
 *
 * 0:  no alarm.
 * 1:  ALARM !!!
 *
 * alarm_tbl[0][]
 *  byte0       byte1    byte2
 *  01001001    10100001    10100001 ...
 *         |
 *         |
 *         V
 *       Start from here,This is the first block.
 * alarm_tbl[1][]
 * ...
 *
 */
unsigned char alarm_tbl[ALARM_SENSITY_LEVEL_MAX][              \
                    (IMG_WIDTH_MAX / JPEG_BLOCK_DIM) *         \
                        (IMG_HEIGHT_MAX / JPEG_BLOCK_DIM) / 8];

/* The current width & height of image */
unsigned short img_width = 320;
unsigned short img_height = 240;

/* State of motion detecting process */
#define MD_STATE_FRAM1 1
#define MD_STATE_FRAM2 2
#define MD_STATE_FRAM3 3
unsigned char motion_detect_state = MD_STATE_FRAM1;

/* Registered alarm zone */
PAlarmZone alarm_zone_tbl[ALARM_ZONE_MAX];


/* 
 * Initial or reset the motion detecting system. 
 * 
 * @return: 0 for OK
 */
int reset_motion_detect(void)
{
    motion_detect_state = MD_STATE_FRAM1;
        memset(last_y_direction, 0x00, sizeof(last_y_direction));

        return 0;
}


/* 
 * Set dimention of image 
 * When the dimention of image has changed, you should call this function.
 * Default setting: 320 * 240
 *
 * @width: width of image, unit by pixel
 * @height: height
 * 
 * @return: 0 for OK
 */
int set_img_dim(unsigned short width, unsigned short height)
{
    if (width > IMG_WIDTH_MAX ||
            height > IMG_HEIGHT_MAX )
        return EC_DIM;
    
    if (width < IMG_WIDTH_MIN ||
            height < IMG_HEIGHT_MIN )
        return EC_DIM;

    img_width = width;
    img_height = height;

    reset_motion_detect();

        return 0;
}


/* 
 * Add alarm zone.
 *
 * @zone: selected alarm zone, a rectangle(check header file for details). 
 * @id:   Registered id.
 * @alarm_level: use macro defined in the header file.
 * 
 * @return: 0 for OK.
 */
int add_alarm_zone(Rect* zone, int id, unsigned char alarm_level)
{
    PAlarmZone p;
    int i;
    
    /* Check the zone id, no repeated */
    for (i = 0; i < ALARM_ZONE_MAX; i++) {
        if (alarm_zone_tbl[i]) {
            if (alarm_zone_tbl[i]->id == id)
                return EC_ID;
        }
    }

    /* Find an empty slot */
    for (i = 0; i < ALARM_ZONE_MAX; i++) {
        if (!alarm_zone_tbl[i])
            break;
    }

    /* No empty slot */
    if (i == ALARM_ZONE_MAX)
        return EC_NO_SLOT;

    /* Get memory */
    p = (PAlarmZone )malloc(sizeof(AlarmZone));
    if (!p) {
        return EC_MEM;
    }

    /* Check the range of rectangle */
    if (zone->left > 999) zone->left = 999;
    if (zone->right > 999) zone->right = 999;
    if (zone->top > 999) zone->top = 999;
    if (zone->bottom > 999) zone->bottom = 999;
    
    /* Initialize */
    p->id = id;
    p->alarm_level = alarm_level;
    memcpy(&p->zone, zone, sizeof(Rect));

    /* Insert to one empty slot */
    alarm_zone_tbl[i] = p;

    return 0;
}


/*
 * Remove alarm zone by id.
 *
 * @id:  id of alarm zone.
 *
 * @return: 0 for OK
 */
int rm_alarm_zone(int id)
{
    int i;
    
    for (i = 0; i < ALARM_ZONE_MAX; i++) {
        if (alarm_zone_tbl[i]) {  
            if (alarm_zone_tbl[i]->id == id) {
                free(alarm_zone_tbl[i]);
                alarm_zone_tbl[i] = NULL;
                return 0;
            }
        }
    }

    return EC_ID;
}


/*
 * Remove all alarm zone .
 *
 * @return: 0 for OK
 */
int rm_all_alarm(void)
{
    memset(alarm_zone_tbl, 0x00, sizeof(alarm_zone_tbl));
    return 0;
}


/*
 * Invoke this at every image frame 
 *
 * @img:         An image object pointer. Contains buffer and length.
 * @alarm_info:  Alarm info, this is an array.
 * @alarm_count: The valid length of @alarm_info array. 
 *
 * @return: 0 for OK. The output parameters are valid only when return 0.
 */
int motion_detect(Image* __INPUT__ img, 
        AlarmInfoArray* __OUTPUT__ alarm_info, int* __OUTPUT__ alarm_count)
{
    int blocks;
    int i;
    int delta_y;    

    /* Decode grayscale */
    blocks = decode_y(img->pbuf, img->len, crt_y);

    switch (motion_detect_state) {
        /* Not ready, only one frame. */
        case MD_STATE_FRAM1: 
            last_y_len = blocks;
            memcpy(last_y, crt_y, sizeof(crt_y));
            motion_detect_state = MD_STATE_FRAM2;
            return EC_FRAM1;

        /* Not ready, only two frames. */
        case MD_STATE_FRAM2: 
            /* Dimention of image has changed but didn't invoke set_img_dim() */
            if (blocks != last_y_len) {
                reset_motion_detect();
                return EC_DIM_CHANG;
            }

            for (i = 0; i < blocks; i++) {
                delta_y = crt_y[i] - last_y[i];
                update_y_direction(i, delta_y);
            }
            
            last_y_len = blocks;
            memcpy(last_y, crt_y, sizeof(crt_y));
            motion_detect_state = MD_STATE_FRAM3;
            return EC_FRAM2;

        /* YES! Loops in this case */
        case MD_STATE_FRAM3:
            /* Dimention of image has changed but didn't invoke set_img_dim() */
            if (blocks != last_y_len) {
                reset_motion_detect();
                return EC_DIM_CHANG;
            }

            for (i = 0; i < blocks; i++) {
                delta_y = crt_y[i] - last_y[i];
                update_alarm_tbl(i, delta_y);
            }

            last_y_len = blocks;
            memcpy(last_y, crt_y, sizeof(crt_y));
            
            generate_alarm(alarm_info, alarm_count); 
            return 0;

        /* NOT expected */
        default:
                        return EC_STATE;
    }
}


/* 
 * Caculate the alarm level by y.
 *
 * @delta_y:  grayscale changed.
 * 
 * @return:   Alarm level. Check the header file for details.
 */
unsigned char whichlevel(int delta_y)
{
    if (delta_y < 0)
        delta_y = -delta_y;

#if ALARM_SENSITY_LEVEL_MAX >= 5
    if (delta_y >= ALARM_SENSITY_VAL_LV5) 
        return ALARM_SENSITY_LV5;
    else 
#endif
#if ALARM_SENSITY_LEVEL_MAX >= 4
    if (delta_y >= ALARM_SENSITY_VAL_LV4)
        return ALARM_SENSITY_LV4;
    else 
#if ALARM_SENSITY_LEVEL_MAX >= 3
#endif
    if (delta_y >= ALARM_SENSITY_VAL_LV3)
        return ALARM_SENSITY_LV3;
    else
#if ALARM_SENSITY_LEVEL_MAX >= 2
#endif
    if (delta_y >= ALARM_SENSITY_VAL_LV2)
        return ALARM_SENSITY_LV2;
    else 
#endif
#if ALARM_SENSITY_LEVEL_MAX >= 1
    if (delta_y >= ALARM_SENSITY_VAL_LV1)
        return ALARM_SENSITY_LV1;
    else
#endif
        return 0;  /* NO changing */
}


/*
 *  Update the changed direction of y.
 *  There are 4 blocks in one byte,each block own 2 bits.
 *
 *  @block_index: which jepg block?
 *  @delta_y:     y changed.
 */
void update_y_direction(int block_index, int delta_y)
{
    int i;
        unsigned char level;

    level = whichlevel(delta_y);

    switch (level) {
        case 0:
            /* NO changing. Update all of the table */
            for (i = 0; i < ALARM_SENSITY_LEVEL_MAX; i++) {
                SET_BYTE_BITS(last_y_direction[i][block_index / 4], 
                    (block_index % 4) << 1, 2, BLK_Y_NO_CHANGE);
            }
            break;

#ifdef USING_ALARM_LV5
        case ALARM_SENSITY_LV5:
            SET_BYTE_BITS(last_y_direction[ALARM_SENSITY_LV5 - 1][block_index / 4], 
                (block_index % 4) << 1, 2, delta_y > 0 ? BLK_Y_INCREASED : BLK_Y_DECREASED);
#endif

#ifdef USING_ALARM_LV4
        case ALARM_SENSITY_LV4:
            SET_BYTE_BITS(last_y_direction[ALARM_SENSITY_LV4 - 1][block_index / 4], 
                (block_index % 4) << 1, 2, delta_y > 0 ? BLK_Y_INCREASED : BLK_Y_DECREASED);
#endif

#ifdef USING_ALARM_LV3
        case ALARM_SENSITY_LV3:
            SET_BYTE_BITS(last_y_direction[ALARM_SENSITY_LV3 - 1][block_index / 4], 
                (block_index % 4) << 1, 2, delta_y > 0 ? BLK_Y_INCREASED : BLK_Y_DECREASED);
#endif

#ifdef USING_ALARM_LV2
        case ALARM_SENSITY_LV2:
            SET_BYTE_BITS(last_y_direction[ALARM_SENSITY_LV2 - 1][block_index / 4], 
                (block_index % 4) << 1, 2, delta_y > 0 ? BLK_Y_INCREASED : BLK_Y_DECREASED);
#endif

#ifdef USING_ALARM_LV1
        case ALARM_SENSITY_LV1:
            SET_BYTE_BITS(last_y_direction[ALARM_SENSITY_LV1 - 1][block_index / 4], 
                (block_index % 4) << 1, 2, delta_y > 0 ? BLK_Y_INCREASED : BLK_Y_DECREASED);
#endif
            break;
        default:
            ; /* WARNING! */ 
    } 
}


/*
 *  Update the alarm table.
 *
 *  @block_index: which jepg block?
 *  @delta_y:     y changed.
 */
void update_alarm_tbl(int block_index, int delta_y)
{
    unsigned char level;

    level = whichlevel(delta_y);

    switch (level) {
#ifdef USING_ALARM_LV5
        case ALARM_SENSITY_LV5:
            if ( 
                 /* Bright  -> Dark -> Bright */
                 (delta_y > 0 && TEST_BYTE_BITS(
                        last_y_direction[ALARM_SENSITY_LV5 - 1][block_index / 4],
                        (block_index % 4) << 1, 2, BLK_Y_DECREASED))  || 
                 /* Dark -> Bright -> Dark */
                 (delta_y < 0 && TEST_BYTE_BITS(
                        last_y_direction[ALARM_SENSITY_LV5 - 1][block_index / 4],
                        (block_index % 4) << 1, 2, BLK_Y_INCREASED))  
                    ) {
                alarm_tbl[ALARM_SENSITY_LV5 - 1][block_index / 8] |= 0x01 << (block_index % 8);
            }
#endif

#ifdef USING_ALARM_LV4
        case ALARM_SENSITY_LV4:
            if ( 
                 /* Bright  -> Dark -> Bright */
                 (delta_y > 0 && TEST_BYTE_BITS(
                        last_y_direction[ALARM_SENSITY_LV4 - 1][block_index / 4],
                        (block_index % 4) << 1, 2, BLK_Y_DECREASED))  || 
                 /* Dark -> Bright -> Dark */
                 (delta_y < 0 && TEST_BYTE_BITS(
                        last_y_direction[ALARM_SENSITY_LV4 - 1][block_index / 4],
                        (block_index % 4) << 1, 2, BLK_Y_INCREASED))  
                    ) {
                alarm_tbl[ALARM_SENSITY_LV4 - 1][block_index / 8] |= 0x01 << (block_index % 8);
            }
#endif

#ifdef USING_ALARM_LV3
        case ALARM_SENSITY_LV3:
            if ( 
                 /* Bright  -> Dark -> Bright */
                 (delta_y > 0 && TEST_BYTE_BITS(
                        last_y_direction[ALARM_SENSITY_LV3 - 1][block_index / 4],
                        (block_index % 4) << 1, 2, BLK_Y_DECREASED))  || 
                 /* Dark -> Bright -> Dark */
                 (delta_y < 0 && TEST_BYTE_BITS(
                        last_y_direction[ALARM_SENSITY_LV3 - 1][block_index / 4],
                        (block_index % 4) << 1, 2, BLK_Y_INCREASED))  
                    ) {
                alarm_tbl[ALARM_SENSITY_LV3 - 1][block_index / 8] |= 0x01 << (block_index % 8);
            }
#endif

#ifdef USING_ALARM_LV2
        case ALARM_SENSITY_LV2:
            if ( 
                 /* Bright  -> Dark -> Bright */
                 (delta_y > 0 && TEST_BYTE_BITS(
                        last_y_direction[ALARM_SENSITY_LV2 - 1][block_index / 4],
                        (block_index % 4) << 1, 2, BLK_Y_DECREASED))  || 
                 /* Dark -> Bright -> Dark */
                 (delta_y < 0 && TEST_BYTE_BITS(
                        last_y_direction[ALARM_SENSITY_LV2 - 1][block_index / 4],
                        (block_index % 4) << 1, 2, BLK_Y_INCREASED))  
                    ) {
                alarm_tbl[ALARM_SENSITY_LV2 - 1][block_index / 8] |= 0x01 << (block_index % 8);
            }
#endif

#ifdef USING_ALARM_LV1
        case ALARM_SENSITY_LV1:
            if ( 
                 /* Bright  -> Dark -> Bright */
                 (delta_y > 0 && TEST_BYTE_BITS(
                        last_y_direction[ALARM_SENSITY_LV1 - 1][block_index / 4],
                        (block_index % 4) << 1, 2, BLK_Y_DECREASED))  || 
                 /* Dark -> Bright -> Dark */
                 (delta_y < 0 && TEST_BYTE_BITS(
                        last_y_direction[ALARM_SENSITY_LV1 - 1][block_index / 4],
                        (block_index % 4) << 1, 2, BLK_Y_INCREASED))  
                    ) {
                alarm_tbl[ALARM_SENSITY_LV1 - 1][block_index / 8] |= 0x01 << (block_index % 8);
            }
#endif
            break;
        default:
            ; /* WARNING! */ 
    }

    /* Cover the old table */
    update_y_direction(block_index, delta_y);
}


/*
 * Scan the alarm table, when valid and the corresponding area 
 *   is registered, return alarm info.
 *
 * @alarm_info:  Alarm info, this is an array.
 * @alarm_count: The valid length of @alarm_info array. 
 */
void generate_alarm(AlarmInfoArray* __OUTPUT__ alarm_info, int* __OUTPUT__ alarm_count)
{
    int i;
    Rect blk_zone;
    int index = 0;

    /* Reset the count */
        *alarm_count = 0;
        
    for (i = 0; i < ALARM_ZONE_MAX; i++) {
        if (alarm_zone_tbl[i]) {
            coordinate_transfer(&(alarm_zone_tbl[i]->zone), &blk_zone);
            if (is_alarm(&blk_zone, alarm_zone_tbl[i]->alarm_level)) {
                memcpy(&(*alarm_info)[index], alarm_zone_tbl[i], sizeof(AlarmZone));
                index++; 
            }
        }
    }
    *alarm_count = index;

    /* Reset alarm_tbl. This is very IMPORTANT! */
        memset(alarm_tbl, 0x00, sizeof(alarm_tbl));
}


/* 
 * Check whether the selected block zone contains valid alarm.
 *
 * @blk_zone: Rectangle descripted by jpeg block coordinate system.
 * @alarm_level:  which alarm level do you want to check?
 *
 * @return:  true for ALARM and false for NO alarm.
 */
bool is_alarm(Rect* blk_zone, unsigned char alarm_level)
{
    int row, col;
    int blk_index;

    for (row = blk_zone->top; row <= blk_zone->bottom; row++) {
        for (col = blk_zone->left; col <= blk_zone->right; col++) {
            blk_index = img_width / JPEG_BLOCK_DIM * row + col;
            if (alarm_tbl[alarm_level - 1][blk_index / 8] & (0x01 << (blk_index % 8))) {
                return true;
                        }
        }
    }
    
    return false; 
}


/*
 *  Transfer the rectangle from image view coordinate system to jpeg block coordinate system.
 *
 *  @alarm_zone: Selected alarm zone.
 *  @blk_zone:   jpeg block coordinate system. See header file for details.
 */
void coordinate_transfer(Rect* alarm_zone, Rect* __OUTPUT__ blk_zone)
{
    blk_zone->left = img_width / JPEG_BLOCK_DIM * alarm_zone->left / 1000;
    blk_zone->right = img_width / JPEG_BLOCK_DIM * alarm_zone->right / 1000;
    blk_zone->top = img_height / JPEG_BLOCK_DIM * alarm_zone->top / 1000;
    blk_zone->bottom = img_height / JPEG_BLOCK_DIM * alarm_zone->bottom / 1000;
}

这是一次识别的算法,能够滤除背景光变化。

在服务器端还有肤色识别以及轮廓识别甚至轨迹判断等算法。

参见:http://bbs.21ic.com/icview-359290-1-1.html

  

你可能感兴趣的:(视频移动侦测VMD的实现)