diff --git a/images/pic5_contours.png b/images/pic5_contours.png new file mode 100644 index 0000000..ba18b18 Binary files /dev/null and b/images/pic5_contours.png differ diff --git a/images/shapes.png b/images/shapes.png new file mode 100644 index 0000000..357bf4e Binary files /dev/null and b/images/shapes.png differ diff --git a/opencv/cxcore.go b/opencv/cxcore.go index 7abf3ba..28b6de7 100644 --- a/opencv/cxcore.go +++ b/opencv/cxcore.go @@ -113,6 +113,11 @@ func (img *IplImage) GetROI() Rect { return Rect(r) } +/* Equalizes the histogram of a grayscale image */ +func (img *IplImage) EqualizeHist(dest *IplImage) { + C.cvEqualizeHist(unsafe.Pointer(img), unsafe.Pointer(dest)) +} + /* Reshape changes shape of the image without copying data. A value of `0` means that channels or rows remain unchanged. @@ -142,6 +147,11 @@ func (img *IplImage) Get3D(x, y, z int) Scalar { return Scalar(ret) } +/* Sets every element of an array to a given value. */ +func (img *IplImage) Set(value Scalar) { + C.cvSet(unsafe.Pointer(img), (C.CvScalar)(value), nil) +} + /* Set1D sets a particular element in the image */ func (img *IplImage) Set1D(x int, value Scalar) { C.cvSet1D(unsafe.Pointer(img), C.int(x), (C.CvScalar)(value)) @@ -540,6 +550,13 @@ func (src *IplImage) EqualizeHist(dst *IplImage) { /****************************************************************************************\ * Dynamic data structures * \****************************************************************************************/ +func (seq *Seq) Release() { + C.cvReleaseMemStorage(&seq.storage) +} + +func (seq *Seq) Total() int { + return (int)(seq.total) +} /****************************************************************************************\ * Drawing * diff --git a/opencv/cxtype.go b/opencv/cxtype.go index 9fe7603..984baa4 100644 --- a/opencv/cxtype.go +++ b/opencv/cxtype.go @@ -661,6 +661,18 @@ type Graph C.CvGraph type Chain C.CvChain type Contour C.CvContour +const ( + CV_RETR_EXTERNAL = C.CV_RETR_EXTERNAL + CV_RETR_LIST = C.CV_RETR_LIST + CV_RETR_CCOMP = C.CV_RETR_CCOMP + CV_RETR_TREE = C.CV_RETR_TREE + + CV_CHAIN_APPROX_NONE = C.CV_CHAIN_APPROX_NONE + CV_CHAIN_APPROX_SIMPLE = C.CV_CHAIN_APPROX_SIMPLE + CV_CHAIN_APPROX_TC89_L1 = C.CV_CHAIN_APPROX_TC89_L1 + CV_CHAIN_APPROX_TC89_KCOS = C.CV_CHAIN_APPROX_TC89_KCOS +) + /****************************************************************************************\ * Sequence types * \****************************************************************************************/ diff --git a/opencv/imgproc.go b/opencv/imgproc.go index 93b8070..8861bab 100644 --- a/opencv/imgproc.go +++ b/opencv/imgproc.go @@ -52,3 +52,34 @@ func Crop(src *IplImage, x, y, width, height int) *IplImage { return dest } + +/* Returns a Seq of countours in an image, detected according to the parameters. + Caller must Release() the Seq returned */ +func (image *IplImage) FindContours(mode, method int, offset Point) *Seq { + storage := C.cvCreateMemStorage(0) + header_size := (C.size_t)(unsafe.Sizeof(C.CvContour{})) + var seq *C.CvSeq + C.cvFindContours( + unsafe.Pointer(image), + storage, + &seq, + C.int(header_size), + C.int(mode), + C.int(method), + C.cvPoint(C.int(offset.X), C.int(offset.Y))) + + return (*Seq)(seq) +} + +//cvDrawContours(CvArr* img, CvSeq* contour, CvScalar externalColor, CvScalar holeColor, int maxLevel, int thickness=1, int lineType=8 +func DrawContours(image *IplImage, contours *Seq, externalColor, holeColor Scalar, maxLevel, thickness, lineType int, offset Point) { + C.cvDrawContours( + unsafe.Pointer(image), + (*C.CvSeq)(contours), + (C.CvScalar)(externalColor), + (C.CvScalar)(holeColor), + C.int(maxLevel), + C.int(thickness), + C.int(lineType), + C.cvPoint(C.int(offset.X), C.int(offset.Y))) +} diff --git a/opencv/imgproc_test.go b/opencv/imgproc_test.go index 85ed22e..9f4509b 100644 --- a/opencv/imgproc_test.go +++ b/opencv/imgproc_test.go @@ -3,7 +3,9 @@ package opencv import ( "path" "runtime" + "os" "testing" + "syscall" ) func TestResize(t *testing.T) { @@ -55,3 +57,57 @@ func TestCrop(t *testing.T) { t.Fatalf("excepted width is 200, returned %d\n", crop.Height()) } } + +func TestFindContours(t *testing.T) { + _, currentfile, _, _ := runtime.Caller(0) + filename := path.Join(path.Dir(currentfile), "../images/pic5.png") + + image := LoadImage(filename) + if image == nil { + t.Fatal("LoadImage fail") + } + defer image.Release() + + grayscale := CreateImage(image.Width(), image.Height(), IPL_DEPTH_8U, 1) + CvtColor(image, grayscale, CV_BGR2GRAY) + defer grayscale.Release() + + + edges := CreateImage(grayscale.Width(), grayscale.Height(), grayscale.Depth(), grayscale.Channels()) + defer edges.Release() + Canny(grayscale, edges, 50, 200, 3) + + seq := edges.FindContours(CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point{0, 0}) + defer seq.Release() + + contours := CreateImage(grayscale.Width(), grayscale.Height(), grayscale.Depth(), grayscale.Channels()) + white := NewScalar(255, 255, 255, 0) + contours.Set(white) + + black := NewScalar(0, 0, 0, 0) + red := NewScalar(0, 255, 0, 0) + + for ; seq != nil; seq = (*Seq)(seq.h_next) { + DrawContours(contours, seq, red, black, 0, 2, 8, Point{0, 0}) + } + + filename = path.Join(path.Dir(currentfile), "../images/pic5_contours.png") + // Uncomment this code to create the test image "../images/shapes_contours.png" + // It is part of the repo, and what this test compares against + // + //SaveImage(filename), contours, 0) + + tempfilename := path.Join(os.TempDir(), "pic5_contours.png") + defer syscall.Unlink(tempfilename) + SaveImage(tempfilename, contours, 0) + + // Compare actual image with expected image + same, err := BinaryCompare(filename, tempfilename) + if err != nil { + t.Fatal(err) + } + if !same { + t.Error("Expected contour file != actual contour file") + } + +}