Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

optic flow and video writer

Hi All, I'm using Optic flow (motempl) for motion detection. I'm trying to write a video if motion is detected, but i think i'm doing it wrong. I sort of integrated it (not perfectly i know) in that it writes a frame each time it sees motion. I have to do it this way because if i put it in a while() loop then obviously opencv stalls.

double FPS = 10.0;
int width,height;
cv::VideoWriter out;

static void help(void)
{    
printf(
    "\nThis program demonstrated the use of motion templates -- basically using the gradients\n"
    "of thresholded layers of decaying frame differencing. New movements are stamped on top with floating system\n"
    "time code and motions too old are thresholded away. This is the 'motion history file'. The program reads from the camera of your choice or from\n"
    "a file. Gradients of motion history are used to detect direction of motion etc\n"
    "Usage :\n"
    "./motempl [camera number 0-n or file name, default is camera 0]\n"
    );
}
// various tracking parameters (in seconds)
const double MHI_DURATION = 5;
const double MAX_TIME_DELTA = 0.5;
const double MIN_TIME_DELTA = 0.05;
// number of cyclic frame buffer used for motion detection
// (should, probably, depend on FPS)

// ring image buffer
vector<Mat> buf;
int last = 0;

// temporary images
Mat mhi, orient, mask, segmask, zplane;
vector<Rect> regions;

// parameters:
//  img - input video frame  
//  dst - resultant motion picture
//  args - optional parameters

static void update_mhi(const Mat& img, Mat& dst, int diff_threshold) { double timestamp = (double)clock() / CLOCKS_PER_SEC; // get current time in seconds Size size = img.size(); int i, idx1 = last; Rect comp_rect; double count; double angle; Point center; double magnitude; Scalar color;

// allocate images at the beginning or
// reallocate them if the frame size is changed
if (mhi.size() != size)
{
    mhi = Mat::zeros(size, CV_32F);
    zplane = Mat::zeros(size, CV_8U);

    buf[0] = Mat::zeros(size, CV_8U);
    buf[1] = Mat::zeros(size, CV_8U);
}

cvtColor(img, buf[last], COLOR_BGR2GRAY); // convert frame to grayscale

int idx2 = (last + 1) % 2; // index of (last - (N-1))th frame
last = idx2;

Mat silh = buf[idx2];
absdiff(buf[idx1], buf[idx2], silh); // get difference between frames

threshold(silh, silh, diff_threshold, 1, THRESH_BINARY); // and threshold it
updateMotionHistory(silh, mhi, timestamp, MHI_DURATION); // update MHI

// convert MHI to blue 8u image
mhi.convertTo(mask, CV_8U, 255. / MHI_DURATION, (MHI_DURATION - timestamp)*255. / MHI_DURATION);

Mat planes[] = { mask, zplane, zplane };
merge(planes, 3, dst);

// calculate motion gradient orientation and valid orientation mask
calcMotionGradient(mhi, mask, orient, MAX_TIME_DELTA, MIN_TIME_DELTA, 3);

// segment motion: get sequence of motion components
// segmask is marked motion components map. It is not used further
regions.clear();
segmentMotion(mhi, segmask, regions, timestamp, MAX_TIME_DELTA);

// iterate through the motion components,
// One more iteration (i == -1) corresponds to the whole image (global motion)
for (i = -1; i < (int)regions.size(); i++) {

    if (i < 0) { // case of the whole image
        comp_rect = Rect(0, 0, size.width, size.height);
        color = Scalar(255, 255, 255);
        magnitude = 100;
    }
    else { // i-th motion component
        comp_rect = regions[i];
        if (comp_rect.width + comp_rect.height < 10) // reject very small components i.e. sensitivity parameter
            continue;
        color = Scalar(0, 0, 255);
        magnitude = 30;
    }

    // select component ROI
    Mat silh_roi = silh(comp_rect);
    Mat mhi_roi = mhi(comp_rect);
    Mat orient_roi = orient(comp_rect);
    Mat mask_roi = mask(comp_rect);

    // calculate orientation
    angle = calcGlobalOrientation(orient_roi, mask_roi, mhi_roi, timestamp, MHI_DURATION);
    angle = 360.0 - angle;  // adjust for images with top-left origin

    count = norm(silh_roi, NORM_L1);; // calculate number of points within silhouette ROI

    // check for the case of little motion
    if (count < comp_rect.width*comp_rect.height * 0.05) 
        continue; 
    else 
    out << img; // Write a frame to the videofile

    // draw a clock with arrow indicating the direction
    center = Point((comp_rect.x + comp_rect.width / 2),
        (comp_rect.y + comp_rect.height / 2));

    circle(img, center, cvRound(magnitude*1.2), color, 3, 16, 0);
    line(img, center, Point(cvRound(center.x + magnitude*cos(angle*CV_PI / 180)),
        cvRound(center.y - magnitude*sin(angle*CV_PI / 180))), color, 3, 16, 0);
}

}

int main(int argc, char** argv) { VideoCapture cap;

help();

if (argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))
    cap.open(argc == 2 ? argv[1][0] - '0' : 0);
else if (argc == 2)
    cap.open(argv[1]);

if (!cap.isOpened())
{
    printf("Could not initialize video capture\n");
    return 0;
}

// Get width and height of frames 
width = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_WIDTH));
height = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_HEIGHT));

// Open a video file for writing (the MP4V codec works on OS X and Windows)
cv::VideoWriter out("output.mov", CV_FOURCC('m','p', '4', 'v'), FPS, cv::Size(width, height));
if(!out.isOpened()) {
    std::cout <<"Error! Unable to open video file for output." << std::endl;
    std::exit(-1);
 }


buf.resize(2);
Mat image, motion;
for (;;)
{
    cap >> image;
    if (image.empty())
        break;

    update_mhi(image, motion, 30);
    imshow("Image", image);
    imshow("Motion", motion);

    if (waitKey(10) >= 0)
        break;
}

return 0;

}