コード例 #1
1
ファイル: radon.go プロジェクト: verisart/phash
//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
}
コード例 #2
0
ファイル: images.go プロジェクト: nkhuyu/htc2015
func loadImageRotated(image_name string, angle float64) (*sdl.Surface, error) {
	file, err := assets.Asset(image_name)
	if err != nil {
		return nil, err
	}
	i, _, err := image.Decode(bytes.NewReader(file))
	if err != nil {
		return nil, err
	}

	dst := image.NewRGBA(i.Bounds())
	err = graphics.Rotate(dst, i, &graphics.RotateOptions{
		Angle: angle * tau})
	if err != nil {
		return nil, err
	}

	var buf bytes.Buffer
	err = png.Encode(&buf, dst)
	if err != nil {
		return nil, err
	}
	b := buf.Bytes()

	return img.Load_RW(sdl.RWFromMem(unsafe.Pointer(&b[0]), len(b)), 0)
}
コード例 #3
0
ファイル: radon.go プロジェクト: verisart/phash
//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
}
コード例 #4
0
ファイル: screen.go プロジェクト: meishichao/airinput
func SnapshotRotated() (dst *image.RGBA, err error) {
	img, err := Snapshot()
	if err != nil {
		return
	}
	rotate := Rotation()
	options := &graphics.RotateOptions{math.Pi * float64(4-rotate) / 2.0}
	if rotate%2 != 0 {
		dst = image.NewRGBA(image.Rect(0, 0, img.Bounds().Dy(), img.Bounds().Dx()))
	} else {
		dst = image.NewRGBA(img.Bounds())
	}
	err = graphics.Rotate(dst, img, options)
	if err != nil {
		return
	}
	return
}
コード例 #5
0
ファイル: proxy.go プロジェクト: josharian/goofyproxy
func flipImage(body []byte) []byte {
	// Is it an image?
	img, typ, err := image.Decode(bytes.NewReader(body))
	if err != nil {
		return body
	}
	dst := image.NewRGBA(img.Bounds())
	graphics.Rotate(dst, img, &graphics.RotateOptions{Angle: math.Pi})
	var buf bytes.Buffer
	switch typ {
	case "png":
		err = png.Encode(&buf, dst)
	case "jpeg":
		err = jpeg.Encode(&buf, dst, nil)
	case "gif":
		err = gif.Encode(&buf, dst, nil)
	}
	if err != nil || buf.Len() == 0 {
		return body
	}
	body = buf.Bytes()
	return body
}
コード例 #6
0
ファイル: convey_test.go プロジェクト: postfix/phash-1
func getImgBag(dir, filename string, angle Angle) *ImageBag {
	img, found := testImages[dir+filename]
	if !found {
		testImages[dir+filename] = new(ImageBag)
		testImages[dir+filename].Dir = dir
		testImages[dir+filename].Filename = filename
		testImages[dir+filename].Rotations = map[Angle]*ImageBag{}
		testImages[dir+filename].InitialiseFromFileInfo()
		img = testImages[dir+filename]
	}

	if angle != 0 {
		rotatedImage, found := img.Rotations[angle]
		if !found {
			draw := manipulator.CopyImage(img.Radon.Image)
			err := graphics.Rotate(draw, img.Radon.Image, &graphics.RotateOptions{Angle: float64(angle)})
			if err != nil {
				panic(err)
			}
			rImg := ImageBag{
				Dir:      dir,
				Filename: filename,
				Angle:    angle,
				ImageDigest: phash.ImageDigest{
					Radon: radon.ImageDigest{
						Image:  draw,
						Format: img.Radon.Format}}}
			img.Rotations[angle] = &rImg

			return img.Rotations[angle]
		}
		return rotatedImage
	}

	return img
}
コード例 #7
0
ファイル: file.go プロジェクト: kitgary/platform
func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, channelId, userId string) {

	go func() {
		dest := "teams/" + teamId + "/channels/" + channelId + "/users/" + userId + "/"

		for i, filename := range filenames {
			name := filename[:strings.LastIndex(filename, ".")]
			go func() {
				// Decode image bytes into Image object
				img, _, err := image.Decode(bytes.NewReader(fileData[i]))
				if err != nil {
					l4g.Error("Unable to decode image channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
					return
				}

				width := img.Bounds().Dx()
				height := img.Bounds().Dy()

				// Get the image's orientation and ignore any errors since not all images will have orientation data
				orientation, _ := getImageOrientation(fileData[i])

				// Create a temporary image that will be manipulated and then used to make the thumbnail and preview image
				var temp *image.RGBA
				switch orientation {
				case Upright, UprightMirrored, UpsideDown, UpsideDownMirrored:
					temp = image.NewRGBA(img.Bounds())
				case RotatedCCW, RotatedCCWMirrored, RotatedCW, RotatedCWMirrored:
					bounds := img.Bounds()
					temp = image.NewRGBA(image.Rect(bounds.Min.Y, bounds.Min.X, bounds.Max.Y, bounds.Max.X))

					width, height = height, width
				}

				// Draw a white background since JPEGs lack transparency
				draw.Draw(temp, temp.Bounds(), image.NewUniform(color.White), image.Point{}, draw.Src)

				// Copy the original image onto the temporary one while rotating it as necessary
				switch orientation {
				case UpsideDown, UpsideDownMirrored:
					// rotate 180 degrees
					err := graphics.Rotate(temp, img, &graphics.RotateOptions{Angle: math.Pi})
					if err != nil {
						l4g.Error("Unable to rotate image")
					}
				case RotatedCW, RotatedCWMirrored:
					// rotate 90 degrees CCW
					graphics.Rotate(temp, img, &graphics.RotateOptions{Angle: 3 * math.Pi / 2})
					if err != nil {
						l4g.Error("Unable to rotate image")
					}
				case RotatedCCW, RotatedCCWMirrored:
					// rotate 90 degrees CW
					graphics.Rotate(temp, img, &graphics.RotateOptions{Angle: math.Pi / 2})
					if err != nil {
						l4g.Error("Unable to rotate image")
					}
				case Upright, UprightMirrored:
					draw.Draw(temp, temp.Bounds(), img, img.Bounds().Min, draw.Over)
				}

				img = temp

				// Create thumbnail
				go func() {
					thumbWidth := float64(utils.Cfg.ImageSettings.ThumbnailWidth)
					thumbHeight := float64(utils.Cfg.ImageSettings.ThumbnailHeight)
					imgWidth := float64(width)
					imgHeight := float64(height)

					var thumbnail image.Image
					if imgHeight < thumbHeight && imgWidth < thumbWidth {
						thumbnail = img
					} else if imgHeight/imgWidth < thumbHeight/thumbWidth {
						thumbnail = resize.Resize(0, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.Lanczos3)
					} else {
						thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, 0, img, resize.Lanczos3)
					}

					buf := new(bytes.Buffer)
					err = jpeg.Encode(buf, thumbnail, &jpeg.Options{Quality: 90})
					if err != nil {
						l4g.Error("Unable to encode image as jpeg channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}

					if err := writeFile(buf.Bytes(), dest+name+"_thumb.jpg"); err != nil {
						l4g.Error("Unable to upload thumbnail channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}
				}()

				// Create preview
				go func() {
					var preview image.Image
					if width > int(utils.Cfg.ImageSettings.PreviewWidth) {
						preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.Lanczos3)
					} else {
						preview = img
					}

					buf := new(bytes.Buffer)

					err = jpeg.Encode(buf, preview, &jpeg.Options{Quality: 90})
					if err != nil {
						l4g.Error("Unable to encode image as preview jpg channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}

					if err := writeFile(buf.Bytes(), dest+name+"_preview.jpg"); err != nil {
						l4g.Error("Unable to upload preview channelId=%v userId=%v filename=%v err=%v", channelId, userId, filename, err)
						return
					}
				}()
			}()
		}
	}()
}
コード例 #8
0
ファイル: rotate.go プロジェクト: verticalpalette/dandubois
// rotate rotates the Painting's image counter-clockwise by angle in degrees.
// angle modulo 360 must be one of 0, 90, 180, 270.
func (p *Painting) rotate(c appengine.Context, angle int) error {
	switch math.Abs(float64(angle % 360)) {
	case 0, 90, 180, 270:
		break
	default:
		return errors.New(fmt.Sprintf("painting: Unsupported angle %f.", angle))
	}

	if p.Image == (Image{}) {
		return nil
	}

	// Read the image from the blobstore.
	r := blobstore.NewReader(c, p.Image.BlobKey)
	src, _, err := image.Decode(r)
	if err != nil {
		return err
	}

	// Create the rotated image.
	srcRect := src.Bounds()
	var dstRect image.Rectangle
	if angle == 0 || angle == 180 {
		dstRect = srcRect
	} else {
		dstRect = image.Rect(0, 0, srcRect.Dy(), srcRect.Dx())
	}

	dst := image.NewNRGBA(dstRect)
	err = graphics.Rotate(dst, src, &graphics.RotateOptions{
		Angle: float64(angle%360) * math.Pi / 180,
	})
	if err != nil {
		return err
	}

	// Create a new blob for the rotated image.
	w, err := blobstore.Create(c, "image/png")
	if err != nil {
		return err
	}

	err = png.Encode(w, dst)
	if err != nil {
		return err
	}

	err = w.Close()
	if err != nil {
		return err
	}

	// Delete the old blob.
	deleteBlobLater.Call(c, p.Image.BlobKey)

	// Update the image metadata.
	p.Image.BlobKey, err = w.Key()
	if err != nil {
		return err
	}

	p.Image.Width = dstRect.Dx()
	p.Image.Height = dstRect.Dy()

	u, err := aeimage.ServingURL(c, p.Image.BlobKey, nil)
	if err != nil {
		return err
	}
	p.Image.URL = u.String()

	err = p.Save(c)
	if err != nil {
		return err
	}

	return nil
}
コード例 #9
0
ファイル: image.go プロジェクト: rbberry/chart
func (ig *ImageGraphics) Text(x, y int, t string, align string, rot int, f chart.Font) {
	if len(align) == 1 {
		align = "c" + align
	}

	textImage := ig.textBox(t, f)
	bounds := textImage.Bounds()
	w, h := bounds.Dx(), bounds.Dy()
	var centerX, centerY int

	if rot != 0 {
		alpha := float64(rot) / 180 * math.Pi
		cos := math.Cos(alpha)
		sin := math.Sin(alpha)
		hs, hc := float64(h)*sin, float64(h)*cos
		ws, wc := float64(w)*sin, float64(w)*cos
		W := int(math.Ceil(hs + wc))
		H := int(math.Ceil(hc + ws))
		rotated := image.NewAlpha(image.Rect(0, 0, W, H))
		graphics.Rotate(rotated, textImage, &graphics.RotateOptions{-alpha})
		textImage = rotated
		centerX, centerY = W/2, H/2

		switch align {
		case "bl":
			centerX, centerY = int(hs), H
		case "bc":
			centerX, centerY = W-int(wc/2), int(ws/2)
		case "br":
			centerX, centerY = W, int(hc)
		case "tl":
			centerX, centerY = 0, H-int(hc)
		case "tc":
			centerX, centerY = int(ws/2), H-int(ws/2)
		case "tr":
			centerX, centerY = W-int(hs), 0
		case "cl":
			centerX, centerY = int(hs/2), H-int(hc/2)
		case "cr":
			centerX, centerY = W-int(hs/2), int(hc/2)
		}
	} else {
		centerX, centerY = w/2, h/2
		switch align[0] {
		case 'b':
			centerY = h
		case 't':
			centerY = 0
		}
		switch align[1] {
		case 'l':
			centerX = 0
		case 'r':
			centerX = w
		}
	}

	bounds = textImage.Bounds()
	w, h = bounds.Dx(), bounds.Dy()
	x -= centerX
	y -= centerY
	x += ig.x0
	y += ig.y0

	var col color.Color
	if f.Color != nil {
		col = f.Color
	} else {
		col = color.NRGBA{0, 0, 0, 0xff}
	}
	tcol := image.NewUniform(col)

	draw.DrawMask(ig.Image, image.Rect(x, y, x+w, y+h), tcol, image.ZP,
		textImage, textImage.Bounds().Min, draw.Over)
}
コード例 #10
0
ファイル: image.go プロジェクト: ajstarks/chart
func (ig *ImageGraphics) Text(x, y int, t string, align string, rot int, f chart.Font) {
	if len(align) == 1 {
		align = "c" + align
	}
	// fw, fh, _ := ig.FontMetrics(f)
	//fmt.Printf("Text '%s' at (%d,%d) %s\n", t, x,y, align)
	// TODO: handle rot

	size := ig.relFontsizeToPixel(f.Size)
	textImage := ig.textBox(t, size)
	bounds := textImage.Bounds()
	w, h := bounds.Dx(), bounds.Dy()
	var centerX, centerY int

	if rot != 0 {
		alpha := float64(rot) / 180 * math.Pi
		cos := math.Cos(alpha)
		sin := math.Sin(alpha)
		hs, hc := float64(h)*sin, float64(h)*cos
		ws, wc := float64(w)*sin, float64(w)*cos
		W := int(math.Ceil(hs + wc))
		H := int(math.Ceil(hc + ws))
		rotated := image.NewAlpha(image.Rect(0, 0, W, H))
		graphics.Rotate(rotated, textImage, &graphics.RotateOptions{-alpha})
		textImage = rotated
		centerX, centerY = W/2, H/2

		switch align {
		case "bl":
			centerX, centerY = int(hs), H
		case "bc":
			centerX, centerY = W-int(wc/2), int(ws/2)
		case "br":
			centerX, centerY = W, int(hc)
		case "tl":
			centerX, centerY = 0, H-int(hc)
		case "tc":
			centerX, centerY = int(ws/2), H-int(ws/2)
		case "tr":
			centerX, centerY = W-int(hs), 0
		case "cl":
			centerX, centerY = int(hs/2), H-int(hc/2)
		case "cr":
			centerX, centerY = W-int(hs/2), int(hc/2)
		}
	} else {
		centerX, centerY = w/2, h/2
		switch align[0] {
		case 'b':
			centerY = h
		case 't':
			centerY = 0
		}
		switch align[1] {
		case 'l':
			centerX = 0
		case 'r':
			centerX = w
		}
	}

	bounds = textImage.Bounds()
	w, h = bounds.Dx(), bounds.Dy()
	x -= centerX
	y -= centerY
	x += ig.x0
	y += ig.y0

	col := "#000000"
	if f.Color != "" {
		col = f.Color
	}
	r, g, b := chart.Color2rgb(col)
	tcol := image.NewUniform(color.RGBA{uint8(r), uint8(g), uint8(b), 255})

	draw.DrawMask(ig.Image, image.Rect(x, y, x+w, y+h), tcol, image.ZP,
		textImage, textImage.Bounds().Min, draw.Over)
}
コード例 #11
0
ファイル: app.go プロジェクト: ohlinux/photoshare
func (c Application) PostUpload(name string) rev.Result {
	c.Validation.Required(name)

	if c.Validation.HasErrors() {
		c.FlashParams()
		c.Validation.Keep()
		return c.Redirect(Application.Upload)
	}

	photoDir := path.Join(PHOTO_DIRECTORY, name)
	thumbDir := path.Join(PHOTO_DIRECTORY, "thumbs", name)
	err := os.MkdirAll(photoDir, 0777)
	if err != nil {
		c.FlashParams()
		c.Flash.Error("Error making directory:", err)
		return c.Redirect(Application.Upload)
	}
	err = os.MkdirAll(thumbDir, 0777)
	if err != nil {
		c.FlashParams()
		c.Flash.Error("Error making directory:", err)
		return c.Redirect(Application.Upload)
	}

	photos := c.Params.Files["photos[]"]
	for _, photoFileHeader := range photos {
		// Open the photo.
		input, err := photoFileHeader.Open()
		if err != nil {
			c.FlashParams()
			c.Flash.Error("Error opening photo:", err)
			return c.Redirect(Application.Upload)
		}

		photoBytes, err := ioutil.ReadAll(input)
		if err != nil || len(photoBytes) == 0 {
			rev.ERROR.Println("Failed to read image:", err)
			continue
		}
		input.Close()

		// Decode the photo.
		photoImage, format, err := image.Decode(bytes.NewReader(photoBytes))
		if err != nil {
			rev.ERROR.Println("Failed to decode image:", err)
			continue
		}

		// Decode the EXIF data
		x, err := exif.Decode(bytes.NewReader(photoBytes))
		if err != nil {
			rev.ERROR.Println("Failed to decode image exif:", err)
			continue
		}

		var orientation int = 1
		if orientationTag, err := x.Get(exif.Orientation); err == nil {
			orientation = int(orientationTag.Int(0))
		}

		photoName := path.Base(photoFileHeader.Filename)

		// Create a thumbnail
		thumbnail := image.NewRGBA(image.Rect(0, 0, 256, 256))
		err = graphics.Thumbnail(thumbnail, photoImage)
		if err != nil {
			rev.ERROR.Println("Failed to create thumbnail:", err)
			continue
		}

		// If the EXIF said to, rotate the thumbnail.
		// TODO: maintain the EXIF in the thumb instead.
		if orientation != 1 {
			if angleRadians, ok := ORIENTATION_ANGLES[orientation]; ok {
				rotatedThumbnail := image.NewRGBA(image.Rect(0, 0, 256, 256))
				err = graphics.Rotate(rotatedThumbnail, thumbnail, &graphics.RotateOptions{Angle: angleRadians})
				if err != nil {
					rev.ERROR.Println("Failed to rotate:", err)
				} else {
					thumbnail = rotatedThumbnail
				}
			}
		}

		thumbnailFile, err := os.Create(path.Join(thumbDir, photoName))
		if err != nil {
			c.FlashParams()
			c.Flash.Error("Error creating file:", err)
			return c.Redirect(Application.Upload)
		}

		err = jpeg.Encode(thumbnailFile, thumbnail, nil)
		if err != nil {
			c.FlashParams()
			c.Flash.Error("Failed to save thumbnail:", err)
			return c.Redirect(Application.Upload)
		}

		// Save the photo
		output, err := os.Create(path.Join(photoDir, photoName))
		if err != nil {
			c.FlashParams()
			c.Flash.Error("Error creating file:", err)
			return c.Redirect(Application.Upload)
		}

		_, err = io.Copy(output, bytes.NewReader(photoBytes))
		output.Close()
		if err != nil {
			c.FlashParams()
			c.Flash.Error("Error writing photo:", err)
			return c.Redirect(Application.Upload)
		}

		var taken time.Time
		if takenTag, err := x.Get("DateTimeOriginal"); err == nil {
			taken, err = time.Parse("2006:01:02 15:04:05", takenTag.StringVal())
			if err != nil {
				rev.ERROR.Println("Failed to parse time:", takenTag.StringVal(), ":", err)
			}
		}

		// Save a record of the photo to our database.
		rect := photoImage.Bounds()
		photo := models.Photo{
			Username: name,
			Format:   format,
			Name:     photoName,
			Width:    rect.Max.X - rect.Min.X,
			Height:   rect.Max.Y - rect.Min.Y,
			Uploaded: time.Now(),
			Taken:    taken,
		}

		c.Txn.Insert(&photo)
	}

	c.Flash.Success("%d photos uploaded.", len(photos))
	return c.Redirect(Application.View)
}
コード例 #12
0
ファイル: greenyfy.go プロジェクト: coel/greenyfy
func do(c appengine.Context, key string) (*bytes.Buffer, error) {

	client := urlfetch.Client(c)
	resp, err := client.Get(key)

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	c.Infof("HTTP GET returned status %v", resp.Status)

	img, _, err := image.Decode(resp.Body)
	if err != nil {
		return nil, err
	}

	bnds := img.Bounds()
	if bnds.Dx() > 1024 {
		c.Infof("Resizing image", bnds.Dx())
		img = resize.Resize(1024, 0, img, resize.Lanczos3)
	}

	faces, err := findFaces(c, &img)
	// todo: should I pass back by reference?
	if err != nil {
		return nil, err
	}

	bnds = img.Bounds()

	m := image.NewRGBA(image.Rect(0, 0, bnds.Dx(), bnds.Dy()))

	draw.Draw(m, bnds, img, image.Point{0, 0}, draw.Src)

	brd, err := getBeardCached(c)
	if err != nil {
		return nil, err
	}

	for _, face := range faces {
		brd_resized := resize.Resize(uint(face.Rectangle.Width*2), 0, brd, resize.Lanczos3)
		brd_bnds := brd_resized.Bounds()

		vert := (face.Landmarks.MouthLeft.Y+face.Landmarks.MouthRight.Y)/2 - float32(brd_bnds.Dy())*0.5

		rb := image.NewRGBA(image.Rect(0, 0, brd_bnds.Dx(), brd_bnds.Dy()))

		rad := float64(face.Attributes.Pose.Roll) * math.Pi / 180
		graphics.Rotate(rb, brd_resized, &graphics.RotateOptions{rad})

		mid := face.Rectangle.Left + face.Rectangle.Width/2
		lt := mid - (float32(brd_bnds.Dx()) / 2)
		sr := image.Rect(0, 0, brd_bnds.Dx()*4, brd_bnds.Dy()*4)
		dp := image.Point{int(float64(lt)), int(float64(vert))}
		rt := image.Rectangle{dp, dp.Add(sr.Size())}

		draw.Draw(m, rt, rb, sr.Min, draw.Over)
	}

	img_out := image.Image(m)

	buffer := new(bytes.Buffer)
	if err := jpeg.Encode(buffer, img_out, nil); err != nil {
		return nil, err
	}

	return buffer, nil
}