Merge pull request #17 from sprungknoedl/golang-image
added path from go image to opencv image and back.
This commit is contained in:
commit
3f53df8324
5 changed files with 244 additions and 4 deletions
|
|
@ -20,6 +20,7 @@ func init() {
|
||||||
const (
|
const (
|
||||||
CV_BGR2GRAY = C.CV_BGR2GRAY
|
CV_BGR2GRAY = C.CV_BGR2GRAY
|
||||||
CV_BGR2BGRA = C.CV_BGR2BGRA
|
CV_BGR2BGRA = C.CV_BGR2BGRA
|
||||||
|
CV_RGBA2BGRA = C.CV_RGBA2BGRA
|
||||||
|
|
||||||
CV_BLUR = C.CV_BLUR
|
CV_BLUR = C.CV_BLUR
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,11 @@ func CreateImage(w, h, depth, channels int) *IplImage {
|
||||||
return (*IplImage)(img)
|
return (*IplImage)(img)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SetData assigns user data to the image header */
|
||||||
|
func (img *IplImage) SetData(data unsafe.Pointer, step int) {
|
||||||
|
C.cvSetData(unsafe.Pointer(img), data, C.int(step))
|
||||||
|
}
|
||||||
|
|
||||||
/* Releases (i.e. deallocates) IPL image header */
|
/* Releases (i.e. deallocates) IPL image header */
|
||||||
func (img *IplImage) ReleaseHeader() {
|
func (img *IplImage) ReleaseHeader() {
|
||||||
img_c := (*C.IplImage)(img)
|
img_c := (*C.IplImage)(img)
|
||||||
|
|
@ -69,6 +74,10 @@ func (img *IplImage) Release() {
|
||||||
C.cvReleaseImage(&img_c)
|
C.cvReleaseImage(&img_c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (img *IplImage) Zero() {
|
||||||
|
C.cvSetZero(unsafe.Pointer(img))
|
||||||
|
}
|
||||||
|
|
||||||
/* Creates a copy of IPL image (widthStep may differ) */
|
/* Creates a copy of IPL image (widthStep may differ) */
|
||||||
func (img *IplImage) Clone() *IplImage {
|
func (img *IplImage) Clone() *IplImage {
|
||||||
p := C.cvCloneImage((*C.IplImage)(img))
|
p := C.cvCloneImage((*C.IplImage)(img))
|
||||||
|
|
@ -114,6 +123,39 @@ func (img *IplImage) Reshape(channels, rows, _type int) *Mat {
|
||||||
return (*Mat)(n)
|
return (*Mat)(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get1D return a specific element from a 1-dimensional matrix. */
|
||||||
|
func (img *IplImage) Get1D(x int) Scalar {
|
||||||
|
ret := C.cvGet1D(unsafe.Pointer(img), C.int(x))
|
||||||
|
return Scalar(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get2D return a specific element from a 2-dimensional matrix. */
|
||||||
|
func (img *IplImage) Get2D(x, y int) Scalar {
|
||||||
|
ret := C.cvGet2D(unsafe.Pointer(img), C.int(y), C.int(x))
|
||||||
|
return Scalar(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get3D return a specific element from a 3-dimensional matrix. */
|
||||||
|
func (img *IplImage) Get3D(x, y, z int) Scalar {
|
||||||
|
ret := C.cvGet3D(unsafe.Pointer(img), C.int(z), C.int(y), C.int(x))
|
||||||
|
return Scalar(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set2D sets a particular element in the image */
|
||||||
|
func (img *IplImage) Set2D(x, y int, value Scalar) {
|
||||||
|
C.cvSet2D(unsafe.Pointer(img), C.int(y), C.int(x), (C.CvScalar)(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set3D sets a particular element in the image */
|
||||||
|
func (img *IplImage) Set3D(x, y, z int, value Scalar) {
|
||||||
|
C.cvSet3D(unsafe.Pointer(img), C.int(z), C.int(y), C.int(x), (C.CvScalar)(value))
|
||||||
|
}
|
||||||
|
|
||||||
/* GetMat returns the matrix header for an image.*/
|
/* GetMat returns the matrix header for an image.*/
|
||||||
func (img *IplImage) GetMat() *Mat {
|
func (img *IplImage) GetMat() *Mat {
|
||||||
var null C.int
|
var null C.int
|
||||||
|
|
@ -155,6 +197,11 @@ func (mat *Mat) InitHeader(rows, cols, type_ int, data unsafe.Pointer, step int)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SetData assigns user data to the matrix header. */
|
||||||
|
func (mat *Mat) SetData(data unsafe.Pointer, step int) {
|
||||||
|
C.cvSetData(unsafe.Pointer(mat), data, C.int(step))
|
||||||
|
}
|
||||||
|
|
||||||
/* Releases CvMat header and deallocates matrix data
|
/* Releases CvMat header and deallocates matrix data
|
||||||
(reference counting is used for data) */
|
(reference counting is used for data) */
|
||||||
func (mat *Mat) Release() {
|
func (mat *Mat) Release() {
|
||||||
|
|
@ -179,6 +226,10 @@ func (mat *Mat) Clone() *Mat {
|
||||||
return (*Mat)(mat_new)
|
return (*Mat)(mat_new)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mat *Mat) Zero() {
|
||||||
|
C.cvSetZero(unsafe.Pointer(mat))
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reshape changes shape of the matrix without copying data. A value of `0` means
|
Reshape changes shape of the matrix without copying data. A value of `0` means
|
||||||
that channels or rows remain unchanged.
|
that channels or rows remain unchanged.
|
||||||
|
|
@ -275,6 +326,21 @@ func (m *Mat) Get3D(x, y, z int) Scalar {
|
||||||
return Scalar(ret)
|
return Scalar(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set1D sets a particular element in them matrix */
|
||||||
|
func (m *Mat) Set1D(x int, value Scalar) {
|
||||||
|
C.cvSet1D(unsafe.Pointer(m), C.int(x), (C.CvScalar)(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set2D sets a particular element in them matrix */
|
||||||
|
func (m *Mat) Set2D(x, y int, value Scalar) {
|
||||||
|
C.cvSet2D(unsafe.Pointer(m), C.int(x), C.int(y), (C.CvScalar)(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set3D sets a particular element in them matrix */
|
||||||
|
func (m *Mat) Set3D(x, y, z int, value Scalar) {
|
||||||
|
C.cvSet3D(unsafe.Pointer(m), C.int(x), C.int(y), C.int(z), (C.CvScalar)(value))
|
||||||
|
}
|
||||||
|
|
||||||
/* GetImage returns the image header for the matrix. */
|
/* GetImage returns the image header for the matrix. */
|
||||||
func (m *Mat) GetImage(channels int) *IplImage {
|
func (m *Mat) GetImage(channels int) *IplImage {
|
||||||
tmp := CreateImage(m.Cols(), m.Rows(), m.Type(), channels)
|
tmp := CreateImage(m.Cols(), m.Rows(), m.Type(), channels)
|
||||||
|
|
|
||||||
|
|
@ -602,8 +602,8 @@ const (
|
||||||
|
|
||||||
type Scalar C.CvScalar
|
type Scalar C.CvScalar
|
||||||
|
|
||||||
func NewScalar(b, g, r int) Scalar {
|
func NewScalar(b, g, r, a float64) Scalar {
|
||||||
rv := C.cvScalar(C.double(b), C.double(g), C.double(r), C.double(0))
|
rv := C.cvScalar(C.double(b), C.double(g), C.double(r), C.double(a))
|
||||||
return (Scalar)(rv)
|
return (Scalar)(rv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
77
opencv/goimage.go
Normal file
77
opencv/goimage.go
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
package opencv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
/* DecodeImageMem decodes an image from an in memory byte buffer. */
|
||||||
|
func DecodeImageMem(data []byte) *IplImage {
|
||||||
|
buf := CreateMatHeader(1, len(data), CV_8U)
|
||||||
|
buf.SetData(unsafe.Pointer(&data[0]), CV_AUTOSTEP)
|
||||||
|
|
||||||
|
return DecodeImage(unsafe.Pointer(buf), CV_LOAD_IMAGE_UNCHANGED)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FromImage converts a go image.Image to an opencv.IplImage. */
|
||||||
|
func FromImage(img image.Image) *IplImage {
|
||||||
|
b := img.Bounds()
|
||||||
|
model := color.RGBAModel
|
||||||
|
dst := CreateImage(
|
||||||
|
b.Max.X-b.Min.X,
|
||||||
|
b.Max.Y-b.Min.Y,
|
||||||
|
IPL_DEPTH_8U, 4)
|
||||||
|
|
||||||
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
||||||
|
for x := b.Min.X; x < b.Max.X; x++ {
|
||||||
|
px := img.At(x, y)
|
||||||
|
c := model.Convert(px).(color.RGBA)
|
||||||
|
|
||||||
|
value := NewScalar(float64(c.B), float64(c.G), float64(c.R), float64(c.A))
|
||||||
|
dst.Set2D(x, y, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FromImageUnsafe create an opencv.IplImage that shares the buffer with the
|
||||||
|
go image.RGBA image. All changes made from opencv might affect go! */
|
||||||
|
func FromImageUnsafe(img *image.RGBA) *IplImage {
|
||||||
|
b := img.Bounds()
|
||||||
|
buf := CreateImageHeader(
|
||||||
|
b.Max.X-b.Min.X,
|
||||||
|
b.Max.Y-b.Min.Y,
|
||||||
|
IPL_DEPTH_8U, 4)
|
||||||
|
dst := CreateImage(
|
||||||
|
b.Max.X-b.Min.X,
|
||||||
|
b.Max.Y-b.Min.Y,
|
||||||
|
IPL_DEPTH_8U, 4)
|
||||||
|
|
||||||
|
buf.SetData(unsafe.Pointer(&img.Pix[0]), CV_AUTOSTEP)
|
||||||
|
CvtColor(buf, dst, CV_RGBA2BGRA)
|
||||||
|
buf.Release()
|
||||||
|
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ToImage converts a opencv.IplImage to an go image.Image */
|
||||||
|
func (img *IplImage) ToImage() image.Image {
|
||||||
|
out := image.NewNRGBA(image.Rect(0, 0, img.Width(), img.Height()))
|
||||||
|
if img.Depth() != IPL_DEPTH_8U {
|
||||||
|
return nil // TODO return error
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := 0; y < img.Height(); y++ {
|
||||||
|
for x := 0; x < img.Width(); x++ {
|
||||||
|
s := img.Get2D(x, y).Val()
|
||||||
|
b, g, r, a := s[2], s[1], s[0], s[3]
|
||||||
|
|
||||||
|
c := color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
|
||||||
|
out.Set(x, y, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
96
opencv/goimage_test.go
Normal file
96
opencv/goimage_test.go
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
package opencv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
_ "image/jpeg"
|
||||||
|
_ "image/png"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFromImage(t *testing.T) {
|
||||||
|
fh, err := os.Open("../images/pic5.png")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
img, _, err := image.Decode(fh)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ocv := FromImage(img)
|
||||||
|
if ocv == nil {
|
||||||
|
t.Fatal("failed to convert image")
|
||||||
|
}
|
||||||
|
|
||||||
|
width := img.Bounds().Max.X - img.Bounds().Min.X
|
||||||
|
height := img.Bounds().Max.Y - img.Bounds().Min.Y
|
||||||
|
if ocv.Width() != width || ocv.Height() != height {
|
||||||
|
t.Fatalf("loaded image has wrong dimensions: got %dx%d, expected %dx%d", ocv.Width(), ocv.Height(), width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := [4]float64{98, 139, 26, 255}
|
||||||
|
px := ocv.Get2D(50, 90).Val()
|
||||||
|
if !reflect.DeepEqual(px, ex) {
|
||||||
|
t.Fatalf("wrong color @50,90: got %v, expected %v", px, ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromImageUnsafe(t *testing.T) {
|
||||||
|
fh, err := os.Open("../images/pic5.png")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
img, _, err := image.Decode(fh)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rgba, ok := img.(*image.RGBA)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("image is no RGBA image")
|
||||||
|
}
|
||||||
|
|
||||||
|
ocv := FromImageUnsafe(rgba)
|
||||||
|
if ocv == nil {
|
||||||
|
t.Fatal("failed to convert image")
|
||||||
|
}
|
||||||
|
|
||||||
|
width := img.Bounds().Max.X - img.Bounds().Min.X
|
||||||
|
height := img.Bounds().Max.Y - img.Bounds().Min.Y
|
||||||
|
if ocv.Width() != width || ocv.Height() != height {
|
||||||
|
t.Fatalf("loaded image has wrong dimensions: got %dx%d, expected %dx%d", ocv.Width(), ocv.Height(), width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
ex := [4]float64{98, 139, 26, 255}
|
||||||
|
px := ocv.Get2D(50, 90).Val()
|
||||||
|
if !reflect.DeepEqual(px, ex) {
|
||||||
|
t.Fatalf("wrong color @50,90: got %v, expected %v", px, ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFromImage(b *testing.B) {
|
||||||
|
fh, _ := os.Open("../images/pic5.png")
|
||||||
|
img, _, _ := image.Decode(fh)
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ocv := FromImage(img)
|
||||||
|
ocv.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFromImageUnsafe(b *testing.B) {
|
||||||
|
fh, _ := os.Open("../images/pic5.png")
|
||||||
|
img, _, _ := image.Decode(fh)
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
rgba := img.(*image.RGBA)
|
||||||
|
ocv := FromImageUnsafe(rgba)
|
||||||
|
ocv.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue