OpenCV温故而知新: 人流统计 2


应用场景

人流统计主要被广泛应用于一些商场,或者步行街等与人流相关的商业活动场所,这也是以人流来评价一个地方(或者是在一定时间内)的商业价值的一个重要手段。除这个人流统计之外,其实还有一个类似的算法是车流统计,车流统计主要用于高速公路或者马路。但从算法实现、技术角度来讲,这二者其实并没有太大的差别。

补充说明

本算法在OpenCV的自带示例里有完整代码,你完全可以参考该代码进行学习和修改。

全域类别说明

struct Object

{

bool existFlag ;                        //使用 0 no 1 yes

int missedFrame;                            //追丢的frame数

int grade;                                        //跟追踪到的物体距离分数

CvPoint mean[200];                //轨迹点

int circleFrame;                       //在头部有圆形的张数

}

class HumanCounter

{

private:

CvPoint pt0,pt1,pt2,pt3,pt4,pt5;          //绘图时使用

IplImage*    templ              ;    //人的pattern

IplImage*    hsv                ;    //当前的frame

IplImage*    hsv_head           ;    //框人的部份

IplImage*    newbg_model        ;   //一开始的背景

IplImage*    templ_new          ;    //scale之后新的pattern

IplImage*    backgroundImg      ;    //更新过后的背景

IplImage*    backgroundImgModel ;    //背景相减时使用

IplImage*    foregroundImg      ;    //前景 Gray Level

IplImage*    result             ;     //pattern比对的结果

IplImage*    trace              ;     //轨迹

IplImage *    regrayImg         ;     //撷取人的上部

IplImage *   reBGRImg          ;    //前景 BGR

CvSeq  *    circles            ;     //记录圆的Sequence

IplImage *mask                 ;    //屏蔽

CvMemStorage* storage          ;     //记录圆的Memory

int a,b,FrameNum;             //in 人数 , out 人数 ,第几张frame

int p,i,j,tmpCounte,;                //  用于回圈

int nWidth,nHeight,chnal,nWidthStep;    //读入影像的宽.高.频道

int x1, y1, x2, y2;                     //定义的line

double nA,nB;                    //y=nA*x+nB

CvRect nrect;                //set ROI

bool system_on;              //开始数人数

bool SetThreshold_on;         //人的大小,背景参数设定完成

bool SetRoi_on;              //ROI设定完成

bool Circle_on;               //使用找人头的功能

int block_x1[10],block_y1[10],block_x2[10],block_y2[10];

//存框框左上及右下坐标

float nscale;                  //pattern scale的大小

int nthreshold;                 //背景相减的threshold

int center_x[1000],center_y[1000], CenterNum;

//每个pattern比对到的中心 以及个数

int ptr[10];                          //轨迹中有几个转折点

bool circle_ok[10];                //每次框人时是否有圆

public:

struct Object humanObj[10];            //宣告10个物体

HumanCounter(int width, int height,int nChannels,int widthStep ,int t=0);

//将frame的信息传入 , t=1时启用找圆功能

t=0 时关闭找圆功能

//默认值是关闭状态

~HumanCounter();

SetThreshold(float scale,int threshold ); //读取人的大小及背景参数的值

SetRoi(int ra,int ry,int rw,int rh,int sx,int sy,int sw,int sh); //读取设定的ROI范围

loadtempl();                         //读取pattern

loadframe(IplImage*  tmp_frame);  //读取当前frame

loadframe(BYTE* pImageArray);    //可读取BYTE* pImageArray

subimage();                         //背景相减

FindCircle();                         //找圆

Template_Match();                    //比对人型样板

GetPoint(int t,int k);       //读取框人的坐标    可以不读取

//t从0到10代表每张frame画框的次数,最多10个

//K=1时读取左上x坐标

//K=2时读取左上y坐标

//K=3时读取右下x坐标

//K=4时读取右下y坐标

ResetPoint();            //reset框人的坐标

//使用GetPoint(int t,int k)  时一定要用

getInOut(int k);        // k=1取得in 人数 , k=2取得out 人数

resetInOut();                        //人数归零

IplImage* GetImage(int imgnum);       //回传最后结果

//Imgnum=1 回传当前frame

//Imgnum=2 回传前景  BGR

//Imgnum=3 回传轨迹

releaseimage();                      //释放所有内存空间

};

成员函式说明

HumanCounter::HumanCounter(int width, int height,int nChannels,int widthStep,int t){          

目的:

initial all variables we’ll use the program

方法:

set all variable will use

参数:

Parameter

Type

Description

width

int

frame的宽

height

int

frame的高

nChannels

int

frame的channels

widthStep

int

输入影像widthStep

t

int

t=1 启用circle 功能,t=0关闭circle 功能

}

