opencv,動態目標檢測


可以從靜態場景中檢測出移動的物體,並對目標進行標記和計數。本文的主要工作包括:在圖像預處理階段,本文采用HSV色彩空間減輕了目標陰影對目標提取的影響,采用中值濾波器去掉了椒鹽噪聲,采用圖像二值化使圖像變的簡單,采用圖像學去噪中的腐蝕和膨脹分別提取消除圖像噪聲和填充圖像空洞。在動態目標識別的階段,采用三幀差分法提取出動態的目標,並用更新運動歷史圖像的方法來減輕重影現象。最后通過在原圖像幀中畫矩形框的方式來標記動態目標的位置,並顯示當前運動目標的個數,以及當前幀率、系統運行時間、當前幀數等系統信息。

 gra.h

#ifndef GRA_H_INCLUDED
#define GRA_H_INCLUDED

#endif // GRA2_H_INCLUDED
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <time.h>
#include <ctype.h>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;


//聲明函數
void bgrToGray(IplImage *src,IplImage *dst);
IplImage* doPyrDown( IplImage* src );
IplImage* doPyrUp( IplImage* src );
void graAbsDiff(IplImage* src1,IplImage* src2,IplImage* dst);
void graZero(IplImage* src);
void graAdd(IplImage* src1,IplImage* src2,IplImage* dst);



//轉化為灰度圖,傳入三通道的圖像,返回單通道的圖像
void bgrToGray(IplImage *src,IplImage *dst)
{


    if(src==NULL)
    {
        printf("bgrToGray傳入的圖像為空");
        return;
    }
    int i,j,k,avg;
    int height,width,step,channels;
    uchar *data,*dstdata;
    height=src->height;
    width=src->width;
    step=src->widthStep;
    channels=src->nChannels;
    data=(uchar*)src->imageData;
    dstdata=(uchar*)dst->imageData;


    for(i=0; i<height; i++)
    {
        for(j=0; j<width; j++)
        {
            //for(k=0; k<lena->nChannels; k++)
            // {

            avg=0.072169*data[ i * step+ j * channels + 1]+0.715160*data[ i *step+ j * channels + 2]+0.212671*data[ i *step+ j *channels + 3];
            //data[i*lena->widthStep+j*lena->nChannels+k]=255-data[ i * lena->widthStep+ j * lena->nChannels + k];
            // }

            dstdata[i*dst->widthStep+j]=avg;

        }
    }
}


IplImage* doPyrDown( IplImage* src )
{
    IplImage* result = cvCreateImage( cvSize( src -> width/2, src -> height/2 ), src -> depth, src -> nChannels );
    //庫函數調用cvPyrDown
    cvPyrDown( src, result, CV_GAUSSIAN_5x5 );  //高斯變換

    return  result;
}
IplImage* doPyrUp( IplImage* src )
{
    IplImage* result = cvCreateImage( cvSize( src -> width*2, src -> height*2 ), src -> depth, src -> nChannels );
    cvPyrUp( src, result, CV_GAUSSIAN_5x5 );

    return result;
}


