func getGray(src *image.Gray, x, y, borderMethod int) uint8 { bound := src.Bounds() if x < 0 { switch borderMethod { case BORDER_COPY: x = 0 default: return 0 } } else if x >= bound.Max.X { switch borderMethod { case BORDER_COPY: x = bound.Max.X - 1 default: return 0 } } if y < 0 { switch borderMethod { case BORDER_COPY: y = 0 default: return 0 } } else if y >= bound.Max.Y { switch borderMethod { case BORDER_COPY: y = bound.Max.Y - 1 default: return 0 } } i := (y-bound.Min.Y)*src.Stride + (x - bound.Min.X) return src.Pix[i] }
func Contrast(picture *image.Gray, contrast uint8) *image.Gray { bounds := picture.Bounds() newPic := image.NewGray(bounds) average := getAverage(picture) for x := 0; x < bounds.Dx(); x++ { for y := 0; y < bounds.Dy(); y++ { pixel := picture.At(x, y).(color.Gray).Y if pixel > average { if pixel+contrast < pixel { pixel = 0xff } else { pixel += contrast } } else if pixel < average { if pixel-contrast > pixel { pixel = 0x00 } else { pixel -= contrast } } newPic.Set(x, y, color.Gray{pixel}) } } return newPic }
func valueAt(img *image.Gray, x, y float32) float32 { dx, dy := x/float32(screenWidth), y/float32(screenHeight) b := img.Bounds().Max px, py := int(dx*float32(b.X)), int(dy*float32(b.Y)) v := float32(img.At(px, py).(color.Gray).Y) / 255 return v }
func New(im image.Gray) *Integral { rect := im.Bounds() width := rect.Dx() height := rect.Dy() data := make([]uint32, width*height) for x := rect.Min.X; x < rect.Max.X; x++ { for y := rect.Min.Y; y < rect.Max.Y; y++ { if x == 0 && y == 0 { data[0] = uint32(im.Pix[0]) } else if x == 0 && y != 0 { data[y*height+x] = data[(y-1)*height+x] + uint32(im.Pix[y*im.Stride+x]) } else if y == 0 && x != 0 { data[y*height+x] = data[y*height+x-1] + uint32(im.Pix[y*im.Stride+x]) } else { data[y*height+x] = data[(y-1)*height+x] + data[y*height+x-1] - data[(y-1)*height+x-1] + uint32(im.Pix[y*im.Stride+x]) } } } intImg := &Integral{ Data: data, Width: width, Height: height, } return intImg }
func Blobify(img *image.Gray) Blobs { var blobs Blobs visited := make([]bool, img.Bounds().Max.X*img.Bounds().Max.Y) // Scan for first/next blob. x, y := 0, 0 for { x, y = nextPixel(x, y, img, visited) if x < 0 || y < 0 { for _, b := range blobs.blobs { // Only compute bridges for wide blobs if float64(b.Bounds.Dx()) >= blobs.avgWidth()*1.75 { b.computeBridges(blobs.avgWidth()) } } log.Printf("Returning Blobs with count: %d\n", len(blobs.blobs)) return blobs } // Extract blob. b := blobAt(x, y, img, visited) x = b.Bounds.Max.X + 1 blobs.addBlob(b) } }
func nearestGray(in *image.Gray, out *image.Gray, scale float64, coeffs []bool, offset []int, filterLength int) { newBounds := out.Bounds() maxX := in.Bounds().Dx() - 1 for x := newBounds.Min.X; x < newBounds.Max.X; x++ { row := in.Pix[x*in.Stride:] for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { var gray float32 var sum float32 start := offset[y] ci := y * filterLength for i := 0; i < filterLength; i++ { if coeffs[ci+i] { xi := start + i switch { case xi < 0: xi = 0 case xi >= maxX: xi = maxX } gray += float32(row[xi]) sum++ } } offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X) out.Pix[offset] = floatToUint8(gray / sum) } } }
func resizeGray(in *image.Gray, out *image.Gray, scale float64, coeffs []int16, offset []int, filterLength int) { newBounds := out.Bounds() maxX := in.Bounds().Dx() - 1 for x := newBounds.Min.X; x < newBounds.Max.X; x++ { row := in.Pix[(x-newBounds.Min.X)*in.Stride:] for y := newBounds.Min.Y; y < newBounds.Max.Y; y++ { var gray int32 var sum int32 start := offset[y] ci := y * filterLength for i := 0; i < filterLength; i++ { coeff := coeffs[ci+i] if coeff != 0 { xi := start + i switch { case xi < 0: xi = 0 case xi >= maxX: xi = maxX } gray += int32(coeff) * int32(row[xi]) sum += int32(coeff) } } offset := (y-newBounds.Min.Y)*out.Stride + (x - newBounds.Min.X) out.Pix[offset] = clampUint8(gray / sum) } } }
// checkCircle checks that a circle with a center in (x, y) and a radius r fits to the base image and all pixels are high. func checkCircle(base *image.Gray, level byte, pxSize, x, y, r float64) bool { width := float64(base.Bounds().Dx()) * pxSize height := float64(base.Bounds().Dy()) * pxSize if x < r || x > width-r || y < r || y > height-r { return false } x0 := int((x - r) / pxSize) y0 := int((y - r) / pxSize) x1 := int((x + r) / pxSize) y1 := int((y + r) / pxSize) for cy := y0; cy <= y1; cy++ { i0 := cy * base.Stride for cx := x0; cx <= x1; cx++ { if !inside(x, y, r, (x-r)+float64(cx-x0)*pxSize, (y-r)+float64(cy-y0)*pxSize) { continue } if base.Pix[i0+cx] != level { // circle hits background //fmt.Printf("checkCircle(pxSize=%f, x=%f, y=%f, r=%f, i0=%d, cx=%d, base.Pix[i0+cx]=%d\n", // pxSize, x, y, r, i0, cx, base.Pix[i0+cx]) return false } } } return true }
func fillTriangle(base *image.Gray, level byte, bbox image.Rectangle, ox, oy float64) []Point { basePxSize := *pxSize / float64(*n) width := float64(base.Bounds().Dx()) * basePxSize height := float64(base.Bounds().Dy()) * basePxSize dy := (*toolDiameter) / 2 dx := dy * 1.73205080757 // sqrt(3) var centers []Point for i := 0; ; i++ { cx := ox + float64(i)*dx if cx >= width { break } if cx < float64(bbox.Min.X-1)*basePxSize || cx >= float64(bbox.Max.X+1)*basePxSize { continue } for j := 0; ; j++ { cy := oy + float64(j)*dy if cy >= height { break } if cy < float64(bbox.Min.Y-1)*basePxSize || cy >= float64(bbox.Max.Y+1)*basePxSize { continue } if (i+j)%2 == 1 { continue } if checkCircle(base, level, basePxSize, cx, cy, (*toolDiameter)/2) { centers = append(centers, Point{cx, cy}) } } } return centers }
func fillQuad(base *image.Gray, level byte, bbox image.Rectangle, ox, oy float64) []Point { basePxSize := *pxSize / float64(*n) width := float64(base.Bounds().Dx()) * basePxSize height := float64(base.Bounds().Dy()) * basePxSize dx := *toolDiameter dy := *toolDiameter var centers []Point for i := 0; ; i++ { cx := ox + float64(i)*dx if cx >= width { break } if cx < float64(bbox.Min.X-1)*basePxSize || cx >= float64(bbox.Max.X+1)*basePxSize { //fmt.Printf("bbox={%f,%f}-{%f,%f}, cx: %f, skip...\n", // float64(bbox.Min.X)*basePxSize, float64(bbox.Min.Y)*basePxSize, float64(bbox.Max.X)*basePxSize, float64(bbox.Max.Y)*basePxSize, cx) continue } for j := 0; ; j++ { cy := oy + float64(j)*dy if cy >= height { break } if cy < float64(bbox.Min.Y-1)*basePxSize || cy >= float64(bbox.Max.Y+1)*basePxSize { //fmt.Printf("bbox={%f,%f}-{%f,%f}, cy: %f, skip...\n", // float64(bbox.Min.X)*basePxSize, float64(bbox.Min.Y)*basePxSize, float64(bbox.Max.X)*basePxSize, float64(bbox.Max.Y)*basePxSize, cy) continue } if checkCircle(base, level, basePxSize, cx, cy, (*toolDiameter)/2) { centers = append(centers, Point{cx, cy}) } } } return centers }
//BackProjectGray computes back projection of img // in Gray16 by performing an addition // of backprojection by line. // 16Gray avoids white noise. func BackProjectGray(img image.Gray) (*image.Gray16, error) { size := img.Bounds().Size() width := size.Y nbProj := size.X step := 180.0 / float64(nbProj) out := image.NewGray16(image.Rect(0, 0, width, width)) for X := 0; X < nbProj; X++ { //Extract a 1D-projection (one row Y of sinogram) line := img.SubImage(image.Rect(X, 0, X+1, width)).(*image.Gray) // 3- Do the backprojection and rotate accordingly wideLine := resize.Resize(uint(width), uint(width), line, resize.Lanczos3).(*image.Gray) θ := manipulator.Rad(float64(X)*step) + math.Pi/2 rotatedWideLine := image.NewGray(image.Rect(0, 0, width, width)) err := graphics.Rotate(rotatedWideLine, wideLine, &graphics.RotateOptions{Angle: θ}) if err != nil { return out, err } // 4- Add the rotated backprojection in the output image for x := 0; x < width; x++ { for y := 0; y < width; y++ { point := uint16(out.At(x, y).(color.Gray16).Y) + uint16(rotatedWideLine.At(x, y).(color.Gray).Y) out.Set(x, y, color.Gray16{uint16(point)}) } } } return out, nil }
// GreyscaleDct Computes the Dct of a greyscale image func GreyscaleDct(img image.Gray) uint64 { // func DctImageHashOne(img image.Image) ([][]float64) { R := img.Bounds() N := R.Dx() // width M := R.Dy() // height DCTMatrix := make([][]float64, N) for u := 0; u < N; u++ { DCTMatrix[u] = make([]float64, M) for v := 0; v < M; v++ { DCTMatrix[u][v] = dctPoint(img, u, v, N, M) // fmt.Println( "DCTMatrix[", u, "][", v, "] is ", DCTMatrix[u][v]) } } total := 0.0 for u := 0; u < N/2; u++ { for v := 0; v < M/2; v++ { total += DCTMatrix[u][v] } } total -= DCTMatrix[0][0] avg := total / float64(((N/2)*(M/2))-1) fmt.Println("got average ", avg) var hash uint64 for u := 0; u < N/2; u++ { for v := 0; v < M/2; v++ { hash = hash * 2 if DCTMatrix[u][v] > avg { hash++ } } } return hash }
func meanHeight(g *image.Gray) uint8 { b := g.Bounds() var total uint64 for i := b.Min.X; i < b.Max.X; i++ { for j := b.Min.Y; j < b.Max.Y; j++ { total += uint64(g.GrayAt(i, j).Y) } } return uint8(total / uint64((b.Dx() * b.Dy()))) }
func getAverage(picture *image.Gray) uint8 { var sum uint64 = 0 bounds := picture.Bounds() for x := 0; x < bounds.Dx(); x++ { for y := 0; y < bounds.Dy(); y++ { sum += uint64(picture.At(x, y).(color.Gray).Y) } } return uint8(sum / uint64(bounds.Dx()*bounds.Dy())) }
// Transforms gray input image color depth to the palette defined in p // The algorithm simply assigns the nearest palette color to each pixel, // without distributing error in any way, so expect color banding func ditheringAverage(img *image.Gray, p *color.Palette) { size := img.Bounds() for y := size.Min.Y; y < size.Max.Y; y++ { for x := size.Min.X; x < size.Max.X; x++ { c1 := img.GrayAt(x, y) c2 := p.Convert(c1).(color.Gray) img.SetGray(x, y, c2) } } }
// Interpolate uint8/pixel images. func interpolate1x8(src *image.Gray, dstW, dstH int) image.Image { srcRect := src.Bounds() srcW := srcRect.Dx() srcH := srcRect.Dy() ww, hh := uint64(dstW), uint64(dstH) dx, dy := uint64(srcW), uint64(srcH) n, sum := dx*dy, make([]uint64, dstW*dstH) for y := 0; y < srcH; y++ { pixOffset := src.PixOffset(0, y) for x := 0; x < srcW; x++ { // Get the source pixel. val64 := uint64(src.Pix[pixOffset]) pixOffset++ // Spread the source pixel over 1 or more destination rows. py := uint64(y) * hh for remy := hh; remy > 0; { qy := dy - (py % dy) if qy > remy { qy = remy } // Spread the source pixel over 1 or more destination columns. px := uint64(x) * ww index := (py/dy)*ww + (px / dx) for remx := ww; remx > 0; { qx := dx - (px % dx) if qx > remx { qx = remx } qxy := qx * qy sum[index] += val64 * qxy index++ px += qx remx -= qx } py += qy remy -= qy } } } dst := image.NewGray(image.Rect(0, 0, dstW, dstH)) index := 0 for y := 0; y < dstH; y++ { pixOffset := dst.PixOffset(0, y) for x := 0; x < dstW; x++ { dst.Pix[pixOffset] = uint8(sum[index] / n) pixOffset++ index++ } } return dst }
// grayToY stores the 8x8 region of m whose top-left corner is p in yBlock. func grayToY(m *image.Gray, p image.Point, yBlock *block) { b := m.Bounds() xmax := b.Max.X - 1 ymax := b.Max.Y - 1 pix := m.Pix for j := 0; j < 8; j++ { for i := 0; i < 8; i++ { idx := m.PixOffset(min(p.X+i, xmax), min(p.Y+j, ymax)) yBlock[8*j+i] = int32(pix[idx]) } } }
func GrayImageToMatrix(src image.Gray) (*matrix.Dense, error) { bounds := src.Bounds() mtx := make([][]float64, bounds.Max.X) for x := 0; x < bounds.Max.X; x++ { mtx[x] = make([]float64, bounds.Max.Y) for y := 0; y < bounds.Max.Y; y++ { _, _, b, _ := src.At(x, y).RGBA() mtx[x][y] = float64(b) } } return matrix.NewDense(mtx) }
func tightBounds(m *image.Gray) (r image.Rectangle) { r = m.Bounds() for ; r.Min.Y < r.Max.Y && emptyRow(m, r, r.Min.Y+0); r.Min.Y++ { } for ; r.Min.Y < r.Max.Y && emptyRow(m, r, r.Max.Y-1); r.Max.Y-- { } for ; r.Min.X < r.Max.X && emptyCol(m, r, r.Min.X+0); r.Min.X++ { } for ; r.Min.X < r.Max.X && emptyCol(m, r, r.Max.X-1); r.Max.X-- { } return r }
func toGray(o *ora.ORA, name string) (*image.Gray, error) { var p *image.Gray if l := o.Layer(name); l != nil { p = image.NewGray(o.Bounds()) i, err := l.Image() if err != nil { return nil, err } draw.Draw(p, image.Rect(0, 0, p.Bounds().Max.X, p.Bounds().Max.Y), i, image.Point{}, draw.Src) } return p, nil }
// Fill in missing pixels func interpolateMissingPixels(img image.Gray, mask []bool) { var wg sync.WaitGroup newMask := make([]bool, len(mask), len(mask)) copy(newMask, mask) couldNotFill := false imgWidth := img.Bounds().Max.X imgHeight := img.Bounds().Max.Y for x := 0; x < imgWidth; x++ { wg.Add(1) go func(x int) { for y := 0; y < imgHeight; y++ { imgPos := img.PixOffset(x, y) if mask[imgPos] == true { continue } var sum int cnt := 0 if x > 0 && mask[imgPos-1] { sum += int(img.Pix[imgPos-1]) cnt++ } if x < imgWidth-1 && mask[imgPos+1] { sum += int(img.Pix[imgPos+1]) cnt++ } if y > 0 && mask[imgPos-img.Stride] { sum += int(img.Pix[imgPos-img.Stride]) cnt++ } if y < imgHeight-1 && mask[imgPos+img.Stride] { sum += int(img.Pix[imgPos+img.Stride]) cnt++ } if cnt != 0 { img.Pix[imgPos] = uint8(sum / cnt) newMask[imgPos] = true } else { couldNotFill = true } } wg.Done() }(x) } wg.Wait() if couldNotFill { interpolateMissingPixels(img, newMask) } }
func scaleGray(src *image.Gray, scale uint8) *image.Gray { bounds := src.Bounds() dst := image.NewGray(bounds) copy(dst.Pix, src.Pix) for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { dst.SetGray(x, y, color.Gray{grayAt(src, x, y).Y * scale}) } } return dst }
func GrayImageToMatrix(src image.Gray) (*mat64.Dense, error) { bounds := src.Bounds() rows := bounds.Max.Y cols := bounds.Max.X mtx := make([]float64, rows*cols) for x := 0; x < cols; x++ { for y := 0; y < rows; y++ { _, _, b, _ := src.At(x, y).RGBA() mtx[y*cols+x] = float64(b) } } return mat64.NewDense(rows, cols, mtx), nil }
func average(oldPic *image.Gray) *Picture { bounds := oldPic.Bounds() newWidth := charWidth * (bounds.Dx() / charWidth) newHeight := charHeight * (bounds.Dy() / charHeight) newPic := New(newWidth/charWidth, newHeight/charHeight) for x := 0; x < newWidth-charWidth; x += charWidth { for y := 0; y < newHeight-charHeight; y += charHeight { sum := 0 for i := x; i < x+charWidth; i++ { for j := y; j < y+charHeight; j++ { sum += int(oldPic.At(i, j).(color.Gray).Y) } } newPic.Set(x/charWidth, y/charHeight, uint8(sum/(charWidth*charHeight))) } } return newPic }
//AutoCorrelateProjections calculates a cross-correlation of each radial //projection with itself func AutoCorrelateProjections(projections image.Gray) []float64 { size := projections.Bounds().Size() width := size.X nbProj := size.Y out := make([]float64, nbProj*width) // for each projection for θ := 0; θ < nbProj; θ++ { // log.Println("θ:", θ) left := projections.PixOffset(0, θ) right := projections.PixOffset(width, θ) projection := projections.Pix[left:right] out = append(out, AutoCorrelateSeries(projection)...) } return out }
// floodFill fills 4-connected non-background pixels starting from (x,y) with level. func floodFill(base *image.Gray, level byte, x, y int) image.Rectangle { bbox := image.Rect(x, y, x, y) cur := []int{y*base.Stride + x} for len(cur) > 0 { var pix []int try := func(j int) { if base.Pix[j] != 0 && base.Pix[j] != 254 && base.Pix[j] != level { base.Pix[j] = level pix = append(pix, j) x := j % base.Stride y := j / base.Stride if x < bbox.Min.X { bbox.Min.X = x } if x > bbox.Max.X { bbox.Max.X = x } if y < bbox.Min.Y { bbox.Min.Y = y } if y > bbox.Max.Y { bbox.Max.Y = y } } } for _, i := range cur { if i%base.Stride != 0 { try(i - 1) } if i%base.Stride != base.Stride-1 { try(i + 1) } if i/base.Stride > 0 { try(i - base.Stride) } if i/base.Stride < base.Bounds().Dy()-1 { try(i + base.Stride) } } cur = pix } return bbox }
func (bilinear) Gray(src *image.Gray, x, y float64) color.Gray { p := findLinearSrc(src.Bounds(), x, y) // Array offsets for the surrounding pixels. off00 := offGray(src, p.low.X, p.low.Y) off01 := offGray(src, p.high.X, p.low.Y) off10 := offGray(src, p.low.X, p.high.Y) off11 := offGray(src, p.high.X, p.high.Y) var fc float64 fc += float64(src.Pix[off00]) * p.frac00 fc += float64(src.Pix[off01]) * p.frac01 fc += float64(src.Pix[off10]) * p.frac10 fc += float64(src.Pix[off11]) * p.frac11 var c color.Gray c.Y = uint8(fc + 0.5) return c }
func bilinearGray(src *image.Gray, transform TransformFunc, width, height, borderMethod int) image.Image { dst := image.NewGray(image.Rect(0, 0, width, height)) b := src.Bounds() j := 0 for ydst := 0; ydst < height; ydst++ { for xdst := 0; xdst < width; xdst++ { X, Y := transform(xdst, ydst) X -= 0.5 Y -= 0.5 x := int(X) y := int(Y) if X < 0.0 { x -= 1 } if Y < 0.0 { y -= 1 } // Are all neighbours outside the source image? if x < -1 || y < -1 || x >= b.Dx() || y >= b.Dy() { j++ continue } // Pixel weights dx := X - float64(x) dy := Y - float64(y) // Image boundaries x += b.Min.X y += b.Min.Y // Calculate new color, add 0.5 to round to nearest integer. var G float64 = 0.5 G += float64(getGray(src, x, y, borderMethod)) * (1 - dx) G += float64(getGray(src, x+1, y, borderMethod)) * dx G *= (1 - dy) var g float64 = 0 g += float64(getGray(src, x, y+1, borderMethod)) * (1 - dx) g += float64(getGray(src, x+1, y+1, borderMethod)) * dx G += g * dy dst.Pix[j] = uint8(G) j++ } } return dst }
// encode image.Gray func encodeGray(cinfo *C.struct_jpeg_compress_struct, src *image.Gray, p *EncoderOptions) (err error) { // Set up compression parameters cinfo.image_width = C.JDIMENSION(src.Bounds().Dx()) cinfo.image_height = C.JDIMENSION(src.Bounds().Dy()) cinfo.input_components = 1 cinfo.in_color_space = C.JCS_GRAYSCALE C.jpeg_set_defaults(cinfo) setupEncoderOptions(cinfo, p) compInfo := (*C.jpeg_component_info)(unsafe.Pointer(cinfo.comp_info)) compInfo.h_samp_factor, compInfo.v_samp_factor = 1, 1 // libjpeg raw data in is in planar format, which avoids unnecessary // planar->packed->planar conversions. cinfo.raw_data_in = C.TRUE // Start compression C.jpeg_start_compress(cinfo, C.TRUE) // Allocate JSAMPIMAGE to hold pointers to one iMCU worth of image data // this is a safe overestimate; we use the return value from // jpeg_read_raw_data to figure out what is the actual iMCU row count. var rowPtr [AlignSize]C.JSAMPROW arrayPtr := [1]C.JSAMPARRAY{ C.JSAMPARRAY(unsafe.Pointer(&rowPtr[0])), } var rows C.JDIMENSION for rows = 0; rows < cinfo.image_height; { // First fill in the pointers into the plane data buffers for j := 0; j < int(C.DCTSIZE*compInfo.v_samp_factor); j++ { rowPtr[j] = C.JSAMPROW(unsafe.Pointer(&src.Pix[src.Stride*(int(rows)+j)])) } // Get the data rows += C.jpeg_write_raw_data(cinfo, C.JSAMPIMAGE(unsafe.Pointer(&arrayPtr[0])), C.JDIMENSION(C.DCTSIZE*compInfo.v_samp_factor)) } C.jpeg_finish_compress(cinfo) return }
func resize1x8(src *image.Gray, dstW, dstH int) image.Image { srcRect := src.Bounds() srcW := srcRect.Dx() srcH := srcRect.Dy() dstW64, dstH64 := uint64(dstW), uint64(dstH) srcW64, srcH64 := uint64(srcW), uint64(srcH) dst := image.NewGray(image.Rect(0, 0, dstW, dstH)) var x, y uint64 dstI := 0 for y = 0; y < dstH64; y++ { srcY := int(y * srcH64 / dstH64) for x = 0; x < dstW64; x++ { srcX := int(x * srcW64 / dstW64) dst.Pix[dstI] = src.Pix[srcY*srcW+srcX] dstI++ } } return dst }