//ProjectGray returns a greyscale sinogram (or radon projection) of img // N(default: 360): number of image rotation on which a projection will be done // A naive simplistic approach // Sinograms looks like this : // θ1 θ2 θ3...θN // | | | | // | | | | func ProjectGray(src image.Image, N int) (*image.Gray, error) { if N == 0 { N = 360 } step := 180.0 / float64(N) size := src.Bounds().Size() overX := int(float64(size.X) * 1.1) overY := int(float64(size.Y) * 1.1) var img image.Image = image.NewGray(image.Rect(0, 0, size.X+overX, size.Y+overY)) img = imaging.Overlay(img, src, image.Pt(overX/2, overY/2), 1) size = img.Bounds().Size() D := max(size.X, size.Y) out := image.NewGray(image.Rect(0, 0, N, D)) // for each given angle θ for n := 0; n < N; n++ { θ := float64(n) * step draw := image.NewRGBA(image.Rect(0, 0, img.Bounds().Dy(), img.Bounds().Dx())) //have a duplicate img rotated by θ err := graphics.Rotate(draw, img, &graphics.RotateOptions{Angle: manipulator.Rad(θ)}) if err != nil { return out, err } sinogram := make([]float64, size.X) // get column average profile for y := 0; y < size.Y; y++ { for x := 0; x < size.X; x++ { greyColor, _ := color.GrayModel.Convert(draw.At(x, y)).(color.Gray) sinogram[x] = sinogram[x] + float64(greyColor.Y) } } //Set out line with sinogram for d := 0; d < D; d++ { out.Set(n, d, color.Gray{uint8(sinogram[d] / float64(size.Y))}) } } return out, nil }
//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 }