Beispiel #1
0
func newAtFuncGray(p *image.Gray) AtFunc {
	return func(x, y int) (r, g, b, a uint32) {
		i := p.PixOffset(x, y)
		yy := uint32(p.Pix[i])
		yy |= yy << 8
		return yy, yy, yy, 0xffff
	}
}
Beispiel #2
0
// 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
}
Beispiel #3
0
// 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])
		}
	}
}
Beispiel #4
0
// 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)
	}
}
Beispiel #5
0
//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
}
Beispiel #6
0
func meanVertical(src image.Gray, radius int) (dst image.Gray) {
	var wg sync.WaitGroup
	norm := float64(radius*2 + 1)
	dst = *image.NewGray(src.Bounds())
	width, height := src.Bounds().Max.X, src.Bounds().Max.Y

	for x := 0; x < width; x++ {
		wg.Add(1)
		go func(x int) {

			total := 0.0

			for ky := -radius; ky <= radius; ky++ {
				total += float64(src.Pix[src.PixOffset(x, inRange(ky, height))])
			}
			dst.Pix[dst.PixOffset(x, 0)] = uint8(total / norm)

			for y := 1; y < height; y++ {
				total -= float64(src.Pix[src.PixOffset(x, inRange(y-radius-1, height))])
				total += float64(src.Pix[src.PixOffset(x, inRange(y+radius, height))])

				dst.Pix[dst.PixOffset(x, y)] = uint8(total / norm)
			}
			wg.Done()
		}(x)
	}
	wg.Wait()
	return
}
Beispiel #7
0
func meanHorizontal(src image.Gray, radius int) (dst image.Gray) {
	var wg sync.WaitGroup
	norm := float64(radius*2 + 1)
	dst = *image.NewGray(src.Bounds())
	width, height := src.Bounds().Max.X, src.Bounds().Max.Y

	for y := 0; y < height; y++ {
		wg.Add(1)
		go func(y int) {
			total := 0.0

			for kx := -radius; kx <= radius; kx++ {
				total += float64(src.Pix[src.PixOffset(inRange(kx, width), y)])
			}
			dst.Pix[dst.PixOffset(0, y)] = uint8(total / norm)

			for x := 1; x < width; x++ {
				total -= float64(src.Pix[src.PixOffset(inRange(x-radius-1, width), y)])
				total += float64(src.Pix[src.PixOffset(inRange(x+radius, width), y)])

				dst.Pix[dst.PixOffset(x, y)] = uint8(total / norm)
			}
			wg.Done()
		}(y)
	}
	wg.Wait()
	return
}
Beispiel #8
0
func evaluateGrids(src image.Gray, grids []lineGrid) []lineGrid {
	for _, grid := range grids {
		hCount := len(grid.Horizontal)
		vCount := len(grid.Vertical)
		fragments := make([]lineFragment, hCount+vCount)

		firstVertLine := grid.Vertical[0]
		lastVertLine := grid.Vertical[vCount-1]
		for j, h := range grid.Horizontal {
			_, start := intersection(h, firstVertLine)
			_, end := intersection(h, lastVertLine)
			fragments[j] = lineFragment{start, end}
		}

		firstHorizLine := grid.Horizontal[0]
		lastHorizLine := grid.Horizontal[hCount-1]
		for j, h := range grid.Vertical {
			_, start := intersection(h, firstHorizLine)
			_, end := intersection(h, lastHorizLine)
			fragments[hCount+j] = lineFragment{start, end}
		}

		score := 0.0
		for _, fragment := range fragments {
			points := pointsOnLineFragment(fragment)
			value := 1.0 / fragment.Length()
			for _, point := range points {
				if src.Pix[src.PixOffset(point.X, point.Y)] != 0 {
					score += value
				}
			}
		}
		grid.Score = grid.Score * score / float64(len(fragments))
	}

	sort.Sort(lineGridByScore(grids))

	return grids
}
Beispiel #9
0
// copies and resizes src into dst's r
func scaleDownTo(dst *image.Gray, r image.Rectangle, src *image.Gray) error {
	if src.Bounds().Dx()%r.Dx() != 0 || src.Bounds().Dy()%r.Dy() != 0 {
		return errors.New("Source image size not multiple of rectagle size")
	}

	dx := src.Bounds().Dx() / r.Dx()
	dy := src.Bounds().Dy() / r.Dy()
	area := dx * dy
	for y := r.Min.Y; y < r.Max.Y; y++ {
		for x := r.Min.X; x < r.Max.X; x++ {
			col := 0
			for v := 0; v < dy; v++ {
				for u := 0; u < dx; u++ {
					offset := src.PixOffset((x-r.Min.X)*dx+u, (y-r.Min.Y)*dy+v)
					col += int(src.Pix[offset])
				}
			}
			dst.SetGray(x, y, color.Gray{uint8(col / area)})
		}
	}
	return nil
}
Beispiel #10
0
func (p *perspectiveTrasnformation) warpPerspective(src image.Gray) image.Gray {
	var wg sync.WaitGroup
	maxX := 0.0
	maxY := 0.0
	for _, p := range p.dstPoints {
		maxX = math.Max(maxX, p.X)
		maxY = math.Max(maxY, p.Y)
	}
	dst := *image.NewGray(image.Rect(0, 0, int(maxX), int(maxY)))
	mask := make([]bool, len(dst.Pix), len(dst.Pix))

	srcWidth := src.Bounds().Max.X
	srcHeight := src.Bounds().Max.Y
	for x := 0; x < srcWidth; x++ {
		wg.Add(1)
		go func(x int) {
			for y := 0; y < srcHeight; y++ {
				newX, newY := p.Project(float64(x), float64(y))
				if newX < 0 || newX >= maxX || newY < 0 || newY >= maxY {
					continue
				}

				g := src.Pix[src.PixOffset(x, y)]

				dstPos := dst.PixOffset(int(newX), int(newY))
				dst.Pix[dstPos] = g
				mask[dstPos] = true
			}
			wg.Done()
		}(x)
	}

	wg.Wait()
	interpolateMissingPixels(dst, mask)
	return dst
}
Beispiel #11
0
func sdfize(img *image.Gray) {
	w := img.Bounds().Size().X
	h := img.Bounds().Size().Y
	g1 := make([]xys, w*h)
	g2 := make([]xys, w*h)

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			a := img.Pix[img.PixOffset(x, y)]
			if a < 0x7F {
				g2[y*w+x] = infinity
			} else {
				g1[y*w+x] = infinity
			}
		}
	}

	generateSDF(int16(w), int16(h), g1)
	generateSDF(int16(w), int16(h), g2)

	for y := 0; y < h; y++ {
		for x := 0; x < w; x++ {
			pos := y*w + x
			dist1 := distance(g1[pos].x, g1[pos].y)
			dist2 := distance(g2[pos].x, g2[pos].y)
			dist := 2 * 128 * (dist1 - dist2) / float64(w)
			if dist < -128 {
				dist = -128
			} else if dist > 127 {
				dist = 127
			}
			c := uint8(128 + dist)
			img.SetGray(x, y, color.Gray{c})
		}
	}
}
Beispiel #12
0
// Scale the source image down to the destination image.
// Preserves aspect ratio, leaving unused destination pixels untouched.
// At present it just uses a simple box filter.
// It might be possible to improve performance and clarity by making all
// pixel fractions 1/16 and using essentially fixed-point arithmetic.
func scaleDown(src SippImage, dst *image.Gray) {
	srcRect := src.Bounds()
	dstRect := dst.Bounds()
	//fmt.Println("srcRect:<", srcRect, ">")
	//fmt.Println("dstRect:<", dstRect, ">")

	srcWidth := srcRect.Dx()
	srcHeight := srcRect.Dy()
	dstWidth := dstRect.Dx()
	dstHeight := dstRect.Dy()
	//fmt.Println("srcWidth:<", srcWidth, ">")
	//fmt.Println("srcHeight:<", srcHeight, ">")
	//fmt.Println("dstWidth:<", dstWidth, ">")
	//fmt.Println("dstHeight:<", dstHeight, ">")

	srcAR := float64(srcWidth) / float64(srcHeight)
	dstAR := float64(dstWidth) / float64(dstHeight)
	//fmt.Println("srcAR:<", srcAR, ">")
	//fmt.Println("dstAR:<", dstAR, ">")

	var scale float64
	var outWidth int
	var outHeight int
	if srcAR < dstAR {
		// scale vertically and use a horizontal offset
		scale = float64(srcHeight) / float64(dstHeight)
		outWidth = int(float64(srcWidth) / scale)
		outHeight = dstHeight
		//fmt.Println("Scaling vertically")
	} else {
		// scale horizontally and use a vertical offset
		scale = float64(srcWidth) / float64(dstWidth)
		outHeight = int(float64(srcHeight) / scale)
		outWidth = dstWidth
		//fmt.Println("Scaling horizontally")
	}
	//fmt.Println("scale:<", scale, ">")
	//fmt.Println("outWidth:<", outWidth, ">")
	//fmt.Println("outHeight:<", outHeight, ">")

	// One of the following will be 0.
	hoff := (dstWidth - outWidth) / 2
	voff := (dstHeight - outHeight) / 2

	// Scale 16-bit images down to 8. We incour the cost spuriously for 8-bit
	// images so that we can access the source polymorphically.
	var scaleBpp float64 = 1.0
	if src.Bpp() == 16 {
		scaleBpp = 1.0 / 256.0
	}

	//fmt.Println("scaleBpp:", scaleBpp)

	hfilter := preComputeFilter(scale, outWidth, srcWidth, scaleBpp)

	intrm := image.NewGray(image.Rect(0, 0, outWidth, srcHeight))

	for inty := 0; inty < srcHeight; inty++ {
		// Apply the filter to the source row, generating an intermediate row
		for intx := 0; intx < outWidth; intx++ {
			var val float64
			for i := 0; i < hfilter[intx].n; i++ {
				val = val + src.Val(hfilter[intx].idx+i, inty)*hfilter[intx].weights[i]
			}
			val = math.Floor(val + 0.5)
			if val > 255.0 {
				intrm.Pix[intrm.PixOffset(intx, inty)] = 255
			} else {
				intrm.Pix[intrm.PixOffset(intx, inty)] = uint8(val)
			}
		}
	}

	vfilter := preComputeFilter(scale, outHeight, srcHeight, 1.0)

	for outx := 0; outx < outWidth; outx++ {
		// Apply the filter to the intermediate column, generating an output column
		for outy := 0; outy < outHeight; outy++ {
			var val float64
			for i := 0; i < vfilter[outy].n; i++ {
				index := intrm.PixOffset(outx, vfilter[outy].idx+i)
				val = val + float64(intrm.Pix[index])*vfilter[outy].weights[i]
			}
			dst.Pix[dst.PixOffset(outx+hoff, outy+voff)] = uint8(math.Floor(val + 0.5))
		}
	}
}
Beispiel #13
0
func newSetFuncGray(p *image.Gray) SetFunc {
	return func(x, y int, r, g, b, a uint32) {
		i := p.PixOffset(x, y)
		p.Pix[i] = uint8(((299*r + 587*g + 114*b + 500) / 1000) >> 8)
	}
}
Beispiel #14
0
func houghLines(src image.Gray, thetas []float64, threshold uint64, limit int) []polarLine {
	if thetas == nil {
		thetas = generateThetas(-math.Pi/2, math.Pi/2, math.Pi/180.0)
	}
	maxY, maxX := src.Bounds().Max.Y, src.Bounds().Max.X
	maxR := 2 * math.Hypot(float64(maxX), float64(maxY))
	offset := maxR / 2

	hAcc := make([][]uint64, int(maxR), int(maxR))
	for i := range hAcc {
		hAcc[i] = make([]uint64, len(thetas), len(thetas))
	}

	sin := make([]float64, len(thetas), len(thetas))
	cos := make([]float64, len(thetas), len(thetas))
	for i, th := range thetas {
		sin[i] = math.Sin(th)
		cos[i] = math.Cos(th)
	}

	var wg sync.WaitGroup
	for y := 0; y < maxY; y++ {
		wg.Add(1)
		go func(y int) {
			for x := 0; x < maxX; x++ {
				val := src.Pix[src.PixOffset(x, y)]
				if val == 0 {
					continue
				}

				for i := range thetas {
					r := float64(x)*cos[i] + float64(y)*sin[i]
					iry := int(r + offset)
					atomic.AddUint64(&hAcc[iry][i], 1)
				}
			}
			wg.Done()
		}(y)
	}
	wg.Wait()

	linesSet := make(map[string]bool)
	var lines []polarLine
	for i := range hAcc {
		r := i - int(offset)
		thetaOffset := 0.0
		if r < 0 {
			thetaOffset = math.Pi
			r *= -1
		}
		for j, count := range hAcc[i] {
			if count < 2 || count < threshold {
				continue
			}

			line := polarLine{
				Theta:    thetas[j] + thetaOffset,
				Distance: r,
				Count:    count,
			}
			hash := line.HashKey()
			if !linesSet[hash] {
				linesSet[hash] = true
				lines = append(lines, line)
			}
		}
	}

	sort.Sort(polarLinesByCount(lines))

	if limit > 0 && len(lines) > limit {
		lines = lines[:limit]
	}

	return lines
}