Image Processing with gonum matrices

771 views
Skip to first unread message

Braunson -

unread,
May 24, 2018, 12:20:03 PM5/24/18
to gonum-dev
Hello everyone,

I'm doing some work with a large dataset of images. We have thousands of images acquired and placed into a HDF5 file(another wrapper that gonum provides https://github.com/gonum/hdf5) using Python. I was wondering if there was a way with gonum to take the matrix data from the HDF5 file into a file after doing some post processing.

I'd know how to do this in Python with Numpy but I'd love to leverage the speed of Go for this task. I think I would be fine figuring out how to read the HDF5 file data using your library, the main concern is using that data to create a matrix that can be saved as an individual .png or .jpeg!

A similar pipeline in Python would be:

array = numpy.array(image_data).reshape(width, height, 3) # RGB
array = numpy.array(image_data).reshape(width, height) # RAW16

# Do stuff to the array
from PIL import Image
im = Image.fromarray(array)
im.save("something.png")


Thanks for taking the time to read this! I'm really interested in Go and I haven't seen many people doing image processing with it yet.

Sebastien Binet

unread,
May 24, 2018, 12:54:34 PM5/24/18
to bma...@gmail.com, gonum-dev
Hi Braunson,

Welcome!

first of all, gonum/mat doesn't work very well with integer data elements (the only matrix element type we can deal with is float64 (and, IIRC, to some extent, complex128))
I think, to deal with image manipulation, you're probably better off with golang.org/x/image and golang.org/x/image/draw
but perhaps other Gonumers (?) may have a different take on this.

that said, I have a pure-Go package that reads/writes numpy array files:

reading the numpy file would look like:

r, err := npyio.NewReader(f)
if err != nil {
log.Fatal(err)
}

fmt.Printf("npy-header: %v\n", r.Header)
shape := r.Header.Descr.Shape
raw := make([]int16, shape[0]*shape[1])

err = r.Read(&raw)
if err != nil {
log.Fatal(err)
}

but I realize you'd also want to write a new NumPy data file :)
and I also realize that even if npyio can write 1-dim arrays correctly, it can't (ATM) specify a shape (width,height,3):

should be easy to add though... (famous last words)

feel free to file an issue against sbinet/npyio :)

hth,
-s

Braunson -

unread,
May 24, 2018, 1:41:01 PM5/24/18
to gonum-dev
Thanks Sebastien!

I don't think I would have a use for the NumPy datafile project, as noted we are using HDF5 and I have been able to open the image data using the gonum library for that.

    s2 := make([]uint8, 2048*2448*3)
    err = dset.Read(&s2)
    if err != nil {
        panic(err)
    }

    // display the fields
    fmt.Printf(":: size: length %v  capacity %v", len(s2), cap(s2))

However this doesn't really make a lot of sense in its current form since it should be a matrix of 2048 x 2448 x 3. Is there any support within GoNum or Golang at all for conversion of some data like this into an image file? From the Image libraries I can't seem to find a way to save this image, especially because it does not have an Alpha band(not RGBA).

I apologize if this is off topic of this mailing list, please let me know if there is a better place to be asking. I thought using Gonum's matrices like I would numpy would be the way to go.

Sebastien Binet

unread,
May 25, 2018, 3:58:18 AM5/25/18
to bma...@gmail.com, gonum-dev
On Thu, May 24, 2018 at 7:41 PM Braunson - <bma...@gmail.com> wrote:
Thanks Sebastien!

I don't think I would have a use for the NumPy datafile project, as noted we are using HDF5 and I have been able to open the image data using the gonum library for that.

    s2 := make([]uint8, 2048*2448*3)
    err = dset.Read(&s2)
    if err != nil {
        panic(err)
    }

    // display the fields
    fmt.Printf(":: size: length %v  capacity %v", len(s2), cap(s2))

However this doesn't really make a lot of sense in its current form since it should be a matrix of 2048 x 2448 x 3. Is there any support within GoNum or Golang at all for conversion of some data like this into an image file? From the Image libraries I can't seem to find a way to save this image, especially because it does not have an Alpha band(not RGBA).

ok.
then, it would be something along these lines (untested! some transpose mistakes are possible :P):

var (
width  = 2048
height = 2448
)
s2 := make([]uint8, width*height*3)

// ...read from hdf5...

to1D := func(x, y, z int) int {
return (z * height * width) + (y * width) + x
}
// display the fields
fmt.Printf(":: size: length %v  capacity %v\n", len(s2), cap(s2))

img := image.NewRGBA(image.Rect(0, 0, width, height))
for ix := 0; ix < width; ix++ {
for iy := 0; iy < height; iy++ {
ir := to1D(ix, iy, 0)
ig := to1D(ix, iy, 1)
ib := to1D(ix, iy, 2)
img.SetRGBA(ix, iy, color.RGBA{R: s2[ir], G: s2[ig], B: s2[ib], A: 255})
}
}
o, err := os.Create("out.png")
if err != nil {
log.Fatal(err)
}
defer o.Close()

err = png.Encode(o, img)
if err != nil {
log.Fatal(err)
}

err = o.Close()
if err != nil {
log.Fatal(err)
}

then, if you don't need to be inter-operable with python via npy array files, it would be probably sensible to just save the image.RGBA as a PNG image :)

-s

Braunson -

unread,
May 25, 2018, 10:32:56 AM5/25/18
to gonum-dev
Thanks again sebastien! This works perfectly.

The only slight change needed is the math for converting to a 1D is as follows:

    var (
        width  = 2048
        height = 2448
        rgb = 3
    )


    to1D := func(x, y, z int) int {
        return (x * height * rgb) + (rgb * y) + z
    }

Thanks again for the help though, and thanks for the awesome HDF5 library :)
Reply all
Reply to author
Forward
0 new messages