// Copyright (c) 2025 Nikolai Papin // // This file is part of the Auto Attendance app that looks for // self-attend QR-codes during lectures and opens their URLs in your // browser. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See // the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package vision import ( "errors" "git.weirdcat.su/weirdcat/auto-attendance/internal/logger" "github.com/makiuchi-d/gozxing" "github.com/makiuchi-d/gozxing/qrcode" "gocv.io/x/gocv" ) type Vision interface { AnalyzeImage(filePath string) (data VisionData, err error) } type visionImpl struct { log *logger.Logger } var ( ErrImageEmpty = errors.New("Image from file was empty") ) // AnalyzeImage implements Vision. func (v *visionImpl) AnalyzeImage(filePath string) (data VisionData, err error) { // TODO: scanning for multiple QR-codes at once v.log.Debug("analyzing image for qr codes", "filePath", filePath) img := gocv.IMRead(filePath, gocv.IMReadColor) if img.Empty() { v.log.Error("could not read image file", "filePath", filePath) return VisionData{}, ErrImageEmpty } defer img.Close() // Convert to grayscale for QR code detection gray := gocv.NewMat() defer gray.Close() gocv.CvtColor(img, &gray, gocv.ColorBGRToGray) // Convert gocv.Mat to image.Image for gozxing imgGray, err := gray.ToImage() if err != nil { v.log.Error("failed to convert image", "error", err) return VisionData{}, err } // Create a binary bitmap from the image bmp, err := gozxing.NewBinaryBitmapFromImage(imgGray) if err != nil { v.log.Error("failed to create binary bitmap", "error", err) return VisionData{}, err } reader := qrcode.NewQRCodeReader() result, err := reader.Decode(bmp, nil) if err != nil { v.log.Debug("no qr code found in image", "filePath", filePath) return VisionData{}, nil } v.log.Info("QR code decoded successfully", "content", result.GetText()) data = VisionData{result.GetText()} return data, nil } func NewVision(log *logger.Logger) Vision { return &visionImpl{log: log} }