~slist/qmotion/2.3

« back to all changes in this revision

Viewing changes to motiondetector.cpp

  • Committer: Stephane List
  • Date: 2010-12-12 14:54:49 UTC
  • Revision ID: slist@lilotux.net-20101212145449-t3p63qypyb1jsgyn
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2008, 2009, 2010 by Stephane List                       *
 
3
 *   slist@lilotux.net                                                     *
 
4
 *                                                                         *
 
5
 *   This program is free software; you can redistribute it and/or modify  *
 
6
 *   it under the terms of the GNU General Public License as published by  *
 
7
 *   the Free Software Foundation; either version 2 of the License, or     *
 
8
 *   (at your option) any later version.                                   *
 
9
 *                                                                         *
 
10
 *   This program is distributed in the hope that it will be useful,       *
 
11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 
12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 
13
 *   GNU General Public License for more details.                          *
 
14
 *                                                                         *
 
15
 *   You should have received a copy of the GNU General Public License     *
 
16
 *   along with this program; if not, write to the                         *
 
17
 *   Free Software Foundation, Inc.,                                       *
 
18
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 
19
 ***************************************************************************/
 
20
 
 
21
#include "motiondetector.h"
 
22
 
 
23
#include <QDebug>
 
24
#include <QTime>
 
25
 
 
26
// various tracking parameters (in seconds)
 
27
const double MHI_DURATION = 1;
 
28
const double MAX_TIME_DELTA = 0.5;
 
29
const double MIN_TIME_DELTA = 0.05;
 
30
// number of cyclic frame buffer used for motion detection
 
31
// (should, probably, depend on FPS)
 
32
const int N = 4;
 
33
const int MAGNITUDE_GLOBAL = 100;
 
34
const int MAGNITUDE_COMPONENT = 30;
 
35
 
 
36
MotionDetector::MotionDetector(QObject *parent)
 
37
    : QObject(parent),
 
38
    show_global_(true),
 
39
    show_component_(true),
 
40
    motion_(0),
 
41
    last(0),
 
42
    buf(0),
 
43
    mhi(0),
 
44
    orient(0),
 
45
    mask(0),
 
46
    red(0),
 
47
    green(0),
 
48
    blue(0),
 
49
    segmask(0),
 
50
    storage(0)
 
51
{
 
52
    qDebug() << "MotionDetector::MotionDetector";
 
53
}
 
54
 
 
55
 
 
56
MotionDetector::~MotionDetector()
 
57
{
 
58
    qDebug() << "MotionDetector::~MotionDetector";
 
59
}
 
60
 
 
61
 
 
62
void MotionDetector::input(const IplImage & image)
 
63
{
 
64
    //qDebug() << "MotionDetector::input";
 
65
    if( !motion_ )
 
66
    {
 
67
        motion_ = cvCreateImage(cvSize(image.width,image.height), 8, 3 );
 
68
        cvZero(motion_);
 
69
        motion_->origin = image.origin;
 
70
    }
 
71
 
 
72
    update_mhi(&image, motion_, threshold_);
 
73
 
 
74
    //emit output(image);
 
75
    emit output(*motion_);
 
76
 
 
77
}
 
78
 
 
79
void MotionDetector::set_motion_color(const QColor & col)
 
80
{
 
81
    //qDebug() << "color set to : " << col;
 
82
    color_ = col;
 
83
};
 
84
 
 
85
void MotionDetector::set_threshold(int t)
 
86
{
 
87
    //qDebug() << "threshold set to : " << t;
 
88
    threshold_ = t;
 
89
}
 
90
 
 
91
// parameters:
 
92
//  img - input video frame
 
93
//  dst - resultant motion picture
 
94
//  diff_threshold - threshold to avoid motion detection because of noise in the video
 
95
void MotionDetector::update_mhi(const IplImage* img, IplImage* dst, int diff_threshold )
 
