3 * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com>
4 * Copyright (C) 2011 Nicola Murino <nicola.murino@gmail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
24 * Alternatively, the contents of this file may be used under the
25 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
26 * which case the following provisions apply instead of the ones
29 * This library is free software; you can redistribute it and/or
30 * modify it under the terms of the GNU Library General Public
31 * License as published by the Free Software Foundation; either
32 * version 2 of the License, or (at your option) any later version.
34 * This library is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
37 * Library General Public License for more details.
39 * You should have received a copy of the GNU Library General Public
40 * License along with this library; if not, write to the
41 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
42 * Boston, MA 02110-1301, USA.
45 /* This breaks the build for reasons that aren't entirely clear to me yet */
47 //#ifdef HAVE_CONFIG_H
58 #define bzero(p, l) memset(p, 0, l)
60 #include <arpa/inet.h>
62 #include "MotionCells.h"
64 uint64_t ntohl64 (uint64_t val);
65 uint64_t htonl64 (uint64_t val);
68 ntohl64 (uint64_t val)
71 uint32_t low = (uint32_t) (val & 0x00000000FFFFFFFFLL);
72 uint32_t high = (uint32_t) ((val & 0xFFFFFFFF00000000LL) >> 32);
75 res64 = (uint64_t) high + (((uint64_t) low) << 32);
81 htonl64 (uint64_t val)
84 uint32_t low = (uint32_t) (val & 0x00000000FFFFFFFFLL);
85 uint32_t high = (uint32_t) ((val & 0xFFFFFFFF00000000LL) >> 32);
88 res64 = (uint64_t) high + (((uint64_t) low) << 32);
92 MotionCells::MotionCells ()
95 m_motioncells_idx_count = 0;
96 m_motioncellsidxcstr = NULL;
97 m_saveInDatafile = false;
101 transparencyimg = NULL;
102 m_pdifferenceImage = NULL;
104 m_initdatafilefailed = new char[BUSMSGLEN];
105 m_savedatafilefailed = new char[BUSMSGLEN];
115 MotionCells::~MotionCells ()
118 fclose (mc_savefile);
121 delete[]m_initdatafilefailed;
122 delete[]m_savedatafilefailed;
123 if (m_motioncellsidxcstr)
124 delete[]m_motioncellsidxcstr;
126 cvReleaseImage (&m_pcurFrame);
128 cvReleaseImage (&m_pprevFrame);
130 cvReleaseImage (&transparencyimg);
131 if (m_pdifferenceImage)
132 cvReleaseImage (&m_pdifferenceImage);
134 cvReleaseImage (&m_pbwImage);
138 MotionCells::performDetectionMotionCells (IplImage * p_frame,
139 double p_sensitivity, double p_framerate, int p_gridx, int p_gridy,
140 gint64 timestamp_millisec, bool p_isVisible, bool p_useAlpha,
141 int motionmaskcoord_count, motionmaskcoordrect * motionmaskcoords,
142 int motionmaskcells_count, motioncellidx * motionmaskcellsidx,
143 cellscolor motioncellscolor, int motioncells_count,
144 motioncellidx * motioncellsidx, gint64 starttime, char *p_datafile,
145 bool p_changed_datafile, int p_thickness)
150 p_framerate >= 1 ? p_framerate <= 5 ? sumframecnt = 1
151 : p_framerate <= 10 ? sumframecnt = 2
152 : p_framerate <= 15 ? sumframecnt = 3
153 : p_framerate <= 20 ? sumframecnt = 4
154 : p_framerate <= 25 ? sumframecnt = 5
155 : p_framerate <= 30 ? sumframecnt = 6 : sumframecnt = 0 : sumframecnt = 0;
158 m_changed_datafile = p_changed_datafile;
159 if (m_framecnt >= sumframecnt) {
160 m_useAlpha = p_useAlpha;
163 if (m_changed_datafile) {
164 ret = initDataFile (p_datafile, starttime);
169 m_frameSize = cvGetSize (p_frame);
170 m_frameSize.width /= 2;
171 m_frameSize.height /= 2;
172 setMotionCells (m_frameSize.width, m_frameSize.height);
173 m_sensitivity = 1 - p_sensitivity;
174 m_isVisible = p_isVisible;
175 m_pcurFrame = cvCloneImage (p_frame);
176 IplImage *m_pcurgreyImage = cvCreateImage (m_frameSize, IPL_DEPTH_8U, 1);
177 IplImage *m_pprevgreyImage = cvCreateImage (m_frameSize, IPL_DEPTH_8U, 1);
178 IplImage *m_pgreyImage = cvCreateImage (m_frameSize, IPL_DEPTH_8U, 1);
179 IplImage *m_pcurDown =
180 cvCreateImage (m_frameSize, m_pcurFrame->depth, m_pcurFrame->nChannels);
181 IplImage *m_pprevDown = cvCreateImage (m_frameSize, m_pprevFrame->depth,
182 m_pprevFrame->nChannels);
183 m_pbwImage = cvCreateImage (m_frameSize, IPL_DEPTH_8U, 1);
184 cvPyrDown (m_pprevFrame, m_pprevDown);
185 cvCvtColor (m_pprevDown, m_pprevgreyImage, CV_RGB2GRAY);
186 cvPyrDown (m_pcurFrame, m_pcurDown);
187 cvCvtColor (m_pcurDown, m_pcurgreyImage, CV_RGB2GRAY);
188 m_pdifferenceImage = cvCloneImage (m_pcurgreyImage);
189 //cvSmooth(m_pcurgreyImage, m_pcurgreyImage, CV_GAUSSIAN, 3, 0);//TODO camera noise reduce,something smoothing, and rethink runningavg weights
191 //Minus the current gray frame from the 8U moving average.
192 cvAbsDiff (m_pprevgreyImage, m_pcurgreyImage, m_pdifferenceImage);
194 //Convert the image to black and white.
195 cvAdaptiveThreshold (m_pdifferenceImage, m_pbwImage, 255,
196 CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, 7);
198 // Dilate and erode to get object blobs
199 cvDilate (m_pbwImage, m_pbwImage, NULL, 2);
200 cvErode (m_pbwImage, m_pbwImage, NULL, 2);
202 //mask-out the overlay on difference image
203 if (motionmaskcoord_count > 0)
204 performMotionMaskCoords (motionmaskcoords, motionmaskcoord_count);
205 if (motionmaskcells_count > 0)
206 performMotionMask (motionmaskcellsidx, motionmaskcells_count);
207 if (getIsNonZero (m_pbwImage)) { //detect Motion
208 if (m_MotionCells.size () > 0) //it contains previous motioncells what we used when frames dropped
209 m_MotionCells.clear ();
211 cvReleaseImage (&transparencyimg);
212 (motioncells_count > 0) ?
213 calculateMotionPercentInMotionCells (motioncellsidx,
215 : calculateMotionPercentInMotionCells (motionmaskcellsidx, 0);
217 transparencyimg = cvCreateImage (cvGetSize (p_frame), p_frame->depth, 3);
218 cvSetZero (transparencyimg);
219 if (m_motioncellsidxcstr)
220 delete[]m_motioncellsidxcstr;
221 m_motioncells_idx_count = m_MotionCells.size () * MSGLEN; //one motion cell idx: (lin idx : col idx,) it's 4 character except last motion cell idx
222 m_motioncellsidxcstr = new char[m_motioncells_idx_count];
223 char *tmpstr = new char[MSGLEN];
224 for (int i = 0; i < MSGLEN; i++)
226 for (unsigned int i = 0; i < m_MotionCells.size (); i++) {
228 pt1.x = m_MotionCells.at (i).cell_pt1.x * 2;
229 pt1.y = m_MotionCells.at (i).cell_pt1.y * 2;
230 pt2.x = m_MotionCells.at (i).cell_pt2.x * 2;
231 pt2.y = m_MotionCells.at (i).cell_pt2.y * 2;
232 if (m_useAlpha && m_isVisible) {
233 cvRectangle (transparencyimg,
236 CV_RGB (motioncellscolor.B_channel_value,
237 motioncellscolor.G_channel_value,
238 motioncellscolor.R_channel_value), CV_FILLED);
239 } else if (m_isVisible) {
240 cvRectangle (p_frame,
243 CV_RGB (motioncellscolor.B_channel_value,
244 motioncellscolor.G_channel_value,
245 motioncellscolor.R_channel_value), p_thickness);
248 if (i < m_MotionCells.size () - 1) {
249 snprintf (tmpstr, MSGLEN, "%d:%d,", m_MotionCells.at (i).lineidx,
250 m_MotionCells.at (i).colidx);
252 snprintf (tmpstr, MSGLEN, "%d:%d", m_MotionCells.at (i).lineidx,
253 m_MotionCells.at (i).colidx);
256 strncpy (m_motioncellsidxcstr, tmpstr, m_motioncells_idx_count);
258 strcat (m_motioncellsidxcstr, tmpstr);
260 if (m_MotionCells.size () == 0)
261 strncpy (m_motioncellsidxcstr, " ", m_motioncells_idx_count);
263 if (m_useAlpha && m_isVisible) {
264 if (m_MotionCells.size () > 0)
265 blendImages (p_frame, transparencyimg, m_alpha, m_beta);
270 if (mc_savefile && m_saveInDatafile) {
271 ret = saveMotionCells (timestamp_millisec);
276 m_motioncells_idx_count = 0;
277 if (m_MotionCells.size () > 0)
278 m_MotionCells.clear ();
280 cvReleaseImage (&transparencyimg);
284 cvReleaseImage (&m_pprevFrame);
285 m_pprevFrame = cvCloneImage (m_pcurFrame);
288 cvReleaseImage (&m_pcurFrame);
289 if (m_pdifferenceImage)
290 cvReleaseImage (&m_pdifferenceImage);
292 cvReleaseImage (&m_pcurgreyImage);
293 if (m_pprevgreyImage)
294 cvReleaseImage (&m_pprevgreyImage);
296 cvReleaseImage (&m_pgreyImage);
298 cvReleaseImage (&m_pbwImage);
300 cvReleaseImage (&m_pprevDown);
302 cvReleaseImage (&m_pcurDown);
304 for (int i = 0; i < m_gridy; ++i) {
310 if (p_framerate <= 5) {
311 if (m_MotionCells.size () > 0)
312 m_MotionCells.clear ();
314 cvReleaseImage (&transparencyimg);
316 } else { //we do frame drop
317 m_motioncells_idx_count = 0;
319 for (unsigned int i = 0; i < m_MotionCells.size (); i++) {
321 pt1.x = m_MotionCells.at (i).cell_pt1.x * 2;
322 pt1.y = m_MotionCells.at (i).cell_pt1.y * 2;
323 pt2.x = m_MotionCells.at (i).cell_pt2.x * 2;
324 pt2.y = m_MotionCells.at (i).cell_pt2.y * 2;
325 if (m_useAlpha && m_isVisible) {
326 cvRectangle (transparencyimg,
329 CV_RGB (motioncellscolor.B_channel_value,
330 motioncellscolor.G_channel_value,
331 motioncellscolor.R_channel_value), CV_FILLED);
332 } else if (m_isVisible) {
333 cvRectangle (p_frame,
336 CV_RGB (motioncellscolor.B_channel_value,
337 motioncellscolor.G_channel_value,
338 motioncellscolor.R_channel_value), p_thickness);
342 if (m_useAlpha && m_isVisible) {
343 if (m_MotionCells.size () > 0)
344 blendImages (p_frame, transparencyimg, m_alpha, m_beta);
351 MotionCells::initDataFile (char *p_datafile, gint64 starttime) //p_date is increased with difference between current and previous buffer ts
354 if (strncmp (p_datafile, " ", 1)) {
355 mc_savefile = fopen (p_datafile, "w");
356 if (mc_savefile == NULL) {
357 //fprintf(stderr, "%s %d:initDataFile:fopen:%d (%s)\n", __FILE__, __LINE__, errno,
359 strncpy (m_initdatafilefailed, strerror (errno), BUSMSGLEN - 1);
360 m_initerrorcode = errno;
363 m_saveInDatafile = true;
367 bzero (&m_header, sizeof (MotionCellHeader));
368 m_header.headersize = htonl (MC_HEADER);
369 m_header.type = htonl (MC_TYPE);
370 m_header.version = htonl (MC_VERSION);
371 //it needs these bytes
373 htonl ((int) ceil (ceil (m_gridx * m_gridy / 8.0) / 4.0) * 4 +
374 sizeof (mcd.timestamp));
375 m_header.gridx = htonl (m_gridx);
376 m_header.gridy = htonl (m_gridy);
377 m_header.starttime = htonl64 (starttime);
379 snprintf (m_header.name, sizeof (m_header.name), "%s %dx%d", MC_VERSIONTEXT,
380 ntohl (m_header.gridx), ntohl (m_header.gridy));
381 m_changed_datafile = false;
386 MotionCells::saveMotionCells (gint64 timestamp_millisec)
389 MotionCellData mc_data;
390 mc_data.timestamp = htonl (timestamp_millisec);
392 //There is no datafile
393 if (mc_savefile == NULL)
396 if (ftello (mc_savefile) == 0) {
397 //cerr << "Writing out file header"<< m_header.headersize <<":" << sizeof(MotionCellHeader) << " itemsize:"
398 //<< m_header.itemsize << endl;
399 if (fwrite (&m_header, sizeof (MotionCellHeader), 1, mc_savefile) != 1) {
400 //fprintf(stderr, "%s %d:saveMotionCells:fwrite:%d (%s)\n", __FILE__, __LINE__, errno,
402 strncpy (m_savedatafilefailed, strerror (errno), BUSMSGLEN - 1);
403 m_saveerrorcode = errno;
410 ntohl (m_header.itemsize) - sizeof (mc_data.timestamp));
411 if (mc_data.data == NULL) {
412 //fprintf(stderr, "%s %d:saveMotionCells:calloc:%d (%s)\n", __FILE__, __LINE__, errno,
414 strncpy (m_savedatafilefailed, strerror (errno), BUSMSGLEN - 1);
415 m_saveerrorcode = errno;
419 for (unsigned int i = 0; i < m_MotionCells.size (); i++) {
421 m_MotionCells.at (i).lineidx * ntohl (m_header.gridx) +
422 m_MotionCells.at (i).colidx;
423 int bytenum = (int) floor (bitnum / 8.0);
424 int shift = bitnum - bytenum * 8;
425 mc_data.data[bytenum] = mc_data.data[bytenum] | (1 << shift);
426 //cerr << "Motion Detected " << "line:" << m_MotionCells.at(i).lineidx << " col:" << m_MotionCells.at(i).colidx;
427 //cerr << " bitnum " << bitnum << " bytenum " << bytenum << " shift " << shift << " value " << (int)mc_data.data[bytenum] << endl;
430 if (fwrite (&mc_data.timestamp, sizeof (mc_data.timestamp), 1,
432 //fprintf(stderr, "%s %d:saveMotionCells:fwrite:%d (%s)\n", __FILE__, __LINE__, errno,
434 strncpy (m_savedatafilefailed, strerror (errno), BUSMSGLEN - 1);
435 m_saveerrorcode = errno;
439 if (fwrite (mc_data.data,
440 ntohl (m_header.itemsize) - sizeof (mc_data.timestamp), 1,
442 //fprintf(stderr, "%s %d:saveMotionCells:fwrite:%d (%s)\n", __FILE__, __LINE__, errno,
444 strncpy (m_savedatafilefailed, strerror (errno), BUSMSGLEN - 1);
445 m_saveerrorcode = errno;
454 MotionCells::calculateMotionPercentInCell (int p_row, int p_col,
455 double *p_cellarea, double *p_motionarea)
457 double cntpixelsnum = 0;
458 double cntmotionpixelnum = 0;
460 int ybegin = floor ((double) p_row * m_cellheight);
461 int yend = floor ((double) (p_row + 1) * m_cellheight);
462 int xbegin = floor ((double) (p_col) * m_cellwidth);
463 int xend = floor ((double) (p_col + 1) * m_cellwidth);
464 int cellw = xend - xbegin;
465 int cellh = yend - ybegin;
466 int cellarea = cellw * cellh;
467 *p_cellarea = cellarea;
468 int thresholdmotionpixelnum = floor ((double) cellarea * m_sensitivity);
470 for (int i = ybegin; i < yend; i++) {
471 for (int j = xbegin; j < xend; j++) {
473 if ((((uchar *) (m_pbwImage->imageData + m_pbwImage->widthStep * i))[j]) >
476 if (cntmotionpixelnum >= thresholdmotionpixelnum) { //we dont needs calculate anymore
477 *p_motionarea = cntmotionpixelnum;
478 return (cntmotionpixelnum / cntpixelsnum);
481 int remainingpixelsnum = cellarea - cntpixelsnum;
482 if ((cntmotionpixelnum + remainingpixelsnum) < thresholdmotionpixelnum) { //moving pixels number will be less than threshold
489 return (cntmotionpixelnum / cntpixelsnum);
493 MotionCells::calculateMotionPercentInMotionCells (motioncellidx *
494 p_motioncellsidx, int p_motioncells_count)
496 if (p_motioncells_count == 0) {
497 for (int i = 0; i < m_gridy; i++) {
498 for (int j = 0; j < m_gridx; j++) {
499 m_pCells[i][j].MotionPercent = calculateMotionPercentInCell (i, j,
500 &m_pCells[i][j].CellArea, &m_pCells[i][j].MotionArea);
501 m_pCells[i][j].hasMotion =
502 m_sensitivity < m_pCells[i][j].MotionPercent ? true : false;
503 if (m_pCells[i][j].hasMotion) {
507 mci.cell_pt1.x = floor ((double) j * m_cellwidth);
508 mci.cell_pt1.y = floor ((double) i * m_cellheight);
509 mci.cell_pt2.x = floor ((double) (j + 1) * m_cellwidth);
510 mci.cell_pt2.y = floor ((double) (i + 1) * m_cellheight);
511 int w = mci.cell_pt2.x - mci.cell_pt1.x;
512 int h = mci.cell_pt2.y - mci.cell_pt1.y;
513 mci.motioncell = cvRect (mci.cell_pt1.x, mci.cell_pt1.y, w, h);
514 m_MotionCells.push_back (mci);
519 for (int k = 0; k < p_motioncells_count; ++k) {
521 int i = p_motioncellsidx[k].lineidx;
522 int j = p_motioncellsidx[k].columnidx;
523 m_pCells[i][j].MotionPercent =
524 calculateMotionPercentInCell (i, j,
525 &m_pCells[i][j].CellArea, &m_pCells[i][j].MotionArea);
526 m_pCells[i][j].hasMotion =
527 m_pCells[i][j].MotionPercent > m_sensitivity ? true : false;
528 if (m_pCells[i][j].hasMotion) {
530 mci.lineidx = p_motioncellsidx[k].lineidx;
531 mci.colidx = p_motioncellsidx[k].columnidx;
532 mci.cell_pt1.x = floor ((double) j * m_cellwidth);
533 mci.cell_pt1.y = floor ((double) i * m_cellheight);
534 mci.cell_pt2.x = floor ((double) (j + 1) * m_cellwidth);
535 mci.cell_pt2.y = floor ((double) (i + 1) * m_cellheight);
536 int w = mci.cell_pt2.x - mci.cell_pt1.x;
537 int h = mci.cell_pt2.y - mci.cell_pt1.y;
538 mci.motioncell = cvRect (mci.cell_pt1.x, mci.cell_pt1.y, w, h);
539 m_MotionCells.push_back (mci);
546 MotionCells::performMotionMaskCoords (motionmaskcoordrect * p_motionmaskcoords,
547 int p_motionmaskcoords_count)
555 for (int i = 0; i < p_motionmaskcoords_count; i++) {
556 upperleft.x = p_motionmaskcoords[i].upper_left_x;
557 upperleft.y = p_motionmaskcoords[i].upper_left_y;
558 lowerright.x = p_motionmaskcoords[i].lower_right_x;
559 lowerright.y = p_motionmaskcoords[i].lower_right_y;
560 cvRectangle (m_pbwImage, upperleft, lowerright, CV_RGB (0, 0, 0),
566 MotionCells::performMotionMask (motioncellidx * p_motionmaskcellsidx,
567 int p_motionmaskcells_count)
569 for (int k = 0; k < p_motionmaskcells_count; k++) {
570 int beginy = p_motionmaskcellsidx[k].lineidx * m_cellheight;
571 int beginx = p_motionmaskcellsidx[k].columnidx * m_cellwidth;
573 (double) p_motionmaskcellsidx[k].columnidx * m_cellwidth + m_cellwidth;
575 (double) p_motionmaskcellsidx[k].lineidx * m_cellheight + m_cellheight;
576 for (int i = beginy; i < endy; i++)
577 for (int j = beginx; j < endx; j++) {
578 ((uchar *) (m_pbwImage->imageData + m_pbwImage->widthStep * i))[j] = 0;
583 ///BGR if we use only OpenCV
584 //RGB if we use gst+OpenCV
586 MotionCells::blendImages (IplImage * p_actFrame, IplImage * p_cellsFrame,
587 float p_alpha, float p_beta)
590 int height = p_actFrame->height;
591 int width = p_actFrame->width;
592 int step = p_actFrame->widthStep / sizeof (uchar);
593 int channels = p_actFrame->nChannels;
594 int cellstep = p_cellsFrame->widthStep / sizeof (uchar);
595 uchar *curImageData = (uchar *) p_actFrame->imageData;
596 uchar *cellImageData = (uchar *) p_cellsFrame->imageData;
598 for (int i = 0; i < height; i++)
599 for (int j = 0; j < width; j++)
600 for (int k = 0; k < channels; k++)
601 if (cellImageData[i * cellstep + j * channels + k] > 0) {
602 curImageData[i * step + j * channels + k] =
603 round ((double) curImageData[i * step + j * channels +
604 k] * p_alpha + ((double) cellImageData[i * cellstep +
605 j * channels + k] * p_beta));