58 lines
1.8 KiB
Go
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()
|
|
}
|