96
{
 
97
    double timestamp = (double)clock()/CLOCKS_PER_SEC; // get current time in seconds
 
98
    CvSize size = cvSize(img->width,img->height); // get current frame size
 
99
    int i, idx1 = last, idx2;
 
100
    IplImage* silh;
 
101
    CvSeq* seq;
 
102
    CvRect comp_rect;
 
103
    double count, angle, magnitude;
 
104
    CvPoint center;
 
105
    CvScalar color;
 
106
 
 
107
    //qDebug() << "MotionDetector::update_mhi";
 
108
 
 
109
    // allocate images at the beginning or
 
110
    // reallocate them if the frame size is changed
 
111
    if( !mhi || mhi->width != size.width || mhi->height != size.height ) {
 
112
        if( buf == 0 ) {
 
113
            buf = (IplImage**)malloc(N*sizeof(buf[0]));
 
114
            memset( buf, 0, N*sizeof(buf[0]));
 
115
        }
 
116
        for( i = 0; i < N; i++ ) {
 
117
            cvReleaseImage( &buf[i] );
 
118
            buf[i] = cvCreateImage( size, IPL_DEPTH_8U, 1 );
 
119
            cvZero( buf[i] );
 
120
        }
 
121
        cvReleaseImage( &mhi );
 
122
        cvReleaseImage( &orient );
 
123
        cvReleaseImage( &segmask );
 
124
        cvReleaseImage( &mask );
 
125
        cvReleaseImage( &red );
 
126
        cvReleaseImage( &green );
 
127
        cvReleaseImage( &blue );
 
128
 
 
129
        mhi = cvCreateImage( size, IPL_DEPTH_32F, 1 );
 
130
        cvZero( mhi ); // clear MHI at the beginning
 
131
        orient = cvCreateImage( size, IPL_DEPTH_32F, 1 );
 
132
        segmask = cvCreateImage( size, IPL_DEPTH_32F, 1 );
 
133
        mask = cvCreateImage( size, IPL_DEPTH_8U, 1 );
 
134
        red = cvCreateImage( size, IPL_DEPTH_8U, 1 );
 
135
        green = cvCreateImage( size, IPL_DEPTH_8U, 1 );
 
136
        blue = cvCreateImage( size, IPL_DEPTH_8U, 1 );
 
137
    }
 
138
 
 
139
    cvCvtColor( img, buf[last], CV_BGR2GRAY ); // convert frame to grayscale
 
140
 
 
141
    //emit output(*buf[last]);
 
142
 
 
143
    idx2 = (last + 1) % N; // index of (last - (N-1))th frame
 
144
    last = idx2;
 
145
 
 
146
    silh = buf[idx2];
 
147
    cvAbsDiff( buf[idx1], buf[idx2], silh ); // get difference between frames
 
148
 
 
149
    //emit output(*silh);
 
150
 
 
151
    cvThreshold( silh, silh, diff_threshold, 1, CV_THRESH_BINARY ); // and threshold it
 
152
 
 
153
    //emit output(*silh);
 
154
 
 
155
    cvUpdateMotionHistory( silh, mhi, timestamp, MHI_DURATION ); // update MHI
 
156
 
 
157
    // convert MHI to blue 8u image
 
158
    cvCvtScale( mhi, mask, 255./MHI_DURATION, (MHI_DURATION - timestamp)*255./MHI_DURATION );
 
159
 
 
160
    // convert 8u image to 32u image using custom color
 
161
    cvCvtScale( mask, red,   color_.red() / 255.0, 0.);
 
162
    cvCvtScale( mask, green, color_.green() / 255.0, 0.);
 
163
    cvCvtScale( mask, blue,  color_.blue() / 255.0, 0.);
 
164
 
 
165
    cvZero( dst );
 
166
    cvCvtPlaneToPix( blue, green,  red, 0, dst );
 
167
 
 
168
    // calculate motion gradient orientation and valid orientation mask
 
169
    cvCalcMotionGradient( mhi, mask, orient, MAX_TIME_DELTA, MIN_TIME_DELTA, 3 );
 
170
 
 
171
    if( !storage )
 
172
        storage = cvCreateMemStorage(0);
 
173
    else
 
174
        cvClearMemStorage(storage);
 
175
 
 
176
    // segment motion: get sequence of motion components
 
177
    // segmask is marked motion components map. It is not used further
 
178
    seq = cvSegmentMotion( mhi, segmask, storage, timestamp, MAX_TIME_DELTA );
 
179
 
 
180
    // iterate through the motion components,
 
181
    // One more iteration (i == -1) corresponds to the whole image (global motion)
 
182
    for( i = -1; i < seq->total; i++ ) {
 
183
 
 
184
        if( i < 0 ) { // case of the whole image
 
185
            comp_rect = cvRect( 0, 0, size.width, size.height );
 
186
            color = CV_RGB(255,255,255); // white
 
187
            magnitude = MAGNITUDE_GLOBAL;
 
188
        }
 
189
        else { // i-th motion component
 
190
            comp_rect = ((CvConnectedComp*)cvGetSeqElem( seq, i ))->rect;
 
191
            if( comp_rect.width + comp_rect.height < 100 ) // reject very small components
 
192
                continue;
 
193
            color = CV_RGB(255,0,0); // red
 
194
            magnitude = MAGNITUDE_COMPONENT;
 
195
        }
 
196
 
 
197
        // select component ROI
 
198
        cvSetImageROI( silh, comp_rect );
 
199
        cvSetImageROI( mhi, comp_rect );
 
200
        cvSetImageROI( orient, comp_rect );
 
201
        cvSetImageROI( mask, comp_rect );
 
202
 
 
203
        // calculate orientation
 
204
        angle = cvCalcGlobalOrientation( orient, mask, mhi, timestamp, MHI_DURATION);
 
205
        angle = 360.0 - angle;  // adjust for images with top-left origin
 
206
 
 
207
        count = cvNorm( silh, 0, CV_L1, 0 ); // calculate number of points within silhouette ROI
 
208
 
 
209
        cvResetImageROI( mhi );
 
210
        cvResetImageROI( orient );
 
211
        cvResetImageROI( mask );
 
212
        cvResetImageROI( silh );
 
213
 
 
214
        // check for the case of little motion
 
215
        if( count < comp_rect.width*comp_rect.height * 0.05 )
 
216
            continue;
 
217
 
 
218
        if (((magnitude == MAGNITUDE_GLOBAL) && show_global_)
 
219
            || ((magnitude == MAGNITUDE_COMPONENT) && show_component_))
 
220
            {
 
221
            // draw a clock with arrow indicating the direction
 
222
            center = cvPoint(comp_rect.x + comp_rect.width / 2,comp_rect.y + comp_rect.height / 2);
 
223
 
 
224
            magnitude = (magnitude * size.height) / (3 * MAGNITUDE_GLOBAL);
 
225
 
 
226
            cvCircle(dst,center,cvRound(magnitude * 1.2),color,3,CV_AA,0);
 
227
            cvLine(dst,center
 
228
                   ,cvPoint(cvRound(center.x + magnitude * cos(angle * CV_PI / 180))
 
229
                            ,cvRound(center.y - magnitude * sin(angle * CV_PI / 180)))
 
230
                   ,color,3,CV_AA,0);
 
231
        }
 
232
        if (i == -1)
 
233
        {
 
234
            //qDebug() << "emit Motion Detected !";
 
235
            emit motion();
 
236
        }
 
237
    }
 
238
}
 
239