//兩個圖像矩陣之差的絕對值
void graAbsDiff(IplImage* src1,IplImage* src2,IplImage* dst)
{
    if(src1==NULL||src2==NULL)
    {
        printf("graAbsDiff傳入的圖像為空");
        return;
    }
    int i,j,k;
    int height,width,step,channels;
    uchar *dataSrc1,*dataSrc2,*dataDst;
    height=src1->height;
    width=src1->width;
    step=src1->widthStep;
    channels=src1->nChannels;
    dataSrc1=(uchar*)src1->imageData;
    dataSrc2=(uchar*)src2->imageData;
    dataDst=(uchar*)dst->imageData;
    // dstdata=(uchar*)dst->imageData;
    //printf("\ngraZero(IplImage* src): 將要清零的圖像通道為%d",channels);

    for(i=0; i<height; i++)
    {
        for(j=0; j<width; j++)
        {
            for(k=0; k<channels; k++)
            {
                dataDst[i*step+j*channels+k]=abs(dataSrc1[i*step+j*channels+k]-dataSrc2[i*step+j*channels+k]);
            }
        }
    }

}
//清零矩陣值
void graZero(IplImage* src)
{
    if(src==NULL)
    {
        printf("graZero傳入的圖像為空");
        return;
    }
    int i,j,k;
    int height,width,step,channels;
    uchar *data,*dstdata;
    height=src->height;
    width=src->width;
    step=src->widthStep;
    channels=src->nChannels;
    data=(uchar*)src->imageData;
    // dstdata=(uchar*)dst->imageData;
    //printf("\ngraZero(IplImage* src): 將要清零的圖像通道為%d",channels);

    for(i=0; i<height; i++)
    {
        for(j=0; j<width; j++)
        {
            for(k=0; k<channels; k++)
            {
                data[i*step+j*channels+k]=0;
            }
        }
    }
}

//計算兩個數組的元素級的和
void graAdd(IplImage* src1,IplImage* src2,IplImage* dst)
{
    if(src1==NULL||src2==NULL)
    {
        printf("graAbsDiff傳入的圖像為空");
        return;
    }
    int i,j,k;
    int height,width,step,channels;
    uchar *dataSrc1,*dataSrc2,*dataDst;
    height=src1->height;
    width=src1->width;
    step=src1->widthStep;
    channels=src1->nChannels;
    dataSrc1=(uchar*)src1->imageData;
    dataSrc2=(uchar*)src2->imageData;
    dataDst=(uchar*)dst->imageData;
    // dstdata=(uchar*)dst->imageData;
    //printf("\ngraZero(IplImage* src): 將要清零的圖像通道為%d",channels);

    for(i=0; i<height; i++)
    {
        for(j=0; j<width; j++)
        {
            for(k=0; k<channels; k++)
            {
                dataDst[i*step+j*channels+k]=abs(dataSrc1[i*step+j*channels+k]+dataSrc2[i*step+j*channels+k]);
            }
        }
    }

}

//二值化處理
void graThreshold(IplImage* src1,IplImage* dst,double threshold,double max_value)
{
    if(src1==NULL)
    {
        printf("\ngraThreshold沒有傳入圖像");
        return ;
    }
    int i,j,k;
    int height,width,step,channels;
    uchar *dataSrc1,*dataDst;
    height=src1->height;
    width=src1->width;
    step=src1->widthStep;
    channels=src1->nChannels;
    if(channels!=1)
    {
        printf("將要把彩色圖像二值化");
        bgrToGray(src1,dst);
    }
    dataSrc1=(uchar*)src1->imageData;
    dataDst=(uchar*)dst->imageData;
    // dstdata=(uchar*)dst->imageData;
    //printf("\ngraZero(IplImage* src): 將要清零的圖像通道為%d",channels);

    for(i=0; i<height; i++)
    {
        for(j=0; j<width; j++)
        {

            if(dataSrc1[i*step+j]>threshold)
            {
                dataDst[i*step+j]=max_value;
            }
            else
            {
                dataDst[i*step+j]=0;
            }
        }
    }
}

