Update project

This commit is contained in:
2026-07-23 15:36:11 +05:30
parent 1197cfc161
commit 14fffa40c6
84 changed files with 20918 additions and 2761 deletions

View File

View File

@@ -0,0 +1,86 @@
import 'dart:io';
import 'dart:math' as math;
import 'package:image/image.dart' as img;
/// Holds the two vector embeddings: one for the cropped product image,
/// one for the OCR-extracted label text.
class VectorEmbeddingResult {
final List<double> imageVector;
final List<double> textVector;
const VectorEmbeddingResult({
required this.imageVector,
required this.textVector,
});
}
/// Computes vector embeddings entirely on-device — no backend call.
///
/// NOTE: these are lightweight placeholder embeddings (a downsampled
/// grayscale pixel vector for the image, a character-hashing vector for
/// the text), NOT a trained model like CLIP. They're deterministic and
/// good enough to wire up printing/plumbing now. Swap `_imageVector` /
/// `_textVector` for a real on-device model (e.g. a TFLite feature
/// extractor) or a backend call later without touching the call site.
class VectorService {
static const int _imageVectorSize = 64; // 8x8 downsampled grayscale
static const int _textVectorSize = 32; // hashed char buckets
static Future<VectorEmbeddingResult> getEmbeddings({
required String imagePath,
required String labelText,
}) async {
final imageVector = await _imageVector(imagePath);
final textVector = _textVector(labelText);
return VectorEmbeddingResult(
imageVector: imageVector,
textVector: textVector,
);
}
/// Downsamples the image to an 8x8 grayscale grid and flattens it into
/// a normalized (0-1) vector of length [_imageVectorSize].
static Future<List<double>> _imageVector(String imagePath) async {
final bytes = await File(imagePath).readAsBytes();
final decoded = img.decodeImage(bytes);
if (decoded == null) {
return List<double>.filled(_imageVectorSize, 0);
}
final side = math.sqrt(_imageVectorSize).round(); // 8
final resized = img.copyResize(decoded, width: side, height: side);
final gray = img.grayscale(resized);
final vector = <double>[];
for (var y = 0; y < side; y++) {
for (var x = 0; x < side; x++) {
final pixel = gray.getPixel(x, y);
vector.add(pixel.r / 255.0); // grayscale => r == g == b
}
}
return vector;
}
/// Simple character-hashing bag-of-characters vector, normalized so
/// values sum to 1 (empty text => all zeros).
static List<double> _textVector(String text) {
final vector = List<double>.filled(_textVectorSize, 0);
final normalized = text.toLowerCase();
if (normalized.isEmpty) return vector;
for (final rune in normalized.runes) {
final bucket = rune % _textVectorSize;
vector[bucket] += 1;
}
final total = vector.fold<double>(0, (sum, v) => sum + v);
if (total > 0) {
for (var i = 0; i < vector.length; i++) {
vector[i] = vector[i] / total;
}
}
return vector;
}
}