diff --git a/h5g_group.go b/h5g_group.go index d3503ea..10ea541 100644 --- a/h5g_group.go +++ b/h5g_group.go @@ -11,7 +11,9 @@ package hdf5 import "C" import ( + "errors" "fmt" + "image" "unsafe" ) @@ -169,3 +171,19 @@ func (g *CommonFG) LinkExists(name string) bool { defer C.free(unsafe.Pointer(c_name)) return C.H5Lexists(g.id, c_name, 0) > 0 } + +// CreateImage create a image(RGB only) set with given name in the receiver. +func (g *CommonFG) CreateImage(name string, img image.Image) error { + if g.LinkExists(name) { + return errors.New("hdf5: name already exist") + } + return newImage(g.id, name, img) +} + +// ReadImage read a image dataset, returning it as an image.Image. +func (g *CommonFG) ReadImage(name string) (image.Image, error) { + if !g.LinkExists(name) { + return nil, errors.New("hdf5: name doesn't exist") + } + return getImage(g.id, name) +} diff --git a/h5g_group_test.go b/h5g_group_test.go index 3fd5197..12a9604 100644 --- a/h5g_group_test.go +++ b/h5g_group_test.go @@ -5,6 +5,9 @@ package hdf5 import ( + "image" + "image/color" + "image/jpeg" "os" "testing" ) @@ -128,3 +131,51 @@ func TestGroup(t *testing.T) { } } + +func TestImage(t *testing.T) { + f, err := CreateFile(fname, F_ACC_TRUNC) + if err != nil { + t.Fatalf("CreateFile failed: %v", err) + } + defer os.Remove(fname) + defer f.Close() + img := image.NewRGBA(image.Rect(0, 0, 1000, 500)) + for y := 200; y < 300; y++ { + for x := 400; x < 600; x++ { + img.Set(x, y, color.RGBA{255, 0, 255, 255}) + } + } + if err != nil { + t.Fatalf("image decoding failed: %v", err) + } + g1, err := f.CreateGroup("foo") + if err != nil { + t.Fatalf("couldn't create group: %v", err) + } + defer g1.Close() + err = g1.CreateImage("image", img) + if err != nil { + t.Fatalf("image saving failed: %v", err) + } + imgRead, err := g1.ReadImage("image") + if err != nil { + t.Fatalf("image reading failed: %v", err) + } + gotWidth := imgRead.Bounds().Max.X + gotHeight := imgRead.Bounds().Max.Y + if gotWidth != 1000 || gotHeight != 500 { + t.Errorf("image dimension mismatch: got %dx%d, want:1000x500", gotWidth, gotHeight) + } + + imgfile, err := os.Create("img.jpg") + if err != nil { + t.Fatalf("image file creation failed: %v", err) + } + defer os.Remove("img.jpg") + defer imgfile.Close() + + err = jpeg.Encode(imgfile, imgRead, nil) + if err != nil { + t.Errorf("unexpected error saving image: %v", err) + } +} diff --git a/h5i_image.go b/h5i_image.go new file mode 100644 index 0000000..e797bdb --- /dev/null +++ b/h5i_image.go @@ -0,0 +1,74 @@ +// license that can be found in the LICENSE file. + +package hdf5 + +// #include "hdf5.h" +// #include "hdf5_hl.h" +// #include +// #include +import "C" + +import ( + "errors" + "image" + "image/color" + "unsafe" +) + +// newImage takes a image object and convert it to a hdf5 format and write to the id node +func newImage(id C.hid_t, name string, img image.Image) error { + width := img.Bounds().Max.X + height := img.Bounds().Max.Y + var c_width, c_height C.hsize_t + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_name)) + var buf []byte + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + r, g, b, _ := img.At(x, y).RGBA() + buf = append(buf, byte(r>>8), byte(g>>8), byte(b>>8)) + } + } + c_width = C.hsize_t(width) + c_height = C.hsize_t(height) + c_image := (*C.uchar)(unsafe.Pointer(&buf[0])) + + status := C.H5IMmake_image_24bit(id, c_name, c_width, c_height, C.CString("INTERLACE_PIXEL"), c_image) + if status < 0 { + return errors.New("hdf5: failed to create true color image") + } + return nil +} + +// +func getImage(id C.hid_t, name string) (image.Image, error) { + //TODO(zyc-sudo) Should handle interlace and npal better, yet these two are not needed for simple image read and write. + var width, height, planes C.hsize_t + var npals C.hssize_t + var interlace C.char + c_name := C.CString(name) + defer C.free(unsafe.Pointer(c_name)) + rc := C.H5IMget_image_info(id, c_name, &width, &height, &planes, &interlace, &npals) + err := h5err(rc) + if err != nil { + return nil, err + } + gbuf := make([]uint8, width*height*planes) + c_image := (*C.uchar)(unsafe.Pointer(&gbuf[0])) + rc = C.H5IMread_image(id, c_name, c_image) + + err = h5err(rc) + if err != nil { + return nil, err + } + + g_width := int(width) + g_height := int(height) + img := image.NewRGBA(image.Rect(0, 0, g_width, g_height)) + for y := 0; y < g_height; y++ { + for x := 0; x < g_width; x++ { + img.Set(x, y, color.RGBA{gbuf[y*g_width*3+x*3], gbuf[y*g_width*3+x*3+1], gbuf[y*g_width*3+x*3+2], 255}) + } + } + return img, err +}