//中值濾波
bool graFilterMid(IplImage* &image,int k)
{
    if(image==NULL)
    {
        printf("\nFilterMid沒有傳入圖像");
        return false;
    }
    uchar *ImagePix=(uchar *)image->imageData;
    int m=(k-1)/2;
    for(int x=m; x<image->height-m; ++x)
    {
        for(int y=m; y<image->width-m; ++y)
        {
            uchar PixArray[100];
            int t=0;
            for(int i=-m; i<m+1; ++i)
            {
                for(int j=-m; j<m+1; j++)
                {
                    PixArray[t++]=((uchar *)image->imageData)[(x+i)*image->widthStep+y+j];
                }
            }


            //冒泡排序
            for(int i=0; i<k*k-1; ++i)
                for(int j=0; j<k*k-i-1; ++j)
                {
                    if(PixArray[j]>PixArray[j+1])
                    {
                        uchar k=PixArray[j];
                        PixArray[j]=PixArray[j+1];
                        PixArray[j+1]=k;
                    }
                }
            ImagePix[x*image->widthStep+y]=PixArray[(k*k-1)/2];
        }
    }
    return true;
}
void saturate_SV(IplImage* image)
{
    for(int y = 0; y < image -> height; ++y)
    {
        //ptr指針指向第y行的起始位置
        uchar* ptr=(uchar*)(
                       image -> imageData + y * image->widthStep);//(字節類型指針:uchar*)
        //改變S和V在x維的值
        for(int x = 0; x < image -> width; ++x)
        {
            //ptr[3 * x + 0] =180;
            //ptr[3 * x + 1] =0;
            ptr[3 * x + 2] =255;
        }
    }
}

main.cpp

#include "gra.h"

using namespace cv;
using namespace std;

// various tracking parameters (in seconds)
const double MHI_DURATION = 0.5;
const double MAX_TIME_DELTA = 0.5;
const double MIN_TIME_DELTA = 0.05;
const int N = 3;
int num=0;

//設置當目標區域的面積大於某個值時才畫矩形框
const int CONTOUR_MAX_AERA = 1600;

// ring pFrame buffer
IplImage **buf = 0;
int last = 0;

// 臨時圖像
IplImage *mhi = 0; // MHI: motion history image

int filter = CV_GAUSSIAN_5x5;

CvPoint pt[4];


