summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--preprocmulti.go12
-rw-r--r--sauvola.go36
-rw-r--r--test_helpers.go2
-rw-r--r--testdata/pg1_integralsauvola_k0.3_w19.pngbin19456 -> 19618 bytes
-rw-r--r--testdata/pg1_integralsauvola_k0.5_w19.pngbin18241 -> 18439 bytes
-rw-r--r--testdata/pg1_integralsauvola_k0.5_w41.pngbin18260 -> 18284 bytes
-rw-r--r--util.go12
-rw-r--r--wipesides.go27
-rw-r--r--wipesides_test.go13
11 files changed, 56 insertions, 52 deletions
diff --git a/go.mod b/go.mod
index cd9d133..b2c2c0c 100644
--- a/go.mod
+++ b/go.mod
@@ -7,5 +7,5 @@ require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/wcharczuk/go-chart v2.0.2-0.20191206192251-962b9abdec2b+incompatible
golang.org/x/image v0.0.0-20200618115811-c13761719519 // indirect
- rescribe.xyz/integralimg v0.1.1
+ rescribe.xyz/integralimg v0.2.1
)
diff --git a/go.sum b/go.sum
index 7b10a68..d1d33ca 100644
--- a/go.sum
+++ b/go.sum
@@ -16,5 +16,5 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20181205014116-22934f0fdb62/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-rescribe.xyz/integralimg v0.1.1 h1:riLayPKKM5bs/ZyIcYhbUs4qP8wQOWc+tMxOF3vuscI=
-rescribe.xyz/integralimg v0.1.1/go.mod h1:2euyPigpyIixTWO6JtFEhDp/3YKA6yy+d8g17oL0L0s=
+rescribe.xyz/integralimg v0.2.1 h1:OhYifujtlVGYm/WfLl8ios/bouzIWcwuzbRyCZQiab0=
+rescribe.xyz/integralimg v0.2.1/go.mod h1:2euyPigpyIixTWO6JtFEhDp/3YKA6yy+d8g17oL0L0s=
diff --git a/preprocmulti.go b/preprocmulti.go
index e671108..25d35ea 100644
--- a/preprocmulti.go
+++ b/preprocmulti.go
@@ -49,10 +49,8 @@ func PreProcMulti(inPath string, ksizes []float64, binType string, binWsize int,
if err != nil {
return donePaths, err
}
- b := img.Bounds()
- gray := image.NewGray(image.Rect(0, 0, b.Dx(), b.Dy()))
- draw.Draw(gray, b, img, b.Min, draw.Src)
+ b := img.Bounds()
if binWsize == 0 {
binWsize = autowsize(b)
}
@@ -61,11 +59,15 @@ func PreProcMulti(inPath string, ksizes []float64, binType string, binWsize int,
binWsize++
}
+ intImg := integralimg.NewImage(b)
+ draw.Draw(intImg, b, img, b.Min, draw.Src)
+ intSqImg := integralimg.NewSqImage(b)
+ draw.Draw(intSqImg, b, img, b.Min, draw.Src)
+
var clean, threshimg image.Image
- integrals := integralimg.ToAllIntegralImg(gray)
for _, k := range ksizes {
- threshimg = PreCalcedSauvola(integrals, gray, k, binWsize)
+ threshimg = PreCalcedSauvola(*intImg, *intSqImg, img, k, binWsize)
if binType == "zeroinv" {
threshimg, err = BinToZeroInv(threshimg.(*image.Gray), img.(*image.RGBA))
diff --git a/sauvola.go b/sauvola.go
index ee7c870..9eef01b 100644
--- a/sauvola.go
+++ b/sauvola.go
@@ -41,38 +41,32 @@ func Sauvola(img image.Image, ksize float64, windowsize int) *image.Gray {
// "Efficient Implementation of Local Adaptive Thresholding Techniques Using Integral Images"
// and
// https://stackoverflow.com/questions/13110733/computing-image-integral
-func IntegralSauvola(img *image.Gray, ksize float64, windowsize int) *image.Gray {
+func IntegralSauvola(img image.Image, ksize float64, windowsize int) *image.Gray {
b := img.Bounds()
- new := image.NewGray(b)
-
- integrals := integralimg.ToAllIntegralImg(img)
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- m, dev := integrals.MeanStdDevWindow(x, y, windowsize)
- threshold := m * (1 + ksize*((dev/128)-1))
- if img.GrayAt(x, y).Y < uint8(threshold) {
- new.SetGray(x, y, color.Gray{0})
- } else {
- new.SetGray(x, y, color.Gray{255})
- }
- }
- }
+ intImg := integralimg.NewImage(b)
+ draw.Draw(intImg, b, img, b.Min, draw.Src)
+ intSqImg := integralimg.NewSqImage(b)
+ draw.Draw(intSqImg, b, img, b.Min, draw.Src)
- return new
+ return PreCalcedSauvola(*intImg, *intSqImg, img, ksize, windowsize)
}
// PreCalcedSauvola Implements Sauvola's algorithm using precalculated Integral Images
-func PreCalcedSauvola(integrals integralimg.WithSq, img *image.Gray, ksize float64, windowsize int) *image.Gray {
- // TODO: have this be the root function that the other two reference
+func PreCalcedSauvola(intImg integralimg.Image, intSqImg integralimg.SqImage, img image.Image, ksize float64, windowsize int) *image.Gray {
b := img.Bounds()
+ gray := image.NewGray(b)
+ draw.Draw(gray, b, img, b.Min, draw.Src)
new := image.NewGray(b)
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
- m, dev := integrals.MeanStdDevWindow(x, y, windowsize)
- threshold := m * (1 + ksize*((dev/128)-1))
- if img.GrayAt(x, y).Y < uint8(threshold) {
+ m, dev := integralimg.MeanStdDevWindow(intImg, intSqImg, x, y, windowsize)
+ // Divide by 255 to adjust from Gray16 used by integralimg to 8 bit Gray
+ m8 := m / 255
+ dev8 := dev / 255
+ threshold := m8 * (1 + ksize*((dev8/128)-1))
+ if gray.GrayAt(x, y).Y < uint8(math.Round(threshold)) {
new.SetGray(x, y, color.Gray{0})
} else {
new.SetGray(x, y, color.Gray{255})
diff --git a/test_helpers.go b/test_helpers.go
index 97a43dd..d36ef67 100644
--- a/test_helpers.go
+++ b/test_helpers.go
@@ -30,7 +30,7 @@ func decode(s string) (*image.Gray, error) {
return gray, nil
}
-func imgsequal(img1 *image.Gray, img2 *image.Gray) bool {
+func imgsequal(img1, img2 image.Image) bool {
b := img1.Bounds()
if !b.Eq(img2.Bounds()) {
return false
diff --git a/testdata/pg1_integralsauvola_k0.3_w19.png b/testdata/pg1_integralsauvola_k0.3_w19.png
index bdf5712..00aa1d8 100644
--- a/testdata/pg1_integralsauvola_k0.3_w19.png
+++ b/testdata/pg1_integralsauvola_k0.3_w19.png
Binary files differ
diff --git a/testdata/pg1_integralsauvola_k0.5_w19.png b/testdata/pg1_integralsauvola_k0.5_w19.png
index 5db2d9a..77a7194 100644
--- a/testdata/pg1_integralsauvola_k0.5_w19.png
+++ b/testdata/pg1_integralsauvola_k0.5_w19.png
Binary files differ
diff --git a/testdata/pg1_integralsauvola_k0.5_w41.png b/testdata/pg1_integralsauvola_k0.5_w41.png
index 050d037..5ddc854 100644
--- a/testdata/pg1_integralsauvola_k0.5_w41.png
+++ b/testdata/pg1_integralsauvola_k0.5_w41.png
Binary files differ
diff --git a/util.go b/util.go
index 304b209..1f8c9a5 100644
--- a/util.go
+++ b/util.go
@@ -8,14 +8,14 @@ import (
"errors"
"image"
"math"
+
+ "rescribe.xyz/integralimg"
)
-type UsefulImg interface {
- // TODO: name better; maybe verb, x-er
- // TODO: implement these for regular image, and use them to make
- // image functions generic for integral and non- images
- MeanWindow()
- MeanStdDevWindow()
+type ImageWindower interface {
+ image.Image
+ GetWindow(x, y, size int) integralimg.Window
+ GetVerticalWindow(x, width int) integralimg.Window
}
func mean(i []int) float64 {
diff --git a/wipesides.go b/wipesides.go
index 26b7d25..e87d209 100644
--- a/wipesides.go
+++ b/wipesides.go
@@ -4,7 +4,6 @@
package preproc
-// TODO: switch to an interface rather than integralimg.I
// TODO: optionally return the edges chosen
import (
@@ -21,7 +20,7 @@ import (
)
// returns the proportion of the given window that is black pixels
-func proportion(i integralimg.I, x int, size int) float64 {
+func proportion(i ImageWindower, x int, size int) float64 {
w := i.GetVerticalWindow(x, size)
return w.Proportion()
}
@@ -30,7 +29,7 @@ func proportion(i integralimg.I, x int, size int) float64 {
// find the one with the lowest proportion of black pixels.
// if there are multiple lines with the same proportion (e.g. zero),
// choose the middle one.
-func findbestedge(img integralimg.I, x int, w int) int {
+func findbestedge(img ImageWindower, x int, w int) int {
var best float64
var bestxs []int
@@ -59,8 +58,8 @@ func findbestedge(img integralimg.I, x int, w int) int {
// findedges finds the edges of the main content, by moving a window of wsize
// from near the middle of the image to the left and right, stopping when it reaches
// a point at which there is a lower proportion of black pixels than thresh.
-func findedges(img integralimg.I, wsize int, thresh float64) (int, int) {
- maxx := len(img[0]) - 1
+func findedges(img ImageWindower, wsize int, thresh float64) (int, int) {
+ maxx := img.Bounds().Dx() - 1
var lowedge, highedge int = 0, maxx
// don't start at the middle, as this will fail for 2 column layouts,
@@ -88,8 +87,8 @@ func findedges(img integralimg.I, wsize int, thresh float64) (int, int) {
// but working from the outside of the image inwards, rather than from the
// middle outwards.
// TODO: test what difference this makes
-func findedgesOutin(img integralimg.I, wsize int, thresh float64) (int, int) {
- maxx := len(img[0]) - 1
+func findedgesOutin(img ImageWindower, wsize int, thresh float64) (int, int) {
+ maxx := img.Bounds().Dx() - 1
var lowedge, highedge int = 0, maxx
for x := maxx-wsize; x > 0; x-- {
@@ -165,8 +164,10 @@ func sideways(img *image.Gray) *image.Gray {
// Wipe fills the sections of image which fall outside the content
// area with white, providing the content area is above min %
func Wipe(img *image.Gray, wsize int, thresh float64, min int) *image.Gray {
- integral := integralimg.ToIntegralImg(img)
- lowedge, highedge := findedges(integral, wsize, thresh)
+ b := img.Bounds()
+ intImg := integralimg.NewImage(b)
+ draw.Draw(intImg, b, img, b.Min, draw.Src)
+ lowedge, highedge := findedges(*intImg, wsize, thresh)
if toonarrow(img, lowedge, highedge, min) {
return img
}
@@ -177,9 +178,11 @@ func Wipe(img *image.Gray, wsize int, thresh float64, min int) *image.Gray {
// content area with white, providing the content area is above min %
func VWipe(img *image.Gray, wsize int, thresh float64, min int) *image.Gray {
rotimg := sideways(img)
- integral := integralimg.ToIntegralImg(rotimg)
+ b := rotimg.Bounds()
+ intImg := integralimg.NewImage(b)
+ draw.Draw(intImg, b, rotimg, b.Min, draw.Src)
// TODO: test whether there are any places where Outin makes a real difference
- lowedge, highedge:= findedgesOutin(integral, wsize, thresh)
+ lowedge, highedge:= findedgesOutin(*intImg, wsize, thresh)
if toonarrow(img, lowedge, highedge, min) {
return img
}
@@ -200,10 +203,10 @@ func VWipe(img *image.Gray, wsize int, thresh float64, min int) *image.Gray {
// vmin: minimum % of content area height to consider valid.
func WipeFile(inPath string, outPath string, hwsize int, hthresh float64, hmin int, vwsize int, vthresh float64, vmin int) error {
f, err := os.Open(inPath)
- defer f.Close()
if err != nil {
return errors.New(fmt.Sprintf("Could not open file %s: %v", inPath, err))
}
+ defer f.Close()
img, _, err := image.Decode(f)
if err != nil {
return errors.New(fmt.Sprintf("Could not decode image: %v", err))
diff --git a/wipesides_test.go b/wipesides_test.go
index d6a9af3..c6ff711 100644
--- a/wipesides_test.go
+++ b/wipesides_test.go
@@ -11,6 +11,7 @@ package preproc
import (
"fmt"
"image"
+ "image/draw"
"image/png"
"os"
"testing"
@@ -77,8 +78,10 @@ func TestWipeSides(t *testing.T) {
if err != nil {
t.Fatalf("Could not open file %s: %v\n", c.filename, err)
}
- integral := integralimg.ToIntegralImg(img)
- leftedge, rightedge := findedges(integral, c.wsize, c.thresh)
+ b := img.Bounds()
+ integral := integralimg.NewImage(b)
+ draw.Draw(integral, b, img, b.Min, draw.Src)
+ leftedge, rightedge := findedges(*integral, c.wsize, c.thresh)
if leftedge < c.minleft {
t.Errorf("Left edge %d < minimum %d", leftedge, c.minleft)
}
@@ -113,8 +116,10 @@ func TestWipeSides(t *testing.T) {
if err != nil {
t.Fatalf("Could not open file %s: %v\n", c.filename, err)
}
- integral := integralimg.ToIntegralImg(sideways(img))
- topedge, bottomedge := findedges(integral, c.wsize, c.thresh)
+ b := img.Bounds()
+ intImg := integralimg.NewImage(b)
+ draw.Draw(intImg, b, img, b.Min, draw.Src)
+ topedge, bottomedge := findedges(*intImg, c.wsize, c.thresh)
if topedge < c.mintop {
t.Errorf("Top edge %d < minimum %d", topedge, c.mintop)
}