-
Notifications
You must be signed in to change notification settings - Fork 27
Text Recognition #221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Text Recognition #221
Changes from all commits
1aa93be
82ebc88
f71fb04
a0ed03c
84c9f39
a7c9912
c335f27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "@infinitered/react-native-mlkit-text-recognition": major | ||
| "example-app": major | ||
| --- | ||
|
|
||
| Added first version of Text Recognition module |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "permissions": { | ||
| "allow": [ | ||
| "WebFetch(domain:developers.google.com)" | ||
| ], | ||
| "deny": [], | ||
| "ask": [] | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| import React, { FC, useState, useEffect, useCallback } from "react" | ||
| import { observer } from "mobx-react-lite" | ||
| import { ViewStyle, View, ImageStyle, TextStyle } from "react-native" | ||
| import { NativeStackScreenProps } from "@react-navigation/native-stack" | ||
| import { AppStackScreenProps } from "../navigators" | ||
| import { Text, Icon, ImageSelector, Screen } from "../components" | ||
| import { useTypedNavigation } from "../navigators/useTypedNavigation" | ||
|
|
||
| import { recognizeText } from "@infinitered/react-native-mlkit-text-recognition" | ||
| import { UseExampleImageStatus, SelectedImage } from "../utils/useExampleImage" | ||
|
|
||
| type TextRecognitionScreenProps = NativeStackScreenProps<AppStackScreenProps<"TextRecognition">> | ||
|
|
||
| export const TextRecognitionScreen: FC<TextRecognitionScreenProps> = observer( | ||
| function TextRecognitionScreen() { | ||
| const navigation = useTypedNavigation<"TextRecognition">() | ||
|
|
||
| const [image, setImage] = useState<SelectedImage | null>(null) | ||
|
|
||
| const handleImageChange = useCallback((nextImage: SelectedImage) => { | ||
| setImage(nextImage) | ||
| }, []) | ||
|
|
||
| const [result, setResult] = useState<string | null>(null) | ||
| const [status, setStatus] = useState< | ||
| "init" | "noPermissions" | "done" | "error" | "loading" | UseExampleImageStatus | ||
| >("init") | ||
|
|
||
| const onStatusChange = React.useCallback( | ||
| (status: "init" | "noPermissions" | "done" | "error" | "loading" | UseExampleImageStatus) => { | ||
| setStatus(status) | ||
| }, | ||
| [], | ||
| ) | ||
|
|
||
| useEffect(() => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| const recognizeImage = async () => { | ||
| if (!image?.uri) return | ||
| setStatus("recognizing") | ||
| try { | ||
| const recognitionResult = await recognizeText(image.uri) | ||
| setResult(recognitionResult.text) | ||
| setStatus("done") | ||
| } catch (error) { | ||
| console.error("Error recognizing image:", error) | ||
| setStatus("error") | ||
| } | ||
| } | ||
|
|
||
| recognizeImage().then(() => null) | ||
| }, [image]) | ||
|
|
||
| const statusMessage = React.useMemo(() => { | ||
| if (!image && status !== "init") { | ||
| setStatus("init") | ||
| } | ||
| switch (status) { | ||
| case "init": | ||
| return "Take a photo or select one from your camera roll" | ||
| case "noPermissions": | ||
| return "You need to grant camera permissions to take a photo" | ||
| case "takingPhoto": | ||
| return "Taking photo..." | ||
| case "selectingPhoto": | ||
| return "Selecting photo..." | ||
| case "done": | ||
| return "Done!" | ||
| case "error": | ||
| return "Error during recognition!" | ||
| case "recognizing": | ||
| return "Recognizing Image..." | ||
| case "loading": | ||
| return "Loading Example Images..." | ||
| default: | ||
| throw new Error("Invalid status") | ||
| } | ||
| }, [result, image, status]) | ||
|
|
||
| const clearResults = useCallback(() => { | ||
| setResult(null) | ||
| }, []) | ||
|
|
||
| return ( | ||
| <Screen style={$root} preset="scroll" safeAreaEdges={["top", "bottom"]}> | ||
| <View> | ||
| <Icon icon={"back"} onPress={() => navigation.navigate("Home")} style={$backIcon} /> | ||
| <Text preset={"heading"} text="Text Recognition" /> | ||
| <Text style={$description}>Take a photo, and extract text from it.</Text> | ||
| </View> | ||
| <ImageSelector | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: did you mean to include taking pictures? Looks like this is just an image select: ScreenRecording_11-11-2025.14-29-25_1.MP4 |
||
| onImageChange={handleImageChange} | ||
| onImageClear={clearResults} | ||
| onStatusChange={onStatusChange} | ||
| statusMessage={statusMessage} | ||
| status={status} | ||
| isLoading={false} | ||
| images={{ | ||
| filter: "all", | ||
| groupBy: "label", | ||
| }} | ||
| /> | ||
|
|
||
| {result && ( | ||
| <View style={$resultContainer}> | ||
| <Text>{result}</Text> | ||
| </View> | ||
| )} | ||
| </Screen> | ||
| ) | ||
| }, | ||
| ) | ||
|
|
||
| const $root: ViewStyle = { | ||
| flex: 1, | ||
| padding: 16, | ||
| display: "flex", | ||
| flexDirection: "column", | ||
| } | ||
| const $backIcon: ImageStyle = { marginVertical: 8 } | ||
|
|
||
| const $description: TextStyle = { | ||
| marginVertical: 8, | ||
| color: "rgba(0,0,0,0.6)", | ||
| } | ||
|
|
||
| const $resultContainer: ViewStyle = { | ||
| width: "100%", | ||
| borderWidth: 1, | ||
| marginVertical: 24, | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,7 @@ | ||
| import { | ||
| ImagePickerResult, | ||
| launchCameraAsync, | ||
| launchImageLibraryAsync, | ||
| ImagePickerOptions, | ||
| MediaTypeOptions, | ||
| ImagePickerAsset, | ||
| useCameraPermissions, | ||
| } from "expo-image-picker" | ||
|
|
@@ -17,13 +15,15 @@ import { | |
| imageFilters, | ||
| imageGroupers, | ||
| } from "./examples" | ||
| import { launchImageLibrary } from "react-native-image-picker" | ||
|
|
||
| export type UseExampleImageStatus = | ||
| | "init" | ||
| | "noPermissions" | ||
| | "takingPhoto" | ||
| | "selectingPhoto" | ||
| | "classifying" | ||
| | "recognizing" | ||
| | "done" | ||
| | "error" | ||
| | "loading" | ||
|
|
@@ -56,7 +56,7 @@ interface ZippedImage extends RandomImage { | |
| } | ||
|
|
||
| const IMAGE_PICKER_OPTIONS: ImagePickerOptions = { | ||
| mediaTypes: MediaTypeOptions.Images, | ||
| mediaTypes: "images", | ||
| allowsEditing: false, | ||
| quality: 0.5, | ||
| } | ||
|
|
@@ -127,7 +127,9 @@ export function useExampleImage(predicates?: { | |
| return | ||
| } | ||
| setStatus("takingPhoto") | ||
| const result = await launchCameraAsync(IMAGE_PICKER_OPTIONS) | ||
| const result = await launchImageLibrary({ | ||
| mediaType: "photo", | ||
| }) | ||
|
Comment on lines
+130
to
+132
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did we need to add |
||
| if (result.assets?.[0]) { | ||
| setImage({ ...result.assets?.[0], localUri: result.assets?.[0].uri } as SelectedImage) | ||
| } else { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,34 +36,36 @@ | |
| "@infinitered/react-native-mlkit-face-detection": "workspace:^3.1.0", | ||
| "@infinitered/react-native-mlkit-image-labeling": "workspace:^3.1.0", | ||
| "@infinitered/react-native-mlkit-object-detection": "workspace:^3.1.0", | ||
| "@react-native-async-storage/async-storage": "^2.0.0", | ||
| "@infinitered/react-native-mlkit-text-recognition": "workspace:^1.0.0", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: I don't think we need all the changes here, and it might be breaking our Android builds. Can we revisit these changes to a minimal set of dependency upgrades? |
||
| "@react-native-async-storage/async-storage": "1.23.1", | ||
| "@react-navigation/native": "^6.0.8", | ||
| "@react-navigation/native-stack": "^6.0.2", | ||
| "@shopify/flash-list": "1.7.1", | ||
| "@shopify/flash-list": "1.7.3", | ||
| "apisauce": "3.0.1", | ||
| "date-fns": "^2.30.0", | ||
| "expo": "^52.0.28", | ||
| "expo-build-properties": "~0.13.2", | ||
| "expo-dev-client": "~5.0.11", | ||
| "expo-font": "~13.0.3", | ||
| "expo-image": "~2.0.4", | ||
| "expo-image-picker": "~16.0.5", | ||
| "expo": "~52.0.47", | ||
| "expo-build-properties": "~0.13.3", | ||
| "expo-dev-client": "~5.0.20", | ||
| "expo-font": "~13.0.4", | ||
| "expo-image": "~2.0.7", | ||
| "expo-image-picker": "~16.0.6", | ||
| "expo-linking": "~7.0.5", | ||
| "expo-localization": "~16.0.1", | ||
| "expo-splash-screen": "~0.29.21", | ||
| "expo-splash-screen": "~0.29.24", | ||
| "expo-status-bar": "~2.0.1", | ||
| "i18n-js": "3.9.2", | ||
| "mobx": "6.10.2", | ||
| "mobx-react-lite": "4.0.5", | ||
| "mobx-state-tree": "5.3.0", | ||
| "react": "18.3.1", | ||
| "react-dom": "^18.3.1", | ||
| "react-native": "^0.76.0", | ||
| "react-native": "0.76.9", | ||
| "react-native-device-info": "^14.0.4", | ||
| "react-native-gesture-handler": "^2.20.0", | ||
| "react-native-gesture-handler": "~2.20.2", | ||
| "react-native-image-picker": "^8.2.1", | ||
| "react-native-reanimated": "^3.16.1", | ||
| "react-native-safe-area-context": "^4.12.0", | ||
| "react-native-screens": "^3.34.0", | ||
| "react-native-safe-area-context": "4.12.0", | ||
| "react-native-screens": "~4.4.0", | ||
| "react-native-web": "~0.19.13" | ||
| }, | ||
| "devDependencies": { | ||
|
|
@@ -100,7 +102,7 @@ | |
| "eslint-plugin-reactotron": "^0.1.2", | ||
| "expo-atlas": "^0.3.0", | ||
| "jest": "^29.2.1", | ||
| "jest-expo": "~52.0.3", | ||
| "jest-expo": "~52.0.6", | ||
| "patch-package": "6.4.7", | ||
| "postinstall-prepare": "1.0.1", | ||
| "prettier": "2.8.8", | ||
|
|
@@ -111,7 +113,7 @@ | |
| "reactotron-react-native": "^5.0.5", | ||
| "ts-jest": "^29.1.1", | ||
| "ts-node": "^10.9.2", | ||
| "typescript": "~5.1.6" | ||
| "typescript": "^5.3.3" | ||
| }, | ||
| "engines": { | ||
| "node": ">=18" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "label": "Text Recognition", | ||
| "position": 500, | ||
| "link": { | ||
| "type": "generated-index", | ||
| "description": "Recognize text" | ||
| } | ||
| } |

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: please remove this claude config - I don't want other people to get the same permissions you granted on your machine.