summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick White <git@njw.name>2020-07-27 00:15:05 +0100
committerNick White <git@njw.name>2020-07-27 00:15:05 +0100
commit4627227579c6922406517cf8ad0eb05728d71e0f (patch)
treee6324da1f400bdfff6bee5b163328fb90adfc00a
parent97753052db0239d907631d4a480ac742412827d3 (diff)
Update wipesides and pggraph to new functions in integralimg, and remove experimental splittable
-rw-r--r--cmd/pggraph/main.go4
-rw-r--r--cmd/splittable/main.go146
-rw-r--r--util.go7
-rw-r--r--wipesides.go35
4 files changed, 24 insertions, 168 deletions
diff --git a/cmd/pggraph/main.go b/cmd/pggraph/main.go
index 6c13fe2..59946ad 100644
--- a/cmd/pggraph/main.go
+++ b/cmd/pggraph/main.go
@@ -22,6 +22,7 @@ import (
chart "github.com/wcharczuk/go-chart"
"rescribe.xyz/integralimg"
+ "rescribe.xyz/preproc"
)
const usage = `Usage: pggraph [-vertical] [-width] inimg graphname
@@ -178,8 +179,7 @@ func main() {
points := make(map[int]float64)
maxx := b.Dx() - 1
for x := 0; x+*width < maxx; x += *width {
- w := intImg.GetVerticalWindow(x, *width)
- points[x] = w.Proportion()
+ points[x] = preproc.ProportionSlice(intImg, x, *width)
}
f, err = os.Create(flag.Arg(1))
diff --git a/cmd/splittable/main.go b/cmd/splittable/main.go
deleted file mode 100644
index 3fc5ace..0000000
--- a/cmd/splittable/main.go
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2020 Nick White.
-// Use of this source code is governed by the GPLv3
-// license that can be found in the LICENSE file.
-
-// splittable is an experimental program to split a table into
-// individual cells suitable for OCR
-package main
-
-import (
- "flag"
- "fmt"
- "image"
- "image/draw"
- _ "image/jpeg"
- "image/png"
- "log"
- "os"
-
- "rescribe.xyz/integralimg"
-)
-
-const usage = `Usage: splittable [-t thresh] [-w winsize] inimg outbase
-
-splittable is an experimental program to split a table into individual
-cells suitable for OCR. It does this by detecting lines. At present it
-just detects vertical lines and outputs images for each section
-between those lines.
-
-`
-
-// returns the proportion of the given window that is black pixels
-func proportion(i integralimg.Image, x int, size int) float64 {
- w := i.GetVerticalWindow(x, size)
- return w.Proportion()
-}
-
-// findbestvline goes through every vertical line from x to x+w to
-// find the one with the lowest proportion of black pixels.
-func findbestvline(img integralimg.Image, x int, w int) int {
- var bestx int
- var best float64
-
- if w == 1 {
- return x
- }
-
- right := x + w
- for ; x < right; x++ {
- prop := proportion(img, x, 1)
- if prop > best {
- best = prop
- bestx = x
- }
- }
-
- return bestx
-}
-
-// findvlines finds vertical lines, returning an array of x coordinates
-// for each line. It works by moving a window of wsize across the image,
-// marking each place where there is a higher proportion of black pixels
-// than thresh.
-func findvlines(img integralimg.Image, wsize int, thresh float64) []int {
- maxx := img.Bounds().Dx() - 1
- var lines []int
-
- for x := 0; x < maxx-wsize; x+=wsize {
- if proportion(img, x, wsize) >= thresh {
- l := findbestvline(img, x, wsize)
- lines = append(lines, l)
- }
- }
-
- return lines
-}
-
-func drawsection(img *image.Gray, x1 int, x2 int) *image.Gray {
- b := img.Bounds()
- width := x2-x1
- new := image.NewGray(image.Rect(0, b.Min.Y, width, b.Max.Y))
-
- for x := 0; x < width; x++ {
- for y := b.Min.Y; y < b.Max.Y; y++ {
- new.SetGray(x, y, img.GrayAt(x1 + x, y))
- }
- }
-
- return new
-}
-
-func main() {
- flag.Usage = func() {
- fmt.Fprintf(flag.CommandLine.Output(), usage)
- flag.PrintDefaults()
- }
- thresh := flag.Float64("t", 0.85, "Threshold for the proportion of black pixels below which a window is determined to be a line. Higher means fewer lines will be found.")
- wsize := flag.Int("w", 1, "Window size for mask finding algorithm.")
- flag.Parse()
- if flag.NArg() < 2 {
- flag.Usage()
- os.Exit(1)
- }
-
- f, err := os.Open(flag.Arg(0))
- defer f.Close()
- 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)
-
- integral := integralimg.NewImage(b)
- draw.Draw(integral, b, gray, b.Min, draw.Src)
- vlines := findvlines(*integral, *wsize, *thresh)
-
- for i, v := range vlines {
- fmt.Printf("line detected at x=%d\n", v)
-
- if i+1 >= len(vlines) {
- break
- }
- section := drawsection(gray, v, vlines[i+1])
-
- fn := fmt.Sprintf("%s-%d.png", flag.Arg(1), v)
- f, err = os.Create(fn)
- if err != nil {
- log.Fatalf("Could not create file %s: %v\n", fn, err)
- }
- defer f.Close()
- err := png.Encode(f, section)
- if err != nil {
- log.Fatalf("Could not encode image %s: %v\n", fn, err)
- }
- }
-
-
- // TODO: find horizontal lines too
- // TODO: do rotation
- // TODO: output table cells
- // TODO: potentially send cells straight to tesseract
-}
diff --git a/util.go b/util.go
index 1f8c9a5..7cbaebc 100644
--- a/util.go
+++ b/util.go
@@ -8,14 +8,11 @@ import (
"errors"
"image"
"math"
-
- "rescribe.xyz/integralimg"
)
-type ImageWindower interface {
+type SummableImage interface {
image.Image
- GetWindow(x, y, size int) integralimg.Window
- GetVerticalWindow(x, width int) integralimg.Window
+ Sum(r image.Rectangle) uint64
}
func mean(i []int) float64 {
diff --git a/wipesides.go b/wipesides.go
index a306250..7480a7f 100644
--- a/wipesides.go
+++ b/wipesides.go
@@ -19,17 +19,22 @@ import (
"rescribe.xyz/integralimg"
)
-// returns the proportion of the given window that is black pixels
-func proportion(i ImageWindower, x int, size int) float64 {
- w := i.GetVerticalWindow(x, size)
- return w.Proportion()
+// ProportionSlice returns the proportion of black pixels in a
+// vertical slice of an image starting at x, width pixels wide.
+func ProportionSlice(i SummableImage, x int, width int) float64 {
+ r := image.Rect(x, 0, x + width, i.Bounds().Dy())
+ in := r.Intersect(i.Bounds())
+ area := in.Dx() * in.Dy()
+ // 1 << 16 - 1 as we're using Gray16, so 1 << 16 - 1 = white
+ numwhite := float64(i.Sum(in)) / float64(1 << 16 - 1)
+ return float64(area) / float64(numwhite) - 1
}
// 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 ImageWindower, x int, w int) int {
+func findbestedge(img SummableImage, x int, w int) int {
var best float64
var bestxs []int
@@ -41,7 +46,7 @@ func findbestedge(img ImageWindower, x int, w int) int {
right := x + w
for ; x < right; x++ {
- prop := proportion(img, x, 1)
+ prop := ProportionSlice(img, x, 1)
if prop < best {
bestxs = make([]int, 0)
best = prop
@@ -55,10 +60,10 @@ func findbestedge(img ImageWindower, x int, w int) int {
return middlex
}
-// findedges finds the edges of the main content, by moving a window of wsize
+// findedges finds the edges of the main content, by moving a wsize width vertical slice
// 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 ImageWindower, wsize int, thresh float64) (int, int) {
+func findedges(img SummableImage, wsize int, thresh float64) (int, int) {
maxx := img.Bounds().Dx() - 1
var lowedge, highedge int = 0, maxx
@@ -67,14 +72,14 @@ func findedges(img ImageWindower, wsize int, thresh float64) (int, int) {
notcentre := maxx / 10
for x := maxx/2 + notcentre; x < maxx-wsize; x++ {
- if proportion(img, x, wsize) <= thresh {
+ if ProportionSlice(img, x, wsize) <= thresh {
highedge = findbestedge(img, x, wsize)
break
}
}
for x := maxx/2 - notcentre; x > 0; x-- {
- if proportion(img, x, wsize) <= thresh {
+ if ProportionSlice(img, x, wsize) <= thresh {
lowedge = findbestedge(img, x, wsize)
break
}
@@ -87,19 +92,19 @@ func findedges(img ImageWindower, 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 ImageWindower, wsize int, thresh float64) (int, int) {
+func findedgesOutin(img SummableImage, wsize int, thresh float64) (int, int) {
maxx := img.Bounds().Dx() - 1
var lowedge, highedge int = 0, maxx
for x := maxx-wsize; x > 0; x-- {
- if proportion(img, x, wsize) > thresh {
+ if ProportionSlice(img, x, wsize) > thresh {
highedge = findbestedge(img, x, wsize)
break
}
}
for x := 0; x < maxx-wsize; x++ {
- if proportion(img, x, wsize) > thresh {
+ if ProportionSlice(img, x, wsize) > thresh {
lowedge = findbestedge(img, x, wsize)
break
}
@@ -196,10 +201,10 @@ 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.
-// hwsize: window size for horizontal wipe algorithm.
+// hwsize: window size (width) 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.
+// vwsize: window size (height) 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 {