diff options
author | Robert Jobbagy <jobbagy.robert@gmail.com> | 2011-07-27 18:58:15 +0200 |
---|---|---|
committer | Stefan Sauer <ensonic@google.com> | 2011-07-28 10:28:50 +0200 |
commit | 4723a5d90ce02cb4d720b1be7ebdf365f81910e4 (patch) | |
tree | 7ea506c21a957d8f37f9700aa88424ddef16be83 /ext/opencv | |
parent | 976f4b0bbf8efcd0054fc4c0f9ca74029e85dc69 (diff) |
motioncells: new element to detect areas of motion
Diffstat (limited to 'ext/opencv')
-rw-r--r-- | ext/opencv/Makefile.am | 12 | ||||
-rw-r--r-- | ext/opencv/MotionCells.cpp | 593 | ||||
-rw-r--r-- | ext/opencv/MotionCells.h | 259 | ||||
-rw-r--r-- | ext/opencv/gstmotioncells.c | 1111 | ||||
-rw-r--r-- | ext/opencv/gstmotioncells.h | 124 | ||||
-rw-r--r-- | ext/opencv/gstopencv.c | 4 | ||||
-rw-r--r-- | ext/opencv/motioncells_wrapper.cpp | 213 | ||||
-rw-r--r-- | ext/opencv/motioncells_wrapper.h | 89 |
8 files changed, 2403 insertions, 2 deletions
diff --git a/ext/opencv/Makefile.am b/ext/opencv/Makefile.am index d5a70edad..a32e16cfa 100644 --- a/ext/opencv/Makefile.am +++ b/ext/opencv/Makefile.am @@ -16,7 +16,12 @@ libgstopencv_la_SOURCES = gstopencv.c \ gstfacedetect.c \ gstpyramidsegment.c \ gsttemplatematch.c \ - gsttextoverlay.c + gsttextoverlay.c \ + gstmotioncells.c \ + motioncells_wrapper.cpp \ + MotionCells.cpp + +libgstopencv_la_CXXFLAGS = $(GST_CXXFLAGS) $(OPENCV_CFLAGS) # flags used to compile this facedetect # add other _CFLAGS and _LIBS as needed @@ -46,4 +51,7 @@ noinst_HEADERS = gstopencvvideofilter.h gstopencvutils.h \ gstfacedetect.h \ gstpyramidsegment.h \ gsttemplatematch.h \ - gsttextoverlay.h + gsttextoverlay.h \ + gstmotioncells.h \ + motioncells_wrapper.h \ + MotionCells.h diff --git a/ext/opencv/MotionCells.cpp b/ext/opencv/MotionCells.cpp new file mode 100644 index 000000000..2b81b305d --- /dev/null +++ b/ext/opencv/MotionCells.cpp @@ -0,0 +1,593 @@ +/* + * GStreamer + * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com> + * Copyright (C) 2011 Nicola Murino <nicola.murino@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <cstdlib> +#include <errno.h> +#include <math.h> +#include <gst/gst.h> +#include <arpa/inet.h> +#include "MotionCells.h" + +uint64_t ntohl64 (uint64_t val); +uint64_t htonl64 (uint64_t val); + +uint64_t +ntohl64 (uint64_t val) +{ + uint64_t res64; + uint32_t low = (uint32_t) (val & 0x00000000FFFFFFFFLL); + uint32_t high = (uint32_t) ((val & 0xFFFFFFFF00000000LL) >> 32); + low = ntohl (low); + high = ntohl (high); + res64 = (uint64_t) high + (((uint64_t) low) << 32); + return res64; +} + + +uint64_t +htonl64 (uint64_t val) +{ + uint64_t res64; + uint32_t low = (uint32_t) (val & 0x00000000FFFFFFFFLL); + uint32_t high = (uint32_t) ((val & 0xFFFFFFFF00000000LL) >> 32); + low = htonl (low); + high = htonl (high); + res64 = (uint64_t) high + (((uint64_t) low) << 32); + return res64; +} + +MotionCells::MotionCells () +{ + m_framecnt = 0; + m_motioncells_idx_count = 0; + m_motioncellsidxcstr = NULL; + m_saveInDatafile = false; + mc_savefile = NULL; + m_pcurFrame = NULL; + m_pprevFrame = NULL; + transparencyimg = NULL; + m_pdifferenceImage = NULL; + m_pbwImage = NULL; + m_initdatafilefailed = new char[BUSMSGLEN]; + m_savedatafilefailed = new char[BUSMSGLEN]; + m_initerrorcode = 0; + m_saveerrorcode = 0; + m_alpha = 0.5; + m_beta = 0.5; + +} + +MotionCells::~MotionCells () +{ + if (mc_savefile) { + fclose (mc_savefile); + mc_savefile = NULL; + } + delete[]m_initdatafilefailed; + delete[]m_savedatafilefailed; + if (m_motioncellsidxcstr) + delete[]m_motioncellsidxcstr; + if (m_pcurFrame) + cvReleaseImage (&m_pcurFrame); + if (m_pprevFrame) + cvReleaseImage (&m_pprevFrame); + if (transparencyimg) + cvReleaseImage (&transparencyimg); + if (m_pdifferenceImage) + cvReleaseImage (&m_pdifferenceImage); + if (m_pbwImage) + cvReleaseImage (&m_pbwImage); +} + +int +MotionCells::performDetectionMotionCells (IplImage * p_frame, + double p_sensitivity, double p_framerate, int p_gridx, int p_gridy, + gint64 timestamp_millisec, bool p_isVisible, bool p_useAlpha, + int motionmaskcoord_count, motionmaskcoordrect * motionmaskcoords, + int motionmaskcells_count, motioncellidx * motionmaskcellsidx, + cellscolor motioncellscolor, int motioncells_count, + motioncellidx * motioncellsidx, gint64 starttime, char *p_datafile, + bool p_changed_datafile, int p_thickness) +{ + + int sumframecnt = 0; + int ret = 0; + p_framerate >= 1 ? p_framerate <= 5 ? sumframecnt = 1 + : p_framerate <= 10 ? sumframecnt = 2 + : p_framerate <= 15 ? sumframecnt = 3 + : p_framerate <= 20 ? sumframecnt = 4 + : p_framerate <= 25 ? sumframecnt = 5 : sumframecnt = 0 : sumframecnt = 0; + + m_framecnt++; + m_changed_datafile = p_changed_datafile; + if (m_framecnt >= sumframecnt) { + m_useAlpha = p_useAlpha; + m_gridx = p_gridx; + m_gridy = p_gridy; + if (m_changed_datafile) { + ret = initDataFile (p_datafile, starttime); + if (ret != 0) + return ret; + } + + m_frameSize = cvGetSize (p_frame); + m_frameSize.width /= 2; + m_frameSize.height /= 2; + setMotionCells (m_frameSize.width, m_frameSize.height); + m_sensitivity = 1 - p_sensitivity; + m_isVisible = p_isVisible; + m_pcurFrame = cvCloneImage (p_frame); + IplImage *m_pcurgreyImage = cvCreateImage (m_frameSize, IPL_DEPTH_8U, 1); + IplImage *m_pprevgreyImage = cvCreateImage (m_frameSize, IPL_DEPTH_8U, 1); + IplImage *m_pgreyImage = cvCreateImage (m_frameSize, IPL_DEPTH_8U, 1); + IplImage *m_pcurDown = + cvCreateImage (m_frameSize, m_pcurFrame->depth, m_pcurFrame->nChannels); + IplImage *m_pprevDown = cvCreateImage (m_frameSize, m_pprevFrame->depth, + m_pprevFrame->nChannels); + m_pbwImage = cvCreateImage (m_frameSize, IPL_DEPTH_8U, 1); + cvPyrDown (m_pprevFrame, m_pprevDown); + cvCvtColor (m_pprevDown, m_pprevgreyImage, CV_RGB2GRAY); + if (m_pprevFrame) + cvReleaseImage (&m_pprevFrame); + cvPyrDown (m_pcurFrame, m_pcurDown); + cvCvtColor (m_pcurDown, m_pcurgreyImage, CV_RGB2GRAY); + m_pdifferenceImage = cvCloneImage (m_pcurgreyImage); + //cvSmooth(m_pcurgreyImage, m_pcurgreyImage, CV_GAUSSIAN, 3, 0);//TODO camera noise reduce,something smoothing, and rethink runningavg weights + + //Minus the current gray frame from the 8U moving average. + cvAbsDiff (m_pprevgreyImage, m_pcurgreyImage, m_pdifferenceImage); + + //Convert the image to black and white. + cvAdaptiveThreshold (m_pdifferenceImage, m_pbwImage, 255, + CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, 7); + + // Dilate and erode to get object blobs + cvDilate (m_pbwImage, m_pbwImage, NULL, 2); + cvErode (m_pbwImage, m_pbwImage, NULL, 2); + + //mask-out the overlay on difference image + if (motionmaskcoord_count > 0) + performMotionMaskCoords (motionmaskcoords, motionmaskcoord_count); + if (motionmaskcells_count > 0) + performMotionMask (motionmaskcellsidx, motionmaskcells_count); + if (getIsNonZero (m_pbwImage)) { //detect Motion + GST_DEBUG ("DETECT MOTION \n"); + if (m_MotionCells.size () > 0) //it contains previous motioncells what we used when frames dropped + m_MotionCells.clear (); + if (transparencyimg) + cvReleaseImage (&transparencyimg); + (motioncells_count > 0) ? + calculateMotionPercentInMotionCells (motioncellsidx, + motioncells_count) + : calculateMotionPercentInMotionCells (motionmaskcellsidx, 0); + + transparencyimg = cvCreateImage (cvGetSize (p_frame), p_frame->depth, 3); + cvSetZero (transparencyimg); + if (m_motioncellsidxcstr) + delete[]m_motioncellsidxcstr; + 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 + m_motioncellsidxcstr = new char[m_motioncells_idx_count]; + char *tmpstr = new char[MSGLEN]; + for (int i = 0; i < MSGLEN; i++) + tmpstr[i] = ' '; + for (unsigned int i = 0; i < m_MotionCells.size (); i++) { + CvPoint pt1, pt2; + pt1.x = m_MotionCells.at (i).cell_pt1.x * 2; + pt1.y = m_MotionCells.at (i).cell_pt1.y * 2; + pt2.x = m_MotionCells.at (i).cell_pt2.x * 2; + pt2.y = m_MotionCells.at (i).cell_pt2.y * 2; + if (m_useAlpha && m_isVisible) { + cvRectangle (transparencyimg, + pt1, + pt2, + CV_RGB (motioncellscolor.B_channel_value, + motioncellscolor.G_channel_value, + motioncellscolor.R_channel_value), CV_FILLED); + } else if (m_isVisible) { + cvRectangle (p_frame, + pt1, + pt2, + CV_RGB (motioncellscolor.B_channel_value, + motioncellscolor.G_channel_value, + motioncellscolor.R_channel_value), p_thickness); + } + + if (i < m_MotionCells.size () - 1) { + snprintf (tmpstr, MSGLEN, "%d:%d,", m_MotionCells.at (i).lineidx, + m_MotionCells.at (i).colidx); + } else { + snprintf (tmpstr, MSGLEN, "%d:%d", m_MotionCells.at (i).lineidx, + m_MotionCells.at (i).colidx); + } + if (i == 0) + strncpy (m_motioncellsidxcstr, tmpstr, m_motioncells_idx_count); + else + strcat (m_motioncellsidxcstr, tmpstr); + } + if (m_MotionCells.size () == 0) + strncpy (m_motioncellsidxcstr, " ", m_motioncells_idx_count); + + if (m_useAlpha && m_isVisible) { + if (m_MotionCells.size () > 0) + blendImages (p_frame, transparencyimg, m_alpha, m_beta); + } + + delete[]tmpstr; + + if (mc_savefile && m_saveInDatafile) { + ret = saveMotionCells (timestamp_millisec); + if (ret != 0) + return ret; + } + } else { + m_motioncells_idx_count = 0; + if (m_MotionCells.size () > 0) + m_MotionCells.clear (); + if (transparencyimg) + cvReleaseImage (&transparencyimg); + } + + m_pprevFrame = cvCloneImage (m_pcurFrame); + m_framecnt = 0; + if (m_pcurFrame) + cvReleaseImage (&m_pcurFrame); + if (m_pdifferenceImage) + cvReleaseImage (&m_pdifferenceImage); + if (m_pcurgreyImage) + cvReleaseImage (&m_pcurgreyImage); + if (m_pprevgreyImage) + cvReleaseImage (&m_pprevgreyImage); + if (m_pgreyImage) + cvReleaseImage (&m_pgreyImage); + if (m_pbwImage) + cvReleaseImage (&m_pbwImage); + if (m_pprevDown) + cvReleaseImage (&m_pprevDown); + if (m_pcurDown) + cvReleaseImage (&m_pcurDown); + if (m_pCells) { + for (int i = 0; i < m_gridy; ++i) { + delete[]m_pCells[i]; + } + delete[]m_pCells; + } + + if (p_framerate <= 5) { + if (m_MotionCells.size () > 0) + m_MotionCells.clear (); + if (transparencyimg) + cvReleaseImage (&transparencyimg); + } + } else { //we do frame drop + m_motioncells_idx_count = 0; + ret = -2; + for (unsigned int i = 0; i < m_MotionCells.size (); i++) { + CvPoint pt1, pt2; + pt1.x = m_MotionCells.at (i).cell_pt1.x * 2; + pt1.y = m_MotionCells.at (i).cell_pt1.y * 2; + pt2.x = m_MotionCells.at (i).cell_pt2.x * 2; + pt2.y = m_MotionCells.at (i).cell_pt2.y * 2; + if (m_useAlpha && m_isVisible) { + cvRectangle (transparencyimg, + pt1, + pt2, + CV_RGB (motioncellscolor.B_channel_value, + motioncellscolor.G_channel_value, + motioncellscolor.R_channel_value), CV_FILLED); + } else if (m_isVisible) { + cvRectangle (p_frame, + pt1, + pt2, + CV_RGB (motioncellscolor.B_channel_value, + motioncellscolor.G_channel_value, + motioncellscolor.R_channel_value), p_thickness); + } + + } + if (m_useAlpha && m_isVisible) { + if (m_MotionCells.size () > 0) + blendImages (p_frame, transparencyimg, m_alpha, m_beta); + } + } + return ret; +} + +int +MotionCells::initDataFile (char *p_datafile, gint64 starttime) //p_date is increased with difference between current and previous buffer ts +{ + MotionCellData mcd; + if (strncmp (p_datafile, " ", 1)) { + mc_savefile = fopen (p_datafile, "w"); + if (mc_savefile == NULL) { + //fprintf(stderr, "%s %d:initDataFile:fopen:%d (%s)\n", __FILE__, __LINE__, errno, + //strerror(errno)); + strncpy (m_initdatafilefailed, strerror (errno), BUSMSGLEN - 1); + m_initerrorcode = errno; + return 1; + } else { + m_saveInDatafile = true; + } + } else + mc_savefile = NULL; + bzero (&m_header, sizeof (MotionCellHeader)); + m_header.headersize = htonl (MC_HEADER); + m_header.type = htonl (MC_TYPE); + m_header.version = htonl (MC_VERSION); + //it needs these bytes + m_header.itemsize = + htonl ((int) ceil (ceil (m_gridx * m_gridy / 8.0) / 4.0) * 4 + + sizeof (mcd.timestamp)); + m_header.gridx = htonl (m_gridx); + m_header.gridy = htonl (m_gridy); + m_header.starttime = htonl64 (starttime); + + snprintf (m_header.name, sizeof (m_header.name), "%s %dx%d", MC_VERSIONTEXT, + ntohl (m_header.gridx), ntohl (m_header.gridy)); + m_changed_datafile = false; + return 0; +} + +int +MotionCells::saveMotionCells (gint64 timestamp_millisec) +{ + + MotionCellData mc_data; + mc_data.timestamp = htonl (timestamp_millisec); + mc_data.data = NULL; + //There is no datafile + if (mc_savefile == NULL) + return 0; + + if (ftello (mc_savefile) == 0) { + //cerr << "Writing out file header"<< m_header.headersize <<":" << sizeof(MotionCellHeader) << " itemsize:" + //<< m_header.itemsize << endl; + if (fwrite (&m_header, sizeof (MotionCellHeader), 1, mc_savefile) != 1) { + //fprintf(stderr, "%s %d:saveMotionCells:fwrite:%d (%s)\n", __FILE__, __LINE__, errno, + //strerror(errno)); + strncpy (m_savedatafilefailed, strerror (errno), BUSMSGLEN - 1); + m_saveerrorcode = errno; + return -1; + } + } + + mc_data.data = + (char *) calloc (1, + ntohl (m_header.itemsize) - sizeof (mc_data.timestamp)); + if (mc_data.data == NULL) { + //fprintf(stderr, "%s %d:saveMotionCells:calloc:%d (%s)\n", __FILE__, __LINE__, errno, + //strerror(errno)); + strncpy (m_savedatafilefailed, strerror (errno), BUSMSGLEN - 1); + m_saveerrorcode = errno; + return -1; + } + + for (unsigned int i = 0; i < m_MotionCells.size (); i++) { + int bitnum = + m_MotionCells.at (i).lineidx * ntohl (m_header.gridx) + + m_MotionCells.at (i).colidx; + int bytenum = (int) floor (bitnum / 8.0); + int shift = bitnum - bytenum * 8; + mc_data.data[bytenum] = mc_data.data[bytenum] | (1 << shift); + //cerr << "Motion Detected " << "line:" << m_MotionCells.at(i).lineidx << " col:" << m_MotionCells.at(i).colidx; + //cerr << " bitnum " << bitnum << " bytenum " << bytenum << " shift " << shift << " value " << (int)mc_data.data[bytenum] << endl; + } + + if (fwrite (&mc_data.timestamp, sizeof (mc_data.timestamp), 1, + mc_savefile) != 1) { + //fprintf(stderr, "%s %d:saveMotionCells:fwrite:%d (%s)\n", __FILE__, __LINE__, errno, + //strerror(errno)); + strncpy (m_savedatafilefailed, strerror (errno), BUSMSGLEN - 1); + m_saveerrorcode = errno; + return -1; + } + + if (fwrite (mc_data.data, + ntohl (m_header.itemsize) - sizeof (mc_data.timestamp), 1, + mc_savefile) != 1) { + //fprintf(stderr, "%s %d:saveMotionCells:fwrite:%d (%s)\n", __FILE__, __LINE__, errno, + //strerror(errno)); + strncpy (m_savedatafilefailed, strerror (errno), BUSMSGLEN - 1); + m_saveerrorcode = errno; + return -1; + } + + free (mc_data.data); + return 0; +} + +double +MotionCells::calculateMotionPercentInCell (int p_row, int p_col, + double *p_cellarea, double *p_motionarea) +{ + double cntpixelsnum = 0; + double cntmotionpixelnum = 0; + + int ybegin = floor ((double) p_row * m_cellheight); + int yend = floor ((double) (p_row + 1) * m_cellheight); + int xbegin = floor ((double) (p_col) * m_cellwidth); + int xend = floor ((double) (p_col + 1) * m_cellwidth); + int cellw = xend - xbegin; + int cellh = yend - ybegin; + int cellarea = cellw * cellh; + *p_cellarea = cellarea; + int thresholdmotionpixelnum = floor ((double) cellarea * m_sensitivity); + + for (int i = ybegin; i < yend; i++) { + for (int j = xbegin; j < xend; j++) { + cntpixelsnum++; + if ((((uchar *) (m_pbwImage->imageData + m_pbwImage->widthStep * i))[j]) > + 0) { + cntmotionpixelnum++; + if (cntmotionpixelnum >= thresholdmotionpixelnum) { //we dont needs calculate anymore + *p_motionarea = cntmotionpixelnum; + return (cntmotionpixelnum / cntpixelsnum); + } + } + int remainingpixelsnum = cellarea - cntpixelsnum; + if ((cntmotionpixelnum + remainingpixelsnum) < thresholdmotionpixelnum) { //moving pixels number will be less than threshold + *p_motionarea = 0; + return 0; + } + } + } + + return (cntmotionpixelnum / cntpixelsnum); +} + +void +MotionCells::calculateMotionPercentInMotionCells (motioncellidx * + p_motioncellsidx, int p_motioncells_count) +{ + if (p_motioncells_count == 0) { + for (int i = 0; i < m_gridy; i++) { + for (int j = 0; j < m_gridx; j++) { + m_pCells[i][j].MotionPercent = calculateMotionPercentInCell (i, j, + &m_pCells[i][j].CellArea, &m_pCells[i][j].MotionArea); + m_pCells[i][j].hasMotion = + m_sensitivity < m_pCells[i][j].MotionPercent ? true : false; + if (m_pCells[i][j].hasMotion) { + MotionCellsIdx mci; + mci.lineidx = i; + mci.colidx = j; + mci.cell_pt1.x = floor ((double) j * m_cellwidth); + mci.cell_pt1.y = floor ((double) i * m_cellheight); + mci.cell_pt2.x = floor ((double) (j + 1) * m_cellwidth); + mci.cell_pt2.y = floor ((double) (i + 1) * m_cellheight); + int w = mci.cell_pt2.x - mci.cell_pt1.x; + int h = mci.cell_pt2.y - mci.cell_pt1.y; + mci.motioncell = cvRect (mci.cell_pt1.x, mci.cell_pt1.y, w, h); + m_MotionCells.push_back (mci); + } + } + } + } else { + for (int k = 0; k < p_motioncells_count; ++k) { + + int i = p_motioncellsidx[k].lineidx; + int j = p_motioncellsidx[k].columnidx; + m_pCells[i][j].MotionPercent = + calculateMotionPercentInCell (i, j, + &m_pCells[i][j].CellArea, &m_pCells[i][j].MotionArea); + m_pCells[i][j].hasMotion = + m_pCells[i][j].MotionPercent > m_sensitivity ? true : false; + if (m_pCells[i][j].hasMotion) { + MotionCellsIdx mci; + mci.lineidx = p_motioncellsidx[k].lineidx; + mci.colidx = p_motioncellsidx[k].columnidx; + mci.cell_pt1.x = floor ((double) j * m_cellwidth); + mci.cell_pt1.y = floor ((double) i * m_cellheight); + mci.cell_pt2.x = floor ((double) (j + 1) * m_cellwidth); + mci.cell_pt2.y = floor ((double) (i + 1) * m_cellheight); + int w = mci.cell_pt2.x - mci.cell_pt1.x; + int h = mci.cell_pt2.y - mci.cell_pt1.y; + mci.motioncell = cvRect (mci.cell_pt1.x, mci.cell_pt1.y, w, h); + m_MotionCells.push_back (mci); + } + } + } +} + +void +MotionCells::performMotionMaskCoords (motionmaskcoordrect * p_motionmaskcoords, + int p_motionmaskcoords_count) +{ + CvPoint upperleft; + upperleft.x = 0; + upperleft.y = 0; + CvPoint lowerright; + lowerright.x = 0; + lowerright.y = 0; + for (int i = 0; i < p_motionmaskcoords_count; i++) { + upperleft.x = p_motionmaskcoords[i].upper_left_x; + upperleft.y = p_motionmaskcoords[i].upper_left_y; + lowerright.x = p_motionmaskcoords[i].lower_right_x; + lowerright.y = p_motionmaskcoords[i].lower_right_y; + cvRectangle (m_pbwImage, upperleft, lowerright, CV_RGB (0, 0, 0), + CV_FILLED); + } +} + +void +MotionCells::performMotionMask (motioncellidx * p_motionmaskcellsidx, + int p_motionmaskcells_count) +{ + for (int k = 0; k < p_motionmaskcells_count; k++) { + int beginy = p_motionmaskcellsidx[k].lineidx * m_cellheight; + int beginx = p_motionmaskcellsidx[k].columnidx * m_cellwidth; + int endx = + (double) p_motionmaskcellsidx[k].columnidx * m_cellwidth + m_cellwidth; + int endy = + (double) p_motionmaskcellsidx[k].lineidx * m_cellheight + m_cellheight; + for (int i = beginy; i < endy; i++) + for (int j = beginx; j < endx; j++) { + ((uchar *) (m_pbwImage->imageData + m_pbwImage->widthStep * i))[j] = 0; + } + } +} + +///BGR if we use only OpenCV +//RGB if we use gst+OpenCV +void +MotionCells::blendImages (IplImage * p_actFrame, IplImage * p_cellsFrame, + float p_alpha, float p_beta) +{ + + int height = p_actFrame->height; + int width = p_actFrame->width; + int step = p_actFrame->widthStep / sizeof (uchar); + int channels = p_actFrame->nChannels; + int cellstep = p_cellsFrame->widthStep / sizeof (uchar); + uchar *curImageData = (uchar *) p_actFrame->imageData; + uchar *cellImageData = (uchar *) p_cellsFrame->imageData; + + for (int i = 0; i < height; i++) + for (int j = 0; j < width; j++) + for (int k = 0; k < channels; k++) + if (cellImageData[i * cellstep + j * channels + k] > 0) { + curImageData[i * step + j * channels + k] = + round ((double) curImageData[i * step + j * channels + + k] * p_alpha + ((double) cellImageData[i * cellstep + + j * channels + k] * p_beta)); + } +} diff --git a/ext/opencv/MotionCells.h b/ext/opencv/MotionCells.h new file mode 100644 index 000000000..ee84fd6b5 --- /dev/null +++ b/ext/opencv/MotionCells.h @@ -0,0 +1,259 @@ +/* + * GStreamer + * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com> + * Copyright (C) 2011 Nicola Murino <nicola.murino@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef MOTIONCELLS_H_ +#define MOTIONCELLS_H_ + +#include <cv.h> // includes OpenCV definitions +#include <highgui.h> // includes highGUI definitions +#include <iostream> +#include <fstream> +#include <vector> +#include <cstdio> +#include <cmath> +#include <glib.h> + +//MotionCells defines +#define MC_HEADER 64 +#define MC_TYPE 1 +#define MC_VERSION 1 +#define MC_VERSIONTEXT "MotionCells-1" +#define MSGLEN 6 +#define BUSMSGLEN 20 + +using namespace std; + +struct MotionCellHeader{ + gint32 headersize; + gint32 type; + gint32 version; + gint32 itemsize; + gint32 gridx; + gint32 gridy; + gint64 starttime; + char name[MC_HEADER - 32]; +}; + +struct MotionCellData{ + gint32 timestamp; + char *data; +}; + +typedef struct { + int upper_left_x; + int upper_left_y; + int lower_right_x; + int lower_right_y; +} motionmaskcoordrect; + +typedef struct { + int R_channel_value; + int G_channel_value; + int B_channel_value; +} cellscolor; + +typedef struct { + int lineidx; + int columnidx; +} motioncellidx; + +struct Cell +{ + double MotionArea; + double CellArea; + double MotionPercent; + bool hasMotion; +}; + +struct MotionCellsIdx +{ + CvRect motioncell; + //Points for the edges of the rectangle. + CvPoint cell_pt1; + CvPoint cell_pt2; + int lineidx; + int colidx; +}; + +struct OverlayRegions +{ + CvPoint upperleft; + CvPoint lowerright; +}; + +class MotionCells +{ +public: + + MotionCells (); + virtual ~ MotionCells (); + + int performDetectionMotionCells (IplImage * p_frame, double p_sensitivity, + double p_framerate, int p_gridx, int p_gridy, gint64 timestamp_millisec, + bool p_isVisble, bool p_useAlpha, int motionmaskcoord_count, + motionmaskcoordrect * motionmaskcoords, int motionmaskcells_count, + motioncellidx * motionmaskcellsidx, cellscolor motioncellscolor, + int motioncells_count, motioncellidx * motioncellsidx, gint64 starttime, + char *datafile, bool p_changed_datafile, int p_thickness); + + void setPrevFrame (IplImage * p_prevframe) + { + m_pprevFrame = cvCloneImage (p_prevframe); + } + char *getMotionCellsIdx () + { + return m_motioncellsidxcstr; + } + + int getMotionCellsIdxCount () + { + return m_motioncells_idx_count; + } + + bool getChangedDataFile () + { + return m_changed_datafile; + } + + char *getDatafileInitFailed () + { + return m_initdatafilefailed; + } + + char *getDatafileSaveFailed () + { + return m_savedatafilefailed; + } + + int getInitErrorCode () + { + return m_initerrorcode; + } + + int getSaveErrorCode () + { + return m_saveerrorcode; + } + + void freeDataFile () + { + if (mc_savefile) { + fclose (mc_savefile); + mc_savefile = NULL; + m_saveInDatafile = false; + } + } + +private: + + double calculateMotionPercentInCell (int p_row, int p_col, double *p_cellarea, + double *p_motionarea); + void performMotionMaskCoords (motionmaskcoordrect * p_motionmaskcoords, + int p_motionmaskcoords_count); + void performMotionMask (motioncellidx * p_motionmaskcellsidx, + int p_motionmaskcells_count); + void calculateMotionPercentInMotionCells (motioncellidx * + p_motionmaskcellsidx, int p_motionmaskcells_count = 0); + int saveMotionCells (gint64 timestamp_millisec); + int initDataFile (char *p_datafile, gint64 starttime); + void blendImages (IplImage * p_actFrame, IplImage * p_cellsFrame, + float p_alpha, float p_beta); + + void setData (IplImage * img, int lin, int col, uchar valor) + { + ((uchar *) (img->imageData + img->widthStep * lin))[col] = valor; + } + + uchar getData (IplImage * img, int lin, int col) + { + return ((uchar *) (img->imageData + img->widthStep * lin))[col]; + } + + bool getIsNonZero (IplImage * img) + { + for (int lin = 0; lin < img->height; lin++) + for (int col = 0; col < img->width; col++) { + if ((((uchar *) (img->imageData + img->widthStep * lin))[col]) > 0) + return true; + } + return false; + } + + void setMotionCells (int p_frameWidth, int p_frameHeight) + { + m_cellwidth = (double) p_frameWidth / (double) m_gridx; + m_cellheight = (double) p_frameHeight / (double) m_gridy; + m_pCells = new Cell *[m_gridy]; + for (int i = 0; i < m_gridy; i++) + m_pCells[i] = new Cell[m_gridx]; + + //init cells + for (int i = 0; i < m_gridy; i++) + for (int j = 0; j < m_gridx; j++) { + m_pCells[i][j].MotionArea = 0; + m_pCells[i][j].CellArea = 0; + m_pCells[i][j].MotionPercent = 0; + m_pCells[i][j].hasMotion = false; + } + } + + IplImage *m_pcurFrame, *m_pprevFrame, *m_pdifferenceImage, + *m_pbwImage,*transparencyimg; + CvSize m_frameSize; + bool m_isVisible, m_changed_datafile, m_useAlpha, m_saveInDatafile; + Cell **m_pCells; + vector < MotionCellsIdx > m_MotionCells; + vector < OverlayRegions > m_OverlayRegions; + int m_gridx, m_gridy; + double m_cellwidth, m_cellheight; + double m_alpha, m_beta; + double m_thresholdBoundingboxArea, m_cellArea, m_sensitivity; + int m_framecnt, m_motioncells_idx_count, m_initerrorcode, m_saveerrorcode; + char *m_motioncellsidxcstr, *m_initdatafilefailed, *m_savedatafilefailed; + FILE *mc_savefile; + MotionCellHeader m_header; + +}; + +#endif /* MOTIONCELLS_H_ */ diff --git a/ext/opencv/gstmotioncells.c b/ext/opencv/gstmotioncells.c new file mode 100644 index 000000000..029295931 --- /dev/null +++ b/ext/opencv/gstmotioncells.c @@ -0,0 +1,1111 @@ +/* + * GStreamer MotioCells detect areas of motion + * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com> + * Copyright (C) 2011 Nicola Murino <nicola.murino@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-motioncells + * + * Performs motion detection on videos. + * + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch-0.10 videotestsrc pattern=18 ! videorate ! videoscale ! video/x-raw-yuv,width=320,height=240,framerate=5/1 ! ffmpegcolorspace ! motioncells ! ffmpegcolorspace ! xvimagesink + * ]| + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <glib.h> +#include "gstmotioncells.h" +#include "motioncells_wrapper.h" +#include <sys/time.h> +#include <time.h> +#include <limits.h> + +GST_DEBUG_CATEGORY_STATIC (gst_motion_cells_debug); +#define GST_CAT_DEFAULT gst_motion_cells_debug + +#define GRID_DEF 10 +#define GRID_MIN 8 +#define GRID_MAX 32 +#define SENSITIVITY_DEFAULT 0.5 +#define SENSITIVITY_MIN 0 +#define SENSITIVITY_MAX 1 +#define THRESHOLD_MIN 0 +#define THRESHOLD_DEFAULT 0.01 +#define THRESHOLD_MAX 1.0 +#define GAP_MIN 1 +#define GAP_DEF 5 +#define GAP_MAX 60 +#define POST_NO_MOTION_MIN 0 +#define POST_NO_MOTION_DEF 0 +#define POST_NO_MOTION_MAX 180 +#define MINIMUM_MOTION_FRAMES_MIN 1 +#define MINIMUM_MOTION_FRAMES_DEF 1 +#define MINIMUM_MOTION_FRAMES_MAX 60 +#define THICKNESS_MIN -1 +#define THICKNESS_DEF 1 +#define THICKNESS_MAX 5 +#define DATE_MIN 0 +#define DATE_DEF 1 +#define DATE_MAX LONG_MAX +#define DEF_DATAFILEEXT "vamc" +#define MSGLEN 6 +#define BUSMSGLEN 20 + +#define GFREE(POINTER)\ + {\ + g_free(POINTER);\ + POINTER = NULL;\ + } + +int instanceCounter = 0; +gboolean element_id_was_max = false; + +/* Filter signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_GRID_X, + PROP_GRID_Y, + PROP_SENSITIVITY, + PROP_THRESHOLD, + PROP_DISPLAY, + PROP_DATE, + PROP_DATAFILE, + PROP_DATAFILE_EXT, + PROP_MOTIONMASKCOORD, + PROP_MOTIONMASKCELLSPOS, + PROP_CELLSCOLOR, + PROP_MOTIONCELLSIDX, + PROP_GAP, + PROP_POSTNOMOTION, + PROP_MINIMUNMOTIONFRAMES, + PROP_CALCULATEMOTION, + PROP_POSTALLMOTION, + PROP_USEALPHA, + PROP_MOTIONCELLTHICKNESS +}; + +/* the capabilities of the inputs and outputs. + */ +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-raw-rgb")); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-raw-rgb")); + +GST_BOILERPLATE (GstMotioncells, gst_motion_cells, GstElement, + GST_TYPE_ELEMENT); + +static void gst_motion_cells_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_motion_cells_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_motion_cells_set_caps (GstPad * pad, GstCaps * caps); +static GstFlowReturn gst_motion_cells_chain (GstPad * pad, GstBuffer * buf); + +static void gst_motioncells_update_motion_cells (GstMotioncells * filter); +static void gst_motioncells_update_motion_masks (GstMotioncells * filter); + +/* Clean up */ +static void +gst_motion_cells_finalize (GObject * obj) +{ + GstMotioncells *filter = gst_motion_cells (obj); + + motion_cells_free (filter->id); + + //freeing previously allocated dynamic array + if (filter->motionmaskcoord_count > 0) { + GFREE (filter->motionmaskcoords); + } + + if (filter->motionmaskcells_count > 0) { + GFREE (filter->motionmaskcellsidx); + } + if (filter->motioncells_count > 0) { + GFREE (filter->motioncellsidx); + } + + if (filter->cvImage) { + cvReleaseImage (&filter->cvImage); + } + + GFREE (filter->motioncellscolor); + GFREE (filter->prev_datafile); + GFREE (filter->cur_datafile); + GFREE (filter->basename_datafile); + GFREE (filter->datafile_extension); + + g_mutex_free (filter->propset_mutex); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +/* GObject vmethod implementations */ +static void +gst_motion_cells_base_init (gpointer gclass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); + + gst_element_class_set_details_simple (element_class, + "motioncells", + "Filter/Effect/Video", + "Performs motion detection on videos and images, providing detected motion cells index via bus messages", + "Robert Jobbagy <jobbagy dot robert at gmail dot com>, Nicola Murino <nicola dot murino at gmail.com>"); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); +} + +/* initialize the motioncells's class */ +static void +gst_motion_cells_class_init (GstMotioncellsClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_motion_cells_finalize); + gobject_class->set_property = gst_motion_cells_set_property; + gobject_class->get_property = gst_motion_cells_get_property; + + g_object_class_install_property (gobject_class, PROP_GRID_X, + g_param_spec_int ("gridx", "Number of Horizontal Grids", + "You can give number of horizontal grid cells.", GRID_MIN, GRID_MAX, + GRID_DEF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_GRID_Y, + g_param_spec_int ("gridy", "Number of Vertical Grids", + "You can give number of vertical grid cells.", GRID_MIN, GRID_MAX, + GRID_DEF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_SENSITIVITY, + g_param_spec_double ("sensitivity", "Motion Sensitivity", + "You can tunning the element motion sensitivity.", SENSITIVITY_MIN, + SENSITIVITY_MAX, SENSITIVITY_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_THRESHOLD, + g_param_spec_double ("threshold", "Lower bound of motion cells number", + "Threshold value for motion, when motion cells number greater sum cells * threshold, we show motion.", + THRESHOLD_MIN, THRESHOLD_MAX, THRESHOLD_DEFAULT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_GAP, + g_param_spec_int ("gap", + "Gap is time in second, elapsed time from last motion timestamp. ", + "If elapsed time minus form last motion timestamp is greater or equal than gap then we post motion finished bus message. ", + GAP_MIN, GAP_MAX, GAP_DEF, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_POSTNOMOTION, + g_param_spec_int ("postnomotion", "POSTNOMOTION", + "If non 0 post a no_motion event is posted on the bus if no motion is detected for N seconds", + POST_NO_MOTION_MIN, POST_NO_MOTION_MAX, POST_NO_MOTION_DEF, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MINIMUNMOTIONFRAMES, + g_param_spec_int ("minimummotionframes", "MINIMUN MOTION FRAMES", + "Define the minimum number of motion frames that trigger a motion event", + MINIMUM_MOTION_FRAMES_MIN, MINIMUM_MOTION_FRAMES_MAX, + MINIMUM_MOTION_FRAMES_DEF, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DISPLAY, + g_param_spec_boolean ("display", "Display", + "Motion Cells visible or not on Current Frame", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_POSTALLMOTION, + g_param_spec_boolean ("postallmotion", "Post All Motion", + "Element post bus msg for every motion frame or just motion start and motion stop", + FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_USEALPHA, + g_param_spec_boolean ("usealpha", "Use alpha", + "Use or not alpha blending on frames with motion cells", TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DATE, + g_param_spec_long ("date", "Motion Cell Date", + "Current Date in milliseconds", DATE_MIN, DATE_MAX, DATE_DEF, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DATAFILE, + g_param_spec_string ("datafile", "DataFile", + "Location of motioncells data file (empty string means no saving)", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_DATAFILE_EXT, + g_param_spec_string ("datafileextension", "DataFile Extension", + "Extension of datafile", DEF_DATAFILEEXT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MOTIONMASKCOORD, + g_param_spec_string ("motionmaskcoords", "Motion Mask with Coordinates", + "The upper left x, y and lower right x, y coordinates separated with \":\", " + "describe a region. Regions separated with \",\"", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MOTIONMASKCELLSPOS, + g_param_spec_string ("motionmaskcellspos", + "Motion Mask with Cells Position", + "The line and column idx separated with \":\" what cells want we mask-out, " + "describe a cell. Cells separated with \",\"", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CELLSCOLOR, + g_param_spec_string ("cellscolor", "Color of Motion Cells", + "The color of motion cells separated with \",\"", "255,255,0", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MOTIONCELLSIDX, + g_param_spec_string ("motioncellsidx", "Motion Cells Of Interest(MOCI)", + "The line and column idx separated with \":\", " + "describe a cell. Cells separated with \",\"", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_CALCULATEMOTION, + g_param_spec_boolean ("calculatemotion", "Calculate Motion", + "If needs calculate motion on frame you need this property setting true otherwise false", + TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_MOTIONCELLTHICKNESS, + g_param_spec_int ("motioncellthickness", "Motion Cell Thickness", + "Motion Cell Border Thickness, if it's -1 then motion cell will be fill", + THICKNESS_MIN, THICKNESS_MAX, THICKNESS_DEF, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +/* initialize the new element + * instantiate pads and add them to element + * set pad callback functions + * initialize instance structure + */ +static void +gst_motion_cells_init (GstMotioncells * filter, GstMotioncellsClass * gclass) +{ + filter->propset_mutex = g_mutex_new (); + filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + gst_pad_set_setcaps_function (filter->sinkpad, + GST_DEBUG_FUNCPTR (gst_motion_cells_set_caps)); + gst_pad_set_getcaps_function (filter->sinkpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); + gst_pad_set_chain_function (filter->sinkpad, + GST_DEBUG_FUNCPTR (gst_motion_cells_chain)); + + filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_set_getcaps_function (filter->srcpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); + + gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad); + gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad); + + filter->display = TRUE; + filter->calculate_motion = TRUE; + + filter->prevgridx = 0; + filter->prevgridy = 0; + filter->gridx = GRID_DEF; + filter->gridy = GRID_DEF; + filter->gap = GAP_DEF; + filter->postnomotion = POST_NO_MOTION_DEF; + filter->minimum_motion_frames = MINIMUM_MOTION_FRAMES_DEF; + + filter->prev_datafile = g_strdup (NULL); + filter->cur_datafile = g_strdup (NULL); + filter->basename_datafile = g_strdup (NULL); + filter->datafile_extension = g_strdup (DEF_DATAFILEEXT); + filter->sensitivity = SENSITIVITY_DEFAULT; + filter->threshold = THRESHOLD_DEFAULT; + + filter->motionmaskcoord_count = 0; + filter->motionmaskcoords = NULL; + filter->motionmaskcells_count = 0; + filter->motionmaskcellsidx = NULL; + filter->motioncellscolor = g_new0 (cellscolor, 1); + filter->motioncellscolor->R_channel_value = 255; + filter->motioncellscolor->G_channel_value = 255; + filter->motioncellscolor->B_channel_value = 0; + filter->motioncellsidx = NULL; + filter->motioncells_count = 0; + filter->motion_begin_timestamp = 0; + filter->last_motion_timestamp = 0; + filter->last_nomotion_notified = 0; + filter->consecutive_motion = 0; + filter->motion_timestamp = 0; + filter->prev_buff_timestamp = 0; + filter->cur_buff_timestamp = 0; + filter->diff_timestamp = -1; + gettimeofday (&filter->tv, NULL); + filter->starttime = 1000 * filter->tv.tv_sec; + filter->previous_motion = false; + filter->changed_datafile = false; + filter->postallmotion = false; + filter->usealpha = true; + filter->firstdatafile = false; + filter->firstgridx = true; + filter->firstgridy = true; + filter->changed_gridx = false; + filter->changed_gridy = false; + filter->firstframe = true; + filter->changed_startime = false; + filter->sent_init_error_msg = false; + filter->sent_save_error_msg = false; + filter->thickness = THICKNESS_DEF; + + filter->datafileidx = 0; + g_mutex_lock (filter->propset_mutex); + filter->id = instanceCounter; + motion_cells_init (); + g_mutex_unlock (filter->propset_mutex); + +} + +static void +gst_motion_cells_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstMotioncells *filter = gst_motion_cells (object); + //variables for overlay regions setup + gchar **strs, **colorstr, **motioncellsstr, **motionmaskcellsstr; + int i, ux, uy, lx, ly; + int r, g, b; + int cellscolorscnt = 0; + int linidx, colidx, masklinidx, maskcolidx; + int tmpux = -1; + int tmpuy = -1; + int tmplx = -1; + int tmply = -1; + GstStateChangeReturn ret; + + g_mutex_lock (filter->propset_mutex); + switch (prop_id) { + case PROP_GRID_X: + ret = gst_element_get_state (GST_ELEMENT (filter), + &filter->state, NULL, 250 * GST_NSECOND); + filter->gridx = g_value_get_int (value); + if (filter->prevgridx != filter->gridx + && ret == GST_STATE_CHANGE_SUCCESS + && filter->state == GST_STATE_PLAYING) { + filter->changed_gridx = true; + } + filter->prevgridx = filter->gridx; + break; + case PROP_GRID_Y: + ret = gst_element_get_state (GST_ELEMENT (filter), + &filter->state, NULL, 250 * GST_NSECOND); + filter->gridy = g_value_get_int (value); + if (filter->prevgridy != filter->gridy + && ret == GST_STATE_CHANGE_SUCCESS + && filter->state == GST_STATE_PLAYING) { + filter->changed_gridy = true; + } + filter->prevgridy = filter->gridy; + break; + case PROP_GAP: + filter->gap = g_value_get_int (value); + break; + case PROP_POSTNOMOTION: + filter->postnomotion = g_value_get_int (value); + break; + case PROP_MINIMUNMOTIONFRAMES: + filter->minimum_motion_frames = g_value_get_int (value); + break; + case PROP_SENSITIVITY: + filter->sensitivity = g_value_get_double (value); + break; + case PROP_THRESHOLD: + filter->threshold = g_value_get_double (value); + break; + case PROP_DISPLAY: + filter->display = g_value_get_boolean (value); + break; + case PROP_POSTALLMOTION: + filter->postallmotion = g_value_get_boolean (value); + break; + case PROP_USEALPHA: + filter->usealpha = g_value_get_boolean (value); + break; + case PROP_CALCULATEMOTION: + filter->calculate_motion = g_value_get_boolean (value); + break; + case PROP_DATE: + ret = gst_element_get_state (GST_ELEMENT (filter), + &filter->state, NULL, 250 * GST_NSECOND); + if (ret == GST_STATE_CHANGE_SUCCESS && filter->state == GST_STATE_PLAYING) { + filter->changed_startime = true; + } + filter->starttime = g_value_get_long (value); + break; + case PROP_DATAFILE: + GFREE (filter->cur_datafile); + GFREE (filter->basename_datafile); + filter->basename_datafile = g_value_dup_string (value); + + if (strlen (filter->basename_datafile) == 0) { + filter->cur_datafile = g_strdup (NULL); + break; + } + filter->cur_datafile = + g_strdup_printf ("%s-0.%s", filter->basename_datafile, + filter->datafile_extension); + if (g_strcmp0 (filter->prev_datafile, filter->basename_datafile) != 0) { + filter->changed_datafile = TRUE; + filter->sent_init_error_msg = FALSE; + filter->sent_save_error_msg = FALSE; + filter->datafileidx = 0; + motion_cells_free_resources (filter->id); + } else { + filter->changed_datafile = FALSE; + } + + GFREE (filter->prev_datafile); + filter->prev_datafile = g_strdup (filter->basename_datafile); + break; + case PROP_DATAFILE_EXT: + GFREE (filter->datafile_extension); + filter->datafile_extension = g_value_dup_string (value); + break; + case PROP_MOTIONMASKCOORD: + strs = g_strsplit (g_value_get_string (value), ",", 255); + GFREE (filter->motionmaskcoords); + //setting number of regions + for (filter->motionmaskcoord_count = 0; + strs[filter->motionmaskcoord_count] != NULL; + ++filter->motionmaskcoord_count); + if (filter->motionmaskcoord_count > 0) { + sscanf (strs[0], "%d:%d:%d:%d", &tmpux, &tmpuy, &tmplx, &tmply); + if (tmpux > -1 && tmpuy > -1 && tmplx > -1 && tmply > -1) { + filter->motionmaskcoords = + g_new0 (motionmaskcoordrect, filter->motionmaskcoord_count); + + for (i = 0; i < filter->motionmaskcoord_count; ++i) { + sscanf (strs[i], "%d:%d:%d:%d", &ux, &uy, &lx, &ly); + ux = CLAMP (ux, 0, filter->width - 1); + uy = CLAMP (uy, 0, filter->height - 1); + lx = CLAMP (lx, 0, filter->width - 1); + ly = CLAMP (ly, 0, filter->height - 1); + filter->motionmaskcoords[i].upper_left_x = ux; + filter->motionmaskcoords[i].upper_left_y = uy; + filter->motionmaskcoords[i].lower_right_x = lx; + filter->motionmaskcoords[i].lower_right_y = ly; + } + } else { + filter->motionmaskcoord_count = 0; + } + } + if (strs) + g_strfreev (strs); + tmpux = -1; + tmpuy = -1; + tmplx = -1; + tmply = -1; + break; + case PROP_MOTIONMASKCELLSPOS: + motionmaskcellsstr = g_strsplit (g_value_get_string (value), ",", 255); + GFREE (filter->motionmaskcellsidx); + //setting number of regions + for (filter->motionmaskcells_count = 0; + motionmaskcellsstr[filter->motionmaskcells_count] != NULL; + ++filter->motionmaskcells_count); + if (filter->motionmaskcells_count > 0) { + sscanf (motionmaskcellsstr[0], "%d:%d", &tmpux, &tmpuy); + if (tmpux > -1 && tmpuy > -1) { + filter->motionmaskcellsidx = + g_new0 (motioncellidx, filter->motionmaskcells_count); + for (i = 0; i < filter->motionmaskcells_count; ++i) { + sscanf (motionmaskcellsstr[i], "%d:%d", &masklinidx, &maskcolidx); + filter->motionmaskcellsidx[i].lineidx = masklinidx; + filter->motionmaskcellsidx[i].columnidx = maskcolidx; + } + } else { + filter->motionmaskcells_count = 0; + } + } + if (motionmaskcellsstr) + g_strfreev (motionmaskcellsstr); + tmpux = -1; + tmpuy = -1; + tmplx = -1; + tmply = -1; + break; + case PROP_CELLSCOLOR: + colorstr = g_strsplit (g_value_get_string (value), ",", 255); + for (cellscolorscnt = 0; colorstr[cellscolorscnt] != NULL; + ++cellscolorscnt); + if (cellscolorscnt == 3) { + sscanf (colorstr[0], "%d", &r); + sscanf (colorstr[1], "%d", &g); + sscanf (colorstr[2], "%d", &b); + //check right RGB color format + r = CLAMP (r, 1, 255); + g = CLAMP (g, 1, 255); + b = CLAMP (b, 1, 255); + filter->motioncellscolor->R_channel_value = r; + filter->motioncellscolor->G_channel_value = g; + filter->motioncellscolor->B_channel_value = b; + } + if (colorstr) + g_strfreev (colorstr); + break; + case PROP_MOTIONCELLSIDX: + motioncellsstr = g_strsplit (g_value_get_string (value), ",", 255); + + //setting number of regions + for (filter->motioncells_count = 0; + motioncellsstr[filter->motioncells_count] != NULL; + ++filter->motioncells_count); + if (filter->motioncells_count > 0) { + sscanf (motioncellsstr[0], "%d:%d", &tmpux, &tmpuy); + if (tmpux > -1 && tmpuy > -1) { + GFREE (filter->motioncellsidx); + + filter->motioncellsidx = + g_new0 (motioncellidx, filter->motioncells_count); + + for (i = 0; i < filter->motioncells_count; ++i) { + sscanf (motioncellsstr[i], "%d:%d", &linidx, &colidx); + filter->motioncellsidx[i].lineidx = linidx; + filter->motioncellsidx[i].columnidx = colidx; + } + } else { + filter->motioncells_count = 0; + } + } + if (motioncellsstr) + g_strfreev (motioncellsstr); + tmpux = -1; + tmpuy = -1; + tmplx = -1; + tmply = -1; + break; + case PROP_MOTIONCELLTHICKNESS: + filter->thickness = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + g_mutex_unlock (filter->propset_mutex); +} + +static void +gst_motion_cells_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstMotioncells *filter = gst_motion_cells (object); + GString *str; + int i; + + switch (prop_id) { + case PROP_GRID_X: + g_value_set_int (value, filter->gridx); + break; + case PROP_GRID_Y: + g_value_set_int (value, filter->gridy); + break; + case PROP_GAP: + g_value_set_int (value, filter->gap); + break; + case PROP_POSTNOMOTION: + g_value_set_int (value, filter->postnomotion); + break; + case PROP_MINIMUNMOTIONFRAMES: + g_value_set_int (value, filter->minimum_motion_frames); + break; + case PROP_SENSITIVITY: + g_value_set_double (value, filter->sensitivity); + break; + case PROP_THRESHOLD: + g_value_set_double (value, filter->threshold); + break; + case PROP_DISPLAY: + g_value_set_boolean (value, filter->display); + break; + case PROP_POSTALLMOTION: + g_value_set_boolean (value, filter->postallmotion); + break; + case PROP_USEALPHA: + g_value_set_boolean (value, filter->usealpha); + break; + case PROP_CALCULATEMOTION: + g_value_set_boolean (value, filter->calculate_motion); + break; + case PROP_DATE: + g_value_set_long (value, filter->starttime); + break; + case PROP_DATAFILE: + g_value_set_string (value, filter->basename_datafile); + break; + case PROP_DATAFILE_EXT: + g_value_set_string (value, filter->datafile_extension); + break; + case PROP_MOTIONMASKCOORD: + str = g_string_new (""); + for (i = 0; i < filter->motionmaskcoord_count; ++i) { + if (i < filter->motionmaskcoord_count - 1) + g_string_append_printf (str, "%d:%d:%d:%d,", + filter->motionmaskcoords[i].upper_left_x, + filter->motionmaskcoords[i].upper_left_y, + filter->motionmaskcoords[i].lower_right_x, + filter->motionmaskcoords[i].lower_right_y); + else + g_string_append_printf (str, "%d:%d:%d:%d", + filter->motionmaskcoords[i].upper_left_x, + filter->motionmaskcoords[i].upper_left_y, + filter->motionmaskcoords[i].lower_right_x, + filter->motionmaskcoords[i].lower_right_y); + + } + g_value_set_string (value, str->str); + g_string_free (str, TRUE); + break; + case PROP_MOTIONMASKCELLSPOS: + str = g_string_new (""); + for (i = 0; i < filter->motionmaskcells_count; ++i) { + if (i < filter->motionmaskcells_count - 1) + g_string_append_printf (str, "%d:%d,", + filter->motionmaskcellsidx[i].lineidx, + filter->motionmaskcellsidx[i].columnidx); + else + g_string_append_printf (str, "%d:%d", + filter->motionmaskcellsidx[i].lineidx, + filter->motionmaskcellsidx[i].columnidx); + } + g_value_set_string (value, str->str); + g_string_free (str, TRUE); + break; + case PROP_CELLSCOLOR: + str = g_string_new (""); + + g_string_printf (str, "%d,%d,%d", + filter->motioncellscolor->R_channel_value, + filter->motioncellscolor->G_channel_value, + filter->motioncellscolor->B_channel_value); + + g_value_set_string (value, str->str); + g_string_free (str, TRUE); + break; + case PROP_MOTIONCELLSIDX: + str = g_string_new (""); + for (i = 0; i < filter->motioncells_count; ++i) { + if (i < filter->motioncells_count - 1) + g_string_append_printf (str, "%d:%d,", + filter->motioncellsidx[i].lineidx, + filter->motioncellsidx[i].columnidx); + else + g_string_append_printf (str, "%d:%d", + filter->motioncellsidx[i].lineidx, + filter->motioncellsidx[i].columnidx); + } + g_value_set_string (value, str->str); + g_string_free (str, TRUE); + break; + case PROP_MOTIONCELLTHICKNESS: + g_value_set_int (value, filter->thickness); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_motioncells_update_motion_cells (GstMotioncells * filter) +{ + int i = 0; + int cellscnt = 0; + int j = 0; + int newcellscnt; + motioncellidx *motioncellsidx; + for (i = 0; i < filter->motioncells_count; i++) { + if ((filter->gridx <= filter->motioncellsidx[i].columnidx) || + (filter->gridy <= filter->motioncellsidx[i].lineidx)) { + cellscnt++; + } + } + newcellscnt = filter->motioncells_count - cellscnt; + motioncellsidx = g_new0 (motioncellidx, newcellscnt); + for (i = 0; i < filter->motioncells_count; i++) { + if ((filter->motioncellsidx[i].lineidx < filter->gridy) && + (filter->motioncellsidx[i].columnidx < filter->gridx)) { + motioncellsidx[j].lineidx = filter->motioncellsidx[i].lineidx; + motioncellsidx[j].columnidx = filter->motioncellsidx[i].columnidx; + j++; + } + } + GFREE (filter->motioncellsidx); + filter->motioncells_count = newcellscnt; + filter->motioncellsidx = g_new0 (motioncellidx, filter->motioncells_count); + j = 0; + for (i = 0; i < filter->motioncells_count; i++) { + filter->motioncellsidx[i].lineidx = motioncellsidx[j].lineidx; + filter->motioncellsidx[i].columnidx = motioncellsidx[j].columnidx; + j++; + } + GFREE (motioncellsidx); +} + +static void +gst_motioncells_update_motion_masks (GstMotioncells * filter) +{ + + int i = 0; + int maskcnt = 0; + int j = 0; + int newmaskcnt; + motioncellidx *motionmaskcellsidx; + for (i = 0; i < filter->motionmaskcells_count; i++) { + if ((filter->gridx <= filter->motionmaskcellsidx[i].columnidx) || + (filter->gridy <= filter->motionmaskcellsidx[i].lineidx)) { + maskcnt++; + } + } + newmaskcnt = filter->motionmaskcells_count - maskcnt; + motionmaskcellsidx = g_new0 (motioncellidx, newmaskcnt); + for (i = 0; i < filter->motionmaskcells_count; i++) { + if ((filter->motionmaskcellsidx[i].lineidx < filter->gridy) && + (filter->motionmaskcellsidx[i].columnidx < filter->gridx)) { + motionmaskcellsidx[j].lineidx = filter->motionmaskcellsidx[i].lineidx; + motionmaskcellsidx[j].columnidx = filter->motionmaskcellsidx[i].columnidx; + j++; + } + } + GFREE (filter->motionmaskcellsidx); + filter->motionmaskcells_count = newmaskcnt; + filter->motionmaskcellsidx = + g_new0 (motioncellidx, filter->motionmaskcells_count); + j = 0; + for (i = 0; i < filter->motionmaskcells_count; i++) { + filter->motionmaskcellsidx[i].lineidx = motionmaskcellsidx[j].lineidx; + filter->motionmaskcellsidx[i].columnidx = motionmaskcellsidx[j].columnidx; + j++; + } + GFREE (motionmaskcellsidx); +} + +/* GstElement vmethod implementations */ + +/* this function handles the link with other elements */ +static gboolean +gst_motion_cells_set_caps (GstPad * pad, GstCaps * caps) +{ + GstMotioncells *filter; + GstPad *otherpad; + GstStructure *structure; + int numerator, denominator; + + filter = gst_motion_cells (gst_pad_get_parent (pad)); + structure = gst_caps_get_structure (caps, 0); + gst_structure_get_int (structure, "width", &filter->width); + gst_structure_get_int (structure, "height", &filter->height); + gst_structure_get_fraction (structure, "framerate", &numerator, &denominator); + filter->framerate = (double) numerator / (double) denominator; + if (filter->cvImage) + cvReleaseImage (&filter->cvImage); + filter->cvImage = + cvCreateImage (cvSize (filter->width, filter->height), IPL_DEPTH_8U, 3); + + otherpad = (pad == filter->srcpad) ? filter->sinkpad : filter->srcpad; + gst_object_unref (filter); + + return gst_pad_set_caps (otherpad, caps); +} + +/* chain function + * this function does the actual processing + */ +static GstFlowReturn +gst_motion_cells_chain (GstPad * pad, GstBuffer * buf) +{ + + GstMotioncells *filter; + + filter = gst_motion_cells (GST_OBJECT_PARENT (pad)); + if (filter->calculate_motion) { + double sensitivity; + int framerate, gridx, gridy, motionmaskcells_count, motionmaskcoord_count, + motioncells_count, i; + int thickness, success, motioncellsidxcnt, numberOfCells, + motioncellsnumber, cellsOfInterestNumber; + int mincellsOfInterestNumber, motiondetect; + char *datafile; + bool display, changed_datafile, useAlpha; + gint64 starttime; + motionmaskcoordrect *motionmaskcoords; + motioncellidx *motionmaskcellsidx; + cellscolor motioncellscolor; + motioncellidx *motioncellsidx; + g_mutex_lock (filter->propset_mutex); + buf = gst_buffer_make_writable (buf); + filter->cvImage->imageData = (char *) GST_BUFFER_DATA (buf); + if (filter->firstframe) { + setPrevFrame (filter->cvImage, filter->id); + filter->firstframe = FALSE; + } + + sensitivity = filter->sensitivity; + framerate = filter->framerate; + gridx = filter->gridx; + gridy = filter->gridy; + display = filter->display; + motionmaskcoord_count = filter->motionmaskcoord_count; + motionmaskcoords = + g_new0 (motionmaskcoordrect, filter->motionmaskcoord_count); + for (i = 0; i < filter->motionmaskcoord_count; i++) { //we need divide 2 because we use gauss pyramid in C++ side + motionmaskcoords[i].upper_left_x = + filter->motionmaskcoords[i].upper_left_x / 2; + motionmaskcoords[i].upper_left_y = + filter->motionmaskcoords[i].upper_left_y / 2; + motionmaskcoords[i].lower_right_x = + filter->motionmaskcoords[i].lower_right_x / 2; + motionmaskcoords[i].lower_right_y = + filter->motionmaskcoords[i].lower_right_y / 2; + } + + motioncellscolor.R_channel_value = + filter->motioncellscolor->R_channel_value; + motioncellscolor.G_channel_value = + filter->motioncellscolor->G_channel_value; + motioncellscolor.B_channel_value = + filter->motioncellscolor->B_channel_value; + + if ((filter->changed_gridx || filter->changed_gridy + || filter->changed_startime)) { + if ((g_strcmp0 (filter->cur_datafile, NULL) != 0)) { + GFREE (filter->cur_datafile); + filter->datafileidx++; + filter->cur_datafile = + g_strdup_printf ("%s-%d.%s", filter->basename_datafile, + filter->datafileidx, filter->datafile_extension); + filter->changed_datafile = TRUE; + motion_cells_free_resources (filter->id); + } + if (filter->motioncells_count > 0) + gst_motioncells_update_motion_cells (filter); + if (filter->motionmaskcells_count > 0) + gst_motioncells_update_motion_masks (filter); + filter->changed_gridx = FALSE; + filter->changed_gridy = FALSE; + filter->changed_startime = FALSE; + } + datafile = g_strdup (filter->cur_datafile); + filter->cur_buff_timestamp = (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND); + filter->starttime += + (filter->cur_buff_timestamp - filter->prev_buff_timestamp); + starttime = filter->starttime; + if (filter->changed_datafile || filter->diff_timestamp < 0) + filter->diff_timestamp = + (gint64) (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND); + changed_datafile = filter->changed_datafile; + motionmaskcells_count = filter->motionmaskcells_count; + motionmaskcellsidx = g_new0 (motioncellidx, filter->motionmaskcells_count); + for (i = 0; i < filter->motionmaskcells_count; i++) { + motionmaskcellsidx[i].lineidx = filter->motionmaskcellsidx[i].lineidx; + motionmaskcellsidx[i].columnidx = filter->motionmaskcellsidx[i].columnidx; + } + motioncells_count = filter->motioncells_count; + motioncellsidx = g_new0 (motioncellidx, filter->motioncells_count); + for (i = 0; i < filter->motioncells_count; i++) { + motioncellsidx[i].lineidx = filter->motioncellsidx[i].lineidx; + motioncellsidx[i].columnidx = filter->motioncellsidx[i].columnidx; + } + useAlpha = filter->usealpha; + thickness = filter->thickness; + success = + perform_detection_motion_cells (filter->cvImage, sensitivity, framerate, + gridx, gridy, + (gint64) (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND) - + filter->diff_timestamp, display, useAlpha, motionmaskcoord_count, + motionmaskcoords, motionmaskcells_count, motionmaskcellsidx, + motioncellscolor, motioncells_count, motioncellsidx, starttime, + datafile, changed_datafile, thickness, filter->id); + if ((success == 1) && (filter->sent_init_error_msg == false)) { + char *initfailedreason; + int initerrorcode; + GstStructure *s; + GstMessage *m; + initfailedreason = getInitDataFileFailed (filter->id); + initerrorcode = getInitErrorCode (filter->id); + s = gst_structure_new ("motion", "init_error_code", G_TYPE_INT, + initerrorcode, "details", G_TYPE_STRING, initfailedreason, NULL); + m = gst_message_new_element (GST_OBJECT (filter), s); + gst_element_post_message (GST_ELEMENT (filter), m); + filter->sent_init_error_msg = TRUE; + } + if ((success == -1) && (filter->sent_save_error_msg == false)) { + char *savefailedreason; + int saveerrorcode; + GstStructure *s; + GstMessage *m; + savefailedreason = getSaveDataFileFailed (filter->id); + saveerrorcode = getSaveErrorCode (filter->id); + s = gst_structure_new ("motion", "save_error_code", G_TYPE_INT, + saveerrorcode, "details", G_TYPE_STRING, savefailedreason, NULL); + m = gst_message_new_element (GST_OBJECT (filter), s); + gst_element_post_message (GST_ELEMENT (filter), m); + filter->sent_save_error_msg = TRUE; + } + if (success == -2) { //frame dropped + filter->prev_buff_timestamp = filter->cur_buff_timestamp; + //free + GFREE (datafile); + GFREE (motionmaskcoords); + GFREE (motionmaskcellsidx); + GFREE (motioncellsidx); + g_mutex_unlock (filter->propset_mutex); + return gst_pad_push (filter->srcpad, buf); + } + filter->changed_datafile = getChangedDataFile (filter->id); + motioncellsidxcnt = getMotionCellsIdxCnt (filter->id); + numberOfCells = filter->gridx * filter->gridy; + motioncellsnumber = motioncellsidxcnt / MSGLEN; + cellsOfInterestNumber = (filter->motioncells_count > 0) ? //how many cells interest for us + (filter->motioncells_count) : (numberOfCells); + mincellsOfInterestNumber = + floor ((double) cellsOfInterestNumber * filter->threshold); + motiondetect = (motioncellsnumber >= mincellsOfInterestNumber) ? 1 : 0; + if ((motioncellsidxcnt > 0) && (motiondetect == 1)) { + char *detectedmotioncells; + filter->last_motion_timestamp = GST_BUFFER_TIMESTAMP (buf); + detectedmotioncells = getMotionCellsIdx (filter->id); + if (detectedmotioncells) { + filter->consecutive_motion++; + if ((filter->previous_motion == false) + && (filter->consecutive_motion >= filter->minimum_motion_frames)) { + GstStructure *s; + GstMessage *m; + filter->previous_motion = true; + filter->motion_begin_timestamp = GST_BUFFER_TIMESTAMP (buf); + s = gst_structure_new ("motion", "motion_cells_indices", + G_TYPE_STRING, detectedmotioncells, "motion_begin", G_TYPE_UINT64, + filter->motion_begin_timestamp, NULL); + m = gst_message_new_element (GST_OBJECT (filter), s); + gst_element_post_message (GST_ELEMENT (filter), m); + } else if (filter->postallmotion) { + GstStructure *s; + GstMessage *m; + filter->motion_timestamp = GST_BUFFER_TIMESTAMP (buf); + s = gst_structure_new ("motion", "motion_cells_indices", + G_TYPE_STRING, detectedmotioncells, "motion", G_TYPE_UINT64, + filter->motion_timestamp, NULL); + m = gst_message_new_element (GST_OBJECT (filter), s); + gst_element_post_message (GST_ELEMENT (filter), m); + } + } else { + GstStructure *s; + GstMessage *m; + s = gst_structure_new ("motion", "motion_cells_indices", G_TYPE_STRING, + "error", NULL); + m = gst_message_new_element (GST_OBJECT (filter), s); + gst_element_post_message (GST_ELEMENT (filter), m); + } + } else { + filter->consecutive_motion = 0; + if ((((GST_BUFFER_TIMESTAMP (buf) - + filter->last_motion_timestamp) / 1000000000l) >= + filter->gap) + && (filter->last_motion_timestamp > 0)) { + GST_DEBUG ("POST MOTION FINISHED MSG\n"); + if (filter->previous_motion) { + GstStructure *s; + GstMessage *m; + filter->previous_motion = false; + s = gst_structure_new ("motion", "motion_finished", G_TYPE_UINT64, + filter->last_motion_timestamp, NULL); + m = gst_message_new_element (GST_OBJECT (filter), s); + gst_element_post_message (GST_ELEMENT (filter), m); + } + } + } + if (filter->postnomotion > 0) { + guint64 last_buf_timestamp = GST_BUFFER_TIMESTAMP (buf) / 1000000000l; + if ((last_buf_timestamp - + (filter->last_motion_timestamp / 1000000000l)) >= + filter->postnomotion) { + GST_DEBUG ("POST NO MOTION MSG\n"); + if ((last_buf_timestamp - + (filter->last_nomotion_notified / 1000000000l)) >= + filter->postnomotion) { + GstStructure *s; + GstMessage *m; + filter->last_nomotion_notified = GST_BUFFER_TIMESTAMP (buf); + s = gst_structure_new ("motion", "no_motion", G_TYPE_UINT64, + filter->last_motion_timestamp, NULL); + m = gst_message_new_element (GST_OBJECT (filter), s); + gst_element_post_message (GST_ELEMENT (filter), m); + } + } + } + filter->prev_buff_timestamp = filter->cur_buff_timestamp; + //free + GFREE (datafile); + GFREE (motionmaskcoords); + GFREE (motionmaskcellsidx); + GFREE (motioncellsidx); + + g_mutex_unlock (filter->propset_mutex); + } + + return gst_pad_push (filter->srcpad, buf); +} + +/* entry point to initialize the plug-in + * initialize the plug-in itself + * register the element factories and other features + */ +gboolean +gst_motioncells_plugin_init (GstPlugin * plugin) +{ + /* debug category for fltering log messages */ + GST_DEBUG_CATEGORY_INIT (gst_motion_cells_debug, + "motioncells", + 0, + "Performs motion detection on videos, providing detected positions via bus messages"); + + return gst_element_register (plugin, "motioncells", GST_RANK_NONE, + GST_TYPE_MOTIONCELLS); +} diff --git a/ext/opencv/gstmotioncells.h b/ext/opencv/gstmotioncells.h new file mode 100644 index 000000000..d26a2d6dd --- /dev/null +++ b/ext/opencv/gstmotioncells.h @@ -0,0 +1,124 @@ +/* + * GStreamer + * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com> + * Copyright (C) 2011 Nicola Murino <nicola.murino@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_MOTIONCELLS_H__ +#define __GST_MOTIONCELLS_H__ + +#include <gst/gst.h> +#include <cv.h> + +G_BEGIN_DECLS +/* #defines don't like whitespacey bits */ +#define GST_TYPE_MOTIONCELLS \ + (gst_motion_cells_get_type()) +#define gst_motion_cells(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MOTIONCELLS,GstMotioncells)) +#define gst_motion_cells_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MOTIONCELLS,GstMotioncellsClass)) +#define GST_IS_MOTIONCELLS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MOTIONCELLS)) +#define GST_IS_MOTIONCELLS_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MOTIONCELLS)) +typedef struct _GstMotioncells GstMotioncells; +typedef struct _GstMotioncellsClass GstMotioncellsClass; + +typedef struct { + int upper_left_x; + int upper_left_y; + int lower_right_x; + int lower_right_y; +} motionmaskcoordrect; + +typedef struct { + int R_channel_value; + int G_channel_value; + int B_channel_value; +} cellscolor; + +typedef struct { + int lineidx; + int columnidx; +} motioncellidx; + +struct _GstMotioncells +{ + GstElement element; + GstPad *sinkpad, *srcpad; + GstState state; + gboolean display, calculate_motion, firstgridx, firstgridy, changed_gridx, + changed_gridy, changed_startime; + gboolean previous_motion, changed_datafile, postallmotion, usealpha, + firstdatafile, firstframe; + gboolean sent_init_error_msg, sent_save_error_msg; + gchar *prev_datafile, *cur_datafile, *basename_datafile, *datafile_extension; + gint prevgridx, gridx, prevgridy, gridy, id; + gdouble sensitivity, threshold; + IplImage *cvImage; + motionmaskcoordrect *motionmaskcoords; + cellscolor *motioncellscolor; + motioncellidx *motioncellsidx, *motionmaskcellsidx; + int motionmaskcoord_count, motioncells_count, motionmaskcells_count; + int gap, thickness, datafileidx, postnomotion, minimum_motion_frames; + guint64 motion_begin_timestamp, last_motion_timestamp, motion_timestamp, + last_nomotion_notified, prev_buff_timestamp, cur_buff_timestamp; + gint64 diff_timestamp, starttime; + guint64 consecutive_motion; + gint width, height; + //time stuff + struct timeval tv; + GMutex *propset_mutex; + double framerate; +}; + +struct _GstMotioncellsClass +{ + GstElementClass parent_class; +}; + +GType gst_motion_cells_get_type (void); + +gboolean gst_motioncells_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_MOTION_CELLS_H__ */ diff --git a/ext/opencv/gstopencv.c b/ext/opencv/gstopencv.c index e12ca7802..8d9def2b2 100644 --- a/ext/opencv/gstopencv.c +++ b/ext/opencv/gstopencv.c @@ -32,6 +32,7 @@ #include "gstedgedetect.h" #include "gstfaceblur.h" #include "gstfacedetect.h" +#include "gstmotioncells.h" #include "gstpyramidsegment.h" #include "gsttemplatematch.h" #include "gsttextoverlay.h" @@ -66,6 +67,9 @@ plugin_init (GstPlugin * plugin) if (!gst_facedetect_plugin_init (plugin)) return FALSE; + if (!gst_motioncells_plugin_init (plugin)) + return FALSE; + if (!gst_pyramidsegment_plugin_init (plugin)) return FALSE; diff --git a/ext/opencv/motioncells_wrapper.cpp b/ext/opencv/motioncells_wrapper.cpp new file mode 100644 index 000000000..b768f9ece --- /dev/null +++ b/ext/opencv/motioncells_wrapper.cpp @@ -0,0 +1,213 @@ +/* + * GStreamer + * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com> + * Copyright (C) 2011 Nicola Murino <nicola.murino@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <limits.h> +#include "motioncells_wrapper.h" + +extern int instanceCounter; +extern bool element_id_was_max; +MotionCells *mc; +char p_str[] = "idx failed"; + +void +motion_cells_init () +{ + mc = new MotionCells (); + instanceOfMC tmpmc; + tmpmc.id = instanceCounter; + tmpmc.mc = mc; + motioncellsvector.push_back (tmpmc); + if ((instanceCounter < INT_MAX) && !element_id_was_max) { + instanceCounter++; + element_id_was_max = false; + } else { + element_id_was_max = true; + instanceCounter = motioncellsfreeids.back (); + motioncellsfreeids.pop_back (); + } +} + +int +perform_detection_motion_cells (IplImage * p_image, double p_sensitivity, + double p_framerate, int p_gridx, int p_gridy, long int p_timestamp_millisec, + bool p_isVisible, bool p_useAlpha, int motionmaskcoord_count, + motionmaskcoordrect * motionmaskcoords, int motionmaskcells_count, + motioncellidx * motionmaskcellsidx, cellscolor motioncellscolor, + int motioncells_count, motioncellidx * motioncellsidx, gint64 starttime, + char *p_datafile, bool p_changed_datafile, int p_thickness, int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + return motioncellsvector.at (idx).mc->performDetectionMotionCells (p_image, + p_sensitivity, p_framerate, p_gridx, p_gridy, p_timestamp_millisec, + p_isVisible, p_useAlpha, motionmaskcoord_count, motionmaskcoords, + motionmaskcells_count, motionmaskcellsidx, motioncellscolor, + motioncells_count, motioncellsidx, starttime, p_datafile, + p_changed_datafile, p_thickness); +} + + +void +setPrevFrame (IplImage * p_prevFrame, int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + motioncellsvector.at (idx).mc->setPrevFrame (p_prevFrame); +} + +char * +getMotionCellsIdx (int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + if (idx > -1) + return motioncellsvector.at (idx).mc->getMotionCellsIdx (); + else { + return p_str; + } + +} + +int +getMotionCellsIdxCnt (int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + if (idx > -1) + return motioncellsvector.at (idx).mc->getMotionCellsIdxCount (); + else + return 0; +} + +bool +getChangedDataFile (int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + if (idx > -1) + return motioncellsvector.at (idx).mc->getChangedDataFile (); + else + return false; +} + +int +searchIdx (int p_id) +{ + for (unsigned int i = 0; i < motioncellsvector.size (); i++) { + instanceOfMC tmpmc; + tmpmc = motioncellsvector.at (i); + if (tmpmc.id == p_id) { + return i; + } + } + return -1; +} + +char * +getInitDataFileFailed (int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + if (idx > -1) + return motioncellsvector.at (idx).mc->getDatafileInitFailed (); + else { + return p_str; + } +} + +char * +getSaveDataFileFailed (int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + if (idx > -1) + return motioncellsvector.at (idx).mc->getDatafileSaveFailed (); + else { + return p_str; + } +} + +int +getInitErrorCode (int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + if (idx > -1) + return motioncellsvector.at (idx).mc->getInitErrorCode (); + else + return -1; +} + +int +getSaveErrorCode (int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + if (idx > -1) + return motioncellsvector.at (idx).mc->getSaveErrorCode (); + else + return -1; +} + +void +motion_cells_free (int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + if (idx > -1) { + delete motioncellsvector.at (idx).mc; + motioncellsvector.erase (motioncellsvector.begin () + idx); + motioncellsfreeids.push_back (p_id); + } +} + +void +motion_cells_free_resources (int p_id) +{ + int idx = 0; + idx = searchIdx (p_id); + if (idx > -1) + motioncellsvector.at (idx).mc->freeDataFile (); +} diff --git a/ext/opencv/motioncells_wrapper.h b/ext/opencv/motioncells_wrapper.h new file mode 100644 index 000000000..0feaafa8b --- /dev/null +++ b/ext/opencv/motioncells_wrapper.h @@ -0,0 +1,89 @@ +/* + * GStreamer + * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com> + * Copyright (C) 2011 Nicola Murino <nicola.murino@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, the contents of this file may be used under the + * GNU Lesser General Public License Version 2.1 (the "LGPL"), in + * which case the following provisions apply instead of the ones + * mentioned above: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef MOTIONCELLS_WRAPPER_H +#define MOTIONCELLS_WRAPPER_H + +#include <stdbool.h> + +#ifdef __cplusplus +#include "MotionCells.h" +struct instanceOfMC +{ + int id; + MotionCells *mc; +}; +vector < instanceOfMC > motioncellsvector; +vector < int >motioncellsfreeids; + +int searchIdx (int p_id); +extern "C" +{ +#endif + + void motion_cells_init (); + int perform_detection_motion_cells (IplImage * p_image, double p_sensitivity, + double p_framerate, int p_gridx, int p_gridy, + long int p_timestamp_millisec, bool p_isVisible, bool p_useAlpha, + int motionmaskcoord_count, motionmaskcoordrect * motionmaskcoords, + int motionmaskcells_count, motioncellidx * motionmaskcellsidx, + cellscolor motioncellscolor, int motioncells_count, + motioncellidx * motioncellsidx, gint64 starttime, char *datafile, + bool p_changed_datafile, int p_thickness, int p_id); + void setPrevFrame (IplImage * p_prevFrame, int p_id); + void motion_cells_free (int p_id); + void motion_cells_free_resources (int p_id); + char *getMotionCellsIdx (int p_id); + int getMotionCellsIdxCnt (int p_id); + bool getChangedDataFile (int p_id); + char *getInitDataFileFailed (int p_id); + char *getSaveDataFileFailed (int p_id); + int getInitErrorCode (int p_id); + int getSaveErrorCode (int p_id); + +#ifdef __cplusplus +} +#endif + +#endif /* MOTIONCELLS_WRAPPER_H */ |