应用场景
跌倒侦测主要应用于针对老人、小孩的住家看护之类的场景,如:家里的,或者养老院的老人。一旦出现跌倒,必须做到一定的反应。
工作原理
利用背景差检测画面的差异,并检测差异部分是否接近、类似人形。
补充说明
由于本算法对其应用场景有一定的特殊要求,若是放到不太合适的地方容易出现许多误报的状况。
以下是接口说明。
- 全域类别说明:
struct COEF_ELLIPSE{
//The parameters of the ellipse=========
int x ; //椭圆中心点x坐标
int y ; //椭圆中心点y坐标
int x_t_1 ; //椭圆中心点上一时刻x坐标
int y_t_1 ; //椭圆中心点上一时刻y坐标
int x_t_2 ; //椭圆中心点前两时刻x坐标
int y_t_2 ; //椭圆中心点前两时刻y坐标
double RegionSize; //记录该前景区域的pixel数
double a ; //椭圆短轴长
double a_1; //上一时刻椭圆短轴长
double b ; //椭圆长轴长
double b_1; //上一时刻椭圆长轴长
double theta ; //椭圆与x轴方向的夹角
double theta_t_1 ; //上一时刻的夹角
double theta_t_2 ; //前2时刻的夹角
//======================================
//The foreground image and the motion history image=======
IplImage* Img_MHI ; //Motion History Image
IplImage* Img_peoples ; //The Foregrounde Image
IplImage* Img_peoples_t_1 ; //The Foregrounde Image
//========================================================
//The conuter=====================================
double C_motion ; //coefficient of motion
int Fall_cof_count ; //累积符合条件的frame数
int Usual_cof_count ; //累积正常情况的frame数
int cof_t_1 ; //上一时刻的状态,1-unusual, -1-usual
long int fal_time ; //跌倒后维持在地板上的时间,若超过此时间人未站起来即为跌倒事件
//================================================
//operators=======================================
bool check_updata ; //判断这些参数是否有被更新过, false = 未更新过, true = 更新过
bool Fall_detected; //若有跌倒事件发生为true
COEF_ELLIPSE* next_node; //用于link list,连结下一个节点用
//================================================
};
class CFallDetect {
private:
struct COEF_ELLIPSE* DataObj ; //人型对象的数据串结
int nWidth ; //the size of the frame
int nHeight ;
double pi ; //定义圆周率
int count ; //计算现在的frame数,用于判断建立background的frame数
int UpdateCount ; //updata background frequence
int people_area ; //define the size of people region
int Fill_val ; //The parameters of the function, cvFloodFill
CvPoint seed_point; //They are used in the function, Find_object
CvScalar new_val ; //填充前景时的新值,用于找寻前景位置
CvScalar lo_diff ; //填充颜色所需要的参数
CvScalar up_diff ; //填充颜色所需要的参数
CvConnectedComp comp; //填充完前景后所记录的信息皆在此
int peoples_detect_last_time ; //纪录上一时刻侦测到的人员数
int peoples_detect_this_time ; //记录此时刻侦测到的人员数
int coef_peoples_detect ; //记录前景detect的状态
double a ; //椭圆的短轴长
double b ; //椭圆的长轴长
double theta ; //椭圆与水平方向的夹角
int C_motionthr ; //动量的门坎值
double Ratiothr ; //长轴短轴比的门坎值
double Thetathr ; //椭圆与水平方向夹角的变化量门坎值
double Center_Positionthr ; //中心点变化量门坎值
int After_Falltim ; //跌到后维持低motion的时间长度门坎,用于判断跌倒事件的发生
int Fall_detect ; //若有跌倒事件发生为 1
int Fall_num ; //纪录跌倒的次数
public:
IplImage *Foreground ; //前景影像
IplImage *Object ; //找出的人的影像
IplImage *Background ; //背景模型
IplImage *frame_copy ; //待处理的影像
CFallDetect(int ,int ,int ,int ) ; //参数化建构子
virtual ~CFallDetect() ; //解构子
void DoDetect(bool, int, int, double); //最上层的函式
void UpdateBackgroundImage(int, int, double) ; // 建背景影像与更新
void GetForeground(char*, int ) ; // 取得前景影像
void RGB_TO_YUV(unsigned char *,double*,double* ,double *) ; //转换至YUVspace
void Find_Object() ; //侦测跌倒事件
void Ellipse_Parameter(IplImage*,int* ,int* ) ; //compute the parameters of the ellipse
void Set_Node(IplImage* ,int ,int ,double) ; //set new node
void Find_Match(struct COEF_ELLIPSE** ,int ,int ) ; //画面中有多人时,判断此时刻与上一
时刻match的资料
void Updata_Node(struct COEF_ELLIPSE** ,IplImage* ,int ,int ,double ); //更新节点数据
void Motion_History_Image(IplImage* ,IplImage*
,struct COEF_ELLIPSE* ) ;//compute the motion history image
int Fall_Detect(struct COEF_ELLIPSE *) ; //Detect Falls
int Alarm(); //若有跌倒事件发出警讯
};
2.成员函式说明
CFallDetect::CFallDetect(int w,int h,int Fall_tim,int obj_area){
目的:Initial all variables we will use in the program
方法:建构子
参数:
Parameter |
Type |
Description |
w |
int |
输入的画面的宽 |
h |
int |
输入的画面的高 |
Fall_tim |
int |
跌倒后躺在地下的时间,若超过此值即判定为跌倒 |
obj_area |
int |
设定是为人的前景区域大小的门坎值 |
}
CFallDetect::~CFallDetect(){
目的:释放内存
方法:解构子
}
int CFallDetect::Alarm(){
目的:发出alram讯号
方法:若全域参数Fall_detect为true时,回传1若否回传0
回传:
value |
Type |
Description |
0 |
int |
没有侦测到跌倒事件 |
1 |
int |
有侦测到跌倒事件 |
}
void CFallDetect::DoDetect(bool bulidbackground,int Sub_Thr,int update_freq,double update_we){
目的:整个系统的最上层
方法:依照所设定的时间点,呼叫所需要的副函式
参数:
Parameter |
Type |
Description |
bulidbackground |
Bool |
使否为建立背景模型的时间点 |
Sub_Thr |
int |
前景与背景分离出来所需相差的门坎值 |
update_freq |
int |
背景更新频率 |
update_we |
Double |
背景模型更新的权重 |
}
void CFallDetect::RGB_TO_YUV(unsigned char *DataSource,double* Y,double* U,double *V){
目的:转换RGB color domain到YUV color domain
方法:利用函式中的数学运算
参数:
Parameter |
Type |
Description |
DataSource |
unsigned char* |
指向IplImage影像data指针 |
Y |
double |
Y channel的值 |
U |
double |
U channel的值 |
V |
double |
V channel的值 |
}
void CFallDetect::UpdateBackgroundImage(int IntervalFrame, int select, double alpha){
目的:更新背景模型
方法:利用alpha权重,加权此时刻的输入影像与上一时刻的背景模型,运算后更新之。主要更新的区域为非前景的对应区域中,但每隔1000张画面针对整张画面做更新。
参数:
Parameter |
Type |
Description |
IntervalFrame |
int |
背景模型更新的频率 |
select |
int |
为0则做初始建立背景模型的运算,为1做更新背景模型的运算 |
alpha |
double |
更新背景模型的权重参数 |
}
void CFallDetect::GetForeground(char* COLOR_SPACE_TYPE, int background_thresh){
目的:取得前景影像
方法:直接将输入的影像对背景模型转到相同的color space下做相减,若pixel差值大于门坎值则判定为前景,此为需要经过膨胀与侵蚀的型态处理运算
参数:
Parameter |
Type |
Description |
COLOR_SPACE_TYPE |
char* |
欲处理运算的color space,提供 USE_HSV 、USE_YUV 与 USE_RGB可以使用。 |
background_thresh |
int |
前景与背景的差异门坎值 |
}
void CFallDetect::Find_Object(){
目的:判断跌倒事件,包含建立人型对象串结结构、更新人型对象节点、计算人型前景椭圆参数、判断跌倒
方法:呼叫对应的副函式,详细内容请看各个有使用到的副函式的说明
}
void CFallDetect::Find_Match(struct COEF_ELLIPSE** update_ptr,int object_x,int object_y){
目的:比对目前的资料是要更新到link list串结中那个节点
方法:已知目前中心点,去比对目前link list中所有的中心点看哪比距离最近,此节点即为我们欲更新的节点
参数:
Parameter |
Type |
Description |
update_ptr |
struct COEF_ELLIPSE** |
计算完成后即为欲更新的那个节点的内存位置。 |
object_xh |
int |
计算出来的前景中心点x坐标 |
object_y |
int |
计算出来的前景中心点y坐标 |
}
void CFallDetect::Updata_Node(struct COEF_ELLIPSE** update_ptr,IplImage* Img_Object,int
object_x,int object_y,double obj_size){
目的:比对出欲更新的节点后,利用此函式更新节点的信息
方法:将目前数值更新到目标节点中
参数:
Parameter |
Type |
Description |
update_ptr |
struct COEF_ELLIPSE** |
欲更新的那个节点的内存位置。 |
Img_Object |
IplImage* |
欲记录下来的此时侦测到的前景影像 |
object_x |
int |
计算出来的前景中心点x坐标 |
object_y |
Int |
计算出来的前景中心点y坐标 |
obj_size |
double |
该前景区域的pixel数 |
}
void CFallDetect::Ellipse_Parameter(IplImage* PeopleImg,int* x,int* y){
目的:计算fit 前景的椭圆的参数
方法:利用型态分析做处理
参数:
Parameter |
Type |
Description |
PeopleImg |
IplImage* |
侦测出来的单人前景影像 |
x |
int* |
计算出来后即为的前景中心点x坐标 |
y |
int* |
计算出来后即为的前景中心点y坐标 |
}
void CFallDetect::Set_Node(IplImage* Img_Temp_people,int object_x,int object_y,double region_size){
目的:当画面中有多个人员时,需要新增一个人员对象节点,以link list串结在一起
方法:初始化节点,并寻找现有的串结数据的最后一笔数据,并连结上去
参数:
Parameter |
Type |
Description |
Img_Temp_people |
IplImage* |
找到的新前景影像。 |
object_x |
int |
计算出来的前景中心点x坐标 |
object_y |
int |
计算出来的前景中心点y坐标 |
region_size |
double |
该前景区域的总共pixel数 |
}
void CFallDetect::Motion_History_Image(IplImage* people_copy,IplImage* Img_peoples
,struct COEF_ELLIPSE* Coef_peoples){
目的:计算动量参数
方法:利用Motion History Image(MHI)去计算动量的参数,MHI主要记录了现在与过去的动量。
参数:
Parameter |
Type |
Description |
people_copy |
IplImage* |
上一时刻的前景。 |
Img_peoples |
int |
此时刻的前景 |
Coef_peoples |
int |
人型对象串结欲更新节点的内存位置 |
}
int CFallDetect::Fall_Detect(struct COEF_ELLIPSE *People){
目的:判断跌倒
方法:先利用4个参数:动量大小、长轴短轴比的变化量、长轴与水平方向夹角的变化量、中心点位移的变化量,定义出有可能跌倒的时间点,在有可能跌倒的时间点后,于我们设定的时间内若不在有大动量产生,此时即确定侦测出一个跌倒事件。
参数:
Parameter |
Type |
Description |
People |
struct COEF_ELLIPSE * |
人型对象的数据串结。 |
回传:
Value |
Type |
Description |
-1 |
int |
没有跌倒事件 |
2 |
int |
侦测到跌倒事件 |
}
测试程序及序列
- 测试程序: http://rg4.net/p/easyiv/libfalldetect_test.7z
- 测试视频序列:http://rg4.net/p/easyiv/libfalldetect_sample.7z
测试程序说明:
- 指定测试视频:libxxxdetect_test.exe 111.avi,这个111.avi是你输入的视频文件。若不指定则默认打开当前目录下的libxxxdetect_sample.avi文件,若这个文件不存在则打开电脑上的摄像头。
- 开始后请先通过鼠标拖拉点选设置检测区域。
- 附带文件:无。
- 测试程序快捷键:按r重新设定ROI检测区域,按p暂停处理,按t or ESC键中止。
您好,偶然看到個溫故知新的偵測人跌倒的部分,感到非常的欣喜,
不過學生有些疑慮,多人偵測部分,說明是有新的人進來就新增加連結,可是人在影像中一直在移動,請問是怎麼確認新加進的資料確實是新進入的物體?
同理中….也不懂怎麼在多人當中.判斷到有人跌倒….
Dear Lori,
我这边这个算法实现的还是非常单纯(如果要说的好听点的话)的。其只是判断有没有新的人横向的类似人体的差异图(取灰度图与背景图比较)出现,并持续一段时间(可设定参数)。若是则认为有人物跌倒,反之认为正常。
移动的人不会被误判为跌倒。但若是画面中有多人,并且多人围观一个跌倒的人的话,再并且多人中有人与跌倒的人重合,则容易被忽略。
非常感謝您的回應!
確實在重和人體過多的時候,取得的前景會是橫向的.
感覺您的演算法適用在距離較遠,人與人較為分散的廣場上.
若是室內影像太近,可能就會誤判了.(希望沒有理解錯誤.)
有種豁然開朗的感覺.
謝謝您的說明!
是的.
不仅如此,其实绝大多数的这种intellegent算法都会有类似的局限性,就是需要在一种特定的场景里才能正确的、准确的工作。
不然,就得先从机器视觉开始做起,先做测距、做标定,然后再来分析识别具体场景及物体。。。。
但这对一般程序员来说都很难