int main(int argc, char** argv)
{
    printf("程序開始,按ESC可以退出程序");
    IplImage *pFrame = NULL;
    IplImage *pFrame1 = NULL;

    int inMonitor=0;
    int num=0;
    float fps;
    char str[5];
    double t = 0;
    int countsInMonitor=0;
    double runT=clock();
    char runTime[10];
    double runT2;
    char* str3;
    int seconds=0;
    IplImage* motion = 0;
    CvCapture* pCapture = 0;

    int width,height;

    if(argc>2)
    {
        fprintf(stderr, "Usage: bkgrd [video_file_name]\n");
        return -1;
    }
    //如果沒有傳入參數,則讀取攝像頭
    if (argc ==1)
    {
        if( !(pCapture = cvCaptureFromCAM(-1)))
        {
            fprintf(stderr, "Can not open camera.\n");
            return -2;
        }
    }

    //打開視頻文件
    if(argc == 2)
        if( !(pCapture = cvCaptureFromFile(argv[1])))
        {
            fprintf(stderr, "Can not open video file %s\n", argv[1]);
            return -2;
        }


    if( pCapture )
    {


        pFrame=cvQueryFrame( pCapture );
        IplImage *silh1,*silh2,*silh;
        pFrame1 = cvCreateImage( cvSize(pFrame->width,pFrame->height), 8, 3 );
        width=pFrame->width;
        height=pFrame->height;

        //標記監控區域
        CvPoint pt1_Rect;
        CvPoint pt2_Rect;
        CvPoint pt3_Rect;
        CvPoint pt4_Rect;
        pt1_Rect.x=(1.0/5)*width;
        pt1_Rect.y=0;
        pt2_Rect.x=(1.0/5)*width;
        pt2_Rect.y=pFrame->height;
        pt3_Rect.x=(4.0/5)*width;
        pt3_Rect.y=0;
        pt4_Rect.x=(4.0/5)*width;
        pt4_Rect.y=pFrame->height;
        int thickness=1;
        int line_type=8;
        CvScalar color=CV_RGB(100,100,100);
        //設定監控區域
        CvRect ROI_rect;
        ROI_rect.x=pt1_Rect.x;
        ROI_rect.y=pt1_Rect.y;
        ROI_rect.width=pt3_Rect.x-pt1_Rect.x;
        ROI_rect.height=pt2_Rect.y-pt1_Rect.y;
        //有東西進入監控區域是輸入的信息
        char warnings[20];



        silh1 = cvCreateImage( cvSize(pFrame->width,pFrame->height), 8, 1 );
        silh2 = cvCreateImage( cvSize(pFrame->width,pFrame->height), 8, 1 );
        silh = cvCreateImage( cvSize(pFrame->width,pFrame->height), 8, 1 );
        cvNamedWindow("Motion",CV_WINDOW_AUTOSIZE);
        cvMoveWindow("Motion",100,100);
        for(;;)
        {
            t = (double)getTickCount();
            num++;

            if( !cvGrabFrame( pCapture ))
                break;
            pFrame = cvRetrieveFrame( pCapture );

            if( pFrame )
            {
                if( !motion )
                {
                    motion = cvCreateImage( cvSize(pFrame->width,pFrame->height), 8, 1 );
                    cvZero( motion );
                    motion->origin = pFrame->origin;
                }
            }

            //update_mhi( image, motion);
            //clock()返回處理器調用某個進程或函數所花費的時間。

            double timestamp = clock()/100.; // get current time in seconds
            CvSize size = cvSize(pFrame->width,pFrame->height); // get current frame size
            int i, j, idx1, idx2,idx3;

            uchar val;
            float temp;
            IplImage* pyr = cvCreateImage(cvSize((size.width & -2)/2, (size.height & -2)/2), 8, 1 );
            CvMemStorage *stor;
            CvSeq *cont, *result, *squares;
            CvSeqReader reader;

            //如果mhi的值為空。創建mhi,buf。第一次進入循環時運行
            if( !mhi || mhi->width != size.width || mhi->height != size.height )
            {
                if( buf == 0 )
                {
                    buf = (IplImage**)malloc(N*sizeof(buf[0]));
                    memset( buf, 0, N*sizeof(buf[0]));
                }

                for( i = 0; i < N; i++ )
                {
                    cvReleaseImage( &buf[i] );
                    buf[i] = cvCreateImage(size, IPL_DEPTH_8U, 1 );
                    printf("sd");
                    cvZero( buf[i] );
                }
                cvReleaseImage( &mhi );
                mhi = cvCreateImage( size, IPL_DEPTH_32F, 1 );
                cvZero( mhi ); // clear MHI at the beginning
            } // end of if(mhi)

            cvCvtColor(pFrame,pFrame1,CV_BGR2HSV);
            saturate_SV(pFrame1);
            cvCvtColor(pFrame1,pFrame1,CV_HSV2BGR);
            //cvCvtColor( pFrame, buf[last], CV_BGR2GRAY ); // convert frame to grayscale
            bgrToGray( pFrame1, buf[last]);
            idx1 = last;
            idx2 = (last + 1) % N; // index of (last - (N-1))th frame
            idx3=(idx2+1)%N;
            last = idx3;
            cvShowImage("asda",pFrame1);
            // 做幀差
//cvShowImage("asd",buf[idx1]);

            graAbsDiff( buf[idx1], buf[idx2], silh1 ); // get difference between frames
            graAbsDiff( buf[idx2], buf[idx3], silh2 ); // get difference between frames
            graAdd(silh1,silh2,silh);
            cvShowImage("aaaa",silh);
            // 對差圖像做二值化
            graThreshold( silh, silh, 50, 255); // and threshold it

            //去掉影像(silhouette) 以更新運動歷史圖像
            cvUpdateMotionHistory( silh, mhi, timestamp, MHI_DURATION ); // update MHI

            // convert MHI to blue 8u image
            //線性變換數組,把MHI變為8位的圖像
            cvCvtScale( mhi, motion, 255./MHI_DURATION,
                        (MHI_DURATION - timestamp)*255./MHI_DURATION );


            // 中值濾波,消除小的噪聲
            graFilterMid(motion,3);
            // cvShowImage("中值濾波之后",motion);
            // 向下采樣,去掉噪聲
            //cvPyrDown( motion, pyr, 7 );
            pyr=doPyrDown(motion);
            cvDilate( pyr, pyr, 0, 1 );  // 做膨脹操作,消除目標的不連續空洞
            //cvPyrUp( pyr, motion, 7 );
            motion=doPyrUp(pyr);


            cvSetImageROI(motion,ROI_rect);
            //
            // 下面的程序段用來找到輪廓
            //
            // Create dynamic structure and sequence.
            stor = cvCreateMemStorage(0);
            cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint) , stor);

            // 找到所有輪廓
            cvFindContours( motion, stor, &cont, sizeof(CvContour),
                            CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
            /*
                for(;cont;cont = cont->h_next)
                {
                    // Number point must be more than or equal to 6 (for cvFitEllipse_32f).
                    if( cont->total < 6 )
                        continue;

                    // Draw current contour.
                    cvDrawContours(image,cont,CV_RGB(255,0,0),CV_RGB(255,0,0),0,1, 8, cvPoint(0,0));
                }  // end of for-loop: "cont"
            */

            cvResetImageROI(motion);
            cvSetImageROI(pFrame,ROI_rect);
            // 直接使用CONTOUR中的矩形來畫輪廓
            countsInMonitor=0;
            for(; cont; cont = cont->h_next)
            {
                CvRect r = ((CvContour*)cont)->rect;
                if(r.height * r.width > CONTOUR_MAX_AERA) // 面積小的方形拋棄掉
                {
                    cvRectangle( pFrame, cvPoint(r.x,r.y),
                                 cvPoint(r.x + r.width, r.y + r.height),
                                 CV_RGB(255,0,0), 1, CV_AA,0);
                    inMonitor=1;
                    countsInMonitor++;
                }

            }

            // free memory
            cvReleaseMemStorage(&stor);
            cvReleaseImage( &pyr );

            cvResetImageROI(pFrame);
            //用來顯示當前第幾幀
            CvFont font;
            cvInitFont( &font, CV_FONT_HERSHEY_SIMPLEX,0.5, 0.5, 0, 1, 1);


            //顯示fps
            t = ((double)cvGetTickCount() - t) / getTickFrequency();
            fps = 1.00 / t;
            //sprintf(str, "%5f", fps);
            sprintf(str,"fps: %.1f",fps);
            //addTail(str," f/s ");
            //putText(pFrame, str, Point(0,30), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0,0,255));
            cvPutText(pFrame, str , cvPoint(20,pFrame->height-20), &font, CV_RGB(255,0,0));

            //顯示運行時間
            runT2=clock();
            seconds=(int)runT2/1000;
            sprintf(runTime,"time: %ds",seconds);
            cvPutText(pFrame, runTime, cvPoint(100,pFrame->height-20), &font, CV_RGB(255,0,0));

            char texta[10];
            //把int型num轉化為字符串
            sprintf(texta,"%dth frame",num);
            //addTail(texta," frame");
            cvPutText(pFrame, texta , cvPoint(200,pFrame->height-20), &font, CV_RGB(255,0,0));

            //如果有人進入則提醒
            if(inMonitor==1)
            {
                sprintf(warnings,"%d in monitor area",countsInMonitor);
                cvPutText(pFrame, warnings , cvPoint(0.25*width,height-60), &font, CV_RGB(255,0,0));
            }

            //標記監控區域
            cvLine( pFrame, pt1_Rect, pt2_Rect,color ,thickness, line_type, 0 );
            cvLine( pFrame, pt3_Rect, pt4_Rect,color ,thickness, line_type, 0 );

            cvShowImage( "Motion", pFrame );
            inMonitor=0;
            if( cvWaitKey(10) >= 0 )
                break;
        }
        cvReleaseCapture( &pCapture );
        cvDestroyWindow( "Motion" );
        printf("\n程序結束,一共運行了%d s",seconds);
    }

    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM