Files

58 lines
1.8 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package ocr
import (
"context"
"image"
"image/color"
)
// Preprocess returns owned pixels in recognition coordinates. Scale uses nearest
// neighbor sampling so recognized word boxes map back through the capture transform.
func Preprocess(ctx context.Context, source image.Image, options Options) (image.Image, error) {
if source == nil || !valid_dimensions(source.Bounds().Dx(), source.Bounds().Dy()) {
return nil, ErrInvalidImage
}
scale := options.Scale
if scale == 0 {
scale = 1
}
width, height := source.Bounds().Dx(), source.Bounds().Dy()
if scale < 1 || scale > 4 || height > MaxPixels/scale || !valid_dimensions(width*scale, height*scale) ||
options.Threshold < 0 || options.Threshold > 255 {
return nil, ErrLimit
}
if scale == 1 && !options.Grayscale && !options.Invert && !options.BlackWhite {
return source, nil
}
processed := image.NewRGBA(image.Rect(0, 0, width*scale, height*scale))
for y := 0; y < height*scale; y++ {
if y%32 == 0 && ctx.Err() != nil {
return nil, ctx.Err()
}
for x := 0; x < width*scale; x++ {
r, g, b, _ := source.At(source.Bounds().Min.X+x/scale, source.Bounds().Min.Y+y/scale).RGBA()
red, green, blue := uint8(r>>8), uint8(g>>8), uint8(b>>8)
gray := uint8((299*uint32(red) + 587*uint32(green) + 114*uint32(blue) + 500) / 1000)
if options.Grayscale || options.BlackWhite {
red, green, blue = gray, gray, gray
}
if options.Invert {
red, green, blue = 255-red, 255-green, 255-blue
}
if options.BlackWhite {
value := uint8(0)
if int(red) >= options.Threshold {
value = 255
}
red, green, blue = value, value, value
}
processed.SetRGBA(x, y, color.RGBA{R: red, G: green, B: blue, A: 255})
}
}
return processed, ctx.Err()
}