4

I've been trying for sometime to open an image in binary mode with Go. In Python I'd use the Pillow and image.open() (rb mode). Example.

img = Image.open("PNG.png")
pix = img.getdata()  #where 0 is black and 1 is white pixel

That would open the image with very clean binary of white and black dots like the image below. Example In go I've tried os.Open(file.jpg) to open the file.. I've tried decoding it with image.Decode(), I've loaded the file into bytes.Buffer, I've tried fmt.Sprintf("%b", data), all of the solutions give a byte array. Converting that byte array to binary looks nothing like the image above. I've also tried encoding/binary and its the same story with just getting bytes and the binary generated isn't what i want...

Most recently I've tried this

package main

import (
    "fmt"
    "image"
    "image/jpeg"
    "io"
    "log"
    "os"
)

// Pixel struct example
type Pixel struct {
    R int
    G int
    B int
    A int
}

func main() {
    // You can register another format here
    image.RegisterFormat("jpg", "jpg", jpeg.Decode, jpeg.DecodeConfig)

    file, err := os.Open("/Users/marcsantiago/Desktop/2033bb1b194adace86f99c7bb7d72e81.jpg")

    if err != nil {
        log.Fatalln("Error: File could not be opened")

    }

    defer file.Close()

    pixels, err := getPixels(file)

    if err != nil {
        log.Fatalln("Error: Image could not be decoded")
    }
    black := Pixel{0, 0, 0, 255}

    for i := range pixels {
        if pixels[i] == black {
            fmt.Print("0")
        } else {
            fmt.Print("1")
        }

    }
}

func getPixels(file io.Reader) ([]Pixel, error) {
    img, _, err := image.Decode(file)

    if err != nil {
        return nil, err
    }

    bounds := img.Bounds()
    width, height := bounds.Max.X, bounds.Max.Y

    var pixels []Pixel
    for y := 0; y < height; y++ {
        for x := 0; x < width; x++ {
            pixels = append(pixels, rgbaToPixel(img.At(x, y).RGBA()))
        }
    }
    return pixels, nil
}

// img.At(x, y).RGBA() returns four uint32 values; we want a Pixel
func rgbaToPixel(r uint32, g uint32, b uint32, a uint32) Pixel {
    return Pixel{int(r / 257), int(g / 257), int(b / 257), int(a / 257)}
}

So that I can compare the binary against what I expect I converted the rgba to 1 and 0s where 0 == black... it still doesn't match up not even close. Example enter image description here

Help please. I'm out of ideas. PS. This site http://www.dcode.fr/binary-image, also opens the image and generates the data I'm expecting.

UPDATE: This is the image i'm working with.. Image to decode

reticentroot
  • 3,612
  • 2
  • 22
  • 39
  • It think it is not related to opening file in `"rb"` mode. It may caused by the pixel format in the image. It is hard to find what was not working in your code without the input file `image.jpg` – putu Jun 11 '17 at 22:30
  • @putu I'll remedy that.. all I know for sure that is in python image.open is a slightly beefy wrapper for open("file", "rb") and when I read the data opened by python it looks correct. – reticentroot Jun 11 '17 at 22:42

1 Answers1

4

For example,

package main

import (
    "bytes"
    "fmt"
    "image"
    "os"

    _ "image/jpeg"
)

func main() {
    fName := "ggk3Z.jpg"
    f, err := os.Open(fName)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    defer f.Close()
    img, _, err := image.Decode(f)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    // http://www.dcode.fr/binary-image
    var txt bytes.Buffer
    bounds := img.Bounds()
    for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
        for x := bounds.Min.X; x < bounds.Max.X; x++ {
            r, g, b, _ := img.At(x, y).RGBA()
            bin := "0"
            if float64((r+g+b))/3 > 0.5 {
                bin = "1"
            }
            txt.WriteString(bin)
        }
        txt.WriteString("\n")
    }
    fmt.Fprint(os.Stdout, txt.String())
}
peterSO
  • 158,998
  • 31
  • 281
  • 276
  • 1
    Your solution, while providing binary data of the file, isn't quite what i'm looking for. If you examine the first image in the post, that is the binary data returned from the site and from python's Pillow image.open() function. If you run your code against the image I gave you you'll see that the output is extremely different, but no different then the other methods i mentioned I tried. I've updated the nature of the question as I think it may be more precise and added some more information, however I do like your solution in terms of efficiency for other applications :-), so ++ for that – reticentroot Jun 11 '17 at 23:45
  • 1
    @reticentroot: See my revised answer. – peterSO Jun 12 '17 at 00:27
  • 1
    This is closer, but shares the same faults as the example code in my post (but faster). The expected output should be about 4kb, whereas ours is closer to 4mb. The 0 groupings are messy and much too large (00110.. vs just 0). I don't know if you've run this code against the same image i am posted, I've provide an example image of what I expect and what i'm getting, our codes provide the same output. The site I provided outputs the correct binary, as well as the two of python lines I provided. At this point I'm not sure Go's image library is capable of processing the image in the way I want. – reticentroot Jun 12 '17 at 00:58
  • 1
    I noticed that you used the formula listed on the site. I've tried that as well. I also noticed that on the site there is a checkbox for REDUCE THE SIZE (HEIGHT/WEIGHT) OF THE PICTURE, I believe that the Pillow module and this site are doing similar things to achieve the desired effect. When that box is unchecked it is closer to what our code is outputting. Still i like the speed of your example and could be a good starting point is reversing what Pillow is doing. It's not the the answer I was hopping for, but its a good answer and a good direction. If you think of anything else plea add on. – reticentroot Jun 12 '17 at 01:00
  • 4
    @reticentroot: The conversion from `RGB` to binary image should be: `float64((r+g+b))/(3*65535)` since in `Go` each RGB color component is represented as 16-bit unsigned. To get similar result, resize (e.g. using [https://github.com/nfnt/resize](https://github.com/nfnt/resize)) the image (to WxH = 100x44) before converting it to binary. – putu Jun 12 '17 at 03:05
  • @putu thanks between that and Peter's solution I think all should be well. – reticentroot Jun 12 '17 at 03:12
  • While @peterSO 's answer is kinda what I would've answered, maybe the problem with messy grouping you have between 0s and 1s will be fixed by fixing an evenly size for every runes, try : `go get golang.org/x/text` then `package main import ( "fmt" "golang.org/x/text/width" ) func main() { str := width.Widen.String("0000101110100011001") fmt.Println(str) }` ...and maybe the output size could be solutioned by comparing 8² pixels? Just an idea if you don't need the exact match... – Cerberus Nov 15 '18 at 21:00