summaryrefslogtreecommitdiff
path: root/sauvola.go
diff options
context:
space:
mode:
authorNick White <git@njw.name>2020-07-23 23:09:43 +0100
committerNick White <git@njw.name>2020-07-23 23:09:43 +0100
commit1ef9df29fb2d5bb6585e0d0ed99e13a900289e7b (patch)
tree124ddc2d71e3090fa131468cdf22af076a177063 /sauvola.go
parent6a36400351bea5052431bb1feace358fa67a5cf9 (diff)
Update to v0.2.1 of integralimg, and improve various things
- Improve integral sauvola by rounding threshold correctly - The Sauvola functions can now process any image.Image, not just an image.Gray - Add ImageWindower interface and use it to generalise wipesides.go - Rely on Bounds() for image bounds rather than implementation- specific stuff in integralimg Note that a couple of the wipesides tests are now failing. It's possible that this is due to fixed or introduced bugs (let's hope the former) changing sensible thresholds. Will need to look into this and sort it.
Diffstat (limited to 'sauvola.go')
-rw-r--r--sauvola.go36
1 files changed, 15 insertions, 21 deletions
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})