func calibrate(videoFile string, config Configuration) { // It takes a little while for the white balance to stabalise on the logitech. // So grab a frame, wait to stabalise for white balance to stabalise, then grab again // for and save as the calibration frame. camera, err := getVideoSource(videoFile) if err != nil { // No valid video source, abort. log.Printf("ERROR: Unable to get video source") log.Print(err) return } calibrationFrame := C.cvQueryFrame(camera) time.Sleep(1250 * time.Millisecond) C.cvReleaseCapture(&camera) camera, err = getVideoSource(videoFile) if err != nil { // No valid video source, abort log.Printf("ERROR: Unable to get video source") log.Print(err) return } defer C.cvReleaseCapture(&camera) // Build the calibration image from the first frame that comes off the camera. calibrationFrame = C.cvQueryFrame(camera) fileName := "calibrationFrame.jpg" file := C.CString(fileName) C.cvSaveImage(file, unsafe.Pointer(calibrationFrame), nil) C.free(unsafe.Pointer(file)) // Broadcast calibration results to the mothership. f, err := os.Open(fileName) if err != nil { log.Printf("ERROR: Unable to open calibration frame to broadcast") log.Print(err) return } defer f.Close() post(fileName, config.MothershipAddress+"/scout_api/calibrated", config.UUID, f) }
/* stop capturing/reading and free resources */ func (capture *Capture) Release() { cap_c := (*C.CvCapture)(capture) C.cvReleaseCapture(&cap_c) }
func measure(deltaC chan Command, videoFile string, debug bool, config Configuration) { camera, err := getVideoSource(videoFile) if err != nil { // No valid video source. Abort measuring. log.Printf("ERROR: Unable to get video source") log.Print(err) return } defer C.cvReleaseCapture(&camera) // Make sure we release the camera when the operating system crushes us. c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) go func() { <-c log.Printf("INFO: The OS shut down the scout.") C.cvReleaseCapture(&camera) return }() scene := initScene() // Build the calibration frame from disk. var calibrationFrame *C.IplImage if _, err := os.Stat("calibrationFrame.jpg"); err == nil { file := C.CString("calibrationFrame.jpg") calibrationFrame = C.cvLoadImage(file, C.CV_LOAD_IMAGE_COLOR) defer C.cvReleaseImage(&calibrationFrame) C.free(unsafe.Pointer(file)) } else { log.Printf("ERROR: Unable to measure, missing calibration frame") log.Print(err) return } // Create a frame to hold the foreground mask results. mask := C.cvCreateImage(C.cvSize(calibrationFrame.width, calibrationFrame.height), C.IPL_DEPTH_8U, 1) defer C.cvReleaseImage(&mask) // Push the initial calibration frame into the MOG2 image subtractor. C.initMOG2(C.int(config.MogHistoryLength), C.double(config.MogThreshold), C.int(config.MogDetectShadows)) C.applyMOG2(unsafe.Pointer(calibrationFrame), unsafe.Pointer(mask)) // Current frame counter. frame := int64(0) measuring := true // Start monitoring from the camera. for measuring && C.cvGrabFrame(camera) != 0 { // See if there are any new commands on the deltaC channel. select { case c := <-deltaC: switch { case c == STOP_MEASURE: log.Printf("INFO: Stopping measure") measuring = false } default: // Procceed with measuring. } // Subtract the calibration frame from the current frame. nextFrame := C.cvRetrieveFrame(camera, 0) C.applyMOG2(unsafe.Pointer(nextFrame), unsafe.Pointer(mask)) // Filter the foreground mask to clean up any noise or holes (morphological-closing). C.cvSmooth(unsafe.Pointer(mask), unsafe.Pointer(mask), C.CV_GAUSSIAN, C.int(config.GaussianSmooth), 0, 0.0, 0.0) C.cvThreshold(unsafe.Pointer(mask), unsafe.Pointer(mask), C.double(config.ForegroundThresh), 255, 0) C.cvDilate(unsafe.Pointer(mask), unsafe.Pointer(mask), nil, C.int(config.DilationIterations)) // Detect contours in filtered foreground mask storage := C.cvCreateMemStorage(0) contours := C.cvCreateSeq(0, C.size_t(unsafe.Sizeof(C.CvSeq{})), C.size_t(unsafe.Sizeof(C.CvPoint{})), storage) offset := C.cvPoint(C.int(0), C.int(0)) num := int(C.cvFindContours(unsafe.Pointer(mask), storage, &contours, C.int(unsafe.Sizeof(C.CvContour{})), C.CV_RETR_LIST, C.CV_CHAIN_APPROX_SIMPLE, offset)) var detectedObjects []Waypoint // Track each of the detected contours. for contours != nil { area := float64(C.cvContourArea(unsafe.Pointer(contours), C.cvSlice(0, 0x3fffffff), 0)) // Only track large objects. if area > config.MinArea { boundingBox := C.cvBoundingRect(unsafe.Pointer(contours), 0) w := int(boundingBox.width / 2) h := int(boundingBox.height / 2) x := int(boundingBox.x) + w y := int(boundingBox.y) + h detectedObjects = append(detectedObjects, Waypoint{x, y, w, h, 0.0}) if debug { // DEBUG -- Render contours and bounding boxes around detected objects. pt1 := C.cvPoint(boundingBox.x, boundingBox.y) pt2 := C.cvPoint(boundingBox.x+boundingBox.width, boundingBox.y+boundingBox.height) C.cvDrawContours(unsafe.Pointer(nextFrame), contours, C.cvScalar(12.0, 212.0, 250.0, 255), C.cvScalar(0, 0, 0, 0), 2, 1, 8, offset) C.cvRectangle(unsafe.Pointer(nextFrame), pt1, pt2, C.cvScalar(16.0, 8.0, 186.0, 255), C.int(5), C.int(8), C.int(0)) } } else { num-- } contours = contours.h_next } scene.update(detectedObjects, debug, config) C.cvClearMemStorage(storage) C.cvReleaseMemStorage(&storage) if debug { var font C.CvFont C.cvInitFont(&font, C.CV_FONT_HERSHEY_SIMPLEX, C.double(0.5), C.double(0.5), C.double(1.0), C.int(2), C.CV_AA) txt := C.CString("Hello friend.") C.cvPutText(unsafe.Pointer(nextFrame), txt, C.cvPoint(2, 2), &font, C.cvScalar(255.0, 255.0, 255.0, 255)) C.free(unsafe.Pointer(txt)) // DEBUG -- render current interaction path for detected objects. for _, i := range scene.Interactions { for _, w := range i.Path { pt1 := C.cvPoint(C.int(w.XPixels), C.int(w.YPixels)) C.cvCircle(unsafe.Pointer(nextFrame), pt1, C.int(10), C.cvScalar(109.0, 46.0, 0.0, 255), C.int(2), C.int(8), C.int(0)) } w := i.lastWaypoint() txt := C.CString(fmt.Sprintf("%01d", i.SceneID)) C.cvPutText(unsafe.Pointer(nextFrame), txt, C.cvPoint(C.int(w.XPixels+10), C.int(w.YPixels+10)), &font, C.cvScalar(255.0, 255.0, 255.0, 255)) C.free(unsafe.Pointer(txt)) } for _, i := range scene.idleInteractions { w := i.lastWaypoint() pt1 := C.cvPoint(C.int(w.XPixels-w.HalfWidthPixels+5), C.int(w.YPixels-w.HalfHeightPixels+5)) pt2 := C.cvPoint(C.int(w.XPixels+w.HalfWidthPixels-5), C.int(w.YPixels+w.HalfHeightPixels-5)) C.cvRectangle(unsafe.Pointer(nextFrame), pt1, pt2, C.cvScalar(16.0, 186.0, 8.0, 255), C.int(5), C.int(8), C.int(0)) txt := C.CString("i:" + fmt.Sprintf("%01d", i.SceneID)) C.cvPutText(unsafe.Pointer(nextFrame), txt, C.cvPoint(C.int(w.XPixels+10), C.int(w.YPixels+10)), &font, C.cvScalar(255.0, 255.0, 255.0, 255)) C.free(unsafe.Pointer(txt)) } file := C.CString("f" + fmt.Sprintf("%03d", frame) + "-detected.png") C.cvSaveImage(file, unsafe.Pointer(nextFrame), nil) C.free(unsafe.Pointer(file)) frame++ } } log.Printf("INFO: Finished measure") scene.close(debug, config) }
func (cap *Capture) Close() { C.cvReleaseCapture(&cap.handler) }