summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick White <git@njw.name>2020-07-13 18:09:15 +0100
committerNick White <git@njw.name>2020-07-13 18:17:48 +0100
commit0ceaa5d97b585987539c51b8b8ce0705c06fc3a8 (patch)
treee229f7879ddcd9b4f7165e76733cbb88e3b8718e
parentc3ae25f63823501fc7f92e6e291df60c282be138 (diff)
Improve wiper algorithm
Several changes made to wiper to improve things: - If findbestedge finds more than one 'best' line, choose the middle one, rather than the first. - findbestedge selects the line with the lowest number of black pixels, rather than (erroneously) the highest. - Added a findedgesOutin() function, which finds edges like findedges() but looking from the outside edges of the image inwards, rather than from the inside out. More testing is needed to decide whether this is useful; initial testing showed very little difference, but that was limited to lightly binarised images. This is in use by VWipe() at present, where worries about several gutters thanks to the edges of other pages aren't present. - Added separate vwsize, vthresh and vmin arguments to WipeFile so that vertical wiping can have different parameters (which is very important for good quality vertical wiping). - Updated wiping tests. - Rewrote the wipe cmd to use WipeFile() directly.
-rw-r--r--cmd/wipe/main.go31
-rw-r--r--testdata/pg2_integralwipesides_t0.02_w5.pngbin33595 -> 33622 bytes
-rw-r--r--testdata/pg2_integralwipesides_t0.05_w25.pngbin33432 -> 33622 bytes
-rw-r--r--testdata/pg2_integralwipesides_t0.05_w5.pngbin21695 -> 21735 bytes
-rw-r--r--wipesides.go61
-rw-r--r--wipesides_test.go4
6 files changed, 54 insertions, 42 deletions
diff --git a/cmd/wipe/main.go b/cmd/wipe/main.go
index 30b0061..c69836d 100644
--- a/cmd/wipe/main.go
+++ b/cmd/wipe/main.go
@@ -9,10 +9,6 @@ package main
import (
"flag"
"fmt"
- "image"
- "image/draw"
- _ "image/jpeg"
- "image/png"
"log"
"os"
@@ -25,7 +21,7 @@ func main() {
fmt.Fprintf(os.Stderr, "Wipes the sections of an image which are outside the content area.\n")
flag.PrintDefaults()
}
- min := flag.Int("hm", 30, "Minimum percentage of the image width for the content width calculation to be considered valid.")
+ hmin := flag.Int("hm", 30, "Minimum percentage of the image width for the content width calculation to be considered valid.")
thresh := flag.Float64("ht", 0.05, "Threshold for the proportion of black pixels below which a window is determined to be the edge. Higher means more aggressive wiping.")
wsize := flag.Int("hw", 5, "Window size for mask finding algorithm.")
vmin := flag.Int("vm", 30, "Minimum percentage of the image height for the content width calculation to be considered valid.")
@@ -37,29 +33,8 @@ func main() {
os.Exit(1)
}
- f, err := os.Open(flag.Arg(0))
- defer f.Close()
+ err := preproc.WipeFile(flag.Arg(0), flag.Arg(1), *wsize, *thresh, *hmin, *vwsize, *vthresh, *vmin)
if err != nil {
- log.Fatalf("Could not open file %s: %v\n", flag.Arg(0), err)
- }
- img, _, err := image.Decode(f)
- if err != nil {
- log.Fatalf("Could not decode image: %v\n", err)
- }
- b := img.Bounds()
- gray := image.NewGray(image.Rect(0, 0, b.Dx(), b.Dy()))
- draw.Draw(gray, b, img, b.Min, draw.Src)
-
- sidesdone := preproc.Wipe(gray, *wsize, *thresh, *min)
- clean := preproc.VWipe(sidesdone, *vwsize, *vthresh, *vmin)
-
- f, err = os.Create(flag.Arg(1))
- if err != nil {
- log.Fatalf("Could not create file %s: %v\n", flag.Arg(1), err)
- }
- defer f.Close()
- err = png.Encode(f, clean)
- if err != nil {
- log.Fatalf("Could not encode image: %v\n", err)
+ log.Fatalf("Failed to wipe image: %v\n", err)
}
}
diff --git a/testdata/pg2_integralwipesides_t0.02_w5.png b/testdata/pg2_integralwipesides_t0.02_w5.png
index 6b4ccb2..4e8850c 100644
--- a/testdata/pg2_integralwipesides_t0.02_w5.png
+++ b/testdata/pg2_integralwipesides_t0.02_w5.png
Binary files differ
diff --git a/testdata/pg2_integralwipesides_t0.05_w25.png b/testdata/pg2_integralwipesides_t0.05_w25.png
index 39dc88d..4e8850c 100644
--- a/testdata/pg2_integralwipesides_t0.05_w25.png
+++ b/testdata/pg2_integralwipesides_t0.05_w25.png
Binary files differ
diff --git a/testdata/pg2_integralwipesides_t0.05_w5.png b/testdata/pg2_integralwipesides_t0.05_w5.png
index 3a0452f..da09eba 100644
--- a/testdata/pg2_integralwipesides_t0.05_w5.png
+++ b/testdata/pg2_integralwipesides_t0.05_w5.png
Binary files differ
diff --git a/wipesides.go b/wipesides.go
index e4b9ad5..4af3204 100644
--- a/wipesides.go
+++ b/wipesides.go
@@ -4,8 +4,8 @@
package preproc
-// TODO: add minimum size variable (default ~30%?)
// TODO: switch to an interface rather than integralimg.I
+// TODO: optionally return the edges chosen
import (
"errors"
@@ -28,24 +28,32 @@ func proportion(i integralimg.I, x int, size int) float64 {
// findbestedge goes through every vertical line from x to x+w to
// 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 {
- var bestx int
var best float64
+ var bestxs []int
if w == 1 {
return x
}
+ best = 100
+
right := x + w
for ; x < right; x++ {
prop := proportion(img, x, 1)
- if prop > best {
+ if prop < best {
+ bestxs = make([]int, 0)
best = prop
- bestx = x
+ }
+ if prop == best {
+ bestxs = append(bestxs, x)
}
}
+ middlex := bestxs[len(bestxs)/2]
- return bestx
+ return middlex
}
// findedges finds the edges of the main content, by moving a window of wsize
@@ -76,6 +84,31 @@ func findedges(img integralimg.I, wsize int, thresh float64) (int, int) {
return lowedge, highedge
}
+// findedgesOutin finds the edges of the main content as findedges does,
+// 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
+ var lowedge, highedge int = 0, maxx
+
+ for x := maxx-wsize; x > 0; x-- {
+ if proportion(img, x, wsize) > thresh {
+ highedge = findbestedge(img, x, wsize)
+ break
+ }
+ }
+
+ for x := 0; x < maxx-wsize; x++ {
+ if proportion(img, x, wsize) > thresh {
+ lowedge = findbestedge(img, x, wsize)
+ break
+ }
+ }
+
+ return lowedge, highedge
+}
+
// wipesides fills the sections of image not within the boundaries
// of lowedge and highedge with white
func wipesides(img *image.Gray, lowedge int, highedge int) *image.Gray {
@@ -145,7 +178,8 @@ func Wipe(img *image.Gray, wsize int, thresh float64, min int) *image.Gray {
func VWipe(img *image.Gray, wsize int, thresh float64, min int) *image.Gray {
rotimg := sideways(img)
integral := integralimg.ToIntegralImg(rotimg)
- lowedge, highedge := findedges(integral, wsize, thresh)
+ // TODO: test whether there are any places where Outin makes a real difference
+ lowedge, highedge:= findedgesOutin(integral, wsize, thresh)
if toonarrow(img, lowedge, highedge, min) {
return img
}
@@ -158,10 +192,13 @@ func VWipe(img *image.Gray, wsize int, thresh float64, min int) *image.Gray {
// content area is above min %.
// inPath: path of the input image.
// outPath: path to save the output image.
-// wsize: window size for wipe algorithm.
-// thresh: threshold for wipe algorithm.
-// min: minimum % of content area width to consider valid.
-func WipeFile(inPath string, outPath string, wsize int, thresh float64, min int) error {
+// hwsize: window size for horizontal wipe algorithm.
+// hthresh: threshold for horizontal wipe algorithm.
+// hmin: minimum % of content area width to consider valid.
+// vwsize: window size for vertical wipe algorithm.
+// vthresh: threshold for vertical wipe algorithm.
+// 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 {
@@ -175,8 +212,8 @@ func WipeFile(inPath string, outPath string, wsize int, thresh float64, min int)
gray := image.NewGray(image.Rect(0, 0, b.Dx(), b.Dy()))
draw.Draw(gray, b, img, b.Min, draw.Src)
- hclean := Wipe(gray, wsize, thresh, min)
- clean := VWipe(hclean, wsize, thresh, min)
+ hclean := Wipe(gray, hwsize, hthresh, hmin)
+ clean := VWipe(hclean, vwsize, vthresh, vmin)
f, err = os.Create(outPath)
if err != nil {
diff --git a/wipesides_test.go b/wipesides_test.go
index 7ce3472..d6a9af3 100644
--- a/wipesides_test.go
+++ b/wipesides_test.go
@@ -103,8 +103,8 @@ func TestWipeSides(t *testing.T) {
thresh float64
wsize int
}{
- {"testdata/1727_GREENE_0048.png", 172, 237, 2204, 2244, 0.005, 120},
- {"testdata/1687_SCHWEITZER_0030.png", 142, 231, 2595, 2656, 0.005, 90},
+ {"testdata/1727_GREENE_0048.png", 70, 237, 2204, 2450, 0.005, 120},
+ {"testdata/1687_SCHWEITZER_0030.png", 70, 231, 2595, 2770, 0.005, 90},
}
for _, c := range topbottomedgecases {