Files
mesh-catalog/modules/scan/cmd/scan/crop_test.go
T

126 lines
3.8 KiB
Go

package main
import (
"bytes"
"image"
"image/color"
"image/draw"
"image/jpeg"
"math/rand"
"testing"
)
// lidWhite is a w x h gray image of the lid's white, with a few specks of dust in one patch.
func lidWhite(w, h int, seed int64) *image.Gray {
g := image.NewGray(image.Rect(0, 0, w, h))
draw.Draw(g, g.Bounds(), &image.Uniform{color.Gray{255}}, image.Point{}, draw.Src)
r := rand.New(rand.NewSource(seed))
for i := 0; i < 40; i++ {
g.SetGray(100+r.Intn(w/4), 100+r.Intn(h/4), color.Gray{120}) // all in one patch, so counting them would make a box
}
return g
}
func encode(t *testing.T, img image.Image) []byte {
t.Helper()
var b bytes.Buffer
if err := jpeg.Encode(&b, img, &jpeg.Options{Quality: 95}); err != nil {
t.Fatal(err)
}
return b.Bytes()
}
// 1440/AC3: the captured receipt, top-left of a 1696x2203 scan at 200 dpi.
func TestCropFindsTheCapturedReceipt(t *testing.T) {
c, err := Crop(fixture(t, "scan.jpg"), true)
if err != nil {
t.Fatal(err)
}
if !c.Cropped {
t.Fatal("cropped = false for a scan with a receipt on it")
}
img, err := jpeg.Decode(bytes.NewReader(fixture(t, "scan.jpg")))
if err != nil {
t.Fatal(err)
}
box, found := Box(img)
if !found {
t.Fatal("no box found")
}
if box.Min.X > 10 || box.Min.Y > 10 {
t.Errorf("box starts at %v, want within 10 px of (0,0)", box.Min)
}
if box.Max.X < 630 || box.Max.X > 660 {
t.Errorf("right edge %d, want 630..660", box.Max.X)
}
if box.Max.Y < 1730 || box.Max.Y > 1810 {
t.Errorf("bottom %d, want 1730..1810", box.Max.Y)
}
if c.Width != box.Dx() || c.Height != box.Dy() {
t.Errorf("cropped image is %dx%d, the box %dx%d", c.Width, c.Height, box.Dx(), box.Dy())
}
got, err := jpeg.DecodeConfig(bytes.NewReader(c.JPEG))
if err != nil || got.Width != box.Dx() || got.Height != box.Dy() {
t.Errorf("the cropped JPEG is %+v (%v), want %dx%d", got, err, box.Dx(), box.Dy())
}
// About 80-84 mm wide at 200 dpi.
if mm := float64(c.Width) / 200 * 25.4; mm < 80 || mm > 84 {
t.Errorf("%.1f mm wide, want 80-84", mm)
}
}
// 1440/AC4: a plain lid with specks has no document.
func TestCropKeepsAPlainLidWhole(t *testing.T) {
scan := encode(t, lidWhite(800, 1100, 1))
c, err := Crop(scan, true)
if err != nil {
t.Fatal(err)
}
if c.Cropped || c.Width != 800 || c.Height != 1100 || !bytes.Equal(c.JPEG, scan) {
t.Fatalf("a plain lid was cropped: %+v", c)
}
}
// 1440/AC4: a document that fills the platen is no crop either.
func TestCropKeepsAFullPlatenDocumentWhole(t *testing.T) {
g := lidWhite(800, 1100, 2)
draw.Draw(g, image.Rect(0, 0, 790, 1090), &image.Uniform{color.Gray{200}}, image.Point{}, draw.Src)
scan := encode(t, g)
c, err := Crop(scan, true)
if err != nil {
t.Fatal(err)
}
if c.Cropped || c.Width != 800 || c.Height != 1100 {
t.Fatalf("a document filling the platen was cropped: %+v", c)
}
}
// 1440/AC4: crop=false keeps the captured scan whole, though it has a document.
func TestCropOffKeepsTheWholeImage(t *testing.T) {
scan := fixture(t, "scan.jpg")
c, err := Crop(scan, false)
if err != nil {
t.Fatal(err)
}
if c.Cropped || c.Width != 1696 || c.Height != 2203 || !bytes.Equal(c.JPEG, scan) {
t.Fatalf("crop=false cropped: %+v", c)
}
}
// 1440/AC3: a document away from the origin is found where it is, not assumed in the corner.
func TestCropFindsADocumentInTheMiddleOfTheGlass(t *testing.T) {
g := lidWhite(800, 1100, 3)
draw.Draw(g, image.Rect(200, 300, 500, 700), &image.Uniform{color.Gray{240}}, image.Point{}, draw.Src)
img, err := jpeg.Decode(bytes.NewReader(encode(t, g)))
if err != nil {
t.Fatal(err)
}
box, found := Box(img)
if !found {
t.Fatal("no box")
}
if box.Min.X < 190 || box.Min.X > 205 || box.Max.X < 495 || box.Max.X > 510 || box.Min.Y < 290 || box.Min.Y > 305 || box.Max.Y < 695 || box.Max.Y > 710 {
t.Fatalf("box = %v, want about (200,300)-(500,700)", box)
}
}