公司项目需要检测运动物体,我对opencv也没啥研究,google了好久看了好多方法,最简单的就是差分与高斯背景建模了。
旁边搞c++的同事正在搞更nb的算法,等出来了 我再转成C#版的分享。
先看差分
//移动窗口
[System.Runtime.InteropServices.DllImportAttribute("opencv_highgui2410.dll", EntryPoint = "cvMoveWindow")]
public static extern void cvMoveWindow([System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string name, int x, int y);
//代表x帧差分,可以*更改
static int USE_N_FRAMES_DIFF = ; public void PicDiff(string videoPath)
{
int iFrameIndex = ; IntPtr pIplGrayImg = IntPtr.Zero; IntPtr[] pIplFrameDiff = new IntPtr[USE_N_FRAMES_DIFF - ]; IntPtr[] pIplFrame = new IntPtr[USE_N_FRAMES_DIFF]; IntPtr CatchFrame = CvInvoke.cvCreateFileCapture(videoPath);
// 得到总帧数
var count = CvInvoke.cvGetCaptureProperty(CatchFrame, Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_COUNT);
// 视频宽度
int wd = (int)CvInvoke.cvGetCaptureProperty(CatchFrame, Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_WIDTH);
// 视频高度
int hg = (int)CvInvoke.cvGetCaptureProperty(CatchFrame, Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT);
//// 当前帧位置
//CvInvoke.cvGetCaptureProperty(CatchFrame, Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_POS_FRAMES);
//// 帧频
CvInvoke.cvGetCaptureProperty(CatchFrame, Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FPS); CvInvoke.cvNamedWindow("source");
CvInvoke.cvNamedWindow("Out");
cvMoveWindow("source", , );
cvMoveWindow("Out", , );
IntPtr FrameImg; IntPtr rawImage = IntPtr.Zero;
rawImage = CvInvoke.cvCreateImage(new Size(wd, hg), Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, );
IntPtr pIplFrameDiffOr = IntPtr.Zero;
IntPtr pIplFrameDiffOrCC = IntPtr.Zero;
IntPtr pIplFrameSmooth = IntPtr.Zero;
pIplFrameDiffOr = CvInvoke.cvCreateImage(new Size(wd, hg), Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, );
pIplFrameDiffOrCC = CvInvoke.cvCreateImage(new Size(wd, hg), Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, );
pIplFrameSmooth = CvInvoke.cvCreateImage(new Size(wd, hg), Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, ); while ((FrameImg = CvInvoke.cvQueryFrame(CatchFrame)) != IntPtr.Zero)
{ Rectangle cr = CvInvoke.cvGetImageROI(FrameImg); pIplGrayImg = CvInvoke.cvCreateImage(cr.Size, Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, ); CvInvoke.cvCvtColor(FrameImg, pIplGrayImg, Emgu.CV.CvEnum.COLOR_CONVERSION.BGR2GRAY); CvInvoke.cvSaveImage(savename, pIplGrayImg, IntPtr.Zero);
pIplFrame[iFrameIndex % USE_N_FRAMES_DIFF] = pIplGrayImg; if (iFrameIndex >= USE_N_FRAMES_DIFF - )
{
for (int i = ; i < USE_N_FRAMES_DIFF - ; i++)
{ CvInvoke.cvAbsDiff(pIplFrame[i], pIplFrame[i + ], rawImage);
pIplFrameDiff[i] = rawImage;
CvInvoke.cvThreshold(pIplFrameDiff[i], pIplFrameDiff[i], , , Emgu.CV.CvEnum.THRESH.CV_THRESH_BINARY);
//上面第三个参数为设置的阀值以此来根据物体运动时前后帧的差异产生白点
} for (int i = ; i < USE_N_FRAMES_DIFF - ; i++)
{
CvInvoke.cvOr(pIplFrameDiff[i], pIplFrameDiff[i + ], pIplFrameDiffOr, IntPtr.Zero); if (i + < USE_N_FRAMES_DIFF - )
{
CvInvoke.cvCopy(pIplFrameDiffOr, pIplFrameDiff[i + ], IntPtr.Zero);
} }
} CvInvoke.cvShowImage("source", FrameImg);
CvInvoke.cvShowImage("Out", pIplFrameDiffOr); CvInvoke.cvWaitKey();
iFrameIndex++; }
}
程序运行结果如图所示
高斯背景建模
public void guassModel(string videoPath)
{
int iFrameIndex = ;
IntPtr CatchFrame = CvInvoke.cvCreateFileCapture(videoPath);
// 得到总帧数
var count = CvInvoke.cvGetCaptureProperty(CatchFrame, Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_COUNT);
// 视频宽度
int wd = (int)CvInvoke.cvGetCaptureProperty(CatchFrame, Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_WIDTH);
// 视频高度
int hg = (int)CvInvoke.cvGetCaptureProperty(CatchFrame, Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_HEIGHT);
//// 帧频
CvInvoke.cvGetCaptureProperty(CatchFrame, Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FPS);
IntPtr background = CvInvoke.cvCreateImage(new Size(wd, hg), Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, );
IntPtr foreground = CvInvoke.cvCreateImage(new Size(wd, hg), Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_8U, ); IntPtr FrameImg;
Emgu.CV.VideoSurveillance.BGStatModel<Bgr> bg = null;
CvInvoke.cvNamedWindow("bg");
CvInvoke.cvNamedWindow("fg");
CvInvoke.cvNamedWindow("source");
cvMoveWindow("bg", , );
cvMoveWindow("gf", , );
cvMoveWindow("source", , );
while ((FrameImg = CvInvoke.cvQueryFrame(CatchFrame)) != IntPtr.Zero)
{
Image<Bgr, byte> FramePic = new Image<Bgr, byte>(wd, hg);
CvInvoke.cvCopy(FrameImg, FramePic, IntPtr.Zero);
iFrameIndex++;
if (iFrameIndex == )
{
//高斯背景建模参数
Emgu.CV.Structure.MCvGaussBGStatModelParams pstruct = new MCvGaussBGStatModelParams();
pstruct.win_size = ;
pstruct.n_gauss = ;
pstruct.bg_threshold = 0.7;
pstruct.std_threshold = 3.5;
pstruct.minArea = ;
pstruct.weight_init = 0.333;
pstruct.variance_init = ;
bg = new Emgu.CV.VideoSurveillance.BGStatModel<Bgr>(FramePic, ref pstruct);
}
else
{
CvInvoke.cvShowImage("source", FrameImg);
CvInvoke.cvWaitKey();
//更新
bg.Update(FramePic);
background = bg.BackgroundMask;
CvInvoke.cvShowImage("bg", background);
CvInvoke.cvWaitKey();
foreground = bg.ForegroundMask;
CvInvoke.cvShowImage("fg", foreground);
CvInvoke.cvWaitKey();
}
}
CvInvoke.cvDestroyWindow("source");
CvInvoke.cvDestroyWindow("bg");
CvInvoke.cvDestroyWindow("fg");
}
运行结果如图
高斯的缺点就是受光照影响太大,近距离效果不好。
种一棵树最好的时间是十年前,其次是现在。