我是石家庄天宝感应洁具有限公司,要开发一个产品 使用摄像头由高度2-3米向下照射,当有人进入这个范围时,会输出一个电信号,我们检测到这个电信号后会采取相应的动作,我的电话是18932926616,qq:981852805

系统消息 若觉得内容不错,请点击左上角的"赞"图标,以优化网站的内容呈现。 另外,请及时验证注册邮箱,否则收不到21QA发出的红包。 官方Q群:250203055

提问于 01 三月 '16, 21:28

%E5%AE%8B%E5%AE%8F%E6%96%8C1001's gravatar image

宋宏斌1001
11112



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

简介

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

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

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

功能要求

以矩形区域来表征目标
支持多区域报警
支持多级别报警
原理与实现

移动侦测

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

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

采集第一帧图片,计算出灰度值,并保存该灰度值。
采集第二帧图片,计算出灰度值,并与前一次保存的灰度值进行比较,计算出灰度变化方向,并保存该变化方向,其包含三种情形:
灰度不变
灰度变大
灰度变小
用本次计算出来的灰度值覆盖上一次计算出的灰度值。
采集第三帧图片, 计算出灰度值,并与前一次保存的灰度值进行比较,计算出灰度变化方向,并结合前一次的灰度变化方向来做判断, 若本次的灰度变化方向与前一次的灰度变化方向相异,则表明目标区域出现了变化,应该产生报警。即:
本次灰度变小,前一次灰度变大,产生报警。
本次灰度变大,前一次灰度变小,产生报警。
本次灰度不变,不动作。
前一次灰度不变,不动作。
用本次计算出来的灰度值覆盖上一次计算出的灰度值。
用本次计算出来的灰度变化方向覆盖上一次计算出来的灰度变化方向。
继续采集图片,并以上一步的算法来分析。此时进入稳定的循环检测期。
区域报警

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

灰度数组
使用数组 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[] 来存储已注册的报警区域,最大报警区域数可配置。
程序流程
程序流程和之前的描述是一致的,总体比较简单,看代码即可,不在赘述。
灰度计算

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

应用步骤

初始化系统。如果在主程序的启动代码里有对全局变量进行清零操作,则这一步可以省略。
reset_motion_detect();
设置视频分辨率。参数不要超过范围,否则会返回错误。每次改变摄像头的分辨率之后都需要调用此函数重新初始化系统。
set_img_dim(640, 480);
注册报警区域。
参数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 {
      /* 出错了! */
  }
可以添加多个报警区域,区域坐标可以交叉重叠。
开始移动侦测。
  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[i].id, (int)alarm_info[i].alarm_level,
              (int)alarm_info[i].zone.left, (int)alarm_info[i].zone.top,
              (int)alarm_info[i].zone.right, (int)alarm_info[i].zone.bottom);
          */
      }
  } else {
      /* 出错或者未就绪 */
  }
以上代码需要以一定的间隔(比如0.5秒)循环调用,图片帧存在jpeg_buf里,帧长度存在jpeg_len里。
移除报警区域。
移除以上注册的ID号为1的报警区域。
  if (!rm_alarm_zone(1)) {
      /* 成功移除 */;
  } else {
      /* 出错 */
  }
更改配置信息
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
联系我

Author: Hugui

E-Mail: 点这里写邮件

Date: 2012.7.6

/*
* 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;
}

/*
* 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;
}
系统消息 若觉得内容不错,请点击左上角的"赞"图标,以优化网站的内容呈现。 另外,请及时验证注册邮箱,否则收不到21QA发出的红包。 官方Q群:250203055
永久链接

回答于 03 三月 '16, 09:55

%E8%B7%AF%E4%BA%BA%E7%94%B2's gravatar image

路人甲
131432568585

编辑于 06 三月 '16, 14:11

%E5%8E%9F%E9%87%8E%E4%B9%8B%E7%8B%BC's gravatar image

原野之狼
1.9k9399114

你的回答
切换预览

你可以使用订阅来关注这个问题

使用邮箱订阅:

登录后可以订阅更新

使用RSS订阅:

回答

回答与评论

文字标记基础知识

  • *斜体文字* 或者 _斜体文字_
  • **黑体文字** 或者 __黑体文字__
  • 插入超链接: [链接文字](http://url.com/ "标题")
  • 插入图片: ![alt](/path/img.jpg "标题")
  • 编号排列: 1. Foo 2. Bar
  • 输入换行符前请输入两个空格(即:空空回车),仅敲回车无效。
  • 支持基本的HTML标签的使用

问题的标签:

×1

问题发表于: 01 三月 '16, 21:28

问题被查看: 909 次

最近更新: 06 三月 '16, 14:11

相关问题

powered by O*S*Q*A

粤ICP备14040061号-1