func ensureDivis(src image.Image, m int) image.Image { s := src.Bounds().Size() s = s.Sub(s.Mod(image.Rect(0, 0, m, m))) dst := image.NewRGBA(image.Rectangle{image.ZP, s}) draw.Draw(dst, dst.Bounds(), src, image.ZP, draw.Src) return dst }
func (e *engine) LoadTexture(src image.Image) (sprite.Texture, error) { b := src.Bounds() t := &texture{glutil.NewImage(b.Dx(), b.Dy()), b} t.Upload(b, src) // TODO: set "glImage.Pix = nil"?? We don't need the CPU-side image any more. return t, nil }
// Encode writes the Image m to w in JPEG 4:2:0 baseline format with the given // options. Default parameters are used if a nil *Options is passed. func Encode(w io.Writer, m image.Image, o *Options) error { b := m.Bounds() if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 { return errors.New("jpeg: image is too large to encode") } var e encoder if ww, ok := w.(writer); ok { e.w = ww } else { e.w = bufio.NewWriter(w) } // Clip quality to [1, 100]. quality := DefaultQuality if o != nil { quality = o.Quality if quality < 1 { quality = 1 } else if quality > 100 { quality = 100 } } // Convert from a quality rating to a scaling factor. var scale int if quality < 50 { scale = 5000 / quality } else { scale = 200 - quality*2 } // Initialize the quantization tables. for i := range e.quant { for j := range e.quant[i] { x := int(unscaledQuant[i][j]) x = (x*scale + 50) / 100 if x < 1 { x = 1 } else if x > 255 { x = 255 } e.quant[i][j] = uint8(x) } } // Write the Start Of Image marker. e.buf[0] = 0xff e.buf[1] = 0xd8 e.write(e.buf[:2]) // Write the quantization tables. e.writeDQT() // Write the image dimensions. e.writeSOF0(b.Size()) // Write the Huffman tables. e.writeDHT() // Write the image data. e.writeSOS(m) // Write the End Of Image marker. e.buf[0] = 0xff e.buf[1] = 0xd9 e.write(e.buf[:2]) e.flush() return e.err }
// encodeRGB64Data writes image data as 16-bit samples. func encodeRGB64Data(w io.Writer, img image.Image, opts *EncodeOptions) error { // In the background, write each 16-bit color sample into a channel. rect := img.Bounds() width := rect.Max.X - rect.Min.X samples := make(chan uint16, width*3) go func() { cm := npcolor.RGBM64Model{M: opts.MaxValue} for y := rect.Min.Y; y < rect.Max.Y; y++ { for x := rect.Min.X; x < rect.Max.X; x++ { c := cm.Convert(img.At(x, y)).(npcolor.RGBM64) samples <- c.R samples <- c.G samples <- c.B } } close(samples) }() // In the foreground, consume color samples and write them to the image // file. if opts.Plain { return writePlainData(w, samples) } else { return writeRawData(w, samples, 2) } }
func crops(i image.Image, cropWidth, cropHeight, realMinScale float64) []Crop { res := []Crop{} width := i.Bounds().Size().X height := i.Bounds().Size().Y minDimension := math.Min(float64(width), float64(height)) var cropW, cropH float64 if cropWidth != 0.0 { cropW = cropWidth } else { cropW = minDimension } if cropHeight != 0.0 { cropH = cropHeight } else { cropH = minDimension } for scale := maxScale; scale >= realMinScale; scale -= scaleStep { for y := 0; float64(y)+cropH*scale <= float64(height); y += step { for x := 0; float64(x)+cropW*scale <= float64(width); x += step { res = append(res, Crop{ X: x, Y: y, Width: int(cropW * scale), Height: int(cropH * scale), }) } } } return res }
func blur(img image.Image, howmuch float32) image.Image { g := gift.New(gift.Grayscale()) g.Add(gift.GaussianBlur(howmuch)) dst := image.NewRGBA(g.Bounds(img.Bounds())) g.Draw(dst, img) return (dst) }
func varianceF(img image.Image, disk int) (imageF, imageF) { m := meanF(img, disk) // gets a grayscale copy of local mean // create a grayscale version of the original //g := gift.New( gift.Grayscale() ) //v := image.NewRGBA(g.Bounds(img.Bounds())) //g.Draw(v, img) g := gift.New(gift.Grayscale()) dst := image.NewRGBA(g.Bounds(img.Bounds())) g.Draw(dst, img) bounds := img.Bounds() floatData := make([][]float32, bounds.Max.Y-bounds.Min.Y) for i := range floatData { floatData[i] = make([]float32, bounds.Max.X-bounds.Min.X) } for y := bounds.Min.X; y < bounds.Max.X; y++ { for x := bounds.Min.Y; x < bounds.Max.Y; x++ { p1r, p1g, p1b, _ := dst.At(x, y).RGBA() g1 := 0.2125*float64(p1r) + 0.7154*float64(p1g) + 0.0721*float64(p1b) g2 := float64(m[x][y]) floatData[x][y] = float32((g1 - g2) * (g1 - g2)) } } return m, floatData }
// convert an image into ASCII! // watch out, this might be painfully slow... func Convert(m image.Image, p []*TextColor) *Image { c := NewPalette(p) // create image of correct size bounds := m.Bounds() s := bounds.Size() img := NewImage(uint(s.X), uint(s.Y)) // dereference for slice manipulation grid := *img var wg sync.WaitGroup for y := range grid { wg.Add(1) go func(r []*TextColor, y int) { for x := range r { r[x] = c.Convert(m.At(x+bounds.Min.X, y+bounds.Min.Y)).(*TextColor) } wg.Done() }(grid[y], y) } wg.Wait() return img }
func imageBytes(img image.Image) (buf *bytes.Buffer, err error) { var ( bounds image.Rectangle = img.Bounds() rgba *image.RGBA data []byte ) buf = &bytes.Buffer{} rgba = image.NewRGBA(bounds) draw.Draw(rgba, bounds, img, bounds.Min, draw.Src) data = make([]byte, len(rgba.Pix)) var ( destOffset int = len(data) - rgba.Stride ) for srcOffset := 0; srcOffset < len(rgba.Pix); { var ( dest = data[destOffset : destOffset+rgba.Stride] source = rgba.Pix[srcOffset : srcOffset+rgba.Stride] ) copy(dest, source) destOffset -= rgba.Stride srcOffset += rgba.Stride } for x := 0; x < len(data); { buf.WriteByte(data[x+3]) buf.WriteByte(data[x+2]) buf.WriteByte(data[x+1]) buf.WriteByte(data[x+0]) x += 4 } return }
func DepthOf(m image.Image) int { if m, ok := m.(*MemPImage); ok { return SizeofKind(m.XDataType) * 8 } if m, ok := m.(MemP); ok { return SizeofKind(m.DataType() * 8) } switch m.(type) { case *image.Gray: return 1 * 8 case *image.Gray16: return 2 * 8 case *image.NRGBA: return 1 * 8 case *image.NRGBA64: return 2 * 8 case *image.RGBA: return 1 * 8 case *image.RGBA64: return 2 * 8 case *image.YCbCr: return 1 * 8 } return 2 * 8 }
func SizeofImage(m image.Image) int { if m, ok := m.(SizeofImager); ok { return m.SizeofImage() } if m, ok := AsMemPImage(m); ok { return int(unsafe.Sizeof(*m)) + len(m.XPix) } b := m.Bounds() switch m := m.(type) { case *image.Alpha: return int(unsafe.Sizeof(*m)) + b.Dx()*b.Dy()*1 case *image.Alpha16: return int(unsafe.Sizeof(*m)) + b.Dx()*b.Dy()*2 case *image.Gray: return int(unsafe.Sizeof(*m)) + b.Dx()*b.Dy()*1 case *image.Gray16: return int(unsafe.Sizeof(*m)) + b.Dx()*b.Dy()*2 case *image.NRGBA: return int(unsafe.Sizeof(*m)) + b.Dx()*b.Dy()*4 case *image.NRGBA64: return int(unsafe.Sizeof(*m)) + b.Dx()*b.Dy()*8 case *image.RGBA: return int(unsafe.Sizeof(*m)) + b.Dx()*b.Dy()*4 case *image.RGBA64: return int(unsafe.Sizeof(*m)) + b.Dx()*b.Dy()*8 case *image.Uniform: return int(unsafe.Sizeof(*m)) case *image.YCbCr: return int(unsafe.Sizeof(*m)) + len(m.Y) + len(m.Cb) + len(m.Cr) } // return same as RGBA64 size return int(unsafe.Sizeof((*image.RGBA64)(nil))) + b.Dx()*b.Dy()*8 }
func composite(input *gif.GIF, lgtm image.Image) (*gif.GIF, error) { output := gif.GIF{ Delay: input.Delay, LoopCount: input.LoopCount, Disposal: input.Disposal, Config: image.Config{ Width: input.Config.Width, Height: input.Config.Height, }, } x := input.Config.Width/2 - lgtm.Bounds().Dx()/2 y := input.Config.Height/2 - lgtm.Bounds().Dy()/2 fmt.Print("compositting frame...") for i, frame := range input.Image { fmt.Printf("%d ", i+1) draw.Draw(frame, frame.Bounds(), lgtm, image.Point{-x, -y}, draw.Over) output.Image = append(output.Image, frame) } fmt.Println("done") return &output, nil }
func (w *walker) IsVertex(cell image.Point, img image.Image) bool { // a point is a vertex iff it has 4 'different' neightbors, this includes diagonal count := 0 x := cell.X y := cell.Y myColor := color.White for i := -1; i < 2; i++ { for j := -1; j < 2; j++ { Maxsize := img.Bounds().Max Minsize := img.Bounds().Min if x+i > Maxsize.X || x+i < Minsize.X { count++ } if y+i > Maxsize.Y || y+i < Minsize.Y { count++ } if sameColor(myColor, img.At(x+i, y+j)) { count++ } } } if count >= 4 { return true } return false }
func generateHistogramForImage(m image.Image) [][]float64 { bounds := m.Bounds() widthPixels := int(bounds.Max.X - bounds.Min.X) heightPixels := int(bounds.Max.Y - bounds.Min.Y) histogram := make([][]float64, 3) for i := range histogram { histogram[i] = make([]float64, widthPixels) } for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { r, g, b, _ := m.At(x, y).RGBA() // values should be between 0 and 1 relR := float64(r) / 65535.0 relG := float64(g) / 65535.0 relB := float64(b) / 65535.0 histogram[0][x] += relR histogram[1][x] += relG histogram[2][x] += relB } } // create rounded mean value for each x value for i, values := range histogram { for j := range values { histogram[i][j] = roundedMean(histogram[i][j], float64(heightPixels)) } } return histogram }
// Paste pastes the img image to the background image at the specified position and returns the combined image. func Paste(background, img image.Image, pos image.Point) *image.NRGBA { src := toNRGBA(img) dst := Clone(background) // cloned image bounds start at (0, 0) startPt := pos.Sub(background.Bounds().Min) // so we should translate start point endPt := startPt.Add(src.Bounds().Size()) pasteBounds := image.Rectangle{startPt, endPt} if dst.Bounds().Overlaps(pasteBounds) { intersectBounds := dst.Bounds().Intersect(pasteBounds) rowSize := intersectBounds.Dx() * 4 numRows := intersectBounds.Dy() srcStartX := intersectBounds.Min.X - pasteBounds.Min.X srcStartY := intersectBounds.Min.Y - pasteBounds.Min.Y i0 := dst.PixOffset(intersectBounds.Min.X, intersectBounds.Min.Y) j0 := src.PixOffset(srcStartX, srcStartY) di := dst.Stride dj := src.Stride for row := 0; row < numRows; row++ { copy(dst.Pix[i0:i0+rowSize], src.Pix[j0:j0+rowSize]) i0 += di j0 += dj } } return dst }
// TODO use multiple channels to store edge intersections func (sdf *SDF) calc(m image.Image) { max := dist(0, 0, sdf.pad, sdf.pad) - 1 b := m.Bounds() // TODO this space could probably be traversed better for y := b.Min.Y; y < b.Max.Y; y++ { for x := b.Min.X; x < b.Max.X; x++ { _, _, _, ma := m.At(x, y).RGBA() c := nearest(x, y, m.(*image.NRGBA).SubImage(image.Rect(x-sdf.pad, y-sdf.pad, x+sdf.pad, y+sdf.pad))) if c == 0xFF { // check if pixel is inside as a center of opposing edges if ma != 0 { sdf.dst.Set(x, y, color.RGBA{A: 0xFF}) } continue } // return from nearest is always >= 1 // decrement so that c/max returns a unit value inclusive of zero c-- n := 0xFF * (1 - (float64(c) / float64(max))) if ma != 0 { // inside edge sdf.dst.Set(x, y, color.RGBA{A: 0xFF - uint8(n/2)}) } else { // outside edge step := float64(0xFF) / float64(max) if n = n - step; n < 0 { n = 0 } sdf.dst.Set(x, y, color.RGBA{A: uint8(n / 2)}) } } } }
func histogram(img image.Image, bins int) []int { mi, _, _ := getMin(img) ma, _, _ := getMax(img) //fmt.Printf("max and min are : %v %v\n", mi, ma) bounds := img.Bounds() var h []int h = make([]int, bins, bins) 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() a := 0.2125*float64(r) + 0.7154*float64(g) + 0.0721*float64(b) idx := int(math.Floor((a - mi) / (ma - mi) * (float64(bins) - 1.0))) if idx < 0 { idx = 0 } if idx >= bins { idx = bins - 1 } h[idx]++ } } return h }
// newIntegrals returns the integral and the squared integral. func newIntegrals(src image.Image) (*integral, *integral) { b := src.Bounds() srcg, ok := src.(*image.Gray) if !ok { srcg = image.NewGray(b) draw.Draw(srcg, b, src, b.Min, draw.Src) } m := integral{ pix: make([]uint64, b.Max.Y*b.Max.X), stride: b.Max.X, rect: b, } mSq := integral{ pix: make([]uint64, b.Max.Y*b.Max.X), stride: b.Max.X, rect: b, } for y := b.Min.Y; y < b.Max.Y; y++ { for x := b.Min.X; x < b.Max.X; x++ { os := (y-b.Min.Y)*srcg.Stride + x - b.Min.X om := (y-b.Min.Y)*m.stride + x - b.Min.X c := uint64(srcg.Pix[os]) m.pix[om] = c mSq.pix[om] = c * c } } m.integrate() mSq.integrate() return &m, &mSq }
func mean(img image.Image, disk int) image.Image { g := gift.New(gift.Grayscale()) g.Add(gift.Mean(disk, false)) // use square neighborhood dst := image.NewRGBA(g.Bounds(img.Bounds())) g.Draw(dst, img) return (dst) }
func drawPoint(point Point, dst *image.RGBA, src image.Image) { p := image.Point{point.X, point.Y} srcRect := src.Bounds() size := srcRect.Size() rect := image.Rectangle{p, p.Add(size)} draw.Draw(dst, rect, src, srcRect.Min, draw.Src) }
// toRGBA translates the given image to RGBA format if necessary. // Optionally scales it by the given amount. func toRGBA(src image.Image, scale int) *image.RGBA { if scale < 1 { scale = 1 } dst, ok := src.(*image.RGBA) if ok && scale == 1 { return dst } // Scale image to match new size. ib := src.Bounds() rect := image.Rect(0, 0, ib.Dx()*scale, ib.Dy()*scale) if !ok { // Image is not RGBA, so we create it. dst = image.NewRGBA(rect) } for sy := 0; sy < ib.Dy(); sy++ { for sx := 0; sx < ib.Dx(); sx++ { dx := sx * scale dy := sy * scale pixel := src.At(sx, sy) for scy := 0; scy < scale; scy++ { for scx := 0; scx < scale; scx++ { dst.Set(dx+scx, dy+scy, pixel) } } } } return dst }
func MultiScale(im image.Image, tmpls map[string]*detect.FeatTmpl, opts detect.MultiScaleOpts) ([]Det, error) { if len(tmpls) == 0 { return nil, nil } scales := imgpyr.Scales(im.Bounds().Size(), minDims(tmpls), opts.MaxScale, opts.PyrStep).Elems() ims := imgpyr.NewGenerator(im, scales, opts.Interp) pyr := featpyr.NewGenerator(ims, opts.Transform, opts.Pad) var dets []Det l, err := pyr.First() if err != nil { return nil, err } for l != nil { for key, tmpl := range tmpls { pts := detect.Points(l.Feat, tmpl.Image, tmpl.Bias, opts.DetFilter.LocalMax, opts.DetFilter.MinScore) // Convert to scored rectangles in the image. for _, pt := range pts { rect := pyr.ToImageRect(l.Image.Index, pt.Point, tmpl.Interior) dets = append(dets, Det{detect.Det{pt.Score + tmpl.Bias, rect}, key}) } } var err error l, err = pyr.Next(l) if err != nil { return nil, err } } Sort(dets) inds := detect.SuppressIndex(DetSlice(dets), opts.SupprFilter.MaxNum, opts.SupprFilter.Overlap) dets = detsSubset(dets, inds) return dets, nil }
func edgeDetect(i image.Image, o image.Image) { w := i.Bounds().Size().X h := i.Bounds().Size().Y cies := makeCies(i) for y := 0; y < h; y++ { for x := 0; x < w; x++ { var lightness float64 if x == 0 || x >= w-1 || y == 0 || y >= h-1 { //lightness = cie((*i).At(x, y)) lightness = 0 } else { lightness = cies[y*w+x]*4.0 - cies[x+(y-1)*w] - cies[x-1+y*w] - cies[x+1+y*w] - cies[x+(y+1)*w] } nc := color.RGBA{0, uint8(bounds(lightness)), 0, 255} o.(*image.RGBA).Set(x, y, nc) } } }
// Canny detects and returns edges from the given image. // Each dst pixel is given one of three values: // 0xff: an edge // 0x80: possibly an edge // 0x00: not an edge func Canny(dst *image.Gray, src image.Image) error { if dst == nil { return errors.New("edge: dst is nil") } if src == nil { return errors.New("edge: src is nil") } b := src.Bounds() srcGray, ok := src.(*image.Gray) if !ok { srcGray = image.NewGray(b) draw.Draw(srcGray, b, src, b.Min, draw.Src) } if err := graphics.Blur(srcGray, srcGray, nil); err != nil { return err } mag, dir := image.NewGray(b), image.NewGray(b) if err := Sobel(mag, dir, srcGray); err != nil { return err } // Non-maximum supression. for y := b.Min.Y; y < b.Max.Y; y++ { for x := b.Min.X; x < b.Max.X; x++ { d := dir.Pix[(y-b.Min.Y)*dir.Stride+(x-b.Min.X)*1] var m0, m1 uint8 switch d { case 0: // west and east m0 = atOrZero(mag, x-1, y) m1 = atOrZero(mag, x+1, y) case 45: // north-east and south-west m0 = atOrZero(mag, x+1, y-1) m1 = atOrZero(mag, x-1, y+1) case 90: // north and south m0 = atOrZero(mag, x, y-1) m1 = atOrZero(mag, x, y+1) case 135: // north-west and south-east m0 = atOrZero(mag, x-1, y-1) m1 = atOrZero(mag, x+1, y+1) default: return fmt.Errorf("edge: bad direction (%d, %d): %d", x, y, d) } m := mag.Pix[(y-b.Min.Y)*mag.Stride+(x-b.Min.X)*1] if m > m0 && m > m1 { m = 0xff } else if m > m0 || m > m1 { m = 0x80 } else { m = 0x00 } dst.Pix[(y-b.Min.Y)*dst.Stride+(x-b.Min.X)*1] = m } } return nil }
// create an Alpha Icon or Cursor from an Image // http://support.microsoft.com/kb/318876 func createAlphaCursorOrIconFromImage(im image.Image, hotspot image.Point, fIcon bool) (HICON, error) { hBitmap, err := hBitmapFromImage(im) if err != nil { return 0, err } defer DeleteObject(HGDIOBJ(hBitmap)) // Create an empty mask bitmap. hMonoBitmap := CreateBitmap(int32(im.Bounds().Dx()), int32(im.Bounds().Dy()), 1, 1, nil) if hMonoBitmap == 0 { return 0, newError("CreateBitmap failed") } defer DeleteObject(HGDIOBJ(hMonoBitmap)) var ii ICONINFO if fIcon { ii.FIcon = TRUE } ii.XHotspot = uint32(hotspot.X) ii.YHotspot = uint32(hotspot.Y) ii.HbmMask = hMonoBitmap ii.HbmColor = hBitmap // Create the alpha cursor with the alpha DIB section. hIconOrCursor := CreateIconIndirect(&ii) return hIconOrCursor, nil }
// Encode image.Image with Mapniks image encoder. func Encode(img image.Image, format string) ([]byte, error) { var i *C.mapnik_image_t switch img := img.(type) { // XXX does mapnik expect NRGBA or RGBA? this might stop working //as expected if we start encoding images with full alpha channel case *image.NRGBA: i = C.mapnik_image_from_raw( (*C.uint8_t)(unsafe.Pointer(&img.Pix[0])), C.int(img.Bounds().Dx()), C.int(img.Bounds().Dy()), ) case *image.RGBA: i = C.mapnik_image_from_raw( (*C.uint8_t)(unsafe.Pointer(&img.Pix[0])), C.int(img.Bounds().Dx()), C.int(img.Bounds().Dy()), ) } if i == nil { return nil, errors.New("unable to create image from raw") } defer C.mapnik_image_free(i) cformat := C.CString(format) b := C.mapnik_image_to_blob(i, cformat) if b == nil { return nil, errors.New("mapnik: " + C.GoString(C.mapnik_image_last_error(i))) } C.free(unsafe.Pointer(cformat)) defer C.mapnik_image_blob_free(b) return C.GoBytes(unsafe.Pointer(b.ptr), C.int(b.len)), nil }
// writeSOS writes the StartOfScan marker. func (e *encoder) writeSOS(m image.Image) { e.write(sosHeader) var ( // Scratch buffers to hold the YCbCr values. yBlock block cbBlock [4]block crBlock [4]block cBlock block // DC components are delta-encoded. prevDCY, prevDCCb, prevDCCr int ) bounds := m.Bounds() rgba, _ := m.(*image.RGBA) for y := bounds.Min.Y; y < bounds.Max.Y; y += 16 { for x := bounds.Min.X; x < bounds.Max.X; x += 16 { for i := 0; i < 4; i++ { xOff := (i & 1) * 8 yOff := (i & 2) * 4 p := image.Point{x + xOff, y + yOff} if rgba != nil { rgbaToYCbCr(rgba, p, &yBlock, &cbBlock[i], &crBlock[i]) } else { toYCbCr(m, p, &yBlock, &cbBlock[i], &crBlock[i]) } prevDCY = e.writeBlock(&yBlock, 0, prevDCY) } scale(&cBlock, &cbBlock) prevDCCb = e.writeBlock(&cBlock, 1, prevDCCb) scale(&cBlock, &crBlock) prevDCCr = e.writeBlock(&cBlock, 1, prevDCCr) } } // Pad the last byte with 1's. e.emit(0x7f, 7) }
// CropAnchor cuts out a rectangular region with the specified size // from the image using the specified anchor point and returns the cropped image. func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA { srcBounds := img.Bounds() pt := anchorPt(srcBounds, width, height, anchor) r := image.Rect(0, 0, width, height).Add(pt) b := srcBounds.Intersect(r) return Crop(img, b) }
func hough(im image.Image, ntx, mry int) draw.Image { nimx := im.Bounds().Max.X mimy := im.Bounds().Max.Y mry = int(mry/2) * 2 him := image.NewGray(image.Rect(0, 0, ntx, mry)) draw.Draw(him, him.Bounds(), image.NewUniform(color.White), image.ZP, draw.Src) rmax := math.Hypot(float64(nimx), float64(mimy)) dr := rmax / float64(mry/2) dth := math.Pi / float64(ntx) for jx := 0; jx < nimx; jx++ { for iy := 0; iy < mimy; iy++ { col := color.GrayModel.Convert(im.At(jx, iy)).(color.Gray) if col.Y == 255 { continue } for jtx := 0; jtx < ntx; jtx++ { th := dth * float64(jtx) r := float64(jx)*math.Cos(th) + float64(iy)*math.Sin(th) iry := mry/2 - int(math.Floor(r/dr+.5)) col = him.At(jtx, iry).(color.Gray) if col.Y > 0 { col.Y-- him.SetGray(jtx, iry, col) } } } } return him }
func ReadShadowData(im image.Image) (b []byte, err error) { head, err := ReadShadowHeader(im) if err != nil { return nil, err } length := int(ReadShadowLength(head)) var bk []byte = make([]byte, length*8) b = make([]byte, length) _, err = SetImage(im, func(index, x, y int, in, out image.Image) { if index >= 64 && index < length*8+64 { R := readRGBAColor(im.At(x, y)).R bk[index-64] = uint8(R & 1) } }) var bb [8]byte var bs []byte for i := 0; i < length; i++ { bs = bk[8*i : 8*(i+1)] for j := 0; j < 8; j++ { bb[j] = bs[j] } b[i] = Bits2Byte(bb) } return }