Migrate from .NET MAUI to Expo + Convex
Complete rewrite of Scry using TypeScript stack:
- Expo/React Native with Expo Router (file-based routing)
- Convex backend (serverless functions + real-time database)
- Adaptive camera system (expo-camera in Expo Go, Vision Camera in production)
- React Native Skia + fast-opencv for image processing
- GDPR-compliant auth setup with Zitadel OIDC (pending configuration)
Key features:
- Card recognition pipeline ported to TypeScript
- Perceptual hashing (192-bit color pHash)
- CLAHE preprocessing for lighting normalization
- Local SQLite cache with Convex sync
- Collection management with offline support
Removes all .NET/MAUI code (src/, test/, tools/).
💘 Generated with Crush
Assisted-by: Claude Opus 4.5 via Crush <crush@charm.land>
This commit is contained in:
parent
56499d5af9
commit
83ab4df537
138 changed files with 19136 additions and 7681 deletions
95
lib/recognition/imageLoader.ts
Normal file
95
lib/recognition/imageLoader.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* Image loader utilities for React Native.
|
||||
* Loads images from file paths and returns pixel data for recognition.
|
||||
*/
|
||||
|
||||
import * as ImageManipulator from "expo-image-manipulator";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export interface LoadedImage {
|
||||
pixels: Uint8Array;
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load image from a file path and return RGBA pixel data.
|
||||
* Uses expo-image-manipulator to resize the image to a manageable size first.
|
||||
*
|
||||
* Note: expo-image-manipulator doesn't directly provide pixel data.
|
||||
* We need to use a canvas (web) or Skia (native) to decode pixels.
|
||||
* For now, this provides the resized image URI for the Skia-based decoder.
|
||||
*/
|
||||
export async function loadImageForRecognition(
|
||||
uri: string,
|
||||
targetWidth: number = 480, // Reasonable size for processing
|
||||
targetHeight: number = 640
|
||||
): Promise<{ uri: string; width: number; height: number }> {
|
||||
// Normalize the URI for the platform
|
||||
const normalizedUri =
|
||||
Platform.OS === "android" && !uri.startsWith("file://")
|
||||
? `file://${uri}`
|
||||
: uri;
|
||||
|
||||
// Resize the image for faster processing
|
||||
const result = await ImageManipulator.manipulateAsync(
|
||||
normalizedUri,
|
||||
[
|
||||
{
|
||||
resize: {
|
||||
width: targetWidth,
|
||||
height: targetHeight,
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
compress: 1,
|
||||
format: ImageManipulator.SaveFormat.PNG,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
uri: result.uri,
|
||||
width: result.width,
|
||||
height: result.height,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load image and get base64 data.
|
||||
* Useful for passing to native modules or Skia.
|
||||
*/
|
||||
export async function loadImageAsBase64(
|
||||
uri: string,
|
||||
targetWidth: number = 480,
|
||||
targetHeight: number = 640
|
||||
): Promise<{ base64: string; width: number; height: number }> {
|
||||
// Normalize the URI
|
||||
const normalizedUri =
|
||||
Platform.OS === "android" && !uri.startsWith("file://")
|
||||
? `file://${uri}`
|
||||
: uri;
|
||||
|
||||
const result = await ImageManipulator.manipulateAsync(
|
||||
normalizedUri,
|
||||
[
|
||||
{
|
||||
resize: {
|
||||
width: targetWidth,
|
||||
height: targetHeight,
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
compress: 1,
|
||||
format: ImageManipulator.SaveFormat.PNG,
|
||||
base64: true,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
base64: result.base64 || "",
|
||||
width: result.width,
|
||||
height: result.height,
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue