Ross Light avatar Ross Light committed 0ce5de7

Ensure calls to OpenCV are on main thread (fixes issues on Mac)

Comments (0)

Files changed (9)

 // Copy copies elements from src to dst. If mask is not nil, then only elements
 // that have a non-zero mask element will be copied.
 func Copy(src, dst, mask Arr) {
-	if mask == nil {
-		C.cvCopy(src.arr(), dst.arr(), nil)
-	} else {
-		C.cvCopy(src.arr(), dst.arr(), mask.arr())
-	}
+	do(func() {
+		if mask == nil {
+			C.cvCopy(src.arr(), dst.arr(), nil)
+		} else {
+			C.cvCopy(src.arr(), dst.arr(), mask.arr())
+		}
+	})
 }
 
 // ConvertScale converts from src to dst.  Each element is multiplied by scale
 // then increased by shift.
 func ConvertScale(src, dst Arr, scale, shift float64) {
-	C.cvConvertScale(src.arr(), dst.arr(), C.double(scale), C.double(shift))
+	do(func() {
+		C.cvConvertScale(src.arr(), dst.arr(), C.double(scale), C.double(shift))
+	})
 }
 // added to every point in the contour.
 func FindContours(image Arr, storage MemStorage, mode, method int, offset Point) (Seq, error) {
 	var seq Seq
-	result := C.cvFindContours(image.arr(), storage.s, &seq.seq, C.sizeof_CvContour, C.int(mode), C.int(method), C.CvPoint{C.int(offset.X), C.int(offset.Y)})
+	var result C.int
+	do(func() {
+		result = C.cvFindContours(image.arr(), storage.s, &seq.seq, C.sizeof_CvContour, C.int(mode), C.int(method), C.CvPoint{C.int(offset.X), C.int(offset.Y)})
+	})
 	if result < 0 {
 		// TODO: Get error string
 		return Seq{}, errors.New("FindContours failed")
 // supported. parameter is the desired approximation accuracy. parameter2
 // should be zero to indicate only the given contour.
 func ApproxPoly(srcSeq Seq, storage MemStorage, method int, parameter float64, parameter2 int) Seq {
-	return Seq{C.cvApproxPoly(unsafe.Pointer(srcSeq.seq), C.sizeof_CvContour, storage.s, C.int(method), C.double(parameter), C.int(parameter2))}
+	var seq Seq
+	do(func() {
+		seq = Seq{C.cvApproxPoly(unsafe.Pointer(srcSeq.seq), C.sizeof_CvContour, storage.s, C.int(method), C.double(parameter), C.int(parameter2))}
+	})
+	return seq
 }
 
 // ContourArea returns the area inside contour. If oriented is true, then a
 	} else {
 		corient = 0
 	}
-	return float64(C.cvContourArea(contour.arr(), C.CvSlice{C.int(slice.Start), C.int(slice.End)}, corient))
+	var area float64
+	do(func() {
+		area = float64(C.cvContourArea(contour.arr(), C.CvSlice{C.int(slice.Start), C.int(slice.End)}, corient))
+	})
+	return area
 }
 
 // ArcLength returns the length of the contour. If isClosed is negative, then
 // the contour is checked to see whether the contour should be considered
 // closed.  If isClosed is zero or one, then it overrides the contour's flags.
 func ArcLength(contour Arr, slice Slice, isClosed int) float64 {
-	return float64(C.cvArcLength(contour.arr(), C.CvSlice{C.int(slice.Start), C.int(slice.End)}, C.int(isClosed)))
+	var length float64
+	do(func() {
+		length = float64(C.cvArcLength(contour.arr(), C.CvSlice{C.int(slice.Start), C.int(slice.End)}, C.int(isClosed)))
+	})
+	return length
 }
 
 // ContourPerimeter returns the length of a closed contour.
 
 // CheckContourConvexity returns true if the contour is convex.
 func CheckContourConvexity(contour Arr) bool {
-	return C.cvCheckContourConvexity(contour.arr()) != 0
+	var result C.int
+	do(func() {
+		result = C.cvCheckContourConvexity(contour.arr())
+	})
+	return result != 0
 }
 
 // Threshold applies a fixed-level threshold to a grayscale image.
 func Threshold(src, dst Arr, thresh, maxVal float64, thresholdType int) float64 {
-	return float64(C.cvThreshold(src.arr(), dst.arr(), C.double(thresh), C.double(maxVal), C.int(thresholdType)))
+	var result float64
+	do(func() {
+		result = float64(C.cvThreshold(src.arr(), dst.arr(), C.double(thresh), C.double(maxVal), C.int(thresholdType)))
+	})
+	return result
 }
 
 // Filtering algorithms
 
 // PyrDown smooths and down-samples the input image.
 func PyrDown(src, dst Arr, filter int) {
-	C.cvPyrDown(src.arr(), dst.arr(), C.int(filter))
+	do(func() {
+		C.cvPyrDown(src.arr(), dst.arr(), C.int(filter))
+	})
 }
 
 // PyrUp up-samples the input image and smooths the result.
 func PyrUp(src, dst Arr, filter int) {
-	C.cvPyrDown(src.arr(), dst.arr(), C.int(filter))
+	do(func() {
+		C.cvPyrDown(src.arr(), dst.arr(), C.int(filter))
+	})
 }
 
 // IplConvKernel is a convolution kernel.
 // Dilate applies a maximum filter to the input image one or more times.  If
 // element is nil, a 3x3 rectangular element is used.
 func Dilate(src, dst Arr, element *IplConvKernel, iterations int) {
-	C.cvDilate(src.arr(), dst.arr(), (*C.IplConvKernel)(unsafe.Pointer(element)), C.int(iterations))
+	do(func() {
+		C.cvDilate(src.arr(), dst.arr(), (*C.IplConvKernel)(unsafe.Pointer(element)), C.int(iterations))
+	})
 }
 
 // Erode applies a minimum filter to the input image one or more times.  If
 // element is nil, a 3x3 rectangular element is used.
 func Erode(src, dst Arr, element *IplConvKernel, iterations int) {
-	C.cvErode(src.arr(), dst.arr(), (*C.IplConvKernel)(unsafe.Pointer(element)), C.int(iterations))
+	do(func() {
+		C.cvErode(src.arr(), dst.arr(), (*C.IplConvKernel)(unsafe.Pointer(element)), C.int(iterations))
+	})
 }
 		npts[i] = C.int(len(points[i]))
 	}
 
-	C.cvPolyLine(img.arr(), &pts[0], &npts[0], C.int(len(points)), cc, color.cvScalar(), C.int(thickness), C.int(lineType), C.int(shift))
+	do(func() {
+		C.cvPolyLine(img.arr(), &pts[0], &npts[0], C.int(len(points)), cc, color.cvScalar(), C.int(thickness), C.int(lineType), C.int(shift))
+	})
 }
 
 // Release closes the capture source.
 func (c Capture) Release() {
-	C.cvReleaseCapture(&c.capture)
+	do(func() {
+		C.cvReleaseCapture(&c.capture)
+	})
 }
 
 // QueryFrame returns a new frame from the capture source.
 func (c Capture) QueryFrame() (*IplImage, error) {
-	image := C.cvQueryFrame(c.capture)
+	var image *C.IplImage
+	do(func() {
+		image = C.cvQueryFrame(c.capture)
+	})
 	if image == nil {
 		return nil, errors.New("query failed")
 	}
 
 // CaptureFromCAM creates a new capture source for the given device.
 func CaptureFromCAM(device int) (Capture, error) {
-	c := C.cvCaptureFromCAM(C.int(device))
+	var c *C.CvCapture
+	do(func() {
+		c = C.cvCaptureFromCAM(C.int(device))
+	})
 	if c == nil {
 		return Capture{}, errors.New("Capture failed")
 	}
 	s := C.CString(filename)
 	defer C.free(unsafe.Pointer(s))
 
-	c := C.cvCaptureFromFile(s)
+	var c *C.CvCapture
+	do(func() {
+		c = C.cvCaptureFromFile(s)
+	})
 	if c == nil {
 		return Capture{}, errors.New("Capture failed")
 	}
 	cname := C.CString(name)
 	defer C.free(unsafe.Pointer(cname))
 
-	image := C.cvLoadImage(cname, C.int(color))
+	var image *C.IplImage
+	do(func() {
+		image = C.cvLoadImage(cname, C.int(color))
+	})
 	if image == nil {
 		return nil, errors.New("LoadImage failed")
 	}
 func ShowImage(name string, img Arr) {
 	cname := C.CString(name)
 	defer C.free(unsafe.Pointer(cname))
-	C.cvShowImage(cname, img.arr())
+	do(func() {
+		C.cvShowImage(cname, img.arr())
+	})
 }
 
 // Window flags
 	cname := C.CString(name)
 	defer C.free(unsafe.Pointer(cname))
 	// TODO: Check result
-	C.cvNamedWindow(cname, C.int(flags))
+	do(func() {
+		C.cvNamedWindow(cname, C.int(flags))
+	})
 }
 
 // WaitKey obtains key input from the user. If delay is non-zero, then the
 // function will return if a key has not been hit before delay has elapsed.
 func WaitKey(delay time.Duration) rune {
-	return rune(C.cvWaitKey(C.int(delay.Nanoseconds() / 1e6)))
+	var key rune
+	do(func() {
+		key = rune(C.cvWaitKey(C.int(delay.Nanoseconds() / 1e6)))
+	})
+	return key
 }
 
 // DestroyWindow will close the window called name.
 func DestroyWindow(name string) {
 	cname := C.CString(name)
 	defer C.free(unsafe.Pointer(cname))
-	C.cvDestroyWindow(cname)
+	do(func() {
+		C.cvDestroyWindow(cname)
+	})
 }
 // NewImage creates a new image.
 func NewImage(size Size, depth, channels int) *IplImage {
 	// XXX: This should be garbage-collected by Go.
-	i := C.cvCreateImage(C.CvSize{C.int(size.Width), C.int(size.Height)}, C.int(depth), C.int(channels))
+	var i *C.IplImage
+	do(func() {
+		i = C.cvCreateImage(C.CvSize{C.int(size.Width), C.int(size.Height)}, C.int(depth), C.int(channels))
+	})
 	return (*IplImage)(unsafe.Pointer(i))
 }
 
 
 // Size returns the width and height of the image.
 func (i *IplImage) Size() Size {
-	s := C.cvGetSize(i.arr())
+	var s C.CvSize
+	do(func() {
+		s = C.cvGetSize(i.arr())
+	})
 	return Size{int(s.width), int(s.height)}
 }
 
 // Clone returns an image that has a copy of the i's data.
 func (i *IplImage) Clone() *IplImage {
-	return (*IplImage)(unsafe.Pointer(C.cvCloneImage((*C.IplImage)(unsafe.Pointer(i)))))
+	var ii *C.IplImage
+	do(func() {
+		ii = C.cvCloneImage((*C.IplImage)(unsafe.Pointer(i)))
+	})
+	return (*IplImage)(unsafe.Pointer(ii))
 }
 
 // SetCOI sets the image's channel of interest.
 func (i *IplImage) SetCOI(channel int) {
-	C.cvSetImageCOI((*C.IplImage)(unsafe.Pointer(i)), C.int(channel))
+	do(func() {
+		C.cvSetImageCOI((*C.IplImage)(unsafe.Pointer(i)), C.int(channel))
+	})
 }
 
 // SetROI sets the image's region of interest.
 func (i *IplImage) SetROI(r Rect) {
-	C.cvSetImageROI((*C.IplImage)(unsafe.Pointer(i)),
-		C.CvRect{C.int(r.X), C.int(r.Y), C.int(r.Width), C.int(r.Height)})
+	do(func() {
+		C.cvSetImageROI((*C.IplImage)(unsafe.Pointer(i)),
+			C.CvRect{C.int(r.X), C.int(r.Y), C.int(r.Width), C.int(r.Height)})
+	})
 }
 
 // Release destroys the memory associated with the image.
 func (i *IplImage) Release() {
-	C.cvReleaseImage((**C.IplImage)(unsafe.Pointer(&i)))
+	do(func() {
+		C.cvReleaseImage((**C.IplImage)(unsafe.Pointer(&i)))
+	})
 }
+package cv
+
+import (
+	"runtime"
+)
+
+// OpenCV has some issues with multiple threads.  To overcome this, we use a sneaky approach documented here:
+// http://code.google.com/p/go-wiki/wiki/LockOSThread
+
+func init() {
+	runtime.LockOSThread()
+}
+
+// Main must be called from the program's main function.
+func Main() {
+	for f := range mainfunc {
+		f()
+	}
+}
+
+var mainfunc = make(chan func())
+
+func do(f func()) {
+	done := make(chan struct{})
+	mainfunc <- func() {
+		f()
+		close(done)
+	}
+	<-done
+}
 	if i < 0 || i >= int(s.seq.total) {
 		panic("Seq index out of bounds")
 	}
-	return unsafe.Pointer(C.cvGetSeqElem(s.seq, C.int(i)))
+	var ptr unsafe.Pointer
+	do(func() {
+		ptr = unsafe.Pointer(C.cvGetSeqElem(s.seq, C.int(i)))
+	})
+	return ptr
 }
 
 // PointAt returns the element at i converted to a point.
 }
 
 func (s Seq) Size() Size {
-	sz := C.cvGetSize(s.arr())
+	var sz C.CvSize
+	do(func() {
+		sz = C.cvGetSize(s.arr())
+	})
 	return Size{int(sz.width), int(sz.height)}
 }
 
 // NewMemStorage creates new memory storage. A blockSize of zero uses the
 // default block size.
 func NewMemStorage(blockSize int) MemStorage {
-	return MemStorage{C.cvCreateMemStorage(C.int(blockSize))}
+	var ms MemStorage
+	do(func() {
+		ms = MemStorage{C.cvCreateMemStorage(C.int(blockSize))}
+	})
+	return ms
 }
 
 // Release deallocates all of the memory in the pool.
 func (s MemStorage) Release() {
-	C.cvReleaseMemStorage(&s.s)
+	do(func() {
+		C.cvReleaseMemStorage(&s.s)
+	})
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.