diff options
author | Nick White <git@njw.name> | 2020-02-13 09:04:38 +0000 |
---|---|---|
committer | Nick White <git@njw.name> | 2020-02-13 09:04:38 +0000 |
commit | 699634be83582c47075db8520ab262669990a325 (patch) | |
tree | 27770493494ca9b0258a06e72431fd46b6c7a50f | |
parent | 787d63fc5d13c6250bd33da5a8e1eadbe86188cd (diff) |
Add experimental and partial splittable tool
-rw-r--r-- | cmd/splittable/main.go | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/cmd/splittable/main.go b/cmd/splittable/main.go new file mode 100644 index 0000000..1029db1 --- /dev/null +++ b/cmd/splittable/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "flag" + "fmt" + "image" + "image/draw" + _ "image/jpeg" + "image/png" + "log" + "os" + + "rescribe.xyz/preproc/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.I, 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.I, 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.I, wsize int, thresh float64) []int { + maxx := len(img[0]) - 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.ToIntegralImg(gray) + 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 +} |