HumanCounter::~HumanCounter(){                                                     

目的: 释放宣告的内存空间

}

HumanCounter::SetThreshold(float scale,int threshold){                                             

目的:

将pattern的大小及背景参数读入

方法:

将pattern的大小读入及背景参数读入,完成后SetThreshold_on设为true

参数:

Parameter

Type

Description

scale

float

pattern的大小

threshold

int

背景参数

 

}                                                                                    

 

 

HumanCounter::SetRoi(int rx,int ry,int rw,int rh,,int sx,int sy,int sw,int sh){                                                   

目的:

自动产生进出线方程式

方法:

将使用者圈选的innerROI范围(sx,sy,sw,sh)和调整后的outerROI范围(rx,ry,rw,rh)读入,并自动在调整后的ROI产生进出线(距离上面4/5高的地方)及其方程式,完成后SetRoi_on 设为true

参数:

Parameter

Type

Description

rx

int

outerROI左上角x坐标

ry

int

outerROI左上角y坐标

rw

int

outerROI的宽

rh

int

outerROI的高

sx

int

innerROI左上角x坐标

sy

int

innerROI左上角y坐标

sw

int

innerROI的宽

sh

int

innerROI的高

 

}                                                                                   

 

HumanCounter::loadtempl(){                                                                   

目的:读取body pattern

}                                                                                    

HumanCounter:: subimage()                                                           

目的:

建立foreground

方法:

直接将hsv、backgroundImg作背景相减,并搭配threshold来产生前景影像,放置于foregroundImg的内存指针区块内

}

HumanCounter:FindCircle()                                                           

目的:

寻找有圆的地方

方法:

宣告一个 head_rect大小的范围,将人的框框上部1/5的地方resize成原大小的1/9,并在附近搜寻圆形,若有圆则将circle_ok[CenterNum-10] 设为true

}

HumanCounter:Template_Match()                                                      

目的:

使用人形模型跟前景比对

方法:

建立新pattern,设定result(pattern比对结果)的大小,regrayImg 当前影像只包括框人的上半1/5,建立ROI后开始比对,将比对结果高的区域框起来后,覆盖掉附近区域并将中心点存入center_x , center_y ,CenterNum++,最后释放result、templ_new、regrayImg

}

HumanCounter:: loadframe(IplImage*    tmp_frame)                                    

目的:

把video读进来做处理

方法:

1.背景相减得到foreground=> subimage()2.画出进出线3.开始做比对=> Template_Match()

4.判断框框之间的关系,若有物体出现跟先前的物体作比对

5.当物体轨迹存在且missedframe大于5时,判断其轨迹是否进入或离开(计数进出人数),完成后清除轨迹

参数:

 

Parameter

Type

Description

tmp_frame

IplImage

输入来源影像

}

HumanCounter:: getInOut(int k){                                                       

目的:

得到进出人数

方法:

当k=1回传in人数;当k=2回传out人数

参数:

Parameter

Type

Description

k

int

回传进出人数的判断

}

HumanCounter::GetPoint(int t,int k){                                                    

目的:

得到画框的点坐标

方法:

k=1时读取左上x坐标;k=2时读取左上y坐标;k=3时读取右下x坐标;k=4时读取右下y坐标

参数:

Parameter

Type

Description

t

int

从0到10代表每张frame画框的次数,最多10个

k

int

读取坐标的选择

}

HumanCounter::ResetPoint(){                                                           

目的:

将所有框框坐标归零

}

HumanCounter:: resetInOut()                                                          

目的:

进出人数归零

}

HumanCounter:: releaseimage()                                                        

目的:

Release variables and storage

}

IplImage* HumanCounter:: GetImage(int imgnum)                                       

目的:

回传影像数据

方法:

imgnum=1=>回传当前影像;imgnum=2 =>回传前景 BGR;imgnum=3 =>回传轨迹

参数:

Parameter

Type

Description

imgnum

int

判断回传影像数据

}

测试程序及序列

测试程序: http://rg4.net/p/easyiv/libhumancounter_test.7z

测试视频序列:http://rg4.net/p/easyiv/libhumancounter_sample.7z


Leave a Reply to zhenlong pu Cancel reply

Your email address will not be published. Required fields are marked *

2 thoughts on “OpenCV温故而知新: 人流统计

  • zhenlong pu

    你好,我是opencv的初学者,你这个很不错,能不能共享下源码,让我学习一下,我是菜鸟对视频分析不是很清楚