126 lines
3.8 KiB
Go
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)
|
|
}
|
|
}
|